GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,465 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
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 I2C_SET_DEFAULT_BUS_NUM
|
||||
bool "Set default I2C bus number"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Set default number of I2C bus to be accessed. This option provides
|
||||
behaviour similar to old (i.e. pre DM) I2C bus driver.
|
||||
|
||||
config I2C_DEFAULT_BUS_NUMBER
|
||||
hex "I2C default bus number"
|
||||
depends on I2C_SET_DEFAULT_BUS_NUM
|
||||
default 0x0
|
||||
help
|
||||
Number of default I2C bus to use
|
||||
|
||||
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_AT91
|
||||
bool "Atmel I2C driver"
|
||||
depends on DM_I2C && ARCH_AT91
|
||||
help
|
||||
Add support for the Atmel I2C driver. A serious problem is that there
|
||||
is no documented way to issue repeated START conditions for more than
|
||||
two messages, as needed to support combined I2C messages. Use the
|
||||
i2c-gpio driver unless your system can cope with this limitation.
|
||||
Binding info: doc/device-tree-bindings/i2c/i2c-at91.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_DAVINCI
|
||||
bool "Davinci I2C Controller"
|
||||
depends on (ARCH_KEYSTONE || ARCH_DAVINCI)
|
||||
help
|
||||
Say yes here to add support for Davinci and Keystone I2C controller
|
||||
|
||||
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_ASPEED
|
||||
bool "Aspeed I2C Controller"
|
||||
depends on DM_I2C && ARCH_ASPEED
|
||||
help
|
||||
Say yes here to select Aspeed I2C Host Controller. The driver
|
||||
supports AST2500 and AST2400 controllers, but is very limited.
|
||||
Only single master mode is supported and only byte-by-byte
|
||||
synchronous reads and writes are supported, no Pool Buffers or DMA.
|
||||
|
||||
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_IMX_LPI2C
|
||||
bool "NXP i.MX LPI2C driver"
|
||||
help
|
||||
Add support for the NXP i.MX LPI2C driver.
|
||||
|
||||
config SYS_I2C_MESON
|
||||
bool "Amlogic Meson I2C driver"
|
||||
depends on DM_I2C && ARCH_MESON
|
||||
help
|
||||
Add support for the I2C controller available in Amlogic Meson
|
||||
SoCs. The controller supports programmable bus speed including
|
||||
standard (100kbits/s) and fast (400kbit/s) speed and allows the
|
||||
software to define a flexible format of the bit streams. It has an
|
||||
internal buffer holding up to 8 bytes for transfers and supports
|
||||
both 7-bit and 10-bit addresses.
|
||||
|
||||
config SYS_I2C_MXC
|
||||
bool "NXP MXC I2C driver"
|
||||
help
|
||||
Add support for the NXP I2C driver. This supports up to four bus
|
||||
channels and operating on standard mode up to 100 kbits/s and fast
|
||||
mode up to 400 kbits/s.
|
||||
|
||||
# These settings are not used with DM_I2C, however SPL doesn't use
|
||||
# DM_I2C even if DM_I2C is enabled, and so might use these settings even
|
||||
# when main u-boot does not!
|
||||
if SYS_I2C_MXC && (!DM_I2C || SPL)
|
||||
config SYS_I2C_MXC_I2C1
|
||||
bool "NXP MXC I2C1"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 1.
|
||||
Required for SoCs which have I2C MXC controller 1 eg LS1088A, LS2080A
|
||||
|
||||
config SYS_I2C_MXC_I2C2
|
||||
bool "NXP MXC I2C2"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 2.
|
||||
Required for SoCs which have I2C MXC controller 2 eg LS1088A, LS2080A
|
||||
|
||||
config SYS_I2C_MXC_I2C3
|
||||
bool "NXP MXC I2C3"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 3.
|
||||
Required for SoCs which have I2C MXC controller 3 eg LS1088A, LS2080A
|
||||
|
||||
config SYS_I2C_MXC_I2C4
|
||||
bool "NXP MXC I2C4"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 4.
|
||||
Required for SoCs which have I2C MXC controller 4 eg LS1088A, LS2080A
|
||||
|
||||
config SYS_I2C_MXC_I2C5
|
||||
bool "NXP MXC I2C5"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 5.
|
||||
Required for SoCs which have I2C MXC controller 5 eg LX2160A
|
||||
|
||||
config SYS_I2C_MXC_I2C6
|
||||
bool "NXP MXC I2C6"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 6.
|
||||
Required for SoCs which have I2C MXC controller 6 eg LX2160A
|
||||
|
||||
config SYS_I2C_MXC_I2C7
|
||||
bool "NXP MXC I2C7"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 7.
|
||||
Required for SoCs which have I2C MXC controller 7 eg LX2160A
|
||||
|
||||
config SYS_I2C_MXC_I2C8
|
||||
bool "NXP MXC I2C8"
|
||||
help
|
||||
Add support for NXP MXC I2C Controller 8.
|
||||
Required for SoCs which have I2C MXC controller 8 eg LX2160A
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C1
|
||||
config SYS_MXC_I2C1_SPEED
|
||||
int "I2C Channel 1 speed"
|
||||
default 40000000 if TARGET_LS2080A_SIMU || TARGET_LS2080A_EMU
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 1 speed
|
||||
|
||||
config SYS_MXC_I2C1_SLAVE
|
||||
int "I2C1 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C1 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C2
|
||||
config SYS_MXC_I2C2_SPEED
|
||||
int "I2C Channel 2 speed"
|
||||
default 40000000 if TARGET_LS2080A_SIMU || TARGET_LS2080A_EMU
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 2 speed
|
||||
|
||||
config SYS_MXC_I2C2_SLAVE
|
||||
int "I2C2 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C2 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C3
|
||||
config SYS_MXC_I2C3_SPEED
|
||||
int "I2C Channel 3 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 3 speed
|
||||
|
||||
config SYS_MXC_I2C3_SLAVE
|
||||
int "I2C3 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C3 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C4
|
||||
config SYS_MXC_I2C4_SPEED
|
||||
int "I2C Channel 4 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 4 speed
|
||||
|
||||
config SYS_MXC_I2C4_SLAVE
|
||||
int "I2C4 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C4 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C5
|
||||
config SYS_MXC_I2C5_SPEED
|
||||
int "I2C Channel 5 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 5 speed
|
||||
|
||||
config SYS_MXC_I2C5_SLAVE
|
||||
int "I2C5 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C5 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C6
|
||||
config SYS_MXC_I2C6_SPEED
|
||||
int "I2C Channel 6 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 6 speed
|
||||
|
||||
config SYS_MXC_I2C6_SLAVE
|
||||
int "I2C6 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C6 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C7
|
||||
config SYS_MXC_I2C7_SPEED
|
||||
int "I2C Channel 7 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 7 speed
|
||||
|
||||
config SYS_MXC_I2C7_SLAVE
|
||||
int "I2C7 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C7 Slave
|
||||
endif
|
||||
|
||||
if SYS_I2C_MXC_I2C8
|
||||
config SYS_MXC_I2C8_SPEED
|
||||
int "I2C Channel 8 speed"
|
||||
default 100000
|
||||
help
|
||||
MXC I2C Channel 8 speed
|
||||
|
||||
config SYS_MXC_I2C8_SLAVE
|
||||
int "I2C8 Slave"
|
||||
default 0
|
||||
help
|
||||
MXC I2C8 Slave
|
||||
endif
|
||||
|
||||
config SYS_I2C_OMAP24XX
|
||||
bool "TI OMAP2+ I2C driver"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_K3
|
||||
help
|
||||
Add support for the OMAP2+ I2C driver.
|
||||
|
||||
if SYS_I2C_OMAP24XX
|
||||
config SYS_OMAP24_I2C_SLAVE
|
||||
int "I2C Slave addr channel 0"
|
||||
default 1
|
||||
help
|
||||
OMAP24xx I2C Slave address channel 0
|
||||
|
||||
config SYS_OMAP24_I2C_SPEED
|
||||
int "I2C Slave channel 0 speed"
|
||||
default 100000
|
||||
help
|
||||
OMAP24xx Slave speed channel 0
|
||||
endif
|
||||
|
||||
config SYS_I2C_RCAR_I2C
|
||||
bool "Renesas RCar I2C driver"
|
||||
depends on (RCAR_GEN3 || RCAR_GEN2) && DM_I2C
|
||||
help
|
||||
Support for Renesas RCar I2C controller.
|
||||
|
||||
config SYS_I2C_RCAR_IIC
|
||||
bool "Renesas RCar Gen3 IIC driver"
|
||||
depends on (RCAR_GEN3 || RCAR_GEN2) && DM_I2C
|
||||
help
|
||||
Support for Renesas RCar Gen3 IIC controller.
|
||||
|
||||
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, controlled 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. See sandbox.dts as an example.
|
||||
|
||||
config SYS_I2C_S3C24X0
|
||||
bool "Samsung I2C driver"
|
||||
depends on ARCH_EXYNOS4 && DM_I2C
|
||||
help
|
||||
Support for Samsung I2C controller as Samsung SoCs.
|
||||
|
||||
config SYS_I2C_STM32F7
|
||||
bool "STMicroelectronics STM32F7 I2C support"
|
||||
depends on (STM32F7 || STM32H7 || ARCH_STM32MP) && DM_I2C
|
||||
help
|
||||
Enable this option to add support for STM32 I2C controller
|
||||
introduced with STM32F7/H7 SoCs. This I2C controller supports :
|
||||
_ Slave and master modes
|
||||
_ Multimaster capability
|
||||
_ Standard-mode (up to 100 kHz)
|
||||
_ Fast-mode (up to 400 kHz)
|
||||
_ Fast-mode Plus (up to 1 MHz)
|
||||
_ 7-bit and 10-bit addressing mode
|
||||
_ Multiple 7-bit slave addresses (2 addresses, 1 with configurable mask)
|
||||
_ All 7-bit addresses acknowledge mode
|
||||
_ General call
|
||||
_ Programmable setup and hold times
|
||||
_ Easy to use event management
|
||||
_ Optional clock stretching
|
||||
_ Software reset
|
||||
|
||||
config SYS_I2C_TEGRA
|
||||
bool "NVIDIA Tegra internal I2C controller"
|
||||
depends on TEGRA
|
||||
help
|
||||
Support for NVIDIA I2C controller available in Tegra SoCs.
|
||||
|
||||
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.
|
||||
|
||||
config SYS_I2C_VERSATILE
|
||||
bool "Arm Ltd Versatile I2C bus driver"
|
||||
depends on DM_I2C && (TARGET_VEXPRESS_CA15_TC2 || TARGET_VEXPRESS64_JUNO)
|
||||
help
|
||||
Add support for the Arm Ltd Versatile Express I2C driver. The I2C host
|
||||
controller is present in the development boards manufactured by Arm Ltd.
|
||||
|
||||
config SYS_I2C_MVTWSI
|
||||
bool "Marvell I2C driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Support for Marvell I2C controllers as used on the orion5x and
|
||||
kirkwood SoC families.
|
||||
|
||||
config TEGRA186_BPMP_I2C
|
||||
bool "Enable Tegra186 BPMP-based I2C driver"
|
||||
depends on TEGRA186_BPMP
|
||||
help
|
||||
Support for Tegra I2C controllers managed by the BPMP (Boot and
|
||||
Power Management Processor). On Tegra186, some I2C controllers are
|
||||
directly controlled by the main CPU, whereas others are controlled
|
||||
by the BPMP, and can only be accessed by the main CPU via IPC
|
||||
requests to the BPMP. This driver covers the latter case.
|
||||
|
||||
config SYS_I2C_BUS_MAX
|
||||
int "Max I2C busses"
|
||||
depends on ARCH_KEYSTONE || ARCH_OMAP2PLUS || ARCH_SOCFPGA
|
||||
default 2 if TI816X
|
||||
default 3 if OMAP34XX || AM33XX || AM43XX || ARCH_KEYSTONE
|
||||
default 4 if ARCH_SOCFPGA || OMAP44XX || TI814X
|
||||
default 5 if OMAP54XX
|
||||
help
|
||||
Define the maximum number of available I2C buses.
|
||||
|
||||
config SYS_I2C_XILINX_XIIC
|
||||
bool "Xilinx AXI I2C driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Support for Xilinx AXI I2C controller.
|
||||
|
||||
config SYS_I2C_IHS
|
||||
bool "gdsys IHS I2C driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Support for gdsys IHS I2C driver on FPGA bus.
|
||||
|
||||
source "drivers/i2c/muxes/Kconfig"
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,42 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
obj-$(CONFIG_DM_I2C) += i2c-uclass.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_I2C_MV) += mv_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C) += i2c_core.o
|
||||
obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.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_IHS) += ihs_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o
|
||||
obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
|
||||
obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
|
||||
obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_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_STM32F7) += stm32f7_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_VERSATILE) += i2c-versatile.o
|
||||
obj-$(CONFIG_SYS_I2C_XILINX_XIIC) += xilinx_xiic.o
|
||||
obj-$(CONFIG_TEGRA186_BPMP_I2C) += tegra186_bpmp_i2c.o
|
||||
|
||||
obj-$(CONFIG_I2C_MUX) += muxes/
|
||||
@@ -0,0 +1,354 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2012-2020 ASPEED Technology Inc.
|
||||
* Copyright 2016 IBM Corporation
|
||||
* Copyright 2017 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/scu_ast2500.h>
|
||||
|
||||
#include "ast_i2c.h"
|
||||
|
||||
#define I2C_TIMEOUT_US 100000
|
||||
#define I2C_SLEEP_STEP_US 20
|
||||
|
||||
#define HIGHSPEED_TTIMEOUT 3
|
||||
|
||||
/*
|
||||
* Device private data
|
||||
*/
|
||||
struct ast_i2c_priv {
|
||||
/* This device's clock */
|
||||
struct clk clk;
|
||||
/* Device registers */
|
||||
struct ast_i2c_regs *regs;
|
||||
/* I2C speed in Hz */
|
||||
int speed;
|
||||
};
|
||||
|
||||
/*
|
||||
* Given desired divider ratio, return the value that needs to be set
|
||||
* in Clock and AC Timing Control register
|
||||
*/
|
||||
static u32 get_clk_reg_val(ulong divider_ratio)
|
||||
{
|
||||
ulong inc = 0, div;
|
||||
ulong scl_low, scl_high, data;
|
||||
|
||||
for (div = 0; divider_ratio >= 16; div++) {
|
||||
inc |= (divider_ratio & 1);
|
||||
divider_ratio >>= 1;
|
||||
}
|
||||
divider_ratio += inc;
|
||||
scl_low = (divider_ratio >> 1) - 1;
|
||||
scl_high = divider_ratio - scl_low - 2;
|
||||
data = I2CD_CACTC_BASE
|
||||
| (scl_high << I2CD_TCKHIGH_SHIFT)
|
||||
| (scl_low << I2CD_TCKLOW_SHIFT)
|
||||
| (div << I2CD_BASE_DIV_SHIFT);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void ast_i2c_clear_interrupts(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
writel(~0, &priv->regs->isr);
|
||||
}
|
||||
|
||||
static void ast_i2c_init_bus(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Reset device */
|
||||
writel(0, &priv->regs->fcr);
|
||||
/* Enable Master Mode. Assuming single-master */
|
||||
writel(I2CD_MASTER_EN
|
||||
| I2CD_M_SDA_LOCK_EN
|
||||
| I2CD_MULTI_MASTER_DIS | I2CD_M_SCL_DRIVE_EN,
|
||||
&priv->regs->fcr);
|
||||
/* Enable Interrupts */
|
||||
writel(I2CD_INTR_TX_ACK
|
||||
| I2CD_INTR_TX_NAK
|
||||
| I2CD_INTR_RX_DONE
|
||||
| I2CD_INTR_BUS_RECOVER_DONE
|
||||
| I2CD_INTR_NORMAL_STOP
|
||||
| I2CD_INTR_ABNORMAL, &priv->regs->icr);
|
||||
}
|
||||
|
||||
static int ast_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->regs = devfdt_get_addr_ptr(dev);
|
||||
if (IS_ERR(priv->regs))
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &priv->clk);
|
||||
if (ret < 0) {
|
||||
debug("%s: Can't get clock for %s: %d\n", __func__, dev->name,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct ast2500_scu *scu;
|
||||
|
||||
debug("Enabling I2C%u\n", dev->seq);
|
||||
|
||||
/*
|
||||
* Get all I2C devices out of Reset.
|
||||
* Only needs to be done once, but doing it for every
|
||||
* device does not hurt.
|
||||
*/
|
||||
scu = ast_get_scu();
|
||||
ast_scu_unlock(scu);
|
||||
clrbits_le32(&scu->sysreset_ctrl1, SCU_SYSRESET_I2C);
|
||||
ast_scu_lock(scu);
|
||||
|
||||
ast_i2c_init_bus(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_wait_isr(struct udevice *dev, u32 flag)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
int timeout = I2C_TIMEOUT_US;
|
||||
|
||||
while (!(readl(&priv->regs->isr) & flag) && timeout > 0) {
|
||||
udelay(I2C_SLEEP_STEP_US);
|
||||
timeout -= I2C_SLEEP_STEP_US;
|
||||
}
|
||||
|
||||
ast_i2c_clear_interrupts(dev);
|
||||
if (timeout <= 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_send_stop(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
writel(I2CD_M_STOP_CMD, &priv->regs->csr);
|
||||
|
||||
return ast_i2c_wait_isr(dev, I2CD_INTR_NORMAL_STOP);
|
||||
}
|
||||
|
||||
static int ast_i2c_wait_tx(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
int timeout = I2C_TIMEOUT_US;
|
||||
u32 flag = I2CD_INTR_TX_ACK | I2CD_INTR_TX_NAK;
|
||||
u32 status = readl(&priv->regs->isr) & flag;
|
||||
int ret = 0;
|
||||
|
||||
while (!status && timeout > 0) {
|
||||
status = readl(&priv->regs->isr) & flag;
|
||||
udelay(I2C_SLEEP_STEP_US);
|
||||
timeout -= I2C_SLEEP_STEP_US;
|
||||
}
|
||||
|
||||
if (status == I2CD_INTR_TX_NAK)
|
||||
ret = -EREMOTEIO;
|
||||
|
||||
if (timeout <= 0)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
ast_i2c_clear_interrupts(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ast_i2c_start_txn(struct udevice *dev, uint devaddr)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Start and Send Device Address */
|
||||
writel(devaddr, &priv->regs->trbbr);
|
||||
writel(I2CD_M_START_CMD | I2CD_M_TX_CMD, &priv->regs->csr);
|
||||
|
||||
return ast_i2c_wait_tx(dev);
|
||||
}
|
||||
|
||||
static int ast_i2c_read_data(struct udevice *dev, u8 chip_addr, u8 *buffer,
|
||||
size_t len, bool send_stop)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 i2c_cmd = I2CD_M_RX_CMD;
|
||||
int ret;
|
||||
|
||||
ret = ast_i2c_start_txn(dev, (chip_addr << 1) | I2C_M_RD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (; len > 0; len--, buffer++) {
|
||||
if (len == 1)
|
||||
i2c_cmd |= I2CD_M_S_RX_CMD_LAST;
|
||||
writel(i2c_cmd, &priv->regs->csr);
|
||||
ret = ast_i2c_wait_isr(dev, I2CD_INTR_RX_DONE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*buffer = (readl(&priv->regs->trbbr) & I2CD_RX_DATA_MASK)
|
||||
>> I2CD_RX_DATA_SHIFT;
|
||||
}
|
||||
ast_i2c_clear_interrupts(dev);
|
||||
|
||||
if (send_stop)
|
||||
return ast_i2c_send_stop(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_write_data(struct udevice *dev, u8 chip_addr, u8
|
||||
*buffer, size_t len, bool send_stop)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = ast_i2c_start_txn(dev, (chip_addr << 1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (; len > 0; len--, buffer++) {
|
||||
writel(*buffer, &priv->regs->trbbr);
|
||||
writel(I2CD_M_TX_CMD, &priv->regs->csr);
|
||||
ret = ast_i2c_wait_tx(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (send_stop)
|
||||
return ast_i2c_send_stop(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_deblock(struct udevice *dev)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
struct ast_i2c_regs *regs = priv->regs;
|
||||
u32 csr = readl(®s->csr);
|
||||
bool sda_high = csr & I2CD_SDA_LINE_STS;
|
||||
bool scl_high = csr & I2CD_SCL_LINE_STS;
|
||||
int ret = 0;
|
||||
|
||||
if (sda_high && scl_high) {
|
||||
/* Bus is idle, no deblocking needed. */
|
||||
return 0;
|
||||
} else if (sda_high) {
|
||||
/* Send stop command */
|
||||
debug("Unterminated TXN in (%x), sending stop\n", csr);
|
||||
ret = ast_i2c_send_stop(dev);
|
||||
} else if (scl_high) {
|
||||
/* Possibly stuck slave */
|
||||
debug("Bus stuck (%x), attempting recovery\n", csr);
|
||||
writel(I2CD_BUS_RECOVER_CMD, ®s->csr);
|
||||
ret = ast_i2c_wait_isr(dev, I2CD_INTR_BUS_RECOVER_DONE);
|
||||
} else {
|
||||
/* Just try to reinit the device. */
|
||||
ast_i2c_init_bus(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ast_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ast_i2c_deblock(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
debug("i2c_read: chip=0x%x, len=0x%x, flags=0x%x\n",
|
||||
msg->addr, msg->len, msg->flags);
|
||||
ret = ast_i2c_read_data(dev, msg->addr, msg->buf,
|
||||
msg->len, (nmsgs == 1));
|
||||
} else {
|
||||
debug("i2c_write: chip=0x%x, len=0x%x, flags=0x%x\n",
|
||||
msg->addr, msg->len, msg->flags);
|
||||
ret = ast_i2c_write_data(dev, msg->addr, msg->buf,
|
||||
msg->len, (nmsgs == 1));
|
||||
}
|
||||
if (ret) {
|
||||
debug("%s: error (%d)\n", __func__, ret);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_i2c_set_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct ast_i2c_priv *priv = dev_get_priv(dev);
|
||||
struct ast_i2c_regs *regs = priv->regs;
|
||||
ulong i2c_rate, divider;
|
||||
|
||||
debug("Setting speed for I2C%d to <%u>\n", dev->seq, speed);
|
||||
if (!speed) {
|
||||
debug("No valid speed specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_rate = clk_get_rate(&priv->clk);
|
||||
divider = i2c_rate / speed;
|
||||
|
||||
priv->speed = speed;
|
||||
if (speed > I2C_HIGHSPEED_RATE) {
|
||||
debug("Enable High Speed\n");
|
||||
setbits_le32(®s->fcr, I2CD_M_HIGH_SPEED_EN
|
||||
| I2CD_M_SDA_DRIVE_1T_EN
|
||||
| I2CD_SDA_DRIVE_1T_EN);
|
||||
writel(HIGHSPEED_TTIMEOUT, ®s->cactcr2);
|
||||
} else {
|
||||
debug("Enabling Normal Speed\n");
|
||||
writel(I2CD_NO_TIMEOUT_CTRL, ®s->cactcr2);
|
||||
}
|
||||
|
||||
writel(get_clk_reg_val(divider), ®s->cactcr1);
|
||||
ast_i2c_clear_interrupts(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops ast_i2c_ops = {
|
||||
.xfer = ast_i2c_xfer,
|
||||
.set_bus_speed = ast_i2c_set_speed,
|
||||
.deblock = ast_i2c_deblock,
|
||||
};
|
||||
|
||||
static const struct udevice_id ast_i2c_ids[] = {
|
||||
{ .compatible = "aspeed,ast2400-i2c-bus" },
|
||||
{ .compatible = "aspeed,ast2500-i2c-bus" },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ast_i2c) = {
|
||||
.name = "ast_i2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = ast_i2c_ids,
|
||||
.probe = ast_i2c_probe,
|
||||
.ofdata_to_platdata = ast_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct ast_i2c_priv),
|
||||
.ops = &ast_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2012-2020 ASPEED Technology Inc.
|
||||
* Copyright 2016 IBM Corporation
|
||||
* Copyright 2017 Google, Inc.
|
||||
*/
|
||||
#ifndef __AST_I2C_H_
|
||||
#define __AST_I2C_H_
|
||||
|
||||
struct ast_i2c_regs {
|
||||
u32 fcr;
|
||||
u32 cactcr1;
|
||||
u32 cactcr2;
|
||||
u32 icr;
|
||||
u32 isr;
|
||||
u32 csr;
|
||||
u32 sdar;
|
||||
u32 pbcr;
|
||||
u32 trbbr;
|
||||
#ifdef CONFIG_ASPEED_AST2500
|
||||
u32 dma_mbar;
|
||||
u32 dma_tlr;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Device Register Definition */
|
||||
/* 0x00 : I2CD Function Control Register */
|
||||
#define I2CD_BUFF_SEL_MASK (0x7 << 20)
|
||||
#define I2CD_BUFF_SEL(x) (x << 20)
|
||||
#define I2CD_M_SDA_LOCK_EN (0x1 << 16)
|
||||
#define I2CD_MULTI_MASTER_DIS (0x1 << 15)
|
||||
#define I2CD_M_SCL_DRIVE_EN (0x1 << 14)
|
||||
#define I2CD_MSB_STS (0x1 << 9)
|
||||
#define I2CD_SDA_DRIVE_1T_EN (0x1 << 8)
|
||||
#define I2CD_M_SDA_DRIVE_1T_EN (0x1 << 7)
|
||||
#define I2CD_M_HIGH_SPEED_EN (0x1 << 6)
|
||||
#define I2CD_DEF_ADDR_EN (0x1 << 5)
|
||||
#define I2CD_DEF_ALERT_EN (0x1 << 4)
|
||||
#define I2CD_DEF_ARP_EN (0x1 << 3)
|
||||
#define I2CD_DEF_GCALL_EN (0x1 << 2)
|
||||
#define I2CD_SLAVE_EN (0x1 << 1)
|
||||
#define I2CD_MASTER_EN (0x1)
|
||||
|
||||
/* 0x04 : I2CD Clock and AC Timing Control Register #1 */
|
||||
/* Base register value. These bits are always set by the driver. */
|
||||
#define I2CD_CACTC_BASE 0xfff00300
|
||||
#define I2CD_TCKHIGH_SHIFT 16
|
||||
#define I2CD_TCKLOW_SHIFT 12
|
||||
#define I2CD_THDDAT_SHIFT 10
|
||||
#define I2CD_TO_DIV_SHIFT 8
|
||||
#define I2CD_BASE_DIV_SHIFT 0
|
||||
|
||||
/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
|
||||
#define I2CD_tTIMEOUT 1
|
||||
#define I2CD_NO_TIMEOUT_CTRL 0
|
||||
|
||||
/* 0x0c : I2CD Interrupt Control Register &
|
||||
* 0x10 : I2CD Interrupt Status Register
|
||||
*
|
||||
* These share bit definitions, so use the same values for the enable &
|
||||
* status bits.
|
||||
*/
|
||||
#define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14)
|
||||
#define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13)
|
||||
#define I2CD_INTR_SMBUS_ALERT (0x1 << 12)
|
||||
#define I2CD_INTR_SMBUS_ARP_ADDR (0x1 << 11)
|
||||
#define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10)
|
||||
#define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9)
|
||||
#define I2CD_INTR_GCALL_ADDR (0x1 << 8)
|
||||
#define I2CD_INTR_SLAVE_MATCH (0x1 << 7)
|
||||
#define I2CD_INTR_SCL_TIMEOUT (0x1 << 6)
|
||||
#define I2CD_INTR_ABNORMAL (0x1 << 5)
|
||||
#define I2CD_INTR_NORMAL_STOP (0x1 << 4)
|
||||
#define I2CD_INTR_ARBIT_LOSS (0x1 << 3)
|
||||
#define I2CD_INTR_RX_DONE (0x1 << 2)
|
||||
#define I2CD_INTR_TX_NAK (0x1 << 1)
|
||||
#define I2CD_INTR_TX_ACK (0x1 << 0)
|
||||
|
||||
/* 0x14 : I2CD Command/Status Register */
|
||||
#define I2CD_SDA_OE (0x1 << 28)
|
||||
#define I2CD_SDA_O (0x1 << 27)
|
||||
#define I2CD_SCL_OE (0x1 << 26)
|
||||
#define I2CD_SCL_O (0x1 << 25)
|
||||
#define I2CD_TX_TIMING (0x1 << 24)
|
||||
#define I2CD_TX_STATUS (0x1 << 23)
|
||||
|
||||
/* Tx State Machine */
|
||||
#define I2CD_IDLE 0x0
|
||||
#define I2CD_MACTIVE 0x8
|
||||
#define I2CD_MSTART 0x9
|
||||
#define I2CD_MSTARTR 0xa
|
||||
#define I2CD_MSTOP 0xb
|
||||
#define I2CD_MTXD 0xc
|
||||
#define I2CD_MRXACK 0xd
|
||||
#define I2CD_MRXD 0xe
|
||||
#define I2CD_MTXACK 0xf
|
||||
#define I2CD_SWAIT 0x1
|
||||
#define I2CD_SRXD 0x4
|
||||
#define I2CD_STXACK 0x5
|
||||
#define I2CD_STXD 0x6
|
||||
#define I2CD_SRXACK 0x7
|
||||
#define I2CD_RECOVER 0x3
|
||||
|
||||
#define I2CD_SCL_LINE_STS (0x1 << 18)
|
||||
#define I2CD_SDA_LINE_STS (0x1 << 17)
|
||||
#define I2CD_BUS_BUSY_STS (0x1 << 16)
|
||||
#define I2CD_SDA_OE_OUT_DIR (0x1 << 15)
|
||||
#define I2CD_SDA_O_OUT_DIR (0x1 << 14)
|
||||
#define I2CD_SCL_OE_OUT_DIR (0x1 << 13)
|
||||
#define I2CD_SCL_O_OUT_DIR (0x1 << 12)
|
||||
#define I2CD_BUS_RECOVER_CMD (0x1 << 11)
|
||||
#define I2CD_S_ALT_EN (0x1 << 10)
|
||||
#define I2CD_RX_DMA_ENABLE (0x1 << 9)
|
||||
#define I2CD_TX_DMA_ENABLE (0x1 << 8)
|
||||
|
||||
/* Command Bit */
|
||||
#define I2CD_RX_BUFF_ENABLE (0x1 << 7)
|
||||
#define I2CD_TX_BUFF_ENABLE (0x1 << 6)
|
||||
#define I2CD_M_STOP_CMD (0x1 << 5)
|
||||
#define I2CD_M_S_RX_CMD_LAST (0x1 << 4)
|
||||
#define I2CD_M_RX_CMD (0x1 << 3)
|
||||
#define I2CD_S_TX_CMD (0x1 << 2)
|
||||
#define I2CD_M_TX_CMD (0x1 << 1)
|
||||
#define I2CD_M_START_CMD 0x1
|
||||
|
||||
#define I2CD_RX_DATA_SHIFT 8
|
||||
#define I2CD_RX_DATA_MASK (0xff << I2CD_RX_DATA_SHIFT)
|
||||
|
||||
#define I2C_HIGHSPEED_RATE 400000
|
||||
|
||||
#endif /* __AST_I2C_H_ */
|
||||
@@ -0,0 +1,323 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Atmel I2C driver.
|
||||
*
|
||||
* (C) Copyright 2016 Songjun Wu <songjun.wu@atmel.com>
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <mach/clk.h>
|
||||
|
||||
#include "at91_i2c.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define I2C_TIMEOUT_MS 100
|
||||
|
||||
static int at91_wait_for_xfer(struct at91_i2c_bus *bus, u32 status)
|
||||
{
|
||||
struct at91_i2c_regs *reg = bus->regs;
|
||||
ulong start_time = get_timer(0);
|
||||
u32 sr;
|
||||
|
||||
bus->status = 0;
|
||||
|
||||
do {
|
||||
sr = readl(®->sr);
|
||||
bus->status |= sr;
|
||||
|
||||
if (sr & TWI_SR_NACK)
|
||||
return -EREMOTEIO;
|
||||
else if (sr & status)
|
||||
return 0;
|
||||
} while (get_timer(start_time) < I2C_TIMEOUT_MS);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int at91_i2c_xfer_msg(struct at91_i2c_bus *bus, struct i2c_msg *msg)
|
||||
{
|
||||
struct at91_i2c_regs *reg = bus->regs;
|
||||
bool is_read = msg->flags & I2C_M_RD;
|
||||
u32 i;
|
||||
int ret = 0;
|
||||
|
||||
readl(®->sr);
|
||||
if (is_read) {
|
||||
writel(TWI_CR_START, ®->cr);
|
||||
|
||||
for (i = 0; !ret && i < (msg->len - 1); i++) {
|
||||
ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY);
|
||||
msg->buf[i] = readl(®->rhr);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
writel(TWI_CR_STOP, ®->cr);
|
||||
|
||||
ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
msg->buf[i] = readl(®->rhr);
|
||||
|
||||
} else {
|
||||
writel(msg->buf[0], ®->thr);
|
||||
ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY);
|
||||
|
||||
for (i = 1; !ret && (i < msg->len); i++) {
|
||||
writel(msg->buf[i], ®->thr);
|
||||
ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
writel(TWI_CR_STOP, ®->cr);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = at91_wait_for_xfer(bus, TWI_SR_TXCOMP);
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
if (bus->status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_LOCK)) {
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (bus->status & TWI_SR_LOCK)
|
||||
writel(TWI_CR_LOCKCLR, ®->cr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at91_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
struct at91_i2c_regs *reg = bus->regs;
|
||||
struct i2c_msg *m_start = msg;
|
||||
bool is_read;
|
||||
u32 int_addr_flag = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (nmsgs == 2) {
|
||||
int internal_address = 0;
|
||||
int i;
|
||||
|
||||
/* 1st msg is put into the internal address, start with 2nd */
|
||||
m_start = &msg[1];
|
||||
|
||||
/* the max length of internal address is 3 bytes */
|
||||
if (msg->len > 3)
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0; i < msg->len; ++i) {
|
||||
const unsigned addr = msg->buf[msg->len - 1 - i];
|
||||
|
||||
internal_address |= addr << (8 * i);
|
||||
int_addr_flag += TWI_MMR_IADRSZ_1;
|
||||
}
|
||||
|
||||
writel(internal_address, ®->iadr);
|
||||
}
|
||||
|
||||
is_read = m_start->flags & I2C_M_RD;
|
||||
|
||||
writel((m_start->addr << 16) | int_addr_flag |
|
||||
(is_read ? TWI_MMR_MREAD : 0), ®->mmr);
|
||||
|
||||
ret = at91_i2c_xfer_msg(bus, m_start);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate symmetric clock as stated in datasheet:
|
||||
* twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
|
||||
*/
|
||||
static void at91_calc_i2c_clock(struct udevice *dev, int i2c_clk)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
const struct at91_i2c_pdata *pdata = bus->pdata;
|
||||
int offset = pdata->clk_offset;
|
||||
int max_ckdiv = pdata->clk_max_div;
|
||||
int ckdiv, cdiv, div;
|
||||
unsigned long src_rate;
|
||||
|
||||
src_rate = bus->bus_clk_rate;
|
||||
|
||||
div = max(0, (int)DIV_ROUND_UP(src_rate, 2 * i2c_clk) - offset);
|
||||
ckdiv = fls(div >> 8);
|
||||
cdiv = div >> ckdiv;
|
||||
|
||||
if (ckdiv > max_ckdiv) {
|
||||
ckdiv = max_ckdiv;
|
||||
cdiv = 255;
|
||||
}
|
||||
|
||||
bus->speed = DIV_ROUND_UP(src_rate,
|
||||
(cdiv * (1 << ckdiv) + offset) * 2);
|
||||
|
||||
bus->cwgr_val = (ckdiv << 16) | (cdiv << 8) | cdiv;
|
||||
}
|
||||
|
||||
static int at91_i2c_enable_clk(struct udevice *dev)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
struct clk clk;
|
||||
ulong clk_rate;
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &clk);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
ret = clk_enable(&clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_rate = clk_get_rate(&clk);
|
||||
if (!clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
bus->bus_clk_rate = clk_rate;
|
||||
|
||||
clk_free(&clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
|
||||
at91_calc_i2c_clock(dev, speed);
|
||||
|
||||
writel(bus->cwgr_val, &bus->regs->cwgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at91_i2c_get_bus_speed(struct udevice *dev)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
|
||||
return bus->speed;
|
||||
}
|
||||
|
||||
static int at91_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
int node = dev_of_offset(dev);
|
||||
|
||||
bus->regs = (struct at91_i2c_regs *)devfdt_get_addr(dev);
|
||||
bus->pdata = (struct at91_i2c_pdata *)dev_get_driver_data(dev);
|
||||
bus->clock_frequency = fdtdec_get_int(blob, node,
|
||||
"clock-frequency", 100000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops at91_i2c_ops = {
|
||||
.xfer = at91_i2c_xfer,
|
||||
.set_bus_speed = at91_i2c_set_bus_speed,
|
||||
.get_bus_speed = at91_i2c_get_bus_speed,
|
||||
};
|
||||
|
||||
static int at91_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct at91_i2c_bus *bus = dev_get_priv(dev);
|
||||
struct at91_i2c_regs *reg = bus->regs;
|
||||
int ret;
|
||||
|
||||
ret = at91_i2c_enable_clk(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(TWI_CR_SWRST, ®->cr);
|
||||
|
||||
at91_calc_i2c_clock(dev, bus->clock_frequency);
|
||||
|
||||
writel(bus->cwgr_val, ®->cwgr);
|
||||
writel(TWI_CR_MSEN, ®->cr);
|
||||
writel(TWI_CR_SVDIS, ®->cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct at91_i2c_pdata at91rm9200_config = {
|
||||
.clk_max_div = 5,
|
||||
.clk_offset = 3,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata at91sam9261_config = {
|
||||
.clk_max_div = 5,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata at91sam9260_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata at91sam9g20_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata at91sam9g10_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata at91sam9x5_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata sama5d4_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 4,
|
||||
};
|
||||
|
||||
static const struct at91_i2c_pdata sama5d2_config = {
|
||||
.clk_max_div = 7,
|
||||
.clk_offset = 3,
|
||||
};
|
||||
|
||||
static const struct udevice_id at91_i2c_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-i2c", .data = (long)&at91rm9200_config },
|
||||
{ .compatible = "atmel,at91sam9260-i2c", .data = (long)&at91sam9260_config },
|
||||
{ .compatible = "atmel,at91sam9261-i2c", .data = (long)&at91sam9261_config },
|
||||
{ .compatible = "atmel,at91sam9g20-i2c", .data = (long)&at91sam9g20_config },
|
||||
{ .compatible = "atmel,at91sam9g10-i2c", .data = (long)&at91sam9g10_config },
|
||||
{ .compatible = "atmel,at91sam9x5-i2c", .data = (long)&at91sam9x5_config },
|
||||
{ .compatible = "atmel,sama5d4-i2c", .data = (long)&sama5d4_config },
|
||||
{ .compatible = "atmel,sama5d2-i2c", .data = (long)&sama5d2_config },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_at91) = {
|
||||
.name = "i2c_at91",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = at91_i2c_ids,
|
||||
.probe = at91_i2c_probe,
|
||||
.ofdata_to_platdata = at91_i2c_ofdata_to_platdata,
|
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.priv_auto_alloc_size = sizeof(struct at91_i2c_bus),
|
||||
.ops = &at91_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef _AT91_I2C_H
|
||||
#define _AT91_I2C_H
|
||||
|
||||
#define TWI_CR_START BIT(0) /* Send a Start Condition */
|
||||
#define TWI_CR_MSEN BIT(2) /* Master Transfer Enable */
|
||||
#define TWI_CR_STOP BIT(1) /* Send a Stop Condition */
|
||||
#define TWI_CR_SVDIS BIT(5) /* Slave Transfer Disable */
|
||||
#define TWI_CR_SWRST BIT(7) /* Software Reset */
|
||||
#define TWI_CR_ACMEN BIT(16) /* Alternative Command Mode Enable */
|
||||
#define TWI_CR_ACMDIS BIT(17) /* Alternative Command Mode Disable */
|
||||
#define TWI_CR_LOCKCLR BIT(26) /* Lock Clear */
|
||||
|
||||
#define TWI_MMR_MREAD BIT(12) /* Master Read Direction */
|
||||
#define TWI_MMR_IADRSZ_1 BIT(8) /* Internal Device Address Size */
|
||||
|
||||
#define TWI_SR_TXCOMP BIT(0) /* Transmission Complete */
|
||||
#define TWI_SR_RXRDY BIT(1) /* Receive Holding Register Ready */
|
||||
#define TWI_SR_TXRDY BIT(2) /* Transmit Holding Register Ready */
|
||||
#define TWI_SR_OVRE BIT(6) /* Overrun Error */
|
||||
#define TWI_SR_UNRE BIT(7) /* Underrun Error */
|
||||
#define TWI_SR_NACK BIT(8) /* Not Acknowledged */
|
||||
#define TWI_SR_LOCK BIT(23) /* TWI Lock due to Frame Errors */
|
||||
|
||||
#define TWI_ACR_DATAL(len) ((len) & 0xff)
|
||||
#define TWI_ACR_DIR_READ BIT(8)
|
||||
|
||||
#define TWI_CWGR_HOLD_MAX 0x1f
|
||||
#define TWI_CWGR_HOLD(x) (((x) & TWI_CWGR_HOLD_MAX) << 24)
|
||||
|
||||
struct at91_i2c_regs {
|
||||
u32 cr;
|
||||
u32 mmr;
|
||||
u32 smr;
|
||||
u32 iadr;
|
||||
u32 cwgr;
|
||||
u32 rev_0[3];
|
||||
u32 sr;
|
||||
u32 ier;
|
||||
u32 idr;
|
||||
u32 imr;
|
||||
u32 rhr;
|
||||
u32 thr;
|
||||
u32 smbtr;
|
||||
u32 rev_1;
|
||||
u32 acr;
|
||||
u32 filtr;
|
||||
u32 rev_2;
|
||||
u32 swmr;
|
||||
u32 fmr;
|
||||
u32 flr;
|
||||
u32 rev_3;
|
||||
u32 fsr;
|
||||
u32 fier;
|
||||
u32 fidr;
|
||||
u32 fimr;
|
||||
u32 rev_4[29];
|
||||
u32 wpmr;
|
||||
u32 wpsr;
|
||||
u32 rev_5[6];
|
||||
};
|
||||
|
||||
struct at91_i2c_pdata {
|
||||
unsigned clk_max_div;
|
||||
unsigned clk_offset;
|
||||
};
|
||||
|
||||
struct at91_i2c_bus {
|
||||
struct at91_i2c_regs *regs;
|
||||
u32 status;
|
||||
ulong bus_clk_rate;
|
||||
u32 clock_frequency;
|
||||
u32 speed;
|
||||
u32 cwgr_val;
|
||||
const struct at91_i2c_pdata *pdata;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#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,
|
||||
.ops = &cros_ec_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <cros_ec.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct cros_ec_i2c_bus {
|
||||
int remote_bus;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct cros_ec_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
return cros_ec_i2c_tunnel(dev->parent, i2c_bus->remote_bus, msg, nmsgs);
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct cros_ec_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev_of_offset(dev);
|
||||
|
||||
i2c_bus->remote_bus = fdtdec_get_uint(blob, node, "google,remote-bus",
|
||||
0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.ofdata_to_platdata = cros_ec_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct cros_ec_i2c_bus),
|
||||
.ops = &cros_ec_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,509 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* TI DaVinci (TMS320DM644x) I2C driver.
|
||||
*
|
||||
* (C) Copyright 2012-2014
|
||||
* Texas Instruments Incorporated, <www.ti.com>
|
||||
* (C) Copyright 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* NOTE: This driver should be converted to driver model before June 2017.
|
||||
* Please see doc/driver-model/i2c-howto.txt for instructions.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <dm.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/i2c_defs.h>
|
||||
#include <asm/io.h>
|
||||
#include "davinci_i2c.h"
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
/* Information about i2c controller */
|
||||
struct i2c_bus {
|
||||
int id;
|
||||
uint speed;
|
||||
struct i2c_regs *regs;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define CHECK_NACK() \
|
||||
do {\
|
||||
if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\
|
||||
REG(&(i2c_base->i2c_con)) = 0;\
|
||||
return 1;\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int _wait_for_bus(struct i2c_regs *i2c_base)
|
||||
{
|
||||
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_regs *i2c_base, int mask)
|
||||
{
|
||||
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_regs *i2c_base)
|
||||
{
|
||||
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_regs *i2c_base,
|
||||
uint speed)
|
||||
{
|
||||
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));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _davinci_i2c_init(struct i2c_regs *i2c_base,
|
||||
uint speed, int slaveadd)
|
||||
{
|
||||
if (REG(&(i2c_base->i2c_con)) & I2C_CON_EN) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
_davinci_i2c_setspeed(i2c_base, 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_read(struct i2c_regs *i2c_base, uint8_t chip,
|
||||
uint32_t addr, int alen, uint8_t *buf, int len)
|
||||
{
|
||||
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(i2c_base))
|
||||
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(i2c_base, 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(i2c_base,
|
||||
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(i2c_base, 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(i2c_base, 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(i2c_base, I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_flush_rx(i2c_base);
|
||||
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_regs *i2c_base, uint8_t chip,
|
||||
uint32_t addr, int alen, uint8_t *buf, int len)
|
||||
{
|
||||
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(i2c_base))
|
||||
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(i2c_base, 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(i2c_base, 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(i2c_base, 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(i2c_base, I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_flush_rx(i2c_base);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _davinci_i2c_probe_chip(struct i2c_regs *i2c_base, uint8_t chip)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
if (chip == REG(&(i2c_base->i2c_oa)))
|
||||
return rc;
|
||||
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
if (_wait_for_bus(i2c_base))
|
||||
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(i2c_base);
|
||||
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(i2c_base))
|
||||
return 1;
|
||||
}
|
||||
|
||||
_flush_rx(i2c_base);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 3
|
||||
case 2:
|
||||
return (struct i2c_regs *)I2C2_BASE;
|
||||
#endif
|
||||
#if CONFIG_SYS_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;
|
||||
}
|
||||
|
||||
static uint davinci_i2c_setspeed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
uint ret;
|
||||
|
||||
adap->speed = speed;
|
||||
ret = _davinci_i2c_setspeed(i2c_base, speed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void davinci_i2c_init(struct i2c_adapter *adap, int speed,
|
||||
int slaveadd)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
|
||||
adap->speed = speed;
|
||||
_davinci_i2c_init(i2c_base, speed, slaveadd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
return _davinci_i2c_read(i2c_base, chip, addr, alen, buf, len);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return _davinci_i2c_write(i2c_base, chip, addr, alen, buf, len);
|
||||
}
|
||||
|
||||
static int davinci_i2c_probe_chip(struct i2c_adapter *adap, uint8_t chip)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
|
||||
return _davinci_i2c_probe_chip(i2c_base, chip);
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_0, davinci_i2c_init, davinci_i2c_probe_chip,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE,
|
||||
0)
|
||||
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_1, davinci_i2c_init, davinci_i2c_probe_chip,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED1,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE1,
|
||||
1)
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_2, davinci_i2c_init, davinci_i2c_probe_chip,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED2,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE2,
|
||||
2)
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_DM_I2C */
|
||||
|
||||
static int davinci_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++) {
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = _davinci_i2c_read(i2c_bus->regs, msg->addr,
|
||||
0, 0, msg->buf, msg->len);
|
||||
} else {
|
||||
ret = _davinci_i2c_write(i2c_bus->regs, msg->addr,
|
||||
0, 0, msg->buf, msg->len);
|
||||
}
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_i2c_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->speed = speed;
|
||||
return _davinci_i2c_setspeed(i2c_bus->regs, speed);
|
||||
}
|
||||
|
||||
static int davinci_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->id = dev->seq;
|
||||
i2c_bus->regs = (struct i2c_regs *)devfdt_get_addr(dev);
|
||||
|
||||
i2c_bus->speed = 100000;
|
||||
_davinci_i2c_init(i2c_bus->regs, i2c_bus->speed, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
|
||||
return _davinci_i2c_probe_chip(i2c_bus->regs, chip_addr);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops davinci_i2c_ops = {
|
||||
.xfer = davinci_i2c_xfer,
|
||||
.probe_chip = davinci_i2c_probe_chip,
|
||||
.set_bus_speed = davinci_i2c_set_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id davinci_i2c_ids[] = {
|
||||
{ .compatible = "ti,davinci-i2c"},
|
||||
{ .compatible = "ti,keystone-i2c"},
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_davinci) = {
|
||||
.name = "i2c_davinci",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = davinci_i2c_ids,
|
||||
.probe = davinci_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_bus),
|
||||
.ops = &davinci_i2c_ops,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2004-2014
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Some changes copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
*/
|
||||
#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
|
||||
@@ -0,0 +1,683 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
|
||||
*/
|
||||
|
||||
#include <clk.h>
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <pci.h>
|
||||
#include <reset.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;
|
||||
struct reset_ctl_bulk resets;
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
struct clk clk;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
|
||||
static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
|
||||
{
|
||||
u32 ena = enable ? IC_ENABLE_0B : 0;
|
||||
|
||||
writel(ena, &i2c_base->ic_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int 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 0;
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
#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 bus_mhz)
|
||||
{
|
||||
unsigned int cntl;
|
||||
unsigned int hcnt, lcnt;
|
||||
unsigned int ena;
|
||||
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;
|
||||
|
||||
/* Get enable setting for restore later */
|
||||
ena = readl(&i2c_base->ic_enable) & IC_ENABLE_0B;
|
||||
|
||||
/* 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 = (bus_mhz * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (bus_mhz * 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 = (bus_mhz * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (bus_mhz * 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 = (bus_mhz * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (bus_mhz * 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);
|
||||
|
||||
/* Restore back i2c now speed set */
|
||||
if (ena == IC_ENABLE_0B)
|
||||
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;
|
||||
unsigned int active = 0;
|
||||
|
||||
#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 (!active) {
|
||||
/*
|
||||
* Avoid writing to ic_cmd_data multiple times
|
||||
* in case this loop spins too quickly and the
|
||||
* ic_status RFNE bit isn't set after the first
|
||||
* write. Subsequent writes to ic_cmd_data can
|
||||
* trigger spurious i2c transfer.
|
||||
*/
|
||||
if (len == 1)
|
||||
writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
|
||||
else
|
||||
writel(IC_CMD, &i2c_base->ic_cmd_data);
|
||||
active = 1;
|
||||
}
|
||||
|
||||
if (readl(&i2c_base->ic_status) & IC_STATUS_RFNE) {
|
||||
*buffer++ = (uchar)readl(&i2c_base->ic_cmd_data);
|
||||
len--;
|
||||
start_time_rx = get_timer(0);
|
||||
active = 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 int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable i2c */
|
||||
ret = dw_i2c_enable(i2c_base, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(IC_CON_SD | IC_CON_RE | 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, IC_CLK);
|
||||
writel(slaveaddr, &i2c_base->ic_sar);
|
||||
#endif
|
||||
|
||||
/* Enable i2c */
|
||||
ret = dw_i2c_enable(i2c_base, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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, IC_CLK);
|
||||
}
|
||||
|
||||
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);
|
||||
ulong rate;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
rate = clk_get_rate(&i2c->clk);
|
||||
if (IS_ERR_VALUE(rate))
|
||||
return -EINVAL;
|
||||
|
||||
/* Convert to MHz */
|
||||
rate /= 1000000;
|
||||
#else
|
||||
rate = IC_CLK;
|
||||
#endif
|
||||
return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed,
|
||||
rate);
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
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 *)devfdt_get_addr_ptr(bus);
|
||||
}
|
||||
|
||||
ret = reset_get_bulk(bus, &priv->resets);
|
||||
if (ret)
|
||||
dev_warn(bus, "Can't get reset: %d\n", ret);
|
||||
else
|
||||
reset_deassert_bulk(&priv->resets);
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_get_by_index(bus, 0, &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
|
||||
clk_free(&priv->clk);
|
||||
dev_err(bus, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return __dw_i2c_init(priv->regs, 0, 0);
|
||||
}
|
||||
|
||||
static int designware_i2c_remove(struct udevice *dev)
|
||||
{
|
||||
struct dw_i2c *priv = dev_get_priv(dev);
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
clk_disable(&priv->clk);
|
||||
clk_free(&priv->clk);
|
||||
#endif
|
||||
|
||||
return reset_release_bulk(&priv->resets);
|
||||
}
|
||||
|
||||
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),
|
||||
.remove = designware_i2c_remove,
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
.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 */
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,560 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2016, Google Inc
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* David Mueller, ELSOFT AG, d.mueller@elsoft.ch
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include "s3c24x0_i2c.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* HSI2C-specific register description */
|
||||
|
||||
/* I2C_CTL Register bits */
|
||||
#define HSI2C_FUNC_MODE_I2C (1u << 0)
|
||||
#define HSI2C_MASTER (1u << 3)
|
||||
#define HSI2C_RXCHON (1u << 6) /* Write/Send */
|
||||
#define HSI2C_TXCHON (1u << 7) /* Read/Receive */
|
||||
#define HSI2C_SW_RST (1u << 31)
|
||||
|
||||
/* I2C_FIFO_CTL Register bits */
|
||||
#define HSI2C_RXFIFO_EN (1u << 0)
|
||||
#define HSI2C_TXFIFO_EN (1u << 1)
|
||||
#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16)
|
||||
#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4)
|
||||
|
||||
/* I2C_TRAILING_CTL Register bits */
|
||||
#define HSI2C_TRAILING_COUNT (0xff)
|
||||
|
||||
/* I2C_INT_EN Register bits */
|
||||
#define HSI2C_TX_UNDERRUN_EN (1u << 2)
|
||||
#define HSI2C_TX_OVERRUN_EN (1u << 3)
|
||||
#define HSI2C_RX_UNDERRUN_EN (1u << 4)
|
||||
#define HSI2C_RX_OVERRUN_EN (1u << 5)
|
||||
#define HSI2C_INT_TRAILING_EN (1u << 6)
|
||||
#define HSI2C_INT_I2C_EN (1u << 9)
|
||||
|
||||
#define HSI2C_INT_ERROR_MASK (HSI2C_TX_UNDERRUN_EN |\
|
||||
HSI2C_TX_OVERRUN_EN |\
|
||||
HSI2C_RX_UNDERRUN_EN |\
|
||||
HSI2C_RX_OVERRUN_EN |\
|
||||
HSI2C_INT_TRAILING_EN)
|
||||
|
||||
/* I2C_CONF Register bits */
|
||||
#define HSI2C_AUTO_MODE (1u << 31)
|
||||
#define HSI2C_10BIT_ADDR_MODE (1u << 30)
|
||||
#define HSI2C_HS_MODE (1u << 29)
|
||||
|
||||
/* I2C_AUTO_CONF Register bits */
|
||||
#define HSI2C_READ_WRITE (1u << 16)
|
||||
#define HSI2C_STOP_AFTER_TRANS (1u << 17)
|
||||
#define HSI2C_MASTER_RUN (1u << 31)
|
||||
|
||||
/* I2C_TIMEOUT Register bits */
|
||||
#define HSI2C_TIMEOUT_EN (1u << 31)
|
||||
|
||||
/* I2C_TRANS_STATUS register bits */
|
||||
#define HSI2C_MASTER_BUSY (1u << 17)
|
||||
#define HSI2C_SLAVE_BUSY (1u << 16)
|
||||
#define HSI2C_TIMEOUT_AUTO (1u << 4)
|
||||
#define HSI2C_NO_DEV (1u << 3)
|
||||
#define HSI2C_NO_DEV_ACK (1u << 2)
|
||||
#define HSI2C_TRANS_ABORT (1u << 1)
|
||||
#define HSI2C_TRANS_SUCCESS (1u << 0)
|
||||
#define HSI2C_TRANS_ERROR_MASK (HSI2C_TIMEOUT_AUTO |\
|
||||
HSI2C_NO_DEV | HSI2C_NO_DEV_ACK |\
|
||||
HSI2C_TRANS_ABORT)
|
||||
#define HSI2C_TRANS_FINISHED_MASK (HSI2C_TRANS_ERROR_MASK | HSI2C_TRANS_SUCCESS)
|
||||
|
||||
|
||||
/* I2C_FIFO_STAT Register bits */
|
||||
#define HSI2C_RX_FIFO_EMPTY (1u << 24)
|
||||
#define HSI2C_RX_FIFO_FULL (1u << 23)
|
||||
#define HSI2C_TX_FIFO_EMPTY (1u << 8)
|
||||
#define HSI2C_TX_FIFO_FULL (1u << 7)
|
||||
#define HSI2C_RX_FIFO_LEVEL(x) (((x) >> 16) & 0x7f)
|
||||
#define HSI2C_TX_FIFO_LEVEL(x) ((x) & 0x7f)
|
||||
|
||||
#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10)
|
||||
|
||||
#define HSI2C_TIMEOUT_US 10000 /* 10 ms, finer granularity */
|
||||
|
||||
/*
|
||||
* Wait for transfer completion.
|
||||
*
|
||||
* This function reads the interrupt status register waiting for the INT_I2C
|
||||
* bit to be set, which indicates copletion of a transaction.
|
||||
*
|
||||
* @param i2c: pointer to the appropriate register bank
|
||||
*
|
||||
* @return: I2C_OK in case of successful completion, I2C_NOK_TIMEOUT in case
|
||||
* the status bits do not get set in time, or an approrpiate error
|
||||
* value in case of transfer errors.
|
||||
*/
|
||||
static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c)
|
||||
{
|
||||
int i = HSI2C_TIMEOUT_US;
|
||||
|
||||
while (i-- > 0) {
|
||||
u32 int_status = readl(&i2c->usi_int_stat);
|
||||
|
||||
if (int_status & HSI2C_INT_I2C_EN) {
|
||||
u32 trans_status = readl(&i2c->usi_trans_status);
|
||||
|
||||
/* Deassert pending interrupt. */
|
||||
writel(int_status, &i2c->usi_int_stat);
|
||||
|
||||
if (trans_status & HSI2C_NO_DEV_ACK) {
|
||||
debug("%s: no ACK from device\n", __func__);
|
||||
return I2C_NACK;
|
||||
}
|
||||
if (trans_status & HSI2C_NO_DEV) {
|
||||
debug("%s: no device\n", __func__);
|
||||
return I2C_NOK;
|
||||
}
|
||||
if (trans_status & HSI2C_TRANS_ABORT) {
|
||||
debug("%s: arbitration lost\n", __func__);
|
||||
return I2C_NOK_LA;
|
||||
}
|
||||
if (trans_status & HSI2C_TIMEOUT_AUTO) {
|
||||
debug("%s: device timed out\n", __func__);
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
return I2C_OK;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
debug("%s: transaction timeout!\n", __func__);
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
|
||||
static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus)
|
||||
{
|
||||
struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
|
||||
ulong clkin;
|
||||
unsigned int op_clk = i2c_bus->clock_frequency;
|
||||
unsigned int i = 0, utemp0 = 0, utemp1 = 0;
|
||||
unsigned int t_ftl_cycle;
|
||||
|
||||
#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
|
||||
clkin = get_i2c_clk();
|
||||
#else
|
||||
clkin = get_PCLK();
|
||||
#endif
|
||||
/* FPCLK / FI2C =
|
||||
* (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
|
||||
* uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
|
||||
* uTemp1 = (TSCLK_L + TSCLK_H + 2)
|
||||
* uTemp2 = TSCLK_L + TSCLK_H
|
||||
*/
|
||||
t_ftl_cycle = (readl(&hsregs->usi_conf) >> 16) & 0x7;
|
||||
utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
|
||||
|
||||
/* CLK_DIV max is 256 */
|
||||
for (i = 0; i < 256; i++) {
|
||||
utemp1 = utemp0 / (i + 1);
|
||||
if ((utemp1 < 512) && (utemp1 > 4)) {
|
||||
i2c_bus->clk_cycle = utemp1 - 2;
|
||||
i2c_bus->clk_div = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus)
|
||||
{
|
||||
struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
|
||||
unsigned int t_sr_release;
|
||||
unsigned int n_clkdiv;
|
||||
unsigned int t_start_su, t_start_hd;
|
||||
unsigned int t_stop_su;
|
||||
unsigned int t_data_su, t_data_hd;
|
||||
unsigned int t_scl_l, t_scl_h;
|
||||
u32 i2c_timing_s1;
|
||||
u32 i2c_timing_s2;
|
||||
u32 i2c_timing_s3;
|
||||
u32 i2c_timing_sla;
|
||||
|
||||
n_clkdiv = i2c_bus->clk_div;
|
||||
t_scl_l = i2c_bus->clk_cycle / 2;
|
||||
t_scl_h = i2c_bus->clk_cycle / 2;
|
||||
t_start_su = t_scl_l;
|
||||
t_start_hd = t_scl_l;
|
||||
t_stop_su = t_scl_l;
|
||||
t_data_su = t_scl_l / 2;
|
||||
t_data_hd = t_scl_l / 2;
|
||||
t_sr_release = i2c_bus->clk_cycle;
|
||||
|
||||
i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8;
|
||||
i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
|
||||
i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0;
|
||||
i2c_timing_sla = t_data_hd << 0;
|
||||
|
||||
writel(HSI2C_TRAILING_COUNT, &hsregs->usi_trailing_ctl);
|
||||
|
||||
/* Clear to enable Timeout */
|
||||
clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0);
|
||||
|
||||
/* set AUTO mode */
|
||||
writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf);
|
||||
|
||||
/* Enable completion conditions' reporting. */
|
||||
writel(HSI2C_INT_I2C_EN, &hsregs->usi_int_en);
|
||||
|
||||
/* Enable FIFOs */
|
||||
writel(HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN, &hsregs->usi_fifo_ctl);
|
||||
|
||||
/* Currently operating in Fast speed mode. */
|
||||
writel(i2c_timing_s1, &hsregs->usi_timing_fs1);
|
||||
writel(i2c_timing_s2, &hsregs->usi_timing_fs2);
|
||||
writel(i2c_timing_s3, &hsregs->usi_timing_fs3);
|
||||
writel(i2c_timing_sla, &hsregs->usi_timing_sla);
|
||||
}
|
||||
|
||||
/* SW reset for the high speed bus */
|
||||
static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus)
|
||||
{
|
||||
struct exynos5_hsi2c *i2c = i2c_bus->hsregs;
|
||||
u32 i2c_ctl;
|
||||
|
||||
/* Set and clear the bit for reset */
|
||||
i2c_ctl = readl(&i2c->usi_ctl);
|
||||
i2c_ctl |= HSI2C_SW_RST;
|
||||
writel(i2c_ctl, &i2c->usi_ctl);
|
||||
|
||||
i2c_ctl = readl(&i2c->usi_ctl);
|
||||
i2c_ctl &= ~HSI2C_SW_RST;
|
||||
writel(i2c_ctl, &i2c->usi_ctl);
|
||||
|
||||
/* Initialize the configure registers */
|
||||
hsi2c_ch_init(i2c_bus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the appropriate bit of the fifo status register until the interface is
|
||||
* ready to process the next byte or timeout expires.
|
||||
*
|
||||
* In addition to the FIFO status register this function also polls the
|
||||
* interrupt status register to be able to detect unexpected transaction
|
||||
* completion.
|
||||
*
|
||||
* When FIFO is ready to process the next byte, this function returns I2C_OK.
|
||||
* If in course of polling the INT_I2C assertion is detected, the function
|
||||
* returns I2C_NOK. If timeout happens before any of the above conditions is
|
||||
* met - the function returns I2C_NOK_TOUT;
|
||||
|
||||
* @param i2c: pointer to the appropriate i2c register bank.
|
||||
* @param rx_transfer: set to True if the receive transaction is in progress.
|
||||
* @return: as described above.
|
||||
*/
|
||||
static unsigned hsi2c_poll_fifo(struct exynos5_hsi2c *i2c, bool rx_transfer)
|
||||
{
|
||||
u32 fifo_bit = rx_transfer ? HSI2C_RX_FIFO_EMPTY : HSI2C_TX_FIFO_FULL;
|
||||
int i = HSI2C_TIMEOUT_US;
|
||||
|
||||
while (readl(&i2c->usi_fifo_stat) & fifo_bit) {
|
||||
if (readl(&i2c->usi_int_stat) & HSI2C_INT_I2C_EN) {
|
||||
/*
|
||||
* There is a chance that assertion of
|
||||
* HSI2C_INT_I2C_EN and deassertion of
|
||||
* HSI2C_RX_FIFO_EMPTY happen simultaneously. Let's
|
||||
* give FIFO status priority and check it one more
|
||||
* time before reporting interrupt. The interrupt will
|
||||
* be reported next time this function is called.
|
||||
*/
|
||||
if (rx_transfer &&
|
||||
!(readl(&i2c->usi_fifo_stat) & fifo_bit))
|
||||
break;
|
||||
return I2C_NOK;
|
||||
}
|
||||
if (!i--) {
|
||||
debug("%s: FIFO polling timeout!\n", __func__);
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
return I2C_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Preapre hsi2c transaction, either read or write.
|
||||
*
|
||||
* Set up transfer as described in section 27.5.1.2 'I2C Channel Auto Mode' of
|
||||
* the 5420 UM.
|
||||
*
|
||||
* @param i2c: pointer to the appropriate i2c register bank.
|
||||
* @param chip: slave address on the i2c bus (with read/write bit exlcuded)
|
||||
* @param len: number of bytes expected to be sent or received
|
||||
* @param rx_transfer: set to true for receive transactions
|
||||
* @param: issue_stop: set to true if i2c stop condition should be generated
|
||||
* after this transaction.
|
||||
* @return: I2C_NOK_TOUT in case the bus remained busy for HSI2C_TIMEOUT_US,
|
||||
* I2C_OK otherwise.
|
||||
*/
|
||||
static int hsi2c_prepare_transaction(struct exynos5_hsi2c *i2c,
|
||||
u8 chip,
|
||||
u16 len,
|
||||
bool rx_transfer,
|
||||
bool issue_stop)
|
||||
{
|
||||
u32 conf;
|
||||
|
||||
conf = len | HSI2C_MASTER_RUN;
|
||||
|
||||
if (issue_stop)
|
||||
conf |= HSI2C_STOP_AFTER_TRANS;
|
||||
|
||||
/* Clear to enable Timeout */
|
||||
writel(readl(&i2c->usi_timeout) & ~HSI2C_TIMEOUT_EN, &i2c->usi_timeout);
|
||||
|
||||
/* Set slave address */
|
||||
writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr);
|
||||
|
||||
if (rx_transfer) {
|
||||
/* i2c master, read transaction */
|
||||
writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
|
||||
&i2c->usi_ctl);
|
||||
|
||||
/* read up to len bytes, stop after transaction is finished */
|
||||
writel(conf | HSI2C_READ_WRITE, &i2c->usi_auto_conf);
|
||||
} else {
|
||||
/* i2c master, write transaction */
|
||||
writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
|
||||
&i2c->usi_ctl);
|
||||
|
||||
/* write up to len bytes, stop after transaction is finished */
|
||||
writel(conf, &i2c->usi_auto_conf);
|
||||
}
|
||||
|
||||
/* Reset all pending interrupt status bits we care about, if any */
|
||||
writel(HSI2C_INT_I2C_EN, &i2c->usi_int_stat);
|
||||
|
||||
return I2C_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait while i2c bus is settling down (mostly stop gets completed).
|
||||
*/
|
||||
static int hsi2c_wait_while_busy(struct exynos5_hsi2c *i2c)
|
||||
{
|
||||
int i = HSI2C_TIMEOUT_US;
|
||||
|
||||
while (readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY) {
|
||||
if (!i--) {
|
||||
debug("%s: bus busy\n", __func__);
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
return I2C_OK;
|
||||
}
|
||||
|
||||
static int hsi2c_write(struct exynos5_hsi2c *i2c,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char alen,
|
||||
unsigned char data[],
|
||||
unsigned short len,
|
||||
bool issue_stop)
|
||||
{
|
||||
int i, rv = 0;
|
||||
|
||||
if (!(len + alen)) {
|
||||
/* Writes of zero length not supported in auto mode. */
|
||||
debug("%s: zero length writes not supported\n", __func__);
|
||||
return I2C_NOK;
|
||||
}
|
||||
|
||||
rv = hsi2c_prepare_transaction
|
||||
(i2c, chip, len + alen, false, issue_stop);
|
||||
if (rv != I2C_OK)
|
||||
return rv;
|
||||
|
||||
/* Move address, if any, and the data, if any, into the FIFO. */
|
||||
for (i = 0; i < alen; i++) {
|
||||
rv = hsi2c_poll_fifo(i2c, false);
|
||||
if (rv != I2C_OK) {
|
||||
debug("%s: address write failed\n", __func__);
|
||||
goto write_error;
|
||||
}
|
||||
writel(addr[i], &i2c->usi_txdata);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
rv = hsi2c_poll_fifo(i2c, false);
|
||||
if (rv != I2C_OK) {
|
||||
debug("%s: data write failed\n", __func__);
|
||||
goto write_error;
|
||||
}
|
||||
writel(data[i], &i2c->usi_txdata);
|
||||
}
|
||||
|
||||
rv = hsi2c_wait_for_trx(i2c);
|
||||
|
||||
write_error:
|
||||
if (issue_stop) {
|
||||
int tmp_ret = hsi2c_wait_while_busy(i2c);
|
||||
if (rv == I2C_OK)
|
||||
rv = tmp_ret;
|
||||
}
|
||||
|
||||
writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int hsi2c_read(struct exynos5_hsi2c *i2c,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char alen,
|
||||
unsigned char data[],
|
||||
unsigned short len)
|
||||
{
|
||||
int i, rv, tmp_ret;
|
||||
bool drop_data = false;
|
||||
|
||||
if (!len) {
|
||||
/* Reads of zero length not supported in auto mode. */
|
||||
debug("%s: zero length read adjusted\n", __func__);
|
||||
drop_data = true;
|
||||
len = 1;
|
||||
}
|
||||
|
||||
if (alen) {
|
||||
/* Internal register adress needs to be written first. */
|
||||
rv = hsi2c_write(i2c, chip, addr, alen, NULL, 0, false);
|
||||
if (rv != I2C_OK)
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = hsi2c_prepare_transaction(i2c, chip, len, true, true);
|
||||
|
||||
if (rv != I2C_OK)
|
||||
return rv;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
rv = hsi2c_poll_fifo(i2c, true);
|
||||
if (rv != I2C_OK)
|
||||
goto read_err;
|
||||
if (drop_data)
|
||||
continue;
|
||||
data[i] = readl(&i2c->usi_rxdata);
|
||||
}
|
||||
|
||||
rv = hsi2c_wait_for_trx(i2c);
|
||||
|
||||
read_err:
|
||||
tmp_ret = hsi2c_wait_while_busy(i2c);
|
||||
if (rv == I2C_OK)
|
||||
rv = tmp_ret;
|
||||
|
||||
writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int exynos_hs_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
|
||||
int ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = hsi2c_read(hsregs, msg->addr, 0, 0, msg->buf,
|
||||
msg->len);
|
||||
} else {
|
||||
ret = hsi2c_write(hsregs, msg->addr, 0, 0, msg->buf,
|
||||
msg->len, true);
|
||||
}
|
||||
if (ret) {
|
||||
exynos5_i2c_reset(i2c_bus);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->clock_frequency = speed;
|
||||
|
||||
if (hsi2c_get_clk_details(i2c_bus))
|
||||
return -EFAULT;
|
||||
hsi2c_ch_init(i2c_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
uchar buf[1];
|
||||
int ret;
|
||||
|
||||
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).
|
||||
*/
|
||||
ret = hsi2c_read(i2c_bus->hsregs, chip, 0, 0, buf, 1);
|
||||
|
||||
return ret != I2C_OK;
|
||||
}
|
||||
|
||||
static int s3c_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
int node;
|
||||
|
||||
node = dev_of_offset(dev);
|
||||
|
||||
i2c_bus->hsregs = (struct exynos5_hsi2c *)devfdt_get_addr(dev);
|
||||
|
||||
i2c_bus->id = pinmux_decode_periph_id(blob, node);
|
||||
|
||||
i2c_bus->clock_frequency = fdtdec_get_int(blob, node,
|
||||
"clock-frequency", 100000);
|
||||
i2c_bus->node = node;
|
||||
i2c_bus->bus_num = dev->seq;
|
||||
|
||||
exynos_pinmux_config(i2c_bus->id, PINMUX_FLAG_HS_MODE);
|
||||
|
||||
i2c_bus->active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops exynos_hs_i2c_ops = {
|
||||
.xfer = exynos_hs_i2c_xfer,
|
||||
.probe_chip = s3c24x0_i2c_probe,
|
||||
.set_bus_speed = s3c24x0_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos_hs_i2c_ids[] = {
|
||||
{ .compatible = "samsung,exynos5-hsi2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(hs_i2c) = {
|
||||
.name = "i2c_s3c_hs",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = exynos_hs_i2c_ids,
|
||||
.ofdata_to_platdata = s3c_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus),
|
||||
.ops = &exynos_hs_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,654 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2006,2009 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 2012, Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
* Changes for multibus/multiadapter I2C support.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <i2c.h> /* Functional interface */
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/fsl_i2c.h> /* HW definitions */
|
||||
#include <clk.h>
|
||||
#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 */
|
||||
|
||||
#ifdef __M68K__
|
||||
/*
|
||||
* 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[] = {
|
||||
{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 uint set_i2c_bus_speed(const struct fsl_i2c_base *base,
|
||||
uint i2c_clk, uint speed)
|
||||
{
|
||||
ushort divider = min(i2c_clk / speed, (uint)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 */
|
||||
ushort a, b, ga, gb;
|
||||
ulong 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) {
|
||||
ushort 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, ", fdr);
|
||||
debug("div: %ld, ", est_div);
|
||||
debug("ga: 0x%x, gb: 0x%x, ", ga, gb);
|
||||
debug("a: %d, b: %d, speed: %d\n", 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
|
||||
uint 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 uint 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;
|
||||
uint flags = 0;
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447
|
||||
uint 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 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("%s: MAL\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(csr & I2C_SR_MCF)) {
|
||||
debug("%s: unfinished\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) {
|
||||
debug("%s: No RXACK\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while ((get_ticks() - timeval) < timeout);
|
||||
|
||||
debug("%s: timed out\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static 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 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 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 unknown 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 uint __i2c_set_bus_speed(const struct fsl_i2c_base *base,
|
||||
uint 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 uint fsl_i2c_set_bus_speed(struct i2c_adapter *adap, uint 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, uint 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);
|
||||
struct clk clock;
|
||||
|
||||
dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct fsl_i2c_base));
|
||||
|
||||
if (!dev->base)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->index = dev_read_u32_default(bus, "cell-index", -1);
|
||||
dev->slaveadd = dev_read_u32_default(bus, "u-boot,i2c-slave-addr",
|
||||
0x7f);
|
||||
dev->speed = dev_read_u32_default(bus, "clock-frequency", 400000);
|
||||
|
||||
if (!clk_get_by_index(bus, 0, &clock))
|
||||
dev->i2c_clk = clk_get_rate(&clock);
|
||||
else
|
||||
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
|
||||
* actual 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 */
|
||||
@@ -0,0 +1,506 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <dm/root.h>
|
||||
#include <i2c.h>
|
||||
#include <fdtdec.h>
|
||||
#include <mapmem.h>
|
||||
#include <wait_bit.h>
|
||||
#include <clk.h>
|
||||
|
||||
/* 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_INTERRUPTS_MASK (CDNS_I2C_INTERRUPT_COMP | \
|
||||
CDNS_I2C_INTERRUPT_DATA | \
|
||||
CDNS_I2C_INTERRUPT_NACK | \
|
||||
CDNS_I2C_INTERRUPT_TO | \
|
||||
CDNS_I2C_INTERRUPT_SLVRDY | \
|
||||
CDNS_I2C_INTERRUPT_RXOVF | \
|
||||
CDNS_I2C_INTERRUPT_TXOVF | \
|
||||
CDNS_I2C_INTERRUPT_RXUNF | \
|
||||
CDNS_I2C_INTERRUPT_ARBLOST)
|
||||
|
||||
#define CDNS_I2C_FIFO_DEPTH 16
|
||||
#define CDNS_I2C_TRANSFER_SIZE_MAX 255 /* Controller transfer limit */
|
||||
#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_TRANSFER_SIZE_MAX - 3)
|
||||
|
||||
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
|
||||
|
||||
#define CDNS_I2C_ARB_LOST_MAX_RETRIES 10
|
||||
|
||||
#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 */
|
||||
|
||||
int hold_flag;
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
struct cdns_i2c_platform_data {
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
/* 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++) {
|
||||
int_status = readl(&cdns_i2c->interrupt_status);
|
||||
if (int_status & mask)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static inline u32 is_arbitration_lost(struct cdns_i2c_regs *regs)
|
||||
{
|
||||
return (readl(®s->interrupt_status) & CDNS_I2C_INTERRUPT_ARBLOST);
|
||||
}
|
||||
|
||||
static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 len)
|
||||
{
|
||||
u8 *cur_data = data;
|
||||
struct cdns_i2c_regs *regs = i2c_bus->regs;
|
||||
u32 ret;
|
||||
|
||||
/* Set the controller in Master transmit mode and clear FIFO */
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO);
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_RW);
|
||||
|
||||
/* Check message size against FIFO depth, and set hold bus bit
|
||||
* if it is greater than FIFO depth
|
||||
*/
|
||||
if (len > CDNS_I2C_FIFO_DEPTH)
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
/* Clear the interrupts in status register */
|
||||
writel(CDNS_I2C_INTERRUPTS_MASK, ®s->interrupt_status);
|
||||
|
||||
writel(addr, ®s->address);
|
||||
|
||||
while (len-- && !is_arbitration_lost(regs)) {
|
||||
writel(*(cur_data++), ®s->data);
|
||||
if (len && readl(®s->transfer_size) == CDNS_I2C_FIFO_DEPTH) {
|
||||
ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
|
||||
CDNS_I2C_INTERRUPT_ARBLOST);
|
||||
if (ret & CDNS_I2C_INTERRUPT_ARBLOST)
|
||||
return -EAGAIN;
|
||||
if (ret & CDNS_I2C_INTERRUPT_COMP)
|
||||
continue;
|
||||
/* Release the bus */
|
||||
clrbits_le32(®s->control,
|
||||
CDNS_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (len && is_arbitration_lost(regs))
|
||||
return -EAGAIN;
|
||||
|
||||
/* All done... release the bus */
|
||||
if (!i2c_bus->hold_flag)
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
/* Wait for the address and data to be sent */
|
||||
ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
|
||||
CDNS_I2C_INTERRUPT_ARBLOST);
|
||||
if (!(ret & (CDNS_I2C_INTERRUPT_ARBLOST |
|
||||
CDNS_I2C_INTERRUPT_COMP)))
|
||||
return -ETIMEDOUT;
|
||||
if (ret & CDNS_I2C_INTERRUPT_ARBLOST)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool cdns_is_hold_quirk(int hold_quirk, int curr_recv_count)
|
||||
{
|
||||
return hold_quirk && (curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1);
|
||||
}
|
||||
|
||||
static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 recv_count)
|
||||
{
|
||||
u8 *cur_data = data;
|
||||
struct cdns_i2c_regs *regs = i2c_bus->regs;
|
||||
u32 curr_recv_count;
|
||||
int updatetx, hold_quirk;
|
||||
u32 ret;
|
||||
|
||||
curr_recv_count = recv_count;
|
||||
|
||||
/* Check for the message size against the FIFO depth */
|
||||
if (recv_count > CDNS_I2C_FIFO_DEPTH)
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
|
||||
CDNS_I2C_CONTROL_RW);
|
||||
|
||||
if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
|
||||
curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
|
||||
writel(curr_recv_count, ®s->transfer_size);
|
||||
} else {
|
||||
writel(recv_count, ®s->transfer_size);
|
||||
}
|
||||
|
||||
/* Start reading data */
|
||||
writel(addr, ®s->address);
|
||||
|
||||
updatetx = recv_count > curr_recv_count;
|
||||
|
||||
hold_quirk = (i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
|
||||
|
||||
while (recv_count && !is_arbitration_lost(regs)) {
|
||||
while (readl(®s->status) & CDNS_I2C_STATUS_RXDV) {
|
||||
if (recv_count < CDNS_I2C_FIFO_DEPTH &&
|
||||
!i2c_bus->hold_flag) {
|
||||
clrbits_le32(®s->control,
|
||||
CDNS_I2C_CONTROL_HOLD);
|
||||
}
|
||||
*(cur_data)++ = readl(®s->data);
|
||||
recv_count--;
|
||||
curr_recv_count--;
|
||||
|
||||
if (cdns_is_hold_quirk(hold_quirk, curr_recv_count))
|
||||
break;
|
||||
}
|
||||
|
||||
if (cdns_is_hold_quirk(hold_quirk, curr_recv_count)) {
|
||||
/* wait while fifo is full */
|
||||
while (readl(®s->transfer_size) !=
|
||||
(curr_recv_count - CDNS_I2C_FIFO_DEPTH))
|
||||
;
|
||||
/*
|
||||
* Check number of bytes to be received against maximum
|
||||
* transfer size and update register accordingly.
|
||||
*/
|
||||
if ((recv_count - CDNS_I2C_FIFO_DEPTH) >
|
||||
CDNS_I2C_TRANSFER_SIZE) {
|
||||
writel(CDNS_I2C_TRANSFER_SIZE,
|
||||
®s->transfer_size);
|
||||
curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
|
||||
CDNS_I2C_FIFO_DEPTH;
|
||||
} else {
|
||||
writel(recv_count - CDNS_I2C_FIFO_DEPTH,
|
||||
®s->transfer_size);
|
||||
curr_recv_count = recv_count;
|
||||
}
|
||||
} else if (recv_count && !hold_quirk && !curr_recv_count) {
|
||||
writel(addr, ®s->address);
|
||||
if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
|
||||
writel(CDNS_I2C_TRANSFER_SIZE,
|
||||
®s->transfer_size);
|
||||
curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
|
||||
} else {
|
||||
writel(recv_count, ®s->transfer_size);
|
||||
curr_recv_count = recv_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the address and data to be sent */
|
||||
ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
|
||||
CDNS_I2C_INTERRUPT_ARBLOST);
|
||||
if (!(ret & (CDNS_I2C_INTERRUPT_ARBLOST |
|
||||
CDNS_I2C_INTERRUPT_COMP)))
|
||||
return -ETIMEDOUT;
|
||||
if (ret & CDNS_I2C_INTERRUPT_ARBLOST)
|
||||
return -EAGAIN;
|
||||
|
||||
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 = 0;
|
||||
int count;
|
||||
bool hold_quirk;
|
||||
struct i2c_msg *message = msg;
|
||||
int num_msgs = nmsgs;
|
||||
|
||||
hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
||||
|
||||
if (nmsgs > 1) {
|
||||
/*
|
||||
* This controller does not give completion interrupt after a
|
||||
* master receive message if HOLD bit is set (repeated start),
|
||||
* resulting in SW timeout. Hence, if a receive message is
|
||||
* followed by any other message, an error is returned
|
||||
* indicating that this sequence is not supported.
|
||||
*/
|
||||
for (count = 0; (count < nmsgs - 1) && hold_quirk; count++) {
|
||||
if (msg[count].flags & I2C_M_RD) {
|
||||
printf("Can't do repeated start after a receive message\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_bus->hold_flag = 1;
|
||||
setbits_le32(&i2c_bus->regs->control, CDNS_I2C_CONTROL_HOLD);
|
||||
} else {
|
||||
i2c_bus->hold_flag = 0;
|
||||
}
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (u8 retry = 0; retry < CDNS_I2C_ARB_LOST_MAX_RETRIES &&
|
||||
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 = 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);
|
||||
}
|
||||
if (ret == -EAGAIN) {
|
||||
msg = message;
|
||||
nmsgs = num_msgs;
|
||||
retry++;
|
||||
printf("%s,arbitration lost, retrying:%d\n", __func__,
|
||||
retry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
|
||||
struct cdns_i2c_platform_data *pdata =
|
||||
(struct cdns_i2c_platform_data *)dev_get_driver_data(dev);
|
||||
struct clk clk;
|
||||
int ret;
|
||||
|
||||
i2c_bus->regs = (struct cdns_i2c_regs *)dev_read_addr(dev);
|
||||
if (!i2c_bus->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata)
|
||||
i2c_bus->quirks = pdata->quirks;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_bus->input_freq = clk_get_rate(&clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops cdns_i2c_ops = {
|
||||
.xfer = cdns_i2c_xfer,
|
||||
.set_bus_speed = cdns_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct cdns_i2c_platform_data r1p10_i2c_def = {
|
||||
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
|
||||
};
|
||||
|
||||
static const struct udevice_id cdns_i2c_of_match[] = {
|
||||
{ .compatible = "cdns,i2c-r1p10", .data = (ulong)&r1p10_i2c_def },
|
||||
{ .compatible = "cdns,i2c-r1p14" },
|
||||
{ /* 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,
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
/*
|
||||
* i2c emulation works using an 'emul' node at the bus level. Each device in
|
||||
* that node is in the UCLASS_I2C_EMUL uclass, and emulates one i2c device. A
|
||||
* pointer to the device it emulates is in the 'dev' property of the emul device
|
||||
* uclass platdata (struct i2c_emul_platdata), put there by i2c_emul_find().
|
||||
* When sandbox wants an emulator for a device, it calls i2c_emul_find() which
|
||||
* searches for the emulator with the correct address. To find the device for an
|
||||
* emulator, call i2c_emul_get_device().
|
||||
*
|
||||
* The 'emul' node is in the UCLASS_I2C_EMUL_PARENT uclass. We use a separate
|
||||
* uclass so avoid having strange devices on the I2C bus.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct i2c_emul_uc_platdata - information about the emulator for this device
|
||||
*
|
||||
* This is used by devices in UCLASS_I2C_EMUL to record information about the
|
||||
* device being emulated. It is accessible with dev_get_uclass_platdata()
|
||||
*
|
||||
* @dev: Device being emulated
|
||||
*/
|
||||
struct i2c_emul_uc_platdata {
|
||||
struct udevice *dev;
|
||||
};
|
||||
|
||||
struct udevice *i2c_emul_get_device(struct udevice *emul)
|
||||
{
|
||||
struct i2c_emul_uc_platdata *uc_plat = dev_get_uclass_platdata(emul);
|
||||
|
||||
return uc_plat->dev;
|
||||
}
|
||||
|
||||
int i2c_emul_find(struct udevice *dev, struct udevice **emulp)
|
||||
{
|
||||
struct i2c_emul_uc_platdata *uc_plat;
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev,
|
||||
"sandbox,emul", &emul);
|
||||
if (ret) {
|
||||
log_err("No emulators for device '%s'\n", dev->name);
|
||||
return ret;
|
||||
}
|
||||
uc_plat = dev_get_uclass_platdata(emul);
|
||||
uc_plat->dev = dev;
|
||||
*emulp = emul;
|
||||
|
||||
return device_probe(emul);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(i2c_emul) = {
|
||||
.id = UCLASS_I2C_EMUL,
|
||||
.name = "i2c_emul",
|
||||
.per_device_platdata_auto_alloc_size =
|
||||
sizeof(struct i2c_emul_uc_platdata),
|
||||
};
|
||||
|
||||
/*
|
||||
* This uclass is a child of the i2c bus. Its platdata is not defined here so
|
||||
* is defined by its parent, UCLASS_I2C, which uses struct dm_i2c_chip. See
|
||||
* per_child_platdata_auto_alloc_size in UCLASS_DRIVER(i2c).
|
||||
*/
|
||||
UCLASS_DRIVER(i2c_emul_parent) = {
|
||||
.id = UCLASS_I2C_EMUL_PARENT,
|
||||
.name = "i2c_emul_parent",
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
};
|
||||
|
||||
static const struct udevice_id i2c_emul_parent_ids[] = {
|
||||
{ .compatible = "sandbox,i2c-emul-parent" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_emul_parent_drv) = {
|
||||
.name = "i2c_emul_parent_drv",
|
||||
.id = UCLASS_I2C_EMUL_PARENT,
|
||||
.of_match = i2c_emul_parent_ids,
|
||||
};
|
||||
@@ -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(dev);
|
||||
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:
|
||||
pr_err("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,
|
||||
};
|
||||
@@ -0,0 +1,715 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <malloc.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#ifdef CONFIG_DM_GPIO
|
||||
#include <asm/gpio.h>
|
||||
#endif
|
||||
|
||||
#define I2C_MAX_OFFSET_LEN 4
|
||||
|
||||
enum {
|
||||
PIN_SDA = 0,
|
||||
PIN_SCL,
|
||||
PIN_COUNT,
|
||||
};
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* detect the presence of the chip on the bus */
|
||||
ret = i2c_probe_chip(bus, chip_addr, 0);
|
||||
debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name,
|
||||
chip_addr, ret);
|
||||
if (ret) {
|
||||
debug("Cannot detect I2C chip %02x on bus %d\n", chip_addr,
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_GPIO
|
||||
static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit)
|
||||
{
|
||||
if (bit)
|
||||
dm_gpio_set_dir_flags(pin, GPIOD_IS_IN);
|
||||
else
|
||||
dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT |
|
||||
GPIOD_ACTIVE_LOW |
|
||||
GPIOD_IS_OUT_ACTIVE);
|
||||
}
|
||||
|
||||
static int i2c_gpio_get_pin(struct gpio_desc *pin)
|
||||
{
|
||||
return dm_gpio_get_value(pin);
|
||||
}
|
||||
|
||||
static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin,
|
||||
struct gpio_desc *scl_pin)
|
||||
{
|
||||
int counter = 9;
|
||||
int ret = 0;
|
||||
|
||||
i2c_gpio_set_pin(sda_pin, 1);
|
||||
i2c_gpio_set_pin(scl_pin, 1);
|
||||
udelay(5);
|
||||
|
||||
/* Toggle SCL until slave release SDA */
|
||||
while (counter-- >= 0) {
|
||||
i2c_gpio_set_pin(scl_pin, 1);
|
||||
udelay(5);
|
||||
i2c_gpio_set_pin(scl_pin, 0);
|
||||
udelay(5);
|
||||
if (i2c_gpio_get_pin(sda_pin))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Then, send I2C stop */
|
||||
i2c_gpio_set_pin(sda_pin, 0);
|
||||
udelay(5);
|
||||
|
||||
i2c_gpio_set_pin(scl_pin, 1);
|
||||
udelay(5);
|
||||
|
||||
i2c_gpio_set_pin(sda_pin, 1);
|
||||
udelay(5);
|
||||
|
||||
if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin))
|
||||
ret = -EREMOTEIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_deblock_gpio(struct udevice *bus)
|
||||
{
|
||||
struct gpio_desc gpios[PIN_COUNT];
|
||||
int ret, ret0;
|
||||
|
||||
ret = gpio_request_list_by_name(bus, "gpios", gpios,
|
||||
ARRAY_SIZE(gpios), GPIOD_IS_IN);
|
||||
if (ret != ARRAY_SIZE(gpios)) {
|
||||
debug("%s: I2C Node '%s' has no 'gpios' property %s\n",
|
||||
__func__, dev_read_name(bus), bus->name);
|
||||
if (ret >= 0) {
|
||||
gpio_free_list(bus, gpios, ret);
|
||||
ret = -ENOENT;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pinctrl_select_state(bus, "gpio");
|
||||
if (ret) {
|
||||
debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n",
|
||||
__func__, dev_read_name(bus), bus->name);
|
||||
goto out_no_pinctrl;
|
||||
}
|
||||
|
||||
ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]);
|
||||
|
||||
ret = pinctrl_select_state(bus, "default");
|
||||
if (ret) {
|
||||
debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n",
|
||||
__func__, dev_read_name(bus), bus->name);
|
||||
}
|
||||
|
||||
ret = !ret ? ret0 : ret;
|
||||
|
||||
out_no_pinctrl:
|
||||
gpio_free_list(bus, gpios, ARRAY_SIZE(gpios));
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int i2c_deblock_gpio(struct udevice *bus)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif // CONFIG_DM_GPIO
|
||||
|
||||
int i2c_deblock(struct udevice *bus)
|
||||
{
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
|
||||
if (!ops->deblock)
|
||||
return i2c_deblock_gpio(bus);
|
||||
|
||||
return ops->deblock(bus);
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip)
|
||||
{
|
||||
int addr;
|
||||
|
||||
chip->offset_len = dev_read_u32_default(dev, "u-boot,i2c-offset-len",
|
||||
1);
|
||||
chip->flags = 0;
|
||||
addr = dev_read_u32_default(dev, "reg", -1);
|
||||
if (addr == -1) {
|
||||
debug("%s: I2C Node '%s' has no 'reg' property %s\n", __func__,
|
||||
dev_read_name(dev), dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
chip->chip_addr = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int i2c_pre_probe(struct udevice *dev)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
|
||||
unsigned int max = 0;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
i2c->max_transaction_bytes = 0;
|
||||
dev_for_each_subnode(node, dev) {
|
||||
ret = ofnode_read_u32(node,
|
||||
"u-boot,i2c-transaction-bytes",
|
||||
&max);
|
||||
if (!ret && max > i2c->max_transaction_bytes)
|
||||
i2c->max_transaction_bytes = max;
|
||||
}
|
||||
|
||||
debug("%s: I2C bus: %s max transaction bytes: %d\n", __func__,
|
||||
dev->name, i2c->max_transaction_bytes);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_post_probe(struct udevice *dev)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
|
||||
|
||||
i2c->speed_hz = dev_read_u32_default(dev, "clock-frequency", 100000);
|
||||
|
||||
return dm_i2c_set_bus_speed(dev, i2c->speed_hz);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int i2c_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
|
||||
|
||||
if (!dev_of_valid(dev))
|
||||
return 0;
|
||||
return i2c_chip_ofdata_to_platdata(dev, plat);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct i2c_priv {
|
||||
int max_id;
|
||||
};
|
||||
|
||||
static int i2c_post_bind(struct udevice *dev)
|
||||
{
|
||||
struct uclass *class = dev->uclass;
|
||||
struct i2c_priv *priv = class->priv;
|
||||
int ret = 0;
|
||||
|
||||
/* Just for sure */
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
debug("%s: %s, req_seq=%d\n", __func__, dev->name, dev->req_seq);
|
||||
|
||||
/* if there is no alias ID, use the first free */
|
||||
if (dev->req_seq == -1)
|
||||
dev->req_seq = ++priv->max_id;
|
||||
|
||||
debug("%s: %s, new req_seq=%d\n", __func__, dev->name, dev->req_seq);
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
ret = dm_scan_fdt_dev(dev);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_uclass_init(struct uclass *class)
|
||||
{
|
||||
struct i2c_priv *priv = class->priv;
|
||||
|
||||
/* Just for sure */
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the last allocated alias. */
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
priv->max_id = dev_read_alias_highest_id("i2c");
|
||||
#else
|
||||
priv->max_id = -1;
|
||||
#endif
|
||||
|
||||
debug("%s: highest alias id is %d\n", __func__, priv->max_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(i2c) = {
|
||||
.id = UCLASS_I2C,
|
||||
.name = "i2c",
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
.post_bind = i2c_post_bind,
|
||||
.init = i2c_uclass_init,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_priv),
|
||||
.pre_probe = i2c_pre_probe,
|
||||
.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,
|
||||
};
|
||||
@@ -0,0 +1,330 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014 Panasonic Corporation
|
||||
* Copyright (C) 2015-2016 Socionext Inc.
|
||||
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <fdtdec.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_priv {
|
||||
struct udevice *dev;
|
||||
struct uniphier_fi2c_regs __iomem *regs; /* register base */
|
||||
unsigned long fioclk; /* internal operation clock */
|
||||
unsigned long timeout; /* time out (us) */
|
||||
};
|
||||
|
||||
static void uniphier_fi2c_reset(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
writel(I2C_RST_RST, &priv->regs->rst);
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_check_bus_busy(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(&priv->regs->sr, val, !(val & I2C_SR_DB), 100);
|
||||
if (ret < 0) {
|
||||
dev_dbg(priv->dev, "error: device busy too long. reset...\n");
|
||||
uniphier_fi2c_reset(priv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_probe(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
struct uniphier_fi2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
addr = devfdt_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = devm_ioremap(dev, addr, SZ_128);
|
||||
if (!priv->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->fioclk = FIOCLK;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
/* bus forcible reset */
|
||||
uniphier_fi2c_reset(priv);
|
||||
|
||||
writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, &priv->regs->brst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_irq(struct uniphier_fi2c_priv *priv, u32 flags,
|
||||
bool *stop)
|
||||
{
|
||||
u32 irq;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(&priv->regs->intr, irq, irq & flags,
|
||||
priv->timeout);
|
||||
if (ret < 0) {
|
||||
dev_dbg(priv->dev, "error: time out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq & I2C_INT_AL) {
|
||||
dev_dbg(priv->dev, "error: arbitration lost\n");
|
||||
*stop = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq & I2C_INT_NA) {
|
||||
dev_dbg(priv->dev, "error: no answer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int issue_stop(struct uniphier_fi2c_priv *priv, int old_ret)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(priv->dev, "stop condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STO, &priv->regs->cr);
|
||||
|
||||
ret = uniphier_fi2c_check_bus_busy(priv);
|
||||
if (ret < 0)
|
||||
dev_dbg(priv->dev, "error: device busy after operation\n");
|
||||
|
||||
return old_ret ? old_ret : ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_transmit(struct uniphier_fi2c_priv *priv, 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 = priv->regs;
|
||||
|
||||
dev_dbg(priv->dev, "%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);
|
||||
|
||||
dev_dbg(priv->dev, "start condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STA, ®s->cr);
|
||||
|
||||
ret = wait_for_irq(priv, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
while (len--) {
|
||||
dev_dbg(priv->dev, "sending %x\n", *buf);
|
||||
writel(*buf++, ®s->dttx);
|
||||
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
ret = wait_for_irq(priv, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
if (*stop)
|
||||
ret = issue_stop(priv, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_receive(struct uniphier_fi2c_priv *priv, 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 = priv->regs;
|
||||
|
||||
dev_dbg(priv->dev, "%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(priv, 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);
|
||||
|
||||
dev_dbg(priv->dev, "start condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STA | (len == 1 ? I2C_CR_NACK : 0),
|
||||
®s->cr);
|
||||
|
||||
while (len--) {
|
||||
ret = wait_for_irq(priv, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
*buf++ = readl(®s->dtrx);
|
||||
dev_dbg(priv->dev, "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(priv, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
int ret;
|
||||
struct uniphier_fi2c_priv *priv = dev_get_priv(bus);
|
||||
bool stop;
|
||||
|
||||
ret = uniphier_fi2c_check_bus_busy(priv);
|
||||
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(priv, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
else
|
||||
ret = uniphier_fi2c_transmit(priv, 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_priv *priv = dev_get_priv(bus);
|
||||
struct uniphier_fi2c_regs __iomem *regs = priv->regs;
|
||||
|
||||
/* max supported frequency is 400 kHz */
|
||||
if (speed > 400000)
|
||||
return -EINVAL;
|
||||
|
||||
ret = uniphier_fi2c_check_bus_busy(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* make sure the bus is idle when changing the frequency */
|
||||
writel(I2C_BRST_RSCLO, ®s->brst);
|
||||
|
||||
clk_count = priv->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.
|
||||
*/
|
||||
priv->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,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_fi2c_priv),
|
||||
.ops = &uniphier_fi2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014 Panasonic Corporation
|
||||
* Copyright (C) 2015-2016 Socionext Inc.
|
||||
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.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_priv {
|
||||
struct udevice *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_priv *priv = dev_get_priv(dev);
|
||||
|
||||
addr = devfdt_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = devm_ioremap(dev, addr, SZ_64);
|
||||
if (!priv->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->input_clk = IOBUS_FREQ;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
/* deassert reset */
|
||||
writel(0x3, &priv->regs->brst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_and_recv_byte(struct uniphier_i2c_priv *priv, u32 dtrm)
|
||||
{
|
||||
writel(dtrm, &priv->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(priv->wait_us);
|
||||
|
||||
return readl(&priv->regs->drec);
|
||||
}
|
||||
|
||||
static int send_byte(struct uniphier_i2c_priv *priv, u32 dtrm, bool *stop)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 drec;
|
||||
|
||||
drec = send_and_recv_byte(priv, dtrm);
|
||||
|
||||
if (drec & I2C_DREC_LAB) {
|
||||
dev_dbg(priv->dev, "uniphier_i2c: bus arbitration failed\n");
|
||||
*stop = false;
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
if (drec & I2C_DREC_LRB) {
|
||||
dev_dbg(priv->dev, "uniphier_i2c: slave did not return ACK\n");
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_transmit(struct uniphier_i2c_priv *priv, uint addr,
|
||||
uint len, const u8 *buf, bool *stop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(priv->dev, "%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
ret = send_byte(priv, I2C_DTRM_STA | I2C_DTRM_NACK | addr << 1, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
while (len--) {
|
||||
ret = send_byte(priv, I2C_DTRM_NACK | *buf++, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (*stop)
|
||||
writel(I2C_DTRM_STO | I2C_DTRM_NACK, &priv->regs->dtrm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_receive(struct uniphier_i2c_priv *priv, uint addr,
|
||||
uint len, u8 *buf, bool *stop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(priv->dev, "%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
ret = send_byte(priv, I2C_DTRM_STA | I2C_DTRM_NACK |
|
||||
I2C_DTRM_RD | addr << 1, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
while (len--)
|
||||
*buf++ = send_and_recv_byte(priv, len ? 0 : I2C_DTRM_NACK);
|
||||
|
||||
fail:
|
||||
if (*stop)
|
||||
writel(I2C_DTRM_STO | I2C_DTRM_NACK, &priv->regs->dtrm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
int ret = 0;
|
||||
struct uniphier_i2c_priv *priv = 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(priv, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
else
|
||||
ret = uniphier_i2c_transmit(priv, 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_priv *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,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_i2c_priv),
|
||||
.ops = &uniphier_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,280 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2018 Arm Ltd.
|
||||
* Author: Liviu Dudau <liviu.dudau@foss.arm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define I2C_CONTROL_REG 0x00
|
||||
#define I2C_SET_REG 0x00
|
||||
#define I2C_CLEAR_REG 0x04
|
||||
|
||||
#define SCL BIT(0)
|
||||
#define SDA BIT(1)
|
||||
|
||||
struct versatile_i2c_priv {
|
||||
phys_addr_t base;
|
||||
u32 delay;
|
||||
};
|
||||
|
||||
static inline void versatile_sda_set(struct versatile_i2c_priv *priv, u8 state)
|
||||
{
|
||||
writel(SDA, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
|
||||
udelay(priv->delay);
|
||||
}
|
||||
|
||||
static inline int versatile_sda_get(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
int v = !!(readl(priv->base + I2C_CONTROL_REG) & SDA);
|
||||
|
||||
udelay(priv->delay);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void versatile_scl_set(struct versatile_i2c_priv *priv, u8 state)
|
||||
{
|
||||
writel(SCL, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
|
||||
udelay(priv->delay);
|
||||
}
|
||||
|
||||
static inline int versatile_scl_get(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
int v = !!(readl(priv->base + I2C_CONTROL_REG) & SCL);
|
||||
|
||||
udelay(priv->delay);
|
||||
return v;
|
||||
}
|
||||
|
||||
/* start: SDA goes from high to low while SCL is high */
|
||||
static void versatile_i2c_start(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
udelay(priv->delay);
|
||||
versatile_sda_set(priv, 1);
|
||||
versatile_scl_set(priv, 1);
|
||||
versatile_sda_set(priv, 0);
|
||||
}
|
||||
|
||||
/* stop: SDA goes from low to high while SCL is high */
|
||||
static void versatile_i2c_stop(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
versatile_scl_set(priv, 0);
|
||||
versatile_sda_set(priv, 0);
|
||||
versatile_scl_set(priv, 1);
|
||||
versatile_sda_set(priv, 1);
|
||||
}
|
||||
|
||||
/* read a bit from the SDA line (data or ACK/NACK) */
|
||||
static u8 versatile_i2c_read_bit(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
versatile_scl_set(priv, 0);
|
||||
versatile_sda_set(priv, 1);
|
||||
versatile_scl_set(priv, 1);
|
||||
udelay(priv->delay);
|
||||
return (u8)versatile_sda_get(priv);
|
||||
}
|
||||
|
||||
/* write a bit on the SDA line */
|
||||
static void versatile_i2c_write_bit(struct versatile_i2c_priv *priv, u8 bit)
|
||||
{
|
||||
versatile_scl_set(priv, 0);
|
||||
versatile_sda_set(priv, bit);
|
||||
versatile_scl_set(priv, 1);
|
||||
udelay(priv->delay);
|
||||
}
|
||||
|
||||
/* send a reset sequence of 9 clocks with SDA high */
|
||||
static void versatile_i2c_reset_bus(struct versatile_i2c_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
versatile_i2c_write_bit(priv, 1);
|
||||
|
||||
versatile_i2c_stop(priv);
|
||||
}
|
||||
|
||||
/* write byte without start/stop sequence */
|
||||
static int versatile_i2c_write_byte(struct versatile_i2c_priv *priv, u8 byte)
|
||||
{
|
||||
u8 nak, i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
versatile_i2c_write_bit(priv, byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
|
||||
/* read ACK */
|
||||
nak = versatile_i2c_read_bit(priv);
|
||||
versatile_scl_set(priv, 0);
|
||||
|
||||
return nak; /* not a nack is an ack */
|
||||
}
|
||||
|
||||
static int versatile_i2c_read_byte(struct versatile_i2c_priv *priv,
|
||||
u8 *byte, u8 ack)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
*byte = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
*byte <<= 1;
|
||||
*byte |= versatile_i2c_read_bit(priv);
|
||||
}
|
||||
/* write the nack */
|
||||
versatile_i2c_write_bit(priv, ack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_i2c_send_slave_addr(struct versatile_i2c_priv *priv,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
u8 addr;
|
||||
int ret;
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
/* 10-bit address, send extended address code first */
|
||||
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
|
||||
ret = versatile_i2c_write_byte(priv, addr);
|
||||
if (ret) {
|
||||
versatile_i2c_stop(priv);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* remaining bits */
|
||||
ret = versatile_i2c_write_byte(priv, msg->addr & 0xff);
|
||||
if (ret) {
|
||||
versatile_i2c_stop(priv);
|
||||
return -EIO;
|
||||
}
|
||||
/* reads need to resend the addr */
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
versatile_i2c_start(priv);
|
||||
addr |= 1;
|
||||
ret = versatile_i2c_write_byte(priv, addr);
|
||||
if (ret) {
|
||||
versatile_i2c_stop(priv);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* normal 7-bit address */
|
||||
addr = msg->addr << 1;
|
||||
if (msg->flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
ret = versatile_i2c_write_byte(priv, addr);
|
||||
if (ret) {
|
||||
versatile_i2c_stop(priv);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_i2c_message_xfer(struct versatile_i2c_priv *priv,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
int i, ret;
|
||||
u8 ack;
|
||||
|
||||
versatile_i2c_start(priv);
|
||||
if (versatile_i2c_send_slave_addr(priv, msg))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ack = (msg->len - i - 1) == 0 ? 1 : 0;
|
||||
ret = versatile_i2c_read_byte(priv, &msg->buf[i], ack);
|
||||
} else {
|
||||
ret = versatile_i2c_write_byte(priv, msg->buf[i]);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
versatile_i2c_stop(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int versatile_i2c_xfer(struct udevice *bus,
|
||||
struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct versatile_i2c_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
for ( ; nmsgs > 0; nmsgs--, msg++) {
|
||||
ret = versatile_i2c_message_xfer(priv, msg);
|
||||
if (ret)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_i2c_chip_probe(struct udevice *bus,
|
||||
uint chip, uint chip_flags)
|
||||
{
|
||||
/* probe the presence of a slave by writing a 0-size message */
|
||||
struct i2c_msg msg = { .addr = chip, .flags = chip_flags,
|
||||
.len = 0, .buf = NULL };
|
||||
struct versatile_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
return versatile_i2c_message_xfer(priv, &msg);
|
||||
}
|
||||
|
||||
static int versatile_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct versatile_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->delay = 1000000 / (speed << 2);
|
||||
|
||||
versatile_i2c_reset_bus(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct versatile_i2c_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->base = (phys_addr_t)dev_read_addr(dev);
|
||||
priv->delay = 25; /* 25us * 4 = 100kHz */
|
||||
/*
|
||||
* U-Boot still doesn't assign automatically
|
||||
* sequence numbers to devices
|
||||
*/
|
||||
dev->req_seq = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops versatile_i2c_ops = {
|
||||
.xfer = versatile_i2c_xfer,
|
||||
.probe_chip = versatile_i2c_chip_probe,
|
||||
.set_bus_speed = versatile_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id versatile_i2c_of_match[] = {
|
||||
{ .compatible = "arm,versatile-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(versatile_i2c) = {
|
||||
.name = "i2c-bus-versatile",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = versatile_i2c_of_match,
|
||||
.probe = versatile_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct versatile_i2c_priv),
|
||||
.ops = &versatile_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,348 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
#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;
|
||||
|
||||
#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;
|
||||
|
||||
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 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);
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#ifdef CONFIG_DM_I2C
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#else
|
||||
#include <gdsys_fpga.h>
|
||||
#endif
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
struct ihs_i2c_priv {
|
||||
uint speed;
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
struct ihs_i2c_regs {
|
||||
u16 interrupt_status;
|
||||
u16 interrupt_enable_control;
|
||||
u16 write_mailbox_ext;
|
||||
u16 write_mailbox;
|
||||
u16 read_mailbox_ext;
|
||||
u16 read_mailbox;
|
||||
};
|
||||
|
||||
#define ihs_i2c_set(map, member, val) \
|
||||
regmap_set(map, struct ihs_i2c_regs, member, val)
|
||||
|
||||
#define ihs_i2c_get(map, member, valp) \
|
||||
regmap_get(map, struct ihs_i2c_regs, member, valp)
|
||||
|
||||
#else /* !CONFIG_DM_I2C */
|
||||
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
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
|
||||
enum {
|
||||
I2CINT_ERROR_EV = BIT(13),
|
||||
I2CINT_TRANSMIT_EV = BIT(14),
|
||||
I2CINT_RECEIVE_EV = BIT(15),
|
||||
};
|
||||
|
||||
enum {
|
||||
I2CMB_READ = 0 << 10,
|
||||
I2CMB_WRITE = 1 << 10,
|
||||
I2CMB_1BYTE = 0 << 11,
|
||||
I2CMB_2BYTE = 1 << 11,
|
||||
I2CMB_DONT_HOLD_BUS = 0 << 13,
|
||||
I2CMB_HOLD_BUS = 1 << 13,
|
||||
I2CMB_NATIVE = 2 << 14,
|
||||
};
|
||||
|
||||
enum {
|
||||
I2COP_WRITE = 0,
|
||||
I2COP_READ = 1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
static int wait_for_int(struct udevice *dev, int read)
|
||||
#else
|
||||
static int wait_for_int(bool read)
|
||||
#endif
|
||||
{
|
||||
u16 val;
|
||||
uint ctr = 0;
|
||||
#ifdef CONFIG_DM_I2C
|
||||
struct ihs_i2c_priv *priv = dev_get_priv(dev);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_get(priv->map, interrupt_status, &val);
|
||||
#else
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
#endif
|
||||
/* Wait until error or receive/transmit interrupt was raised */
|
||||
while (!(val & (I2CINT_ERROR_EV
|
||||
| (read ? I2CINT_RECEIVE_EV : I2CINT_TRANSMIT_EV)))) {
|
||||
udelay(10);
|
||||
if (ctr++ > 5000) {
|
||||
debug("%s: timed out\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_get(priv->map, interrupt_status, &val);
|
||||
#else
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (val & I2CINT_ERROR_EV) ? -EIO : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
static int ihs_i2c_transfer(struct udevice *dev, uchar chip,
|
||||
uchar *buffer, int len, int read, bool is_last)
|
||||
#else
|
||||
static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read,
|
||||
bool is_last)
|
||||
#endif
|
||||
{
|
||||
u16 val;
|
||||
u16 data;
|
||||
int res;
|
||||
#ifdef CONFIG_DM_I2C
|
||||
struct ihs_i2c_priv *priv = dev_get_priv(dev);
|
||||
#endif
|
||||
|
||||
/* Clear interrupt status */
|
||||
data = I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV;
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_set(priv->map, interrupt_status, data);
|
||||
ihs_i2c_get(priv->map, interrupt_status, &val);
|
||||
#else
|
||||
I2C_SET_REG(interrupt_status, data);
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
#endif
|
||||
|
||||
/* If we want to write and have data, write the bytes to the mailbox */
|
||||
if (!read && len) {
|
||||
val = buffer[0];
|
||||
|
||||
if (len > 1)
|
||||
val |= buffer[1] << 8;
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_set(priv->map, write_mailbox_ext, val);
|
||||
#else
|
||||
I2C_SET_REG(write_mailbox_ext, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
data = I2CMB_NATIVE
|
||||
| (read ? 0 : I2CMB_WRITE)
|
||||
| (chip << 1)
|
||||
| ((len > 1) ? I2CMB_2BYTE : 0)
|
||||
| (is_last ? 0 : I2CMB_HOLD_BUS);
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_set(priv->map, write_mailbox, data);
|
||||
#else
|
||||
I2C_SET_REG(write_mailbox, data);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
res = wait_for_int(dev, read);
|
||||
#else
|
||||
res = wait_for_int(read);
|
||||
#endif
|
||||
if (res) {
|
||||
if (res == -ETIMEDOUT)
|
||||
debug("%s: time out while waiting for event\n", __func__);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* If we want to read, get the bytes from the mailbox */
|
||||
if (read) {
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ihs_i2c_get(priv->map, read_mailbox_ext, &val);
|
||||
#else
|
||||
I2C_GET_REG(read_mailbox_ext, &val);
|
||||
#endif
|
||||
buffer[0] = val & 0xff;
|
||||
if (len > 1)
|
||||
buffer[1] = val >> 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
static int ihs_i2c_send_buffer(struct udevice *dev, uchar chip, u8 *data, int len, bool hold_bus, int read)
|
||||
#else
|
||||
static int ihs_i2c_send_buffer(uchar chip, u8 *data, int len, bool hold_bus,
|
||||
int read)
|
||||
#endif
|
||||
{
|
||||
int res;
|
||||
|
||||
while (len) {
|
||||
int transfer = min(len, 2);
|
||||
bool is_last = len <= transfer;
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
res = ihs_i2c_transfer(dev, chip, data, transfer, read,
|
||||
hold_bus ? false : is_last);
|
||||
#else
|
||||
res = ihs_i2c_transfer(chip, data, transfer, read,
|
||||
hold_bus ? false : is_last);
|
||||
#endif
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
data += transfer;
|
||||
len -= transfer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
static int ihs_i2c_address(struct udevice *dev, uchar chip, u8 *addr, int alen,
|
||||
bool hold_bus)
|
||||
#else
|
||||
static int ihs_i2c_address(uchar chip, u8 *addr, int alen, bool hold_bus)
|
||||
#endif
|
||||
{
|
||||
#ifdef CONFIG_DM_I2C
|
||||
return ihs_i2c_send_buffer(dev, chip, addr, alen, hold_bus, I2COP_WRITE);
|
||||
#else
|
||||
return ihs_i2c_send_buffer(chip, addr, alen, hold_bus, I2COP_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
static int ihs_i2c_access(struct udevice *dev, uchar chip, u8 *addr,
|
||||
int alen, uchar *buffer, int len, int read)
|
||||
#else
|
||||
static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, u8 *addr,
|
||||
int alen, uchar *buffer, int len, int read)
|
||||
#endif
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Don't hold the bus if length of data to send/receive is zero */
|
||||
if (len <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
res = ihs_i2c_address(dev, chip, addr, alen, len);
|
||||
#else
|
||||
res = ihs_i2c_address(chip, addr, alen, len);
|
||||
#endif
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
return ihs_i2c_send_buffer(dev, chip, buffer, len, false, read);
|
||||
#else
|
||||
return ihs_i2c_send_buffer(chip, buffer, len, false, read);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
|
||||
int ihs_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct ihs_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
regmap_init_mem(dev_ofnode(bus), &priv->map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_set_bus_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
struct ihs_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
if (speed != priv->speed && priv->speed != 0)
|
||||
return -EINVAL;
|
||||
|
||||
priv->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
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\n", __func__);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
omsg = nmsgs == 1 ? &dummy : msg;
|
||||
dmsg = nmsgs == 1 ? msg : msg + 1;
|
||||
|
||||
if (dmsg->flags & I2C_M_RD)
|
||||
return ihs_i2c_access(bus, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len,
|
||||
I2COP_READ);
|
||||
else
|
||||
return ihs_i2c_access(bus, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len,
|
||||
I2COP_WRITE);
|
||||
}
|
||||
|
||||
static int ihs_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
||||
u32 chip_flags)
|
||||
{
|
||||
uchar buffer[2];
|
||||
int res;
|
||||
|
||||
res = ihs_i2c_transfer(bus, chip_addr, buffer, 0, I2COP_READ, true);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops ihs_i2c_ops = {
|
||||
.xfer = ihs_i2c_xfer,
|
||||
.probe_chip = ihs_i2c_probe_chip,
|
||||
.set_bus_speed = ihs_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id ihs_i2c_ids[] = {
|
||||
{ .compatible = "gdsys,ihs_i2cmaster", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_ihs) = {
|
||||
.name = "i2c_ihs",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = ihs_i2c_ids,
|
||||
.probe = ihs_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct ihs_i2c_priv),
|
||||
.ops = &ihs_i2c_ops,
|
||||
};
|
||||
|
||||
#else /* CONFIG_DM_I2C */
|
||||
|
||||
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];
|
||||
int res;
|
||||
|
||||
res = ihs_i2c_transfer(chip, buffer, 0, I2COP_READ, true);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
u8 addr_bytes[4];
|
||||
|
||||
put_unaligned_le32(addr, addr_bytes);
|
||||
|
||||
return ihs_i2c_access(adap, chip, addr_bytes, alen, buffer, len,
|
||||
I2COP_READ);
|
||||
}
|
||||
|
||||
static int ihs_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
u8 addr_bytes[4];
|
||||
|
||||
put_unaligned_le32(addr, addr_bytes);
|
||||
|
||||
return ihs_i2c_access(adap, chip, addr_bytes, alen, buffer, len,
|
||||
I2COP_WRITE);
|
||||
}
|
||||
|
||||
static unsigned int ihs_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
if (speed != adap->speed)
|
||||
return -EINVAL;
|
||||
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
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
@@ -0,0 +1,522 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2016 Freescale Semiconductors, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <imx_lpi2c.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#define LPI2C_FIFO_SIZE 4
|
||||
#define LPI2C_NACK_TOUT_MS 1
|
||||
#define LPI2C_TIMEOUT_MS 100
|
||||
|
||||
static int bus_i2c_init(struct udevice *bus, int speed);
|
||||
|
||||
/* Weak linked function for overridden by some SoC power function */
|
||||
int __weak init_i2c_power(unsigned i2c_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_lpci2c_check_busy_bus(const struct imx_lpi2c_reg *regs)
|
||||
{
|
||||
lpi2c_status_t result = LPI2C_SUCESS;
|
||||
u32 status;
|
||||
|
||||
status = readl(®s->msr);
|
||||
|
||||
if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK))
|
||||
result = LPI2C_BUSY;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int imx_lpci2c_check_clear_error(struct imx_lpi2c_reg *regs)
|
||||
{
|
||||
lpi2c_status_t result = LPI2C_SUCESS;
|
||||
u32 val, status;
|
||||
|
||||
status = readl(®s->msr);
|
||||
/* errors to check for */
|
||||
status &= LPI2C_MSR_NDF_MASK | LPI2C_MSR_ALF_MASK |
|
||||
LPI2C_MSR_FEF_MASK | LPI2C_MSR_PLTF_MASK;
|
||||
|
||||
if (status) {
|
||||
if (status & LPI2C_MSR_PLTF_MASK)
|
||||
result = LPI2C_PIN_LOW_TIMEOUT_ERR;
|
||||
else if (status & LPI2C_MSR_ALF_MASK)
|
||||
result = LPI2C_ARB_LOST_ERR;
|
||||
else if (status & LPI2C_MSR_NDF_MASK)
|
||||
result = LPI2C_NAK_ERR;
|
||||
else if (status & LPI2C_MSR_FEF_MASK)
|
||||
result = LPI2C_FIFO_ERR;
|
||||
|
||||
/* clear status flags */
|
||||
writel(0x7f00, ®s->msr);
|
||||
/* reset fifos */
|
||||
val = readl(®s->mcr);
|
||||
val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
|
||||
writel(val, ®s->mcr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs)
|
||||
{
|
||||
lpi2c_status_t result = LPI2C_SUCESS;
|
||||
u32 txcount = 0;
|
||||
ulong start_time = get_timer(0);
|
||||
|
||||
do {
|
||||
txcount = LPI2C_MFSR_TXCOUNT(readl(®s->mfsr));
|
||||
txcount = LPI2C_FIFO_SIZE - txcount;
|
||||
result = imx_lpci2c_check_clear_error(regs);
|
||||
if (result) {
|
||||
debug("i2c: wait for tx ready: result 0x%x\n", result);
|
||||
return result;
|
||||
}
|
||||
if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
|
||||
debug("i2c: wait for tx ready: timeout\n");
|
||||
return -1;
|
||||
}
|
||||
} while (!txcount);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
|
||||
{
|
||||
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
lpi2c_status_t result = LPI2C_SUCESS;
|
||||
|
||||
/* empty tx */
|
||||
if (!len)
|
||||
return result;
|
||||
|
||||
while (len--) {
|
||||
result = bus_i2c_wait_for_tx_ready(regs);
|
||||
if (result) {
|
||||
debug("i2c: send wait for tx ready: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
writel(*txbuf++, ®s->mtdr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
|
||||
{
|
||||
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
lpi2c_status_t result = LPI2C_SUCESS;
|
||||
u32 val;
|
||||
ulong start_time = get_timer(0);
|
||||
|
||||
/* empty read */
|
||||
if (!len)
|
||||
return result;
|
||||
|
||||
result = bus_i2c_wait_for_tx_ready(regs);
|
||||
if (result) {
|
||||
debug("i2c: receive wait fot tx ready: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* clear all status flags */
|
||||
writel(0x7f00, ®s->msr);
|
||||
/* send receive command */
|
||||
val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
|
||||
writel(val, ®s->mtdr);
|
||||
|
||||
while (len--) {
|
||||
do {
|
||||
result = imx_lpci2c_check_clear_error(regs);
|
||||
if (result) {
|
||||
debug("i2c: receive check clear error: %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
|
||||
debug("i2c: receive mrdr: timeout\n");
|
||||
return -1;
|
||||
}
|
||||
val = readl(®s->mrdr);
|
||||
} while (val & LPI2C_MRDR_RXEMPTY_MASK);
|
||||
*rxbuf++ = LPI2C_MRDR_DATA(val);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir)
|
||||
{
|
||||
lpi2c_status_t result;
|
||||
struct imx_lpi2c_reg *regs =
|
||||
(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
u32 val;
|
||||
|
||||
result = imx_lpci2c_check_busy_bus(regs);
|
||||
if (result) {
|
||||
debug("i2c: start check busy bus: 0x%x\n", result);
|
||||
|
||||
/* Try to init the lpi2c then check the bus busy again */
|
||||
bus_i2c_init(bus, 100000);
|
||||
result = imx_lpci2c_check_busy_bus(regs);
|
||||
if (result) {
|
||||
printf("i2c: Error check busy bus: 0x%x\n", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/* clear all status flags */
|
||||
writel(0x7f00, ®s->msr);
|
||||
/* turn off auto-stop condition */
|
||||
val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK;
|
||||
writel(val, ®s->mcfgr1);
|
||||
/* wait tx fifo ready */
|
||||
result = bus_i2c_wait_for_tx_ready(regs);
|
||||
if (result) {
|
||||
debug("i2c: start wait for tx ready: 0x%x\n", result);
|
||||
return result;
|
||||
}
|
||||
/* issue start command */
|
||||
val = LPI2C_MTDR_CMD(0x4) | (addr << 0x1) | dir;
|
||||
writel(val, ®s->mtdr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_stop(struct udevice *bus)
|
||||
{
|
||||
lpi2c_status_t result;
|
||||
struct imx_lpi2c_reg *regs =
|
||||
(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
u32 status;
|
||||
ulong start_time;
|
||||
|
||||
result = bus_i2c_wait_for_tx_ready(regs);
|
||||
if (result) {
|
||||
debug("i2c: stop wait for tx ready: 0x%x\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* send stop command */
|
||||
writel(LPI2C_MTDR_CMD(0x2), ®s->mtdr);
|
||||
|
||||
start_time = get_timer(0);
|
||||
while (1) {
|
||||
status = readl(®s->msr);
|
||||
result = imx_lpci2c_check_clear_error(regs);
|
||||
/* stop detect flag */
|
||||
if (status & LPI2C_MSR_SDF_MASK) {
|
||||
/* clear stop flag */
|
||||
status &= LPI2C_MSR_SDF_MASK;
|
||||
writel(status, ®s->msr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_timer(start_time) > LPI2C_NACK_TOUT_MS) {
|
||||
debug("stop timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_read(struct udevice *bus, u32 chip, u8 *buf, int len)
|
||||
{
|
||||
lpi2c_status_t result;
|
||||
|
||||
result = bus_i2c_start(bus, chip, 1);
|
||||
if (result)
|
||||
return result;
|
||||
result = bus_i2c_receive(bus, buf, len);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int bus_i2c_write(struct udevice *bus, u32 chip, u8 *buf, int len)
|
||||
{
|
||||
lpi2c_status_t result;
|
||||
|
||||
result = bus_i2c_start(bus, chip, 0);
|
||||
if (result)
|
||||
return result;
|
||||
result = bus_i2c_send(bus, buf, len);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
u32 __weak imx_get_i2cclk(u32 i2c_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
|
||||
{
|
||||
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
struct imx_lpi2c_reg *regs;
|
||||
u32 val;
|
||||
u32 preescale = 0, best_pre = 0, clkhi = 0;
|
||||
u32 best_clkhi = 0, abs_error = 0, rate;
|
||||
u32 error = 0xffffffff;
|
||||
u32 clock_rate;
|
||||
bool mode;
|
||||
int i;
|
||||
|
||||
regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
|
||||
if (IS_ENABLED(CONFIG_CLK)) {
|
||||
clock_rate = clk_get_rate(&i2c_bus->per_clk);
|
||||
if (clock_rate <= 0) {
|
||||
dev_err(bus, "Failed to get i2c clk: %d\n", clock_rate);
|
||||
return clock_rate;
|
||||
}
|
||||
} else {
|
||||
clock_rate = imx_get_i2cclk(bus->seq);
|
||||
if (!clock_rate)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mode = (readl(®s->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT;
|
||||
/* disable master mode */
|
||||
val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
|
||||
writel(val | LPI2C_MCR_MEN(0), ®s->mcr);
|
||||
|
||||
for (preescale = 1; (preescale <= 128) &&
|
||||
(error != 0); preescale = 2 * preescale) {
|
||||
for (clkhi = 1; clkhi < 32; clkhi++) {
|
||||
if (clkhi == 1)
|
||||
rate = (clock_rate / preescale) / (1 + 3 + 2 + 2 / preescale);
|
||||
else
|
||||
rate = (clock_rate / preescale / (3 * clkhi + 2 + 2 / preescale));
|
||||
|
||||
abs_error = speed > rate ? speed - rate : rate - speed;
|
||||
|
||||
if (abs_error < error) {
|
||||
best_pre = preescale;
|
||||
best_clkhi = clkhi;
|
||||
error = abs_error;
|
||||
if (abs_error == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard, fast, fast mode plus and ultra-fast transfers. */
|
||||
val = LPI2C_MCCR0_CLKHI(best_clkhi);
|
||||
if (best_clkhi < 2)
|
||||
val |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1);
|
||||
else
|
||||
val |= LPI2C_MCCR0_CLKLO(2 * best_clkhi) | LPI2C_MCCR0_SETHOLD(best_clkhi) |
|
||||
LPI2C_MCCR0_DATAVD(best_clkhi / 2);
|
||||
writel(val, ®s->mccr0);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (best_pre == (1 << i)) {
|
||||
best_pre = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK;
|
||||
writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), ®s->mcfgr1);
|
||||
|
||||
if (mode) {
|
||||
val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
|
||||
writel(val | LPI2C_MCR_MEN(1), ®s->mcr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_i2c_init(struct udevice *bus, int speed)
|
||||
{
|
||||
struct imx_lpi2c_reg *regs;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
|
||||
/* reset peripheral */
|
||||
writel(LPI2C_MCR_RST_MASK, ®s->mcr);
|
||||
writel(0x0, ®s->mcr);
|
||||
/* Disable Dozen mode */
|
||||
writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), ®s->mcr);
|
||||
/* host request disable, active high, external pin */
|
||||
val = readl(®s->mcfgr0);
|
||||
val &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK |
|
||||
LPI2C_MCFGR0_HRSEL_MASK));
|
||||
val |= LPI2C_MCFGR0_HRPOL(0x1);
|
||||
writel(val, ®s->mcfgr0);
|
||||
/* pincfg and ignore ack */
|
||||
val = readl(®s->mcfgr1);
|
||||
val &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK);
|
||||
val |= LPI2C_MCFGR1_PINCFG(0x0); /* 2 pin open drain */
|
||||
val |= LPI2C_MCFGR1_IGNACK(0x0); /* ignore nack */
|
||||
writel(val, ®s->mcfgr1);
|
||||
|
||||
ret = bus_i2c_set_bus_speed(bus, speed);
|
||||
|
||||
/* enable lpi2c in master mode */
|
||||
val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
|
||||
writel(val | LPI2C_MCR_MEN(1), ®s->mcr);
|
||||
|
||||
debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip,
|
||||
u32 chip_flags)
|
||||
{
|
||||
lpi2c_status_t result;
|
||||
|
||||
result = bus_i2c_start(bus, chip, 0);
|
||||
if (result) {
|
||||
bus_i2c_stop(bus);
|
||||
bus_i2c_init(bus, 100000);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = bus_i2c_stop(bus);
|
||||
if (result)
|
||||
bus_i2c_init(bus, 100000);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
int ret = 0, ret_stop;
|
||||
|
||||
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 = bus_i2c_read(bus, msg->addr, msg->buf, msg->len);
|
||||
else {
|
||||
ret = bus_i2c_write(bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
debug("i2c_write: error sending\n");
|
||||
|
||||
ret_stop = bus_i2c_stop(bus);
|
||||
if (ret_stop)
|
||||
debug("i2c_xfer: stop bus error\n");
|
||||
|
||||
ret |= ret_stop;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_lpi2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
return bus_i2c_set_bus_speed(bus, speed);
|
||||
}
|
||||
|
||||
__weak int enable_i2c_clk(unsigned char enable, unsigned int i2c_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_lpi2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
fdt_addr_t addr;
|
||||
int ret;
|
||||
|
||||
i2c_bus->driver_data = dev_get_driver_data(bus);
|
||||
|
||||
addr = devfdt_get_addr(bus);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
i2c_bus->base = addr;
|
||||
i2c_bus->index = bus->seq;
|
||||
i2c_bus->bus = bus;
|
||||
|
||||
/* power up i2c resource */
|
||||
ret = init_i2c_power(bus->seq);
|
||||
if (ret) {
|
||||
debug("init_i2c_power err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_CLK)) {
|
||||
ret = clk_get_by_name(bus, "per", &i2c_bus->per_clk);
|
||||
if (ret) {
|
||||
dev_err(bus, "Failed to get per clk\n");
|
||||
return ret;
|
||||
}
|
||||
ret = clk_enable(&i2c_bus->per_clk);
|
||||
if (ret) {
|
||||
dev_err(bus, "Failed to enable per clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_get_by_name(bus, "ipg", &i2c_bus->ipg_clk);
|
||||
if (ret) {
|
||||
dev_err(bus, "Failed to get ipg clk\n");
|
||||
return ret;
|
||||
}
|
||||
ret = clk_enable(&i2c_bus->ipg_clk);
|
||||
if (ret) {
|
||||
dev_err(bus, "Failed to enable ipg clk\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* To i.MX7ULP, only i2c4-7 can be handled by A7 core */
|
||||
ret = enable_i2c_clk(1, bus->seq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bus_i2c_init(bus, 100000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
debug("i2c : controller bus %d at 0x%lx , speed %d: ",
|
||||
bus->seq, i2c_bus->base,
|
||||
i2c_bus->speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops imx_lpi2c_ops = {
|
||||
.xfer = imx_lpi2c_xfer,
|
||||
.probe_chip = imx_lpi2c_probe_chip,
|
||||
.set_bus_speed = imx_lpi2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id imx_lpi2c_ids[] = {
|
||||
{ .compatible = "fsl,imx7ulp-lpi2c", },
|
||||
{ .compatible = "fsl,imx8qm-lpi2c", },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(imx_lpi2c) = {
|
||||
.name = "imx_lpi2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = imx_lpi2c_ids,
|
||||
.probe = imx_lpi2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct imx_lpi2c_bus),
|
||||
.ops = &imx_lpi2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,321 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SMBus block read/write support added by Stefan Roese:
|
||||
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* PCI Configuration Space (D31:F3): SMBus */
|
||||
#define SMB_BASE 0x20
|
||||
#define HOSTC 0x40
|
||||
#define HST_EN (1 << 0)
|
||||
#define SMB_RCV_SLVA 0x09
|
||||
|
||||
/* SMBus I/O bits. */
|
||||
#define SMBHSTSTAT 0x0
|
||||
#define SMBHSTCTL 0x2
|
||||
#define SMBHSTCMD 0x3
|
||||
#define SMBXMITADD 0x4
|
||||
#define SMBHSTDAT0 0x5
|
||||
#define SMBHSTDAT1 0x6
|
||||
#define SMBBLKDAT 0x7
|
||||
#define SMBTRNSADD 0x9
|
||||
#define SMBSLVDATA 0xa
|
||||
#define SMBAUXCTL 0xd
|
||||
#define SMLINK_PIN_CTL 0xe
|
||||
#define SMBUS_PIN_CTL 0xf
|
||||
|
||||
/* I801 Hosts Status register bits */
|
||||
#define SMBHSTSTS_BYTE_DONE 0x80
|
||||
#define SMBHSTSTS_INUSE_STS 0x40
|
||||
#define SMBHSTSTS_SMBALERT_STS 0x20
|
||||
#define SMBHSTSTS_FAILED 0x10
|
||||
#define SMBHSTSTS_BUS_ERR 0x08
|
||||
#define SMBHSTSTS_DEV_ERR 0x04
|
||||
#define SMBHSTSTS_INTR 0x02
|
||||
#define SMBHSTSTS_HOST_BUSY 0x01
|
||||
|
||||
/* I801 Host Control register bits */
|
||||
#define SMBHSTCNT_INTREN 0x01
|
||||
#define SMBHSTCNT_KILL 0x02
|
||||
#define SMBHSTCNT_LAST_BYTE 0x20
|
||||
#define SMBHSTCNT_START 0x40
|
||||
#define SMBHSTCNT_PEC_EN 0x80 /* ICH3 and later */
|
||||
|
||||
/* Auxiliary control register bits, ICH4+ only */
|
||||
#define SMBAUXCTL_CRC 1
|
||||
#define SMBAUXCTL_E32B 2
|
||||
|
||||
#define SMBUS_TIMEOUT 100 /* 100 ms */
|
||||
|
||||
struct intel_i2c {
|
||||
u32 base;
|
||||
int running;
|
||||
};
|
||||
|
||||
static int smbus_wait_until_ready(u32 base)
|
||||
{
|
||||
unsigned long ts;
|
||||
u8 byte;
|
||||
|
||||
ts = get_timer(0);
|
||||
do {
|
||||
byte = inb(base + SMBHSTSTAT);
|
||||
if (!(byte & 1))
|
||||
return 0;
|
||||
} while (get_timer(ts) < SMBUS_TIMEOUT);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int smbus_wait_until_done(u32 base)
|
||||
{
|
||||
unsigned long ts;
|
||||
u8 byte;
|
||||
|
||||
ts = get_timer(0);
|
||||
do {
|
||||
byte = inb(base + SMBHSTSTAT);
|
||||
if (!((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0))
|
||||
return 0;
|
||||
} while (get_timer(ts) < SMBUS_TIMEOUT);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int smbus_block_read(u32 base, u8 dev, u8 *buffer,
|
||||
int offset, int len)
|
||||
{
|
||||
u8 buf_temp[32];
|
||||
int count;
|
||||
int i;
|
||||
|
||||
debug("%s (%d): dev=0x%x offs=0x%x len=0x%x\n",
|
||||
__func__, __LINE__, dev, offset, len);
|
||||
if (smbus_wait_until_ready(base) < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Setup transaction */
|
||||
|
||||
/* Reset the data buffer index */
|
||||
inb(base + SMBHSTCTL);
|
||||
|
||||
/* Set the device I'm talking too */
|
||||
outb(((dev & 0x7f) << 1) | 1, base + SMBXMITADD);
|
||||
/* Set the command/address... */
|
||||
outb(offset & 0xff, base + SMBHSTCMD);
|
||||
/* Set up for a block read */
|
||||
outb((inb(base + SMBHSTCTL) & (~(0x7) << 2)) | (0x5 << 2),
|
||||
(base + SMBHSTCTL));
|
||||
/* Clear any lingering errors, so the transaction will run */
|
||||
outb(inb(base + SMBHSTSTAT), base + SMBHSTSTAT);
|
||||
|
||||
/* Start the command */
|
||||
outb((inb(base + SMBHSTCTL) | SMBHSTCNT_START), base + SMBHSTCTL);
|
||||
|
||||
/* Poll for transaction completion */
|
||||
if (smbus_wait_until_done(base) < 0) {
|
||||
printf("SMBUS read transaction timeout (dev=0x%x)\n", dev);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
count = inb(base + SMBHSTDAT0);
|
||||
debug("%s (%d): count=%d (len=%d)\n", __func__, __LINE__, count, len);
|
||||
if (count == 0) {
|
||||
debug("ERROR: len=0 on read\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (count < len) {
|
||||
debug("ERROR: too few bytes read\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (count > 32) {
|
||||
debug("ERROR: count=%d too high\n", count);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read all available bytes from buffer */
|
||||
for (i = 0; i < count; i++)
|
||||
buf_temp[i] = inb(base + SMBBLKDAT);
|
||||
|
||||
memcpy(buffer, buf_temp, len);
|
||||
|
||||
/* Return results of transaction */
|
||||
if (!(inb(base + SMBHSTSTAT) & SMBHSTSTS_INTR))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smbus_block_write(u32 base, u8 dev, u8 *buffer,
|
||||
int offset, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
debug("%s (%d): dev=0x%x offs=0x%x len=0x%x\n",
|
||||
__func__, __LINE__, dev, offset, len);
|
||||
if (smbus_wait_until_ready(base) < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Setup transaction */
|
||||
/* Set the device I'm talking too */
|
||||
outb(((dev & 0x7f) << 1) & ~0x01, base + SMBXMITADD);
|
||||
/* Set the command/address... */
|
||||
outb(offset, base + SMBHSTCMD);
|
||||
/* Set up for a block write */
|
||||
outb((inb(base + SMBHSTCTL) & (~(0x7) << 2)) | (0x5 << 2),
|
||||
(base + SMBHSTCTL));
|
||||
/* Clear any lingering errors, so the transaction will run */
|
||||
outb(inb(base + SMBHSTSTAT), base + SMBHSTSTAT);
|
||||
|
||||
/* Write count in DAT0 register */
|
||||
outb(len, base + SMBHSTDAT0);
|
||||
|
||||
/* Write data bytes... */
|
||||
for (i = 0; i < len; i++)
|
||||
outb(*buffer++, base + SMBBLKDAT);
|
||||
|
||||
/* Start the command */
|
||||
outb((inb(base + SMBHSTCTL) | SMBHSTCNT_START), base + SMBHSTCTL);
|
||||
|
||||
/* Poll for transaction completion */
|
||||
if (smbus_wait_until_done(base) < 0) {
|
||||
printf("SMBUS write transaction timeout (dev=0x%x)\n", dev);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Return results of transaction */
|
||||
if (!(inb(base + SMBHSTSTAT) & SMBHSTSTS_INTR))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct intel_i2c *i2c = dev_get_priv(bus);
|
||||
struct i2c_msg *dmsg, *omsg, dummy;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
|
||||
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 -EIO;
|
||||
}
|
||||
|
||||
omsg = nmsgs == 1 ? &dummy : msg;
|
||||
dmsg = nmsgs == 1 ? msg : msg + 1;
|
||||
|
||||
if (dmsg->flags & I2C_M_RD)
|
||||
return smbus_block_read(i2c->base, dmsg->addr, &dmsg->buf[0],
|
||||
omsg->buf[0], dmsg->len);
|
||||
else
|
||||
return smbus_block_write(i2c->base, dmsg->addr, &dmsg->buf[1],
|
||||
dmsg->buf[0], dmsg->len - 1);
|
||||
}
|
||||
|
||||
static int intel_i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct intel_i2c *i2c = dev_get_priv(bus);
|
||||
u8 buf[4];
|
||||
|
||||
return smbus_block_read(i2c->base, chip_addr, buf, 0, 1);
|
||||
}
|
||||
|
||||
static int intel_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct intel_i2c *priv = dev_get_priv(dev);
|
||||
ulong base;
|
||||
|
||||
/* Save base address from PCI BAR */
|
||||
priv->base = (ulong)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_4,
|
||||
PCI_REGION_IO);
|
||||
base = priv->base;
|
||||
|
||||
/* Set SMBus enable. */
|
||||
dm_pci_write_config8(dev, HOSTC, HST_EN);
|
||||
|
||||
/* Disable interrupts */
|
||||
outb(inb(base + SMBHSTCTL) & ~SMBHSTCNT_INTREN, base + SMBHSTCTL);
|
||||
|
||||
/* Set 32-byte data buffer mode */
|
||||
outb(inb(base + SMBAUXCTL) | SMBAUXCTL_E32B, base + SMBAUXCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_i2c_bind(struct udevice *dev)
|
||||
{
|
||||
static int num_cards __attribute__ ((section(".data")));
|
||||
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, "intel_i2c#%u", num_cards++);
|
||||
device_set_name(dev, name);
|
||||
}
|
||||
|
||||
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,
|
||||
.ops = &intel_i2c_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct intel_i2c),
|
||||
.bind = intel_i2c_bind,
|
||||
.probe = intel_i2c_probe,
|
||||
};
|
||||
|
||||
static struct pci_device_id intel_smbus_pci_supported[] = {
|
||||
/* Intel BayTrail SMBus on the PCI bus */
|
||||
{ PCI_VDEVICE(INTEL, 0x0f12) },
|
||||
/* Intel IvyBridge (Panther Point PCH) SMBus on the PCI bus */
|
||||
{ PCI_VDEVICE(INTEL, 0x1e22) },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(intel_i2c, intel_smbus_pci_supported);
|
||||
@@ -0,0 +1,732 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*
|
||||
* NOTE: This driver should be converted to driver model before June 2017.
|
||||
* Please see doc/driver-model/i2c-howto.txt for instructions.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/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
|
||||
@@ -0,0 +1,357 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* LPC32xx I2C interface driver
|
||||
*
|
||||
* (C) Copyright 2014-2015 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/i2c.h>
|
||||
#include <dm.h>
|
||||
#include <mapmem.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
|
||||
|
||||
/* 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
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static struct lpc32xx_i2c_base *lpc32xx_i2c[] = {
|
||||
(struct lpc32xx_i2c_base *)I2C1_BASE,
|
||||
(struct lpc32xx_i2c_base *)I2C2_BASE,
|
||||
(struct lpc32xx_i2c_base *)(USB_BASE + 0x300)
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Set I2C bus speed */
|
||||
static unsigned int __i2c_set_bus_speed(struct lpc32xx_i2c_base *base,
|
||||
unsigned int speed, unsigned int chip)
|
||||
{
|
||||
int half_period;
|
||||
|
||||
if (speed == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* OTG I2C clock source and CLK registers are different */
|
||||
if (chip == 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, &base->clk_hi);
|
||||
writel(half_period, &base->clk_lo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C init called by cmd_i2c when doing 'i2c reset'. */
|
||||
static void __i2c_init(struct lpc32xx_i2c_base *base,
|
||||
int requested_speed, int slaveadd, unsigned int chip)
|
||||
{
|
||||
/* soft reset (auto-clears) */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &base->ctrl);
|
||||
/* set HI and LO periods for half of the default speed */
|
||||
__i2c_set_bus_speed(base, requested_speed, chip);
|
||||
}
|
||||
|
||||
/* I2C probe called by cmd_i2c when doing 'i2c probe'. */
|
||||
static int __i2c_probe_chip(struct lpc32xx_i2c_base *base, u8 dev)
|
||||
{
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &base->ctrl);
|
||||
while (readl(&base->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,
|
||||
&base->tx);
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&base->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 __i2c_read(struct lpc32xx_i2c_base *base, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
int stat, wlen;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &base->ctrl);
|
||||
while (readl(&base->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, &base->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, &base->tx);
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&base->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &base->stat);
|
||||
}
|
||||
/* do we have to read data at all? */
|
||||
if (length) {
|
||||
/* Address slave in read mode */
|
||||
writel(1 | (dev<<1) | LPC32XX_I2C_TX_START, &base->tx);
|
||||
wlen = length;
|
||||
/* get data */
|
||||
while (length | wlen) {
|
||||
/* read status for TFF and RFE */
|
||||
stat = readl(&base->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, &base->tx);
|
||||
}
|
||||
/* must we, can we read a data byte? */
|
||||
if ((length > 0)
|
||||
& (!(stat & LPC32XX_I2C_STAT_RFE))) {
|
||||
length--;
|
||||
/* read byte */
|
||||
*(data++) = readl(&base->rx);
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&base->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &base->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 __i2c_write(struct lpc32xx_i2c_base *base, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &base->ctrl);
|
||||
while (readl(&base->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, &base->tx);
|
||||
else
|
||||
return 0;
|
||||
/* write address bytes */
|
||||
while (alen) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&base->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, &base->tx);
|
||||
}
|
||||
}
|
||||
while (length) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&base->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, &base->tx);
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&base->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &base->stat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static void lpc32xx_i2c_init(struct i2c_adapter *adap,
|
||||
int requested_speed, int slaveadd)
|
||||
{
|
||||
__i2c_init(lpc32xx_i2c[adap->hwadapnr], requested_speed, slaveadd,
|
||||
adap->hwadapnr);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_probe_chip(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
return __i2c_probe_chip(lpc32xx_i2c[adap->hwadapnr], dev);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
return __i2c_read(lpc32xx_i2c[adap->hwadapnr], dev, addr,
|
||||
alen, data, length);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
return __i2c_write(lpc32xx_i2c[adap->hwadapnr], dev, addr,
|
||||
alen, data, length);
|
||||
}
|
||||
|
||||
static unsigned int lpc32xx_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
return __i2c_set_bus_speed(lpc32xx_i2c[adap->hwadapnr], speed,
|
||||
adap->hwadapnr);
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_0, lpc32xx_i2c_init, lpc32xx_i2c_probe_chip,
|
||||
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, lpc32xx_i2c_init, lpc32xx_i2c_probe_chip,
|
||||
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, lpc32xx_i2c_init, NULL,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
100000,
|
||||
0,
|
||||
2)
|
||||
#else /* CONFIG_DM_I2C */
|
||||
static int lpc32xx_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct lpc32xx_i2c_dev *dev = dev_get_platdata(bus);
|
||||
bus->seq = dev->index;
|
||||
|
||||
__i2c_init(dev->base, dev->speed, 0, dev->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
||||
u32 chip_flags)
|
||||
{
|
||||
struct lpc32xx_i2c_dev *dev = dev_get_platdata(bus);
|
||||
return __i2c_probe_chip(dev->base, chip_addr);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct lpc32xx_i2c_dev *dev = dev_get_platdata(bus);
|
||||
struct i2c_msg *dmsg, *omsg, dummy;
|
||||
uint i = 0, address = 0;
|
||||
|
||||
memset(&dummy, 0, sizeof(struct i2c_msg));
|
||||
|
||||
/* We expect either two messages (one with an offset and one with the
|
||||
* actual 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;
|
||||
|
||||
/* the address is expected to be a uint, not a array. */
|
||||
address = omsg->buf[0];
|
||||
for (i = 1; i < omsg->len; i++)
|
||||
address = (address << 8) + omsg->buf[i];
|
||||
|
||||
if (dmsg->flags & I2C_M_RD)
|
||||
return __i2c_read(dev->base, dmsg->addr, address,
|
||||
omsg->len, dmsg->buf, dmsg->len);
|
||||
else
|
||||
return __i2c_write(dev->base, dmsg->addr, address,
|
||||
omsg->len, dmsg->buf, dmsg->len);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct lpc32xx_i2c_dev *dev = dev_get_platdata(bus);
|
||||
return __i2c_set_bus_speed(dev->base, speed, dev->index);
|
||||
}
|
||||
|
||||
static int lpc32xx_i2c_reset(struct udevice *bus)
|
||||
{
|
||||
struct lpc32xx_i2c_dev *dev = dev_get_platdata(bus);
|
||||
|
||||
__i2c_init(dev->base, dev->speed, 0, dev->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops lpc32xx_i2c_ops = {
|
||||
.xfer = lpc32xx_i2c_xfer,
|
||||
.probe_chip = lpc32xx_i2c_probe_chip,
|
||||
.deblock = lpc32xx_i2c_reset,
|
||||
.set_bus_speed = lpc32xx_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_lpc32xx) = {
|
||||
.id = UCLASS_I2C,
|
||||
.name = "i2c_lpc32xx",
|
||||
.probe = lpc32xx_i2c_probe,
|
||||
.ops = &lpc32xx_i2c_ops,
|
||||
};
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
@@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017 - Beniamino Galvani <b.galvani@gmail.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#define I2C_TIMEOUT_MS 100
|
||||
|
||||
/* Control register fields */
|
||||
#define REG_CTRL_START BIT(0)
|
||||
#define REG_CTRL_ACK_IGNORE BIT(1)
|
||||
#define REG_CTRL_STATUS BIT(2)
|
||||
#define REG_CTRL_ERROR BIT(3)
|
||||
#define REG_CTRL_CLKDIV_SHIFT 12
|
||||
#define REG_CTRL_CLKDIV_MASK GENMASK(21, 12)
|
||||
#define REG_CTRL_CLKDIVEXT_SHIFT 28
|
||||
#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28)
|
||||
|
||||
enum {
|
||||
TOKEN_END = 0,
|
||||
TOKEN_START,
|
||||
TOKEN_SLAVE_ADDR_WRITE,
|
||||
TOKEN_SLAVE_ADDR_READ,
|
||||
TOKEN_DATA,
|
||||
TOKEN_DATA_LAST,
|
||||
TOKEN_STOP,
|
||||
};
|
||||
|
||||
struct i2c_regs {
|
||||
u32 ctrl;
|
||||
u32 slave_addr;
|
||||
u32 tok_list0;
|
||||
u32 tok_list1;
|
||||
u32 tok_wdata0;
|
||||
u32 tok_wdata1;
|
||||
u32 tok_rdata0;
|
||||
u32 tok_rdata1;
|
||||
};
|
||||
|
||||
struct meson_i2c_data {
|
||||
unsigned char div_factor;
|
||||
};
|
||||
|
||||
struct meson_i2c {
|
||||
const struct meson_i2c_data *data;
|
||||
struct clk clk;
|
||||
struct i2c_regs *regs;
|
||||
struct i2c_msg *msg; /* Current I2C message */
|
||||
bool last; /* Whether the message is the last */
|
||||
uint count; /* Number of bytes in the current transfer */
|
||||
uint pos; /* Position of current transfer in message */
|
||||
u32 tokens[2]; /* Sequence of tokens to be written */
|
||||
uint num_tokens; /* Number of tokens to be written */
|
||||
};
|
||||
|
||||
static void meson_i2c_reset_tokens(struct meson_i2c *i2c)
|
||||
{
|
||||
i2c->tokens[0] = 0;
|
||||
i2c->tokens[1] = 0;
|
||||
i2c->num_tokens = 0;
|
||||
}
|
||||
|
||||
static void meson_i2c_add_token(struct meson_i2c *i2c, int token)
|
||||
{
|
||||
if (i2c->num_tokens < 8)
|
||||
i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4);
|
||||
else
|
||||
i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4);
|
||||
|
||||
i2c->num_tokens++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve data for the current transfer (which can be at most 8
|
||||
* bytes) from the device internal buffer.
|
||||
*/
|
||||
static void meson_i2c_get_data(struct meson_i2c *i2c, u8 *buf, int len)
|
||||
{
|
||||
u32 rdata0, rdata1;
|
||||
int i;
|
||||
|
||||
rdata0 = readl(&i2c->regs->tok_rdata0);
|
||||
rdata1 = readl(&i2c->regs->tok_rdata1);
|
||||
|
||||
debug("meson i2c: read data %08x %08x len %d\n", rdata0, rdata1, len);
|
||||
|
||||
for (i = 0; i < min(4, len); i++)
|
||||
*buf++ = (rdata0 >> i * 8) & 0xff;
|
||||
|
||||
for (i = 4; i < min(8, len); i++)
|
||||
*buf++ = (rdata1 >> (i - 4) * 8) & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data for the current transfer (which can be at most 8 bytes)
|
||||
* to the device internal buffer.
|
||||
*/
|
||||
static void meson_i2c_put_data(struct meson_i2c *i2c, u8 *buf, int len)
|
||||
{
|
||||
u32 wdata0 = 0, wdata1 = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < min(4, len); i++)
|
||||
wdata0 |= *buf++ << (i * 8);
|
||||
|
||||
for (i = 4; i < min(8, len); i++)
|
||||
wdata1 |= *buf++ << ((i - 4) * 8);
|
||||
|
||||
writel(wdata0, &i2c->regs->tok_wdata0);
|
||||
writel(wdata1, &i2c->regs->tok_wdata1);
|
||||
|
||||
debug("meson i2c: write data %08x %08x len %d\n", wdata0, wdata1, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the next transfer: pick the next 8 bytes in the remaining
|
||||
* part of message and write tokens and data (if needed) to the
|
||||
* device.
|
||||
*/
|
||||
static void meson_i2c_prepare_xfer(struct meson_i2c *i2c)
|
||||
{
|
||||
bool write = !(i2c->msg->flags & I2C_M_RD);
|
||||
int i;
|
||||
|
||||
i2c->count = min(i2c->msg->len - i2c->pos, 8u);
|
||||
|
||||
for (i = 0; i + 1 < i2c->count; i++)
|
||||
meson_i2c_add_token(i2c, TOKEN_DATA);
|
||||
|
||||
if (i2c->count) {
|
||||
if (write || i2c->pos + i2c->count < i2c->msg->len)
|
||||
meson_i2c_add_token(i2c, TOKEN_DATA);
|
||||
else
|
||||
meson_i2c_add_token(i2c, TOKEN_DATA_LAST);
|
||||
}
|
||||
|
||||
if (write)
|
||||
meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count);
|
||||
|
||||
if (i2c->last && i2c->pos + i2c->count >= i2c->msg->len)
|
||||
meson_i2c_add_token(i2c, TOKEN_STOP);
|
||||
|
||||
writel(i2c->tokens[0], &i2c->regs->tok_list0);
|
||||
writel(i2c->tokens[1], &i2c->regs->tok_list1);
|
||||
}
|
||||
|
||||
static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg)
|
||||
{
|
||||
int token;
|
||||
|
||||
token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ :
|
||||
TOKEN_SLAVE_ADDR_WRITE;
|
||||
|
||||
writel(msg->addr << 1, &i2c->regs->slave_addr);
|
||||
meson_i2c_add_token(i2c, TOKEN_START);
|
||||
meson_i2c_add_token(i2c, token);
|
||||
}
|
||||
|
||||
static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
|
||||
int last)
|
||||
{
|
||||
ulong start;
|
||||
|
||||
debug("meson i2c: %s addr %u len %u\n",
|
||||
(msg->flags & I2C_M_RD) ? "read" : "write",
|
||||
msg->addr, msg->len);
|
||||
|
||||
i2c->msg = msg;
|
||||
i2c->last = last;
|
||||
i2c->pos = 0;
|
||||
i2c->count = 0;
|
||||
|
||||
meson_i2c_reset_tokens(i2c);
|
||||
meson_i2c_do_start(i2c, msg);
|
||||
|
||||
do {
|
||||
meson_i2c_prepare_xfer(i2c);
|
||||
|
||||
/* start the transfer */
|
||||
setbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
|
||||
start = get_timer(0);
|
||||
while (readl(&i2c->regs->ctrl) & REG_CTRL_STATUS) {
|
||||
if (get_timer(start) > I2C_TIMEOUT_MS) {
|
||||
clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
|
||||
debug("meson i2c: timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
meson_i2c_reset_tokens(i2c);
|
||||
clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
|
||||
|
||||
if (readl(&i2c->regs->ctrl) & REG_CTRL_ERROR) {
|
||||
debug("meson i2c: error\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
if ((msg->flags & I2C_M_RD) && i2c->count) {
|
||||
meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos,
|
||||
i2c->count);
|
||||
}
|
||||
i2c->pos += i2c->count;
|
||||
} while (i2c->pos < msg->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct meson_i2c *i2c = dev_get_priv(bus);
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < nmsgs; i++) {
|
||||
ret = meson_i2c_xfer_msg(i2c, msg + i, i == nmsgs - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct meson_i2c *i2c = dev_get_priv(bus);
|
||||
ulong clk_rate;
|
||||
unsigned int div;
|
||||
|
||||
clk_rate = clk_get_rate(&i2c->clk);
|
||||
if (IS_ERR_VALUE(clk_rate))
|
||||
return -EINVAL;
|
||||
|
||||
div = DIV_ROUND_UP(clk_rate, speed * i2c->data->div_factor);
|
||||
|
||||
/* clock divider has 12 bits */
|
||||
if (div >= (1 << 12)) {
|
||||
debug("meson i2c: requested bus frequency too low\n");
|
||||
div = (1 << 12) - 1;
|
||||
}
|
||||
|
||||
clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIV_MASK,
|
||||
(div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT);
|
||||
|
||||
clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIVEXT_MASK,
|
||||
(div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT);
|
||||
|
||||
debug("meson i2c: set clk %u, src %lu, div %u\n", speed, clk_rate, div);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct meson_i2c *i2c = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
i2c->data = (const struct meson_i2c_data *)dev_get_driver_data(bus);
|
||||
|
||||
ret = clk_get_by_index(bus, 0, &i2c->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&i2c->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c->regs = dev_read_addr_ptr(bus);
|
||||
clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops meson_i2c_ops = {
|
||||
.xfer = meson_i2c_xfer,
|
||||
.set_bus_speed = meson_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct meson_i2c_data i2c_meson6_data = {
|
||||
.div_factor = 4,
|
||||
};
|
||||
|
||||
static const struct meson_i2c_data i2c_gxbb_data = {
|
||||
.div_factor = 4,
|
||||
};
|
||||
|
||||
static const struct meson_i2c_data i2c_axg_data = {
|
||||
.div_factor = 3,
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_i2c_ids[] = {
|
||||
{.compatible = "amlogic,meson6-i2c", .data = (ulong)&i2c_meson6_data},
|
||||
{.compatible = "amlogic,meson-gx-i2c", .data = (ulong)&i2c_gxbb_data},
|
||||
{.compatible = "amlogic,meson-gxbb-i2c", .data = (ulong)&i2c_gxbb_data},
|
||||
{.compatible = "amlogic,meson-axg-i2c", .data = (ulong)&i2c_axg_data},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_meson) = {
|
||||
.name = "i2c_meson",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = meson_i2c_ids,
|
||||
.probe = meson_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct meson_i2c),
|
||||
.ops = &meson_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
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 partitioning
|
||||
I2C bus and connect multiple devices with the same address to the same
|
||||
I2C controller where driver handles proper routing to target i2c
|
||||
device. Supported chips are PCA9543, PCA9544, PCA9547, PCA9548 and
|
||||
PCA9646.
|
||||
|
||||
config I2C_MUX_GPIO
|
||||
tristate "GPIO-based I2C multiplexer"
|
||||
depends on I2C_MUX && DM_GPIO
|
||||
help
|
||||
If you say yes to this option, support will be included for
|
||||
a GPIO based I2C multiplexer. This driver provides access to
|
||||
I2C busses connected through a MUX, which is controlled
|
||||
through GPIO pins.
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
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
|
||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
@@ -0,0 +1,146 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#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(dev);
|
||||
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),
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* I2C multiplexer using GPIO API
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* Peng Fan <peng.fan@nxp.com>
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/**
|
||||
* struct i2c_mux_gpio_priv - private data for i2c mux gpio
|
||||
*
|
||||
* @values: the reg value of each child node
|
||||
* @n_values: num of regs
|
||||
* @gpios: the mux-gpios array
|
||||
* @n_gpios: num of gpios in mux-gpios
|
||||
* @idle: the value of idle-state
|
||||
*/
|
||||
struct i2c_mux_gpio_priv {
|
||||
u32 *values;
|
||||
int n_values;
|
||||
struct gpio_desc *gpios;
|
||||
int n_gpios;
|
||||
u32 idle;
|
||||
};
|
||||
|
||||
|
||||
static int i2c_mux_gpio_select(struct udevice *dev, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct i2c_mux_gpio_priv *priv = dev_get_priv(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < priv->n_gpios; i++) {
|
||||
ret = dm_gpio_set_value(&priv->gpios[i], (channel >> i) & 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_deselect(struct udevice *dev, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct i2c_mux_gpio_priv *priv = dev_get_priv(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < priv->n_gpios; i++) {
|
||||
ret = dm_gpio_set_value(&priv->gpios[i], (priv->idle >> i) & 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node = dev_of_offset(dev);
|
||||
struct i2c_mux_gpio_priv *mux = dev_get_priv(dev);
|
||||
struct gpio_desc *gpios;
|
||||
u32 *values;
|
||||
int i = 0, subnode, ret;
|
||||
|
||||
mux->n_values = fdtdec_get_child_count(fdt, node);
|
||||
values = devm_kzalloc(dev, sizeof(*mux->values) * mux->n_values,
|
||||
GFP_KERNEL);
|
||||
if (!values) {
|
||||
dev_err(dev, "Cannot alloc values array");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fdt_for_each_subnode(subnode, fdt, node) {
|
||||
*(values + i) = fdtdec_get_uint(fdt, subnode, "reg", -1);
|
||||
i++;
|
||||
}
|
||||
|
||||
mux->values = values;
|
||||
|
||||
mux->idle = fdtdec_get_uint(fdt, node, "idle-state", -1);
|
||||
|
||||
mux->n_gpios = gpio_get_list_count(dev, "mux-gpios");
|
||||
if (mux->n_gpios < 0) {
|
||||
dev_err(dev, "Missing mux-gpios property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpios = devm_kzalloc(dev, sizeof(struct gpio_desc) * mux->n_gpios,
|
||||
GFP_KERNEL);
|
||||
if (!gpios) {
|
||||
dev_err(dev, "Cannot allocate gpios array\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = gpio_request_list_by_name(dev, "mux-gpios", gpios, mux->n_gpios,
|
||||
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
|
||||
if (ret <= 0) {
|
||||
dev_err(dev, "Failed to request mux-gpios\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mux->gpios = gpios;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_mux_ops i2c_mux_gpio_ops = {
|
||||
.select = i2c_mux_gpio_select,
|
||||
.deselect = i2c_mux_gpio_deselect,
|
||||
};
|
||||
|
||||
static const struct udevice_id i2c_mux_gpio_ids[] = {
|
||||
{ .compatible = "i2c-mux-gpio", },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_mux_gpio) = {
|
||||
.name = "i2c_mux_gpio",
|
||||
.id = UCLASS_I2C_MUX,
|
||||
.of_match = i2c_mux_gpio_ids,
|
||||
.ops = &i2c_mux_gpio_ops,
|
||||
.probe = i2c_mux_gpio_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_mux_gpio_priv),
|
||||
};
|
||||
@@ -0,0 +1,224 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
/**
|
||||
* 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 = dev_read_u32_default(dev, "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)
|
||||
{
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
debug("%s: %s\n", __func__, mux->name);
|
||||
/*
|
||||
* There is no compatible string in the sub-nodes, so we must manually
|
||||
* bind these
|
||||
*/
|
||||
dev_for_each_subnode(node, mux) {
|
||||
struct udevice *dev;
|
||||
const char *name;
|
||||
const char *arrow = "->";
|
||||
char *full_name;
|
||||
int parent_name_len, arrow_len, mux_name_len, name_len;
|
||||
|
||||
name = ofnode_get_name(node);
|
||||
|
||||
/* Calculate lenghts of strings */
|
||||
parent_name_len = strlen(mux->parent->name);
|
||||
arrow_len = strlen(arrow);
|
||||
mux_name_len = strlen(mux->name);
|
||||
name_len = strlen(name);
|
||||
|
||||
full_name = calloc(1, parent_name_len + arrow_len +
|
||||
mux_name_len + arrow_len + name_len + 1);
|
||||
if (!full_name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Compose bus name */
|
||||
strcat(full_name, mux->parent->name);
|
||||
strcat(full_name, arrow);
|
||||
strcat(full_name, mux->name);
|
||||
strcat(full_name, arrow);
|
||||
strcat(full_name, name);
|
||||
|
||||
ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv",
|
||||
full_name, node, &dev);
|
||||
debug(" - bind ret=%d, %s, req_seq %d\n", ret,
|
||||
dev ? dev->name : NULL, dev->req_seq);
|
||||
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;
|
||||
|
||||
/* if parent is of i2c uclass already, we'll take that, otherwise
|
||||
* look if we find an i2c-parent phandle
|
||||
*/
|
||||
if (UCLASS_I2C == device_get_uclass_id(mux->parent)) {
|
||||
priv->i2c_bus = dev_get_parent(mux);
|
||||
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus,
|
||||
priv->i2c_bus->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.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,
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 - 2016 Xilinx, Inc.
|
||||
* Copyright (C) 2017 National Instruments Corp
|
||||
* Written by Michal Simek
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum pca_type {
|
||||
PCA9543,
|
||||
PCA9544,
|
||||
PCA9547,
|
||||
PCA9548,
|
||||
PCA9646
|
||||
};
|
||||
|
||||
struct chip_desc {
|
||||
u8 enable; /* Enable mask in ctl register (used for muxes only) */
|
||||
enum muxtype {
|
||||
pca954x_ismux = 0,
|
||||
pca954x_isswi,
|
||||
} muxtype;
|
||||
u32 width;
|
||||
};
|
||||
|
||||
struct pca954x_priv {
|
||||
u32 addr; /* I2C mux address */
|
||||
u32 width; /* I2C mux width - number of busses */
|
||||
struct gpio_desc gpio_mux_reset;
|
||||
};
|
||||
|
||||
static const struct chip_desc chips[] = {
|
||||
[PCA9543] = {
|
||||
.muxtype = pca954x_isswi,
|
||||
.width = 2,
|
||||
},
|
||||
[PCA9544] = {
|
||||
.enable = 0x4,
|
||||
.muxtype = pca954x_ismux,
|
||||
.width = 4,
|
||||
},
|
||||
[PCA9547] = {
|
||||
.enable = 0x8,
|
||||
.muxtype = pca954x_ismux,
|
||||
.width = 8,
|
||||
},
|
||||
[PCA9548] = {
|
||||
.muxtype = pca954x_isswi,
|
||||
.width = 8,
|
||||
},
|
||||
[PCA9646] = {
|
||||
.muxtype = pca954x_isswi,
|
||||
.width = 4,
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
const struct chip_desc *chip = &chips[dev_get_driver_data(mux)];
|
||||
uchar byte;
|
||||
|
||||
if (chip->muxtype == pca954x_ismux)
|
||||
byte = channel | chip->enable;
|
||||
else
|
||||
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,pca9543", .data = PCA9543 },
|
||||
{ .compatible = "nxp,pca9544", .data = PCA9544 },
|
||||
{ .compatible = "nxp,pca9547", .data = PCA9547 },
|
||||
{ .compatible = "nxp,pca9548", .data = PCA9548 },
|
||||
{ .compatible = "nxp,pca9646", .data = PCA9646 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int pca954x_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct pca954x_priv *priv = dev_get_priv(dev);
|
||||
const struct chip_desc *chip = &chips[dev_get_driver_data(dev)];
|
||||
|
||||
priv->addr = dev_read_u32_default(dev, "reg", 0);
|
||||
if (!priv->addr) {
|
||||
debug("MUX not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
priv->width = chip->width;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int pca954x_probe(struct udevice *dev)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DM_GPIO)) {
|
||||
struct pca954x_priv *priv = dev_get_priv(dev);
|
||||
int err;
|
||||
|
||||
err = gpio_request_by_name(dev, "reset-gpios", 0,
|
||||
&priv->gpio_mux_reset, GPIOD_IS_OUT);
|
||||
|
||||
/* it's optional so only bail if we get a real error */
|
||||
if (err && (err != -ENOENT))
|
||||
return err;
|
||||
|
||||
/* dm will take care of polarity */
|
||||
if (dm_gpio_is_valid(&priv->gpio_mux_reset))
|
||||
dm_gpio_set_value(&priv->gpio_mux_reset, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca954x_remove(struct udevice *dev)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DM_GPIO)) {
|
||||
struct pca954x_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (dm_gpio_is_valid(&priv->gpio_mux_reset))
|
||||
dm_gpio_free(dev, &priv->gpio_mux_reset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(pca954x) = {
|
||||
.name = "pca954x",
|
||||
.id = UCLASS_I2C_MUX,
|
||||
.of_match = pca954x_ids,
|
||||
.probe = pca954x_probe,
|
||||
.remove = pca954x_remove,
|
||||
.ops = &pca954x_ops,
|
||||
.ofdata_to_platdata = pca954x_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct pca954x_priv),
|
||||
};
|
||||
@@ -0,0 +1,604 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (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>
|
||||
*
|
||||
* Back ported to the 8xx platform (from the 8260 platform) by
|
||||
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include "mv_i2c.h"
|
||||
|
||||
/* All transfers are described by this data structure */
|
||||
struct mv_i2c_msg {
|
||||
u8 condition;
|
||||
u8 acknack;
|
||||
u8 direction;
|
||||
u8 data;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARMADA_3700
|
||||
/* Armada 3700 has no padding between the registers */
|
||||
struct mv_i2c {
|
||||
u32 ibmr;
|
||||
u32 idbr;
|
||||
u32 icr;
|
||||
u32 isr;
|
||||
u32 isar;
|
||||
};
|
||||
#else
|
||||
struct mv_i2c {
|
||||
u32 ibmr;
|
||||
u32 pad0;
|
||||
u32 idbr;
|
||||
u32 pad1;
|
||||
u32 icr;
|
||||
u32 pad2;
|
||||
u32 isr;
|
||||
u32 pad3;
|
||||
u32 isar;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy implementation that can be overwritten by a board
|
||||
* specific function
|
||||
*/
|
||||
__weak void i2c_clk_enable(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_reset: - reset the host controller
|
||||
*
|
||||
*/
|
||||
static void i2c_reset(struct mv_i2c *base)
|
||||
{
|
||||
u32 icr_mode;
|
||||
|
||||
/* Save bus mode (standard or fast speed) for later use */
|
||||
icr_mode = readl(&base->icr) & ICR_MODE_MASK;
|
||||
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 */
|
||||
/* set control reg values */
|
||||
writel(I2C_ICR_INIT | icr_mode, &base->icr);
|
||||
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(struct mv_i2c *base, 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
|
||||
*/
|
||||
static int i2c_transfer(struct mv_i2c *base, 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(base, 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(base, 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(base, 0, ISR_ACKNAK))
|
||||
goto transfer_error_ack_missing;
|
||||
break;
|
||||
|
||||
case I2C_READ:
|
||||
|
||||
/* check if bus is not busy */
|
||||
if (!i2c_isr_set_cleared(base, 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(base, 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:
|
||||
debug("i2c_transfer: error: 'msg' is empty\n");
|
||||
ret = -1;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_transmit_timeout:
|
||||
debug("i2c_transfer: error: transmit timeout\n");
|
||||
ret = -2;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_ack_missing:
|
||||
debug("i2c_transfer: error: ACK missing\n");
|
||||
ret = -3;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_receive_timeout:
|
||||
debug("i2c_transfer: error: receive timeout\n");
|
||||
ret = -4;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_illegal_param:
|
||||
debug("i2c_transfer: error: illegal parameters\n");
|
||||
ret = -5;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_bus_busy:
|
||||
debug("i2c_transfer: error: bus is busy\n");
|
||||
ret = -6;
|
||||
goto i2c_transfer_finish;
|
||||
|
||||
i2c_transfer_finish:
|
||||
debug("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr));
|
||||
i2c_reset(base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __i2c_read(struct mv_i2c *base, uchar chip, u8 *addr, int alen,
|
||||
uchar *buffer, int len)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
|
||||
debug("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, *addr, alen, len);
|
||||
|
||||
if (len == 0) {
|
||||
printf("reading zero byte is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_reset(base);
|
||||
|
||||
/* dummy chip address write */
|
||||
debug("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(base, &msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
while (--alen >= 0) {
|
||||
debug("i2c_read: send address byte %02x (alen=%d)\n",
|
||||
*addr, alen);
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr[alen];
|
||||
if (i2c_transfer(base, &msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start read sequence */
|
||||
debug("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(base, &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(base, &msg))
|
||||
return -1;
|
||||
|
||||
*buffer = msg.data;
|
||||
debug("i2c_read: reading byte (%p)=0x%02x\n",
|
||||
buffer, *buffer);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
i2c_reset(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __i2c_write(struct mv_i2c *base, uchar chip, u8 *addr, int alen,
|
||||
uchar *buffer, int len)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
|
||||
debug("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, *addr, alen, len);
|
||||
|
||||
i2c_reset(base);
|
||||
|
||||
/* chip address write */
|
||||
debug("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(base, &msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
while (--alen >= 0) {
|
||||
debug("i2c_read: send address byte %02x (alen=%d)\n",
|
||||
*addr, alen);
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr[alen];
|
||||
if (i2c_transfer(base, &msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* write bytes; send NACK at last byte */
|
||||
while (len--) {
|
||||
debug("i2c_write: writing byte (%p)=0x%02x\n",
|
||||
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(base, &msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_reset(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
|
||||
static struct mv_i2c *base_glob;
|
||||
|
||||
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_glob = (struct mv_i2c *)i2c_regs[bus];
|
||||
current_bus = bus;
|
||||
|
||||
if (!bus_initialized[current_bus]) {
|
||||
i2c_board_init(base_glob);
|
||||
bus_initialized[current_bus] = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* API Functions */
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
base_glob = (struct mv_i2c *)i2c_regs[current_bus];
|
||||
#else
|
||||
base_glob = (struct mv_i2c *)CONFIG_MV_I2C_REG;
|
||||
#endif
|
||||
|
||||
if (speed > 100000)
|
||||
val = ICR_FM;
|
||||
else
|
||||
val = ICR_SM;
|
||||
clrsetbits_le32(&base_glob->icr, ICR_MODE_MASK, val);
|
||||
|
||||
i2c_board_init(base_glob);
|
||||
}
|
||||
|
||||
static int __i2c_probe_chip(struct mv_i2c *base, uchar chip)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
|
||||
i2c_reset(base);
|
||||
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1) + 1;
|
||||
if (i2c_transfer(base, &msg))
|
||||
return -1;
|
||||
|
||||
msg.condition = I2C_COND_STOP;
|
||||
msg.acknack = I2C_ACKNAK_SENDNAK;
|
||||
msg.direction = I2C_READ;
|
||||
msg.data = 0x00;
|
||||
if (i2c_transfer(base, &msg))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
return __i2c_probe_chip(base_glob, chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
u8 addr_bytes[4];
|
||||
|
||||
addr_bytes[0] = (addr >> 0) & 0xFF;
|
||||
addr_bytes[1] = (addr >> 8) & 0xFF;
|
||||
addr_bytes[2] = (addr >> 16) & 0xFF;
|
||||
addr_bytes[3] = (addr >> 24) & 0xFF;
|
||||
|
||||
return __i2c_read(base_glob, chip, addr_bytes, alen, buffer, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
u8 addr_bytes[4];
|
||||
|
||||
addr_bytes[0] = (addr >> 0) & 0xFF;
|
||||
addr_bytes[1] = (addr >> 8) & 0xFF;
|
||||
addr_bytes[2] = (addr >> 16) & 0xFF;
|
||||
addr_bytes[3] = (addr >> 24) & 0xFF;
|
||||
|
||||
return __i2c_write(base_glob, chip, addr_bytes, alen, buffer, len);
|
||||
}
|
||||
|
||||
#else /* CONFIG_DM_I2C */
|
||||
|
||||
struct mv_i2c_priv {
|
||||
struct mv_i2c *base;
|
||||
};
|
||||
|
||||
static int mv_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct mv_i2c_priv *i2c = 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
|
||||
* actual data) or one message (just data or offset/data combined)
|
||||
*/
|
||||
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(i2c->base, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len);
|
||||
else
|
||||
return __i2c_write(i2c->base, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len);
|
||||
}
|
||||
|
||||
static int mv_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct mv_i2c_priv *priv = dev_get_priv(bus);
|
||||
u32 val;
|
||||
|
||||
if (speed > 100000)
|
||||
val = ICR_FM;
|
||||
else
|
||||
val = ICR_SM;
|
||||
clrsetbits_le32(&priv->base->icr, ICR_MODE_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct mv_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->base = (void *)devfdt_get_addr_ptr(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops mv_i2c_ops = {
|
||||
.xfer = mv_i2c_xfer,
|
||||
.set_bus_speed = mv_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id mv_i2c_ids[] = {
|
||||
{ .compatible = "marvell,armada-3700-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_mv) = {
|
||||
.name = "i2c_mv",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = mv_i2c_ids,
|
||||
.probe = mv_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct mv_i2c_priv),
|
||||
.ops = &mv_i2c_ops,
|
||||
};
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2011
|
||||
* Marvell Inc, <www.marvell.com>
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
|
||||
|
||||
#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 */
|
||||
#ifdef CONFIG_ARMADA_3700
|
||||
#define ICR_SM 0x00000 /* Standard Mode */
|
||||
#define ICR_FM 0x10000 /* Fast Mode */
|
||||
#define ICR_MODE_MASK 0x30000 /* Mode mask */
|
||||
#else
|
||||
#define ICR_SM 0x00000 /* Standard Mode */
|
||||
#define ICR_FM 0x08000 /* Fast Mode */
|
||||
#define ICR_MODE_MASK 0x18000 /* Mode mask */
|
||||
#endif
|
||||
|
||||
/* ----- 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
|
||||
@@ -0,0 +1,891 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#ifdef CONFIG_DM_I2C
|
||||
#include <dm.h>
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* Include a file that will provide CONFIG_I2C_MVTWSI_BASE*, and possibly other
|
||||
* settings
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
#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_ARCH_SUNXI)
|
||||
#include <asm/arch/i2c.h>
|
||||
#else
|
||||
#error Driver mvtwsi not supported by SoC or board
|
||||
#endif
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
|
||||
/*
|
||||
* On SUNXI, we get CONFIG_SYS_TCLK from this include, so we want to
|
||||
* always have it.
|
||||
*/
|
||||
#if defined(CONFIG_DM_I2C) && defined(CONFIG_ARCH_SUNXI)
|
||||
#include <asm/arch/i2c.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TWSI register structure
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ARCH_SUNXI
|
||||
|
||||
struct mvtwsi_registers {
|
||||
u32 slave_address;
|
||||
u32 xtnd_slave_addr;
|
||||
u32 data;
|
||||
u32 control;
|
||||
u32 status;
|
||||
u32 baudrate;
|
||||
u32 soft_reset;
|
||||
u32 debug; /* Dummy field for build compatibility with mvebu */
|
||||
};
|
||||
|
||||
#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 reserved0[2];
|
||||
u32 soft_reset;
|
||||
u32 reserved1[27];
|
||||
u32 debug;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DM_I2C
|
||||
struct mvtwsi_i2c_dev {
|
||||
/* TWSI Register base for the device */
|
||||
struct mvtwsi_registers *base;
|
||||
/* Number of the device (determined from cell-index property) */
|
||||
int index;
|
||||
/* The I2C slave address for the device */
|
||||
u8 slaveadd;
|
||||
/* The configured I2C speed in Hz */
|
||||
uint speed;
|
||||
/* The current length of a clock period (depending on speed) */
|
||||
uint tick;
|
||||
};
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
|
||||
/*
|
||||
* enum mvtwsi_ctrl_register_fields - Bit masks for flags in the control
|
||||
* register
|
||||
*/
|
||||
enum mvtwsi_ctrl_register_fields {
|
||||
/* Acknowledge bit */
|
||||
MVTWSI_CONTROL_ACK = 0x00000004,
|
||||
/* Interrupt flag */
|
||||
MVTWSI_CONTROL_IFLG = 0x00000008,
|
||||
/* Stop bit */
|
||||
MVTWSI_CONTROL_STOP = 0x00000010,
|
||||
/* Start bit */
|
||||
MVTWSI_CONTROL_START = 0x00000020,
|
||||
/* I2C enable */
|
||||
MVTWSI_CONTROL_TWSIEN = 0x00000040,
|
||||
/* Interrupt enable */
|
||||
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
|
||||
|
||||
/*
|
||||
* enum mvstwsi_status_values - Possible values of I2C controller's status
|
||||
* register
|
||||
*
|
||||
* Only those statuses expected in normal master operation on
|
||||
* non-10-bit-address devices are specified.
|
||||
*
|
||||
* Every status that's unexpected during normal operation (bus errors,
|
||||
* arbitration losses, missing ACKs...) is passed back to the caller as an error
|
||||
* code.
|
||||
*/
|
||||
enum mvstwsi_status_values {
|
||||
/* START condition transmitted */
|
||||
MVTWSI_STATUS_START = 0x08,
|
||||
/* Repeated START condition transmitted */
|
||||
MVTWSI_STATUS_REPEATED_START = 0x10,
|
||||
/* Address + write bit transmitted, ACK received */
|
||||
MVTWSI_STATUS_ADDR_W_ACK = 0x18,
|
||||
/* Data transmitted, ACK received */
|
||||
MVTWSI_STATUS_DATA_W_ACK = 0x28,
|
||||
/* Address + read bit transmitted, ACK received */
|
||||
MVTWSI_STATUS_ADDR_R_ACK = 0x40,
|
||||
/* Address + read bit transmitted, ACK not received */
|
||||
MVTWSI_STATUS_ADDR_R_NAK = 0x48,
|
||||
/* Data received, ACK transmitted */
|
||||
MVTWSI_STATUS_DATA_R_ACK = 0x50,
|
||||
/* Data received, ACK not transmitted */
|
||||
MVTWSI_STATUS_DATA_R_NAK = 0x58,
|
||||
/* No relevant status */
|
||||
MVTWSI_STATUS_IDLE = 0xF8,
|
||||
};
|
||||
|
||||
/*
|
||||
* enum mvstwsi_ack_flags - Determine whether a read byte should be
|
||||
* acknowledged or not.
|
||||
*/
|
||||
enum mvtwsi_ack_flags {
|
||||
/* Send NAK after received byte */
|
||||
MVTWSI_READ_NAK = 0,
|
||||
/* Send ACK after received byte */
|
||||
MVTWSI_READ_ACK = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* calc_tick() - Calculate the duration of a clock cycle from the I2C speed
|
||||
*
|
||||
* @speed: The speed in Hz to calculate the clock cycle duration for.
|
||||
* @return The duration of a clock cycle in ns.
|
||||
*/
|
||||
inline uint calc_tick(uint speed)
|
||||
{
|
||||
/* One tick = the duration of a period at the specified speed in ns (we
|
||||
* add 100 ns to be on the safe side) */
|
||||
return (1000000000u / speed) + 100;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
|
||||
/*
|
||||
* twsi_get_base() - Get controller register base for specified adapter
|
||||
*
|
||||
* @adap: Adapter to get the register base for.
|
||||
* @return Register base for the specified adapter.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* enum mvtwsi_error_class - types of I2C errors
|
||||
*/
|
||||
enum mvtwsi_error_class {
|
||||
/* The controller returned a different status than expected */
|
||||
MVTWSI_ERROR_WRONG_STATUS = 0x01,
|
||||
/* The controller timed out */
|
||||
MVTWSI_ERROR_TIMEOUT = 0x02,
|
||||
};
|
||||
|
||||
/*
|
||||
* mvtwsi_error() - Build I2C return code from error information
|
||||
*
|
||||
* For debugging purposes, this function packs some information of an occurred
|
||||
* error into a return code. These error codes are returned from I2C API
|
||||
* functions (i2c_{read,write}, dm_i2c_{read,write}, etc.).
|
||||
*
|
||||
* @ec: The error class of the error (enum mvtwsi_error_class).
|
||||
* @lc: The last value of the control register.
|
||||
* @ls: The last value of the status register.
|
||||
* @es: The expected value of the status register.
|
||||
* @return The generated error code.
|
||||
*/
|
||||
inline uint mvtwsi_error(uint ec, uint lc, uint ls, uint es)
|
||||
{
|
||||
return ((ec << 24) & 0xFF000000)
|
||||
| ((lc << 16) & 0x00FF0000)
|
||||
| ((ls << 8) & 0x0000FF00)
|
||||
| (es & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_wait() - Wait for I2C bus interrupt flag and check status, or time out.
|
||||
*
|
||||
* @return Zero if status is as expected, or a non-zero code if either a time
|
||||
* out occurred, or the status was not the expected one.
|
||||
*/
|
||||
static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status,
|
||||
uint tick)
|
||||
{
|
||||
int control, status;
|
||||
int timeout = 1000;
|
||||
|
||||
do {
|
||||
control = readl(&twsi->control);
|
||||
if (control & MVTWSI_CONTROL_IFLG) {
|
||||
/*
|
||||
* On Armada 38x it seems that the controller works as
|
||||
* if it first set the MVTWSI_CONTROL_IFLAG in the
|
||||
* control register and only after that it changed the
|
||||
* status register.
|
||||
* This sometimes caused weird bugs which only appeared
|
||||
* on selected I2C speeds and even then only sometimes.
|
||||
* We therefore add here a simple ndealy(100), which
|
||||
* seems to fix this weird bug.
|
||||
*/
|
||||
ndelay(100);
|
||||
status = readl(&twsi->status);
|
||||
if (status == expected_status)
|
||||
return 0;
|
||||
else
|
||||
return mvtwsi_error(
|
||||
MVTWSI_ERROR_WRONG_STATUS,
|
||||
control, status, expected_status);
|
||||
}
|
||||
ndelay(tick); /* One clock cycle */
|
||||
} while (timeout--);
|
||||
status = readl(&twsi->status);
|
||||
return mvtwsi_error(MVTWSI_ERROR_TIMEOUT, control, status,
|
||||
expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_start() - Assert a START condition on the bus.
|
||||
*
|
||||
* This function is used in both single I2C transactions and inside
|
||||
* back-to-back transactions (repeated starts).
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @expected_status: The I2C bus status expected to be asserted after the
|
||||
* operation completion.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if status is as expected, or a non-zero code if either a time
|
||||
* out occurred or the status was not the expected one.
|
||||
*/
|
||||
static int twsi_start(struct mvtwsi_registers *twsi, int expected_status,
|
||||
uint tick)
|
||||
{
|
||||
/* Assert START */
|
||||
writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_START |
|
||||
MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* Wait for controller to process START */
|
||||
return twsi_wait(twsi, expected_status, tick);
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_send() - Send a byte on the I2C bus.
|
||||
*
|
||||
* The byte may be part of an address byte or data.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @byte: The byte to send.
|
||||
* @expected_status: The I2C bus status expected to be asserted after the
|
||||
* operation completion.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if status is as expected, or a non-zero code if either a time
|
||||
* out occurred or the status was not the expected one.
|
||||
*/
|
||||
static int twsi_send(struct mvtwsi_registers *twsi, u8 byte,
|
||||
int expected_status, uint tick)
|
||||
{
|
||||
/* Write byte to data register for sending */
|
||||
writel(byte, &twsi->data);
|
||||
/* Clear any pending interrupt -- that will cause sending */
|
||||
writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_CLEAR_IFLG,
|
||||
&twsi->control);
|
||||
/* Wait for controller to receive byte, and check ACK */
|
||||
return twsi_wait(twsi, expected_status, tick);
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_recv() - Receive a byte on the I2C bus.
|
||||
*
|
||||
* The static variable mvtwsi_control_flags controls whether we ack or nak.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @byte: The byte to send.
|
||||
* @ack_flag: Flag that determines whether the received byte should
|
||||
* be acknowledged by the controller or not (sent ACK/NAK).
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if status is as expected, or a non-zero code if either a time
|
||||
* out occurred or the status was not the expected one.
|
||||
*/
|
||||
static int twsi_recv(struct mvtwsi_registers *twsi, u8 *byte, int ack_flag,
|
||||
uint tick)
|
||||
{
|
||||
int expected_status, status, control;
|
||||
|
||||
/* Compute expected status based on passed ACK flag */
|
||||
expected_status = ack_flag ? MVTWSI_STATUS_DATA_R_ACK :
|
||||
MVTWSI_STATUS_DATA_R_NAK;
|
||||
/* Acknowledge *previous state*, and launch receive */
|
||||
control = MVTWSI_CONTROL_TWSIEN;
|
||||
control |= ack_flag == MVTWSI_READ_ACK ? MVTWSI_CONTROL_ACK : 0;
|
||||
writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* Wait for controller to receive byte, and assert ACK or NAK */
|
||||
status = twsi_wait(twsi, expected_status, tick);
|
||||
/* If we did receive the expected byte, store it */
|
||||
if (status == 0)
|
||||
*byte = readl(&twsi->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_stop() - Assert a STOP condition on the bus.
|
||||
*
|
||||
* This function is also used to force the bus back to idle state (SDA =
|
||||
* SCL = 1).
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out
|
||||
* occurred.
|
||||
*/
|
||||
static int twsi_stop(struct mvtwsi_registers *twsi, uint tick)
|
||||
{
|
||||
int control, stop_status;
|
||||
int status = 0;
|
||||
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 we can't use twsi_wait() */
|
||||
do {
|
||||
stop_status = readl(&twsi->status);
|
||||
if (stop_status == MVTWSI_STATUS_IDLE)
|
||||
break;
|
||||
ndelay(tick); /* One clock cycle */
|
||||
} while (timeout--);
|
||||
control = readl(&twsi->control);
|
||||
if (stop_status != MVTWSI_STATUS_IDLE)
|
||||
status = mvtwsi_error(MVTWSI_ERROR_TIMEOUT,
|
||||
control, status, MVTWSI_STATUS_IDLE);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_calc_freq() - Compute I2C frequency depending on m and n parameters.
|
||||
*
|
||||
* @n: Parameter 'n' for the frequency calculation algorithm.
|
||||
* @m: Parameter 'm' for the frequency calculation algorithm.
|
||||
* @return The I2C frequency corresponding to the passed m and n parameters.
|
||||
*/
|
||||
static uint twsi_calc_freq(const int n, const int m)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_SUNXI
|
||||
return CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n));
|
||||
#else
|
||||
return CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* twsi_reset() - Reset the I2C controller.
|
||||
*
|
||||
* Resetting the controller also resets the baud rate and slave address, hence
|
||||
* they must be re-established after the reset.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
*/
|
||||
static void twsi_reset(struct mvtwsi_registers *twsi)
|
||||
{
|
||||
/* Reset controller */
|
||||
writel(0, &twsi->soft_reset);
|
||||
/* Wait 2 ms -- this is what the Marvell LSP does */
|
||||
udelay(20000);
|
||||
}
|
||||
|
||||
/*
|
||||
* __twsi_i2c_set_bus_speed() - Set the speed of the I2C controller.
|
||||
*
|
||||
* This function sets baud rate to the highest possible value that does not
|
||||
* exceed the requested rate.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @requested_speed: The desired frequency the controller should run at
|
||||
* in Hz.
|
||||
* @return The actual frequency the controller was configured to.
|
||||
*/
|
||||
static uint __twsi_i2c_set_bus_speed(struct mvtwsi_registers *twsi,
|
||||
uint requested_speed)
|
||||
{
|
||||
uint tmp_speed, highest_speed, n, m;
|
||||
uint baud = 0x44; /* Baud rate after controller reset */
|
||||
|
||||
highest_speed = 0;
|
||||
/* Successively try m, n combinations, and use the combination
|
||||
* resulting in the largest speed that's not above the 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);
|
||||
|
||||
/* Wait for controller for one tick */
|
||||
#ifdef CONFIG_DM_I2C
|
||||
ndelay(calc_tick(highest_speed));
|
||||
#else
|
||||
ndelay(10000);
|
||||
#endif
|
||||
return highest_speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* __twsi_i2c_init() - Initialize the I2C controller.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @speed: The initial frequency the controller should run at
|
||||
* in Hz.
|
||||
* @slaveadd: The I2C address to be set for the I2C master.
|
||||
* @actual_speed: A output parameter that receives the actual frequency
|
||||
* in Hz the controller was set to by the function.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out
|
||||
* occurred.
|
||||
*/
|
||||
static void __twsi_i2c_init(struct mvtwsi_registers *twsi, int speed,
|
||||
int slaveadd, uint *actual_speed)
|
||||
{
|
||||
uint tmp_speed;
|
||||
|
||||
/* Reset controller */
|
||||
twsi_reset(twsi);
|
||||
/* Set speed */
|
||||
tmp_speed = __twsi_i2c_set_bus_speed(twsi, speed);
|
||||
if (actual_speed)
|
||||
*actual_speed = tmp_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 */
|
||||
#ifdef CONFIG_DM_I2C
|
||||
(void) twsi_stop(twsi, calc_tick(*actual_speed));
|
||||
#else
|
||||
(void) twsi_stop(twsi, 10000);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_begin() - Start a I2C transaction.
|
||||
*
|
||||
* Begin a I2C transaction with a given expected start status and chip address.
|
||||
* A START is asserted, and the address byte is sent to the I2C controller. The
|
||||
* expected address status will be derived from the direction bit (bit 0) of
|
||||
* the address byte.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @expected_start_status: The I2C status the controller is expected to
|
||||
* assert after the address byte was sent.
|
||||
* @addr: The address byte to be sent.
|
||||
* @tick: The duration of a clock cycle at the current
|
||||
* I2C speed.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out or
|
||||
* unexpected I2C status occurred.
|
||||
*/
|
||||
static int i2c_begin(struct mvtwsi_registers *twsi, int expected_start_status,
|
||||
u8 addr, uint tick)
|
||||
{
|
||||
int status, expected_addr_status;
|
||||
|
||||
/* Compute the expected address status from the direction bit in
|
||||
* the address byte */
|
||||
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(twsi, expected_start_status, tick);
|
||||
/* Send out the address if the start went well */
|
||||
if (status == 0)
|
||||
status = twsi_send(twsi, addr, expected_addr_status, tick);
|
||||
/* Return 0, or the status of the first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* __twsi_i2c_probe_chip() - Probe the given I2C chip address.
|
||||
*
|
||||
* This function begins a I2C read transaction, does a dummy read and NAKs; if
|
||||
* the procedure succeeds, the chip is considered to be present.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @chip: The chip address to probe.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out or
|
||||
* unexpected I2C status occurred.
|
||||
*/
|
||||
static int __twsi_i2c_probe_chip(struct mvtwsi_registers *twsi, uchar chip,
|
||||
uint tick)
|
||||
{
|
||||
u8 dummy_byte;
|
||||
int status;
|
||||
|
||||
/* Begin i2c read */
|
||||
status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1) | 1, tick);
|
||||
/* Dummy read was accepted: receive byte, but NAK it. */
|
||||
if (status == 0)
|
||||
status = twsi_recv(twsi, &dummy_byte, MVTWSI_READ_NAK, tick);
|
||||
/* Stop transaction */
|
||||
twsi_stop(twsi, tick);
|
||||
/* Return 0, or the status of the first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* __twsi_i2c_read() - Read data from a I2C chip.
|
||||
*
|
||||
* This function begins a I2C write transaction, and transmits the address
|
||||
* bytes; then begins a I2C read transaction, and receives the data bytes.
|
||||
*
|
||||
* NOTE: Some devices want a stop right before the second start, while some
|
||||
* will choke if it is there. Since deciding this is not yet supported in
|
||||
* higher level APIs, we need to make a decision here, and for the moment that
|
||||
* will be a repeated start without a preceding stop.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @chip: The chip address to read from.
|
||||
* @addr: The address bytes to send.
|
||||
* @alen: The length of the address bytes in bytes.
|
||||
* @data: The buffer to receive the data read from the chip (has to have
|
||||
* a size of at least 'length' bytes).
|
||||
* @length: The amount of data to be read from the chip in bytes.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out or
|
||||
* unexpected I2C status occurred.
|
||||
*/
|
||||
static int __twsi_i2c_read(struct mvtwsi_registers *twsi, uchar chip,
|
||||
u8 *addr, int alen, uchar *data, int length,
|
||||
uint tick)
|
||||
{
|
||||
int status = 0;
|
||||
int stop_status;
|
||||
int expected_start = MVTWSI_STATUS_START;
|
||||
|
||||
if (alen > 0) {
|
||||
/* Begin i2c write to send the address bytes */
|
||||
status = i2c_begin(twsi, expected_start, (chip << 1), tick);
|
||||
/* Send address bytes */
|
||||
while ((status == 0) && alen--)
|
||||
status = twsi_send(twsi, addr[alen],
|
||||
MVTWSI_STATUS_DATA_W_ACK, tick);
|
||||
/* Send repeated STARTs after the initial START */
|
||||
expected_start = MVTWSI_STATUS_REPEATED_START;
|
||||
}
|
||||
/* Begin i2c read to receive data bytes */
|
||||
if (status == 0)
|
||||
status = i2c_begin(twsi, expected_start, (chip << 1) | 1, tick);
|
||||
/* Receive actual data bytes; set NAK if we if we have nothing more to
|
||||
* read */
|
||||
while ((status == 0) && length--)
|
||||
status = twsi_recv(twsi, data++,
|
||||
length > 0 ?
|
||||
MVTWSI_READ_ACK : MVTWSI_READ_NAK, tick);
|
||||
/* Stop transaction */
|
||||
stop_status = twsi_stop(twsi, tick);
|
||||
/* Return 0, or the status of the first failure */
|
||||
return status != 0 ? status : stop_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* __twsi_i2c_write() - Send data to a I2C chip.
|
||||
*
|
||||
* This function begins a I2C write transaction, and transmits the address
|
||||
* bytes; then begins a new I2C write transaction, and sends the data bytes.
|
||||
*
|
||||
* @twsi: The MVTWSI register structure to use.
|
||||
* @chip: The chip address to read from.
|
||||
* @addr: The address bytes to send.
|
||||
* @alen: The length of the address bytes in bytes.
|
||||
* @data: The buffer containing the data to be sent to the chip.
|
||||
* @length: The length of data to be sent to the chip in bytes.
|
||||
* @tick: The duration of a clock cycle at the current I2C speed.
|
||||
* @return Zero if the operation succeeded, or a non-zero code if a time out or
|
||||
* unexpected I2C status occurred.
|
||||
*/
|
||||
static int __twsi_i2c_write(struct mvtwsi_registers *twsi, uchar chip,
|
||||
u8 *addr, int alen, uchar *data, int length,
|
||||
uint tick)
|
||||
{
|
||||
int status, stop_status;
|
||||
|
||||
/* Begin i2c write to send first the address bytes, then the
|
||||
* data bytes */
|
||||
status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1), tick);
|
||||
/* Send address bytes */
|
||||
while ((status == 0) && (alen-- > 0))
|
||||
status = twsi_send(twsi, addr[alen], MVTWSI_STATUS_DATA_W_ACK,
|
||||
tick);
|
||||
/* Send data bytes */
|
||||
while ((status == 0) && (length-- > 0))
|
||||
status = twsi_send(twsi, *(data++), MVTWSI_STATUS_DATA_W_ACK,
|
||||
tick);
|
||||
/* Stop transaction */
|
||||
stop_status = twsi_stop(twsi, tick);
|
||||
/* Return 0, or the status of the first failure */
|
||||
return status != 0 ? status : stop_status;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static void twsi_i2c_init(struct i2c_adapter *adap, int speed,
|
||||
int slaveadd)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
__twsi_i2c_init(twsi, speed, slaveadd, NULL);
|
||||
}
|
||||
|
||||
static uint twsi_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
uint requested_speed)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
__twsi_i2c_set_bus_speed(twsi, requested_speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
return __twsi_i2c_probe_chip(twsi, chip, 10000);
|
||||
}
|
||||
|
||||
static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *data, int length)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
u8 addr_bytes[4];
|
||||
|
||||
addr_bytes[0] = (addr >> 0) & 0xFF;
|
||||
addr_bytes[1] = (addr >> 8) & 0xFF;
|
||||
addr_bytes[2] = (addr >> 16) & 0xFF;
|
||||
addr_bytes[3] = (addr >> 24) & 0xFF;
|
||||
|
||||
return __twsi_i2c_read(twsi, chip, addr_bytes, alen, data, length,
|
||||
10000);
|
||||
}
|
||||
|
||||
static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *data, int length)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
u8 addr_bytes[4];
|
||||
|
||||
addr_bytes[0] = (addr >> 0) & 0xFF;
|
||||
addr_bytes[1] = (addr >> 8) & 0xFF;
|
||||
addr_bytes[2] = (addr >> 16) & 0xFF;
|
||||
addr_bytes[3] = (addr >> 24) & 0xFF;
|
||||
|
||||
return __twsi_i2c_write(twsi, chip, addr_bytes, alen, data, length,
|
||||
10000);
|
||||
}
|
||||
|
||||
#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
|
||||
#else /* CONFIG_DM_I2C */
|
||||
|
||||
static int mvtwsi_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
||||
u32 chip_flags)
|
||||
{
|
||||
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
||||
return __twsi_i2c_probe_chip(dev->base, chip_addr, dev->tick);
|
||||
}
|
||||
|
||||
static int mvtwsi_i2c_set_bus_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
||||
|
||||
dev->speed = __twsi_i2c_set_bus_speed(dev->base, speed);
|
||||
dev->tick = calc_tick(dev->speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvtwsi_i2c_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
||||
|
||||
dev->base = devfdt_get_addr_ptr(bus);
|
||||
|
||||
if (!dev->base)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->index = fdtdec_get_int(gd->fdt_blob, dev_of_offset(bus),
|
||||
"cell-index", -1);
|
||||
dev->slaveadd = fdtdec_get_int(gd->fdt_blob, dev_of_offset(bus),
|
||||
"u-boot,i2c-slave-addr", 0x0);
|
||||
dev->speed = fdtdec_get_int(gd->fdt_blob, dev_of_offset(bus),
|
||||
"clock-frequency", 100000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void twsi_disable_i2c_slave(struct mvtwsi_registers *twsi)
|
||||
{
|
||||
clrbits_le32(&twsi->debug, BIT(18));
|
||||
}
|
||||
|
||||
static int mvtwsi_i2c_bind(struct udevice *bus)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = devfdt_get_addr_ptr(bus);
|
||||
|
||||
/* Disable the hidden slave in i2c0 of these platforms */
|
||||
if ((IS_ENABLED(CONFIG_ARMADA_38X) || IS_ENABLED(CONFIG_KIRKWOOD))
|
||||
&& bus->req_seq == 0)
|
||||
twsi_disable_i2c_slave(twsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvtwsi_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
||||
uint actual_speed;
|
||||
|
||||
__twsi_i2c_init(dev->base, dev->speed, dev->slaveadd, &actual_speed);
|
||||
dev->speed = actual_speed;
|
||||
dev->tick = calc_tick(dev->speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvtwsi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct mvtwsi_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
|
||||
* actual data) or one message (just data or offset/data combined) */
|
||||
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 __twsi_i2c_read(dev->base, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len,
|
||||
dev->tick);
|
||||
else
|
||||
return __twsi_i2c_write(dev->base, dmsg->addr, omsg->buf,
|
||||
omsg->len, dmsg->buf, dmsg->len,
|
||||
dev->tick);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops mvtwsi_i2c_ops = {
|
||||
.xfer = mvtwsi_i2c_xfer,
|
||||
.probe_chip = mvtwsi_i2c_probe_chip,
|
||||
.set_bus_speed = mvtwsi_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id mvtwsi_i2c_ids[] = {
|
||||
{ .compatible = "marvell,mv64xxx-i2c", },
|
||||
{ .compatible = "marvell,mv78230-i2c", },
|
||||
{ .compatible = "allwinner,sun6i-a31-i2c", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_mvtwsi) = {
|
||||
.name = "i2c_mvtwsi",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = mvtwsi_i2c_ids,
|
||||
.bind = mvtwsi_i2c_bind,
|
||||
.probe = mvtwsi_i2c_probe,
|
||||
.ofdata_to_platdata = mvtwsi_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct mvtwsi_i2c_dev),
|
||||
.ops = &mvtwsi_i2c_ops,
|
||||
};
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2004-2010
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*/
|
||||
#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 4.9 kernel uses
|
||||
* scll_trim = 7
|
||||
* sclh_trim = 5
|
||||
*
|
||||
* These are the trim values for standard and fast speed
|
||||
*/
|
||||
#ifndef I2C_FASTSPEED_SCLL_TRIM
|
||||
#define I2C_FASTSPEED_SCLL_TRIM 7
|
||||
#endif
|
||||
#ifndef I2C_FASTSPEED_SCLH_TRIM
|
||||
#define I2C_FASTSPEED_SCLH_TRIM 5
|
||||
#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_ */
|
||||
@@ -0,0 +1,373 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* drivers/i2c/rcar_i2c.c
|
||||
*
|
||||
* Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Clock configuration based on Linux i2c-rcar.c:
|
||||
* Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2011-2015 Renesas Electronics Corporation
|
||||
* Copyright (C) 2012-14 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <wait_bit.h>
|
||||
|
||||
#define RCAR_I2C_ICSCR 0x00 /* slave ctrl */
|
||||
#define RCAR_I2C_ICMCR 0x04 /* master ctrl */
|
||||
#define RCAR_I2C_ICMCR_MDBS BIT(7) /* non-fifo mode switch */
|
||||
#define RCAR_I2C_ICMCR_FSCL BIT(6) /* override SCL pin */
|
||||
#define RCAR_I2C_ICMCR_FSDA BIT(5) /* override SDA pin */
|
||||
#define RCAR_I2C_ICMCR_OBPC BIT(4) /* override pins */
|
||||
#define RCAR_I2C_ICMCR_MIE BIT(3) /* master if enable */
|
||||
#define RCAR_I2C_ICMCR_TSBE BIT(2)
|
||||
#define RCAR_I2C_ICMCR_FSB BIT(1) /* force stop bit */
|
||||
#define RCAR_I2C_ICMCR_ESG BIT(0) /* enable start bit gen */
|
||||
#define RCAR_I2C_ICSSR 0x08 /* slave status */
|
||||
#define RCAR_I2C_ICMSR 0x0c /* master status */
|
||||
#define RCAR_I2C_ICMSR_MASK 0x7f
|
||||
#define RCAR_I2C_ICMSR_MNR BIT(6) /* Nack */
|
||||
#define RCAR_I2C_ICMSR_MAL BIT(5) /* Arbitration lost */
|
||||
#define RCAR_I2C_ICMSR_MST BIT(4) /* Stop */
|
||||
#define RCAR_I2C_ICMSR_MDE BIT(3)
|
||||
#define RCAR_I2C_ICMSR_MDT BIT(2)
|
||||
#define RCAR_I2C_ICMSR_MDR BIT(1)
|
||||
#define RCAR_I2C_ICMSR_MAT BIT(0)
|
||||
#define RCAR_I2C_ICSIER 0x10 /* slave irq enable */
|
||||
#define RCAR_I2C_ICMIER 0x14 /* master irq enable */
|
||||
#define RCAR_I2C_ICCCR 0x18 /* clock dividers */
|
||||
#define RCAR_I2C_ICCCR_SCGD_OFF 3
|
||||
#define RCAR_I2C_ICSAR 0x1c /* slave address */
|
||||
#define RCAR_I2C_ICMAR 0x20 /* master address */
|
||||
#define RCAR_I2C_ICRXD_ICTXD 0x24 /* data port */
|
||||
/*
|
||||
* First Bit Setup Cycle (Gen3).
|
||||
* Defines 1st bit delay between SDA and SCL.
|
||||
*/
|
||||
#define RCAR_I2C_ICFBSCR 0x38
|
||||
#define RCAR_I2C_ICFBSCR_TCYC17 0x0f /* 17*Tcyc */
|
||||
|
||||
|
||||
enum rcar_i2c_type {
|
||||
RCAR_I2C_TYPE_GEN2,
|
||||
RCAR_I2C_TYPE_GEN3,
|
||||
};
|
||||
|
||||
struct rcar_i2c_priv {
|
||||
void __iomem *base;
|
||||
struct clk clk;
|
||||
u32 intdelay;
|
||||
u32 icccr;
|
||||
enum rcar_i2c_type type;
|
||||
};
|
||||
|
||||
static int rcar_i2c_finish(struct udevice *dev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = wait_for_bit_le32(priv->base + RCAR_I2C_ICMSR, RCAR_I2C_ICMSR_MST,
|
||||
true, 10, true);
|
||||
|
||||
writel(0, priv->base + RCAR_I2C_ICSSR);
|
||||
writel(0, priv->base + RCAR_I2C_ICMSR);
|
||||
writel(0, priv->base + RCAR_I2C_ICMCR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_i2c_recover(struct udevice *dev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 mcr = RCAR_I2C_ICMCR_MDBS | RCAR_I2C_ICMCR_OBPC;
|
||||
u32 mcra = mcr | RCAR_I2C_ICMCR_FSDA;
|
||||
int i;
|
||||
u32 mstat;
|
||||
|
||||
/* Send 9 SCL pulses */
|
||||
for (i = 0; i < 9; i++) {
|
||||
writel(mcra | RCAR_I2C_ICMCR_FSCL, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
writel(mcra, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
/* Send stop condition */
|
||||
udelay(5);
|
||||
writel(mcra, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
writel(mcr, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
writel(mcr | RCAR_I2C_ICMCR_FSCL, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
writel(mcra | RCAR_I2C_ICMCR_FSCL, priv->base + RCAR_I2C_ICMCR);
|
||||
udelay(5);
|
||||
|
||||
mstat = readl(priv->base + RCAR_I2C_ICMSR);
|
||||
return mstat & RCAR_I2C_ICMCR_FSDA ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_set_addr(struct udevice *dev, u8 chip, u8 read)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 mask = RCAR_I2C_ICMSR_MAT |
|
||||
(read ? RCAR_I2C_ICMSR_MDR : RCAR_I2C_ICMSR_MDE);
|
||||
int ret;
|
||||
|
||||
writel(0, priv->base + RCAR_I2C_ICMIER);
|
||||
writel(RCAR_I2C_ICMCR_MDBS, priv->base + RCAR_I2C_ICMCR);
|
||||
writel(0, priv->base + RCAR_I2C_ICMSR);
|
||||
writel(priv->icccr, priv->base + RCAR_I2C_ICCCR);
|
||||
|
||||
/* Wait for the bus */
|
||||
ret = wait_for_bit_le32(priv->base + RCAR_I2C_ICMCR,
|
||||
RCAR_I2C_ICMCR_FSDA, false, 2, true);
|
||||
if (ret) {
|
||||
if (rcar_i2c_recover(dev)) {
|
||||
dev_err(dev, "Bus busy, aborting\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
writel((chip << 1) | read, priv->base + RCAR_I2C_ICMAR);
|
||||
/* Reset */
|
||||
writel(RCAR_I2C_ICMCR_MDBS | RCAR_I2C_ICMCR_MIE | RCAR_I2C_ICMCR_ESG,
|
||||
priv->base + RCAR_I2C_ICMCR);
|
||||
/* Clear Status */
|
||||
writel(0, priv->base + RCAR_I2C_ICMSR);
|
||||
|
||||
ret = wait_for_bit_le32(priv->base + RCAR_I2C_ICMSR, mask,
|
||||
true, 100, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check NAK */
|
||||
if (readl(priv->base + RCAR_I2C_ICMSR) & RCAR_I2C_ICMSR_MNR)
|
||||
return -EREMOTEIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_read_common(struct udevice *dev, struct i2c_msg *msg)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 icmcr = RCAR_I2C_ICMCR_MDBS | RCAR_I2C_ICMCR_MIE;
|
||||
int i, ret = -EREMOTEIO;
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
if (msg->len - 1 == i)
|
||||
icmcr |= RCAR_I2C_ICMCR_FSB;
|
||||
|
||||
writel(icmcr, priv->base + RCAR_I2C_ICMCR);
|
||||
writel((u32)~RCAR_I2C_ICMSR_MDR, priv->base + RCAR_I2C_ICMSR);
|
||||
|
||||
ret = wait_for_bit_le32(priv->base + RCAR_I2C_ICMSR,
|
||||
RCAR_I2C_ICMSR_MDR, true, 100, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg->buf[i] = readl(priv->base + RCAR_I2C_ICRXD_ICTXD) & 0xff;
|
||||
}
|
||||
|
||||
writel((u32)~RCAR_I2C_ICMSR_MDR, priv->base + RCAR_I2C_ICMSR);
|
||||
|
||||
return rcar_i2c_finish(dev);
|
||||
}
|
||||
|
||||
static int rcar_i2c_write_common(struct udevice *dev, struct i2c_msg *msg)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 icmcr = RCAR_I2C_ICMCR_MDBS | RCAR_I2C_ICMCR_MIE;
|
||||
int i, ret = -EREMOTEIO;
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
writel(msg->buf[i], priv->base + RCAR_I2C_ICRXD_ICTXD);
|
||||
writel(icmcr, priv->base + RCAR_I2C_ICMCR);
|
||||
writel((u32)~RCAR_I2C_ICMSR_MDE, priv->base + RCAR_I2C_ICMSR);
|
||||
|
||||
ret = wait_for_bit_le32(priv->base + RCAR_I2C_ICMSR,
|
||||
RCAR_I2C_ICMSR_MDE, true, 100, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel((u32)~RCAR_I2C_ICMSR_MDE, priv->base + RCAR_I2C_ICMSR);
|
||||
icmcr |= RCAR_I2C_ICMCR_FSB;
|
||||
writel(icmcr, priv->base + RCAR_I2C_ICMCR);
|
||||
|
||||
return rcar_i2c_finish(dev);
|
||||
}
|
||||
|
||||
static int rcar_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
ret = rcar_i2c_set_addr(dev, msg->addr, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = rcar_i2c_read_common(dev, msg);
|
||||
else
|
||||
ret = rcar_i2c_write_common(dev, msg);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_probe_chip(struct udevice *dev, uint addr, uint flags)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* Ignore address 0, slave address */
|
||||
if (addr == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rcar_i2c_set_addr(dev, addr, 1);
|
||||
writel(0, priv->base + RCAR_I2C_ICMSR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_i2c_set_speed(struct udevice *dev, uint bus_freq_hz)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
u32 scgd, cdf, round, ick, sum, scl;
|
||||
unsigned long rate;
|
||||
|
||||
/*
|
||||
* calculate SCL clock
|
||||
* see
|
||||
* ICCCR
|
||||
*
|
||||
* ick = clkp / (1 + CDF)
|
||||
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
||||
*
|
||||
* ick : I2C internal clock < 20 MHz
|
||||
* ticf : I2C SCL falling time
|
||||
* tr : I2C SCL rising time
|
||||
* intd : LSI internal delay
|
||||
* clkp : peripheral_clk
|
||||
* F[] : integer up-valuation
|
||||
*/
|
||||
rate = clk_get_rate(&priv->clk);
|
||||
cdf = rate / 20000000;
|
||||
if (cdf >= 8) {
|
||||
dev_err(dev, "Input clock %lu too high\n", rate);
|
||||
return -EIO;
|
||||
}
|
||||
ick = rate / (cdf + 1);
|
||||
|
||||
/*
|
||||
* it is impossible to calculate large scale
|
||||
* number on u32. separate it
|
||||
*
|
||||
* F[(ticf + tr + intd) * ick] with sum = (ticf + tr + intd)
|
||||
* = F[sum * ick / 1000000000]
|
||||
* = F[(ick / 1000000) * sum / 1000]
|
||||
*/
|
||||
sum = 35 + 200 + priv->intdelay;
|
||||
round = (ick + 500000) / 1000000 * sum;
|
||||
round = (round + 500) / 1000;
|
||||
|
||||
/*
|
||||
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
||||
*
|
||||
* Calculation result (= SCL) should be less than
|
||||
* bus_speed for hardware safety
|
||||
*
|
||||
* We could use something along the lines of
|
||||
* div = ick / (bus_speed + 1) + 1;
|
||||
* scgd = (div - 20 - round + 7) / 8;
|
||||
* scl = ick / (20 + (scgd * 8) + round);
|
||||
* (not fully verified) but that would get pretty involved
|
||||
*/
|
||||
for (scgd = 0; scgd < 0x40; scgd++) {
|
||||
scl = ick / (20 + (scgd * 8) + round);
|
||||
if (scl <= bus_freq_hz)
|
||||
goto scgd_find;
|
||||
}
|
||||
dev_err(dev, "it is impossible to calculate best SCL\n");
|
||||
return -EIO;
|
||||
|
||||
scgd_find:
|
||||
dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
|
||||
scl, bus_freq_hz, clk_get_rate(&priv->clk), round, cdf, scgd);
|
||||
|
||||
priv->icccr = (scgd << RCAR_I2C_ICCCR_SCGD_OFF) | cdf;
|
||||
writel(priv->icccr, priv->base + RCAR_I2C_ICCCR);
|
||||
|
||||
if (priv->type == RCAR_I2C_TYPE_GEN3) {
|
||||
/* Set SCL/SDA delay */
|
||||
writel(RCAR_I2C_ICFBSCR_TCYC17, priv->base + RCAR_I2C_ICFBSCR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->base = dev_read_addr_ptr(dev);
|
||||
priv->intdelay = dev_read_u32_default(dev,
|
||||
"i2c-scl-internal-delay-ns", 5);
|
||||
priv->type = dev_get_driver_data(dev);
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* reset slave mode */
|
||||
writel(0, priv->base + RCAR_I2C_ICSIER);
|
||||
writel(0, priv->base + RCAR_I2C_ICSAR);
|
||||
writel(0, priv->base + RCAR_I2C_ICSCR);
|
||||
writel(0, priv->base + RCAR_I2C_ICSSR);
|
||||
|
||||
/* reset master mode */
|
||||
writel(0, priv->base + RCAR_I2C_ICMIER);
|
||||
writel(0, priv->base + RCAR_I2C_ICMCR);
|
||||
writel(0, priv->base + RCAR_I2C_ICMSR);
|
||||
writel(0, priv->base + RCAR_I2C_ICMAR);
|
||||
|
||||
ret = rcar_i2c_set_speed(dev, 100000);
|
||||
if (ret)
|
||||
clk_disable(&priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops rcar_i2c_ops = {
|
||||
.xfer = rcar_i2c_xfer,
|
||||
.probe_chip = rcar_i2c_probe_chip,
|
||||
.set_bus_speed = rcar_i2c_set_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id rcar_i2c_ids[] = {
|
||||
{ .compatible = "renesas,rcar-gen2-i2c", .data = RCAR_I2C_TYPE_GEN2 },
|
||||
{ .compatible = "renesas,rcar-gen3-i2c", .data = RCAR_I2C_TYPE_GEN3 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_rcar) = {
|
||||
.name = "i2c_rcar",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = rcar_i2c_ids,
|
||||
.probe = rcar_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rcar_i2c_priv),
|
||||
.ops = &rcar_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,272 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Renesas RCar IIC driver
|
||||
*
|
||||
* Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Based on
|
||||
* Copyright (C) 2011, 2013 Renesas Solutions Corp.
|
||||
* Copyright (C) 2011, 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct rcar_iic_priv {
|
||||
void __iomem *base;
|
||||
struct clk clk;
|
||||
u8 iccl;
|
||||
u8 icch;
|
||||
};
|
||||
|
||||
#define RCAR_IIC_ICDR 0x00
|
||||
#define RCAR_IIC_ICCR 0x04
|
||||
#define RCAR_IIC_ICSR 0x08
|
||||
#define RCAR_IIC_ICIC 0x0c
|
||||
#define RCAR_IIC_ICCL 0x10
|
||||
#define RCAR_IIC_ICCH 0x14
|
||||
|
||||
/* ICCR */
|
||||
#define RCAR_IIC_ICCR_ICE BIT(7)
|
||||
#define RCAR_IIC_ICCR_RACK BIT(6)
|
||||
#define RCAR_IIC_ICCR_RTS BIT(4)
|
||||
#define RCAR_IIC_ICCR_BUSY BIT(2)
|
||||
#define RCAR_IIC_ICCR_SCP BIT(0)
|
||||
|
||||
/* ICSR / ICIC */
|
||||
#define RCAR_IC_BUSY BIT(4)
|
||||
#define RCAR_IC_TACK BIT(2)
|
||||
#define RCAR_IC_DTE BIT(0)
|
||||
|
||||
#define IRQ_WAIT 1000
|
||||
|
||||
static void sh_irq_dte(struct udevice *dev)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (RCAR_IC_DTE & readb(priv->base + RCAR_IIC_ICSR))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_irq_dte_with_tack(struct udevice *dev)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
u8 icsr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
icsr = readb(priv->base + RCAR_IIC_ICSR);
|
||||
if (RCAR_IC_DTE & icsr)
|
||||
break;
|
||||
if (RCAR_IC_TACK & icsr)
|
||||
return -ETIMEDOUT;
|
||||
udelay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_irq_busy(struct udevice *dev)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (!(RCAR_IC_BUSY & readb(priv->base + RCAR_IIC_ICSR)))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_iic_set_addr(struct udevice *dev, u8 chip, u8 read)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
clrbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE);
|
||||
setbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE);
|
||||
|
||||
writeb(priv->iccl, priv->base + RCAR_IIC_ICCL);
|
||||
writeb(priv->icch, priv->base + RCAR_IIC_ICCH);
|
||||
writeb(RCAR_IC_TACK, priv->base + RCAR_IIC_ICIC);
|
||||
|
||||
writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RTS | RCAR_IIC_ICCR_BUSY,
|
||||
priv->base + RCAR_IIC_ICCR);
|
||||
sh_irq_dte(dev);
|
||||
|
||||
clrbits_8(priv->base + RCAR_IIC_ICSR, RCAR_IC_TACK);
|
||||
writeb(chip << 1 | read, priv->base + RCAR_IIC_ICDR);
|
||||
return sh_irq_dte_with_tack(dev);
|
||||
}
|
||||
|
||||
static void rcar_iic_finish(struct udevice *dev)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
writeb(0, priv->base + RCAR_IIC_ICSR);
|
||||
clrbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE);
|
||||
}
|
||||
|
||||
static int rcar_iic_read_common(struct udevice *dev, struct i2c_msg *msg)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int i, ret = -EREMOTEIO;
|
||||
|
||||
if (rcar_iic_set_addr(dev, msg->addr, 1) != 0)
|
||||
goto err;
|
||||
|
||||
udelay(10);
|
||||
|
||||
writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_SCP,
|
||||
priv->base + RCAR_IIC_ICCR);
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto err;
|
||||
|
||||
msg->buf[i] = readb(priv->base + RCAR_IIC_ICDR) & 0xff;
|
||||
|
||||
if (msg->len - 1 == i) {
|
||||
writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RACK,
|
||||
priv->base + RCAR_IIC_ICCR);
|
||||
}
|
||||
}
|
||||
|
||||
sh_irq_busy(dev);
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
rcar_iic_finish(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_iic_write_common(struct udevice *dev, struct i2c_msg *msg)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int i, ret = -EREMOTEIO;
|
||||
|
||||
if (rcar_iic_set_addr(dev, msg->addr, 0) != 0)
|
||||
goto err;
|
||||
|
||||
udelay(10);
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
writeb(msg->buf[i], priv->base + RCAR_IIC_ICDR);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (msg->flags & I2C_M_STOP) {
|
||||
writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RTS,
|
||||
priv->base + RCAR_IIC_ICCR);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
sh_irq_busy(dev);
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
rcar_iic_finish(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_iic_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = rcar_iic_read_common(dev, msg);
|
||||
else
|
||||
ret = rcar_iic_write_common(dev, msg);
|
||||
|
||||
if (ret)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_iic_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
const unsigned int ratio_high = 4;
|
||||
const unsigned int ratio_low = 5;
|
||||
int clkrate, denom;
|
||||
|
||||
clkrate = clk_get_rate(&priv->clk);
|
||||
if (clkrate < 0)
|
||||
return clkrate;
|
||||
|
||||
/*
|
||||
* Calculate the value for ICCL and ICCH. From the data sheet:
|
||||
* iccl = (p-clock / transfer-rate) * (L / (L + H))
|
||||
* icch = (p clock / transfer rate) * (H / (L + H))
|
||||
* where L and H are the SCL low and high ratio.
|
||||
*/
|
||||
denom = speed * (ratio_high + ratio_low);
|
||||
priv->iccl = DIV_ROUND_CLOSEST(clkrate * ratio_low, denom);
|
||||
priv->icch = DIV_ROUND_CLOSEST(clkrate * ratio_high, denom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_iic_probe_chip(struct udevice *dev, uint addr, uint flags)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
rcar_iic_set_addr(dev, addr, 1);
|
||||
writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_SCP,
|
||||
priv->base + RCAR_IIC_ICCR);
|
||||
ret = sh_irq_dte_with_tack(dev);
|
||||
rcar_iic_finish(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_iic_probe(struct udevice *dev)
|
||||
{
|
||||
struct rcar_iic_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->base = dev_read_addr_ptr(dev);
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rcar_iic_finish(dev);
|
||||
|
||||
return rcar_iic_set_speed(dev, 100000);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops rcar_iic_ops = {
|
||||
.xfer = rcar_iic_xfer,
|
||||
.probe_chip = rcar_iic_probe_chip,
|
||||
.set_bus_speed = rcar_iic_set_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id rcar_iic_ids[] = {
|
||||
{ .compatible = "renesas,rmobile-iic" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(iic_rcar) = {
|
||||
.name = "iic_rcar",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = rcar_iic_ids,
|
||||
.probe = rcar_iic_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rcar_iic_priv),
|
||||
.ops = &rcar_iic_ops,
|
||||
};
|
||||
@@ -0,0 +1,494 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2008-2014 Rockchip Electronics
|
||||
* Peter, Software Engineering, <superpeter.cai@gmail.com>.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-rockchip/clock.h>
|
||||
#include <asm/arch-rockchip/i2c.h>
|
||||
#include <asm/arch-rockchip/periph.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
enum {
|
||||
RK_I2C_LEGACY,
|
||||
RK_I2C_NEW,
|
||||
};
|
||||
|
||||
/**
|
||||
* @controller_type: i2c controller type
|
||||
*/
|
||||
struct rk_i2c_soc_data {
|
||||
int controller_type;
|
||||
};
|
||||
|
||||
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;
|
||||
bool snd_chunk = false;
|
||||
|
||||
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;
|
||||
bytes_xferred = 32;
|
||||
} else {
|
||||
/*
|
||||
* The hw can read up to 32 bytes at a time. If we need
|
||||
* more than one chunk, send an ACK after the last byte.
|
||||
*/
|
||||
con = I2C_CON_EN | I2C_CON_LASTACK;
|
||||
bytes_xferred = bytes_remain_len;
|
||||
}
|
||||
words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
|
||||
|
||||
/*
|
||||
* make sure we are in plain RX mode if we read a second chunk
|
||||
*/
|
||||
if (snd_chunk)
|
||||
con |= I2C_CON_MOD(I2C_MODE_RX);
|
||||
else
|
||||
con |= I2C_CON_MOD(I2C_MODE_TRX);
|
||||
|
||||
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;
|
||||
snd_chunk = true;
|
||||
debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
|
||||
}
|
||||
|
||||
i2c_exit:
|
||||
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 = RK_I2C_FIFO_SIZE;
|
||||
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 && pbuf == buf) {
|
||||
txdata |= (chip << 1);
|
||||
} else if (i == 0 && j <= r_len && pbuf == buf) {
|
||||
txdata |= (reg &
|
||||
(0xff << ((j - 1) * 8))) << 8;
|
||||
} else {
|
||||
txdata |= (*pbuf++)<<(j * 8);
|
||||
}
|
||||
}
|
||||
writel(txdata, ®s->txdata[i]);
|
||||
debug("I2c Write TXDATA[%d] = 0x%08x\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_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;
|
||||
}
|
||||
}
|
||||
|
||||
rk_i2c_send_stop_bit(i2c);
|
||||
rk_i2c_disable(i2c);
|
||||
|
||||
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);
|
||||
struct rk_i2c_soc_data *soc_data;
|
||||
struct udevice *pinctrl;
|
||||
int bus_nr;
|
||||
int ret;
|
||||
|
||||
priv->regs = dev_read_addr_ptr(bus);
|
||||
|
||||
soc_data = (struct rk_i2c_soc_data*)dev_get_driver_data(bus);
|
||||
|
||||
if (soc_data->controller_type == RK_I2C_LEGACY) {
|
||||
ret = dev_read_alias_seq(bus, &bus_nr);
|
||||
if (ret < 0) {
|
||||
debug("%s: Could not get alias for %s: %d\n",
|
||||
__func__, bus->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
|
||||
if (ret) {
|
||||
debug("%s: Cannot find pinctrl device\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pinctrl will switch I2C to new type */
|
||||
ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_I2C0 + bus_nr);
|
||||
if (ret) {
|
||||
debug("%s: Failed to switch I2C to new type %s: %d\n",
|
||||
__func__, bus->name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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 rk_i2c_soc_data rk3066_soc_data = {
|
||||
.controller_type = RK_I2C_LEGACY,
|
||||
};
|
||||
|
||||
static const struct rk_i2c_soc_data rk3188_soc_data = {
|
||||
.controller_type = RK_I2C_LEGACY,
|
||||
};
|
||||
|
||||
static const struct rk_i2c_soc_data rk3228_soc_data = {
|
||||
.controller_type = RK_I2C_NEW,
|
||||
};
|
||||
|
||||
static const struct rk_i2c_soc_data rk3288_soc_data = {
|
||||
.controller_type = RK_I2C_NEW,
|
||||
};
|
||||
|
||||
static const struct rk_i2c_soc_data rk3328_soc_data = {
|
||||
.controller_type = RK_I2C_NEW,
|
||||
};
|
||||
|
||||
static const struct rk_i2c_soc_data rk3399_soc_data = {
|
||||
.controller_type = RK_I2C_NEW,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_i2c_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3066-i2c",
|
||||
.data = (ulong)&rk3066_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3188-i2c",
|
||||
.data = (ulong)&rk3188_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3228-i2c",
|
||||
.data = (ulong)&rk3228_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3288-i2c",
|
||||
.data = (ulong)&rk3288_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3328-i2c",
|
||||
.data = (ulong)&rk3328_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3399-i2c",
|
||||
.data = (ulong)&rk3399_soc_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,346 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2002
|
||||
* David Mueller, ELSOFT AG, d.mueller@elsoft.ch
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#else
|
||||
#include <asm/arch/s3c24x0_cpu.h>
|
||||
#endif
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include "s3c24x0_i2c.h"
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_S3C24X0_SLAVE
|
||||
#define SYS_I2C_S3C24X0_SLAVE_ADDR 0
|
||||
#else
|
||||
#define SYS_I2C_S3C24X0_SLAVE_ADDR CONFIG_SYS_I2C_S3C24X0_SLAVE
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* Wait til the byte transfer is completed.
|
||||
*
|
||||
* @param i2c- pointer to the appropriate i2c register bank.
|
||||
* @return I2C_OK, if transmission was ACKED
|
||||
* I2C_NACK, if transmission was NACKED
|
||||
* I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS
|
||||
*/
|
||||
|
||||
static int WaitForXfer(struct s3c24x0_i2c *i2c)
|
||||
{
|
||||
ulong start_time = get_timer(0);
|
||||
|
||||
do {
|
||||
if (readl(&i2c->iiccon) & I2CCON_IRPND)
|
||||
return (readl(&i2c->iicstat) & I2CSTAT_NACK) ?
|
||||
I2C_NACK : I2C_OK;
|
||||
} while (get_timer(start_time) < I2C_TIMEOUT_MS);
|
||||
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
|
||||
static void read_write_byte(struct s3c24x0_i2c *i2c)
|
||||
{
|
||||
clrbits_le32(&i2c->iiccon, I2CCON_IRPND);
|
||||
}
|
||||
|
||||
static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
|
||||
{
|
||||
ulong freq, pres = 16, div;
|
||||
#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
|
||||
freq = get_i2c_clk();
|
||||
#else
|
||||
freq = get_PCLK();
|
||||
#endif
|
||||
/* calculate prescaler and divisor values */
|
||||
if ((freq / pres / (16 + 1)) > speed)
|
||||
/* set prescaler to 512 */
|
||||
pres = 512;
|
||||
|
||||
div = 0;
|
||||
while ((freq / pres / (div + 1)) > speed)
|
||||
div++;
|
||||
|
||||
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
|
||||
writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
|
||||
|
||||
/* init to SLAVE REVEIVE and set slaveaddr */
|
||||
writel(0, &i2c->iicstat);
|
||||
writel(slaveadd, &i2c->iicadd);
|
||||
/* program Master Transmit (and implicit STOP) */
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
}
|
||||
|
||||
static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->clock_frequency = speed;
|
||||
|
||||
i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency,
|
||||
SYS_I2C_S3C24X0_SLAVE_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int i2c_transfer(struct s3c24x0_i2c *i2c,
|
||||
unsigned char cmd_type,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char addr_len,
|
||||
unsigned char data[],
|
||||
unsigned short data_len)
|
||||
{
|
||||
int i = 0, result;
|
||||
ulong start_time = get_timer(0);
|
||||
|
||||
if (data == 0 || data_len == 0) {
|
||||
/*Don't support data transfer of no length or to address 0 */
|
||||
debug("i2c_transfer: bad call\n");
|
||||
return I2C_NOK;
|
||||
}
|
||||
|
||||
while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
|
||||
if (get_timer(start_time) > I2C_TIMEOUT_MS)
|
||||
return I2C_NOK_TOUT;
|
||||
}
|
||||
|
||||
writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
|
||||
|
||||
/* Get the slave chip address going */
|
||||
writel(chip, &i2c->iicds);
|
||||
if ((cmd_type == I2C_WRITE) || (addr && addr_len))
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
else
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
|
||||
/* Wait for chip address to transmit. */
|
||||
result = WaitForXfer(i2c);
|
||||
if (result != I2C_OK)
|
||||
goto bailout;
|
||||
|
||||
/* If register address needs to be transmitted - do it now. */
|
||||
if (addr && addr_len) {
|
||||
while ((i < addr_len) && (result == I2C_OK)) {
|
||||
writel(addr[i++], &i2c->iicds);
|
||||
read_write_byte(i2c);
|
||||
result = WaitForXfer(i2c);
|
||||
}
|
||||
i = 0;
|
||||
if (result != I2C_OK)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
switch (cmd_type) {
|
||||
case I2C_WRITE:
|
||||
while ((i < data_len) && (result == I2C_OK)) {
|
||||
writel(data[i++], &i2c->iicds);
|
||||
read_write_byte(i2c);
|
||||
result = WaitForXfer(i2c);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_READ:
|
||||
if (addr && addr_len) {
|
||||
/*
|
||||
* Register address has been sent, now send slave chip
|
||||
* address again to start the actual read transaction.
|
||||
*/
|
||||
writel(chip, &i2c->iicds);
|
||||
|
||||
/* Generate a re-START. */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
read_write_byte(i2c);
|
||||
result = WaitForXfer(i2c);
|
||||
|
||||
if (result != I2C_OK)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
while ((i < data_len) && (result == I2C_OK)) {
|
||||
/* disable ACK for final READ */
|
||||
if (i == data_len - 1)
|
||||
writel(readl(&i2c->iiccon)
|
||||
& ~I2CCON_ACKGEN,
|
||||
&i2c->iiccon);
|
||||
read_write_byte(i2c);
|
||||
result = WaitForXfer(i2c);
|
||||
data[i++] = readl(&i2c->iicds);
|
||||
}
|
||||
if (result == I2C_NACK)
|
||||
result = I2C_OK; /* Normal terminated read. */
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("i2c_transfer: bad call\n");
|
||||
result = I2C_NOK;
|
||||
break;
|
||||
}
|
||||
|
||||
bailout:
|
||||
/* Send STOP. */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
read_write_byte(i2c);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
uchar buf[1];
|
||||
int ret;
|
||||
|
||||
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).
|
||||
*/
|
||||
ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1, 0, 0, buf, 1);
|
||||
|
||||
return ret != I2C_OK;
|
||||
}
|
||||
|
||||
static int s3c24x0_do_msg(struct s3c24x0_i2c_bus *i2c_bus, struct i2c_msg *msg,
|
||||
int seq)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = i2c_bus->regs;
|
||||
bool is_read = msg->flags & I2C_M_RD;
|
||||
uint status;
|
||||
uint addr;
|
||||
int ret, i;
|
||||
|
||||
if (!seq)
|
||||
setbits_le32(&i2c->iiccon, I2CCON_ACKGEN);
|
||||
|
||||
/* Get the slave chip address going */
|
||||
addr = msg->addr << 1;
|
||||
writel(addr, &i2c->iicds);
|
||||
status = I2C_TXRX_ENA | I2C_START_STOP;
|
||||
if (is_read)
|
||||
status |= I2C_MODE_MR;
|
||||
else
|
||||
status |= I2C_MODE_MT;
|
||||
writel(status, &i2c->iicstat);
|
||||
if (seq)
|
||||
read_write_byte(i2c);
|
||||
|
||||
/* Wait for chip address to transmit */
|
||||
ret = WaitForXfer(i2c);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (is_read) {
|
||||
for (i = 0; !ret && i < msg->len; i++) {
|
||||
/* disable ACK for final READ */
|
||||
if (i == msg->len - 1)
|
||||
clrbits_le32(&i2c->iiccon, I2CCON_ACKGEN);
|
||||
read_write_byte(i2c);
|
||||
ret = WaitForXfer(i2c);
|
||||
msg->buf[i] = readl(&i2c->iicds);
|
||||
}
|
||||
if (ret == I2C_NACK)
|
||||
ret = I2C_OK; /* Normal terminated read */
|
||||
} else {
|
||||
for (i = 0; !ret && i < msg->len; i++) {
|
||||
writel(msg->buf[i], &i2c->iicds);
|
||||
read_write_byte(i2c);
|
||||
ret = WaitForXfer(i2c);
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
struct s3c24x0_i2c *i2c = i2c_bus->regs;
|
||||
ulong start_time;
|
||||
int ret, i;
|
||||
|
||||
start_time = get_timer(0);
|
||||
while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
|
||||
if (get_timer(start_time) > I2C_TIMEOUT_MS) {
|
||||
debug("Timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
for (ret = 0, i = 0; !ret && i < nmsgs; i++)
|
||||
ret = s3c24x0_do_msg(i2c_bus, &msg[i], i);
|
||||
|
||||
/* Send STOP */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
read_write_byte(i2c);
|
||||
|
||||
return ret ? -EREMOTEIO : 0;
|
||||
}
|
||||
|
||||
static int s3c_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
int node;
|
||||
|
||||
node = dev_of_offset(dev);
|
||||
|
||||
i2c_bus->regs = (struct s3c24x0_i2c *)devfdt_get_addr(dev);
|
||||
|
||||
i2c_bus->id = pinmux_decode_periph_id(blob, node);
|
||||
|
||||
i2c_bus->clock_frequency = fdtdec_get_int(blob, node,
|
||||
"clock-frequency", 100000);
|
||||
i2c_bus->node = node;
|
||||
i2c_bus->bus_num = dev->seq;
|
||||
|
||||
exynos_pinmux_config(i2c_bus->id, 0);
|
||||
|
||||
i2c_bus->active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops s3c_i2c_ops = {
|
||||
.xfer = s3c24x0_i2c_xfer,
|
||||
.probe_chip = s3c24x0_i2c_probe,
|
||||
.set_bus_speed = s3c24x0_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id s3c_i2c_ids[] = {
|
||||
{ .compatible = "samsung,s3c2440-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_s3c) = {
|
||||
.name = "i2c_s3c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = s3c_i2c_ids,
|
||||
.ofdata_to_platdata = s3c_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus),
|
||||
.ops = &s3c_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
#define I2C_WRITE 0
|
||||
#define I2C_READ 1
|
||||
|
||||
#define I2C_OK 0
|
||||
#define I2C_NOK 1
|
||||
#define I2C_NACK 2
|
||||
#define I2C_NOK_LA 3 /* Lost arbitration */
|
||||
#define I2C_NOK_TOUT 4 /* time out */
|
||||
|
||||
/* S3C I2C Controller bits */
|
||||
#define I2CSTAT_BSY 0x20 /* Busy bit */
|
||||
#define I2CSTAT_NACK 0x01 /* Nack bit */
|
||||
#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */
|
||||
#define I2CCON_IRPND 0x10 /* Interrupt pending bit */
|
||||
#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */
|
||||
#define I2C_MODE_MR 0x80 /* Master Receive Mode */
|
||||
#define I2C_START_STOP 0x20 /* START / STOP */
|
||||
#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
|
||||
|
||||
#define I2C_TIMEOUT_MS 10 /* 10 ms */
|
||||
|
||||
#endif /* _S3C24X0_I2C_H */
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Simulate an I2C port
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/test.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
*opsp = NULL;
|
||||
plat = dev_get_parent_platdata(dev);
|
||||
if (!plat->emul) {
|
||||
ret = i2c_emul_find(dev, &plat->emul);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
*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),
|
||||
};
|
||||
@@ -0,0 +1,310 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2011, 2013 Renesas Solutions Corp.
|
||||
* Copyright (C) 2011, 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
*
|
||||
* NOTE: This driver should be converted to driver model before June 2017.
|
||||
* Please see doc/driver-model/i2c-howto.txt for instructions.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,515 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (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.
|
||||
*
|
||||
* This has been changed substantially by Gerald Van Baren, Custom IDEAS,
|
||||
* vanbaren@cideas.com. It was heavily influenced by LiMon, written by
|
||||
* Neil Russell.
|
||||
*
|
||||
* NOTE: This driver should be converted to driver model before June 2017.
|
||||
* Please see doc/driver-model/i2c-howto.txt for instructions.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#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
|
||||
#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
|
||||
# define I2C_SOFT_DECLARATIONS
|
||||
#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
|
||||
@@ -0,0 +1,881 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017 STMicroelectronics
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <reset.h>
|
||||
|
||||
#include <dm/device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/* STM32 I2C registers */
|
||||
struct stm32_i2c_regs {
|
||||
u32 cr1; /* I2C control register 1 */
|
||||
u32 cr2; /* I2C control register 2 */
|
||||
u32 oar1; /* I2C own address 1 register */
|
||||
u32 oar2; /* I2C own address 2 register */
|
||||
u32 timingr; /* I2C timing register */
|
||||
u32 timeoutr; /* I2C timeout register */
|
||||
u32 isr; /* I2C interrupt and status register */
|
||||
u32 icr; /* I2C interrupt clear register */
|
||||
u32 pecr; /* I2C packet error checking register */
|
||||
u32 rxdr; /* I2C receive data register */
|
||||
u32 txdr; /* I2C transmit data register */
|
||||
};
|
||||
|
||||
#define STM32_I2C_CR1 0x00
|
||||
#define STM32_I2C_CR2 0x04
|
||||
#define STM32_I2C_TIMINGR 0x10
|
||||
#define STM32_I2C_ISR 0x18
|
||||
#define STM32_I2C_ICR 0x1C
|
||||
#define STM32_I2C_RXDR 0x24
|
||||
#define STM32_I2C_TXDR 0x28
|
||||
|
||||
/* STM32 I2C control 1 */
|
||||
#define STM32_I2C_CR1_ANFOFF BIT(12)
|
||||
#define STM32_I2C_CR1_ERRIE BIT(7)
|
||||
#define STM32_I2C_CR1_TCIE BIT(6)
|
||||
#define STM32_I2C_CR1_STOPIE BIT(5)
|
||||
#define STM32_I2C_CR1_NACKIE BIT(4)
|
||||
#define STM32_I2C_CR1_ADDRIE BIT(3)
|
||||
#define STM32_I2C_CR1_RXIE BIT(2)
|
||||
#define STM32_I2C_CR1_TXIE BIT(1)
|
||||
#define STM32_I2C_CR1_PE BIT(0)
|
||||
|
||||
/* STM32 I2C control 2 */
|
||||
#define STM32_I2C_CR2_AUTOEND BIT(25)
|
||||
#define STM32_I2C_CR2_RELOAD BIT(24)
|
||||
#define STM32_I2C_CR2_NBYTES_MASK GENMASK(23, 16)
|
||||
#define STM32_I2C_CR2_NBYTES(n) ((n & 0xff) << 16)
|
||||
#define STM32_I2C_CR2_NACK BIT(15)
|
||||
#define STM32_I2C_CR2_STOP BIT(14)
|
||||
#define STM32_I2C_CR2_START BIT(13)
|
||||
#define STM32_I2C_CR2_HEAD10R BIT(12)
|
||||
#define STM32_I2C_CR2_ADD10 BIT(11)
|
||||
#define STM32_I2C_CR2_RD_WRN BIT(10)
|
||||
#define STM32_I2C_CR2_SADD10_MASK GENMASK(9, 0)
|
||||
#define STM32_I2C_CR2_SADD10(n) (n & STM32_I2C_CR2_SADD10_MASK)
|
||||
#define STM32_I2C_CR2_SADD7_MASK GENMASK(7, 1)
|
||||
#define STM32_I2C_CR2_SADD7(n) ((n & 0x7f) << 1)
|
||||
#define STM32_I2C_CR2_RESET_MASK (STM32_I2C_CR2_HEAD10R \
|
||||
| STM32_I2C_CR2_NBYTES_MASK \
|
||||
| STM32_I2C_CR2_SADD7_MASK \
|
||||
| STM32_I2C_CR2_RELOAD \
|
||||
| STM32_I2C_CR2_RD_WRN)
|
||||
|
||||
/* STM32 I2C Interrupt Status */
|
||||
#define STM32_I2C_ISR_BUSY BIT(15)
|
||||
#define STM32_I2C_ISR_ARLO BIT(9)
|
||||
#define STM32_I2C_ISR_BERR BIT(8)
|
||||
#define STM32_I2C_ISR_TCR BIT(7)
|
||||
#define STM32_I2C_ISR_TC BIT(6)
|
||||
#define STM32_I2C_ISR_STOPF BIT(5)
|
||||
#define STM32_I2C_ISR_NACKF BIT(4)
|
||||
#define STM32_I2C_ISR_ADDR BIT(3)
|
||||
#define STM32_I2C_ISR_RXNE BIT(2)
|
||||
#define STM32_I2C_ISR_TXIS BIT(1)
|
||||
#define STM32_I2C_ISR_TXE BIT(0)
|
||||
#define STM32_I2C_ISR_ERRORS (STM32_I2C_ISR_BERR \
|
||||
| STM32_I2C_ISR_ARLO)
|
||||
|
||||
/* STM32 I2C Interrupt Clear */
|
||||
#define STM32_I2C_ICR_ARLOCF BIT(9)
|
||||
#define STM32_I2C_ICR_BERRCF BIT(8)
|
||||
#define STM32_I2C_ICR_STOPCF BIT(5)
|
||||
#define STM32_I2C_ICR_NACKCF BIT(4)
|
||||
|
||||
/* STM32 I2C Timing */
|
||||
#define STM32_I2C_TIMINGR_PRESC(n) ((n & 0xf) << 28)
|
||||
#define STM32_I2C_TIMINGR_SCLDEL(n) ((n & 0xf) << 20)
|
||||
#define STM32_I2C_TIMINGR_SDADEL(n) ((n & 0xf) << 16)
|
||||
#define STM32_I2C_TIMINGR_SCLH(n) ((n & 0xff) << 8)
|
||||
#define STM32_I2C_TIMINGR_SCLL(n) (n & 0xff)
|
||||
|
||||
#define STM32_I2C_MAX_LEN 0xff
|
||||
|
||||
#define STM32_I2C_DNF_DEFAULT 0
|
||||
#define STM32_I2C_DNF_MAX 16
|
||||
|
||||
#define STM32_I2C_ANALOG_FILTER_ENABLE 1
|
||||
#define STM32_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
|
||||
#define STM32_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
|
||||
|
||||
#define STM32_I2C_RISE_TIME_DEFAULT 25 /* ns */
|
||||
#define STM32_I2C_FALL_TIME_DEFAULT 10 /* ns */
|
||||
|
||||
#define STM32_PRESC_MAX BIT(4)
|
||||
#define STM32_SCLDEL_MAX BIT(4)
|
||||
#define STM32_SDADEL_MAX BIT(4)
|
||||
#define STM32_SCLH_MAX BIT(8)
|
||||
#define STM32_SCLL_MAX BIT(8)
|
||||
|
||||
#define STM32_NSEC_PER_SEC 1000000000L
|
||||
|
||||
#define STANDARD_RATE 100000
|
||||
#define FAST_RATE 400000
|
||||
#define FAST_PLUS_RATE 1000000
|
||||
|
||||
enum stm32_i2c_speed {
|
||||
STM32_I2C_SPEED_STANDARD, /* 100 kHz */
|
||||
STM32_I2C_SPEED_FAST, /* 400 kHz */
|
||||
STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */
|
||||
STM32_I2C_SPEED_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_i2c_spec - private i2c specification timing
|
||||
* @rate: I2C bus speed (Hz)
|
||||
* @rate_min: 80% of I2C bus speed (Hz)
|
||||
* @rate_max: 120% of I2C bus speed (Hz)
|
||||
* @fall_max: Max fall time of both SDA and SCL signals (ns)
|
||||
* @rise_max: Max rise time of both SDA and SCL signals (ns)
|
||||
* @hddat_min: Min data hold time (ns)
|
||||
* @vddat_max: Max data valid time (ns)
|
||||
* @sudat_min: Min data setup time (ns)
|
||||
* @l_min: Min low period of the SCL clock (ns)
|
||||
* @h_min: Min high period of the SCL clock (ns)
|
||||
*/
|
||||
|
||||
struct stm32_i2c_spec {
|
||||
u32 rate;
|
||||
u32 rate_min;
|
||||
u32 rate_max;
|
||||
u32 fall_max;
|
||||
u32 rise_max;
|
||||
u32 hddat_min;
|
||||
u32 vddat_max;
|
||||
u32 sudat_min;
|
||||
u32 l_min;
|
||||
u32 h_min;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_i2c_setup - private I2C timing setup parameters
|
||||
* @speed: I2C speed mode (standard, Fast Plus)
|
||||
* @speed_freq: I2C speed frequency (Hz)
|
||||
* @clock_src: I2C clock source frequency (Hz)
|
||||
* @rise_time: Rise time (ns)
|
||||
* @fall_time: Fall time (ns)
|
||||
* @dnf: Digital filter coefficient (0-16)
|
||||
* @analog_filter: Analog filter delay (On/Off)
|
||||
*/
|
||||
struct stm32_i2c_setup {
|
||||
enum stm32_i2c_speed speed;
|
||||
u32 speed_freq;
|
||||
u32 clock_src;
|
||||
u32 rise_time;
|
||||
u32 fall_time;
|
||||
u8 dnf;
|
||||
bool analog_filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_i2c_timings - private I2C output parameters
|
||||
* @prec: Prescaler value
|
||||
* @scldel: Data setup time
|
||||
* @sdadel: Data hold time
|
||||
* @sclh: SCL high period (master mode)
|
||||
* @sclh: SCL low period (master mode)
|
||||
*/
|
||||
struct stm32_i2c_timings {
|
||||
struct list_head node;
|
||||
u8 presc;
|
||||
u8 scldel;
|
||||
u8 sdadel;
|
||||
u8 sclh;
|
||||
u8 scll;
|
||||
};
|
||||
|
||||
struct stm32_i2c_priv {
|
||||
struct stm32_i2c_regs *regs;
|
||||
struct clk clk;
|
||||
struct stm32_i2c_setup *setup;
|
||||
int speed;
|
||||
};
|
||||
|
||||
static const struct stm32_i2c_spec i2c_specs[] = {
|
||||
[STM32_I2C_SPEED_STANDARD] = {
|
||||
.rate = STANDARD_RATE,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 120000,
|
||||
.fall_max = 300,
|
||||
.rise_max = 1000,
|
||||
.hddat_min = 0,
|
||||
.vddat_max = 3450,
|
||||
.sudat_min = 250,
|
||||
.l_min = 4700,
|
||||
.h_min = 4000,
|
||||
},
|
||||
[STM32_I2C_SPEED_FAST] = {
|
||||
.rate = FAST_RATE,
|
||||
.rate_min = 320000,
|
||||
.rate_max = 480000,
|
||||
.fall_max = 300,
|
||||
.rise_max = 300,
|
||||
.hddat_min = 0,
|
||||
.vddat_max = 900,
|
||||
.sudat_min = 100,
|
||||
.l_min = 1300,
|
||||
.h_min = 600,
|
||||
},
|
||||
[STM32_I2C_SPEED_FAST_PLUS] = {
|
||||
.rate = FAST_PLUS_RATE,
|
||||
.rate_min = 800000,
|
||||
.rate_max = 1200000,
|
||||
.fall_max = 100,
|
||||
.rise_max = 120,
|
||||
.hddat_min = 0,
|
||||
.vddat_max = 450,
|
||||
.sudat_min = 50,
|
||||
.l_min = 500,
|
||||
.h_min = 260,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct stm32_i2c_setup stm32f7_setup = {
|
||||
.rise_time = STM32_I2C_RISE_TIME_DEFAULT,
|
||||
.fall_time = STM32_I2C_FALL_TIME_DEFAULT,
|
||||
.dnf = STM32_I2C_DNF_DEFAULT,
|
||||
.analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
|
||||
};
|
||||
|
||||
static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 status = readl(®s->isr);
|
||||
|
||||
if (status & STM32_I2C_ISR_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_i2c_message_start(struct stm32_i2c_priv *i2c_priv,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 cr2 = readl(®s->cr2);
|
||||
|
||||
/* Set transfer direction */
|
||||
cr2 &= ~STM32_I2C_CR2_RD_WRN;
|
||||
if (msg->flags & I2C_M_RD)
|
||||
cr2 |= STM32_I2C_CR2_RD_WRN;
|
||||
|
||||
/* Set slave address */
|
||||
cr2 &= ~(STM32_I2C_CR2_HEAD10R | STM32_I2C_CR2_ADD10);
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
cr2 &= ~STM32_I2C_CR2_SADD10_MASK;
|
||||
cr2 |= STM32_I2C_CR2_SADD10(msg->addr);
|
||||
cr2 |= STM32_I2C_CR2_ADD10;
|
||||
} else {
|
||||
cr2 &= ~STM32_I2C_CR2_SADD7_MASK;
|
||||
cr2 |= STM32_I2C_CR2_SADD7(msg->addr);
|
||||
}
|
||||
|
||||
/* Set nb bytes to transfer and reload or autoend bits */
|
||||
cr2 &= ~(STM32_I2C_CR2_NBYTES_MASK | STM32_I2C_CR2_RELOAD |
|
||||
STM32_I2C_CR2_AUTOEND);
|
||||
if (msg->len > STM32_I2C_MAX_LEN) {
|
||||
cr2 |= STM32_I2C_CR2_NBYTES(STM32_I2C_MAX_LEN);
|
||||
cr2 |= STM32_I2C_CR2_RELOAD;
|
||||
} else {
|
||||
cr2 |= STM32_I2C_CR2_NBYTES(msg->len);
|
||||
}
|
||||
|
||||
/* Write configurations register */
|
||||
writel(cr2, ®s->cr2);
|
||||
|
||||
/* START/ReSTART generation */
|
||||
setbits_le32(®s->cr2, STM32_I2C_CR2_START);
|
||||
}
|
||||
|
||||
/*
|
||||
* RELOAD mode must be selected if total number of data bytes to be
|
||||
* sent is greater than MAX_LEN
|
||||
*/
|
||||
|
||||
static void stm32_i2c_handle_reload(struct stm32_i2c_priv *i2c_priv,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 cr2 = readl(®s->cr2);
|
||||
|
||||
cr2 &= ~STM32_I2C_CR2_NBYTES_MASK;
|
||||
|
||||
if (msg->len > STM32_I2C_MAX_LEN) {
|
||||
cr2 |= STM32_I2C_CR2_NBYTES(STM32_I2C_MAX_LEN);
|
||||
} else {
|
||||
cr2 &= ~STM32_I2C_CR2_RELOAD;
|
||||
cr2 |= STM32_I2C_CR2_NBYTES(msg->len);
|
||||
}
|
||||
|
||||
writel(cr2, ®s->cr2);
|
||||
}
|
||||
|
||||
static int stm32_i2c_wait_flags(struct stm32_i2c_priv *i2c_priv,
|
||||
u32 flags, u32 *status)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 time_start = get_timer(0);
|
||||
|
||||
*status = readl(®s->isr);
|
||||
while (!(*status & flags)) {
|
||||
if (get_timer(time_start) > CONFIG_SYS_HZ) {
|
||||
debug("%s: i2c timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*status = readl(®s->isr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_i2c_check_end_of_message(struct stm32_i2c_priv *i2c_priv)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 mask = STM32_I2C_ISR_ERRORS | STM32_I2C_ISR_NACKF |
|
||||
STM32_I2C_ISR_STOPF;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status & STM32_I2C_ISR_BERR) {
|
||||
debug("%s: Bus error\n", __func__);
|
||||
|
||||
/* Clear BERR flag */
|
||||
setbits_le32(®s->icr, STM32_I2C_ICR_BERRCF);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (status & STM32_I2C_ISR_ARLO) {
|
||||
debug("%s: Arbitration lost\n", __func__);
|
||||
|
||||
/* Clear ARLO flag */
|
||||
setbits_le32(®s->icr, STM32_I2C_ICR_ARLOCF);
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (status & STM32_I2C_ISR_NACKF) {
|
||||
debug("%s: Receive NACK\n", __func__);
|
||||
|
||||
/* Clear NACK flag */
|
||||
setbits_le32(®s->icr, STM32_I2C_ICR_NACKCF);
|
||||
|
||||
/* Wait until STOPF flag is set */
|
||||
mask = STM32_I2C_ISR_STOPF;
|
||||
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
if (status & STM32_I2C_ISR_STOPF) {
|
||||
/* Clear STOP flag */
|
||||
setbits_le32(®s->icr, STM32_I2C_ICR_STOPCF);
|
||||
|
||||
/* Clear control register 2 */
|
||||
setbits_le32(®s->cr2, STM32_I2C_CR2_RESET_MASK);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_i2c_message_xfer(struct stm32_i2c_priv *i2c_priv,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
u32 status;
|
||||
u32 mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
|
||||
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
|
||||
int bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
|
||||
STM32_I2C_MAX_LEN : msg->len;
|
||||
int ret = 0;
|
||||
|
||||
/* Add errors */
|
||||
mask |= STM32_I2C_ISR_ERRORS;
|
||||
|
||||
stm32_i2c_message_start(i2c_priv, msg, stop);
|
||||
|
||||
while (msg->len) {
|
||||
/*
|
||||
* Wait until TXIS/NACKF/BERR/ARLO flags or
|
||||
* RXNE/BERR/ARLO flags are set
|
||||
*/
|
||||
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (status & (STM32_I2C_ISR_NACKF | STM32_I2C_ISR_ERRORS))
|
||||
break;
|
||||
|
||||
if (status & STM32_I2C_ISR_RXNE) {
|
||||
*msg->buf++ = readb(®s->rxdr);
|
||||
msg->len--;
|
||||
bytes_to_rw--;
|
||||
}
|
||||
|
||||
if (status & STM32_I2C_ISR_TXIS) {
|
||||
writeb(*msg->buf++, ®s->txdr);
|
||||
msg->len--;
|
||||
bytes_to_rw--;
|
||||
}
|
||||
|
||||
if (!bytes_to_rw && msg->len) {
|
||||
/* Wait until TCR flag is set */
|
||||
mask = STM32_I2C_ISR_TCR;
|
||||
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
|
||||
STM32_I2C_MAX_LEN : msg->len;
|
||||
mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
|
||||
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
|
||||
|
||||
stm32_i2c_handle_reload(i2c_priv, msg, stop);
|
||||
} else if (!bytes_to_rw) {
|
||||
/* Wait until TC flag is set */
|
||||
mask = STM32_I2C_ISR_TC;
|
||||
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (!stop)
|
||||
/* Message sent, new message has to be sent */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* End of transfer, send stop condition */
|
||||
mask = STM32_I2C_CR2_STOP;
|
||||
setbits_le32(®s->cr2, mask);
|
||||
|
||||
return stm32_i2c_check_end_of_message(i2c_priv);
|
||||
}
|
||||
|
||||
static int stm32_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct stm32_i2c_priv *i2c_priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
ret = stm32_i2c_check_device_busy(i2c_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
ret = stm32_i2c_message_xfer(i2c_priv, msg, nmsgs == 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
|
||||
struct list_head *solutions)
|
||||
{
|
||||
struct stm32_i2c_timings *v;
|
||||
u32 p_prev = STM32_PRESC_MAX;
|
||||
u32 i2cclk = DIV_ROUND_CLOSEST(STM32_NSEC_PER_SEC,
|
||||
setup->clock_src);
|
||||
u32 af_delay_min, af_delay_max;
|
||||
u16 p, l, a;
|
||||
int sdadel_min, sdadel_max, scldel_min;
|
||||
int ret = 0;
|
||||
|
||||
af_delay_min = setup->analog_filter ?
|
||||
STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
|
||||
af_delay_max = setup->analog_filter ?
|
||||
STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
|
||||
|
||||
sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
|
||||
af_delay_min - (setup->dnf + 3) * i2cclk;
|
||||
|
||||
sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
|
||||
af_delay_max - (setup->dnf + 4) * i2cclk;
|
||||
|
||||
scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min;
|
||||
|
||||
if (sdadel_min < 0)
|
||||
sdadel_min = 0;
|
||||
if (sdadel_max < 0)
|
||||
sdadel_max = 0;
|
||||
|
||||
debug("%s: SDADEL(min/max): %i/%i, SCLDEL(Min): %i\n", __func__,
|
||||
sdadel_min, sdadel_max, scldel_min);
|
||||
|
||||
/* Compute possible values for PRESC, SCLDEL and SDADEL */
|
||||
for (p = 0; p < STM32_PRESC_MAX; p++) {
|
||||
for (l = 0; l < STM32_SCLDEL_MAX; l++) {
|
||||
int scldel = (l + 1) * (p + 1) * i2cclk;
|
||||
|
||||
if (scldel < scldel_min)
|
||||
continue;
|
||||
|
||||
for (a = 0; a < STM32_SDADEL_MAX; a++) {
|
||||
int sdadel = (a * (p + 1) + 1) * i2cclk;
|
||||
|
||||
if (((sdadel >= sdadel_min) &&
|
||||
(sdadel <= sdadel_max)) &&
|
||||
(p != p_prev)) {
|
||||
v = calloc(1, sizeof(*v));
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
v->presc = p;
|
||||
v->scldel = l;
|
||||
v->sdadel = a;
|
||||
p_prev = p;
|
||||
|
||||
list_add_tail(&v->node, solutions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_prev == p)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(solutions)) {
|
||||
pr_err("%s: no Prescaler solution\n", __func__);
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
|
||||
struct list_head *solutions,
|
||||
struct stm32_i2c_timings *s)
|
||||
{
|
||||
struct stm32_i2c_timings *v;
|
||||
u32 i2cbus = DIV_ROUND_CLOSEST(STM32_NSEC_PER_SEC,
|
||||
setup->speed_freq);
|
||||
u32 clk_error_prev = i2cbus;
|
||||
u32 i2cclk = DIV_ROUND_CLOSEST(STM32_NSEC_PER_SEC,
|
||||
setup->clock_src);
|
||||
u32 clk_min, clk_max;
|
||||
u32 af_delay_min;
|
||||
u32 dnf_delay;
|
||||
u32 tsync;
|
||||
u16 l, h;
|
||||
bool sol_found = false;
|
||||
int ret = 0;
|
||||
|
||||
af_delay_min = setup->analog_filter ?
|
||||
STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
|
||||
dnf_delay = setup->dnf * i2cclk;
|
||||
|
||||
tsync = af_delay_min + dnf_delay + (2 * i2cclk);
|
||||
clk_max = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
|
||||
clk_min = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
|
||||
|
||||
/*
|
||||
* Among Prescaler possibilities discovered above figures out SCL Low
|
||||
* and High Period. Provided:
|
||||
* - SCL Low Period has to be higher than Low Period of the SCL Clock
|
||||
* defined by I2C Specification. I2C Clock has to be lower than
|
||||
* (SCL Low Period - Analog/Digital filters) / 4.
|
||||
* - SCL High Period has to be lower than High Period of the SCL Clock
|
||||
* defined by I2C Specification
|
||||
* - I2C Clock has to be lower than SCL High Period
|
||||
*/
|
||||
list_for_each_entry(v, solutions, node) {
|
||||
u32 prescaler = (v->presc + 1) * i2cclk;
|
||||
|
||||
for (l = 0; l < STM32_SCLL_MAX; l++) {
|
||||
u32 tscl_l = (l + 1) * prescaler + tsync;
|
||||
|
||||
if ((tscl_l < i2c_specs[setup->speed].l_min) ||
|
||||
(i2cclk >=
|
||||
((tscl_l - af_delay_min - dnf_delay) / 4))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (h = 0; h < STM32_SCLH_MAX; h++) {
|
||||
u32 tscl_h = (h + 1) * prescaler + tsync;
|
||||
u32 tscl = tscl_l + tscl_h +
|
||||
setup->rise_time + setup->fall_time;
|
||||
|
||||
if ((tscl >= clk_min) && (tscl <= clk_max) &&
|
||||
(tscl_h >= i2c_specs[setup->speed].h_min) &&
|
||||
(i2cclk < tscl_h)) {
|
||||
u32 clk_error;
|
||||
|
||||
if (tscl > i2cbus)
|
||||
clk_error = tscl - i2cbus;
|
||||
else
|
||||
clk_error = i2cbus - tscl;
|
||||
|
||||
if (clk_error < clk_error_prev) {
|
||||
clk_error_prev = clk_error;
|
||||
v->scll = l;
|
||||
v->sclh = h;
|
||||
sol_found = true;
|
||||
memcpy(s, v, sizeof(*s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sol_found) {
|
||||
pr_err("%s: no solution at all\n", __func__);
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
|
||||
struct stm32_i2c_setup *setup,
|
||||
struct stm32_i2c_timings *output)
|
||||
{
|
||||
struct stm32_i2c_timings *v, *_v;
|
||||
struct list_head solutions;
|
||||
int ret;
|
||||
|
||||
if (setup->speed >= STM32_I2C_SPEED_END) {
|
||||
pr_err("%s: speed out of bound {%d/%d}\n", __func__,
|
||||
setup->speed, STM32_I2C_SPEED_END - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((setup->rise_time > i2c_specs[setup->speed].rise_max) ||
|
||||
(setup->fall_time > i2c_specs[setup->speed].fall_max)) {
|
||||
pr_err("%s :timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
|
||||
__func__,
|
||||
setup->rise_time, i2c_specs[setup->speed].rise_max,
|
||||
setup->fall_time, i2c_specs[setup->speed].fall_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (setup->dnf > STM32_I2C_DNF_MAX) {
|
||||
pr_err("%s: DNF out of bound %d/%d\n", __func__,
|
||||
setup->dnf, STM32_I2C_DNF_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (setup->speed_freq > i2c_specs[setup->speed].rate) {
|
||||
pr_err("%s: Freq {%d/%d}\n", __func__,
|
||||
setup->speed_freq, i2c_specs[setup->speed].rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&solutions);
|
||||
ret = stm32_i2c_compute_solutions(setup, &solutions);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = stm32_i2c_choose_solution(setup, &solutions, output);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
debug("%s: Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n",
|
||||
__func__, output->presc,
|
||||
output->scldel, output->sdadel,
|
||||
output->scll, output->sclh);
|
||||
|
||||
exit:
|
||||
/* Release list and memory */
|
||||
list_for_each_entry_safe(v, _v, &solutions, node) {
|
||||
list_del(&v->node);
|
||||
free(v);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
|
||||
struct stm32_i2c_timings *timing)
|
||||
{
|
||||
struct stm32_i2c_setup *setup = i2c_priv->setup;
|
||||
int ret = 0;
|
||||
|
||||
setup->speed = i2c_priv->speed;
|
||||
setup->speed_freq = i2c_specs[setup->speed].rate;
|
||||
setup->clock_src = clk_get_rate(&i2c_priv->clk);
|
||||
|
||||
if (!setup->clock_src) {
|
||||
pr_err("%s: clock rate is 0\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = stm32_i2c_compute_timing(i2c_priv, setup, timing);
|
||||
if (ret) {
|
||||
debug("%s: failed to compute I2C timings.\n",
|
||||
__func__);
|
||||
if (i2c_priv->speed > STM32_I2C_SPEED_STANDARD) {
|
||||
i2c_priv->speed--;
|
||||
setup->speed = i2c_priv->speed;
|
||||
setup->speed_freq =
|
||||
i2c_specs[setup->speed].rate;
|
||||
debug("%s: downgrade I2C Speed Freq to (%i)\n",
|
||||
__func__, i2c_specs[setup->speed].rate);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: impossible to compute I2C timings.\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
debug("%s: I2C Speed(%i), Freq(%i), Clk Source(%i)\n", __func__,
|
||||
setup->speed, setup->speed_freq, setup->clock_src);
|
||||
debug("%s: I2C Rise(%i) and Fall(%i) Time\n", __func__,
|
||||
setup->rise_time, setup->fall_time);
|
||||
debug("%s: I2C Analog Filter(%s), DNF(%i)\n", __func__,
|
||||
setup->analog_filter ? "On" : "Off", setup->dnf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
|
||||
{
|
||||
struct stm32_i2c_regs *regs = i2c_priv->regs;
|
||||
struct stm32_i2c_timings t;
|
||||
int ret;
|
||||
u32 timing = 0;
|
||||
|
||||
ret = stm32_i2c_setup_timing(i2c_priv, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable I2C */
|
||||
clrbits_le32(®s->cr1, STM32_I2C_CR1_PE);
|
||||
|
||||
/* Timing settings */
|
||||
timing |= STM32_I2C_TIMINGR_PRESC(t.presc);
|
||||
timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel);
|
||||
timing |= STM32_I2C_TIMINGR_SDADEL(t.sdadel);
|
||||
timing |= STM32_I2C_TIMINGR_SCLH(t.sclh);
|
||||
timing |= STM32_I2C_TIMINGR_SCLL(t.scll);
|
||||
writel(timing, ®s->timingr);
|
||||
|
||||
/* Enable I2C */
|
||||
if (i2c_priv->setup->analog_filter)
|
||||
clrbits_le32(®s->cr1, STM32_I2C_CR1_ANFOFF);
|
||||
else
|
||||
setbits_le32(®s->cr1, STM32_I2C_CR1_ANFOFF);
|
||||
setbits_le32(®s->cr1, STM32_I2C_CR1_PE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct stm32_i2c_priv *i2c_priv = dev_get_priv(bus);
|
||||
|
||||
switch (speed) {
|
||||
case STANDARD_RATE:
|
||||
i2c_priv->speed = STM32_I2C_SPEED_STANDARD;
|
||||
break;
|
||||
case FAST_RATE:
|
||||
i2c_priv->speed = STM32_I2C_SPEED_FAST;
|
||||
break;
|
||||
case FAST_PLUS_RATE:
|
||||
i2c_priv->speed = STM32_I2C_SPEED_FAST_PLUS;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Speed %d not supported\n", __func__, speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return stm32_i2c_hw_config(i2c_priv);
|
||||
}
|
||||
|
||||
static int stm32_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct stm32_i2c_priv *i2c_priv = dev_get_priv(dev);
|
||||
struct reset_ctl reset_ctl;
|
||||
fdt_addr_t addr;
|
||||
int ret;
|
||||
|
||||
addr = dev_read_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
i2c_priv->regs = (struct stm32_i2c_regs *)addr;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &i2c_priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&i2c_priv->clk);
|
||||
if (ret)
|
||||
goto clk_free;
|
||||
|
||||
ret = reset_get_by_index(dev, 0, &reset_ctl);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
reset_assert(&reset_ctl);
|
||||
udelay(2);
|
||||
reset_deassert(&reset_ctl);
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
clk_disable(&i2c_priv->clk);
|
||||
clk_free:
|
||||
clk_free(&i2c_priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct stm32_i2c_priv *i2c_priv = dev_get_priv(dev);
|
||||
u32 rise_time, fall_time;
|
||||
|
||||
i2c_priv->setup = (struct stm32_i2c_setup *)dev_get_driver_data(dev);
|
||||
if (!i2c_priv->setup)
|
||||
return -EINVAL;
|
||||
|
||||
rise_time = dev_read_u32_default(dev, "i2c-scl-rising-time-ns", 0);
|
||||
if (rise_time)
|
||||
i2c_priv->setup->rise_time = rise_time;
|
||||
|
||||
fall_time = dev_read_u32_default(dev, "i2c-scl-falling-time-ns", 0);
|
||||
if (fall_time)
|
||||
i2c_priv->setup->fall_time = fall_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops stm32_i2c_ops = {
|
||||
.xfer = stm32_i2c_xfer,
|
||||
.set_bus_speed = stm32_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id stm32_i2c_of_match[] = {
|
||||
{ .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_setup },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(stm32f7_i2c) = {
|
||||
.name = "stm32f7-i2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = stm32_i2c_of_match,
|
||||
.ofdata_to_platdata = stm32_ofdata_to_platdata,
|
||||
.probe = stm32_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct stm32_i2c_priv),
|
||||
.ops = &stm32_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <misc.h>
|
||||
#include <asm/arch-tegra/bpmp_abi.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct tegra186_bpmp_i2c {
|
||||
uint32_t bpmp_bus_id;
|
||||
};
|
||||
|
||||
static inline void serialize_u16(uint8_t **p, uint16_t val)
|
||||
{
|
||||
(*p)[0] = val & 0xff;
|
||||
(*p)[1] = val >> 8;
|
||||
(*p) += 2;
|
||||
}
|
||||
|
||||
/* These just happen to have the same values as I2C_M_* and SERIALI2C_* */
|
||||
#define SUPPORTED_FLAGS \
|
||||
(I2C_M_TEN | \
|
||||
I2C_M_RD | \
|
||||
I2C_M_STOP | \
|
||||
I2C_M_NOSTART | \
|
||||
I2C_M_REV_DIR_ADDR | \
|
||||
I2C_M_IGNORE_NAK | \
|
||||
I2C_M_NO_RD_ACK | \
|
||||
I2C_M_RECV_LEN)
|
||||
|
||||
static int tegra186_bpmp_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct tegra186_bpmp_i2c *priv = dev_get_priv(dev);
|
||||
struct mrq_i2c_request req;
|
||||
struct mrq_i2c_response resp;
|
||||
uint8_t *p;
|
||||
int left, i, ret;
|
||||
|
||||
req.cmd = CMD_I2C_XFER;
|
||||
req.xfer.bus_id = priv->bpmp_bus_id;
|
||||
p = &req.xfer.data_buf[0];
|
||||
left = ARRAY_SIZE(req.xfer.data_buf);
|
||||
for (i = 0; i < nmsgs; i++) {
|
||||
int len = 6;
|
||||
if (!(msg[i].flags & I2C_M_RD))
|
||||
len += msg[i].len;
|
||||
if ((len >= BIT(16)) || (len > left))
|
||||
return -ENOSPC;
|
||||
|
||||
if (msg[i].flags & ~SUPPORTED_FLAGS)
|
||||
return -EINVAL;
|
||||
|
||||
serialize_u16(&p, msg[i].addr);
|
||||
serialize_u16(&p, msg[i].flags);
|
||||
serialize_u16(&p, msg[i].len);
|
||||
if (!(msg[i].flags & I2C_M_RD)) {
|
||||
memcpy(p, msg[i].buf, msg[i].len);
|
||||
p += msg[i].len;
|
||||
}
|
||||
}
|
||||
req.xfer.data_size = p - &req.xfer.data_buf[0];
|
||||
|
||||
ret = misc_call(dev->parent, MRQ_I2C, &req, sizeof(req), &resp,
|
||||
sizeof(resp));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
p = &resp.xfer.data_buf[0];
|
||||
left = resp.xfer.data_size;
|
||||
if (left > ARRAY_SIZE(resp.xfer.data_buf))
|
||||
return -EINVAL;
|
||||
for (i = 0; i < nmsgs; i++) {
|
||||
if (msg[i].flags & I2C_M_RD) {
|
||||
memcpy(msg[i].buf, p, msg[i].len);
|
||||
p += msg[i].len;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_bpmp_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_bpmp_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct tegra186_bpmp_i2c *priv = dev_get_priv(dev);
|
||||
|
||||
priv->bpmp_bus_id = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
|
||||
"nvidia,bpmp-bus-id", U32_MAX);
|
||||
if (priv->bpmp_bus_id == U32_MAX) {
|
||||
debug("%s: could not parse nvidia,bpmp-bus-id\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops tegra186_bpmp_i2c_ops = {
|
||||
.xfer = tegra186_bpmp_i2c_xfer,
|
||||
.probe_chip = tegra186_bpmp_probe_chip,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra186_bpmp_i2c_ids[] = {
|
||||
{ .compatible = "nvidia,tegra186-bpmp-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_gpio) = {
|
||||
.name = "tegra186_bpmp_i2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = tegra186_bpmp_i2c_ids,
|
||||
.probe = tegra186_bpmp_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct tegra186_bpmp_i2c),
|
||||
.ops = &tegra186_bpmp_i2c_ops,
|
||||
};
|
||||
@@ -0,0 +1,536 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Copyright (c) 2010-2011 NVIDIA Corporation
|
||||
* NVIDIA Corporation <www.nvidia.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <reset.h>
|
||||
#ifndef CONFIG_TEGRA186
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/funcmux.h>
|
||||
#endif
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch-tegra/tegra_i2c.h>
|
||||
|
||||
enum i2c_type {
|
||||
TYPE_114,
|
||||
TYPE_STD,
|
||||
TYPE_DVC,
|
||||
};
|
||||
|
||||
/* Information about i2c controller */
|
||||
struct i2c_bus {
|
||||
int id;
|
||||
struct reset_ctl reset_ctl;
|
||||
struct clk clk;
|
||||
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_assert(&i2c_bus->reset_ctl);
|
||||
udelay(1);
|
||||
reset_deassert(&i2c_bus->reset_ctl);
|
||||
udelay(1);
|
||||
|
||||
/* re-program config register to packet mode */
|
||||
set_packet_mode(i2c_bus);
|
||||
}
|
||||
|
||||
static int i2c_init_clock(struct i2c_bus *i2c_bus, unsigned rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = reset_assert(&i2c_bus->reset_ctl);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_enable(&i2c_bus->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_set_rate(&i2c_bus->clk, rate);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
ret = reset_deassert(&i2c_bus->reset_ctl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
i2c_init_clock(i2c_bus, 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;
|
||||
unsigned rate = CLK_MULT_STD_FAST_MODE *
|
||||
(clk_div_stdfst_mode + 1) * i2c_bus->speed * 2;
|
||||
debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__,
|
||||
clk_div_stdfst_mode);
|
||||
|
||||
i2c_init_clock(i2c_bus, rate);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TEGRA186
|
||||
funcmux_select(i2c_bus->clk.id, i2c_bus->pinmux_config);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
bool is_dvc;
|
||||
|
||||
i2c_bus->id = dev->seq;
|
||||
i2c_bus->type = dev_get_driver_data(dev);
|
||||
i2c_bus->regs = (struct i2c_ctlr *)dev_read_addr(dev);
|
||||
if ((ulong)i2c_bus->regs == FDT_ADDR_T_NONE) {
|
||||
debug("%s: Cannot get regs address\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = reset_get_by_name(dev, "i2c", &i2c_bus->reset_ctl);
|
||||
if (ret) {
|
||||
pr_err("reset_get_by_name() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_get_by_name(dev, "div-clk", &i2c_bus->clk);
|
||||
if (ret) {
|
||||
pr_err("clk_get_by_name() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TEGRA186
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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->clk.id == PERIPH_ID_I2C2)
|
||||
* i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA;
|
||||
*/
|
||||
#endif
|
||||
|
||||
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, speed %d: ",
|
||||
is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, 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,
|
||||
};
|
||||
@@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Xilinx AXI I2C driver
|
||||
*
|
||||
* Copyright (C) 2018 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Based on Linux 4.14.y i2c-xiic.c
|
||||
* Copyright (c) 2002-2007 Xilinx Inc.
|
||||
* Copyright (c) 2009-2010 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <wait_bit.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct xilinx_xiic_priv {
|
||||
void __iomem *base;
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
#define XIIC_MSB_OFFSET 0
|
||||
#define XIIC_REG_OFFSET (0x100+XIIC_MSB_OFFSET)
|
||||
|
||||
/*
|
||||
* Register offsets in bytes from RegisterBase. Three is added to the
|
||||
* base offset to access LSB (IBM style) of the word
|
||||
*/
|
||||
#define XIIC_CR_REG_OFFSET (0x00+XIIC_REG_OFFSET) /* Control Register */
|
||||
#define XIIC_SR_REG_OFFSET (0x04+XIIC_REG_OFFSET) /* Status Register */
|
||||
#define XIIC_DTR_REG_OFFSET (0x08+XIIC_REG_OFFSET) /* Data Tx Register */
|
||||
#define XIIC_DRR_REG_OFFSET (0x0C+XIIC_REG_OFFSET) /* Data Rx Register */
|
||||
#define XIIC_ADR_REG_OFFSET (0x10+XIIC_REG_OFFSET) /* Address Register */
|
||||
#define XIIC_TFO_REG_OFFSET (0x14+XIIC_REG_OFFSET) /* Tx FIFO Occupancy */
|
||||
#define XIIC_RFO_REG_OFFSET (0x18+XIIC_REG_OFFSET) /* Rx FIFO Occupancy */
|
||||
#define XIIC_TBA_REG_OFFSET (0x1C+XIIC_REG_OFFSET) /* 10 Bit Address reg */
|
||||
#define XIIC_RFD_REG_OFFSET (0x20+XIIC_REG_OFFSET) /* Rx FIFO Depth reg */
|
||||
#define XIIC_GPO_REG_OFFSET (0x24+XIIC_REG_OFFSET) /* Output Register */
|
||||
|
||||
/* Control Register masks */
|
||||
#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */
|
||||
#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */
|
||||
#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */
|
||||
#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */
|
||||
#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */
|
||||
#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */
|
||||
#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */
|
||||
|
||||
/* Status Register masks */
|
||||
#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */
|
||||
#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */
|
||||
#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */
|
||||
#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */
|
||||
#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */
|
||||
#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */
|
||||
#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */
|
||||
#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */
|
||||
|
||||
/* Interrupt Status Register masks Interrupt occurs when... */
|
||||
#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */
|
||||
#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */
|
||||
#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */
|
||||
#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */
|
||||
#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */
|
||||
#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */
|
||||
#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */
|
||||
#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */
|
||||
|
||||
/* The following constants specify the depth of the FIFOs */
|
||||
#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */
|
||||
#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */
|
||||
|
||||
/*
|
||||
* Tx Fifo upper bit masks.
|
||||
*/
|
||||
#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */
|
||||
#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */
|
||||
|
||||
/*
|
||||
* The following constants define the register offsets for the Interrupt
|
||||
* registers. There are some holes in the memory map for reserved addresses
|
||||
* to allow other registers to be added and still match the memory map of the
|
||||
* interrupt controller registers
|
||||
*/
|
||||
#define XIIC_DGIER_OFFSET 0x1C /* Device Global Interrupt Enable Register */
|
||||
#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */
|
||||
#define XIIC_IIER_OFFSET 0x28 /* Interrupt Enable Register */
|
||||
#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */
|
||||
|
||||
#define XIIC_RESET_MASK 0xAUL
|
||||
|
||||
static u8 i2c_8bit_addr_from_flags(uint addr, u16 flags)
|
||||
{
|
||||
return (addr << 1) | (flags & I2C_M_RD ? 1 : 0);
|
||||
}
|
||||
|
||||
static void xiic_irq_clr(struct xilinx_xiic_priv *priv, u32 mask)
|
||||
{
|
||||
u32 isr = readl(priv->base + XIIC_IISR_OFFSET);
|
||||
|
||||
writel(isr & mask, priv->base + XIIC_IISR_OFFSET);
|
||||
}
|
||||
|
||||
static int xiic_read_rx(struct xilinx_xiic_priv *priv,
|
||||
struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
u8 bytes_in_fifo;
|
||||
u32 pos = 0;
|
||||
int i, ret;
|
||||
|
||||
while (pos < msg->len) {
|
||||
ret = wait_for_bit_8(priv->base + XIIC_SR_REG_OFFSET,
|
||||
XIIC_SR_RX_FIFO_EMPTY_MASK, false,
|
||||
1000, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bytes_in_fifo = readb(priv->base + XIIC_RFO_REG_OFFSET) + 1;
|
||||
|
||||
if (bytes_in_fifo > msg->len)
|
||||
bytes_in_fifo = msg->len;
|
||||
|
||||
for (i = 0; i < bytes_in_fifo; i++) {
|
||||
msg->buf[pos++] = readb(priv->base +
|
||||
XIIC_DRR_REG_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xiic_tx_fifo_space(struct xilinx_xiic_priv *priv)
|
||||
{
|
||||
/* return the actual space left in the FIFO */
|
||||
return IIC_TX_FIFO_DEPTH - readb(priv->base + XIIC_TFO_REG_OFFSET) - 1;
|
||||
}
|
||||
|
||||
static void xiic_fill_tx_fifo(struct xilinx_xiic_priv *priv,
|
||||
struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
u8 fifo_space = xiic_tx_fifo_space(priv);
|
||||
int len = msg->len;
|
||||
u32 pos = 0;
|
||||
|
||||
len = (len > fifo_space) ? fifo_space : len;
|
||||
|
||||
while (len--) {
|
||||
u16 data = msg->buf[pos++];
|
||||
|
||||
if ((msg->len - pos == 0) && nmsgs == 1) {
|
||||
/* last message in transfer -> STOP */
|
||||
data |= XIIC_TX_DYN_STOP_MASK;
|
||||
}
|
||||
writew(data, priv->base + XIIC_DTR_REG_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
static void xilinx_xiic_set_addr(struct udevice *dev, u8 addr,
|
||||
u16 flags, u32 len, u32 nmsgs)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
xiic_irq_clr(priv, XIIC_INTR_TX_ERROR_MASK);
|
||||
|
||||
if (!(flags & I2C_M_NOSTART)) {
|
||||
/* write the address */
|
||||
u16 data = i2c_8bit_addr_from_flags(addr, flags) |
|
||||
XIIC_TX_DYN_START_MASK;
|
||||
if (nmsgs == 1 && len == 0)
|
||||
/* no data and last message -> add STOP */
|
||||
data |= XIIC_TX_DYN_STOP_MASK;
|
||||
|
||||
writew(data, priv->base + XIIC_DTR_REG_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
static int xilinx_xiic_read_common(struct udevice *dev, struct i2c_msg *msg,
|
||||
u32 nmsgs)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
u8 rx_watermark;
|
||||
|
||||
/* Clear and enable Rx full interrupt. */
|
||||
xiic_irq_clr(priv, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK);
|
||||
|
||||
/* we want to get all but last byte, because the TX_ERROR IRQ is used
|
||||
* to inidicate error ACK on the address, and negative ack on the last
|
||||
* received byte, so to not mix them receive all but last.
|
||||
* In the case where there is only one byte to receive
|
||||
* we can check if ERROR and RX full is set at the same time
|
||||
*/
|
||||
rx_watermark = msg->len;
|
||||
if (rx_watermark > IIC_RX_FIFO_DEPTH)
|
||||
rx_watermark = IIC_RX_FIFO_DEPTH;
|
||||
|
||||
writeb(rx_watermark - 1, priv->base + XIIC_RFD_REG_OFFSET);
|
||||
|
||||
xilinx_xiic_set_addr(dev, msg->addr, msg->flags, msg->len, nmsgs);
|
||||
|
||||
xiic_irq_clr(priv, XIIC_INTR_BNB_MASK);
|
||||
|
||||
writew((msg->len & 0xff) | ((nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0),
|
||||
priv->base + XIIC_DTR_REG_OFFSET);
|
||||
|
||||
if (nmsgs == 1)
|
||||
/* very last, enable bus not busy as well */
|
||||
xiic_irq_clr(priv, XIIC_INTR_BNB_MASK);
|
||||
|
||||
return xiic_read_rx(priv, msg, nmsgs);
|
||||
}
|
||||
|
||||
static int xilinx_xiic_write_common(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
xilinx_xiic_set_addr(dev, msg->addr, msg->flags, msg->len, nmsgs);
|
||||
xiic_fill_tx_fifo(priv, msg, nmsgs);
|
||||
|
||||
ret = wait_for_bit_8(priv->base + XIIC_SR_REG_OFFSET,
|
||||
XIIC_SR_TX_FIFO_EMPTY_MASK, false, 1000, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear any pending Tx empty, Tx Error and then enable them. */
|
||||
xiic_irq_clr(priv, XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK |
|
||||
XIIC_INTR_BNB_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xiic_clear_rx_fifo(struct xilinx_xiic_priv *priv)
|
||||
{
|
||||
u8 sr;
|
||||
|
||||
for (sr = readb(priv->base + XIIC_SR_REG_OFFSET);
|
||||
!(sr & XIIC_SR_RX_FIFO_EMPTY_MASK);
|
||||
sr = readb(priv->base + XIIC_SR_REG_OFFSET))
|
||||
readb(priv->base + XIIC_DRR_REG_OFFSET);
|
||||
}
|
||||
|
||||
static void xiic_reinit(struct xilinx_xiic_priv *priv)
|
||||
{
|
||||
writel(XIIC_RESET_MASK, priv->base + XIIC_RESETR_OFFSET);
|
||||
|
||||
/* Set receive Fifo depth to maximum (zero based). */
|
||||
writeb(IIC_RX_FIFO_DEPTH - 1, priv->base + XIIC_RFD_REG_OFFSET);
|
||||
|
||||
/* Reset Tx Fifo. */
|
||||
writeb(XIIC_CR_TX_FIFO_RESET_MASK, priv->base + XIIC_CR_REG_OFFSET);
|
||||
|
||||
/* Enable IIC Device, remove Tx Fifo reset & disable general call. */
|
||||
writeb(XIIC_CR_ENABLE_DEVICE_MASK, priv->base + XIIC_CR_REG_OFFSET);
|
||||
|
||||
/* make sure RX fifo is empty */
|
||||
xiic_clear_rx_fifo(priv);
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, priv->base + XIIC_DGIER_OFFSET);
|
||||
|
||||
xiic_irq_clr(priv, XIIC_INTR_ARB_LOST_MASK);
|
||||
}
|
||||
|
||||
static int xilinx_xiic_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = wait_for_bit_8(priv->base + XIIC_SR_REG_OFFSET,
|
||||
XIIC_SR_BUS_BUSY_MASK, false, 1000, true);
|
||||
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(dev, "timeout waiting for bus not busy condition\n");
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xiic_reinit(priv);
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = xilinx_xiic_read_common(dev, msg, nmsgs);
|
||||
else
|
||||
ret = xilinx_xiic_write_common(dev, msg, nmsgs);
|
||||
|
||||
if (ret)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xilinx_xiic_probe_chip(struct udevice *dev, uint addr, uint flags)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
xiic_reinit(priv);
|
||||
|
||||
xilinx_xiic_set_addr(dev, addr, 0, 0, 1);
|
||||
ret = wait_for_bit_8(priv->base + XIIC_SR_REG_OFFSET,
|
||||
XIIC_SR_BUS_BUSY_MASK, false, 1000, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = readl(priv->base + XIIC_IISR_OFFSET);
|
||||
if (reg & XIIC_INTR_TX_ERROR_MASK)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_xiic_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_xiic_probe(struct udevice *dev)
|
||||
{
|
||||
struct xilinx_xiic_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->base = dev_read_addr_ptr(dev);
|
||||
|
||||
writel(XIIC_CR_TX_FIFO_RESET_MASK, priv->base + XIIC_CR_REG_OFFSET);
|
||||
xiic_reinit(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops xilinx_xiic_ops = {
|
||||
.xfer = xilinx_xiic_xfer,
|
||||
.probe_chip = xilinx_xiic_probe_chip,
|
||||
.set_bus_speed = xilinx_xiic_set_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id xilinx_xiic_ids[] = {
|
||||
{ .compatible = "xlnx,xps-iic-2.00.a" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(xilinx_xiic) = {
|
||||
.name = "xilinx_axi_i2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = xilinx_xiic_ids,
|
||||
.probe = xilinx_xiic_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct xilinx_xiic_priv),
|
||||
.ops = &xilinx_xiic_ops,
|
||||
};
|
||||
Reference in New Issue
Block a user