summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig40
-rw-r--r--drivers/i2c/Makefile9
-rw-r--r--drivers/i2c/algos/Kconfig1
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c128
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c65
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c121
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.h11
-rw-r--r--drivers/i2c/busses/Kconfig683
-rw-r--r--drivers/i2c/busses/Makefile68
-rw-r--r--drivers/i2c/busses/i2c-acorn.c6
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c49
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c9
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c47
-rw-r--r--drivers/i2c/busses/i2c-altera.c79
-rw-r--r--drivers/i2c/busses/i2c-amd-asf-plat.c370
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-pci.c477
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-plat.c360
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2.h213
-rw-r--r--drivers/i2c/busses/i2c-amd756-s4882.c254
-rw-r--r--drivers/i2c/busses/i2c-amd756.c20
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c275
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c262
-rw-r--r--drivers/i2c/busses/i2c-at91-core.c357
-rw-r--r--drivers/i2c/busses/i2c-at91-master.c (renamed from drivers/i2c/busses/i2c-at91.c)618
-rw-r--r--drivers/i2c/busses/i2c-at91-slave.c142
-rw-r--r--drivers/i2c/busses/i2c-at91.h191
-rw-r--r--drivers/i2c/busses/i2c-au1550.c50
-rw-r--r--drivers/i2c/busses/i2c-axxia.c263
-rw-r--r--drivers/i2c/busses/i2c-bcm-iproc.c1103
-rw-r--r--drivers/i2c/busses/i2c-bcm-kona.c56
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c211
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c157
-rw-r--r--drivers/i2c/busses/i2c-cadence.c1012
-rw-r--r--drivers/i2c/busses/i2c-cbus-gpio.c93
-rw-r--r--drivers/i2c/busses/i2c-ccgx-ucsi.c31
-rw-r--r--drivers/i2c/busses/i2c-ccgx-ucsi.h11
-rw-r--r--drivers/i2c/busses/i2c-cgbc.c406
-rw-r--r--drivers/i2c/busses/i2c-cht-wc.c268
-rw-r--r--drivers/i2c/busses/i2c-cp2615.c336
-rw-r--r--drivers/i2c/busses/i2c-cpm.c32
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c39
-rw-r--r--drivers/i2c/busses/i2c-davinci.c210
-rw-r--r--drivers/i2c/busses/i2c-designware-amdisp.c207
-rw-r--r--drivers/i2c/busses/i2c-designware-amdpsp.c310
-rw-r--r--drivers/i2c/busses/i2c-designware-baytrail.c14
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c668
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h441
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c777
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c257
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c575
-rw-r--r--drivers/i2c/busses/i2c-designware-slave.c202
-rw-r--r--drivers/i2c/busses/i2c-digicolor.c41
-rw-r--r--drivers/i2c/busses/i2c-diolan-u2c.c19
-rw-r--r--drivers/i2c/busses/i2c-dln2.c19
-rw-r--r--drivers/i2c/busses/i2c-efm32.c482
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c86
-rw-r--r--drivers/i2c/busses/i2c-elektor.c20
-rw-r--r--drivers/i2c/busses/i2c-emev2.c63
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c310
-rw-r--r--drivers/i2c/busses/i2c-fsi.c92
-rw-r--r--drivers/i2c/busses/i2c-gpio.c223
-rw-r--r--drivers/i2c/busses/i2c-gxp.c608
-rw-r--r--drivers/i2c/busses/i2c-highlander.c14
-rw-r--r--drivers/i2c/busses/i2c-hisi.c543
-rw-r--r--drivers/i2c/busses/i2c-hix5hd2.c78
-rw-r--r--drivers/i2c/busses/i2c-hydra.c10
-rw-r--r--drivers/i2c/busses/i2c-i801.c1480
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c65
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.h7
-rw-r--r--drivers/i2c/busses/i2c-icy.c215
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c53
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c1186
-rw-r--r--drivers/i2c/busses/i2c-imx.c1177
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c104
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.h12
-rw-r--r--drivers/i2c/busses/i2c-isch.c334
-rw-r--r--drivers/i2c/busses/i2c-ismt.c83
-rw-r--r--drivers/i2c/busses/i2c-jz4780.c267
-rw-r--r--drivers/i2c/busses/i2c-k1.c628
-rw-r--r--drivers/i2c/busses/i2c-keba.c594
-rw-r--r--drivers/i2c/busses/i2c-kempld.c50
-rw-r--r--drivers/i2c/busses/i2c-ljca.c343
-rw-r--r--drivers/i2c/busses/i2c-lpc2k.c77
-rw-r--r--drivers/i2c/busses/i2c-ls2x.c377
-rw-r--r--drivers/i2c/busses/i2c-mchp-pci1xxxx.c1212
-rw-r--r--drivers/i2c/busses/i2c-meson.c252
-rw-r--r--drivers/i2c/busses/i2c-microchip-corei2c.c649
-rw-r--r--drivers/i2c/busses/i2c-mlxbf.c2454
-rw-r--r--drivers/i2c/busses/i2c-mlxcpld.c151
-rw-r--r--drivers/i2c/busses/i2c-mpc.c737
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c1100
-rw-r--r--drivers/i2c/busses/i2c-mt7621.c342
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c276
-rw-r--r--drivers/i2c/busses/i2c-mxs.c79
-rw-r--r--drivers/i2c/busses/i2c-nct6694.c196
-rw-r--r--drivers/i2c/busses/i2c-nforce2-s4985.c249
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c32
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c857
-rw-r--r--drivers/i2c/busses/i2c-npcm7xx.c2621
-rw-r--r--drivers/i2c/busses/i2c-nvidia-gpu.c150
-rw-r--r--drivers/i2c/busses/i2c-ocores.c409
-rw-r--r--drivers/i2c/busses/i2c-octeon-core.c408
-rw-r--r--drivers/i2c/busses/i2c-octeon-core.h68
-rw-r--r--drivers/i2c/busses/i2c-octeon-platdrv.c9
-rw-r--r--drivers/i2c/busses/i2c-omap.c226
-rw-r--r--drivers/i2c/busses/i2c-opal.c36
-rw-r--r--drivers/i2c/busses/i2c-owl.c130
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c275
-rw-r--r--drivers/i2c/busses/i2c-parport.c174
-rw-r--r--drivers/i2c/busses/i2c-parport.h114
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.c (renamed from drivers/i2c/busses/i2c-pasemi.c)298
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.h26
-rw-r--r--drivers/i2c/busses/i2c-pasemi-pci.c86
-rw-r--r--drivers/i2c/busses/i2c-pasemi-platform.c113
-rw-r--r--drivers/i2c/busses/i2c-pca-isa.c17
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c23
-rw-r--r--drivers/i2c/busses/i2c-piix4.c340
-rw-r--r--drivers/i2c/busses/i2c-piix4.h44
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c600
-rw-r--r--drivers/i2c/busses/i2c-pnx.c91
-rw-r--r--drivers/i2c/busses/i2c-powermac.c82
-rw-r--r--drivers/i2c/busses/i2c-puv3.c278
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c17
-rw-r--r--drivers/i2c/busses/i2c-pxa.c948
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c865
-rw-r--r--drivers/i2c/busses/i2c-qcom-geni.c843
-rw-r--r--drivers/i2c/busses/i2c-qup.c144
-rw-r--r--drivers/i2c/busses/i2c-rcar.c804
-rw-r--r--drivers/i2c/busses/i2c-riic.c510
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c202
-rw-r--r--drivers/i2c/busses/i2c-robotfuzz-osif.c19
-rw-r--r--drivers/i2c/busses/i2c-rtl9300.c539
-rw-r--r--drivers/i2c/busses/i2c-rzv2m.c539
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c263
-rw-r--r--drivers/i2c/busses/i2c-scmi.c62
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c18
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c318
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c18
-rw-r--r--drivers/i2c/busses/i2c-simtec.c16
-rw-r--r--drivers/i2c/busses/i2c-sirf.c479
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c34
-rw-r--r--drivers/i2c/busses/i2c-sis630.c30
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c12
-rw-r--r--drivers/i2c/busses/i2c-sprd.c112
-rw-r--r--drivers/i2c/busses/i2c-st.c72
-rw-r--r--drivers/i2c/busses/i2c-stm32.c36
-rw-r--r--drivers/i2c/busses/i2c-stm32.h2
-rw-r--r--drivers/i2c/busses/i2c-stm32f4.c69
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c1186
-rw-r--r--drivers/i2c/busses/i2c-stu300.c1015
-rw-r--r--drivers/i2c/busses/i2c-sun6i-p2wi.c63
-rw-r--r--drivers/i2c/busses/i2c-synquacer.c94
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c22
-rw-r--r--drivers/i2c/busses/i2c-tegra-bpmp.c131
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1980
-rw-r--r--drivers/i2c/busses/i2c-thunderx-pcidrv.c34
-rw-r--r--drivers/i2c/busses/i2c-tiny-usb.c37
-rw-r--r--drivers/i2c/busses/i2c-uniphier-f.c115
-rw-r--r--drivers/i2c/busses/i2c-uniphier.c104
-rw-r--r--drivers/i2c/busses/i2c-usbio.c321
-rw-r--r--drivers/i2c/busses/i2c-versatile.c14
-rw-r--r--drivers/i2c/busses/i2c-via.c27
-rw-r--r--drivers/i2c/busses/i2c-viai2c-common.c203
-rw-r--r--drivers/i2c/busses/i2c-viai2c-common.h85
-rw-r--r--drivers/i2c/busses/i2c-viai2c-wmt.c178
-rw-r--r--drivers/i2c/busses/i2c-viai2c-zhaoxin.c367
-rw-r--r--drivers/i2c/busses/i2c-viapro.c53
-rw-r--r--drivers/i2c/busses/i2c-viperboard.c41
-rw-r--r--drivers/i2c/busses/i2c-virtio.c278
-rw-r--r--drivers/i2c/busses/i2c-wmt.c474
-rw-r--r--drivers/i2c/busses/i2c-xgene-slimpro.c121
-rw-r--r--drivers/i2c/busses/i2c-xiic.c1248
-rw-r--r--drivers/i2c/busses/i2c-xlp9xx.c45
-rw-r--r--drivers/i2c/busses/i2c-xlr.c468
-rw-r--r--drivers/i2c/busses/i2c-zx2967.c611
-rw-r--r--drivers/i2c/busses/scx200_acb.c24
-rw-r--r--drivers/i2c/i2c-atr.c954
-rw-r--r--drivers/i2c/i2c-boardinfo.c22
-rw-r--r--drivers/i2c/i2c-core-acpi.c311
-rw-r--r--drivers/i2c/i2c-core-base.c992
-rw-r--r--drivers/i2c/i2c-core-of-prober.c415
-rw-r--r--drivers/i2c/i2c-core-of.c91
-rw-r--r--drivers/i2c/i2c-core-slave.c44
-rw-r--r--drivers/i2c/i2c-core-smbus.c154
-rw-r--r--drivers/i2c/i2c-core.h81
-rw-r--r--drivers/i2c/i2c-dev.c203
-rw-r--r--drivers/i2c/i2c-mux.c50
-rw-r--r--drivers/i2c/i2c-slave-eeprom.c109
-rw-r--r--drivers/i2c/i2c-slave-testunit.c290
-rw-r--r--drivers/i2c/i2c-smbus.c353
-rw-r--r--drivers/i2c/i2c-stub.c13
-rw-r--r--drivers/i2c/muxes/Kconfig47
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/i2c-arb-gpio-challenge.c101
-rw-r--r--drivers/i2c/muxes/i2c-demux-pinctrl.c47
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpio.c220
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpmux.c34
-rw-r--r--drivers/i2c/muxes/i2c-mux-ltc4306.c24
-rw-r--r--drivers/i2c/muxes/i2c-mux-mlxcpld.c166
-rw-r--r--drivers/i2c/muxes/i2c-mux-mule.c147
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca9541.c57
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c393
-rw-r--r--drivers/i2c/muxes/i2c-mux-pinctrl.c30
-rw-r--r--drivers/i2c/muxes/i2c-mux-reg.c33
205 files changed, 41611 insertions, 17530 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index c6b7fc7b67d6..c232054fddd6 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# I2C subsystem configuration
#
@@ -8,7 +9,7 @@ config I2C
tristate "I2C support"
select RT_MUTEXES
select IRQ_DOMAIN
- ---help---
+ help
I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
or System Management Bus is a subset of the I2C protocol. More
@@ -39,21 +40,13 @@ config I2C_BOARDINFO
bool
default y
-config I2C_COMPAT
- bool "Enable compatibility bits for old user-space"
- default y
- help
- Say Y here if you intend to run lm-sensors 3.1.1 or older, or any
- other user-space package which expects i2c adapters to be class
- devices. If you don't know, say Y.
-
config I2C_CHARDEV
tristate "I2C device interface"
help
Say Y here to use i2c-* device files, usually found in the /dev
directory on your system. They make it possible to have user-space
programs use the I2C bus. Information on how to do this is
- contained in the file <file:Documentation/i2c/dev-interface>.
+ contained in the file <file:Documentation/i2c/dev-interface.rst>.
This support is also available as a module. If so, the module
will be called i2c-dev.
@@ -70,6 +63,15 @@ config I2C_MUX
source "drivers/i2c/muxes/Kconfig"
+config I2C_ATR
+ tristate "I2C Address Translator (ATR) support" if COMPILE_TEST
+ help
+ Enable support for I2C Address Translator (ATR) chips.
+
+ An ATR allows accessing multiple I2C busses from a single
+ physical bus via address translation instead of bus selection as
+ i2c-muxes do.
+
config I2C_HELPER_AUTO
bool "Autoselect pertinent helper modules"
default y
@@ -100,23 +102,37 @@ source "drivers/i2c/busses/Kconfig"
config I2C_STUB
tristate "I2C/SMBus Test Stub"
depends on m
- default 'n'
help
This module may be useful to developers of SMBus client drivers,
especially for certain kinds of sensor chips.
If you do build this module, be sure to read the notes and warnings
- in <file:Documentation/i2c/i2c-stub>.
+ in <file:Documentation/i2c/i2c-stub.rst>.
If you don't know what to do here, definitely say N.
config I2C_SLAVE
bool "I2C slave support"
+ help
+ This enables Linux to act as an I2C slave device. Note that your I2C
+ bus master driver also needs to support this functionality. Please
+ read Documentation/i2c/slave-interface.rst for further details.
if I2C_SLAVE
config I2C_SLAVE_EEPROM
tristate "I2C eeprom slave driver"
+ help
+ This backend makes Linux behave like an I2C EEPROM. Please read
+ Documentation/i2c/slave-eeprom-backend.rst for further details.
+
+config I2C_SLAVE_TESTUNIT
+ tristate "I2C testunit driver"
+ help
+ This backend can be used to trigger test cases for I2C bus masters
+ which require a remote device with certain capabilities, e.g.
+ multi-master, SMBus Host Notify, etc. Please read
+ Documentation/i2c/slave-testunit-backend.rst for further details.
endif
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index bed6ba63c983..d27de18de46f 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -5,16 +5,19 @@
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
-i2c-core-objs := i2c-core-base.o i2c-core-smbus.o
+i2c-core-y := i2c-core-base.o i2c-core-smbus.o
i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o
-i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
-i2c-core-$(CONFIG_OF) += i2c-core-of.o
+i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
+i2c-core-$(CONFIG_OF) += i2c-core-of.o
+i2c-core-$(CONFIG_OF_DYNAMIC) += i2c-core-of-prober.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
+obj-$(CONFIG_I2C_ATR) += i2c-atr.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
+obj-$(CONFIG_I2C_SLAVE_TESTUNIT) += i2c-slave-testunit.o
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index f1cfe7e5508b..aea523529ef6 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# I2C algorithm drivers configuration
#
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index 215303f60d61..9b319a30d2ea 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the i2c algorithms
#
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index c33dcfb87993..6544d27e4419 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -1,21 +1,12 @@
-/* -------------------------------------------------------------------------
- * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters
- * -------------------------------------------------------------------------
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters
+ *
* Copyright (C) 1995-2000 Simon G. Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- * ------------------------------------------------------------------------- */
-
-/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
- <kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de> */
+ *
+ * With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+ * <kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de>
+ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -193,8 +184,9 @@ static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
/* read ack: SDA should be pulled down by slave, or it may
* NAK (usually to report problems with the data we wrote).
+ * Always report ACK if SDA is write-only.
*/
- ack = !getsda(adap); /* ack: sda is pulled low -> success */
+ ack = !adap->getsda || !getsda(adap); /* ack: sda is pulled low -> success */
bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
ack ? "A" : "NA");
@@ -247,71 +239,55 @@ static int test_bus(struct i2c_adapter *i2c_adap)
return -ENODEV;
}
+ if (adap->getsda == NULL)
+ pr_info("%s: SDA is write-only, testing not possible\n", name);
if (adap->getscl == NULL)
- pr_info("%s: Testing SDA only, SCL is not readable\n", name);
+ pr_info("%s: SCL is write-only, testing not possible\n", name);
- sda = getsda(adap);
- scl = (adap->getscl == NULL) ? 1 : getscl(adap);
+ sda = adap->getsda ? getsda(adap) : 1;
+ scl = adap->getscl ? getscl(adap) : 1;
if (!scl || !sda) {
- printk(KERN_WARNING
- "%s: bus seems to be busy (scl=%d, sda=%d)\n",
- name, scl, sda);
+ pr_warn("%s: bus seems to be busy (scl=%d, sda=%d)\n", name, scl, sda);
goto bailout;
}
sdalo(adap);
- sda = getsda(adap);
- scl = (adap->getscl == NULL) ? 1 : getscl(adap);
- if (sda) {
- printk(KERN_WARNING "%s: SDA stuck high!\n", name);
+ if (adap->getsda && getsda(adap)) {
+ pr_warn("%s: SDA stuck high!\n", name);
goto bailout;
}
- if (!scl) {
- printk(KERN_WARNING
- "%s: SCL unexpected low while pulling SDA low!\n",
- name);
+ if (adap->getscl && !getscl(adap)) {
+ pr_warn("%s: SCL unexpected low while pulling SDA low!\n", name);
goto bailout;
}
sdahi(adap);
- sda = getsda(adap);
- scl = (adap->getscl == NULL) ? 1 : getscl(adap);
- if (!sda) {
- printk(KERN_WARNING "%s: SDA stuck low!\n", name);
+ if (adap->getsda && !getsda(adap)) {
+ pr_warn("%s: SDA stuck low!\n", name);
goto bailout;
}
- if (!scl) {
- printk(KERN_WARNING
- "%s: SCL unexpected low while pulling SDA high!\n",
- name);
+ if (adap->getscl && !getscl(adap)) {
+ pr_warn("%s: SCL unexpected low while pulling SDA high!\n", name);
goto bailout;
}
scllo(adap);
- sda = getsda(adap);
- scl = (adap->getscl == NULL) ? 0 : getscl(adap);
- if (scl) {
- printk(KERN_WARNING "%s: SCL stuck high!\n", name);
+ if (adap->getscl && getscl(adap)) {
+ pr_warn("%s: SCL stuck high!\n", name);
goto bailout;
}
- if (!sda) {
- printk(KERN_WARNING
- "%s: SDA unexpected low while pulling SCL low!\n",
- name);
+ if (adap->getsda && !getsda(adap)) {
+ pr_warn("%s: SDA unexpected low while pulling SCL low!\n", name);
goto bailout;
}
sclhi(adap);
- sda = getsda(adap);
- scl = (adap->getscl == NULL) ? 1 : getscl(adap);
- if (!scl) {
- printk(KERN_WARNING "%s: SCL stuck low!\n", name);
+ if (adap->getscl && !getscl(adap)) {
+ pr_warn("%s: SCL stuck low!\n", name);
goto bailout;
}
- if (!sda) {
- printk(KERN_WARNING
- "%s: SDA unexpected low while pulling SCL high!\n",
- name);
+ if (adap->getsda && !getsda(adap)) {
+ pr_warn("%s: SDA unexpected low while pulling SCL high!\n", name);
goto bailout;
}
@@ -429,6 +405,10 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
unsigned char *temp = msg->buf;
int count = msg->len;
const unsigned flags = msg->flags;
+ struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+
+ if (!adap->getsda)
+ return -EOPNOTSUPP;
while (count > 0) {
inval = i2c_inb(i2c_adap);
@@ -612,11 +592,26 @@ bailout:
return ret;
}
+/*
+ * We print a warning when we are not flagged to support atomic transfers but
+ * will try anyhow. That's what the I2C core would do as well. Sadly, we can't
+ * modify the algorithm struct at probe time because this struct is exported
+ * 'const'.
+ */
+static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+
+ if (!adap->can_do_atomic)
+ dev_warn(&i2c_adap->dev, "not flagged for atomic transfers\n");
+
+ return bit_xfer(i2c_adap, msgs, num);
+}
+
static u32 bit_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
- I2C_FUNC_SMBUS_READ_BLOCK_DATA |
- I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL_ALL |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
@@ -624,8 +619,9 @@ static u32 bit_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
const struct i2c_algorithm i2c_bit_algo = {
- .master_xfer = bit_xfer,
- .functionality = bit_func,
+ .xfer = bit_xfer,
+ .xfer_atomic = bit_xfer_atomic,
+ .functionality = bit_func,
};
EXPORT_SYMBOL(i2c_bit_algo);
@@ -663,11 +659,15 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
if (ret < 0)
return ret;
- /* Complain if SCL can't be read */
- if (bit_adap->getscl == NULL) {
+ if (bit_adap->getsda == NULL)
+ dev_warn(&adap->dev, "Not I2C compliant: can't read SDA\n");
+
+ if (bit_adap->getscl == NULL)
dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
+
+ if (bit_adap->getsda == NULL || bit_adap->getscl == NULL)
dev_warn(&adap->dev, "Bus may be unreliable\n");
- }
+
return 0;
}
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 883a290f6a4d..ee86df4cff4b 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters
* Copyright (C) 2004 Arcom Control Systems
* Copyright (C) 2008 Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -39,7 +30,7 @@ static int i2c_debug;
#define pca_clock(adap) adap->i2c_clock
#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val)
#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON)
-#define pca_wait(adap) adap->wait_for_completion(adap->data)
+#define pca_wait(adap) adap->wait_for_completion_cb(adap->data)
static void pca_reset(struct i2c_algo_pca_data *adap)
{
@@ -50,8 +41,22 @@ static void pca_reset(struct i2c_algo_pca_data *adap)
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
pca_outw(adap, I2C_PCA_IND, 0xA5);
pca_outw(adap, I2C_PCA_IND, 0x5A);
+
+ /*
+ * After a reset we need to re-apply any configuration
+ * (calculated in pca_init) to get the bus in a working state.
+ */
+ pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IMODE);
+ pca_outw(adap, I2C_PCA_IND, adap->bus_settings.mode);
+ pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
+ pca_outw(adap, I2C_PCA_IND, adap->bus_settings.tlow);
+ pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
+ pca_outw(adap, I2C_PCA_IND, adap->bus_settings.thi);
+
+ pca_set_con(adap, I2C_PCA_CON_ENSIO);
} else {
adap->reset_chip(adap->data);
+ pca_set_con(adap, I2C_PCA_CON_ENSIO | adap->bus_settings.clock_freq);
}
}
@@ -323,7 +328,8 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
DEB2("BUS ERROR - SDA Stuck low\n");
pca_reset(adap);
goto out;
- case 0x90: /* Bus error - SCL stuck low */
+ case 0x78: /* Bus error - SCL stuck low (PCA9665) */
+ case 0x90: /* Bus error - SCL stuck low (PCA9564) */
DEB2("BUS ERROR - SCL Stuck low\n");
pca_reset(adap);
goto out;
@@ -355,8 +361,8 @@ static u32 pca_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm pca_algo = {
- .master_xfer = pca_xfer,
- .functionality = pca_func,
+ .xfer = pca_xfer,
+ .functionality = pca_func,
};
static unsigned int pca_probe_chip(struct i2c_adapter *adap)
@@ -431,13 +437,14 @@ static int pca_init(struct i2c_adapter *adap)
" Use the nominal frequency.\n", adap->name);
}
- pca_reset(pca_data);
-
clock = pca_clock(pca_data);
printk(KERN_INFO "%s: Clock frequency is %dkHz\n",
adap->name, freqs[clock]);
- pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock);
+ /* Store settings as these will be needed when the PCA chip is reset */
+ pca_data->bus_settings.clock_freq = clock;
+
+ pca_reset(pca_data);
} else {
int clock;
int mode;
@@ -468,17 +475,17 @@ static int pca_init(struct i2c_adapter *adap)
/* To avoid integer overflow, use clock/100 for calculations */
clock = pca_clock(pca_data) / 100;
- if (pca_data->i2c_clock > 1000000) {
+ if (pca_data->i2c_clock > I2C_MAX_FAST_MODE_PLUS_FREQ) {
mode = I2C_PCA_MODE_TURBO;
min_tlow = 14;
min_thi = 5;
raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
- } else if (pca_data->i2c_clock > 400000) {
+ } else if (pca_data->i2c_clock > I2C_MAX_FAST_MODE_FREQ) {
mode = I2C_PCA_MODE_FASTP;
min_tlow = 17;
min_thi = 9;
raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
- } else if (pca_data->i2c_clock > 100000) {
+ } else if (pca_data->i2c_clock > I2C_MAX_STANDARD_MODE_FREQ) {
mode = I2C_PCA_MODE_FAST;
min_tlow = 44;
min_thi = 20;
@@ -504,19 +511,15 @@ static int pca_init(struct i2c_adapter *adap)
thi = tlow * min_thi / min_tlow;
}
+ /* Store settings as these will be needed when the PCA chip is reset */
+ pca_data->bus_settings.mode = mode;
+ pca_data->bus_settings.tlow = tlow;
+ pca_data->bus_settings.thi = thi;
+
pca_reset(pca_data);
printk(KERN_INFO
"%s: Clock frequency is %dHz\n", adap->name, clock * 100);
-
- pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IMODE);
- pca_outw(pca_data, I2C_PCA_IND, mode);
- pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
- pca_outw(pca_data, I2C_PCA_IND, tlow);
- pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
- pca_outw(pca_data, I2C_PCA_IND, thi);
-
- pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
}
udelay(500); /* 500 us for oscillator to stabilise */
@@ -550,8 +553,8 @@ int i2c_pca_add_numbered_bus(struct i2c_adapter *adap)
}
EXPORT_SYMBOL(i2c_pca_add_numbered_bus);
-MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>, "
- "Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_DESCRIPTION("I2C-Bus PCA9564/PCA9665 algorithm");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 5c29a4d397cf..a87ecea7f510 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters
*
* Copyright (C) 1995-1997 Simon G. Vogl
* 1998-2000 Hans Berglund
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
* Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey
* <mbailey@littlefeet-inc.com>
@@ -28,20 +19,12 @@
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-pcf.h>
+#include <linux/string_choices.h>
#include "i2c-algo-pcf.h"
-#define DEB2(x) if (i2c_debug >= 2) x
-#define DEB3(x) if (i2c_debug >= 3) x /* print several statistical values */
-#define DEBPROTO(x) if (i2c_debug >= 9) x;
- /* debug the protocol by showing transferred bits */
#define DEF_TIMEOUT 16
-/*
- * module parameters:
- */
-static int i2c_debug;
-
/* setting states on the bus with the right timing: */
#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val)
@@ -55,27 +38,21 @@ static int i2c_debug;
static void i2c_start(struct i2c_algo_pcf_data *adap)
{
- DEBPROTO(printk(KERN_DEBUG "S "));
set_pcf(adap, 1, I2C_PCF_START);
}
static void i2c_repstart(struct i2c_algo_pcf_data *adap)
{
- DEBPROTO(printk(" Sr "));
set_pcf(adap, 1, I2C_PCF_REPSTART);
}
static void i2c_stop(struct i2c_algo_pcf_data *adap)
{
- DEBPROTO(printk("P\n"));
set_pcf(adap, 1, I2C_PCF_STOP);
}
static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status)
{
- DEB2(printk(KERN_INFO
- "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n",
- *status));
/*
* Cleanup from LAB -- reset and enable ESO.
* This resets the PCF8584; since we've lost the bus, no
@@ -96,9 +73,6 @@ static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status)
if (adap->lab_mdelay)
mdelay(adap->lab_mdelay);
- DEB2(printk(KERN_INFO
- "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n",
- get_pcf(adap, 1)));
}
static int wait_for_bb(struct i2c_algo_pcf_data *adap)
@@ -155,56 +129,48 @@ static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status)
*
* vdovikin: added detect code for PCF8584
*/
-static int pcf_init_8584 (struct i2c_algo_pcf_data *adap)
+static int pcf_init_8584(struct i2c_algo_pcf_data *adap)
{
unsigned char temp;
- DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: PCF state 0x%02x\n",
- get_pcf(adap, 1)));
-
/* S1=0x80: S0 selected, serial interface off */
set_pcf(adap, 1, I2C_PCF_PIN);
/*
* check to see S1 now used as R/W ctrl -
* PCF8584 does that when ESO is zero
*/
- if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp));
+ temp = get_pcf(adap, 1);
+ if ((temp & 0x7f) != 0)
return -ENXIO; /* definitely not PCF8584 */
- }
/* load own address in S0, effective address is (own << 1) */
i2c_outb(adap, get_own(adap));
/* check it's really written */
- if ((temp = i2c_inb(adap)) != get_own(adap)) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S0 (0x%02x).\n", temp));
+ temp = i2c_inb(adap);
+ if (temp != get_own(adap))
return -ENXIO;
- }
/* S1=0xA0, next byte in S2 */
set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1);
/* check to see S2 now selected */
- if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp));
+ temp = get_pcf(adap, 1);
+ if ((temp & 0x7f) != I2C_PCF_ES1)
return -ENXIO;
- }
/* load clock register S2 */
i2c_outb(adap, get_clock(adap));
/* check it's really written, the only 5 lowest bits does matter */
- if (((temp = i2c_inb(adap)) & 0x1f) != get_clock(adap)) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S2 (0x%02x).\n", temp));
+ temp = i2c_inb(adap);
+ if ((temp & 0x1f) != get_clock(adap))
return -ENXIO;
- }
/* Enable serial interface, idle, S0 selected */
set_pcf(adap, 1, I2C_PCF_IDLE);
/* check to see PCF is really idled and we can access status register */
- if ((temp = get_pcf(adap, 1)) != (I2C_PCF_PIN | I2C_PCF_BB)) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S1` (0x%02x).\n", temp));
+ temp = get_pcf(adap, 1);
+ if (temp != (I2C_PCF_PIN | I2C_PCF_BB))
return -ENXIO;
- }
printk(KERN_DEBUG "i2c-algo-pcf.o: detected and initialized PCF8584.\n");
@@ -217,9 +183,7 @@ static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf,
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
int wrcount, status, timeout;
- for (wrcount=0; wrcount<count; ++wrcount) {
- DEB2(dev_dbg(&i2c_adap->dev, "i2c_write: writing %2.2X\n",
- buf[wrcount] & 0xff));
+ for (wrcount = 0; wrcount < count; ++wrcount) {
i2c_outb(adap, buf[wrcount]);
timeout = wait_for_pin(adap, &status);
if (timeout) {
@@ -254,7 +218,8 @@ static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf,
/* increment number of bytes to read by one -- read dummy byte */
for (i = 0; i <= count; i++) {
- if ((wfp = wait_for_pin(adap, &status))) {
+ wfp = wait_for_pin(adap, &status);
+ if (wfp) {
if (wfp == -EINTR)
return -EINTR; /* arbitration lost */
@@ -288,7 +253,7 @@ static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf,
}
-static int pcf_doAddress(struct i2c_algo_pcf_data *adap,
+static void pcf_send_address(struct i2c_algo_pcf_data *adap,
struct i2c_msg *msg)
{
unsigned char addr = i2c_8bit_addr_from_msg(msg);
@@ -296,8 +261,6 @@ static int pcf_doAddress(struct i2c_algo_pcf_data *adap,
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
i2c_outb(adap, addr);
-
- return 0;
}
static int pcf_xfer(struct i2c_adapter *i2c_adap,
@@ -307,7 +270,7 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
struct i2c_msg *pmsg;
int i;
- int ret=0, timeout, status;
+ int timeout, status;
if (adap->xfer_begin)
adap->xfer_begin(adap->data);
@@ -315,20 +278,15 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
/* Check for bus busy */
timeout = wait_for_bb(adap);
if (timeout) {
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: "
- "Timeout waiting for BB in pcf_xfer\n");)
i = -EIO;
goto out;
}
- for (i = 0;ret >= 0 && i < num; i++) {
- pmsg = &msgs[i];
-
- DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n",
- pmsg->flags & I2C_M_RD ? "read" : "write",
- pmsg->len, pmsg->addr, i + 1, num);)
+ for (i = 0; i < num; i++) {
+ int ret;
- ret = pcf_doAddress(adap, pmsg);
+ pmsg = &msgs[i];
+ pcf_send_address(adap, pmsg);
/* Send START */
if (i == 0)
@@ -343,8 +301,6 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
goto out;
}
i2c_stop(adap);
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting "
- "for PIN(1) in pcf_xfer\n");)
i = -EREMOTEIO;
goto out;
}
@@ -352,35 +308,21 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
/* Check LRB (last rcvd bit - slave ack) */
if (status & I2C_PCF_LRB) {
i2c_stop(adap);
- DEB2(printk(KERN_ERR "i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");)
i = -EREMOTEIO;
goto out;
}
- DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
- i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
if (pmsg->flags & I2C_M_RD) {
ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len,
(i + 1 == num));
-
- if (ret != pmsg->len) {
- DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
- "only read %d bytes.\n",ret));
- } else {
- DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: read %d bytes.\n",ret));
- }
} else {
ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len,
(i + 1 == num));
-
- if (ret != pmsg->len) {
- DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
- "only wrote %d bytes.\n",ret));
- } else {
- DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: wrote %d bytes.\n",ret));
- }
}
+
+ if (ret < 0)
+ goto out;
}
out:
@@ -397,8 +339,8 @@ static u32 pcf_func(struct i2c_adapter *adap)
/* exported algorithm data: */
static const struct i2c_algorithm pcf_algo = {
- .master_xfer = pcf_xfer,
- .functionality = pcf_func,
+ .xfer = pcf_xfer,
+ .functionality = pcf_func,
};
/*
@@ -409,12 +351,11 @@ int i2c_pcf_add_bus(struct i2c_adapter *adap)
struct i2c_algo_pcf_data *pcf_adap = adap->algo_data;
int rval;
- DEB2(dev_dbg(&adap->dev, "hw routines registered.\n"));
-
/* register new adapter to i2c module... */
adap->algo = &pcf_algo;
- if ((rval = pcf_init_8584(pcf_adap)))
+ rval = pcf_init_8584(pcf_adap);
+ if (rval)
return rval;
rval = i2c_add_adapter(adap);
@@ -426,7 +367,3 @@ EXPORT_SYMBOL(i2c_pcf_add_bus);
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm");
MODULE_LICENSE("GPL");
-
-module_param(i2c_debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(i2c_debug,
- "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol");
diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h
index 262ee801975b..2448ab192ce5 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.h
+++ b/drivers/i2c/algos/i2c-algo-pcf.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* -------------------------------------------------------------------- */
/* i2c-pcf8584.h: PCF 8584 global defines */
/* -------------------------------------------------------------------- */
/* Copyright (C) 1996 Simon G. Vogl
1999 Hans Berglund
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details. */
+ */
/* -------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl> */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f2c681971201..cea87fcb4a1a 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Sensor device configuration
#
@@ -8,9 +9,16 @@ menu "I2C Hardware Bus support"
comment "PC SMBus host controller drivers"
depends on PCI
+config I2C_CCGX_UCSI
+ tristate
+ help
+ A common module to provide an API to instantiate UCSI device
+ for Cypress CCGx Type-C controller. Individual bus drivers
+ need to select this one on demand.
+
config I2C_ALI1535
tristate "ALI 1535"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the SMB
Host controller on Acer Labs Inc. (ALI) M1535 South Bridges. The SMB
@@ -22,7 +30,7 @@ config I2C_ALI1535
config I2C_ALI1563
tristate "ALI 1563"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the SMB
Host controller on Acer Labs Inc. (ALI) M1563 South Bridges. The SMB
@@ -34,7 +42,7 @@ config I2C_ALI1563
config I2C_ALI15X3
tristate "ALI 15x3"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the
Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces.
@@ -44,7 +52,7 @@ config I2C_ALI15X3
config I2C_AMD756
tristate "AMD 756/766/768/8111 and nVidia nForce"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the AMD
756/766/768 mainboard I2C interfaces. The driver also includes
@@ -54,22 +62,9 @@ config I2C_AMD756
This driver can also be built as a module. If so, the module
will be called i2c-amd756.
-config I2C_AMD756_S4882
- tristate "SMBus multiplexing on the Tyan S4882"
- depends on I2C_AMD756 && X86
- help
- Enabling this option will add specific SMBus support for the Tyan
- S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed
- over 8 different channels, where the various memory module EEPROMs
- and temperature sensors live. Saying yes here will give you access
- to these in addition to the trunk.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-amd756-s4882.
-
config I2C_AMD8111
tristate "AMD 8111"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the
second (SMBus 2.0) AMD 8111 mainboard I2C interface.
@@ -77,6 +72,33 @@ config I2C_AMD8111
This driver can also be built as a module. If so, the module
will be called i2c-amd8111.
+config I2C_AMD_MP2
+ tristate "AMD MP2 PCIe"
+ depends on PCI && ACPI
+ help
+ If you say yes to this option, support will be included for the AMD
+ MP2 PCIe I2C adapter.
+
+ This driver can also be built as modules. If so, the modules will
+ be called i2c-amd-mp2-pci and i2c-amd-mp2-plat.
+
+config I2C_AMD_ASF
+ tristate "AMD ASF I2C Controller Support"
+ depends on I2C_PIIX4
+ select I2C_SLAVE
+ help
+ This option enables support for the AMD ASF (Alert Standard Format)
+ I2C controller. The AMD ASF controller is an SMBus controller with
+ built-in ASF functionality, allowing it to issue generic SMBus
+ packets and communicate with the DASH controller using MCTP over
+ ASF.
+
+ If you have an AMD system with ASF support and want to enable this
+ functionality, say Y or M here. If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will
+ be called i2c_amd_asf_plat.
+
config I2C_HIX5HD2
tristate "Hix5hd2 high-speed I2C driver"
depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
@@ -89,7 +111,8 @@ config I2C_HIX5HD2
config I2C_I801
tristate "Intel 82801 (ICH/PCH)"
- depends on PCI
+ depends on PCI && HAS_IOPORT
+ select P2SB if X86
select CHECK_SIGNATURE if X86 && DMI
select I2C_SMBUS
help
@@ -131,13 +154,34 @@ config I2C_I801
Cannon Lake (PCH)
Cedar Fork (PCH)
Ice Lake (PCH)
+ Comet Lake (PCH)
+ Elkhart Lake (PCH)
+ Tiger Lake (PCH)
+ Jasper Lake (SOC)
+ Emmitsburg (PCH)
+ Alder Lake (PCH)
+ Raptor Lake (PCH)
+ Meteor Lake (SOC and PCH)
+ Birch Stream (SOC)
+ Arrow Lake (SOC)
+ Panther Lake (SOC)
+ Wildcat Lake (SOC)
+ Diamond Rapids (SOC)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
+config I2C_I801_MUX
+ def_bool I2C_I801
+ depends on DMI && I2C_MUX_GPIO
+ depends on !(I2C_I801=y && I2C_MUX=m)
+ help
+ Optional support for multiplexed SMBUS on certain systems with
+ more than 8 memory slots.
+
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
@@ -158,7 +202,8 @@ config I2C_ISMT
config I2C_PIIX4
tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
- depends on PCI
+ depends on PCI && HAS_IOPORT
+ select I2C_SMBUS
help
If you say yes to this option, support will be included for the Intel
PIIX4 family of mainboard I2C interfaces. Specifically, the following
@@ -175,6 +220,7 @@ config I2C_PIIX4
AMD Hudson-2
AMD ML
AMD CZ
+ Hygon CZ
Serverworks OSB4
Serverworks CSB5
Serverworks CSB6
@@ -203,7 +249,7 @@ config I2C_CHT_WC
config I2C_NFORCE2
tristate "Nvidia nForce2, nForce3 and nForce4"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the Nvidia
nForce2, nForce3 and nForce4 families of mainboard I2C interfaces.
@@ -211,22 +257,10 @@ config I2C_NFORCE2
This driver can also be built as a module. If so, the module
will be called i2c-nforce2.
-config I2C_NFORCE2_S4985
- tristate "SMBus multiplexing on the Tyan S4985"
- depends on I2C_NFORCE2 && X86
- help
- Enabling this option will add specific SMBus support for the Tyan
- S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed
- over 4 different channels, where the various memory module EEPROMs
- live. Saying yes here will give you access to these in addition
- to the trunk.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-nforce2-s4985.
-
config I2C_NVIDIA_GPU
tristate "NVIDIA GPU I2C controller"
depends on PCI
+ select I2C_CCGX_UCSI
help
If you say yes to this option, support will be included for the
NVIDIA GPU I2C controller which is used to communicate with the GPU's
@@ -235,7 +269,7 @@ config I2C_NVIDIA_GPU
config I2C_SIS5595
tristate "SiS 5595"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the
SiS5595 SMBus (a subset of I2C) interface.
@@ -245,7 +279,7 @@ config I2C_SIS5595
config I2C_SIS630
tristate "SiS 630/730/964"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the
SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface.
@@ -255,7 +289,7 @@ config I2C_SIS630
config I2C_SIS96X
tristate "SiS 96x"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the SiS
96x SMBus (a subset of I2C) interfaces. Specifically, the following
@@ -273,18 +307,18 @@ config I2C_SIS96X
config I2C_VIA
tristate "VIA VT82C586B"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the VIA
- 82C586B I2C interface
+ 82C586B I2C interface
This driver can also be built as a module. If so, the module
will be called i2c-via.
config I2C_VIAPRO
tristate "VIA VT82C596/82C686/82xx and CX700/VX8xx/VX900"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
If you say yes to this option, support will be included for the VIA
VT82C596 and later SMBus interface. Specifically, the following
@@ -306,6 +340,16 @@ config I2C_VIAPRO
if ACPI
+config I2C_ZHAOXIN
+ tristate "Zhaoxin I2C Interface"
+ depends on PCI || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ ZHAOXIN I2C interface
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-zhaoxin.
+
comment "ACPI drivers"
config I2C_SCMI
@@ -351,7 +395,8 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)"
config I2C_ALTERA
tristate "Altera Soft IP I2C"
- depends on (ARCH_SOCFPGA || NIOS2) && OF
+ depends on ARCH_INTEL_SOCFPGA || NIOS2 || COMPILE_TEST
+ depends on OF
help
If you say yes to this option, support will be included for the
Altera Soft IP I2C interfaces on SoCFPGA and Nios2 architectures.
@@ -371,7 +416,7 @@ config I2C_ASPEED
config I2C_AT91
tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
- depends on ARCH_AT91
+ depends on ARCH_MICROCHIP || COMPILE_TEST
help
This supports the use of the I2C interface on Atmel AT91
processors.
@@ -387,6 +432,19 @@ config I2C_AT91
the latency to fill the transmission register is too long. If you
are facing this situation, use the i2c-gpio driver.
+config I2C_AT91_SLAVE_EXPERIMENTAL
+ bool "Microchip AT91 I2C experimental slave mode"
+ depends on I2C_AT91
+ select I2C_SLAVE
+ help
+ If you say yes to this option, support for the slave mode will be
+ added. Caution: do not use it for production. This feature has not
+ been tested in a heavy way, help wanted.
+ There are known bugs:
+ - It can hang, on a SAMA5D4, after several transfers.
+ - There are some mismatches with a SAMA5D4 as slave and a SAMA5D2 as
+ master.
+
config I2C_AU1550
tristate "Au1550/Au1200/Au1300 SMBus interface"
depends on MIPS_ALCHEMY
@@ -401,6 +459,7 @@ config I2C_AXXIA
tristate "Axxia I2C controller"
depends on ARCH_AXXIA || COMPILE_TEST
default ARCH_AXXIA
+ select I2C_SLAVE
help
Say yes if you want to support the I2C bus on Axxia platforms.
@@ -410,7 +469,8 @@ config I2C_AXXIA
config I2C_BCM2835
tristate "Broadcom BCM2835 I2C controller"
- depends on ARCH_BCM2835
+ depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
+ depends on COMMON_CLK
help
If you say yes to this option, support will be included for the
BCM2835 I2C controller.
@@ -424,6 +484,7 @@ config I2C_BCM_IPROC
tristate "Broadcom iProc I2C controller"
depends on ARCH_BCM_IPROC || COMPILE_TEST
default ARCH_BCM_IPROC
+ select I2C_SLAVE
help
If you say yes to this option, support will be included for the
Broadcom iProc I2C controller.
@@ -432,8 +493,8 @@ config I2C_BCM_IPROC
config I2C_BCM_KONA
tristate "BCM Kona I2C adapter"
- depends on ARCH_BCM_MOBILE
- default y
+ depends on ARCH_BCM_MOBILE || COMPILE_TEST
+ default y if ARCH_BCM_MOBILE
help
If you say yes to this option, support will be included for the
I2C interface on the Broadcom Kona family of processors.
@@ -442,9 +503,9 @@ config I2C_BCM_KONA
config I2C_BRCMSTB
tristate "BRCM Settop/DSL I2C controller"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_63XX || \
- COMPILE_TEST
- default y
+ depends on ARCH_BCM2835 || ARCH_BCMBCA || ARCH_BRCMSTB || \
+ BMIPS_GENERIC || COMPILE_TEST
+ default ARCH_BCM2835 || ARCH_BCMBCA || ARCH_BRCMSTB || BMIPS_GENERIC
help
If you say yes to this option, support will be included for the
I2C interface on the Broadcom Settop/DSL SoCs.
@@ -453,7 +514,7 @@ config I2C_BRCMSTB
config I2C_CADENCE
tristate "Cadence I2C Controller"
- depends on ARCH_ZYNQ || ARM64 || XTENSA
+ depends on ARCH_ZYNQ || ARM64 || XTENSA || RISCV || COMPILE_TEST
help
Say yes here to select Cadence I2C Host Controller. This controller is
e.g. used by Xilinx Zynq.
@@ -468,6 +529,16 @@ config I2C_CBUS_GPIO
This driver can also be built as a module. If so, the module
will be called i2c-cbus-gpio.
+config I2C_CGBC
+ tristate "Congatec I2C Controller"
+ depends on MFD_CGBC
+ help
+ This driver supports the 2 I2C interfaces on the Congatec Board
+ Controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called i2c-cgbc.ko.
+
config I2C_CPM
tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
depends on CPM1 || CPM2
@@ -480,7 +551,7 @@ config I2C_CPM
config I2C_DAVINCI
tristate "DaVinci I2C driver"
- depends on ARCH_DAVINCI || ARCH_KEYSTONE
+ depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST
help
Support for TI DaVinci I2C controller driver.
@@ -492,44 +563,66 @@ config I2C_DAVINCI
For details please see http://www.ti.com/davinci
config I2C_DESIGNWARE_CORE
- tristate
+ tristate "Synopsys DesignWare I2C adapter"
+ select REGMAP
+ help
+ This option enables support for the Synopsys DesignWare I2C adapter.
+ This driver includes support for the I2C host on the Synopsys
+ Designware I2C adapter.
+
+ To compile the driver as a module, choose M here: the module will be
+ called i2c-designware-core.
+
+if I2C_DESIGNWARE_CORE
+
+config I2C_DESIGNWARE_SLAVE
+ bool "Synopsys DesignWare Slave"
+ select I2C_SLAVE
+ help
+ If you say yes to this option, support will be included for the
+ Synopsys DesignWare I2C slave adapter.
config I2C_DESIGNWARE_PLATFORM
- tristate "Synopsys DesignWare Platform"
- select I2C_DESIGNWARE_CORE
+ tristate "Synopsys DesignWare Platform driver"
depends on (ACPI && COMMON_CLK) || !ACPI
+ select MFD_SYSCON if MIPS_BAIKAL_T1
+ default I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
- Synopsys DesignWare I2C adapter.
+ Synopsys DesignWare I2C adapters on the platform bus.
This driver can also be built as a module. If so, the module
will be called i2c-designware-platform.
-config I2C_DESIGNWARE_SLAVE
- bool "Synopsys DesignWare Slave"
- select I2C_SLAVE
- depends on I2C_DESIGNWARE_PLATFORM
+config I2C_DESIGNWARE_AMDISP
+ tristate "Synopsys DesignWare Platform for AMDISP"
+ depends on DRM_AMD_ISP || COMPILE_TEST
+ depends on I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
- Synopsys DesignWare I2C slave adapter.
+ AMDISP Synopsys DesignWare I2C adapter.
- This is not a standalone module, this module compiles together with
- i2c-designware-core.
+ This driver can also be built as a module. If so, the module
+ will be called amd_isp_i2c_designware.
-config I2C_DESIGNWARE_PCI
- tristate "Synopsys DesignWare PCI"
+config I2C_DESIGNWARE_AMDPSP
+ bool "AMD PSP I2C semaphore support"
+ depends on ACPI
+ depends on CRYPTO_DEV_SP_PSP
depends on PCI
- select I2C_DESIGNWARE_CORE
+ depends on I2C_DESIGNWARE_PLATFORM
+ depends on (I2C_DESIGNWARE_PLATFORM=y && CRYPTO_DEV_CCP_DD=y) || \
+ (I2C_DESIGNWARE_PLATFORM=m && CRYPTO_DEV_CCP_DD)
help
- If you say yes to this option, support will be included for the
- Synopsys DesignWare I2C adapter. Only master mode is supported.
+ This driver enables managed host access to the selected I2C bus shared
+ between AMD CPU and AMD PSP.
- This driver can also be built as a module. If so, the module
- will be called i2c-designware-pci.
+ You should say Y if running on an AMD system equipped with the PSP.
config I2C_DESIGNWARE_BAYTRAIL
bool "Intel Baytrail I2C semaphore support"
depends on ACPI
+ depends on I2C_DESIGNWARE_PLATFORM
depends on (I2C_DESIGNWARE_PLATFORM=m && IOSF_MBI) || \
(I2C_DESIGNWARE_PLATFORM=y && IOSF_MBI=y)
help
@@ -539,22 +632,29 @@ config I2C_DESIGNWARE_BAYTRAIL
the platform firmware controlling it. You should say Y if running on
a BayTrail system using the AXP288.
+config I2C_DESIGNWARE_PCI
+ tristate "Synopsys DesignWare PCI driver"
+ depends on PCI
+ select I2C_CCGX_UCSI
+ help
+ If you say yes to this option, support will be included for the
+ Synopsys DesignWare I2C adapters on the PCI bus. Only master mode is
+ supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-designware-pci.
+
+endif
+
config I2C_DIGICOLOR
tristate "Conexant Digicolor I2C driver"
- depends on ARCH_DIGICOLOR
+ depends on ARCH_DIGICOLOR || COMPILE_TEST
help
Support for Conexant Digicolor SoCs (CX92755) I2C controller driver.
This driver can also be built as a module. If so, the module
will be called i2c-digicolor.
-config I2C_EFM32
- tristate "EFM32 I2C controller"
- depends on ARCH_EFM32 || COMPILE_TEST
- help
- This driver supports the i2c block found in Energy Micro's EFM32
- SoCs.
-
config I2C_EG20T
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
@@ -579,11 +679,15 @@ config I2C_EMEV2
I2C interface on the Renesas Electronics EM/EV family of processors.
config I2C_EXYNOS5
- tristate "Exynos5 high-speed I2C driver"
- depends on ARCH_EXYNOS && OF
- default y
+ tristate "Exynos high-speed I2C driver"
+ depends on OF
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ default y if ARCH_EXYNOS
help
- High-speed I2C controller on Exynos5 based Samsung SoCs.
+ High-speed I2C controller on Samsung Exynos5 and newer Samsung SoCs:
+ Exynos5250, Exynos5260, Exynos5410, Exynos542x, Exynos5800,
+ Exynos5433, Exynos7, Exynos850 and ExynosAutoV9.
+ Choose Y here only if you build for such Samsung SoC.
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
@@ -601,9 +705,16 @@ config I2C_GPIO_FAULT_INJECTOR
faults to an I2C bus, so another bus master can be stress-tested.
This is for debugging. If unsure, say 'no'.
+config I2C_GXP
+ tristate "GXP I2C Interface"
+ depends on ARCH_HPE_GXP || COMPILE_TEST
+ help
+ This enables support for GXP I2C interface. The I2C engines can be
+ either I2C master or I2C slaves.
+
config I2C_HIGHLANDER
tristate "Highlander FPGA SMBus interface"
- depends on SH_HIGHLANDER
+ depends on SH_HIGHLANDER || COMPILE_TEST
help
If you say yes to this option, support will be included for
the SMBus interface located in the FPGA on various Highlander
@@ -613,6 +724,16 @@ config I2C_HIGHLANDER
This driver can also be built as a module. If so, the module
will be called i2c-highlander.
+config I2C_HISI
+ tristate "HiSilicon I2C controller"
+ depends on ARM64 || COMPILE_TEST
+ help
+ Say Y here if you want to have Hisilicon I2C controller support
+ available on the Kunpeng Server.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-hisi.
+
config I2C_IBM_IIC
tristate "IBM PPC 4xx on-chip I2C interface"
depends on 4xx
@@ -635,43 +756,75 @@ config I2C_IMG
config I2C_IMX
tristate "IMX I2C interface"
- depends on ARCH_MXC || ARCH_LAYERSCAPE || COLDFIRE
+ depends on ARCH_MXC || ARCH_LAYERSCAPE || ARCH_S32 || COLDFIRE \
+ || COMPILE_TEST
+ select I2C_SLAVE
help
Say Y here if you want to use the IIC bus controller on
- the Freescale i.MX/MXC, Layerscape or ColdFire processors.
+ the Freescale i.MX/MXC/S32G, Layerscape or ColdFire processors.
- This driver can also be built as a module. If so, the module
+ This driver can also be built as a module. If so, the module
will be called i2c-imx.
config I2C_IMX_LPI2C
tristate "IMX Low Power I2C interface"
depends on ARCH_MXC || COMPILE_TEST
+ select I2C_SLAVE
help
- Say Y here if you want to use the Low Power IIC bus controller
- on the Freescale i.MX processors.
+ Say Y here if you want to use the Low Power IIC bus controller
+ on the Freescale i.MX processors.
- This driver can also be built as a module. If so, the module
- will be called i2c-imx-lpi2c.
+ This driver can also be built as a module. If so, the module
+ will be called i2c-imx-lpi2c.
config I2C_IOP3XX
- tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
- depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
+ tristate "Intel IXP4xx on-chip I2C interface"
+ depends on ARCH_IXP4XX || COMPILE_TEST
help
Say Y here if you want to use the IIC bus controller on
- the Intel IOPx3xx I/O Processors or IXP4xx Network Processors.
+ the Intel IXP4xx Network Processors.
This driver can also be built as a module. If so, the module
will be called i2c-iop3xx.
config I2C_JZ4780
tristate "JZ4780 I2C controller interface support"
- depends on MACH_JZ4780 || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
help
If you say yes to this option, support will be included for the
Ingenic JZ4780 I2C controller.
If you don't know what to do here, say N.
+config I2C_K1
+ tristate "SpacemiT K1 I2C adapter"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on OF
+ help
+ This option enables support for the I2C interface on the SpacemiT K1
+ platform.
+
+ If you enable this configuration, the kernel will include support for
+ the I2C adapter specific to the SpacemiT K1 platform. This driver can
+ be used to manage I2C bus transactions, which are necessary for
+ interfacing with I2C peripherals such as sensors, EEPROMs, and other
+ devices.
+
+ This driver can also be built as a module. If so, the
+ module will be called `i2c-k1`.
+
+config I2C_KEBA
+ tristate "KEBA I2C controller support"
+ depends on HAS_IOMEM
+ depends on KEBA_CP500 || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This driver supports the I2C controller found in KEBA system FPGA
+ devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-keba.
+
config I2C_KEMPLD
tristate "Kontron COM I2C Controller"
depends on MFD_KEMPLD
@@ -692,13 +845,51 @@ config I2C_LPC2K
This driver can also be built as a module. If so, the module
will be called i2c-lpc2k.
+config I2C_LS2X
+ tristate "Loongson LS2X I2C adapter"
+ depends on MACH_LOONGSON64 || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Loongson-2K SoCs and Loongson LS7A bridge
+ chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ls2x.
+
+config I2C_MLXBF
+ tristate "Mellanox BlueField I2C controller"
+ depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST
+ depends on ACPI
+ select I2C_SLAVE
+ help
+ Enabling this option will add I2C SMBus support for Mellanox BlueField
+ system.
+
+ This driver can also be built as a module. If so, the module will be
+ called i2c-mlxbf.
+
+ This driver implements an I2C SMBus host controller and enables both
+ master and slave functions.
+
config I2C_MESON
tristate "Amlogic Meson I2C controller"
depends on ARCH_MESON || COMPILE_TEST
+ depends on COMMON_CLK
help
If you say yes to this option, support will be included for the
I2C interface on the Amlogic Meson family of SoCs.
+config I2C_MICROCHIP_CORE
+ tristate "Microchip FPGA I2C controller"
+ depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
+ depends on OF
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on Microchip FPGAs.
+
+ This driver can also be built as a module. If so, the module will be
+ called i2c-microchip-core.
+
config I2C_MPC
tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
depends on PPC
@@ -719,9 +910,16 @@ config I2C_MT65XX
If you want to use MediaTek(R) I2C interface, say Y or M here.
If unsure, say N.
+config I2C_MT7621
+ tristate "MT7621/MT7628 I2C Controller"
+ depends on (RALINK && (SOC_MT7620 || SOC_MT7621)) || ARCH_AIROHA || COMPILE_TEST
+ help
+ Say Y here to include support for I2C controller in the
+ MediaTek MT7621/MT7628 SoCs.
+
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
- depends on MV64X60 || PLAT_ORION || ARCH_SUNXI || ARCH_MVEBU
+ depends on PLAT_ORION || ARCH_SUNXI || ARCH_MVEBU || COMPILE_TEST
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -732,7 +930,7 @@ config I2C_MV64XXX
config I2C_MXS
tristate "Freescale i.MX28 I2C interface"
- depends on SOC_IMX28
+ depends on SOC_IMX28 || COMPILE_TEST
select STMP_DEVICE
help
Say Y here if you want to use the I2C bus controller on
@@ -743,12 +941,21 @@ config I2C_MXS
config I2C_NOMADIK
tristate "ST-Ericsson Nomadik/Ux500 I2C Controller"
- depends on ARM_AMBA
+ depends on ARM_AMBA || COMPILE_TEST
help
If you say yes to this option, support will be included for the
I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
as well as the STA2X11 PCIe I/O HUB.
+config I2C_NPCM
+ tristate "Nuvoton I2C Controller"
+ depends on ARCH_NPCM || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ Nuvoton I2C controller, which is available on the NPCM BMC
+ controllers.
+ Driver can also support slave mode (select I2C_SLAVE).
+
config I2C_OCORES
tristate "OpenCores I2C Controller"
help
@@ -761,8 +968,9 @@ config I2C_OCORES
config I2C_OMAP
tristate "OMAP I2C adapter"
- depends on ARCH_OMAP || ARCH_K3
- default y if MACH_OMAP_H3 || MACH_OMAP_OSK
+ depends on ARCH_OMAP || ARCH_K3 || COMPILE_TEST
+ default MACH_OMAP_OSK
+ select MULTIPLEXER
help
If you say yes to this option, support will be included for the
I2C interface on the Texas Instruments OMAP1/2 family of processors.
@@ -782,10 +990,20 @@ config I2C_PASEMI
help
Supports the PA Semi PWRficient on-chip SMBus interfaces.
+config I2C_APPLE
+ tristate "Apple SMBus platform driver"
+ depends on !I2C_PASEMI
+ depends on ARCH_APPLE || COMPILE_TEST
+ help
+ Say Y here if you want to use the I2C controller present on Apple
+ Silicon chips such as the M1.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-apple.
+
config I2C_PCA_PLATFORM
tristate "PCA9564/PCA9665 as platform device"
select I2C_ALGOPCA
- default n
help
This driver supports a memory mapped Philips PCA9564/PCA9665
parallel bus to I2C bus controller.
@@ -793,18 +1011,9 @@ config I2C_PCA_PLATFORM
This driver can also be built as a module. If so, the module
will be called i2c-pca-platform.
-config I2C_PMCMSP
- tristate "PMC MSP I2C TWI Controller"
- depends on PMC_MSP
- help
- This driver supports the PMC TWI controller on MSP devices.
-
- This driver can also be built as module. If so, the module
- will be called i2c-pmcmsp.
-
config I2C_PNX
tristate "I2C bus support for Philips PNX and NXP LPC targets"
- depends on ARCH_LPC32XX
+ depends on ARCH_LPC32XX || COMPILE_TEST
help
This driver supports the Philips IP3204 I2C IP block master and/or
slave controller
@@ -812,20 +1021,9 @@ config I2C_PNX
This driver can also be built as a module. If so, the module
will be called i2c-pnx.
-config I2C_PUV3
- tristate "PKUnity v3 I2C bus support"
- depends on UNICORE32 && ARCH_PUV3
- select I2C_ALGOBIT
- help
- This driver supports the I2C IP inside the PKUnity-v3 SoC.
- This I2C bus controller is under AMBA/AXI bus.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-puv3.
-
config I2C_PXA
tristate "Intel PXA2XX I2C adapter"
- depends on ARCH_PXA || ARCH_MMP || ARCH_MVEBU || (X86_32 && PCI && OF)
+ depends on ARCH_PXA || ARCH_MMP || ARCH_MVEBU || (X86_32 && PCI && OF) || COMPILE_TEST
help
If you have devices in the PXA I2C bus, say yes to this option.
This driver can also be built as a module. If so, the module
@@ -837,11 +1035,22 @@ config I2C_PXA_PCI
config I2C_PXA_SLAVE
bool "Intel PXA2XX I2C Slave comms support"
depends on I2C_PXA && !X86_32
+ select I2C_SLAVE
help
Support I2C slave mode communications on the PXA I2C bus. This
is necessary for systems where the PXA may be a target on the
I2C bus.
+config I2C_QCOM_CCI
+ tristate "Qualcomm Camera Control Interface"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ built-in camera control interface on the Qualcomm SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-qcom-cci.
+
config I2C_QCOM_GENI
tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
depends on ARCH_QCOM || COMPILE_TEST
@@ -857,7 +1066,7 @@ config I2C_QCOM_GENI
config I2C_QUP
tristate "Qualcomm QUP based I2C controller"
- depends on ARCH_QCOM
+ depends on ARCH_QCOM || COMPILE_TEST
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Qualcomm SoCs.
@@ -885,19 +1094,32 @@ config I2C_RK3X
This driver can also be built as a module. If so, the module will
be called i2c-rk3x.
-config HAVE_S3C2410_I2C
- bool
+config I2C_RTL9300
+ tristate "Realtek RTL9300 I2C controller"
+ depends on MACH_REALTEK_RTL || COMPILE_TEST
+ help
+ Say Y here to include support for the I2C controller in Realtek
+ RTL9300 SoCs.
+
+ This driver can also be built as a module. If so, the module will
+ be called i2c-rtl9300.
+
+config I2C_RZV2M
+ tristate "Renesas RZ/V2M adapter"
+ depends on ARCH_RENESAS || COMPILE_TEST
help
- This will include I2C support for Samsung SoCs. If you want to
- include I2C support for any machine, kindly select this in the
- respective Kconfig file.
+ If you say yes to this option, support will be included for the
+ Renesas RZ/V2M I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-rzv2m.
config I2C_S3C2410
- tristate "S3C2410 I2C Driver"
- depends on HAVE_S3C2410_I2C
+ tristate "S3C/Exynos I2C Driver"
+ depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || COMPILE_TEST
help
Say Y here to include support for I2C controller in the
- Samsung SoCs.
+ Samsung SoCs (S3C, S5Pv210, Exynos).
config I2C_SH7760
tristate "Renesas SH7760 I2C Controller"
@@ -930,26 +1152,17 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
-config I2C_SIRF
- tristate "CSR SiRFprimaII I2C interface"
- depends on ARCH_SIRF
- help
- If you say yes to this option, support will be included for the
- CSR SiRFprimaII I2C interface.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-sirf.
-
config I2C_SPRD
- bool "Spreadtrum I2C interface"
- depends on I2C=y && ARCH_SPRD
+ tristate "Spreadtrum I2C interface"
+ depends on I2C=y && (ARCH_SPRD || COMPILE_TEST)
+ depends on COMMON_CLK
help
If you say yes to this option, support will be included for the
Spreadtrum I2C interface.
config I2C_ST
tristate "STMicroelectronics SSC I2C support"
- depends on ARCH_STI
+ depends on ARCH_STI || COMPILE_TEST
help
Enable this option to add support for STMicroelectronics SoCs
hardware SSC (Synchronous Serial Controller) as an I2C controller.
@@ -971,6 +1184,7 @@ config I2C_STM32F7
tristate "STMicroelectronics STM32F7 I2C support"
depends on ARCH_STM32 || COMPILE_TEST
select I2C_SLAVE
+ select I2C_SMBUS
help
Enable this option to add support for STM32 I2C controller embedded
in STM32F7 SoCs.
@@ -978,19 +1192,6 @@ config I2C_STM32F7
This driver can also be built as module. If so, the module
will be called i2c-stm32f7.
-config I2C_STU300
- tristate "ST Microelectronics DDC I2C interface"
- depends on MACH_U300
- default y if MACH_U300
- help
- If you say yes to this option, support will be included for the
- I2C interface from ST Microelectronics simply called "DDC I2C"
- supporting both I2C and DDC, used in e.g. the U300 series
- mobile platforms.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-stu300.
-
config I2C_SUN6I_P2WI
tristate "Allwinner sun6i internal P2WI controller"
depends on RESET_CONTROLLER
@@ -1016,15 +1217,16 @@ config I2C_SYNQUACER
config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA || (COMPILE_TEST && (ARC || ARM || ARM64 || M68K || RISCV || SUPERH || SPARC))
+ # COMPILE_TEST needs architectures with readsX()/writesX() primitives
help
If you say yes to this option, support will be included for the
I2C controller embedded in NVIDIA Tegra SOCs
config I2C_TEGRA_BPMP
tristate "NVIDIA Tegra BPMP I2C controller"
- depends on TEGRA_BPMP
- default y
+ depends on TEGRA_BPMP || COMPILE_TEST
+ default y if TEGRA_BPMP
help
If you say yes to this option, support will be included for the I2C
controller embedded in NVIDIA Tegra SoCs accessed via the BPMP.
@@ -1062,7 +1264,7 @@ config I2C_VERSATILE
config I2C_WMT
tristate "Wondermedia WM8xxx SoC I2C bus support"
- depends on ARCH_VT8500
+ depends on ARCH_VT8500 || COMPILE_TEST
help
Say yes if you want to support the I2C bus on Wondermedia 8xxx-series
SoCs.
@@ -1101,22 +1303,12 @@ config I2C_XILINX
This driver can also be built as a module. If so, the module
will be called xilinx_i2c.
-config I2C_XLR
- tristate "Netlogic XLR and Sigma Designs I2C support"
- depends on CPU_XLR || ARCH_TANGO
- help
- This driver enables support for the on-chip I2C interface of
- the Netlogic XLR/XLS MIPS processors and Sigma Designs SOCs.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-xlr.
-
config I2C_XLP9XX
- tristate "XLP9XX I2C support"
- depends on CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST
+ tristate "Cavium ThunderX2 I2C support"
+ depends on ARCH_THUNDER2 || COMPILE_TEST
help
This driver enables support for the on-chip I2C interface of
- the Broadcom XLP9xx/XLP5xx MIPS and Vulcan ARM64 processors.
+ the Cavium ThunderX2 processors. (Originally on Netlogic XLP SoCs.)
This driver can also be built as a module. If so, the module will
be called i2c-xlp9xx.
@@ -1125,6 +1317,8 @@ config I2C_RCAR
tristate "Renesas R-Car I2C Controller"
depends on ARCH_RENESAS || COMPILE_TEST
select I2C_SLAVE
+ select I2C_SMBUS
+ select RESET_CONTROLLER if ARCH_RCAR_GEN3 || ARCH_RCAR_GEN4
help
If you say yes to this option, support will be included for the
R-Car I2C controller.
@@ -1145,63 +1339,81 @@ config I2C_DIOLAN_U2C
will be called i2c-diolan-u2c.
config I2C_DLN2
- tristate "Diolan DLN-2 USB I2C adapter"
- depends on MFD_DLN2
- help
+ tristate "Diolan DLN-2 USB I2C adapter"
+ depends on MFD_DLN2
+ help
If you say yes to this option, support will be included for Diolan
DLN2, a USB to I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-dln2.
-config I2C_PARPORT
- tristate "Parallel port adapter"
- depends on PARPORT
- select I2C_ALGOBIT
- select I2C_SMBUS
+config I2C_LJCA
+ tristate "I2C functionality of Intel La Jolla Cove Adapter"
+ depends on USB_LJCA
+ default USB_LJCA
help
- This supports parallel port I2C adapters such as the ones made by
- Philips or Velleman, Analog Devices evaluation boards, and more.
- Basically any adapter using the parallel port as an I2C bus with
- no extra chipset is supported by this driver, or could be.
+ If you say yes to this option, I2C functionality support of Intel
+ La Jolla Cove Adapter (LJCA) will be included.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ljca.
- This driver is a replacement for (and was inspired by) an older
- driver named i2c-philips-par. The new driver supports more devices,
- and makes it easier to add support for new devices.
+config I2C_NCT6694
+ tristate "Nuvoton NCT6694 I2C adapter support"
+ depends on MFD_NCT6694
+ help
+ If you say yes to this option, support will be included for Nuvoton
+ NCT6694, a USB to I2C interface.
- An adapter type parameter is now mandatory. Please read the file
- Documentation/i2c/busses/i2c-parport for details.
+ This driver can also be built as a module. If so, the module will
+ be called i2c-nct6694.
- Another driver exists, named i2c-parport-light, which doesn't depend
- on the parport driver. This is meant for embedded systems. Don't say
- Y here if you intend to say Y or M there.
+config I2C_USBIO
+ tristate "Intel USBIO I2C Adapter support"
+ depends on USB_USBIO
+ default USB_USBIO
+ help
+ Select this option to enable I2C driver for the INTEL
+ USBIO driver stack.
- This support is also available as a module. If so, the module
- will be called i2c-parport.
+ This driver can also be built as a module. If so, the module
+ will be called i2c_usbio.
-config I2C_PARPORT_LIGHT
- tristate "Parallel port adapter (light)"
+config I2C_CP2615
+ tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
+ depends on USB
+ help
+ If you say yes to this option, support will be included for Silicon
+ Labs CP2615's I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-cp2615.
+
+config I2C_PARPORT
+ tristate "Parallel port adapter"
+ depends on PARPORT
select I2C_ALGOBIT
select I2C_SMBUS
help
This supports parallel port I2C adapters such as the ones made by
Philips or Velleman, Analog Devices evaluation boards, and more.
Basically any adapter using the parallel port as an I2C bus with
- no extra chipset is supported by this driver, or could be.
+ no extra chipset is supported by this driver, or could be. Please
+ read the file Documentation/i2c/busses/i2c-parport.rst for details.
- This driver is a light version of i2c-parport. It doesn't depend
- on the parport driver, and uses direct I/O access instead. This
- might be preferred on embedded systems where wasting memory for
- the clean but heavy parport handling is not an option. The
- drawback is a reduced portability and the impossibility to
- daisy-chain other parallel port devices.
+ This support is also available as a module. If so, the module
+ will be called i2c-parport.
- Don't say Y here if you said Y or M to i2c-parport. Saying M to
- both is possible but both modules should not be loaded at the same
- time.
+config I2C_PCI1XXXX
+ tristate "PCI1XXXX I2C Host Adapter"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for
+ Microchip PCI1XXXX's I2C interface.
- This support is also available as a module. If so, the module
- will be called i2c-parport-light.
+ This driver can also be built as a module. If so, the module will
+ be called i2c-mchp-pci1xxxx.
config I2C_ROBOTFUZZ_OSIF
tristate "RobotFuzz Open Source InterFace USB adapter"
@@ -1218,7 +1430,6 @@ config I2C_TAOS_EVM
depends on TTY
select SERIO
select SERIO_SERPORT
- default n
help
This supports TAOS evaluation modules on serial port. In order to
use this driver, you will need the inputattach tool, which is part
@@ -1246,9 +1457,9 @@ config I2C_VIPERBOARD
help
Say yes here to access the I2C part of the Nano River
Technologies Viperboard as I2C master.
- See viperboard API specification and Nano
- River Tech's viperboard.h for detailed meaning
- of the module parameters.
+ See viperboard API specification and Nano
+ River Tech's viperboard.h for detailed meaning
+ of the module parameters.
comment "Other I2C/SMBus bus drivers"
@@ -1264,7 +1475,7 @@ config I2C_ACORN
config I2C_ELEKTOR
tristate "Elektor ISA card"
- depends on ISA && HAS_IOPORT_MAP && BROKEN_ON_SMP
+ depends on ISA && HAS_IOPORT_MAP
select I2C_ALGOPCF
help
This supports the PCF8584 ISA bus I2C adapter. Say Y if you own
@@ -1273,12 +1484,27 @@ config I2C_ELEKTOR
This support is also available as a module. If so, the module
will be called i2c-elektor.
+config I2C_ICY
+ tristate "ICY Zorro card"
+ depends on ZORRO
+ select I2C_ALGOPCF
+ help
+ This supports the PCF8584 Zorro bus I2C adapter, known as ICY.
+ Say Y if you own such an adapter.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-icy.
+
+ If you have a 2019 edition board with an LTC2990 sensor at address
+ 0x4c, loading the module 'ltc2990' is sufficient to enable it.
+
config I2C_MLXCPLD
tristate "Mellanox I2C driver"
- depends on X86_64
+ depends on X86_64 || (ARM64 && ACPI) || COMPILE_TEST
+ depends on HAS_IOPORT
help
This exposes the Mellanox platform I2C busses to the linux I2C layer
- for X86 based systems.
+ for X86 and ARM64/ACPI based systems.
Controller is implemented as CPLD logic.
This driver can also be built as a module. If so, the module will be
@@ -1288,7 +1514,6 @@ config I2C_PCA_ISA
tristate "PCA9564/PCA9665 on an ISA bus"
depends on ISA
select I2C_ALGOPCA
- default n
help
This driver supports ISA boards using the Philips PCA9564/PCA9665
parallel bus to I2C bus controller.
@@ -1309,7 +1534,7 @@ config I2C_SIBYTE
config I2C_CROS_EC_TUNNEL
tristate "ChromeOS EC tunnel I2C bus"
- depends on MFD_CROS_EC
+ depends on CROS_EC
help
If you say yes here you get an I2C bus that will tunnel i2c commands
through to the other side of the ChromeOS EC to the i2c bus
@@ -1327,7 +1552,7 @@ config I2C_XGENE_SLIMPRO
config SCx200_ACB
tristate "Geode ACCESS.bus support"
- depends on X86_32 && PCI
+ depends on X86_32 && PCI && HAS_IOPORT
help
Enable the use of the ACCESS.bus controllers on the Geode SCx200 and
SC1100 processors and the CS5535 and CS5536 Geode companion devices.
@@ -1348,15 +1573,6 @@ config I2C_OPAL
This driver can also be built as a module. If so, the module will be
called as i2c-opal.
-config I2C_ZX2967
- tristate "ZTE ZX2967 I2C support"
- depends on ARCH_ZX
- default y
- help
- Selecting this option will add ZX2967 I2C driver.
- This driver can also be built as a module. If so, the module will be
- called i2c-zx2967.
-
config I2C_FSI
tristate "FSI I2C driver"
depends on FSI
@@ -1368,4 +1584,15 @@ config I2C_FSI
This driver can also be built as a module. If so, the module will be
called as i2c-fsi.
+config I2C_VIRTIO
+ tristate "Virtio I2C Adapter"
+ select VIRTIO
+ help
+ If you say yes to this option, support will be included for the virtio
+ I2C adapter driver. The hardware can be emulated by any device model
+ software according to the virtio protocol.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-virtio.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5f0cb6915969..fb985769f5ff 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -6,19 +6,20 @@
# ACPI drivers
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
+# Auxiliary I2C/SMBus modules
+obj-$(CONFIG_I2C_CCGX_UCSI) += i2c-ccgx-ucsi.o
+
# PC SMBus host controller drivers
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o
obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
-obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_CHT_WC) += i2c-cht-wc.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
-obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
@@ -26,6 +27,7 @@ obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
obj-$(CONFIG_I2C_VIA) += i2c-via.o
obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
+obj-$(CONFIG_I2C_ZHAOXIN) += i2c-viai2c-zhaoxin.o i2c-viai2c-common.o
# Mac SMBus host controller drivers
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
@@ -33,33 +35,39 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
+obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
+obj-$(CONFIG_I2C_AMD_ASF) += i2c-amd-asf-plat.o
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
+i2c-at91-y := i2c-at91-core.o i2c-at91-master.o
+i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL) += i2c-at91-slave.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o
obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
+obj-$(CONFIG_I2C_CGBC) += i2c-cgbc.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
-obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
-i2c-designware-core-objs := i2c-designware-common.o i2c-designware-master.o
-ifeq ($(CONFIG_I2C_DESIGNWARE_SLAVE),y)
-i2c-designware-core-objs += i2c-designware-slave.o
-endif
-obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
-i2c-designware-platform-objs := i2c-designware-platdrv.o
+obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
+i2c-designware-core-y := i2c-designware-common.o
+i2c-designware-core-y += i2c-designware-master.o
+i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o
+obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
+i2c-designware-platform-y := i2c-designware-platdrv.o
+i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
-obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
-i2c-designware-pci-objs := i2c-designware-pcidrv.o
+obj-$(CONFIG_I2C_DESIGNWARE_AMDISP) += i2c-designware-amdisp.o
+obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
+i2c-designware-pci-y := i2c-designware-pcidrv.o
obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
-obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
+obj-$(CONFIG_I2C_HISI) += i2c-hisi.o
obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
@@ -67,39 +75,45 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o
obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
+obj-$(CONFIG_I2C_K1) += i2c-k1.o
+obj-$(CONFIG_I2C_KEBA) += i2c-keba.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
+obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
+obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o
+obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
+obj-$(CONFIG_I2C_NPCM) += i2c-npcm7xx.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
-obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
+obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi-core.o i2c-pasemi-pci.o
+obj-$(CONFIG_I2C_APPLE) += i2c-pasemi-core.o i2c-pasemi-platform.o
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
-obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
-obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QCOM_CCI) += i2c-qcom-cci.o
obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
+obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o
+obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
-obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o
obj-$(CONFIG_I2C_ST) += i2c-st.o
obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
-i2c-stm32f7-drv-objs := i2c-stm32f7.o i2c-stm32.o
obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.o
-obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
+i2c-stm32f7-drv-y := i2c-stm32f7.o i2c-stm32.o
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
@@ -107,22 +121,25 @@ obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
-obj-$(CONFIG_I2C_WMT) += i2c-wmt.o
-i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
+obj-$(CONFIG_I2C_WMT) += i2c-viai2c-wmt.o i2c-viai2c-common.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
-i2c-thunderx-objs := i2c-octeon-core.o i2c-thunderx-pcidrv.o
+i2c-octeon-y := i2c-octeon-core.o i2c-octeon-platdrv.o
obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o
+i2c-thunderx-y := i2c-octeon-core.o i2c-thunderx-pcidrv.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
-obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
-obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o
+obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
+obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o
+obj-$(CONFIG_I2C_NCT6694) += i2c-nct6694.o
+obj-$(CONFIG_I2C_USBIO) += i2c-usbio.o
+obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
-obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
+obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
@@ -134,6 +151,8 @@ obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
+obj-$(CONFIG_I2C_ICY) += i2c-icy.o
+obj-$(CONFIG_I2C_MLXBF) += i2c-mlxbf.o
obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
@@ -141,5 +160,6 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
+obj-$(CONFIG_I2C_VIRTIO) += i2c-virtio.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c
index f4a5ae69bf6a..34bbbb8c52f6 100644
--- a/drivers/i2c/busses/i2c-acorn.c
+++ b/drivers/i2c/busses/i2c-acorn.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ARM IOC/IOMD i2c driver.
*
* Copyright (C) 2000 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* On Acorn machines, the following i2c devices are on the bus:
* - PCF8583 real time clock & static RAM
*/
@@ -81,6 +78,7 @@ static struct i2c_algo_bit_data ioc_data = {
static struct i2c_adapter ioc_ops = {
.nr = 0,
+ .name = "ioc",
.algo_data = &ioc_data,
};
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index 4f2d78868281..1eac35838040 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
* Philip Edelbrock <phil@netroedge.com>,
* Mark D. Studebaker <mdsxyz123@yahoo.com>,
* Dan Eaton <dan.eaton@rocketlogix.com> and
* Stephen Rousset <stephen.rousset@rocketlogix.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
@@ -294,10 +285,8 @@ static int ali1535_transaction(struct i2c_adapter *adap)
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout > MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT)
result = -ETIMEDOUT;
- dev_err(&adap->dev, "SMBus Timeout!\n");
- }
if (temp & ALI1535_STS_FAIL) {
result = -EIO;
@@ -322,10 +311,8 @@ static int ali1535_transaction(struct i2c_adapter *adap)
}
/* check to see if the "command complete" indication is set */
- if (!(temp & ALI1535_STS_DONE)) {
+ if (!(temp & ALI1535_STS_DONE))
result = -ETIMEDOUT;
- dev_err(&adap->dev, "Error: command never completed\n");
- }
dev_dbg(&adap->dev, "Transaction (post): STS=%02x, TYP=%02x, "
"CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
@@ -486,19 +473,20 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter ali1535_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
static const struct pci_device_id ali1535_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
- { },
+ { }
};
-
MODULE_DEVICE_TABLE(pci, ali1535_ids);
static int ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
+ int ret;
+
if (ali1535_setup(dev)) {
dev_warn(&dev->dev,
"ALI1535 not detected, module not inserted.\n");
@@ -510,13 +498,26 @@ static int ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
snprintf(ali1535_adapter.name, sizeof(ali1535_adapter.name),
"SMBus ALI1535 adapter at %04x", ali1535_offset);
- return i2c_add_adapter(&ali1535_adapter);
+ ret = i2c_add_adapter(&ali1535_adapter);
+ if (ret)
+ goto release_region;
+
+ return 0;
+
+release_region:
+ release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+ return ret;
}
static void ali1535_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali1535_adapter);
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+
+ /*
+ * do not call pci_disable_device(dev) since it can cause hard hangs on
+ * some systems during power-off
+ */
}
static struct pci_driver ali1535_driver = {
@@ -528,9 +529,9 @@ static struct pci_driver ali1535_driver = {
module_pci_driver(ali1535_driver);
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "Mark D. Studebaker <mdsxyz123@yahoo.com> "
- "and Dan Eaton <dan.eaton@rocketlogix.com>");
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_AUTHOR("Dan Eaton <dan.eaton@rocketlogix.com>");
MODULE_DESCRIPTION("ALI1535 SMBus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c
index 15517d78d5ff..ee4fd66dedb0 100644
--- a/drivers/i2c/busses/i2c-ali1563.c
+++ b/drivers/i2c/busses/i2c-ali1563.c
@@ -1,4 +1,5 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
*
* Copyright (C) 2004 Patrick Mochel
@@ -12,8 +13,6 @@
*
* This driver is based on a mix of the 15x3, 1535, and i801 drivers,
* with a little help from the ALi 1563 spec.
- *
- * This file is released under the GPLv2
*/
#include <linux/module.h>
@@ -100,7 +99,6 @@ static int ali1563_transaction(struct i2c_adapter *a, int size)
return 0;
if (!timeout) {
- dev_err(&a->dev, "Timeout - Trying to KILL transaction!\n");
/* Issue 'kill' to host controller */
outb_p(HST_CNTL2_KILL, SMB_HST_CNTL2);
data = inb_p(SMB_HST_STS);
@@ -391,7 +389,7 @@ static const struct i2c_algorithm ali1563_algorithm = {
static struct i2c_adapter ali1563_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &ali1563_algorithm,
};
@@ -440,4 +438,5 @@ static struct pci_driver ali1563_pci_driver = {
module_pci_driver(ali1563_pci_driver);
+MODULE_DESCRIPTION("i2c driver for the ALi 1563 Southbridge");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 6e6bf46bcb52..418d11266671 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com> and
Mark D. Studebaker <mdsxyz123@yahoo.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -47,7 +39,7 @@
We make sure that the SMB is enabled. We leave the ACPI alone.
This driver controls the SMB Host only.
- The SMB Slave controller on the M15X3 is not enabled.
+ The SMB Target controller on the M15X3 is not enabled.
This driver does not use interrupts.
*/
@@ -173,14 +165,15 @@ static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
}
if(force_addr) {
+ int ret;
+
dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n",
ali15x3_smba);
- if (PCIBIOS_SUCCESSFUL != pci_write_config_word(ALI15X3_dev,
- SMBBA,
- ali15x3_smba))
+ ret = pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba);
+ if (ret != PCIBIOS_SUCCESSFUL)
goto error;
- if (PCIBIOS_SUCCESSFUL != pci_read_config_word(ALI15X3_dev,
- SMBBA, &a))
+ ret = pci_read_config_word(ALI15X3_dev, SMBBA, &a);
+ if (ret != PCIBIOS_SUCCESSFUL)
goto error;
if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) {
/* make sure it works */
@@ -301,10 +294,8 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout > MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT)
result = -ETIMEDOUT;
- dev_err(&adap->dev, "SMBus Timeout!\n");
- }
if (temp & ALI15X3_STS_TERM) {
result = -EIO;
@@ -468,7 +459,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter ali15x3_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
@@ -481,6 +472,8 @@ MODULE_DEVICE_TABLE (pci, ali15x3_ids);
static int ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
+ int ret;
+
if (ali15x3_setup(dev)) {
dev_err(&dev->dev,
"ALI15X3 not detected, module not inserted.\n");
@@ -492,7 +485,15 @@ static int ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
snprintf(ali15x3_adapter.name, sizeof(ali15x3_adapter.name),
"SMBus ALI15X3 adapter at %04x", ali15x3_smba);
- return i2c_add_adapter(&ali15x3_adapter);
+ ret = i2c_add_adapter(&ali15x3_adapter);
+ if (ret)
+ goto release_region;
+
+ return 0;
+
+release_region:
+ release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
+ return ret;
}
static void ali15x3_remove(struct pci_dev *dev)
@@ -510,8 +511,8 @@ static struct pci_driver ali15x3_driver = {
module_pci_driver(ali15x3_driver);
-MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("ALI15X3 SMBus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-altera.c b/drivers/i2c/busses/i2c-altera.c
index f5e1941e65b5..2da73173ce24 100644
--- a/drivers/i2c/busses/i2c-altera.c
+++ b/drivers/i2c/busses/i2c-altera.c
@@ -1,18 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright Intel Corporation (C) 2017.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- *
* Based on the i2c-axxia.c driver.
*/
#include <linux/clk.h>
@@ -66,7 +55,7 @@
#define ALTR_I2C_XFER_TIMEOUT (msecs_to_jiffies(250))
/**
- * altr_i2c_dev - I2C device context
+ * struct altr_i2c_dev - I2C device context
* @base: pointer to register struct
* @msg: pointer to current message
* @msg_len: number of bytes transferred in msg
@@ -80,7 +69,7 @@
* @fifo_size: size of the FIFO passed in.
* @isr_mask: cached copy of local ISR enables.
* @isr_status: cached copy of local ISR status.
- * @lock: spinlock for IRQ synchronization.
+ * @isr_mutex: mutex for IRQ thread.
*/
struct altr_i2c_dev {
void __iomem *base;
@@ -96,17 +85,14 @@ struct altr_i2c_dev {
u32 fifo_size;
u32 isr_mask;
u32 isr_status;
- spinlock_t lock; /* IRQ synchronization */
+ struct mutex isr_mutex;
};
static void
altr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable)
{
- unsigned long flags;
u32 int_en;
- spin_lock_irqsave(&idev->lock, flags);
-
int_en = readl(idev->base + ALTR_I2C_ISER);
if (enable)
idev->isr_mask = int_en | mask;
@@ -114,8 +100,6 @@ altr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable)
idev->isr_mask = int_en & ~mask;
writel(idev->isr_mask, idev->base + ALTR_I2C_ISER);
-
- spin_unlock_irqrestore(&idev->lock, flags);
}
static void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask)
@@ -158,7 +142,7 @@ static void altr_i2c_init(struct altr_i2c_dev *idev)
(ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT);
u32 t_high, t_low;
- if (idev->bus_clk_rate <= 100000) {
+ if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) {
tmp &= ~ALTR_I2C_CTRL_BSPEED;
/* Standard mode SCL 50/50 */
t_high = divisor * 1 / 2;
@@ -182,13 +166,13 @@ static void altr_i2c_init(struct altr_i2c_dev *idev)
/* SCL Low Time */
writel(t_low, idev->base + ALTR_I2C_SCL_LOW);
/* SDA Hold Time, 300ns */
- writel(div_u64(300 * clk_mhz, 1000), idev->base + ALTR_I2C_SDA_HOLD);
+ writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD);
- /* Mask all master interrupt bits */
+ /* Mask all interrupt bits */
altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false);
}
-/**
+/*
* altr_i2c_transfer - On the last byte to be transmitted, send
* a Stop bit on the last byte.
*/
@@ -201,7 +185,7 @@ static void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data)
writel(data, idev->base + ALTR_I2C_TFR_CMD);
}
-/**
+/*
* altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of
* transfer. Send a Stop bit on the last byte.
*/
@@ -217,9 +201,8 @@ static void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev)
}
}
-/**
+/*
* altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer.
- * @return: Number of bytes left to transfer.
*/
static int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev)
{
@@ -256,10 +239,11 @@ static irqreturn_t altr_i2c_isr(int irq, void *_dev)
struct altr_i2c_dev *idev = _dev;
u32 status = idev->isr_status;
+ mutex_lock(&idev->isr_mutex);
if (!idev->msg) {
dev_warn(idev->dev, "unexpected interrupt\n");
altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
- return IRQ_HANDLED;
+ goto out;
}
read = (idev->msg->flags & I2C_M_RD) != 0;
@@ -312,6 +296,8 @@ static irqreturn_t altr_i2c_isr(int irq, void *_dev)
complete(&idev->msg_complete);
dev_dbg(idev->dev, "Message Complete\n");
}
+out:
+ mutex_unlock(&idev->isr_mutex);
return IRQ_HANDLED;
}
@@ -323,6 +309,7 @@ static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
u32 value;
u8 addr = i2c_8bit_addr_from_msg(msg);
+ mutex_lock(&idev->isr_mutex);
idev->msg = msg;
idev->msg_len = msg->len;
idev->buf = msg->buf;
@@ -347,9 +334,11 @@ static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
altr_i2c_int_enable(idev, imask, true);
altr_i2c_fill_tx_fifo(idev);
}
+ mutex_unlock(&idev->isr_mutex);
time_left = wait_for_completion_timeout(&idev->msg_complete,
ALTR_I2C_XFER_TIMEOUT);
+ mutex_lock(&idev->isr_mutex);
altr_i2c_int_enable(idev, imask, false);
value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE;
@@ -362,6 +351,7 @@ static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
}
altr_i2c_core_disable(idev);
+ mutex_unlock(&idev->isr_mutex);
return idev->msg_err;
}
@@ -386,31 +376,26 @@ static u32 altr_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm altr_i2c_algo = {
- .master_xfer = altr_i2c_xfer,
+ .xfer = altr_i2c_xfer,
.functionality = altr_i2c_func,
};
static int altr_i2c_probe(struct platform_device *pdev)
{
struct altr_i2c_dev *idev = NULL;
- struct resource *res;
int irq, ret;
- u32 val;
idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
if (!idev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- idev->base = devm_ioremap_resource(&pdev->dev, res);
+ idev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(idev->base))
return PTR_ERR(idev->base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "missing interrupt resource\n");
+ if (irq < 0)
return irq;
- }
idev->i2c_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(idev->i2c_clk)) {
@@ -420,24 +405,24 @@ static int altr_i2c_probe(struct platform_device *pdev)
idev->dev = &pdev->dev;
init_completion(&idev->msg_complete);
- spin_lock_init(&idev->lock);
+ mutex_init(&idev->isr_mutex);
- val = device_property_read_u32(idev->dev, "fifo-size",
+ ret = device_property_read_u32(idev->dev, "fifo-size",
&idev->fifo_size);
- if (val) {
+ if (ret) {
dev_err(&pdev->dev, "FIFO size set to default of %d\n",
ALTR_I2C_DFLT_FIFO_SZ);
idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ;
}
- val = device_property_read_u32(idev->dev, "clock-frequency",
+ ret = device_property_read_u32(idev->dev, "clock-frequency",
&idev->bus_clk_rate);
- if (val) {
+ if (ret) {
dev_err(&pdev->dev, "Default to 100kHz\n");
- idev->bus_clk_rate = 100000; /* default clock rate */
+ idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */
}
- if (idev->bus_clk_rate > 400000) {
+ if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) {
dev_err(&pdev->dev, "invalid clock-frequency %d\n",
idev->bus_clk_rate);
return -EINVAL;
@@ -457,10 +442,12 @@ static int altr_i2c_probe(struct platform_device *pdev)
return ret;
}
+ mutex_lock(&idev->isr_mutex);
altr_i2c_init(idev);
+ mutex_unlock(&idev->isr_mutex);
i2c_set_adapdata(&idev->adapter, idev);
- strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
+ strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
idev->adapter.owner = THIS_MODULE;
idev->adapter.algo = &altr_i2c_algo;
idev->adapter.dev.parent = &pdev->dev;
@@ -478,14 +465,12 @@ static int altr_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int altr_i2c_remove(struct platform_device *pdev)
+static void altr_i2c_remove(struct platform_device *pdev)
{
struct altr_i2c_dev *idev = platform_get_drvdata(pdev);
clk_disable_unprepare(idev->i2c_clk);
i2c_del_adapter(&idev->adapter);
-
- return 0;
}
/* Match table for of_platform binding */
diff --git a/drivers/i2c/busses/i2c-amd-asf-plat.c b/drivers/i2c/busses/i2c-amd-asf-plat.c
new file mode 100644
index 000000000000..ca45f0f23321
--- /dev/null
+++ b/drivers/i2c/busses/i2c-amd-asf-plat.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Alert Standard Format Platform Driver
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Sanket Goswami <Sanket.Goswami@amd.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/devm-helpers.h>
+#include <linux/errno.h>
+#include <linux/gfp_types.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/sprintf.h>
+
+#include "i2c-piix4.h"
+
+/* ASF register bits */
+#define ASF_SLV_LISTN 0
+#define ASF_SLV_INTR 1
+#define ASF_SLV_RST 4
+#define ASF_PEC_SP 5
+#define ASF_DATA_EN 7
+#define ASF_MSTR_EN 16
+#define ASF_CLK_EN 17
+
+/* ASF address offsets */
+#define ASFINDEX (0x07 + piix4_smba)
+#define ASFLISADDR (0x09 + piix4_smba)
+#define ASFSTA (0x0A + piix4_smba)
+#define ASFSLVSTA (0x0D + piix4_smba)
+#define ASFDATARWPTR (0x11 + piix4_smba)
+#define ASFSETDATARDPTR (0x12 + piix4_smba)
+#define ASFDATABNKSEL (0x13 + piix4_smba)
+#define ASFSLVEN (0x15 + piix4_smba)
+
+#define ASF_BLOCK_MAX_BYTES 72
+#define ASF_ERROR_STATUS GENMASK(3, 1)
+
+struct amd_asf_dev {
+ struct i2c_adapter adap;
+ void __iomem *eoi_base;
+ struct i2c_client *target;
+ struct delayed_work work_buf;
+ struct sb800_mmio_cfg mmio_cfg;
+ struct resource *port_addr;
+};
+
+static void amd_asf_process_target(struct work_struct *work)
+{
+ struct amd_asf_dev *dev = container_of(work, struct amd_asf_dev, work_buf.work);
+ unsigned short piix4_smba = dev->port_addr->start;
+ u8 data[ASF_BLOCK_MAX_BYTES];
+ u8 bank, reg, cmd;
+ u8 len = 0, idx, val;
+
+ /* Read target status register */
+ reg = inb_p(ASFSLVSTA);
+
+ /* Check if no error bits are set in target status register */
+ if (reg & ASF_ERROR_STATUS) {
+ /* Set bank as full */
+ cmd = 1;
+ reg |= GENMASK(3, 2);
+ outb_p(reg, ASFDATABNKSEL);
+ } else {
+ /* Read data bank */
+ reg = inb_p(ASFDATABNKSEL);
+ bank = (reg & BIT(3)) ? 1 : 0;
+
+ /* Set read data bank */
+ if (bank) {
+ reg |= BIT(4);
+ reg &= ~BIT(3);
+ } else {
+ reg &= ~BIT(4);
+ reg &= ~BIT(2);
+ }
+
+ /* Read command register */
+ outb_p(reg, ASFDATABNKSEL);
+ cmd = inb_p(ASFINDEX);
+ len = inb_p(ASFDATARWPTR);
+ for (idx = 0; idx < len; idx++)
+ data[idx] = inb_p(ASFINDEX);
+
+ /* Clear data bank status */
+ if (bank) {
+ reg |= BIT(3);
+ outb_p(reg, ASFDATABNKSEL);
+ } else {
+ reg |= BIT(2);
+ outb_p(reg, ASFDATABNKSEL);
+ }
+ }
+
+ outb_p(0, ASFSETDATARDPTR);
+ if (cmd & BIT(0))
+ return;
+
+ /*
+ * Although i2c_slave_event() returns an appropriate error code, we
+ * don't check it here because we're operating in the workqueue context.
+ */
+ i2c_slave_event(dev->target, I2C_SLAVE_WRITE_REQUESTED, &val);
+ for (idx = 0; idx < len; idx++) {
+ val = data[idx];
+ i2c_slave_event(dev->target, I2C_SLAVE_WRITE_RECEIVED, &val);
+ }
+ i2c_slave_event(dev->target, I2C_SLAVE_STOP, &val);
+}
+
+static void amd_asf_update_ioport_target(unsigned short piix4_smba, u8 bit,
+ unsigned long offset, bool set)
+{
+ unsigned long reg;
+
+ reg = inb_p(offset);
+ __assign_bit(bit, &reg, set);
+ outb_p(reg, offset);
+}
+
+static void amd_asf_update_mmio_target(struct amd_asf_dev *dev, u8 bit, bool set)
+{
+ unsigned long reg;
+
+ reg = ioread32(dev->mmio_cfg.addr);
+ __assign_bit(bit, &reg, set);
+ iowrite32(reg, dev->mmio_cfg.addr);
+}
+
+static void amd_asf_setup_target(struct amd_asf_dev *dev)
+{
+ unsigned short piix4_smba = dev->port_addr->start;
+
+ /* Reset both host and target before setting up */
+ outb_p(0, SMBHSTSTS);
+ outb_p(0, ASFSLVSTA);
+ outb_p(0, ASFSTA);
+
+ /* Update target address */
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, true);
+ /* Enable target and set the clock */
+ amd_asf_update_mmio_target(dev, ASF_MSTR_EN, false);
+ amd_asf_update_mmio_target(dev, ASF_CLK_EN, true);
+ /* Enable target interrupt */
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, true);
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, false);
+ /* Enable PEC and PEC append */
+ amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
+ amd_asf_update_ioport_target(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
+}
+
+static int amd_asf_access(struct i2c_adapter *adap, u16 addr, u8 command, u8 *data)
+{
+ struct amd_asf_dev *dev = i2c_get_adapdata(adap);
+ unsigned short piix4_smba = dev->port_addr->start;
+ u8 i, len;
+
+ outb_p((addr << 1), SMBHSTADD);
+ outb_p(command, SMBHSTCMD);
+ len = data[0];
+ if (len == 0 || len > ASF_BLOCK_MAX_BYTES)
+ return -EINVAL;
+
+ outb_p(len, SMBHSTDAT0);
+ /* Reset SMBBLKDAT */
+ inb_p(SMBHSTCNT);
+ for (i = 1; i <= len; i++)
+ outb_p(data[i], SMBBLKDAT);
+
+ outb_p(PIIX4_BLOCK_DATA, SMBHSTCNT);
+ /* Enable PEC and PEC append */
+ amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
+ amd_asf_update_ioport_target(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
+
+ return piix4_transaction(adap, piix4_smba);
+}
+
+static int amd_asf_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct amd_asf_dev *dev = i2c_get_adapdata(adap);
+ unsigned short piix4_smba = dev->port_addr->start;
+ u8 asf_data[ASF_BLOCK_MAX_BYTES];
+ struct i2c_msg *dev_msgs = msgs;
+ u8 prev_port;
+ int ret;
+
+ if (msgs->flags & I2C_M_RD) {
+ dev_err(&adap->dev, "ASF: Read not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* Exclude the receive header and PEC */
+ if (msgs->len > ASF_BLOCK_MAX_BYTES - 3) {
+ dev_warn(&adap->dev, "ASF: max message length exceeded\n");
+ return -EOPNOTSUPP;
+ }
+
+ asf_data[0] = dev_msgs->len;
+ memcpy(asf_data + 1, dev_msgs[0].buf, dev_msgs->len);
+
+ ret = piix4_sb800_region_request(&adap->dev, &dev->mmio_cfg);
+ if (ret)
+ return ret;
+
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, false);
+ /* Clear ASF target status */
+ outb_p(0, ASFSLVSTA);
+
+ /* Enable ASF SMBus controller function */
+ amd_asf_update_mmio_target(dev, ASF_MSTR_EN, true);
+ prev_port = piix4_sb800_port_sel(0, &dev->mmio_cfg);
+ ret = amd_asf_access(adap, msgs->addr, msgs[0].buf[0], asf_data);
+ piix4_sb800_port_sel(prev_port, &dev->mmio_cfg);
+ amd_asf_setup_target(dev);
+ piix4_sb800_region_release(&adap->dev, &dev->mmio_cfg);
+ return ret;
+}
+
+static int amd_asf_reg_target(struct i2c_client *target)
+{
+ struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
+ unsigned short piix4_smba = dev->port_addr->start;
+ int ret;
+ u8 reg;
+
+ if (dev->target)
+ return -EBUSY;
+
+ ret = piix4_sb800_region_request(&target->dev, &dev->mmio_cfg);
+ if (ret)
+ return ret;
+
+ reg = (target->addr << 1) | I2C_M_RD;
+ outb_p(reg, ASFLISADDR);
+
+ amd_asf_setup_target(dev);
+ dev->target = target;
+ amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, ASFDATABNKSEL, false);
+ piix4_sb800_region_release(&target->dev, &dev->mmio_cfg);
+
+ return 0;
+}
+
+static int amd_asf_unreg_target(struct i2c_client *target)
+{
+ struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
+ unsigned short piix4_smba = dev->port_addr->start;
+
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, false);
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
+ dev->target = NULL;
+
+ return 0;
+}
+
+static u32 amd_asf_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_PEC | I2C_FUNC_SLAVE;
+}
+
+static const struct i2c_algorithm amd_asf_smbus_algorithm = {
+ .xfer = amd_asf_xfer,
+ .reg_target = amd_asf_reg_target,
+ .unreg_target = amd_asf_unreg_target,
+ .functionality = amd_asf_func,
+};
+
+static irqreturn_t amd_asf_irq_handler(int irq, void *ptr)
+{
+ struct amd_asf_dev *dev = ptr;
+ unsigned short piix4_smba = dev->port_addr->start;
+ u8 target_int = inb_p(ASFSTA);
+
+ if (target_int & BIT(6)) {
+ /* Target Interrupt */
+ outb_p(target_int | BIT(6), ASFSTA);
+ schedule_delayed_work(&dev->work_buf, HZ);
+ } else {
+ /* Controller Interrupt */
+ amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, SMBHSTSTS, true);
+ }
+
+ iowrite32(irq, dev->eoi_base);
+ return IRQ_HANDLED;
+}
+
+static int amd_asf_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct amd_asf_dev *asf_dev;
+ struct resource *eoi_addr;
+ int ret, irq;
+
+ asf_dev = devm_kzalloc(dev, sizeof(*asf_dev), GFP_KERNEL);
+ if (!asf_dev)
+ return dev_err_probe(dev, -ENOMEM, "Failed to allocate memory\n");
+
+ asf_dev->mmio_cfg.use_mmio = true;
+ asf_dev->port_addr = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!asf_dev->port_addr)
+ return dev_err_probe(dev, -EINVAL, "missing IO resources\n");
+
+ /*
+ * The resource obtained via ACPI might not belong to the ASF device address space. Instead,
+ * it could be within other IP blocks of the ASIC, which are crucial for generating
+ * subsequent interrupts. Therefore, we avoid using devm_platform_ioremap_resource() and
+ * use platform_get_resource() and devm_ioremap() separately to prevent any address space
+ * conflicts.
+ */
+ eoi_addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!eoi_addr)
+ return dev_err_probe(dev, -EINVAL, "missing MEM resources\n");
+
+ asf_dev->eoi_base = devm_ioremap(dev, eoi_addr->start, resource_size(eoi_addr));
+ if (!asf_dev->eoi_base)
+ return dev_err_probe(dev, -EBUSY, "failed mapping IO region\n");
+
+ ret = devm_delayed_work_autocancel(dev, &asf_dev->work_buf, amd_asf_process_target);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to create work queue\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "missing IRQ resources\n");
+
+ ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to request irq: %d for use\n", irq);
+
+ asf_dev->adap.owner = THIS_MODULE;
+ asf_dev->adap.algo = &amd_asf_smbus_algorithm;
+ asf_dev->adap.dev.parent = dev;
+
+ i2c_set_adapdata(&asf_dev->adap, asf_dev);
+ snprintf(asf_dev->adap.name, sizeof(asf_dev->adap.name), "AMD ASF adapter");
+
+ return devm_i2c_add_adapter(dev, &asf_dev->adap);
+}
+
+static const struct acpi_device_id amd_asf_acpi_ids[] = {
+ { "AMDI001A" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, amd_asf_acpi_ids);
+
+static struct platform_driver amd_asf_driver = {
+ .driver = {
+ .name = "i2c-amd-asf",
+ .acpi_match_table = amd_asf_acpi_ids,
+ },
+ .probe = amd_asf_probe,
+};
+module_platform_driver(amd_asf_driver);
+
+MODULE_IMPORT_NS("PIIX4_SMBUS");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Alert Standard Format Driver");
diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c
new file mode 100644
index 000000000000..60edbabc2986
--- /dev/null
+++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * AMD MP2 PCIe communication driver
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Elie Morisse <syniurge@gmail.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "i2c-amd-mp2.h"
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+static void amd_mp2_c2p_mutex_lock(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+ /* there is only one data mailbox for two i2c adapters */
+ mutex_lock(&privdata->c2p_lock);
+ privdata->c2p_lock_busid = i2c_common->bus_id;
+}
+
+static void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+ if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
+ pci_warn(privdata->pci_dev,
+ "bus %d attempting to unlock C2P locked by bus %d\n",
+ i2c_common->bus_id, privdata->c2p_lock_busid);
+ return;
+ }
+
+ mutex_unlock(&privdata->c2p_lock);
+}
+
+static int amd_mp2_cmd(struct amd_i2c_common *i2c_common,
+ union i2c_cmd_base i2c_cmd_base)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ void __iomem *reg;
+
+ i2c_common->reqcmd = i2c_cmd_base.s.i2c_cmd;
+
+ reg = privdata->mmio + ((i2c_cmd_base.s.bus_id == 1) ?
+ AMD_C2P_MSG1 : AMD_C2P_MSG0);
+ writel(i2c_cmd_base.ul, reg);
+
+ return 0;
+}
+
+int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ union i2c_cmd_base i2c_cmd_base;
+
+ pci_dbg(privdata->pci_dev, "id: %d\n", i2c_common->bus_id);
+
+ i2c_cmd_base.ul = 0;
+ i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
+ i2c_cmd_base.s.bus_id = i2c_common->bus_id;
+ i2c_cmd_base.s.i2c_speed = i2c_common->i2c_speed;
+
+ amd_mp2_c2p_mutex_lock(i2c_common);
+
+ return amd_mp2_cmd(i2c_common, i2c_cmd_base);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_bus_enable_set);
+
+static void amd_mp2_cmd_rw_fill(struct amd_i2c_common *i2c_common,
+ union i2c_cmd_base *i2c_cmd_base,
+ enum i2c_cmd reqcmd)
+{
+ i2c_cmd_base->s.i2c_cmd = reqcmd;
+ i2c_cmd_base->s.bus_id = i2c_common->bus_id;
+ i2c_cmd_base->s.i2c_speed = i2c_common->i2c_speed;
+ i2c_cmd_base->s.slave_addr = i2c_common->msg->addr;
+ i2c_cmd_base->s.length = i2c_common->msg->len;
+}
+
+int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ union i2c_cmd_base i2c_cmd_base;
+
+ amd_mp2_cmd_rw_fill(i2c_common, &i2c_cmd_base, reqcmd);
+ amd_mp2_c2p_mutex_lock(i2c_common);
+
+ if (i2c_common->msg->len <= 32) {
+ i2c_cmd_base.s.mem_type = use_c2pmsg;
+ if (reqcmd == i2c_write)
+ memcpy_toio(privdata->mmio + AMD_C2P_MSG2,
+ i2c_common->msg->buf,
+ i2c_common->msg->len);
+ } else {
+ i2c_cmd_base.s.mem_type = use_dram;
+ writeq((u64)i2c_common->dma_addr,
+ privdata->mmio + AMD_C2P_MSG2);
+ }
+
+ return amd_mp2_cmd(i2c_common, i2c_cmd_base);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_rw);
+
+static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ struct pci_dev *pdev = privdata->pci_dev;
+ int len = i2c_common->eventval.r.length;
+ u32 slave_addr = i2c_common->eventval.r.slave_addr;
+ bool err = false;
+
+ if (unlikely(len != i2c_common->msg->len)) {
+ pci_err(pdev, "length %d in event doesn't match buffer length %d!\n",
+ len, i2c_common->msg->len);
+ err = true;
+ }
+
+ if (unlikely(slave_addr != i2c_common->msg->addr)) {
+ pci_err(pdev, "unexpected slave address %x (expected: %x)!\n",
+ slave_addr, i2c_common->msg->addr);
+ err = true;
+ }
+
+ if (!err)
+ i2c_common->cmd_success = true;
+}
+
+static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ struct pci_dev *pdev = privdata->pci_dev;
+ enum status_type sts = i2c_common->eventval.r.status;
+ enum response_type res = i2c_common->eventval.r.response;
+ int len = i2c_common->eventval.r.length;
+
+ if (res != command_success) {
+ if (res != command_failed)
+ pci_err(pdev, "invalid response to i2c command!\n");
+ return;
+ }
+
+ switch (i2c_common->reqcmd) {
+ case i2c_read:
+ if (sts == i2c_readcomplete_event) {
+ amd_mp2_pci_check_rw_event(i2c_common);
+ if (len <= 32)
+ memcpy_fromio(i2c_common->msg->buf,
+ privdata->mmio + AMD_C2P_MSG2,
+ len);
+ } else if (sts != i2c_readfail_event) {
+ pci_err(pdev, "invalid i2c status after read (%d)!\n", sts);
+ }
+ break;
+ case i2c_write:
+ if (sts == i2c_writecomplete_event)
+ amd_mp2_pci_check_rw_event(i2c_common);
+ else if (sts != i2c_writefail_event)
+ pci_err(pdev, "invalid i2c status after write (%d)!\n", sts);
+ break;
+ case i2c_enable:
+ if (sts == i2c_busenable_complete)
+ i2c_common->cmd_success = true;
+ else if (sts != i2c_busenable_failed)
+ pci_err(pdev, "invalid i2c status after bus enable (%d)!\n", sts);
+ break;
+ case i2c_disable:
+ if (sts == i2c_busdisable_complete)
+ i2c_common->cmd_success = true;
+ else if (sts != i2c_busdisable_failed)
+ pci_err(pdev, "invalid i2c status after bus disable (%d)!\n", sts);
+ break;
+ default:
+ break;
+ }
+}
+
+void amd_mp2_process_event(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ struct pci_dev *pdev = privdata->pci_dev;
+
+ if (unlikely(i2c_common->reqcmd == i2c_none)) {
+ pci_warn(pdev, "received msg but no cmd was sent (bus = %d)!\n",
+ i2c_common->bus_id);
+ return;
+ }
+
+ __amd_mp2_process_event(i2c_common);
+
+ i2c_common->reqcmd = i2c_none;
+ amd_mp2_c2p_mutex_unlock(i2c_common);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_process_event);
+
+static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
+{
+ struct amd_mp2_dev *privdata = dev;
+ struct pci_dev *pdev = privdata->pci_dev;
+ struct amd_i2c_common *i2c_common;
+ u32 val;
+ unsigned int bus_id;
+ void __iomem *reg;
+ enum irqreturn ret = IRQ_NONE;
+
+ for (bus_id = 0; bus_id < 2; bus_id++) {
+ i2c_common = privdata->busses[bus_id];
+ if (!i2c_common)
+ continue;
+
+ reg = privdata->mmio + ((bus_id == 0) ?
+ AMD_P2C_MSG1 : AMD_P2C_MSG2);
+ val = readl(reg);
+ if (val != 0) {
+ writel(0, reg);
+ writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+ i2c_common->eventval.ul = val;
+ i2c_common->cmd_completion(i2c_common);
+
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (ret != IRQ_HANDLED) {
+ val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
+ if (val != 0) {
+ writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+ pci_warn(pdev, "received irq without message\n");
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common)
+{
+ i2c_common->reqcmd = i2c_none;
+ amd_mp2_c2p_mutex_unlock(i2c_common);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
+
+int amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+ struct pci_dev *pdev = privdata->pci_dev;
+
+ if (i2c_common->bus_id > 1)
+ return -EINVAL;
+
+ if (privdata->busses[i2c_common->bus_id]) {
+ pci_err(pdev, "Bus %d already taken!\n", i2c_common->bus_id);
+ return -EINVAL;
+ }
+
+ privdata->busses[i2c_common->bus_id] = i2c_common;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amd_mp2_register_cb);
+
+int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common)
+{
+ struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+ privdata->busses[i2c_common->bus_id] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amd_mp2_unregister_cb);
+
+static void amd_mp2_clear_reg(struct amd_mp2_dev *privdata)
+{
+ int reg;
+
+ for (reg = AMD_C2P_MSG0; reg <= AMD_C2P_MSG9; reg += 4)
+ writel(0, privdata->mmio + reg);
+
+ for (reg = AMD_P2C_MSG1; reg <= AMD_P2C_MSG2; reg += 4)
+ writel(0, privdata->mmio + reg);
+}
+
+static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
+ struct pci_dev *pci_dev)
+{
+ int irq_flag = 0, rc;
+
+ pci_set_drvdata(pci_dev, privdata);
+
+ rc = pcim_enable_device(pci_dev);
+ if (rc) {
+ pci_err(pci_dev, "Failed to enable MP2 PCI device\n");
+ goto err_pci_enable;
+ }
+
+ rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
+ if (rc) {
+ pci_err(pci_dev, "I/O memory remapping failed\n");
+ goto err_pci_enable;
+ }
+ privdata->mmio = pcim_iomap_table(pci_dev)[2];
+
+ pci_set_master(pci_dev);
+
+ rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
+ if (rc)
+ goto err_dma_mask;
+
+ /* request and enable interrupt */
+ writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+ rc = pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (rc < 0) {
+ dev_err(&pci_dev->dev, "Failed to allocate single IRQ err=%d\n", rc);
+ goto err_dma_mask;
+ }
+
+ privdata->dev_irq = pci_irq_vector(pci_dev, 0);
+ if (!pci_dev->msix_enabled && !pci_dev->msi_enabled)
+ irq_flag = IRQF_SHARED;
+
+ rc = devm_request_irq(&pci_dev->dev, privdata->dev_irq,
+ amd_mp2_irq_isr, irq_flag, dev_name(&pci_dev->dev), privdata);
+ if (rc) {
+ pci_err(pci_dev, "Failure requesting irq %i: %d\n", privdata->dev_irq, rc);
+ goto err_dma_mask;
+ }
+
+ return rc;
+
+err_dma_mask:
+ pci_clear_master(pci_dev);
+err_pci_enable:
+ pci_set_drvdata(pci_dev, NULL);
+ return rc;
+}
+
+static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *id)
+{
+ struct amd_mp2_dev *privdata;
+ int rc;
+
+ privdata = devm_kzalloc(&pci_dev->dev, sizeof(*privdata), GFP_KERNEL);
+ if (!privdata)
+ return -ENOMEM;
+
+ privdata->pci_dev = pci_dev;
+ rc = amd_mp2_pci_init(privdata, pci_dev);
+ if (rc)
+ return rc;
+
+ mutex_init(&privdata->c2p_lock);
+
+ pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
+ pm_runtime_use_autosuspend(&pci_dev->dev);
+ pm_runtime_put_autosuspend(&pci_dev->dev);
+ pm_runtime_allow(&pci_dev->dev);
+
+ privdata->probed = true;
+
+ pci_info(pci_dev, "MP2 device registered.\n");
+ return 0;
+}
+
+static void amd_mp2_pci_remove(struct pci_dev *pci_dev)
+{
+ struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+
+ pm_runtime_forbid(&pci_dev->dev);
+ pm_runtime_get_noresume(&pci_dev->dev);
+
+ pci_clear_master(pci_dev);
+
+ amd_mp2_clear_reg(privdata);
+}
+
+#ifdef CONFIG_PM
+static int amd_mp2_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+ struct amd_i2c_common *i2c_common;
+ unsigned int bus_id;
+ int ret = 0;
+
+ for (bus_id = 0; bus_id < 2; bus_id++) {
+ i2c_common = privdata->busses[bus_id];
+ if (i2c_common)
+ i2c_common->suspend(i2c_common);
+ }
+
+ ret = pci_save_state(pci_dev);
+ if (ret) {
+ pci_err(pci_dev, "pci_save_state failed = %d\n", ret);
+ return ret;
+ }
+
+ pci_disable_device(pci_dev);
+ return ret;
+}
+
+static int amd_mp2_pci_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+ struct amd_i2c_common *i2c_common;
+ unsigned int bus_id;
+ int ret = 0;
+
+ pci_restore_state(pci_dev);
+ ret = pci_enable_device(pci_dev);
+ if (ret < 0) {
+ pci_err(pci_dev, "pci_enable_device failed = %d\n", ret);
+ return ret;
+ }
+
+ for (bus_id = 0; bus_id < 2; bus_id++) {
+ i2c_common = privdata->busses[bus_id];
+ if (i2c_common) {
+ ret = i2c_common->resume(i2c_common);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static UNIVERSAL_DEV_PM_OPS(amd_mp2_pci_pm_ops, amd_mp2_pci_suspend,
+ amd_mp2_pci_resume, NULL);
+#endif /* CONFIG_PM */
+
+static const struct pci_device_id amd_mp2_pci_tbl[] = {
+ {PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2)},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
+
+static struct pci_driver amd_mp2_pci_driver = {
+ .name = "i2c_amd_mp2",
+ .id_table = amd_mp2_pci_tbl,
+ .probe = amd_mp2_pci_probe,
+ .remove = amd_mp2_pci_remove,
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &amd_mp2_pci_pm_ops,
+ },
+#endif
+};
+module_pci_driver(amd_mp2_pci_driver);
+
+struct amd_mp2_dev *amd_mp2_find_device(void)
+{
+ struct device *dev;
+ struct pci_dev *pci_dev;
+ struct amd_mp2_dev *mp2_dev;
+
+ dev = driver_find_next_device(&amd_mp2_pci_driver.driver, NULL);
+ if (!dev)
+ return NULL;
+
+ pci_dev = to_pci_dev(dev);
+ mp2_dev = (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
+ put_device(dev);
+ return mp2_dev;
+}
+EXPORT_SYMBOL_GPL(amd_mp2_find_device);
+
+MODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver");
+MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
+MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c
new file mode 100644
index 000000000000..188e24cc4d35
--- /dev/null
+++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * AMD MP2 platform driver
+ *
+ * Setup the I2C adapters enumerated in the ACPI namespace.
+ * MP2 controllers have 2 separate busses, up to 2 I2C adapters may be listed.
+ *
+ * Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
+ * Elie Morisse <syniurge@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "i2c-amd-mp2.h"
+
+#define AMD_MP2_I2C_MAX_RW_LENGTH ((1 << 12) - 1)
+#define AMD_I2C_TIMEOUT (msecs_to_jiffies(250))
+
+/**
+ * struct amd_i2c_dev - MP2 bus/i2c adapter context
+ * @common: shared context with the MP2 PCI driver
+ * @pdev: platform driver node
+ * @adap: i2c adapter
+ * @cmd_complete: xfer completion object
+ */
+struct amd_i2c_dev {
+ struct amd_i2c_common common;
+ struct platform_device *pdev;
+ struct i2c_adapter adap;
+ struct completion cmd_complete;
+};
+
+#define amd_i2c_dev_common(__common) \
+ container_of(__common, struct amd_i2c_dev, common)
+
+static int i2c_amd_dma_map(struct amd_i2c_common *i2c_common)
+{
+ struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
+ struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+ enum dma_data_direction dma_direction =
+ i2c_common->msg->flags & I2C_M_RD ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ i2c_common->dma_buf = i2c_get_dma_safe_msg_buf(i2c_common->msg, 0);
+ i2c_common->dma_addr = dma_map_single(dev_pci, i2c_common->dma_buf,
+ i2c_common->msg->len,
+ dma_direction);
+
+ if (unlikely(dma_mapping_error(dev_pci, i2c_common->dma_addr))) {
+ dev_err(&i2c_dev->pdev->dev,
+ "Error while mapping dma buffer %p\n",
+ i2c_common->dma_buf);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void i2c_amd_dma_unmap(struct amd_i2c_common *i2c_common)
+{
+ struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
+ enum dma_data_direction dma_direction =
+ i2c_common->msg->flags & I2C_M_RD ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ dma_unmap_single(dev_pci, i2c_common->dma_addr,
+ i2c_common->msg->len, dma_direction);
+
+ i2c_put_dma_safe_msg_buf(i2c_common->dma_buf, i2c_common->msg, true);
+}
+
+static void i2c_amd_start_cmd(struct amd_i2c_dev *i2c_dev)
+{
+ struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+ reinit_completion(&i2c_dev->cmd_complete);
+ i2c_common->cmd_success = false;
+}
+
+static void i2c_amd_cmd_completion(struct amd_i2c_common *i2c_common)
+{
+ struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+ union i2c_event *event = &i2c_common->eventval;
+
+ if (event->r.status == i2c_readcomplete_event)
+ dev_dbg(&i2c_dev->pdev->dev, "readdata:%*ph\n", event->r.length,
+ i2c_common->msg->buf);
+
+ complete(&i2c_dev->cmd_complete);
+}
+
+static int i2c_amd_check_cmd_completion(struct amd_i2c_dev *i2c_dev)
+{
+ struct amd_i2c_common *i2c_common = &i2c_dev->common;
+ unsigned long time_left;
+
+ time_left = wait_for_completion_timeout(&i2c_dev->cmd_complete,
+ i2c_dev->adap.timeout);
+
+ if ((i2c_common->reqcmd == i2c_read ||
+ i2c_common->reqcmd == i2c_write) &&
+ i2c_common->msg->len > 32)
+ i2c_amd_dma_unmap(i2c_common);
+
+ if (time_left == 0) {
+ amd_mp2_rw_timeout(i2c_common);
+ return -ETIMEDOUT;
+ }
+
+ amd_mp2_process_event(i2c_common);
+
+ if (!i2c_common->cmd_success)
+ return -EIO;
+
+ return 0;
+}
+
+static int i2c_amd_enable_set(struct amd_i2c_dev *i2c_dev, bool enable)
+{
+ struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+ i2c_amd_start_cmd(i2c_dev);
+ amd_mp2_bus_enable_set(i2c_common, enable);
+
+ return i2c_amd_check_cmd_completion(i2c_dev);
+}
+
+static int i2c_amd_xfer_msg(struct amd_i2c_dev *i2c_dev, struct i2c_msg *pmsg)
+{
+ struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+ i2c_amd_start_cmd(i2c_dev);
+ i2c_common->msg = pmsg;
+
+ if (pmsg->len > 32)
+ if (i2c_amd_dma_map(i2c_common))
+ return -EIO;
+
+ if (pmsg->flags & I2C_M_RD)
+ amd_mp2_rw(i2c_common, i2c_read);
+ else
+ amd_mp2_rw(i2c_common, i2c_write);
+
+ return i2c_amd_check_cmd_completion(i2c_dev);
+}
+
+static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int i;
+ struct i2c_msg *pmsg;
+ int err = 0;
+
+ /* the adapter might have been deleted while waiting for the bus lock */
+ if (unlikely(!i2c_dev->common.mp2_dev))
+ return -EINVAL;
+
+ amd_mp2_pm_runtime_get(i2c_dev->common.mp2_dev);
+
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ err = i2c_amd_xfer_msg(i2c_dev, pmsg);
+ if (err)
+ break;
+ }
+
+ amd_mp2_pm_runtime_put(i2c_dev->common.mp2_dev);
+ return err ? err : num;
+}
+
+static u32 i2c_amd_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm i2c_amd_algorithm = {
+ .xfer = i2c_amd_xfer,
+ .functionality = i2c_amd_func,
+};
+
+#ifdef CONFIG_PM
+static int i2c_amd_suspend(struct amd_i2c_common *i2c_common)
+{
+ struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+
+ i2c_amd_enable_set(i2c_dev, false);
+ return 0;
+}
+
+static int i2c_amd_resume(struct amd_i2c_common *i2c_common)
+{
+ struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+
+ return i2c_amd_enable_set(i2c_dev, true);
+}
+#endif
+
+static const u32 supported_speeds[] = {
+ I2C_MAX_HIGH_SPEED_MODE_FREQ,
+ I2C_MAX_TURBO_MODE_FREQ,
+ I2C_MAX_FAST_MODE_PLUS_FREQ,
+ I2C_MAX_FAST_MODE_FREQ,
+ I2C_MAX_STANDARD_MODE_FREQ,
+};
+
+static enum speed_enum i2c_amd_get_bus_speed(struct platform_device *pdev)
+{
+ u32 acpi_speed;
+ int i;
+
+ acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
+ /* round down to the lowest standard speed */
+ for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) {
+ if (acpi_speed >= supported_speeds[i])
+ break;
+ }
+ acpi_speed = i < ARRAY_SIZE(supported_speeds) ? supported_speeds[i] : 0;
+
+ switch (acpi_speed) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ return speed100k;
+ case I2C_MAX_FAST_MODE_FREQ:
+ return speed400k;
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ return speed1000k;
+ case I2C_MAX_TURBO_MODE_FREQ:
+ return speed1400k;
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ return speed3400k;
+ default:
+ return speed400k;
+ }
+}
+
+static const struct i2c_adapter_quirks amd_i2c_dev_quirks = {
+ .max_read_len = AMD_MP2_I2C_MAX_RW_LENGTH,
+ .max_write_len = AMD_MP2_I2C_MAX_RW_LENGTH,
+};
+
+static int i2c_amd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct amd_i2c_dev *i2c_dev;
+ struct amd_mp2_dev *mp2_dev;
+ u64 uid;
+
+ ret = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
+ if (ret)
+ return dev_err_probe(dev, ret, "missing UID/bus id!\n");
+ if (uid >= 2)
+ return dev_err_probe(dev, -EINVAL, "incorrect UID/bus id \"%llu\"!\n", uid);
+ dev_dbg(dev, "bus id is %llu\n", uid);
+
+ /* The ACPI namespace doesn't contain information about which MP2 PCI
+ * device an AMDI0011 ACPI device is related to, so assume that there's
+ * only one MP2 PCI device per system.
+ */
+ mp2_dev = amd_mp2_find_device();
+ if (!mp2_dev || !mp2_dev->probed)
+ /* The MP2 PCI device should get probed later */
+ return -EPROBE_DEFER;
+
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+
+ i2c_dev->common.bus_id = uid;
+ i2c_dev->common.mp2_dev = mp2_dev;
+ i2c_dev->pdev = pdev;
+ platform_set_drvdata(pdev, i2c_dev);
+
+ i2c_dev->common.cmd_completion = &i2c_amd_cmd_completion;
+#ifdef CONFIG_PM
+ i2c_dev->common.suspend = &i2c_amd_suspend;
+ i2c_dev->common.resume = &i2c_amd_resume;
+#endif
+
+ /* Register the adapter */
+ amd_mp2_pm_runtime_get(mp2_dev);
+
+ i2c_dev->common.reqcmd = i2c_none;
+ if (amd_mp2_register_cb(&i2c_dev->common))
+ return -EINVAL;
+ device_link_add(&i2c_dev->pdev->dev, &mp2_dev->pci_dev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+
+ i2c_dev->common.i2c_speed = i2c_amd_get_bus_speed(pdev);
+
+ /* Setup i2c adapter description */
+ i2c_dev->adap.owner = THIS_MODULE;
+ i2c_dev->adap.algo = &i2c_amd_algorithm;
+ i2c_dev->adap.quirks = &amd_i2c_dev_quirks;
+ i2c_dev->adap.dev.parent = &pdev->dev;
+ i2c_dev->adap.algo_data = i2c_dev;
+ i2c_dev->adap.timeout = AMD_I2C_TIMEOUT;
+ ACPI_COMPANION_SET(&i2c_dev->adap.dev, ACPI_COMPANION(&pdev->dev));
+ i2c_dev->adap.dev.of_node = pdev->dev.of_node;
+ snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
+ "AMD MP2 i2c bus %u", i2c_dev->common.bus_id);
+ i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
+
+ init_completion(&i2c_dev->cmd_complete);
+
+ /* Enable the bus */
+ if (i2c_amd_enable_set(i2c_dev, true))
+ dev_err(&pdev->dev, "initial bus enable failed\n");
+
+ /* Attach to the i2c layer */
+ ret = i2c_add_adapter(&i2c_dev->adap);
+
+ amd_mp2_pm_runtime_put(mp2_dev);
+
+ if (ret < 0)
+ dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
+
+ return ret;
+}
+
+static void i2c_amd_remove(struct platform_device *pdev)
+{
+ struct amd_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+ i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ i2c_amd_enable_set(i2c_dev, false);
+ amd_mp2_unregister_cb(i2c_common);
+ i2c_common->mp2_dev = NULL;
+
+ i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ i2c_del_adapter(&i2c_dev->adap);
+}
+
+static const struct acpi_device_id i2c_amd_acpi_match[] = {
+ { "AMDI0011" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
+
+static struct platform_driver i2c_amd_plat_driver = {
+ .probe = i2c_amd_probe,
+ .remove = i2c_amd_remove,
+ .driver = {
+ .name = "i2c_amd_mp2",
+ .acpi_match_table = ACPI_PTR(i2c_amd_acpi_match),
+ },
+};
+module_platform_driver(i2c_amd_plat_driver);
+
+MODULE_DESCRIPTION("AMD(R) MP2 I2C Platform Driver");
+MODULE_AUTHOR("Nehal Shah <nehal-bakulchandra.shah@amd.com>");
+MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/i2c/busses/i2c-amd-mp2.h b/drivers/i2c/busses/i2c-amd-mp2.h
new file mode 100644
index 000000000000..9b7e9494dd12
--- /dev/null
+++ b/drivers/i2c/busses/i2c-amd-mp2.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * AMD MP2 I2C adapter driver
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Elie Morisse <syniurge@gmail.com>
+ */
+
+#ifndef I2C_AMD_PCI_MP2_H
+#define I2C_AMD_PCI_MP2_H
+
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#define PCI_DEVICE_ID_AMD_MP2 0x15E6
+
+struct amd_i2c_common;
+struct amd_mp2_dev;
+
+enum {
+ /* MP2 C2P Message Registers */
+ AMD_C2P_MSG0 = 0x10500, /* MP2 Message for I2C0 */
+ AMD_C2P_MSG1 = 0x10504, /* MP2 Message for I2C1 */
+ AMD_C2P_MSG2 = 0x10508, /* DRAM Address Lo / Data 0 */
+ AMD_C2P_MSG3 = 0x1050c, /* DRAM Address HI / Data 1 */
+ AMD_C2P_MSG4 = 0x10510, /* Data 2 */
+ AMD_C2P_MSG5 = 0x10514, /* Data 3 */
+ AMD_C2P_MSG6 = 0x10518, /* Data 4 */
+ AMD_C2P_MSG7 = 0x1051c, /* Data 5 */
+ AMD_C2P_MSG8 = 0x10520, /* Data 6 */
+ AMD_C2P_MSG9 = 0x10524, /* Data 7 */
+
+ /* MP2 P2C Message Registers */
+ AMD_P2C_MSG0 = 0x10680, /* Do not use */
+ AMD_P2C_MSG1 = 0x10684, /* I2C0 interrupt register */
+ AMD_P2C_MSG2 = 0x10688, /* I2C1 interrupt register */
+ AMD_P2C_MSG3 = 0x1068C, /* MP2 debug info */
+ AMD_P2C_MSG_INTEN = 0x10690, /* MP2 interrupt gen register */
+ AMD_P2C_MSG_INTSTS = 0x10694, /* Interrupt status */
+};
+
+/* Command register data structures */
+
+#define i2c_none (-1)
+enum i2c_cmd {
+ i2c_read = 0,
+ i2c_write,
+ i2c_enable,
+ i2c_disable,
+ number_of_sensor_discovered,
+ is_mp2_active,
+ invalid_cmd = 0xF,
+};
+
+enum speed_enum {
+ speed100k = 0,
+ speed400k = 1,
+ speed1000k = 2,
+ speed1400k = 3,
+ speed3400k = 4
+};
+
+enum mem_type {
+ use_dram = 0,
+ use_c2pmsg = 1,
+};
+
+/**
+ * union i2c_cmd_base : bit access of C2P commands
+ * @i2c_cmd: bit 0..3 i2c R/W command
+ * @bus_id: bit 4..7 i2c bus index
+ * @slave_addr: bit 8..15 slave address
+ * @length: bit 16..27 read/write length
+ * @i2c_speed: bit 28..30 bus speed
+ * @mem_type: bit 31 0-DRAM; 1-C2P msg o/p
+ */
+union i2c_cmd_base {
+ u32 ul;
+ struct {
+ enum i2c_cmd i2c_cmd : 4;
+ u8 bus_id : 4;
+ u32 slave_addr : 8;
+ u32 length : 12;
+ enum speed_enum i2c_speed : 3;
+ enum mem_type mem_type : 1;
+ } s;
+};
+
+enum response_type {
+ invalid_response = 0,
+ command_success = 1,
+ command_failed = 2,
+};
+
+enum status_type {
+ i2c_readcomplete_event = 0,
+ i2c_readfail_event = 1,
+ i2c_writecomplete_event = 2,
+ i2c_writefail_event = 3,
+ i2c_busenable_complete = 4,
+ i2c_busenable_failed = 5,
+ i2c_busdisable_complete = 6,
+ i2c_busdisable_failed = 7,
+ invalid_data_length = 8,
+ invalid_slave_address = 9,
+ invalid_i2cbus_id = 10,
+ invalid_dram_addr = 11,
+ invalid_command = 12,
+ mp2_active = 13,
+ numberof_sensors_discovered_resp = 14,
+ i2c_bus_notinitialized
+};
+
+/**
+ * union i2c_event : bit access of P2C events
+ * @response: bit 0..1 i2c response type
+ * @status: bit 2..6 status_type
+ * @mem_type: bit 7 0-DRAM; 1-C2P msg o/p
+ * @bus_id: bit 8..11 i2c bus id
+ * @length: bit 12..23 message length
+ * @slave_addr: bit 24-31 slave address
+ */
+union i2c_event {
+ u32 ul;
+ struct {
+ enum response_type response : 2;
+ enum status_type status : 5;
+ enum mem_type mem_type : 1;
+ u8 bus_id : 4;
+ u32 length : 12;
+ u32 slave_addr : 8;
+ } r;
+};
+
+/**
+ * struct amd_i2c_common - per bus/i2c adapter context, shared
+ * between the pci and the platform driver
+ * @eventval: MP2 event value set by the IRQ handler
+ * @mp2_dev: MP2 pci device this adapter is part of
+ * @msg: i2c message
+ * @cmd_completion: function called by the IRQ handler to signal
+ * the platform driver
+ * @reqcmd: requested i2c command type
+ * @cmd_success: set to true if the MP2 responded to a command with
+ * the expected status and response type
+ * @bus_id: bus index
+ * @i2c_speed: i2c bus speed determined by the slowest slave
+ * @dma_buf: if msg length > 32, holds the DMA buffer virtual address
+ * @dma_addr: if msg length > 32, holds the DMA buffer address
+ */
+struct amd_i2c_common {
+ union i2c_event eventval;
+ struct amd_mp2_dev *mp2_dev;
+ struct i2c_msg *msg;
+ void (*cmd_completion)(struct amd_i2c_common *i2c_common);
+ enum i2c_cmd reqcmd;
+ u8 cmd_success;
+ u8 bus_id;
+ enum speed_enum i2c_speed;
+ u8 *dma_buf;
+ dma_addr_t dma_addr;
+#ifdef CONFIG_PM
+ int (*suspend)(struct amd_i2c_common *i2c_common);
+ int (*resume)(struct amd_i2c_common *i2c_common);
+#endif /* CONFIG_PM */
+};
+
+/**
+ * struct amd_mp2_dev - per PCI device context
+ * @pci_dev: PCI driver node
+ * @busses: MP2 devices may have up to two busses,
+ * each bus corresponding to an i2c adapter
+ * @mmio: iommapped registers
+ * @c2p_lock: controls access to the C2P mailbox shared between
+ * the two adapters
+ * @c2p_lock_busid: id of the adapter which locked c2p_lock
+ */
+struct amd_mp2_dev {
+ struct pci_dev *pci_dev;
+ struct amd_i2c_common *busses[2];
+ void __iomem *mmio;
+ struct mutex c2p_lock;
+ u8 c2p_lock_busid;
+ unsigned int probed;
+ int dev_irq;
+};
+
+/* PCIe communication driver */
+
+int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
+int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable);
+
+void amd_mp2_process_event(struct amd_i2c_common *i2c_common);
+
+void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common);
+
+int amd_mp2_register_cb(struct amd_i2c_common *i2c_common);
+int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common);
+
+struct amd_mp2_dev *amd_mp2_find_device(void);
+
+static inline void amd_mp2_pm_runtime_get(struct amd_mp2_dev *mp2_dev)
+{
+ pm_runtime_get_sync(&mp2_dev->pci_dev->dev);
+}
+
+static inline void amd_mp2_pm_runtime_put(struct amd_mp2_dev *mp2_dev)
+{
+ pm_runtime_put_autosuspend(&mp2_dev->pci_dev->dev);
+}
+
+#endif
diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c
deleted file mode 100644
index a2f5f992af7a..000000000000
--- a/drivers/i2c/busses/i2c-amd756-s4882.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
- *
- * Copyright (C) 2004, 2008 Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-/*
- * We select the channels by sending commands to the Philips
- * PCA9556 chip at I2C address 0x18. The main adapter is used for
- * the non-multiplexed part of the bus, and 4 virtual adapters
- * are defined for the multiplexed addresses: 0x50-0x53 (memory
- * module EEPROM) located on channels 1-4, and 0x4c (LM63)
- * located on multiplexed channels 0 and 5-7. We define one
- * virtual adapter per CPU, which corresponds to two multiplexed
- * channels:
- * CPU0: virtual adapter 1, channels 1 and 0
- * CPU1: virtual adapter 2, channels 2 and 5
- * CPU2: virtual adapter 3, channels 3 and 6
- * CPU3: virtual adapter 4, channels 4 and 7
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-
-extern struct i2c_adapter amd756_smbus;
-
-static struct i2c_adapter *s4882_adapter;
-static struct i2c_algorithm *s4882_algo;
-
-/* Wrapper access functions for multiplexed SMBus */
-static DEFINE_MUTEX(amd756_lock);
-
-static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data)
-{
- int error;
-
- /* We exclude the multiplexed addresses */
- if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
- || addr == 0x18)
- return -ENXIO;
-
- mutex_lock(&amd756_lock);
-
- error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
- command, size, data);
-
- mutex_unlock(&amd756_lock);
-
- return error;
-}
-
-/* We remember the last used channels combination so as to only switch
- channels when it is really needed. This greatly reduces the SMBus
- overhead, but also assumes that nobody will be writing to the PCA9556
- in our back. */
-static u8 last_channels;
-
-static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- u8 channels)
-{
- int error;
-
- /* We exclude the non-multiplexed addresses */
- if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
- return -ENXIO;
-
- mutex_lock(&amd756_lock);
-
- if (last_channels != channels) {
- union i2c_smbus_data mplxdata;
- mplxdata.byte = channels;
-
- error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
- I2C_SMBUS_WRITE, 0x01,
- I2C_SMBUS_BYTE_DATA,
- &mplxdata);
- if (error)
- goto UNLOCK;
- last_channels = channels;
- }
- error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
- command, size, data);
-
-UNLOCK:
- mutex_unlock(&amd756_lock);
- return error;
-}
-
-static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data)
-{
- /* CPU0: channels 1 and 0 enabled */
- return amd756_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x03);
-}
-
-static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data)
-{
- /* CPU1: channels 2 and 5 enabled */
- return amd756_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x24);
-}
-
-static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data)
-{
- /* CPU2: channels 3 and 6 enabled */
- return amd756_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x48);
-}
-
-static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data)
-{
- /* CPU3: channels 4 and 7 enabled */
- return amd756_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x90);
-}
-
-static int __init amd756_s4882_init(void)
-{
- int i, error;
- union i2c_smbus_data ioconfig;
-
- if (!amd756_smbus.dev.parent)
- return -ENODEV;
-
- /* Configure the PCA9556 multiplexer */
- ioconfig.byte = 0x00; /* All I/O to output mode */
- error = i2c_smbus_xfer(&amd756_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
- I2C_SMBUS_BYTE_DATA, &ioconfig);
- if (error) {
- dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n");
- error = -EIO;
- goto ERROR0;
- }
-
- /* Unregister physical bus */
- i2c_del_adapter(&amd756_smbus);
-
- printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
- /* Define the 5 virtual adapters and algorithms structures */
- if (!(s4882_adapter = kcalloc(5, sizeof(struct i2c_adapter),
- GFP_KERNEL))) {
- error = -ENOMEM;
- goto ERROR1;
- }
- if (!(s4882_algo = kcalloc(5, sizeof(struct i2c_algorithm),
- GFP_KERNEL))) {
- error = -ENOMEM;
- goto ERROR2;
- }
-
- /* Fill in the new structures */
- s4882_algo[0] = *(amd756_smbus.algo);
- s4882_algo[0].smbus_xfer = amd756_access_virt0;
- s4882_adapter[0] = amd756_smbus;
- s4882_adapter[0].algo = s4882_algo;
- s4882_adapter[0].dev.parent = amd756_smbus.dev.parent;
- for (i = 1; i < 5; i++) {
- s4882_algo[i] = *(amd756_smbus.algo);
- s4882_adapter[i] = amd756_smbus;
- snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name),
- "SMBus 8111 adapter (CPU%d)", i-1);
- s4882_adapter[i].algo = s4882_algo+i;
- s4882_adapter[i].dev.parent = amd756_smbus.dev.parent;
- }
- s4882_algo[1].smbus_xfer = amd756_access_virt1;
- s4882_algo[2].smbus_xfer = amd756_access_virt2;
- s4882_algo[3].smbus_xfer = amd756_access_virt3;
- s4882_algo[4].smbus_xfer = amd756_access_virt4;
-
- /* Register virtual adapters */
- for (i = 0; i < 5; i++) {
- error = i2c_add_adapter(s4882_adapter+i);
- if (error) {
- printk(KERN_ERR "i2c-amd756-s4882: "
- "Virtual adapter %d registration "
- "failed, module not inserted\n", i);
- for (i--; i >= 0; i--)
- i2c_del_adapter(s4882_adapter+i);
- goto ERROR3;
- }
- }
-
- return 0;
-
-ERROR3:
- kfree(s4882_algo);
- s4882_algo = NULL;
-ERROR2:
- kfree(s4882_adapter);
- s4882_adapter = NULL;
-ERROR1:
- /* Restore physical bus */
- i2c_add_adapter(&amd756_smbus);
-ERROR0:
- return error;
-}
-
-static void __exit amd756_s4882_exit(void)
-{
- if (s4882_adapter) {
- int i;
-
- for (i = 0; i < 5; i++)
- i2c_del_adapter(s4882_adapter+i);
- kfree(s4882_adapter);
- s4882_adapter = NULL;
- }
- kfree(s4882_algo);
- s4882_algo = NULL;
-
- /* Restore physical bus */
- if (i2c_add_adapter(&amd756_smbus))
- printk(KERN_ERR "i2c-amd756-s4882: "
- "Physical bus restoration failed\n");
-}
-
-MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
-MODULE_DESCRIPTION("S4882 SMBus multiplexing");
-MODULE_LICENSE("GPL");
-
-module_init(amd756_s4882_init);
-module_exit(amd756_s4882_exit);
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 274908cd1fde..3621c02f1cba 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
@@ -6,15 +7,6 @@
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -219,7 +211,7 @@ static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE)
- outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */
+ outw_p(data->word, SMB_HOST_DATA);
size = AMD756_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
@@ -264,7 +256,7 @@ static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
data->byte = inw_p(SMB_HOST_DATA);
break;
case AMD756_WORD_DATA:
- data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */
+ data->word = inw_p(SMB_HOST_DATA);
break;
case AMD756_BLOCK_DATA:
data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
@@ -291,9 +283,9 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = amd756_func,
};
-struct i2c_adapter amd756_smbus = {
+static struct i2c_adapter amd756_smbus = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
@@ -406,5 +398,3 @@ module_pci_driver(amd756_driver);
MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(amd756_smbus);
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index 134567f3019f..42a9b1221065 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SMBus 2.0 driver for AMD-8111 IO-Hub.
*
* Copyright (c) 2002 Vojtech Pavlik
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2.
*/
#include <linux/module.h>
@@ -189,9 +186,9 @@ static int amd_ec_write(struct amd_smbus *smbus, unsigned char address,
#define AMD_SMB_PRTCL_PEC 0x80
-static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
+static s32 amd8111_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command, int size,
- union i2c_smbus_data * data)
+ union i2c_smbus_data *data)
{
struct amd_smbus *smbus = adap->algo_data;
unsigned char protocol, len, pec, temp[2];
@@ -202,130 +199,130 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
switch (size) {
- case I2C_SMBUS_QUICK:
- protocol |= AMD_SMB_PRTCL_QUICK;
- read_write = I2C_SMBUS_WRITE;
- break;
-
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_WRITE) {
- status = amd_ec_write(smbus, AMD_SMB_CMD,
- command);
- if (status)
- return status;
- }
- protocol |= AMD_SMB_PRTCL_BYTE;
- break;
-
- case I2C_SMBUS_BYTE_DATA:
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ case I2C_SMBUS_QUICK:
+ protocol |= AMD_SMB_PRTCL_QUICK;
+ read_write = I2C_SMBUS_WRITE;
+ break;
+
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE) {
+ status = amd_ec_write(smbus, AMD_SMB_CMD,
+ command);
if (status)
return status;
- if (read_write == I2C_SMBUS_WRITE) {
- status = amd_ec_write(smbus, AMD_SMB_DATA,
- data->byte);
- if (status)
- return status;
- }
- protocol |= AMD_SMB_PRTCL_BYTE_DATA;
- break;
+ }
+ protocol |= AMD_SMB_PRTCL_BYTE;
+ break;
- case I2C_SMBUS_WORD_DATA:
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ case I2C_SMBUS_BYTE_DATA:
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ if (read_write == I2C_SMBUS_WRITE) {
+ status = amd_ec_write(smbus, AMD_SMB_DATA,
+ data->byte);
if (status)
return status;
- if (read_write == I2C_SMBUS_WRITE) {
- status = amd_ec_write(smbus, AMD_SMB_DATA,
- data->word & 0xff);
- if (status)
- return status;
- status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
- data->word >> 8);
- if (status)
- return status;
- }
- protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
- break;
+ }
+ protocol |= AMD_SMB_PRTCL_BYTE_DATA;
+ break;
- case I2C_SMBUS_BLOCK_DATA:
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
- if (status)
- return status;
- if (read_write == I2C_SMBUS_WRITE) {
- len = min_t(u8, data->block[0],
- I2C_SMBUS_BLOCK_MAX);
- status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
- if (status)
- return status;
- for (i = 0; i < len; i++) {
- status =
- amd_ec_write(smbus, AMD_SMB_DATA + i,
- data->block[i + 1]);
- if (status)
- return status;
- }
- }
- protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
- break;
-
- case I2C_SMBUS_I2C_BLOCK_DATA:
- len = min_t(u8, data->block[0],
- I2C_SMBUS_BLOCK_MAX);
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
- if (status)
- return status;
- status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
- if (status)
- return status;
- if (read_write == I2C_SMBUS_WRITE)
- for (i = 0; i < len; i++) {
- status =
- amd_ec_write(smbus, AMD_SMB_DATA + i,
- data->block[i + 1]);
- if (status)
- return status;
- }
- protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
- break;
-
- case I2C_SMBUS_PROC_CALL:
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
- if (status)
- return status;
+ case I2C_SMBUS_WORD_DATA:
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ if (read_write == I2C_SMBUS_WRITE) {
status = amd_ec_write(smbus, AMD_SMB_DATA,
- data->word & 0xff);
+ data->word & 0xff);
if (status)
return status;
status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
- data->word >> 8);
+ data->word >> 8);
if (status)
return status;
- protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
- read_write = I2C_SMBUS_READ;
- break;
+ }
+ protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
+ break;
- case I2C_SMBUS_BLOCK_PROC_CALL:
+ case I2C_SMBUS_BLOCK_DATA:
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0],
- I2C_SMBUS_BLOCK_MAX - 1);
- status = amd_ec_write(smbus, AMD_SMB_CMD, command);
- if (status)
- return status;
+ I2C_SMBUS_BLOCK_MAX);
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
if (status)
return status;
for (i = 0; i < len; i++) {
- status = amd_ec_write(smbus, AMD_SMB_DATA + i,
- data->block[i + 1]);
+ status =
+ amd_ec_write(smbus, AMD_SMB_DATA + i,
+ data->block[i + 1]);
+ if (status)
+ return status;
+ }
+ }
+ protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
+ break;
+
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ len = min_t(u8, data->block[0],
+ I2C_SMBUS_BLOCK_MAX);
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
+ if (status)
+ return status;
+ if (read_write == I2C_SMBUS_WRITE)
+ for (i = 0; i < len; i++) {
+ status =
+ amd_ec_write(smbus, AMD_SMB_DATA + i,
+ data->block[i + 1]);
if (status)
return status;
}
- protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
- read_write = I2C_SMBUS_READ;
- break;
+ protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
+ break;
- default:
- dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
- return -EOPNOTSUPP;
+ case I2C_SMBUS_PROC_CALL:
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ status = amd_ec_write(smbus, AMD_SMB_DATA,
+ data->word & 0xff);
+ if (status)
+ return status;
+ status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
+ data->word >> 8);
+ if (status)
+ return status;
+ protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
+ read_write = I2C_SMBUS_READ;
+ break;
+
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ len = min_t(u8, data->block[0],
+ I2C_SMBUS_BLOCK_MAX - 1);
+ status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+ if (status)
+ return status;
+ status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
+ if (status)
+ return status;
+ for (i = 0; i < len; i++) {
+ status = amd_ec_write(smbus, AMD_SMB_DATA + i,
+ data->block[i + 1]);
+ if (status)
+ return status;
+ }
+ protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+ read_write = I2C_SMBUS_READ;
+ break;
+
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
@@ -360,40 +357,40 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
return 0;
switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
- status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
- if (status)
- return status;
- break;
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
+ if (status)
+ return status;
+ break;
- case I2C_SMBUS_WORD_DATA:
- case I2C_SMBUS_PROC_CALL:
- status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
- if (status)
- return status;
- status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
- if (status)
- return status;
- data->word = (temp[1] << 8) | temp[0];
- break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
+ if (status)
+ return status;
+ status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
+ if (status)
+ return status;
+ data->word = (temp[1] << 8) | temp[0];
+ break;
- case I2C_SMBUS_BLOCK_DATA:
- case I2C_SMBUS_BLOCK_PROC_CALL:
- status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+ if (status)
+ return status;
+ len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
+ fallthrough;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ for (i = 0; i < len; i++) {
+ status = amd_ec_read(smbus, AMD_SMB_DATA + i,
+ data->block + i + 1);
if (status)
return status;
- len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
- /* fall through */
- case I2C_SMBUS_I2C_BLOCK_DATA:
- for (i = 0; i < len; i++) {
- status = amd_ec_read(smbus, AMD_SMB_DATA + i,
- data->block + i + 1);
- if (status)
- return status;
- }
- data->block[0] = len;
- break;
+ }
+ data->block[0] = len;
+ break;
}
return 0;
@@ -452,7 +449,7 @@ static int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"SMBus2 AMD8111 adapter at %04x", smbus->base);
- smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ smbus->adapter.class = I2C_CLASS_HWMON;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 8dc9161ced38..a26b74c71206 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aspeed 24XX/25XX I2C Controller.
*
* Copyright (C) 2012-2017 ASPEED Technology Inc.
* Copyright 2017 IBM Corporation
* Copyright 2017 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -19,8 +16,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <linux/irqchip/chained_irq.h>
-#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -72,6 +67,7 @@
* These share bit definitions, so use the same values for the enable &
* status bits.
*/
+#define ASPEED_I2CD_INTR_RECV_MASK 0xf000ffff
#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
@@ -111,12 +107,19 @@
#define ASPEED_I2CD_S_TX_CMD BIT(2)
#define ASPEED_I2CD_M_TX_CMD BIT(1)
#define ASPEED_I2CD_M_START_CMD BIT(0)
+#define ASPEED_I2CD_MASTER_CMDS_MASK \
+ (ASPEED_I2CD_M_STOP_CMD | \
+ ASPEED_I2CD_M_S_RX_CMD_LAST | \
+ ASPEED_I2CD_M_RX_CMD | \
+ ASPEED_I2CD_M_TX_CMD | \
+ ASPEED_I2CD_M_START_CMD)
/* 0x18 : I2CD Slave Device Address Register */
#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
enum aspeed_i2c_master_state {
ASPEED_I2C_MASTER_INACTIVE,
+ ASPEED_I2C_MASTER_PENDING,
ASPEED_I2C_MASTER_START,
ASPEED_I2C_MASTER_TX_FIRST,
ASPEED_I2C_MASTER_TX,
@@ -126,12 +129,13 @@ enum aspeed_i2c_master_state {
};
enum aspeed_i2c_slave_state {
- ASPEED_I2C_SLAVE_STOP,
+ ASPEED_I2C_SLAVE_INACTIVE,
ASPEED_I2C_SLAVE_START,
ASPEED_I2C_SLAVE_READ_REQUESTED,
ASPEED_I2C_SLAVE_READ_PROCESSED,
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+ ASPEED_I2C_SLAVE_STOP,
};
struct aspeed_i2c_bus {
@@ -156,6 +160,8 @@ struct aspeed_i2c_bus {
int cmd_err;
/* Protected only by i2c_lock_bus */
int master_xfer_result;
+ /* Multi-master */
+ bool multi_master;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
enum aspeed_i2c_slave_state slave_state;
@@ -164,6 +170,13 @@ struct aspeed_i2c_bus {
static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus);
+/* precondition: bus.lock has been acquired. */
+static void aspeed_i2c_do_stop(struct aspeed_i2c_bus *bus)
+{
+ bus->master_state = ASPEED_I2C_MASTER_STOP;
+ writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG);
+}
+
static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
{
unsigned long time_left, flags;
@@ -181,7 +194,7 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
command);
reinit_completion(&bus->cmd_complete);
- writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG);
+ aspeed_i2c_do_stop(bus);
spin_unlock_irqrestore(&bus->lock, flags);
time_left = wait_for_completion_timeout(
@@ -238,22 +251,51 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
u32 command, irq_handled = 0;
struct i2c_client *slave = bus->slave;
u8 value;
+ int ret;
if (!slave)
return 0;
- command = readl(bus->base + ASPEED_I2C_CMD_REG);
+ /*
+ * Handle stop conditions early, prior to SLAVE_MATCH. Some masters may drive
+ * transfers with low enough latency between the nak/stop phase of the current
+ * command and the start/address phase of the following command that the
+ * interrupts are coalesced by the time we process them.
+ */
+ if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
+ irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
+ bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+ }
+
+ if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
+ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
+ irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
+ bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+ }
+
+ /* Propagate any stop conditions to the slave implementation. */
+ if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+ i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
+ }
- /* Slave was requested, restart state machine. */
+ /*
+ * Now that we've dealt with any potentially coalesced stop conditions,
+ * address any start conditions.
+ */
if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
irq_handled |= ASPEED_I2CD_INTR_SLAVE_MATCH;
bus->slave_state = ASPEED_I2C_SLAVE_START;
}
- /* Slave is not currently active, irq was for someone else. */
- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP)
+ /*
+ * If the slave has been stopped and not started then slave interrupt
+ * handling is complete.
+ */
+ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
return irq_handled;
+ command = readl(bus->base + ASPEED_I2C_CMD_REG);
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
irq_status, command);
@@ -272,21 +314,9 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
}
- /* Slave was asked to stop. */
- if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
- irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
- bus->slave_state = ASPEED_I2C_SLAVE_STOP;
- }
- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
- irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
- bus->slave_state = ASPEED_I2C_SLAVE_STOP;
- }
- if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
- irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
-
switch (bus->slave_state) {
case ASPEED_I2C_SLAVE_READ_REQUESTED:
- if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
dev_err(bus->dev, "Unexpected ACK on read request.\n");
bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
@@ -294,26 +324,39 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_READ_PROCESSED:
- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
dev_err(bus->dev,
"Expected ACK after processed read.\n");
+ break;
+ }
+ irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_WRITE_REQUESTED:
bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
- i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ ret = i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ /*
+ * Slave ACK's on this address phase already but as the backend driver
+ * returns an errno, the bus driver should nack the next incoming byte.
+ */
+ if (ret < 0)
+ writel(ASPEED_I2CD_M_S_RX_CMD_LAST, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_WRITE_RECEIVED:
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
break;
case ASPEED_I2C_SLAVE_STOP:
- i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+ /* Stop event handling is done early. Unreachable. */
+ break;
+ case ASPEED_I2C_SLAVE_START:
+ /* Slave was just started. Waiting for the next event. */;
break;
default:
- dev_err(bus->dev, "unhandled slave_state: %d\n",
+ dev_err(bus->dev, "unknown slave_state: %d\n",
bus->slave_state);
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
break;
}
@@ -328,6 +371,18 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
u8 slave_addr = i2c_8bit_addr_from_msg(msg);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * If it's requested in the middle of a slave session, set the master
+ * state to 'pending' then H/W will continue handling this master
+ * command when the bus comes back to the idle state.
+ */
+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) {
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
+ return;
+ }
+#endif /* CONFIG_I2C_SLAVE */
+
bus->master_state = ASPEED_I2C_MASTER_START;
bus->buf_index = 0;
@@ -343,13 +398,6 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
}
/* precondition: bus.lock has been acquired. */
-static void aspeed_i2c_do_stop(struct aspeed_i2c_bus *bus)
-{
- bus->master_state = ASPEED_I2C_MASTER_STOP;
- writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG);
-}
-
-/* precondition: bus.lock has been acquired. */
static void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus)
{
if (bus->msgs_index + 1 < bus->msgs_count) {
@@ -384,10 +432,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE;
goto out_complete;
- } else {
- /* Master is not currently active, irq was for someone else. */
- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE)
- goto out_no_complete;
}
/*
@@ -399,12 +443,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
if (ret) {
dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
irq_status);
- bus->cmd_err = ret;
- bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS);
- goto out_complete;
+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+ irq_handled = irq_status;
+ bus->cmd_err = ret;
+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
+ goto out_complete;
+ }
}
+ /* Master is not currently active, irq was for someone else. */
+ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE ||
+ bus->master_state == ASPEED_I2C_MASTER_PENDING)
+ goto out_no_complete;
+
/* We are in an invalid state; reset bus to a known state. */
if (!bus->msgs) {
dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n",
@@ -423,6 +475,24 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
* then update the state and handle the new state below.
*/
if (bus->master_state == ASPEED_I2C_MASTER_START) {
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * If a peer master starts a xfer immediately after it queues a
+ * master command, clear the queued master command and change
+ * its state to 'pending'. To simplify handling of pending
+ * cases, it uses S/W solution instead of H/W command queue
+ * handling.
+ */
+ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) {
+ writel(readl(bus->base + ASPEED_I2C_CMD_REG) &
+ ~ASPEED_I2CD_MASTER_CMDS_MASK,
+ bus->base + ASPEED_I2C_CMD_REG);
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
+ dev_dbg(bus->dev,
+ "master goes pending due to a slave start\n");
+ goto out_no_complete;
+ }
+#endif /* CONFIG_I2C_SLAVE */
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) {
bus->cmd_err = -ENXIO;
@@ -457,7 +527,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
goto error_and_stop;
}
irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
- /* fall through */
+ fallthrough;
case ASPEED_I2C_MASTER_TX_FIRST:
if (bus->buf_index < msg->len) {
bus->master_state = ASPEED_I2C_MASTER_TX;
@@ -473,7 +543,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
/* RX may not have completed yet (only address cycle) */
if (!(irq_status & ASPEED_I2CD_INTR_RX_DONE))
goto out_no_complete;
- /* fall through */
+ fallthrough;
case ASPEED_I2C_MASTER_RX:
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) {
dev_err(bus->dev, "master failed to RX\n");
@@ -556,6 +626,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
/* Ack all interrupts except for Rx done */
writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE,
bus->base + ASPEED_I2C_INTR_STS_REG);
+ readl(bus->base + ASPEED_I2C_INTR_STS_REG);
+ irq_received &= ASPEED_I2CD_INTR_RECV_MASK;
irq_remaining = irq_received;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
@@ -566,7 +638,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
* interrupt bits. Each case needs to be handled using corresponding
* handlers depending on the current state.
*/
- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE &&
+ bus->master_state != ASPEED_I2C_MASTER_PENDING) {
irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
irq_remaining &= ~irq_handled;
if (irq_remaining)
@@ -578,6 +651,14 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
irq_handled |= aspeed_i2c_master_irq(bus,
irq_remaining);
}
+
+ /*
+ * Start a pending master command at here if a slave operation is
+ * completed.
+ */
+ if (bus->master_state == ASPEED_I2C_MASTER_PENDING &&
+ bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
+ aspeed_i2c_do_start(bus);
#else
irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
#endif /* CONFIG_I2C_SLAVE */
@@ -589,9 +670,11 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
irq_received, irq_handled);
/* Ack Rx done */
- if (irq_received & ASPEED_I2CD_INTR_RX_DONE)
+ if (irq_received & ASPEED_I2CD_INTR_RX_DONE) {
writel(ASPEED_I2CD_INTR_RX_DONE,
bus->base + ASPEED_I2C_INTR_STS_REG);
+ readl(bus->base + ASPEED_I2C_INTR_STS_REG);
+ }
spin_unlock(&bus->lock);
return irq_remaining ? IRQ_NONE : IRQ_HANDLED;
}
@@ -601,15 +684,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
{
struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
unsigned long time_left, flags;
- int ret = 0;
spin_lock_irqsave(&bus->lock, flags);
bus->cmd_err = 0;
- /* If bus is busy, attempt recovery. We assume a single master
- * environment.
- */
- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
+ /* If bus is busy in a single master environment, attempt recovery. */
+ if (!bus->multi_master &&
+ (readl(bus->base + ASPEED_I2C_CMD_REG) &
+ ASPEED_I2CD_BUS_BUSY_STS)) {
+ int ret;
+
spin_unlock_irqrestore(&bus->lock, flags);
ret = aspeed_i2c_recover_bus(bus);
if (ret)
@@ -629,10 +713,32 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
time_left = wait_for_completion_timeout(&bus->cmd_complete,
bus->adap.timeout);
- if (time_left == 0)
+ if (time_left == 0) {
+ /*
+ * In a multi-master setup, if a timeout occurs, attempt
+ * recovery. But if the bus is idle, we still need to reset the
+ * i2c controller to clear the remaining interrupts.
+ */
+ if (bus->multi_master &&
+ (readl(bus->base + ASPEED_I2C_CMD_REG) &
+ ASPEED_I2CD_BUS_BUSY_STS))
+ aspeed_i2c_recover_bus(bus);
+ else
+ aspeed_i2c_reset(bus);
+
+ /*
+ * If timed out and the state is still pending, drop the pending
+ * master command.
+ */
+ spin_lock_irqsave(&bus->lock, flags);
+ if (bus->master_state == ASPEED_I2C_MASTER_PENDING)
+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
+ spin_unlock_irqrestore(&bus->lock, flags);
+
return -ETIMEDOUT;
- else
- return bus->master_xfer_result;
+ }
+
+ return bus->master_xfer_result;
}
static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
@@ -646,16 +752,22 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
{
u32 addr_reg_val, func_ctrl_reg_val;
- /* Set slave addr. */
- addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG);
- addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
- addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
+ /*
+ * Set slave addr. Reserved bits can all safely be written with zeros
+ * on all of ast2[456]00, so zero everything else to ensure we only
+ * enable a single slave address (ast2500 has two, ast2600 has three,
+ * the enable bits for which are also in this register) so that we don't
+ * end up with additional phantom devices responding on the bus.
+ */
+ addr_reg_val = slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG);
/* Turn on slave mode. */
func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
}
static int aspeed_i2c_reg_slave(struct i2c_client *client)
@@ -672,7 +784,6 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client)
__aspeed_i2c_reg_slave(bus, client->addr);
bus->slave = client;
- bus->slave_state = ASPEED_I2C_SLAVE_STOP;
spin_unlock_irqrestore(&bus->lock, flags);
return 0;
@@ -703,11 +814,11 @@ static int aspeed_i2c_unreg_slave(struct i2c_client *client)
#endif /* CONFIG_I2C_SLAVE */
static const struct i2c_algorithm aspeed_i2c_algo = {
- .master_xfer = aspeed_i2c_master_xfer,
- .functionality = aspeed_i2c_functionality,
+ .xfer = aspeed_i2c_master_xfer,
+ .functionality = aspeed_i2c_functionality,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
- .reg_slave = aspeed_i2c_reg_slave,
- .unreg_slave = aspeed_i2c_unreg_slave,
+ .reg_slave = aspeed_i2c_reg_slave,
+ .unreg_slave = aspeed_i2c_unreg_slave,
#endif /* CONFIG_I2C_SLAVE */
};
@@ -827,7 +938,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
if (ret < 0)
return ret;
- if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
+ if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
+ bus->multi_master = true;
+ else
fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
/* Enable Master Mode */
@@ -874,7 +987,11 @@ static const struct of_device_id aspeed_i2c_bus_of_table[] = {
.compatible = "aspeed,ast2500-i2c-bus",
.data = aspeed_i2c_25xx_get_clk_reg_val,
},
- { },
+ {
+ .compatible = "aspeed,ast2600-i2c-bus",
+ .data = aspeed_i2c_25xx_get_clk_reg_val,
+ },
+ { }
};
MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
@@ -883,15 +1000,13 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
const struct of_device_id *match;
struct aspeed_i2c_bus *bus;
struct clk *parent_clk;
- struct resource *res;
int irq, ret;
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- bus->base = devm_ioremap_resource(&pdev->dev, res);
+ bus->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(bus->base))
return PTR_ERR(bus->base);
@@ -915,7 +1030,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev,
"Could not read bus-frequency property\n");
- bus->bus_frequency = 100000;
+ bus->bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;
}
match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node);
@@ -930,11 +1045,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
init_completion(&bus->cmd_complete);
bus->adap.owner = THIS_MODULE;
bus->adap.retries = 0;
- bus->adap.timeout = 5 * HZ;
bus->adap.algo = &aspeed_i2c_algo;
bus->adap.dev.parent = &pdev->dev;
bus->adap.dev.of_node = pdev->dev.of_node;
- strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name));
+ strscpy(bus->adap.name, pdev->name, sizeof(bus->adap.name));
i2c_set_adapdata(&bus->adap, bus);
bus->dev = &pdev->dev;
@@ -968,7 +1082,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
return 0;
}
-static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+static void aspeed_i2c_remove_bus(struct platform_device *pdev)
{
struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
unsigned long flags;
@@ -984,8 +1098,6 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev)
reset_control_assert(bus->rst);
i2c_del_adapter(&bus->adap);
-
- return 0;
}
static struct platform_driver aspeed_i2c_bus_driver = {
diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c
new file mode 100644
index 000000000000..b64adef778d4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-at91-core.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ * Copyright (C) 2011 Weinmann Medical GmbH
+ * Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ * Evolved from original work by:
+ * Copyright (C) 2004 Rick Bronson
+ * Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+ *
+ * Borrowed heavily from original work by:
+ * Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "i2c-at91.h"
+
+unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
+{
+ return readl_relaxed(dev->base + reg);
+}
+
+void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
+{
+ writel_relaxed(val, dev->base + reg);
+}
+
+void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
+{
+ at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
+}
+
+void at91_twi_irq_save(struct at91_twi_dev *dev)
+{
+ dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
+ at91_disable_twi_interrupts(dev);
+}
+
+void at91_twi_irq_restore(struct at91_twi_dev *dev)
+{
+ at91_twi_write(dev, AT91_TWI_IER, dev->imr);
+}
+
+void at91_init_twi_bus(struct at91_twi_dev *dev)
+{
+ at91_disable_twi_interrupts(dev);
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
+ if (dev->slave_detected)
+ at91_init_twi_bus_slave(dev);
+ else
+ at91_init_twi_bus_master(dev);
+}
+
+static struct at91_twi_pdata at91rm9200_config = {
+ .clk_max_div = 5,
+ .clk_offset = 3,
+ .has_unre_flag = true,
+};
+
+static struct at91_twi_pdata at91sam9261_config = {
+ .clk_max_div = 5,
+ .clk_offset = 4,
+};
+
+static struct at91_twi_pdata at91sam9260_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+};
+
+static struct at91_twi_pdata at91sam9g20_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+};
+
+static struct at91_twi_pdata at91sam9g10_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+};
+
+static const struct platform_device_id at91_twi_devtypes[] = {
+ {
+ .name = "i2c-at91rm9200",
+ .driver_data = (unsigned long) &at91rm9200_config,
+ }, {
+ .name = "i2c-at91sam9261",
+ .driver_data = (unsigned long) &at91sam9261_config,
+ }, {
+ .name = "i2c-at91sam9260",
+ .driver_data = (unsigned long) &at91sam9260_config,
+ }, {
+ .name = "i2c-at91sam9g20",
+ .driver_data = (unsigned long) &at91sam9g20_config,
+ }, {
+ .name = "i2c-at91sam9g10",
+ .driver_data = (unsigned long) &at91sam9g10_config,
+ }, {
+ /* sentinel */
+ }
+};
+
+#if defined(CONFIG_OF)
+static struct at91_twi_pdata at91sam9x5_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+};
+
+static struct at91_twi_pdata sama5d4_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+};
+
+static struct at91_twi_pdata sama5d2_config = {
+ .clk_max_div = 7,
+ .clk_offset = 3,
+ .has_unre_flag = true,
+ .has_alt_cmd = true,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+ .has_adv_dig_filtr = true,
+ .has_ana_filtr = true,
+ .has_clear_cmd = false, /* due to errata, CLEAR cmd is not working */
+};
+
+static struct at91_twi_pdata sam9x60_config = {
+ .clk_max_div = 7,
+ .clk_offset = 3,
+ .has_unre_flag = true,
+ .has_alt_cmd = true,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+ .has_adv_dig_filtr = true,
+ .has_ana_filtr = true,
+ .has_clear_cmd = true,
+};
+
+static const struct of_device_id atmel_twi_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-i2c",
+ .data = &at91rm9200_config,
+ }, {
+ .compatible = "atmel,at91sam9260-i2c",
+ .data = &at91sam9260_config,
+ }, {
+ .compatible = "atmel,at91sam9261-i2c",
+ .data = &at91sam9261_config,
+ }, {
+ .compatible = "atmel,at91sam9g20-i2c",
+ .data = &at91sam9g20_config,
+ }, {
+ .compatible = "atmel,at91sam9g10-i2c",
+ .data = &at91sam9g10_config,
+ }, {
+ .compatible = "atmel,at91sam9x5-i2c",
+ .data = &at91sam9x5_config,
+ }, {
+ .compatible = "atmel,sama5d4-i2c",
+ .data = &sama5d4_config,
+ }, {
+ .compatible = "atmel,sama5d2-i2c",
+ .data = &sama5d2_config,
+ }, {
+ .compatible = "microchip,sam9x60-i2c",
+ .data = &sam9x60_config,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
+#endif
+
+static struct at91_twi_pdata *at91_twi_get_driver_data(
+ struct platform_device *pdev)
+{
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
+ if (!match)
+ return NULL;
+ return (struct at91_twi_pdata *)match->data;
+ }
+ return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
+}
+
+static int at91_twi_probe(struct platform_device *pdev)
+{
+ struct at91_twi_dev *dev;
+ struct resource *mem;
+ int rc;
+ u32 phy_addr;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->dev = &pdev->dev;
+
+ dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+ phy_addr = mem->start;
+
+ dev->pdata = at91_twi_get_driver_data(pdev);
+ if (!dev->pdata)
+ return -ENODEV;
+
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq < 0)
+ return dev->irq;
+
+ platform_set_drvdata(pdev, dev);
+
+ dev->clk = devm_clk_get_enabled(dev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return dev_err_probe(dev->dev, PTR_ERR(dev->clk),
+ "failed to enable clock\n");
+
+ snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
+ i2c_set_adapdata(&dev->adapter, dev);
+ dev->adapter.owner = THIS_MODULE;
+ dev->adapter.class = I2C_CLASS_DEPRECATED;
+ dev->adapter.dev.parent = dev->dev;
+ dev->adapter.nr = pdev->id;
+ dev->adapter.timeout = AT91_I2C_TIMEOUT;
+ dev->adapter.dev.of_node = pdev->dev.of_node;
+
+ dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
+
+ if (dev->slave_detected)
+ rc = at91_twi_probe_slave(pdev, phy_addr, dev);
+ else
+ rc = at91_twi_probe_master(pdev, phy_addr, dev);
+ if (rc)
+ return rc;
+
+ at91_init_twi_bus(dev);
+
+ pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_active(dev->dev);
+ pm_runtime_enable(dev->dev);
+
+ rc = i2c_add_numbered_adapter(&dev->adapter);
+ if (rc) {
+ pm_runtime_disable(dev->dev);
+ pm_runtime_set_suspended(dev->dev);
+
+ return rc;
+ }
+
+ dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
+ at91_twi_read(dev, AT91_TWI_VER));
+ return 0;
+}
+
+static void at91_twi_remove(struct platform_device *pdev)
+{
+ struct at91_twi_dev *dev = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dev->adapter);
+
+ pm_runtime_disable(dev->dev);
+ pm_runtime_set_suspended(dev->dev);
+}
+
+static int __maybe_unused at91_twi_runtime_suspend(struct device *dev)
+{
+ struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(twi_dev->clk);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int __maybe_unused at91_twi_runtime_resume(struct device *dev)
+{
+ struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+
+ return clk_prepare_enable(twi_dev->clk);
+}
+
+static int __maybe_unused at91_twi_suspend_noirq(struct device *dev)
+{
+ if (!pm_runtime_status_suspended(dev))
+ at91_twi_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int __maybe_unused at91_twi_resume_noirq(struct device *dev)
+{
+ struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ ret = at91_twi_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ pm_request_autosuspend(dev);
+
+ at91_init_twi_bus(twi_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops __maybe_unused at91_twi_pm = {
+ .suspend_noirq = at91_twi_suspend_noirq,
+ .resume_noirq = at91_twi_resume_noirq,
+ .runtime_suspend = at91_twi_runtime_suspend,
+ .runtime_resume = at91_twi_runtime_resume,
+};
+
+static struct platform_driver at91_twi_driver = {
+ .probe = at91_twi_probe,
+ .remove = at91_twi_remove,
+ .id_table = at91_twi_devtypes,
+ .driver = {
+ .name = "at91_i2c",
+ .of_match_table = of_match_ptr(atmel_twi_dt_ids),
+ .pm = pm_ptr(&at91_twi_pm),
+ },
+};
+
+static int __init at91_twi_init(void)
+{
+ return platform_driver_register(&at91_twi_driver);
+}
+
+static void __exit at91_twi_exit(void)
+{
+ platform_driver_unregister(&at91_twi_driver);
+}
+
+subsys_initcall(at91_twi_init);
+module_exit(at91_twi_exit);
+
+MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
+MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_i2c");
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91-master.c
index 3f3e8b3bf5ff..894cedbca99f 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91-master.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
*
@@ -10,11 +11,6 @@
*
* Borrowed heavily from original work by:
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/clk.h>
@@ -22,184 +18,64 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/platform_data/dma-atmel.h>
#include <linux/pm_runtime.h>
-#include <linux/pinctrl/consumer.h>
-
-#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
-#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
-#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
-#define AUTOSUSPEND_TIMEOUT 2000
-#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
-
-/* AT91 TWI register definitions */
-#define AT91_TWI_CR 0x0000 /* Control Register */
-#define AT91_TWI_START BIT(0) /* Send a Start Condition */
-#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
-#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
-#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
-#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
-#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
-#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
-#define AT91_TWI_SWRST BIT(7) /* Software Reset */
-#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
-#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
-#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
-#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
-#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
-#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
-#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
-
-#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
-#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
-#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
-
-#define AT91_TWI_IADR 0x000c /* Internal Address Register */
-
-#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
-#define AT91_TWI_CWGR_HOLD_MAX 0x1f
-#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
-
-#define AT91_TWI_SR 0x0020 /* Status Register */
-#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
-#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
-#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
-#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
-#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
-#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
-#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
-
-#define AT91_TWI_INT_MASK \
- (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK)
-
-#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
-#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
-#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
-#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
-#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
-
-#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
-#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
-#define AT91_TWI_ACR_DIR BIT(8)
-
-#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
-#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
-#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
-#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
-#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
-#define AT91_TWI_ONE_DATA 0x0
-#define AT91_TWI_TWO_DATA 0x1
-#define AT91_TWI_FOUR_DATA 0x2
-
-#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
-
-#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
-#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
-#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
-#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
-
-#define AT91_TWI_VER 0x00fc /* Version Register */
-
-struct at91_twi_pdata {
- unsigned clk_max_div;
- unsigned clk_offset;
- bool has_unre_flag;
- bool has_alt_cmd;
- bool has_hold_field;
- struct at_dma_slave dma_slave;
-};
-
-struct at91_twi_dma {
- struct dma_chan *chan_rx;
- struct dma_chan *chan_tx;
- struct scatterlist sg[2];
- struct dma_async_tx_descriptor *data_desc;
- enum dma_data_direction direction;
- bool buf_mapped;
- bool xfer_in_progress;
-};
-
-struct at91_twi_dev {
- struct device *dev;
- void __iomem *base;
- struct completion cmd_complete;
- struct clk *clk;
- u8 *buf;
- size_t buf_len;
- struct i2c_msg *msg;
- int irq;
- unsigned imr;
- unsigned transfer_status;
- struct i2c_adapter adapter;
- unsigned twi_cwgr_reg;
- struct at91_twi_pdata *pdata;
- bool use_dma;
- bool use_alt_cmd;
- bool recv_len_abort;
- u32 fifo_size;
- struct at91_twi_dma dma;
-};
-
-static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
-{
- return readl_relaxed(dev->base + reg);
-}
-
-static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
-{
- writel_relaxed(val, dev->base + reg);
-}
-
-static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
-{
- at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
-}
+#include <linux/string_choices.h>
-static void at91_twi_irq_save(struct at91_twi_dev *dev)
-{
- dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
- at91_disable_twi_interrupts(dev);
-}
+#include "i2c-at91.h"
-static void at91_twi_irq_restore(struct at91_twi_dev *dev)
+void at91_init_twi_bus_master(struct at91_twi_dev *dev)
{
- at91_twi_write(dev, AT91_TWI_IER, dev->imr);
-}
+ struct at91_twi_pdata *pdata = dev->pdata;
+ u32 filtr = 0;
-static void at91_init_twi_bus(struct at91_twi_dev *dev)
-{
- at91_disable_twi_interrupts(dev);
- at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
/* FIFO should be enabled immediately after the software reset */
if (dev->fifo_size)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS);
at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg);
+
+ /* enable digital filter */
+ if (pdata->has_dig_filtr && dev->enable_dig_filt)
+ filtr |= AT91_TWI_FILTR_FILT;
+
+ /* enable advanced digital filter */
+ if (pdata->has_adv_dig_filtr && dev->enable_dig_filt)
+ filtr |= AT91_TWI_FILTR_FILT |
+ (AT91_TWI_FILTR_THRES(dev->filter_width) &
+ AT91_TWI_FILTR_THRES_MASK);
+
+ /* enable analog filter */
+ if (pdata->has_ana_filtr && dev->enable_ana_filt)
+ filtr |= AT91_TWI_FILTR_PADFEN;
+
+ if (filtr)
+ at91_twi_write(dev, AT91_TWI_FILTR, filtr);
}
/*
* Calculate symmetric clock as stated in datasheet:
* twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
*/
-static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
+static void at91_calc_twi_clock(struct at91_twi_dev *dev)
{
- int ckdiv, cdiv, div, hold = 0;
+ int ckdiv, cdiv, div, hold = 0, filter_width = 0;
struct at91_twi_pdata *pdata = dev->pdata;
int offset = pdata->clk_offset;
int max_ckdiv = pdata->clk_max_div;
- u32 twd_hold_time_ns = 0;
+ struct i2c_timings timings, *t = &timings;
+
+ i2c_parse_fw_timings(dev->dev, t, true);
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
- 2 * twi_clk) - offset);
+ 2 * t->bus_freq_hz) - offset);
ckdiv = fls(div >> 8);
cdiv = div >> ckdiv;
@@ -211,15 +87,12 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
}
if (pdata->has_hold_field) {
- of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
- &twd_hold_time_ns);
-
/*
* hold time = HOLD + 3 x T_peripheral_clock
* Use clk rate in kHz to prevent overflows when computing
* hold.
*/
- hold = DIV_ROUND_UP(twd_hold_time_ns
+ hold = DIV_ROUND_UP(t->sda_hold_ns
* (clk_get_rate(dev->clk) / 1000), 1000000);
hold -= 3;
if (hold < 0)
@@ -232,11 +105,29 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
}
}
+ if (pdata->has_adv_dig_filtr) {
+ /*
+ * filter width = 0 to AT91_TWI_FILTR_THRES_MAX
+ * peripheral clocks
+ */
+ filter_width = DIV_ROUND_UP(t->digital_filter_width_ns
+ * (clk_get_rate(dev->clk) / 1000), 1000000);
+ if (filter_width > AT91_TWI_FILTR_THRES_MAX) {
+ dev_warn(dev->dev,
+ "Filter threshold set to its maximum value (%d instead of %d)\n",
+ AT91_TWI_FILTR_THRES_MAX, filter_width);
+ filter_width = AT91_TWI_FILTR_THRES_MAX;
+ }
+ }
+
dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
| AT91_TWI_CWGR_HOLD(hold);
- dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
- cdiv, ckdiv, hold, twd_hold_time_ns);
+ dev->filter_width = filter_width;
+
+ dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns), filter_width %d (%d ns)\n",
+ cdiv, ckdiv, hold, t->sda_hold_ns, filter_width,
+ t->digital_filter_width_ns);
}
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
@@ -247,9 +138,9 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
if (dma->xfer_in_progress) {
if (dma->direction == DMA_FROM_DEVICE)
- dmaengine_terminate_all(dma->chan_rx);
+ dmaengine_terminate_sync(dma->chan_rx);
else
- dmaengine_terminate_all(dma->chan_tx);
+ dmaengine_terminate_sync(dma->chan_tx);
dma->xfer_in_progress = false;
}
if (dma->buf_mapped) {
@@ -270,9 +161,11 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
writeb_relaxed(*dev->buf, dev->base + AT91_TWI_THR);
/* send stop when last byte has been written */
- if (--dev->buf_len == 0)
+ if (--dev->buf_len == 0) {
if (!dev->use_alt_cmd)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
+ at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_TXRDY);
+ }
dev_dbg(dev->dev, "wrote 0x%x, to go %zu\n", *dev->buf, dev->buf_len);
@@ -631,7 +524,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
*/
dev_dbg(dev->dev, "transfer: %s %zu bytes.\n",
- (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
+ str_read_write(dev->msg->flags & I2C_M_RD), dev->buf_len);
reinit_completion(&dev->cmd_complete);
dev->transfer_status = 0;
@@ -690,9 +583,8 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
} else {
at91_twi_write_next_byte(dev);
at91_twi_write(dev, AT91_TWI_IER,
- AT91_TWI_TXCOMP |
- AT91_TWI_NACK |
- AT91_TWI_TXRDY);
+ AT91_TWI_TXCOMP | AT91_TWI_NACK |
+ (dev->buf_len ? AT91_TWI_TXRDY : 0));
}
}
@@ -700,7 +592,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
dev->adapter.timeout);
if (time_left == 0) {
dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR);
- dev_err(dev->dev, "controller timed out\n");
at91_init_twi_bus(dev);
ret = -ETIMEDOUT;
goto error;
@@ -746,6 +637,14 @@ error:
at91_twi_write(dev, AT91_TWI_CR,
AT91_TWI_THRCLR | AT91_TWI_LOCKCLR);
}
+
+ /*
+ * some faulty I2C slave devices might hold SDA down;
+ * we can send a bus clear command, hoping that the pins will be
+ * released
+ */
+ i2c_recover_bus(&dev->adapter);
+
return ret;
}
@@ -756,6 +655,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
unsigned int_addr_flag = 0;
struct i2c_msg *m_start = msg;
bool is_read;
+ u8 *dma_buf = NULL;
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
@@ -803,11 +703,20 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
dev->msg = m_start;
dev->recv_len_abort = false;
+ if (dev->use_dma) {
+ dma_buf = i2c_get_dma_safe_msg_buf(m_start, 1);
+ if (!dma_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ dev->buf = dma_buf;
+ }
+
ret = at91_do_twi_transfer(dev);
+ i2c_put_dma_safe_msg_buf(dma_buf, m_start, !ret);
ret = (ret < 0) ? ret : num;
out:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
@@ -829,128 +738,10 @@ static u32 at91_twi_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm at91_twi_algorithm = {
- .master_xfer = at91_twi_xfer,
- .functionality = at91_twi_func,
-};
-
-static struct at91_twi_pdata at91rm9200_config = {
- .clk_max_div = 5,
- .clk_offset = 3,
- .has_unre_flag = true,
- .has_alt_cmd = false,
- .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9261_config = {
- .clk_max_div = 5,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9260_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9g20_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9g10_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = false,
+ .xfer = at91_twi_xfer,
+ .functionality = at91_twi_func,
};
-static const struct platform_device_id at91_twi_devtypes[] = {
- {
- .name = "i2c-at91rm9200",
- .driver_data = (unsigned long) &at91rm9200_config,
- }, {
- .name = "i2c-at91sam9261",
- .driver_data = (unsigned long) &at91sam9261_config,
- }, {
- .name = "i2c-at91sam9260",
- .driver_data = (unsigned long) &at91sam9260_config,
- }, {
- .name = "i2c-at91sam9g20",
- .driver_data = (unsigned long) &at91sam9g20_config,
- }, {
- .name = "i2c-at91sam9g10",
- .driver_data = (unsigned long) &at91sam9g10_config,
- }, {
- /* sentinel */
- }
-};
-
-#if defined(CONFIG_OF)
-static struct at91_twi_pdata at91sam9x5_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = false,
-};
-
-static struct at91_twi_pdata sama5d4_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = false,
- .has_alt_cmd = false,
- .has_hold_field = true,
-};
-
-static struct at91_twi_pdata sama5d2_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
- .has_unre_flag = true,
- .has_alt_cmd = true,
- .has_hold_field = true,
-};
-
-static const struct of_device_id atmel_twi_dt_ids[] = {
- {
- .compatible = "atmel,at91rm9200-i2c",
- .data = &at91rm9200_config,
- } , {
- .compatible = "atmel,at91sam9260-i2c",
- .data = &at91sam9260_config,
- } , {
- .compatible = "atmel,at91sam9261-i2c",
- .data = &at91sam9261_config,
- } , {
- .compatible = "atmel,at91sam9g20-i2c",
- .data = &at91sam9g20_config,
- } , {
- .compatible = "atmel,at91sam9g10-i2c",
- .data = &at91sam9g10_config,
- }, {
- .compatible = "atmel,at91sam9x5-i2c",
- .data = &at91sam9x5_config,
- }, {
- .compatible = "atmel,sama5d4-i2c",
- .data = &sama5d4_config,
- }, {
- .compatible = "atmel,sama5d2-i2c",
- .data = &sama5d2_config,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
-#endif
-
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
{
int ret = 0;
@@ -985,14 +776,14 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
slave_config.dst_maxburst = 1;
slave_config.device_fc = false;
- dma->chan_tx = dma_request_slave_channel_reason(dev->dev, "tx");
+ dma->chan_tx = dma_request_chan(dev->dev, "tx");
if (IS_ERR(dma->chan_tx)) {
ret = PTR_ERR(dma->chan_tx);
dma->chan_tx = NULL;
goto error;
}
- dma->chan_rx = dma_request_slave_channel_reason(dev->dev, "rx");
+ dma->chan_rx = dma_request_chan(dev->dev, "rx");
if (IS_ERR(dma->chan_rx)) {
ret = PTR_ERR(dma->chan_rx);
dma->chan_rx = NULL;
@@ -1033,219 +824,98 @@ error:
return ret;
}
-static struct at91_twi_pdata *at91_twi_get_driver_data(
- struct platform_device *pdev)
+static int at91_init_twi_recovery_gpio(struct platform_device *pdev,
+ struct at91_twi_dev *dev)
{
- if (pdev->dev.of_node) {
- const struct of_device_id *match;
- match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
- if (!match)
- return NULL;
- return (struct at91_twi_pdata *)match->data;
- }
- return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
-}
-
-static int at91_twi_probe(struct platform_device *pdev)
-{
- struct at91_twi_dev *dev;
- struct resource *mem;
- int rc;
- u32 phy_addr;
- u32 bus_clk_rate;
-
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- init_completion(&dev->cmd_complete);
- dev->dev = &pdev->dev;
+ struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem)
- return -ENODEV;
- phy_addr = mem->start;
-
- dev->pdata = at91_twi_get_driver_data(pdev);
- if (!dev->pdata)
- return -ENODEV;
-
- dev->base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(dev->base))
- return PTR_ERR(dev->base);
-
- dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq < 0)
- return dev->irq;
-
- rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
- dev_name(dev->dev), dev);
- if (rc) {
- dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
- return rc;
- }
-
- platform_set_drvdata(pdev, dev);
-
- dev->clk = devm_clk_get(dev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- dev_err(dev->dev, "no clock defined\n");
- return -ENODEV;
- }
- rc = clk_prepare_enable(dev->clk);
- if (rc)
- return rc;
-
- if (dev->dev->of_node) {
- rc = at91_twi_configure_dma(dev, phy_addr);
- if (rc == -EPROBE_DEFER) {
- clk_disable_unprepare(dev->clk);
- return rc;
- }
- }
-
- if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
- &dev->fifo_size)) {
- dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
+ rinfo->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!rinfo->pinctrl) {
+ dev_info(dev->dev, "pinctrl unavailable, bus recovery not supported\n");
+ return 0;
}
-
- rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
- &bus_clk_rate);
- if (rc)
- bus_clk_rate = DEFAULT_TWI_CLK_HZ;
-
- at91_calc_twi_clock(dev, bus_clk_rate);
- at91_init_twi_bus(dev);
-
- snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
- i2c_set_adapdata(&dev->adapter, dev);
- dev->adapter.owner = THIS_MODULE;
- dev->adapter.class = I2C_CLASS_DEPRECATED;
- dev->adapter.algo = &at91_twi_algorithm;
- dev->adapter.quirks = &at91_twi_quirks;
- dev->adapter.dev.parent = dev->dev;
- dev->adapter.nr = pdev->id;
- dev->adapter.timeout = AT91_I2C_TIMEOUT;
- dev->adapter.dev.of_node = pdev->dev.of_node;
-
- pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
- pm_runtime_use_autosuspend(dev->dev);
- pm_runtime_set_active(dev->dev);
- pm_runtime_enable(dev->dev);
-
- rc = i2c_add_numbered_adapter(&dev->adapter);
- if (rc) {
- clk_disable_unprepare(dev->clk);
-
- pm_runtime_disable(dev->dev);
- pm_runtime_set_suspended(dev->dev);
-
- return rc;
+ if (IS_ERR(rinfo->pinctrl)) {
+ dev_info(dev->dev, "can't get pinctrl, bus recovery not supported\n");
+ return PTR_ERR(rinfo->pinctrl);
}
+ dev->adapter.bus_recovery_info = rinfo;
- dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
- at91_twi_read(dev, AT91_TWI_VER));
return 0;
}
-static int at91_twi_remove(struct platform_device *pdev)
+static int at91_twi_recover_bus_cmd(struct i2c_adapter *adap)
{
- struct at91_twi_dev *dev = platform_get_drvdata(pdev);
+ struct at91_twi_dev *dev = i2c_get_adapdata(adap);
- i2c_del_adapter(&dev->adapter);
- clk_disable_unprepare(dev->clk);
+ dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR);
+ if (!(dev->transfer_status & AT91_TWI_SDA)) {
+ dev_dbg(dev->dev, "SDA is down; sending bus clear command\n");
+ if (dev->use_alt_cmd) {
+ unsigned int acr;
- pm_runtime_disable(dev->dev);
- pm_runtime_set_suspended(dev->dev);
+ acr = at91_twi_read(dev, AT91_TWI_ACR);
+ acr &= ~AT91_TWI_ACR_DATAL_MASK;
+ at91_twi_write(dev, AT91_TWI_ACR, acr);
+ }
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_CLEAR);
+ }
return 0;
}
-#ifdef CONFIG_PM
-
-static int at91_twi_runtime_suspend(struct device *dev)
+static int at91_init_twi_recovery_info(struct platform_device *pdev,
+ struct at91_twi_dev *dev)
{
- struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+ struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
+ bool has_clear_cmd = dev->pdata->has_clear_cmd;
- clk_disable_unprepare(twi_dev->clk);
+ if (!has_clear_cmd)
+ return at91_init_twi_recovery_gpio(pdev, dev);
- pinctrl_pm_select_sleep_state(dev);
+ rinfo->recover_bus = at91_twi_recover_bus_cmd;
+ dev->adapter.bus_recovery_info = rinfo;
return 0;
}
-static int at91_twi_runtime_resume(struct device *dev)
+int at91_twi_probe_master(struct platform_device *pdev,
+ u32 phy_addr, struct at91_twi_dev *dev)
{
- struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
-
- pinctrl_pm_select_default_state(dev);
-
- return clk_prepare_enable(twi_dev->clk);
-}
-
-static int at91_twi_suspend_noirq(struct device *dev)
-{
- if (!pm_runtime_status_suspended(dev))
- at91_twi_runtime_suspend(dev);
-
- return 0;
-}
+ int rc;
-static int at91_twi_resume_noirq(struct device *dev)
-{
- struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
- int ret;
+ init_completion(&dev->cmd_complete);
- if (!pm_runtime_status_suspended(dev)) {
- ret = at91_twi_runtime_resume(dev);
- if (ret)
- return ret;
+ rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
+ dev_name(dev->dev), dev);
+ if (rc) {
+ dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
+ return rc;
}
- pm_runtime_mark_last_busy(dev);
- pm_request_autosuspend(dev);
+ if (dev->dev->of_node) {
+ rc = at91_twi_configure_dma(dev, phy_addr);
+ if (rc == -EPROBE_DEFER)
+ return rc;
+ }
- at91_init_twi_bus(twi_dev);
+ if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
+ &dev->fifo_size)) {
+ dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
+ }
- return 0;
-}
+ dev->enable_dig_filt = of_property_read_bool(pdev->dev.of_node,
+ "i2c-digital-filter");
-static const struct dev_pm_ops at91_twi_pm = {
- .suspend_noirq = at91_twi_suspend_noirq,
- .resume_noirq = at91_twi_resume_noirq,
- .runtime_suspend = at91_twi_runtime_suspend,
- .runtime_resume = at91_twi_runtime_resume,
-};
+ dev->enable_ana_filt = of_property_read_bool(pdev->dev.of_node,
+ "i2c-analog-filter");
+ at91_calc_twi_clock(dev);
-#define at91_twi_pm_ops (&at91_twi_pm)
-#else
-#define at91_twi_pm_ops NULL
-#endif
-
-static struct platform_driver at91_twi_driver = {
- .probe = at91_twi_probe,
- .remove = at91_twi_remove,
- .id_table = at91_twi_devtypes,
- .driver = {
- .name = "at91_i2c",
- .of_match_table = of_match_ptr(atmel_twi_dt_ids),
- .pm = at91_twi_pm_ops,
- },
-};
+ rc = at91_init_twi_recovery_info(pdev, dev);
+ if (rc == -EPROBE_DEFER)
+ return rc;
-static int __init at91_twi_init(void)
-{
- return platform_driver_register(&at91_twi_driver);
-}
+ dev->adapter.algo = &at91_twi_algorithm;
+ dev->adapter.quirks = &at91_twi_quirks;
-static void __exit at91_twi_exit(void)
-{
- platform_driver_unregister(&at91_twi_driver);
+ return 0;
}
-
-subsys_initcall(at91_twi_init);
-module_exit(at91_twi_exit);
-
-MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
-MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:at91_i2c");
diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c
new file mode 100644
index 000000000000..131a67d9d4a6
--- /dev/null
+++ b/drivers/i2c/busses/i2c-at91-slave.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ * Copyright (C) 2017 Juergen Fitschen <me@jue.yt>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "i2c-at91.h"
+
+static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)
+{
+ struct at91_twi_dev *dev = dev_id;
+ const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
+ const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
+ u8 value;
+
+ if (!irqstatus)
+ return IRQ_NONE;
+
+ /* slave address has been detected on I2C bus */
+ if (irqstatus & AT91_TWI_SVACC) {
+ if (status & AT91_TWI_SVREAD) {
+ i2c_slave_event(dev->slave,
+ I2C_SLAVE_READ_REQUESTED, &value);
+ writeb_relaxed(value, dev->base + AT91_TWI_THR);
+ at91_twi_write(dev, AT91_TWI_IER,
+ AT91_TWI_TXRDY | AT91_TWI_EOSACC);
+ } else {
+ i2c_slave_event(dev->slave,
+ I2C_SLAVE_WRITE_REQUESTED, &value);
+ at91_twi_write(dev, AT91_TWI_IER,
+ AT91_TWI_RXRDY | AT91_TWI_EOSACC);
+ }
+ at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
+ }
+
+ /* byte transmitted to remote master */
+ if (irqstatus & AT91_TWI_TXRDY) {
+ i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
+ writeb_relaxed(value, dev->base + AT91_TWI_THR);
+ }
+
+ /* byte received from remote master */
+ if (irqstatus & AT91_TWI_RXRDY) {
+ value = readb_relaxed(dev->base + AT91_TWI_RHR);
+ i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+
+ /* master sent stop */
+ if (irqstatus & AT91_TWI_EOSACC) {
+ at91_twi_write(dev, AT91_TWI_IDR,
+ AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
+ i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int at91_reg_slave(struct i2c_client *slave)
+{
+ struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
+
+ if (dev->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ /* Make sure twi_clk doesn't get turned off! */
+ pm_runtime_get_sync(dev->dev);
+
+ dev->slave = slave;
+ dev->smr = AT91_TWI_SMR_SADR(slave->addr);
+
+ at91_init_twi_bus(dev);
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
+
+ dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
+
+ return 0;
+}
+
+static int at91_unreg_slave(struct i2c_client *slave)
+{
+ struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
+
+ WARN_ON(!dev->slave);
+
+ dev_info(dev->dev, "leaving slave mode\n");
+
+ dev->slave = NULL;
+ dev->smr = 0;
+
+ at91_init_twi_bus(dev);
+
+ pm_runtime_put(dev->dev);
+
+ return 0;
+}
+
+static u32 at91_twi_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SLAVE;
+}
+
+static const struct i2c_algorithm at91_twi_algorithm_slave = {
+ .reg_slave = at91_reg_slave,
+ .unreg_slave = at91_unreg_slave,
+ .functionality = at91_twi_func,
+};
+
+int at91_twi_probe_slave(struct platform_device *pdev,
+ u32 phy_addr, struct at91_twi_dev *dev)
+{
+ int rc;
+
+ rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
+ 0, dev_name(dev->dev), dev);
+ if (rc) {
+ dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
+ return rc;
+ }
+
+ dev->adapter.algo = &at91_twi_algorithm_slave;
+
+ return 0;
+}
+
+void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
+{
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
+ if (dev->slave_detected && dev->smr) {
+ at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
+ }
+}
diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h
new file mode 100644
index 000000000000..942e9c3973bb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-at91.h
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ * Copyright (C) 2011 Weinmann Medical GmbH
+ * Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ * Evolved from original work by:
+ * Copyright (C) 2004 Rick Bronson
+ * Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+ *
+ * Borrowed heavily from original work by:
+ * Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+
+#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
+#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
+#define AUTOSUSPEND_TIMEOUT 2000
+#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
+
+/* AT91 TWI register definitions */
+#define AT91_TWI_CR 0x0000 /* Control Register */
+#define AT91_TWI_START BIT(0) /* Send a Start Condition */
+#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
+#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
+#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
+#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
+#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
+#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
+#define AT91_TWI_SWRST BIT(7) /* Software Reset */
+#define AT91_TWI_CLEAR BIT(15) /* Bus clear command */
+#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
+#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
+#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
+#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
+#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
+#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
+#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
+
+#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
+#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
+#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
+
+#define AT91_TWI_SMR 0x0008 /* Slave Mode Register */
+#define AT91_TWI_SMR_SADR_MAX 0x007f
+#define AT91_TWI_SMR_SADR(x) (((x) & AT91_TWI_SMR_SADR_MAX) << 16)
+
+#define AT91_TWI_IADR 0x000c /* Internal Address Register */
+
+#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
+#define AT91_TWI_CWGR_HOLD_MAX 0x1f
+#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
+
+#define AT91_TWI_SR 0x0020 /* Status Register */
+#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
+#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
+#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
+#define AT91_TWI_SVREAD BIT(3) /* Slave Read */
+#define AT91_TWI_SVACC BIT(4) /* Slave Access */
+#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
+#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
+#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
+#define AT91_TWI_EOSACC BIT(11) /* End Of Slave Access */
+#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
+#define AT91_TWI_SCL BIT(24) /* TWI SCL status */
+#define AT91_TWI_SDA BIT(25) /* TWI SDA status */
+
+#define AT91_TWI_INT_MASK \
+ (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \
+ | AT91_TWI_SVACC | AT91_TWI_EOSACC)
+
+#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
+#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
+#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
+#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
+#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
+
+#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
+#define AT91_TWI_ACR_DATAL_MASK GENMASK(15, 0)
+#define AT91_TWI_ACR_DATAL(len) ((len) & AT91_TWI_ACR_DATAL_MASK)
+#define AT91_TWI_ACR_DIR BIT(8)
+
+#define AT91_TWI_FILTR 0x0044
+#define AT91_TWI_FILTR_FILT BIT(0)
+#define AT91_TWI_FILTR_PADFEN BIT(1)
+#define AT91_TWI_FILTR_THRES(v) ((v) << 8)
+#define AT91_TWI_FILTR_THRES_MAX 7
+#define AT91_TWI_FILTR_THRES_MASK GENMASK(10, 8)
+
+#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
+#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
+#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
+#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
+#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
+#define AT91_TWI_ONE_DATA 0x0
+#define AT91_TWI_TWO_DATA 0x1
+#define AT91_TWI_FOUR_DATA 0x2
+
+#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
+
+#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
+#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
+#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
+#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
+
+#define AT91_TWI_VER 0x00fc /* Version Register */
+
+struct at91_twi_pdata {
+ unsigned clk_max_div;
+ unsigned clk_offset;
+ bool has_unre_flag;
+ bool has_alt_cmd;
+ bool has_hold_field;
+ bool has_dig_filtr;
+ bool has_adv_dig_filtr;
+ bool has_ana_filtr;
+ bool has_clear_cmd;
+};
+
+struct at91_twi_dma {
+ struct dma_chan *chan_rx;
+ struct dma_chan *chan_tx;
+ struct scatterlist sg[2];
+ struct dma_async_tx_descriptor *data_desc;
+ enum dma_data_direction direction;
+ bool buf_mapped;
+ bool xfer_in_progress;
+};
+
+struct at91_twi_dev {
+ struct device *dev;
+ void __iomem *base;
+ struct completion cmd_complete;
+ struct clk *clk;
+ u8 *buf;
+ size_t buf_len;
+ struct i2c_msg *msg;
+ int irq;
+ unsigned imr;
+ unsigned transfer_status;
+ struct i2c_adapter adapter;
+ unsigned twi_cwgr_reg;
+ struct at91_twi_pdata *pdata;
+ bool use_dma;
+ bool use_alt_cmd;
+ bool recv_len_abort;
+ u32 fifo_size;
+ struct at91_twi_dma dma;
+ bool slave_detected;
+ struct i2c_bus_recovery_info rinfo;
+#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
+ unsigned smr;
+ struct i2c_client *slave;
+#endif
+ bool enable_dig_filt;
+ bool enable_ana_filt;
+ u32 filter_width;
+};
+
+unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
+void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val);
+void at91_disable_twi_interrupts(struct at91_twi_dev *dev);
+void at91_twi_irq_save(struct at91_twi_dev *dev);
+void at91_twi_irq_restore(struct at91_twi_dev *dev);
+void at91_init_twi_bus(struct at91_twi_dev *dev);
+
+void at91_init_twi_bus_master(struct at91_twi_dev *dev);
+int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
+ struct at91_twi_dev *dev);
+
+#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
+void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
+int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
+ struct at91_twi_dev *dev);
+
+#else
+static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
+static inline int at91_twi_probe_slave(struct platform_device *pdev,
+ u32 phy_addr, struct at91_twi_dev *dev)
+{
+ return -EINVAL;
+}
+
+#endif
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index 5bcb1f0bb334..b78b38ddac46 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface
* Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
@@ -11,16 +12,6 @@
* This is just a skeleton adapter to use with the Au1550 PSC
* algorithm. It was developed for the Pb1550, but will work with
* any Au1550 board that has a similar PSC configuration.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/delay.h>
@@ -90,11 +81,10 @@ static int wait_ack(struct i2c_au1550_data *adap)
return 0;
}
-static int wait_master_done(struct i2c_au1550_data *adap)
+static int wait_controller_done(struct i2c_au1550_data *adap)
{
int i;
- /* Wait for Master Done. */
for (i = 0; i < 2 * adap->xfer_timeout; i++) {
if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0)
return 0;
@@ -129,12 +119,12 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
if (q)
addr |= PSC_SMBTXRX_STP;
- /* Put byte into fifo, start up master. */
+ /* Put byte into fifo, start up controller */
WR(adap, PSC_SMBTXRX, addr);
WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS);
if (wait_ack(adap))
return -EIO;
- return (q) ? wait_master_done(adap) : 0;
+ return (q) ? wait_controller_done(adap) : 0;
}
static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out)
@@ -184,7 +174,7 @@ static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
/* The last byte has to indicate transfer done. */
WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP);
- if (wait_master_done(adap))
+ if (wait_controller_done(adap))
return -EIO;
buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff);
@@ -213,7 +203,7 @@ static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
data = buf[i];
data |= PSC_SMBTXRX_STP;
WR(adap, PSC_SMBTXRX, data);
- if (wait_master_done(adap))
+ if (wait_controller_done(adap))
return -EIO;
return 0;
}
@@ -255,8 +245,8 @@ static u32 au1550_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm au1550_algo = {
- .master_xfer = au1550_xfer,
- .functionality = au1550_func,
+ .xfer = au1550_xfer,
+ .functionality = au1550_func,
};
static void i2c_au1550_setup(struct i2c_au1550_data *priv)
@@ -311,7 +301,6 @@ static int
i2c_au1550_probe(struct platform_device *pdev)
{
struct i2c_au1550_data *priv;
- struct resource *r;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data),
@@ -319,8 +308,7 @@ i2c_au1550_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->psc_base = devm_ioremap_resource(&pdev->dev, r);
+ priv->psc_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(priv->psc_base))
return PTR_ERR(priv->psc_base);
@@ -330,7 +318,7 @@ i2c_au1550_probe(struct platform_device *pdev)
priv->adap.algo = &au1550_algo;
priv->adap.algo_data = priv;
priv->adap.dev.parent = &pdev->dev;
- strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name));
+ strscpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name));
/* Now, set up the PSC for SMBus PIO mode. */
i2c_au1550_setup(priv);
@@ -345,16 +333,14 @@ i2c_au1550_probe(struct platform_device *pdev)
return 0;
}
-static int i2c_au1550_remove(struct platform_device *pdev)
+static void i2c_au1550_remove(struct platform_device *pdev)
{
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
i2c_au1550_disable(priv);
- return 0;
}
-#ifdef CONFIG_PM
static int i2c_au1550_suspend(struct device *dev)
{
struct i2c_au1550_data *priv = dev_get_drvdata(dev);
@@ -373,21 +359,13 @@ static int i2c_au1550_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops i2c_au1550_pmops = {
- .suspend = i2c_au1550_suspend,
- .resume = i2c_au1550_resume,
-};
-
-#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops)
-
-#else
-#define AU1XPSC_SMBUS_PMOPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(i2c_au1550_pmops,
+ i2c_au1550_suspend, i2c_au1550_resume);
static struct platform_driver au1xpsc_smbus_driver = {
.driver = {
.name = "au1xpsc_smbus",
- .pm = AU1XPSC_SMBUS_PMOPS,
+ .pm = pm_sleep_ptr(&i2c_au1550_pmops),
},
.probe = i2c_au1550_probe,
.remove = i2c_au1550_remove,
diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c
index bf564391091f..0555eeb6903a 100644
--- a/drivers/i2c/busses/i2c-axxia.c
+++ b/drivers/i2c/busses/i2c-axxia.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This driver implements I2C master functionality using the LSI API2C
* controller.
@@ -5,10 +6,6 @@
* NOTE: The controller has a limitation in that it can only do transfers of
* maximum 255 bytes at a time. If a larger transfer is attempted, error code
* (-EINVAL) is returned.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
@@ -80,6 +77,40 @@
MST_STATUS_IP)
#define MST_TX_BYTES_XFRD 0x50
#define MST_RX_BYTES_XFRD 0x54
+#define SLV_ADDR_DEC_CTL 0x58
+#define SLV_ADDR_DEC_GCE BIT(0) /* ACK to General Call Address from own master (loopback) */
+#define SLV_ADDR_DEC_OGCE BIT(1) /* ACK to General Call Address from external masters */
+#define SLV_ADDR_DEC_SA1E BIT(2) /* ACK to addr_1 enabled */
+#define SLV_ADDR_DEC_SA1M BIT(3) /* 10-bit addressing for addr_1 enabled */
+#define SLV_ADDR_DEC_SA2E BIT(4) /* ACK to addr_2 enabled */
+#define SLV_ADDR_DEC_SA2M BIT(5) /* 10-bit addressing for addr_2 enabled */
+#define SLV_ADDR_1 0x5c
+#define SLV_ADDR_2 0x60
+#define SLV_RX_CTL 0x64
+#define SLV_RX_ACSA1 BIT(0) /* Generate ACK for writes to addr_1 */
+#define SLV_RX_ACSA2 BIT(1) /* Generate ACK for writes to addr_2 */
+#define SLV_RX_ACGCA BIT(2) /* ACK data phase transfers to General Call Address */
+#define SLV_DATA 0x68
+#define SLV_RX_FIFO 0x6c
+#define SLV_FIFO_DV1 BIT(0) /* Data Valid for addr_1 */
+#define SLV_FIFO_DV2 BIT(1) /* Data Valid for addr_2 */
+#define SLV_FIFO_AS BIT(2) /* (N)ACK Sent */
+#define SLV_FIFO_TNAK BIT(3) /* Timeout NACK */
+#define SLV_FIFO_STRC BIT(4) /* First byte after start condition received */
+#define SLV_FIFO_RSC BIT(5) /* Repeated Start Condition */
+#define SLV_FIFO_STPC BIT(6) /* Stop Condition */
+#define SLV_FIFO_DV (SLV_FIFO_DV1 | SLV_FIFO_DV2)
+#define SLV_INT_ENABLE 0x70
+#define SLV_INT_STATUS 0x74
+#define SLV_STATUS_RFH BIT(0) /* FIFO service */
+#define SLV_STATUS_WTC BIT(1) /* Write transfer complete */
+#define SLV_STATUS_SRS1 BIT(2) /* Slave read from addr 1 */
+#define SLV_STATUS_SRRS1 BIT(3) /* Repeated start from addr 1 */
+#define SLV_STATUS_SRND1 BIT(4) /* Read request not following start condition */
+#define SLV_STATUS_SRC1 BIT(5) /* Read canceled */
+#define SLV_STATUS_SRAT1 BIT(6) /* Slave Read timed out */
+#define SLV_STATUS_SRDRE1 BIT(7) /* Data written after timed out */
+#define SLV_READ_DUMMY 0x78
#define SCL_HIGH_PERIOD 0x80
#define SCL_LOW_PERIOD 0x84
#define SPIKE_FLTR_LEN 0x88
@@ -87,7 +118,7 @@
#define SDA_HOLD_TIME 0x90
/**
- * axxia_i2c_dev - I2C device context
+ * struct axxia_i2c_dev - I2C device context
* @base: pointer to register struct
* @msg: pointer to current message
* @msg_r: pointer to current read message (sequence transfer)
@@ -99,6 +130,9 @@
* @adapter: core i2c abstraction
* @i2c_clk: clock reference for i2c input clock
* @bus_clk_rate: current i2c bus clock rate
+ * @last: a flag indicating is this is last message in transfer
+ * @slave: associated &i2c_client
+ * @irq: platform device IRQ number
*/
struct axxia_i2c_dev {
void __iomem *base;
@@ -112,6 +146,9 @@ struct axxia_i2c_dev {
struct i2c_adapter adapter;
struct clk *i2c_clk;
u32 bus_clk_rate;
+ bool last;
+ struct i2c_client *slave;
+ int irq;
};
static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
@@ -130,7 +167,7 @@ static void i2c_int_enable(struct axxia_i2c_dev *idev, u32 mask)
writel(int_en | mask, idev->base + MST_INT_ENABLE);
}
-/**
+/*
* ns_to_clk - Convert time (ns) to clock cycles for the given clock frequency.
*/
static u32 ns_to_clk(u64 ns, u32 clk_mhz)
@@ -164,7 +201,7 @@ static int axxia_i2c_init(struct axxia_i2c_dev *idev)
/* Enable Master Mode */
writel(0x1, idev->base + GLOBAL_CONTROL);
- if (idev->bus_clk_rate <= 100000) {
+ if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) {
/* Standard mode SCL 50/50, tSU:DAT = 250 ns */
t_high = divisor * 1 / 2;
t_low = divisor * 1 / 2;
@@ -218,17 +255,12 @@ static int i2c_m_rd(const struct i2c_msg *msg)
return (msg->flags & I2C_M_RD) != 0;
}
-static int i2c_m_ten(const struct i2c_msg *msg)
-{
- return (msg->flags & I2C_M_TEN) != 0;
-}
-
static int i2c_m_recv_len(const struct i2c_msg *msg)
{
return (msg->flags & I2C_M_RECV_LEN) != 0;
}
-/**
+/*
* axxia_i2c_empty_rx_fifo - Fetch data from RX FIFO and update SMBus block
* transfer length if this is the first byte of such a transfer.
*/
@@ -260,7 +292,7 @@ static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev)
return 0;
}
-/**
+/*
* axxia_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer.
* @return: Number of bytes left to transfer.
*/
@@ -277,13 +309,65 @@ static int axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev)
return ret;
}
+static void axxia_i2c_slv_fifo_event(struct axxia_i2c_dev *idev)
+{
+ u32 fifo_status = readl(idev->base + SLV_RX_FIFO);
+ u8 val;
+
+ dev_dbg(idev->dev, "slave irq fifo_status=0x%x\n", fifo_status);
+
+ if (fifo_status & SLV_FIFO_DV1) {
+ if (fifo_status & SLV_FIFO_STRC)
+ i2c_slave_event(idev->slave,
+ I2C_SLAVE_WRITE_REQUESTED, &val);
+
+ val = readl(idev->base + SLV_DATA);
+ i2c_slave_event(idev->slave, I2C_SLAVE_WRITE_RECEIVED, &val);
+ }
+ if (fifo_status & SLV_FIFO_STPC) {
+ readl(idev->base + SLV_DATA); /* dummy read */
+ i2c_slave_event(idev->slave, I2C_SLAVE_STOP, &val);
+ }
+ if (fifo_status & SLV_FIFO_RSC)
+ readl(idev->base + SLV_DATA); /* dummy read */
+}
+
+static irqreturn_t axxia_i2c_slv_isr(struct axxia_i2c_dev *idev)
+{
+ u32 status = readl(idev->base + SLV_INT_STATUS);
+ u8 val;
+
+ dev_dbg(idev->dev, "slave irq status=0x%x\n", status);
+
+ if (status & SLV_STATUS_RFH)
+ axxia_i2c_slv_fifo_event(idev);
+ if (status & SLV_STATUS_SRS1) {
+ i2c_slave_event(idev->slave, I2C_SLAVE_READ_REQUESTED, &val);
+ writel(val, idev->base + SLV_DATA);
+ }
+ if (status & SLV_STATUS_SRND1) {
+ i2c_slave_event(idev->slave, I2C_SLAVE_READ_PROCESSED, &val);
+ writel(val, idev->base + SLV_DATA);
+ }
+ if (status & SLV_STATUS_SRC1)
+ i2c_slave_event(idev->slave, I2C_SLAVE_STOP, &val);
+
+ writel(INT_SLV, idev->base + INTERRUPT_STATUS);
+ return IRQ_HANDLED;
+}
+
static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
{
struct axxia_i2c_dev *idev = _dev;
+ irqreturn_t ret = IRQ_NONE;
u32 status;
- if (!(readl(idev->base + INTERRUPT_STATUS) & INT_MST))
- return IRQ_NONE;
+ status = readl(idev->base + INTERRUPT_STATUS);
+
+ if (status & INT_SLV)
+ ret = axxia_i2c_slv_isr(idev);
+ if (!(status & INT_MST))
+ return ret;
/* Read interrupt status bits */
status = readl(idev->base + MST_INT_STATUS);
@@ -324,15 +408,14 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
/* Stop completed */
i2c_int_disable(idev, ~MST_STATUS_TSS);
complete(&idev->msg_complete);
- } else if (status & MST_STATUS_SNS) {
+ } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) {
/* Transfer done */
- i2c_int_disable(idev, ~MST_STATUS_TSS);
+ int mask = idev->last ? ~0 : ~MST_STATUS_TSS;
+
+ i2c_int_disable(idev, mask);
if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len)
axxia_i2c_empty_rx_fifo(idev);
complete(&idev->msg_complete);
- } else if (status & MST_STATUS_SS) {
- /* Auto/Sequence transfer done */
- complete(&idev->msg_complete);
} else if (status & MST_STATUS_TSS) {
/* Transfer timeout */
idev->msg_err = -ETIMEDOUT;
@@ -351,20 +434,10 @@ static void axxia_i2c_set_addr(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
{
u32 addr_1, addr_2;
- if (i2c_m_ten(msg)) {
- /* 10-bit address
- * addr_1: 5'b11110 | addr[9:8] | (R/nW)
- * addr_2: addr[7:0]
- */
- addr_1 = 0xF0 | ((msg->addr >> 7) & 0x06);
- if (i2c_m_rd(msg))
- addr_1 |= 1; /* Set the R/nW bit of the address */
- addr_2 = msg->addr & 0xFF;
+ if (msg->flags & I2C_M_TEN) {
+ addr_1 = i2c_10bit_addr_hi_from_msg(msg);
+ addr_2 = i2c_10bit_addr_lo_from_msg(msg);
} else {
- /* 7-bit address
- * addr_1: addr[6:0] | (R/nW)
- * addr_2: dont care
- */
addr_1 = i2c_8bit_addr_from_msg(msg);
addr_2 = 0;
}
@@ -405,6 +478,7 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
idev->msg_r = &msgs[1];
idev->msg_xfrd = 0;
idev->msg_xfrd_r = 0;
+ idev->last = true;
axxia_i2c_fill_tx_fifo(idev);
writel(CMD_SEQUENCE, idev->base + MST_COMMAND);
@@ -415,10 +489,6 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
time_left = wait_for_completion_timeout(&idev->msg_complete,
I2C_XFER_TIMEOUT);
- i2c_int_disable(idev, int_mask);
-
- axxia_i2c_empty_rx_fifo(idev);
-
if (idev->msg_err == -ENXIO) {
if (axxia_i2c_handle_seq_nak(idev))
axxia_i2c_init(idev);
@@ -438,9 +508,10 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
return idev->msg_err;
}
-static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
+static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg,
+ bool last)
{
- u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS;
+ u32 int_mask = MST_STATUS_ERR;
u32 rx_xfer, tx_xfer;
unsigned long time_left;
unsigned int wt_value;
@@ -449,6 +520,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
idev->msg_r = msg;
idev->msg_xfrd = 0;
idev->msg_xfrd_r = 0;
+ idev->last = last;
reinit_completion(&idev->msg_complete);
axxia_i2c_set_addr(idev, msg);
@@ -478,8 +550,13 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
if (idev->msg_err)
goto out;
- /* Start manual mode */
- writel(CMD_MANUAL, idev->base + MST_COMMAND);
+ if (!last) {
+ writel(CMD_MANUAL, idev->base + MST_COMMAND);
+ int_mask |= MST_STATUS_SNS;
+ } else {
+ writel(CMD_AUTO, idev->base + MST_COMMAND);
+ int_mask |= MST_STATUS_SS;
+ }
writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
@@ -507,28 +584,6 @@ out:
return idev->msg_err;
}
-static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
-{
- u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS;
- unsigned long time_left;
-
- reinit_completion(&idev->msg_complete);
-
- /* Issue stop */
- writel(0xb, idev->base + MST_COMMAND);
- i2c_int_enable(idev, int_mask);
- time_left = wait_for_completion_timeout(&idev->msg_complete,
- I2C_STOP_TIMEOUT);
- i2c_int_disable(idev, int_mask);
- if (time_left == 0)
- return -ETIMEDOUT;
-
- if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
- dev_warn(idev->dev, "busy after stop\n");
-
- return 0;
-}
-
/* This function checks if the msgs[] array contains messages compatible with
* Sequence mode of operation. This mode assumes there will be exactly one
* write of non-zero length followed by exactly one read of non-zero length,
@@ -558,9 +613,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
i2c_int_enable(idev, MST_STATUS_TSS);
for (i = 0; ret == 0 && i < num; ++i)
- ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
-
- axxia_i2c_stop(idev);
+ ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1));
return ret ? : i;
}
@@ -605,9 +658,58 @@ static u32 axxia_i2c_func(struct i2c_adapter *adap)
return caps;
}
+static int axxia_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct axxia_i2c_dev *idev = i2c_get_adapdata(slave->adapter);
+ u32 slv_int_mask = SLV_STATUS_RFH;
+ u32 dec_ctl;
+
+ if (idev->slave)
+ return -EBUSY;
+
+ idev->slave = slave;
+
+ /* Enable slave mode as well */
+ writel(GLOBAL_MST_EN | GLOBAL_SLV_EN, idev->base + GLOBAL_CONTROL);
+ writel(INT_MST | INT_SLV, idev->base + INTERRUPT_ENABLE);
+
+ /* Set slave address */
+ dec_ctl = SLV_ADDR_DEC_SA1E;
+ if (slave->flags & I2C_CLIENT_TEN)
+ dec_ctl |= SLV_ADDR_DEC_SA1M;
+
+ writel(SLV_RX_ACSA1, idev->base + SLV_RX_CTL);
+ writel(dec_ctl, idev->base + SLV_ADDR_DEC_CTL);
+ writel(slave->addr, idev->base + SLV_ADDR_1);
+
+ /* Enable interrupts */
+ slv_int_mask |= SLV_STATUS_SRS1 | SLV_STATUS_SRRS1 | SLV_STATUS_SRND1;
+ slv_int_mask |= SLV_STATUS_SRC1;
+ writel(slv_int_mask, idev->base + SLV_INT_ENABLE);
+
+ return 0;
+}
+
+static int axxia_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct axxia_i2c_dev *idev = i2c_get_adapdata(slave->adapter);
+
+ /* Disable slave mode */
+ writel(GLOBAL_MST_EN, idev->base + GLOBAL_CONTROL);
+ writel(INT_MST, idev->base + INTERRUPT_ENABLE);
+
+ synchronize_irq(idev->irq);
+
+ idev->slave = NULL;
+
+ return 0;
+}
+
static const struct i2c_algorithm axxia_i2c_algo = {
- .master_xfer = axxia_i2c_xfer,
+ .xfer = axxia_i2c_xfer,
.functionality = axxia_i2c_func,
+ .reg_slave = axxia_i2c_reg_slave,
+ .unreg_slave = axxia_i2c_unreg_slave,
};
static const struct i2c_adapter_quirks axxia_i2c_quirks = {
@@ -619,25 +721,20 @@ static int axxia_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct axxia_i2c_dev *idev = NULL;
- struct resource *res;
void __iomem *base;
- int irq;
int ret = 0;
idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
if (!idev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "missing interrupt resource\n");
- return irq;
- }
+ idev->irq = platform_get_irq(pdev, 0);
+ if (idev->irq < 0)
+ return idev->irq;
idev->i2c_clk = devm_clk_get(&pdev->dev, "i2c");
if (IS_ERR(idev->i2c_clk)) {
@@ -651,7 +748,7 @@ static int axxia_i2c_probe(struct platform_device *pdev)
of_property_read_u32(np, "clock-frequency", &idev->bus_clk_rate);
if (idev->bus_clk_rate == 0)
- idev->bus_clk_rate = 100000; /* default clock rate */
+ idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */
ret = clk_prepare_enable(idev->i2c_clk);
if (ret) {
@@ -665,15 +762,15 @@ static int axxia_i2c_probe(struct platform_device *pdev)
goto error_disable_clk;
}
- ret = devm_request_irq(&pdev->dev, irq, axxia_i2c_isr, 0,
+ ret = devm_request_irq(&pdev->dev, idev->irq, axxia_i2c_isr, 0,
pdev->name, idev);
if (ret) {
- dev_err(&pdev->dev, "failed to claim IRQ%d\n", irq);
+ dev_err(&pdev->dev, "failed to claim IRQ%d\n", idev->irq);
goto error_disable_clk;
}
i2c_set_adapdata(&idev->adapter, idev);
- strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
+ strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
idev->adapter.owner = THIS_MODULE;
idev->adapter.algo = &axxia_i2c_algo;
idev->adapter.bus_recovery_info = &axxia_i2c_recovery_info;
@@ -694,14 +791,12 @@ error_disable_clk:
return ret;
}
-static int axxia_i2c_remove(struct platform_device *pdev)
+static void axxia_i2c_remove(struct platform_device *pdev)
{
struct axxia_i2c_dev *idev = platform_get_drvdata(pdev);
clk_disable_unprepare(idev->i2c_clk);
i2c_del_adapter(&idev->adapter);
-
- return 0;
}
/* Match table for of_platform binding */
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index 4c8c3bc4669c..e418a4f23f15 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2014 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014 Broadcom Corporation
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -17,17 +7,38 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#define IDM_CTRL_DIRECT_OFFSET 0x00
#define CFG_OFFSET 0x00
#define CFG_RESET_SHIFT 31
#define CFG_EN_SHIFT 30
+#define CFG_SLAVE_ADDR_0_SHIFT 28
#define CFG_M_RETRY_CNT_SHIFT 16
#define CFG_M_RETRY_CNT_MASK 0x0f
#define TIM_CFG_OFFSET 0x04
#define TIM_CFG_MODE_400_SHIFT 31
+#define TIM_RAND_SLAVE_STRETCH_SHIFT 24
+#define TIM_RAND_SLAVE_STRETCH_MASK 0x7f
+#define TIM_PERIODIC_SLAVE_STRETCH_SHIFT 16
+#define TIM_PERIODIC_SLAVE_STRETCH_MASK 0x7f
+
+#define S_CFG_SMBUS_ADDR_OFFSET 0x08
+#define S_CFG_EN_NIC_SMB_ADDR3_SHIFT 31
+#define S_CFG_NIC_SMB_ADDR3_SHIFT 24
+#define S_CFG_NIC_SMB_ADDR3_MASK 0x7f
+#define S_CFG_EN_NIC_SMB_ADDR2_SHIFT 23
+#define S_CFG_NIC_SMB_ADDR2_SHIFT 16
+#define S_CFG_NIC_SMB_ADDR2_MASK 0x7f
+#define S_CFG_EN_NIC_SMB_ADDR1_SHIFT 15
+#define S_CFG_NIC_SMB_ADDR1_SHIFT 8
+#define S_CFG_NIC_SMB_ADDR1_MASK 0x7f
+#define S_CFG_EN_NIC_SMB_ADDR0_SHIFT 7
+#define S_CFG_NIC_SMB_ADDR0_SHIFT 0
+#define S_CFG_NIC_SMB_ADDR0_MASK 0x7f
#define M_FIFO_CTRL_OFFSET 0x0c
#define M_FIFO_RX_FLUSH_SHIFT 31
@@ -37,6 +48,14 @@
#define M_FIFO_RX_THLD_SHIFT 8
#define M_FIFO_RX_THLD_MASK 0x3f
+#define S_FIFO_CTRL_OFFSET 0x10
+#define S_FIFO_RX_FLUSH_SHIFT 31
+#define S_FIFO_TX_FLUSH_SHIFT 30
+#define S_FIFO_RX_CNT_SHIFT 16
+#define S_FIFO_RX_CNT_MASK 0x7f
+#define S_FIFO_RX_THLD_SHIFT 8
+#define S_FIFO_RX_THLD_MASK 0x3f
+
#define M_CMD_OFFSET 0x30
#define M_CMD_START_BUSY_SHIFT 31
#define M_CMD_STATUS_SHIFT 25
@@ -46,25 +65,49 @@
#define M_CMD_STATUS_NACK_ADDR 0x2
#define M_CMD_STATUS_NACK_DATA 0x3
#define M_CMD_STATUS_TIMEOUT 0x4
+#define M_CMD_STATUS_FIFO_UNDERRUN 0x5
+#define M_CMD_STATUS_RX_FIFO_FULL 0x6
#define M_CMD_PROTOCOL_SHIFT 9
#define M_CMD_PROTOCOL_MASK 0xf
+#define M_CMD_PROTOCOL_QUICK 0x0
#define M_CMD_PROTOCOL_BLK_WR 0x7
#define M_CMD_PROTOCOL_BLK_RD 0x8
+#define M_CMD_PROTOCOL_PROCESS 0xa
#define M_CMD_PEC_SHIFT 8
#define M_CMD_RD_CNT_SHIFT 0
#define M_CMD_RD_CNT_MASK 0xff
+#define S_CMD_OFFSET 0x34
+#define S_CMD_START_BUSY_SHIFT 31
+#define S_CMD_STATUS_SHIFT 23
+#define S_CMD_STATUS_MASK 0x07
+#define S_CMD_STATUS_SUCCESS 0x0
+#define S_CMD_STATUS_TIMEOUT 0x5
+#define S_CMD_STATUS_MASTER_ABORT 0x7
+
#define IE_OFFSET 0x38
#define IE_M_RX_FIFO_FULL_SHIFT 31
#define IE_M_RX_THLD_SHIFT 30
#define IE_M_START_BUSY_SHIFT 28
#define IE_M_TX_UNDERRUN_SHIFT 27
+#define IE_S_RX_FIFO_FULL_SHIFT 26
+#define IE_S_RX_THLD_SHIFT 25
+#define IE_S_RX_EVENT_SHIFT 24
+#define IE_S_START_BUSY_SHIFT 23
+#define IE_S_TX_UNDERRUN_SHIFT 22
+#define IE_S_RD_EVENT_SHIFT 21
#define IS_OFFSET 0x3c
#define IS_M_RX_FIFO_FULL_SHIFT 31
#define IS_M_RX_THLD_SHIFT 30
#define IS_M_START_BUSY_SHIFT 28
#define IS_M_TX_UNDERRUN_SHIFT 27
+#define IS_S_RX_FIFO_FULL_SHIFT 26
+#define IS_S_RX_THLD_SHIFT 25
+#define IS_S_RX_EVENT_SHIFT 24
+#define IS_S_START_BUSY_SHIFT 23
+#define IS_S_TX_UNDERRUN_SHIFT 22
+#define IS_S_RD_EVENT_SHIFT 21
#define M_TX_OFFSET 0x40
#define M_TX_WR_STATUS_SHIFT 31
@@ -78,19 +121,70 @@
#define M_RX_DATA_SHIFT 0
#define M_RX_DATA_MASK 0xff
+#define S_TX_OFFSET 0x48
+#define S_TX_WR_STATUS_SHIFT 31
+#define S_TX_DATA_SHIFT 0
+#define S_TX_DATA_MASK 0xff
+
+#define S_RX_OFFSET 0x4c
+#define S_RX_STATUS_SHIFT 30
+#define S_RX_STATUS_MASK 0x03
+#define S_RX_PEC_ERR_SHIFT 29
+#define S_RX_DATA_SHIFT 0
+#define S_RX_DATA_MASK 0xff
+
#define I2C_TIMEOUT_MSEC 50000
#define M_TX_RX_FIFO_SIZE 64
+#define M_RX_FIFO_MAX_THLD_VALUE (M_TX_RX_FIFO_SIZE - 1)
+
+#define M_RX_MAX_READ_LEN 255
+#define M_RX_FIFO_THLD_VALUE 50
+
+#define IE_M_ALL_INTERRUPT_SHIFT 27
+#define IE_M_ALL_INTERRUPT_MASK 0x1e
+
+#define SLAVE_READ_WRITE_BIT_MASK 0x1
+#define SLAVE_READ_WRITE_BIT_SHIFT 0x1
+#define SLAVE_MAX_SIZE_TRANSACTION 64
+#define SLAVE_CLOCK_STRETCH_TIME 25
+
+#define IE_S_ALL_INTERRUPT_SHIFT 21
+#define IE_S_ALL_INTERRUPT_MASK 0x3f
+/*
+ * It takes ~18us to reading 10bytes of data, hence to keep tasklet
+ * running for less time, max slave read per tasklet is set to 10 bytes.
+ */
+#define MAX_SLAVE_RX_PER_INT 10
+
+enum i2c_slave_read_status {
+ I2C_SLAVE_RX_FIFO_EMPTY = 0,
+ I2C_SLAVE_RX_START,
+ I2C_SLAVE_RX_DATA,
+ I2C_SLAVE_RX_END,
+};
enum bus_speed_index {
I2C_SPD_100K = 0,
I2C_SPD_400K,
};
+enum bcm_iproc_i2c_type {
+ IPROC_I2C,
+ IPROC_I2C_NIC
+};
+
struct bcm_iproc_i2c_dev {
struct device *device;
+ enum bcm_iproc_i2c_type type;
int irq;
void __iomem *base;
+ void __iomem *idm_base;
+
+ u32 ape_addr_mask;
+
+ /* lock for indirect access through IDM */
+ spinlock_t idm_lock;
struct i2c_adapter adapter;
unsigned int bus_speed;
@@ -100,112 +194,525 @@ struct bcm_iproc_i2c_dev {
struct i2c_msg *msg;
+ struct i2c_client *slave;
+
/* bytes that have been transferred */
unsigned int tx_bytes;
+ /* bytes that have been read */
+ unsigned int rx_bytes;
+ unsigned int thld_bytes;
+
+ bool slave_rx_only;
+ bool rx_start_rcvd;
+ bool slave_read_complete;
+ u32 tx_underrun;
+ u32 slave_int_mask;
+ struct tasklet_struct slave_rx_tasklet;
};
+/* tasklet to process slave rx data */
+static void slave_rx_tasklet_fn(unsigned long);
+
/*
* Can be expanded in the future if more interrupt status bits are utilized
*/
-#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT))
+#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)\
+ | BIT(IS_M_RX_THLD_SHIFT))
-static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
+#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\
+ | BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT)\
+ | BIT(IS_S_TX_UNDERRUN_SHIFT) | BIT(IS_S_RX_FIFO_FULL_SHIFT)\
+ | BIT(IS_S_RX_THLD_SHIFT))
+
+static inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
+ u32 offset)
{
- struct bcm_iproc_i2c_dev *iproc_i2c = data;
- u32 status = readl(iproc_i2c->base + IS_OFFSET);
+ u32 val;
+ unsigned long flags;
+
+ if (iproc_i2c->idm_base) {
+ spin_lock_irqsave(&iproc_i2c->idm_lock, flags);
+ writel(iproc_i2c->ape_addr_mask,
+ iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+ val = readl(iproc_i2c->base + offset);
+ spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags);
+ } else {
+ val = readl(iproc_i2c->base + offset);
+ }
- status &= ISR_MASK;
+ return val;
+}
- if (!status)
- return IRQ_NONE;
+static inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
+ u32 offset, u32 val)
+{
+ unsigned long flags;
+
+ if (iproc_i2c->idm_base) {
+ spin_lock_irqsave(&iproc_i2c->idm_lock, flags);
+ writel(iproc_i2c->ape_addr_mask,
+ iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+ writel(val, iproc_i2c->base + offset);
+ spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags);
+ } else {
+ writel(val, iproc_i2c->base + offset);
+ }
+}
- /* TX FIFO is empty and we have more data to send */
- if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) {
- struct i2c_msg *msg = iproc_i2c->msg;
- unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
- unsigned int i;
- u32 val;
-
- /* can only fill up to the FIFO size */
- tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
- for (i = 0; i < tx_bytes; i++) {
- /* start from where we left over */
- unsigned int idx = iproc_i2c->tx_bytes + i;
+static void bcm_iproc_i2c_slave_init(struct bcm_iproc_i2c_dev *iproc_i2c,
+ bool need_reset)
+{
+ u32 val;
- val = msg->buf[idx];
+ iproc_i2c->tx_underrun = 0;
+ if (need_reset) {
+ /* put controller in reset */
+ val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
+ val |= BIT(CFG_RESET_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
- /* mark the last byte */
- if (idx == msg->len - 1) {
- u32 tmp;
+ /* wait approximately 100 usec as per spec */
+ usleep_range(100, 200);
- val |= BIT(M_TX_WR_STATUS_SHIFT);
+ /* bring controller out of reset */
+ val &= ~(BIT(CFG_RESET_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
+ }
+
+ /* flush TX/RX FIFOs */
+ val = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val);
+
+ /* Maximum slave stretch time */
+ val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+ val &= ~(TIM_RAND_SLAVE_STRETCH_MASK << TIM_RAND_SLAVE_STRETCH_SHIFT);
+ val |= (SLAVE_CLOCK_STRETCH_TIME << TIM_RAND_SLAVE_STRETCH_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
+
+ /* Configure the slave address */
+ val = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
+ val |= BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
+ val &= ~(S_CFG_NIC_SMB_ADDR3_MASK << S_CFG_NIC_SMB_ADDR3_SHIFT);
+ val |= (iproc_i2c->slave->addr << S_CFG_NIC_SMB_ADDR3_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, val);
+
+ /* clear all pending slave interrupts */
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE);
+
+ /* Enable interrupt register to indicate a valid byte in receive fifo */
+ val = BIT(IE_S_RX_EVENT_SHIFT);
+ /* Enable interrupt register to indicate Slave Rx FIFO Full */
+ val |= BIT(IE_S_RX_FIFO_FULL_SHIFT);
+ /* Enable interrupt register to indicate a Master read transaction */
+ val |= BIT(IE_S_RD_EVENT_SHIFT);
+ /* Enable interrupt register for the Slave BUSY command */
+ val |= BIT(IE_S_START_BUSY_SHIFT);
+ iproc_i2c->slave_int_mask = val;
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
+}
+
+static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
+ bool enable)
+{
+ u32 val;
+
+ val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
+ if (enable)
+ val |= BIT(CFG_EN_SHIFT);
+ else
+ val &= ~BIT(CFG_EN_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
+}
+
+static bool bcm_iproc_i2c_check_slave_status
+ (struct bcm_iproc_i2c_dev *iproc_i2c, u32 status)
+{
+ u32 val;
+ bool recover = false;
+
+ /* check slave transmit status only if slave is transmitting */
+ if (!iproc_i2c->slave_rx_only) {
+ val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET);
+ /* status is valid only when START_BUSY is cleared */
+ if (!(val & BIT(S_CMD_START_BUSY_SHIFT))) {
+ val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK;
+ if (val == S_CMD_STATUS_TIMEOUT ||
+ val == S_CMD_STATUS_MASTER_ABORT) {
+ dev_warn(iproc_i2c->device,
+ (val == S_CMD_STATUS_TIMEOUT) ?
+ "slave random stretch time timeout\n" :
+ "Master aborted read transaction\n");
+ recover = true;
+ }
+ }
+ }
+
+ /* RX_EVENT is not valid when START_BUSY is set */
+ if ((status & BIT(IS_S_RX_EVENT_SHIFT)) &&
+ (status & BIT(IS_S_START_BUSY_SHIFT))) {
+ dev_warn(iproc_i2c->device, "Slave aborted read transaction\n");
+ recover = true;
+ }
+
+ if (recover) {
+ /* re-initialize i2c for recovery */
+ bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+ bcm_iproc_i2c_slave_init(iproc_i2c, true);
+ bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+ }
+
+ return recover;
+}
+
+static void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+ u8 rx_data, rx_status;
+ u32 rx_bytes = 0;
+ u32 val;
+
+ while (rx_bytes < MAX_SLAVE_RX_PER_INT) {
+ val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
+ rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
+ rx_data = ((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
+
+ if (rx_status == I2C_SLAVE_RX_START) {
+ /* Start of SMBUS Master write */
+ i2c_slave_event(iproc_i2c->slave,
+ I2C_SLAVE_WRITE_REQUESTED, &rx_data);
+ iproc_i2c->rx_start_rcvd = true;
+ iproc_i2c->slave_read_complete = false;
+ } else if (rx_status == I2C_SLAVE_RX_DATA &&
+ iproc_i2c->rx_start_rcvd) {
+ /* Middle of SMBUS Master write */
+ i2c_slave_event(iproc_i2c->slave,
+ I2C_SLAVE_WRITE_RECEIVED, &rx_data);
+ } else if (rx_status == I2C_SLAVE_RX_END &&
+ iproc_i2c->rx_start_rcvd) {
+ /* End of SMBUS Master write */
+ if (iproc_i2c->slave_rx_only)
+ i2c_slave_event(iproc_i2c->slave,
+ I2C_SLAVE_WRITE_RECEIVED,
+ &rx_data);
+
+ i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP,
+ &rx_data);
+ } else if (rx_status == I2C_SLAVE_RX_FIFO_EMPTY) {
+ iproc_i2c->rx_start_rcvd = false;
+ iproc_i2c->slave_read_complete = true;
+ break;
+ }
+
+ rx_bytes++;
+ }
+}
+
+static void slave_rx_tasklet_fn(unsigned long data)
+{
+ struct bcm_iproc_i2c_dev *iproc_i2c = (struct bcm_iproc_i2c_dev *)data;
+ u32 int_clr;
+
+ bcm_iproc_i2c_slave_read(iproc_i2c);
+
+ /* clear pending IS_S_RX_EVENT_SHIFT interrupt */
+ int_clr = BIT(IS_S_RX_EVENT_SHIFT);
+
+ if (!iproc_i2c->slave_rx_only && iproc_i2c->slave_read_complete) {
+ /*
+ * In case of single byte master-read request,
+ * IS_S_TX_UNDERRUN_SHIFT event is generated before
+ * IS_S_START_BUSY_SHIFT event. Hence start slave data send
+ * from first IS_S_TX_UNDERRUN_SHIFT event.
+ *
+ * This means don't send any data from slave when
+ * IS_S_RD_EVENT_SHIFT event is generated else it will increment
+ * eeprom or other backend slave driver read pointer twice.
+ */
+ iproc_i2c->tx_underrun = 0;
+ iproc_i2c->slave_int_mask |= BIT(IE_S_TX_UNDERRUN_SHIFT);
+
+ /* clear IS_S_RD_EVENT_SHIFT interrupt */
+ int_clr |= BIT(IS_S_RD_EVENT_SHIFT);
+ }
+
+ /* clear slave interrupt */
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, int_clr);
+ /* enable slave interrupts */
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, iproc_i2c->slave_int_mask);
+}
+
+static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
+ u32 status)
+{
+ u32 val;
+ u8 value;
+
+ if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
+ iproc_i2c->tx_underrun++;
+ if (iproc_i2c->tx_underrun == 1)
+ /* Start of SMBUS for Master Read */
+ i2c_slave_event(iproc_i2c->slave,
+ I2C_SLAVE_READ_REQUESTED,
+ &value);
+ else
+ /* Master read other than start */
+ i2c_slave_event(iproc_i2c->slave,
+ I2C_SLAVE_READ_PROCESSED,
+ &value);
+
+ iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
+ /* start transfer */
+ val = BIT(S_CMD_START_BUSY_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
+
+ /* clear interrupt */
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
+ BIT(IS_S_TX_UNDERRUN_SHIFT));
+ }
+
+ /* Stop received from master in case of master read transaction */
+ if (status & BIT(IS_S_START_BUSY_SHIFT)) {
+ /*
+ * Disable interrupt for TX FIFO becomes empty and
+ * less than PKT_LENGTH bytes were output on the SMBUS
+ */
+ iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
+ val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
+
+ /* End of SMBUS for Master Read */
+ val = BIT(S_TX_WR_STATUS_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val);
+
+ val = BIT(S_CMD_START_BUSY_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
+
+ /* flush TX FIFOs */
+ val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET);
+ val |= (BIT(S_FIFO_TX_FLUSH_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val);
+
+ i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);
+
+ /* clear interrupt */
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
+ BIT(IS_S_START_BUSY_SHIFT));
+ }
+
+ /* if the controller has been reset, immediately return from the ISR */
+ if (bcm_iproc_i2c_check_slave_status(iproc_i2c, status))
+ return true;
+
+ /*
+ * Slave events in case of master-write, master-write-read and,
+ * master-read
+ *
+ * Master-write : only IS_S_RX_EVENT_SHIFT event
+ * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
+ * events
+ * Master-read : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
+ * events or only IS_S_RD_EVENT_SHIFT
+ *
+ * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
+ * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
+ * full. This can happen if Master issues write requests of more than
+ * 64 bytes.
+ */
+ if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
+ status & BIT(IS_S_RD_EVENT_SHIFT) ||
+ status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
+ /* disable slave interrupts */
+ val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ val &= ~iproc_i2c->slave_int_mask;
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
+
+ if (status & BIT(IS_S_RD_EVENT_SHIFT))
+ /* Master-write-read request */
+ iproc_i2c->slave_rx_only = false;
+ else
+ /* Master-write request only */
+ iproc_i2c->slave_rx_only = true;
+
+ /* schedule tasklet to read data later */
+ tasklet_schedule(&iproc_i2c->slave_rx_tasklet);
+
+ /* clear IS_S_RX_FIFO_FULL_SHIFT interrupt */
+ if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
+ val = BIT(IS_S_RX_FIFO_FULL_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val);
+ }
+ }
+
+ return true;
+}
+
+static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+ struct i2c_msg *msg = iproc_i2c->msg;
+ u32 val;
+
+ /* Read valid data from RX FIFO */
+ while (iproc_i2c->rx_bytes < msg->len) {
+ val = iproc_i2c_rd_reg(iproc_i2c, M_RX_OFFSET);
+
+ /* rx fifo empty */
+ if (!((val >> M_RX_STATUS_SHIFT) & M_RX_STATUS_MASK))
+ break;
+
+ msg->buf[iproc_i2c->rx_bytes] =
+ (val >> M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
+ iproc_i2c->rx_bytes++;
+ }
+}
+
+static void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+ struct i2c_msg *msg = iproc_i2c->msg;
+ unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
+ unsigned int i;
+ u32 val;
+
+ /* can only fill up to the FIFO size */
+ tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
+ for (i = 0; i < tx_bytes; i++) {
+ /* start from where we left over */
+ unsigned int idx = iproc_i2c->tx_bytes + i;
+
+ val = msg->buf[idx];
+
+ /* mark the last byte */
+ if (idx == msg->len - 1) {
+ val |= BIT(M_TX_WR_STATUS_SHIFT);
+
+ if (iproc_i2c->irq) {
+ u32 tmp;
/*
- * Since this is the last byte, we should
- * now disable TX FIFO underrun interrupt
+ * Since this is the last byte, we should now
+ * disable TX FIFO underrun interrupt
*/
- tmp = readl(iproc_i2c->base + IE_OFFSET);
+ tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
- writel(tmp, iproc_i2c->base + IE_OFFSET);
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET,
+ tmp);
}
+ }
+
+ /* load data into TX FIFO */
+ iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
+ }
- /* load data into TX FIFO */
- writel(val, iproc_i2c->base + M_TX_OFFSET);
+ /* update number of transferred bytes */
+ iproc_i2c->tx_bytes += tx_bytes;
+}
+
+static void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+ struct i2c_msg *msg = iproc_i2c->msg;
+ u32 bytes_left, val;
+
+ bcm_iproc_i2c_read_valid_bytes(iproc_i2c);
+ bytes_left = msg->len - iproc_i2c->rx_bytes;
+ if (bytes_left == 0) {
+ if (iproc_i2c->irq) {
+ /* finished reading all data, disable rx thld event */
+ val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ val &= ~BIT(IS_M_RX_THLD_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
}
- /* update number of transferred bytes */
- iproc_i2c->tx_bytes += tx_bytes;
+ } else if (bytes_left < iproc_i2c->thld_bytes) {
+ /* set bytes left as threshold */
+ val = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET);
+ val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
+ val |= (bytes_left << M_FIFO_RX_THLD_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+ iproc_i2c->thld_bytes = bytes_left;
}
+ /*
+ * bytes_left >= iproc_i2c->thld_bytes,
+ * hence no need to change the THRESHOLD SET.
+ * It will remain as iproc_i2c->thld_bytes itself
+ */
+}
+
+static void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c,
+ u32 status)
+{
+ /* TX FIFO is empty and we have more data to send */
+ if (status & BIT(IS_M_TX_UNDERRUN_SHIFT))
+ bcm_iproc_i2c_send(iproc_i2c);
+ /* RX FIFO threshold is reached and data needs to be read out */
+ if (status & BIT(IS_M_RX_THLD_SHIFT))
+ bcm_iproc_i2c_read(iproc_i2c);
+
+ /* transfer is done */
if (status & BIT(IS_M_START_BUSY_SHIFT)) {
iproc_i2c->xfer_is_done = 1;
- complete(&iproc_i2c->done);
+ if (iproc_i2c->irq)
+ complete(&iproc_i2c->done);
+ }
+}
+
+static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
+{
+ struct bcm_iproc_i2c_dev *iproc_i2c = data;
+ u32 slave_status;
+ u32 status;
+ bool ret;
+
+ status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET);
+ /* process only slave interrupt which are enabled */
+ slave_status = status & iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET) &
+ ISR_MASK_SLAVE;
+
+ if (slave_status) {
+ ret = bcm_iproc_i2c_slave_isr(iproc_i2c, slave_status);
+ if (ret)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
}
- writel(status, iproc_i2c->base + IS_OFFSET);
+ status &= ISR_MASK;
+ if (!status)
+ return IRQ_NONE;
+
+ /* process all master based events */
+ bcm_iproc_i2c_process_m_event(iproc_i2c, status);
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
return IRQ_HANDLED;
}
-static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
+static void bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
{
u32 val;
/* put controller in reset */
- val = readl(iproc_i2c->base + CFG_OFFSET);
- val |= 1 << CFG_RESET_SHIFT;
- val &= ~(1 << CFG_EN_SHIFT);
- writel(val, iproc_i2c->base + CFG_OFFSET);
+ val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
+ val |= BIT(CFG_RESET_SHIFT);
+ val &= ~(BIT(CFG_EN_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
- /* wait 100 usec per spec */
- udelay(100);
+ /* wait approximately 100 usec as per spec */
+ usleep_range(100, 200);
/* bring controller out of reset */
- val &= ~(1 << CFG_RESET_SHIFT);
- writel(val, iproc_i2c->base + CFG_OFFSET);
+ val &= ~(BIT(CFG_RESET_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
/* flush TX/RX FIFOs and set RX FIFO threshold to zero */
- val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT);
- writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
+ val = (BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
/* disable all interrupts */
- writel(0, iproc_i2c->base + IE_OFFSET);
+ val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ val &= ~(IE_M_ALL_INTERRUPT_MASK <<
+ IE_M_ALL_INTERRUPT_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
/* clear all pending interrupts */
- writel(0xffffffff, iproc_i2c->base + IS_OFFSET);
-
- return 0;
-}
-
-static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
- bool enable)
-{
- u32 val;
-
- val = readl(iproc_i2c->base + CFG_OFFSET);
- if (enable)
- val |= BIT(CFG_EN_SHIFT);
- else
- val &= ~BIT(CFG_EN_SHIFT);
- writel(val, iproc_i2c->base + CFG_OFFSET);
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff);
}
static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
@@ -213,7 +720,7 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
{
u32 val;
- val = readl(iproc_i2c->base + M_CMD_OFFSET);
+ val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET);
val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK;
switch (val) {
@@ -221,23 +728,31 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
return 0;
case M_CMD_STATUS_LOST_ARB:
- dev_dbg(iproc_i2c->device, "lost bus arbitration\n");
+ dev_err(iproc_i2c->device, "lost bus arbitration\n");
return -EAGAIN;
case M_CMD_STATUS_NACK_ADDR:
- dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr);
+ dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr);
return -ENXIO;
case M_CMD_STATUS_NACK_DATA:
- dev_dbg(iproc_i2c->device, "NAK data\n");
+ dev_err(iproc_i2c->device, "NAK data\n");
return -ENXIO;
case M_CMD_STATUS_TIMEOUT:
- dev_dbg(iproc_i2c->device, "bus timeout\n");
+ dev_err(iproc_i2c->device, "bus timeout\n");
+ return -ETIMEDOUT;
+
+ case M_CMD_STATUS_FIFO_UNDERRUN:
+ dev_err(iproc_i2c->device, "FIFO under-run\n");
+ return -ENXIO;
+
+ case M_CMD_STATUS_RX_FIFO_FULL:
+ dev_err(iproc_i2c->device, "RX FIFO full\n");
return -ETIMEDOUT;
default:
- dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val);
+ dev_err(iproc_i2c->device, "unknown error code=%d\n", val);
/* re-initialize i2c for recovery */
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
@@ -248,18 +763,81 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
}
}
-static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
- struct i2c_msg *msg)
+static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
+ struct i2c_msg *msg,
+ u32 cmd)
{
- int ret, i;
+ unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
+ u32 val, status;
+ int ret;
+
+ iproc_i2c_wr_reg(iproc_i2c, M_CMD_OFFSET, cmd);
+
+ if (iproc_i2c->irq) {
+ time_left = wait_for_completion_timeout(&iproc_i2c->done,
+ time_left);
+ /* disable all interrupts */
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+ /* read it back to flush the write */
+ iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ /* make sure the interrupt handler isn't running */
+ synchronize_irq(iproc_i2c->irq);
+
+ } else { /* polling mode */
+ unsigned long timeout = jiffies + time_left;
+
+ do {
+ status = iproc_i2c_rd_reg(iproc_i2c,
+ IS_OFFSET) & ISR_MASK;
+ bcm_iproc_i2c_process_m_event(iproc_i2c, status);
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
+
+ if (time_after(jiffies, timeout)) {
+ time_left = 0;
+ break;
+ }
+
+ cpu_relax();
+ cond_resched();
+ } while (!iproc_i2c->xfer_is_done);
+ }
+
+ if (!time_left && !iproc_i2c->xfer_is_done) {
+ /* flush both TX/RX FIFOs */
+ val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+ return -ETIMEDOUT;
+ }
+
+ ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
+ if (ret) {
+ /* flush both TX/RX FIFOs */
+ val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * If 'process_call' is true, then this is a multi-msg transfer that requires
+ * a repeated start between the messages.
+ * More specifically, it must be a write (reg) followed by a read (data).
+ * The i2c quirks are set to enforce this rule.
+ */
+static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
+ struct i2c_msg *msgs, bool process_call)
+{
+ int i;
u8 addr;
- u32 val;
+ u32 val, tmp, val_intr_en;
unsigned int tx_bytes;
- unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
+ struct i2c_msg *msg = &msgs[0];
/* check if bus is busy */
- if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) &
- BIT(M_CMD_START_BUSY_SHIFT))) {
+ if (iproc_i2c_rd_reg(iproc_i2c,
+ M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT)) {
dev_warn(iproc_i2c->device, "bus is busy\n");
return -EBUSY;
}
@@ -268,7 +846,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
/* format and load slave address into the TX FIFO */
addr = i2c_8bit_addr_from_msg(msg);
- writel(addr, iproc_i2c->base + M_TX_OFFSET);
+ iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr);
/*
* For a write transaction, load data into the TX FIFO. Only allow
@@ -281,16 +859,33 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
val = msg->buf[i];
/* mark the last byte */
- if (i == msg->len - 1)
- val |= 1 << M_TX_WR_STATUS_SHIFT;
+ if (!process_call && (i == msg->len - 1))
+ val |= BIT(M_TX_WR_STATUS_SHIFT);
- writel(val, iproc_i2c->base + M_TX_OFFSET);
+ iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
}
iproc_i2c->tx_bytes = tx_bytes;
}
+ /* Process the read message if this is process call */
+ if (process_call) {
+ msg++;
+ iproc_i2c->msg = msg; /* point to second msg */
+
+ /*
+ * The last byte to be sent out should be a slave
+ * address with read operation
+ */
+ addr = i2c_8bit_addr_from_msg(msg);
+ /* mark it the last byte out */
+ val = addr | BIT(M_TX_WR_STATUS_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
+ }
+
/* mark as incomplete before starting the transaction */
- reinit_completion(&iproc_i2c->done);
+ if (iproc_i2c->irq)
+ reinit_completion(&iproc_i2c->done);
+
iproc_i2c->xfer_is_done = 0;
/*
@@ -298,106 +893,164 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
* transaction is done, i.e., the internal start_busy bit, transitions
* from 1 to 0.
*/
- val = BIT(IE_M_START_BUSY_SHIFT);
+ val_intr_en = BIT(IE_M_START_BUSY_SHIFT);
/*
* If TX data size is larger than the TX FIFO, need to enable TX
* underrun interrupt, which will be triggerred when the TX FIFO is
* empty. When that happens we can then pump more data into the FIFO
*/
- if (!(msg->flags & I2C_M_RD) &&
+ if (!process_call && !(msg->flags & I2C_M_RD) &&
msg->len > iproc_i2c->tx_bytes)
- val |= BIT(IE_M_TX_UNDERRUN_SHIFT);
-
- writel(val, iproc_i2c->base + IE_OFFSET);
+ val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
/*
* Now we can activate the transfer. For a read operation, specify the
* number of bytes to read
*/
val = BIT(M_CMD_START_BUSY_SHIFT);
- if (msg->flags & I2C_M_RD) {
- val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
- (msg->len << M_CMD_RD_CNT_SHIFT);
- } else {
- val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
- }
- writel(val, iproc_i2c->base + M_CMD_OFFSET);
- time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left);
+ if (msg->len == 0) {
+ /* SMBUS QUICK Command (Read/Write) */
+ val |= (M_CMD_PROTOCOL_QUICK << M_CMD_PROTOCOL_SHIFT);
+ } else if (msg->flags & I2C_M_RD) {
+ u32 protocol;
- /* disable all interrupts */
- writel(0, iproc_i2c->base + IE_OFFSET);
- /* read it back to flush the write */
- readl(iproc_i2c->base + IE_OFFSET);
+ iproc_i2c->rx_bytes = 0;
+ if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
+ iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
+ else
+ iproc_i2c->thld_bytes = msg->len;
- /* make sure the interrupt handler isn't running */
- synchronize_irq(iproc_i2c->irq);
+ /* set threshold value */
+ tmp = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET);
+ tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
+ tmp |= iproc_i2c->thld_bytes << M_FIFO_RX_THLD_SHIFT;
+ iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, tmp);
- if (!time_left && !iproc_i2c->xfer_is_done) {
- dev_err(iproc_i2c->device, "transaction timed out\n");
+ /* enable the RX threshold interrupt */
+ val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
- /* flush FIFOs */
- val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
- (1 << M_FIFO_TX_FLUSH_SHIFT);
- writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
- return -ETIMEDOUT;
- }
+ protocol = process_call ?
+ M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
- ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
- if (ret) {
- /* flush both TX/RX FIFOs */
- val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
- (1 << M_FIFO_TX_FLUSH_SHIFT);
- writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
- return ret;
+ val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
+ (msg->len << M_CMD_RD_CNT_SHIFT);
+ } else {
+ val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
}
- /*
- * For a read operation, we now need to load the data from FIFO
- * into the memory buffer
- */
- if (msg->flags & I2C_M_RD) {
- for (i = 0; i < msg->len; i++) {
- msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >>
- M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
- }
- }
+ if (iproc_i2c->irq)
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en);
- return 0;
+ return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val);
}
static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
- int ret, i;
-
- /* go through all messages */
- for (i = 0; i < num; i++) {
- ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
- if (ret) {
- dev_dbg(iproc_i2c->device, "xfer failed\n");
- return ret;
+ bool process_call = false;
+ int ret;
+
+ if (num == 2) {
+ /* Repeated start, use process call */
+ process_call = true;
+ if (msgs[1].flags & I2C_M_NOSTART) {
+ dev_err(iproc_i2c->device, "Invalid repeated start\n");
+ return -EOPNOTSUPP;
}
}
+ ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
+ if (ret) {
+ dev_err(iproc_i2c->device, "xfer failed\n");
+ return ret;
+ }
+
return num;
}
-static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
+static u32 bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
+{
+ u32 val;
+
+ val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+
+ if (adap->algo->reg_slave)
+ val |= I2C_FUNC_SLAVE;
+
+ return val;
+}
+
+static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
+
+ if (iproc_i2c->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ iproc_i2c->slave = slave;
+
+ tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn,
+ (unsigned long)iproc_i2c);
+
+ bcm_iproc_i2c_slave_init(iproc_i2c, false);
+
+ return 0;
+}
+
+static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
+ u32 tmp;
+
+ if (!iproc_i2c->slave)
+ return -EINVAL;
+
+ disable_irq(iproc_i2c->irq);
+
+ tasklet_kill(&iproc_i2c->slave_rx_tasklet);
+
+ /* disable all slave interrupts */
+ tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ tmp &= ~(IE_S_ALL_INTERRUPT_MASK <<
+ IE_S_ALL_INTERRUPT_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);
+
+ /* Erase the slave address programmed */
+ tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
+ tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
+ iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp);
+
+ /* flush TX/RX FIFOs */
+ tmp = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT));
+ iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, tmp);
+
+ /* clear all pending slave interrupts */
+ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE);
+
+ iproc_i2c->slave = NULL;
+
+ enable_irq(iproc_i2c->irq);
+
+ return 0;
}
-static const struct i2c_algorithm bcm_iproc_algo = {
- .master_xfer = bcm_iproc_i2c_xfer,
+static struct i2c_algorithm bcm_iproc_algo = {
+ .xfer = bcm_iproc_i2c_xfer,
.functionality = bcm_iproc_i2c_functionality,
+ .reg_slave = bcm_iproc_i2c_reg_slave,
+ .unreg_slave = bcm_iproc_i2c_unreg_slave,
};
static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
- /* need to reserve one byte in the FIFO for the slave address */
- .max_read_len = M_TX_RX_FIFO_SIZE - 1,
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
+ .max_read_len = M_RX_MAX_READ_LEN,
};
static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
@@ -408,27 +1061,24 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
"clock-frequency", &bus_speed);
if (ret < 0) {
dev_info(iproc_i2c->device,
- "unable to interpret clock-frequency DT property\n");
- bus_speed = 100000;
+ "unable to interpret clock-frequency DT property\n");
+ bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
}
- if (bus_speed < 100000) {
- dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n",
- bus_speed);
- dev_err(iproc_i2c->device,
- "valid speeds are 100khz and 400khz\n");
- return -EINVAL;
- } else if (bus_speed < 400000) {
- bus_speed = 100000;
- } else {
- bus_speed = 400000;
- }
+ if (bus_speed < I2C_MAX_STANDARD_MODE_FREQ)
+ return dev_err_probe(iproc_i2c->device, -EINVAL,
+ "%d Hz not supported (out of 100-400 kHz range)\n",
+ bus_speed);
+ else if (bus_speed < I2C_MAX_FAST_MODE_FREQ)
+ bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
+ else
+ bus_speed = I2C_MAX_FAST_MODE_FREQ;
iproc_i2c->bus_speed = bus_speed;
- val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
- val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
- val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
- writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
+ val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+ val &= ~BIT(TIM_CFG_MODE_400_SHIFT);
+ val |= (bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT;
+ iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed);
@@ -437,10 +1087,9 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
static int bcm_iproc_i2c_probe(struct platform_device *pdev)
{
- int irq, ret = 0;
struct bcm_iproc_i2c_dev *iproc_i2c;
struct i2c_adapter *adap;
- struct resource *res;
+ int irq, ret;
iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c),
GFP_KERNEL);
@@ -449,40 +1098,60 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, iproc_i2c);
iproc_i2c->device = &pdev->dev;
+ iproc_i2c->type =
+ (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev);
init_completion(&iproc_i2c->done);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res);
+ iproc_i2c->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(iproc_i2c->base))
return PTR_ERR(iproc_i2c->base);
- ret = bcm_iproc_i2c_init(iproc_i2c);
- if (ret)
- return ret;
+ if (iproc_i2c->type == IPROC_I2C_NIC) {
+ iproc_i2c->idm_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(iproc_i2c->idm_base))
+ return PTR_ERR(iproc_i2c->idm_base);
+
+ ret = of_property_read_u32(iproc_i2c->device->of_node,
+ "brcm,ape-hsls-addr-mask",
+ &iproc_i2c->ape_addr_mask);
+ if (ret < 0)
+ return dev_err_probe(iproc_i2c->device, ret,
+ "'brcm,ape-hsls-addr-mask' missing\n");
+
+ spin_lock_init(&iproc_i2c->idm_lock);
+
+ /* no slave support */
+ bcm_iproc_algo.reg_slave = NULL;
+ bcm_iproc_algo.unreg_slave = NULL;
+ }
+
+ bcm_iproc_i2c_init(iproc_i2c);
ret = bcm_iproc_i2c_cfg_speed(iproc_i2c);
if (ret)
return ret;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(iproc_i2c->device, "no irq resource\n");
- return irq;
- }
- iproc_i2c->irq = irq;
-
- ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0,
- pdev->name, iproc_i2c);
- if (ret < 0) {
- dev_err(iproc_i2c->device, "unable to request irq %i\n", irq);
- return ret;
+ if (irq > 0) {
+ ret = devm_request_irq(iproc_i2c->device, irq,
+ bcm_iproc_i2c_isr, 0, pdev->name,
+ iproc_i2c);
+ if (ret < 0)
+ return dev_err_probe(iproc_i2c->device, ret,
+ "unable to request irq %i\n", irq);
+
+ iproc_i2c->irq = irq;
+ } else {
+ dev_warn(iproc_i2c->device,
+ "no irq resource, falling back to poll mode\n");
}
bcm_iproc_i2c_enable_disable(iproc_i2c, true);
adap = &iproc_i2c->adapter;
i2c_set_adapdata(adap, iproc_i2c);
- strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name));
+ snprintf(adap->name, sizeof(adap->name), "Broadcom iProc (%s)",
+ of_node_full_name(iproc_i2c->device->of_node));
adap->algo = &bcm_iproc_algo;
adap->quirks = &bcm_iproc_i2c_quirks;
adap->dev.parent = &pdev->dev;
@@ -491,31 +1160,37 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
return i2c_add_adapter(adap);
}
-static int bcm_iproc_i2c_remove(struct platform_device *pdev)
+static void bcm_iproc_i2c_remove(struct platform_device *pdev)
{
struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);
- /* make sure there's no pending interrupt when we remove the adapter */
- writel(0, iproc_i2c->base + IE_OFFSET);
- readl(iproc_i2c->base + IE_OFFSET);
- synchronize_irq(iproc_i2c->irq);
+ if (iproc_i2c->irq) {
+ /*
+ * Make sure there's no pending interrupt when we remove the
+ * adapter
+ */
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+ iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ synchronize_irq(iproc_i2c->irq);
+ }
i2c_del_adapter(&iproc_i2c->adapter);
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
-
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
-
static int bcm_iproc_i2c_suspend(struct device *dev)
{
struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev);
- /* make sure there's no pending interrupt when we go into suspend */
- writel(0, iproc_i2c->base + IE_OFFSET);
- readl(iproc_i2c->base + IE_OFFSET);
- synchronize_irq(iproc_i2c->irq);
+ if (iproc_i2c->irq) {
+ /*
+ * Make sure there's no pending interrupt when we go into
+ * suspend
+ */
+ iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+ iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+ synchronize_irq(iproc_i2c->irq);
+ }
/* now disable the controller */
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
@@ -526,22 +1201,19 @@ static int bcm_iproc_i2c_suspend(struct device *dev)
static int bcm_iproc_i2c_resume(struct device *dev)
{
struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev);
- int ret;
u32 val;
/*
* Power domain could have been shut off completely in system deep
* sleep, so re-initialize the block here
*/
- ret = bcm_iproc_i2c_init(iproc_i2c);
- if (ret)
- return ret;
+ bcm_iproc_i2c_init(iproc_i2c);
/* configure to the desired bus speed */
- val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
- val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
- val |= (iproc_i2c->bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
- writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
+ val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+ val &= ~BIT(TIM_CFG_MODE_400_SHIFT);
+ val |= (iproc_i2c->bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT;
+ iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
bcm_iproc_i2c_enable_disable(iproc_i2c, true);
@@ -553,13 +1225,14 @@ static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = {
.resume_early = &bcm_iproc_i2c_resume
};
-#define BCM_IPROC_I2C_PM_OPS (&bcm_iproc_i2c_pm_ops)
-#else
-#define BCM_IPROC_I2C_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
-
static const struct of_device_id bcm_iproc_i2c_of_match[] = {
- { .compatible = "brcm,iproc-i2c" },
+ {
+ .compatible = "brcm,iproc-i2c",
+ .data = (int *)IPROC_I2C,
+ }, {
+ .compatible = "brcm,iproc-nic-i2c",
+ .data = (int *)IPROC_I2C_NIC,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match);
@@ -568,7 +1241,7 @@ static struct platform_driver bcm_iproc_i2c_driver = {
.driver = {
.name = "bcm-iproc-i2c",
.of_match_table = bcm_iproc_i2c_of_match,
- .pm = BCM_IPROC_I2C_PM_OPS,
+ .pm = pm_sleep_ptr(&bcm_iproc_i2c_pm_ops),
},
.probe = bcm_iproc_i2c_probe,
.remove = bcm_iproc_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c
index 4e489a9d16fb..9d8838bbd938 100644
--- a/drivers/i2c/busses/i2c-bcm-kona.c
+++ b/drivers/i2c/busses/i2c-bcm-kona.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2013 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2013 Broadcom Corporation
#include <linux/device.h>
#include <linux/kernel.h>
@@ -95,7 +85,7 @@
#define STD_EXT_CLK_FREQ 13000000UL
#define HS_EXT_CLK_FREQ 104000000UL
-#define MASTERCODE 0x08 /* Mastercodes are 0000_1xxxb */
+#define CONTROLLER_CODE 0x08 /* Controller codes are 0000_1xxxb */
#define I2C_TIMEOUT 100 /* msecs */
@@ -481,12 +471,12 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev,
if (msg->flags & I2C_M_TEN) {
/* First byte is 11110XX0 where XX is upper 2 bits */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7);
+ addr = i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD;
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
return -EREMOTEIO;
/* Second byte is the remaining 8 bits */
- addr = msg->addr & 0xFF;
+ addr = i2c_10bit_addr_lo_from_msg(msg);
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
return -EREMOTEIO;
@@ -496,7 +486,7 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev,
return -EREMOTEIO;
/* Then re-send the first byte with the read bit set */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01;
+ addr = i2c_10bit_addr_hi_from_msg(msg);
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
return -EREMOTEIO;
}
@@ -554,8 +544,8 @@ static int bcm_kona_i2c_switch_to_hs(struct bcm_kona_i2c_dev *dev)
{
int rc;
- /* Send mastercode at standard speed */
- rc = bcm_kona_i2c_write_byte(dev, MASTERCODE, 1);
+ /* Send controller code at standard speed */
+ rc = bcm_kona_i2c_write_byte(dev, CONTROLLER_CODE, 1);
if (rc < 0) {
pr_err("High speed handshake failed\n");
return rc;
@@ -597,7 +587,6 @@ static int bcm_kona_i2c_switch_to_std(struct bcm_kona_i2c_dev *dev)
return rc;
}
-/* Master transfer function */
static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
@@ -647,7 +636,7 @@ static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter,
}
}
- /* Send slave address */
+ /* Send target address */
if (!(pmsg->flags & I2C_M_NOSTART)) {
rc = bcm_kona_i2c_do_addr(dev, pmsg);
if (rc < 0) {
@@ -707,7 +696,7 @@ static uint32_t bcm_kona_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm bcm_algo = {
- .master_xfer = bcm_kona_i2c_xfer,
+ .xfer = bcm_kona_i2c_xfer,
.functionality = bcm_kona_i2c_functionality,
};
@@ -722,17 +711,17 @@ static int bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev)
}
switch (bus_speed) {
- case 100000:
+ case I2C_MAX_STANDARD_MODE_FREQ:
dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
break;
- case 400000:
+ case I2C_MAX_FAST_MODE_FREQ:
dev->std_cfg = &std_cfg_table[BCM_SPD_400K];
break;
- case 1000000:
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ];
break;
- case 3400000:
- /* Send mastercode at 100k */
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ /* Send controller code at 100k */
dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
dev->hs_cfg = &hs_cfg_table[BCM_SPD_3P4MHZ];
break;
@@ -750,7 +739,6 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev)
int rc = 0;
struct bcm_kona_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *iomem;
/* Allocate memory for private data structure */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -762,10 +750,9 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev)
init_completion(&dev->done);
/* Map hardware registers */
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->base = devm_ioremap_resource(dev->device, iomem);
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dev->base))
- return -ENOMEM;
+ return PTR_ERR(dev->base);
/* Get and enable external clock */
dev->external_clk = devm_clk_get(dev->device, NULL);
@@ -823,8 +810,7 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev)
/* Get the interrupt number */
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq < 0) {
- dev_err(dev->device, "no irq resource\n");
- rc = -ENODEV;
+ rc = dev->irq;
goto probe_disable_clk;
}
@@ -852,7 +838,7 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- strlcpy(adap->name, "Broadcom I2C adapter", sizeof(adap->name));
+ strscpy(adap->name, "Broadcom I2C adapter", sizeof(adap->name));
adap->algo = &bcm_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
@@ -872,13 +858,11 @@ probe_disable_clk:
return rc;
}
-static int bcm_kona_i2c_remove(struct platform_device *pdev)
+static void bcm_kona_i2c_remove(struct platform_device *pdev)
{
struct bcm_kona_i2c_dev *dev = platform_get_drvdata(pdev);
i2c_del_adapter(&dev->adapter);
-
- return 0;
}
static const struct of_device_id bcm_kona_i2c_of_match[] = {
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index ec6e69aa3a8e..0d7e2654a534 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -1,15 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * BCM2835 master mode driver
+ * BCM2835 I2C controller driver
*/
#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -20,6 +23,11 @@
#define BCM2835_I2C_FIFO 0x10
#define BCM2835_I2C_DIV 0x14
#define BCM2835_I2C_DEL 0x18
+/*
+ * 16-bit field for the number of SCL cycles to wait after rising SCL
+ * before deciding the target is not responding. 0 disables the
+ * timeout detection.
+ */
#define BCM2835_I2C_CLKT 0x1c
#define BCM2835_I2C_C_READ BIT(0)
@@ -51,12 +59,11 @@
struct bcm2835_i2c_dev {
struct device *dev;
void __iomem *regs;
- struct clk *clk;
int irq;
- u32 bus_clk_rate;
struct i2c_adapter adapter;
struct completion completion;
struct i2c_msg *curr_msg;
+ struct clk *bus_clk;
int num_msgs;
u32 msg_err;
u8 *msg_buf;
@@ -74,12 +81,17 @@ static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
return readl(i2c_dev->regs + reg);
}
-static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev)
+#define to_clk_bcm2835_i2c(_hw) container_of(_hw, struct clk_bcm2835_i2c, hw)
+struct clk_bcm2835_i2c {
+ struct clk_hw hw;
+ struct bcm2835_i2c_dev *i2c_dev;
+};
+
+static int clk_bcm2835_i2c_calc_divider(unsigned long rate,
+ unsigned long parent_rate)
{
- u32 divider, redl, fedl;
+ u32 divider = DIV_ROUND_UP(parent_rate, rate);
- divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk),
- i2c_dev->bus_clk_rate);
/*
* Per the datasheet, the register is always interpreted as an even
* number, by rounding down. In other words, the LSB is ignored. So,
@@ -88,12 +100,23 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev)
if (divider & 1)
divider++;
if ((divider < BCM2835_I2C_CDIV_MIN) ||
- (divider > BCM2835_I2C_CDIV_MAX)) {
- dev_err_ratelimited(i2c_dev->dev, "Invalid clock-frequency\n");
+ (divider > BCM2835_I2C_CDIV_MAX))
return -EINVAL;
- }
- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
+ return divider;
+}
+
+static int clk_bcm2835_i2c_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw);
+ u32 redl, fedl;
+ u32 divider = clk_bcm2835_i2c_calc_divider(rate, parent_rate);
+
+ if (divider == -EINVAL)
+ return -EINVAL;
+
+ bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DIV, divider);
/*
* Number of core clocks to wait after falling edge before
@@ -108,12 +131,67 @@ static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev)
*/
redl = max(divider / 4, 1u);
- bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DEL,
+ bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DEL,
(fedl << BCM2835_I2C_FEDL_SHIFT) |
(redl << BCM2835_I2C_REDL_SHIFT));
return 0;
}
+static int clk_bcm2835_i2c_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ u32 divider = clk_bcm2835_i2c_calc_divider(req->rate, req->best_parent_rate);
+
+ req->rate = DIV_ROUND_UP(req->best_parent_rate, divider);
+
+ return 0;
+}
+
+static unsigned long clk_bcm2835_i2c_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw);
+ u32 divider = bcm2835_i2c_readl(div->i2c_dev, BCM2835_I2C_DIV);
+
+ return DIV_ROUND_UP(parent_rate, divider);
+}
+
+static const struct clk_ops clk_bcm2835_i2c_ops = {
+ .set_rate = clk_bcm2835_i2c_set_rate,
+ .determine_rate = clk_bcm2835_i2c_determine_rate,
+ .recalc_rate = clk_bcm2835_i2c_recalc_rate,
+};
+
+static struct clk *bcm2835_i2c_register_div(struct device *dev,
+ struct clk *mclk,
+ struct bcm2835_i2c_dev *i2c_dev)
+{
+ struct clk_init_data init;
+ struct clk_bcm2835_i2c *priv;
+ char name[32];
+ const char *mclk_name;
+
+ snprintf(name, sizeof(name), "%s_div", dev_name(dev));
+
+ mclk_name = __clk_get_name(mclk);
+
+ init.ops = &clk_bcm2835_i2c_ops;
+ init.name = name;
+ init.parent_names = (const char* []) { mclk_name };
+ init.num_parents = 1;
+ init.flags = 0;
+
+ priv = devm_kzalloc(dev, sizeof(struct clk_bcm2835_i2c), GFP_KERNEL);
+ if (priv == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ priv->hw.init = &init;
+ priv->i2c_dev = i2c_dev;
+
+ clk_hw_register_clkdev(&priv->hw, "div", dev_name(dev));
+ return devm_clk_register(dev, &priv->hw);
+}
+
static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
{
u32 val;
@@ -147,7 +225,7 @@ static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
/*
* Repeated Start Condition (Sr)
* The BCM2835 ARM Peripherals datasheet mentions a way to trigger a Sr when it
- * talks about reading from a slave with 10 bit address. This is achieved by
+ * talks about reading from a target with 10 bit address. This is achieved by
* issuing a write, poll the I2CS.TA flag and wait for it to be set, and then
* issue a read.
* A comment in https://github.com/raspberrypi/linux/issues/254 shows how the
@@ -183,6 +261,15 @@ static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev)
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
}
+static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev)
+{
+ i2c_dev->curr_msg = NULL;
+ i2c_dev->num_msgs = 0;
+
+ i2c_dev->msg_buf = NULL;
+ i2c_dev->msg_buf_remaining = 0;
+}
+
/*
* Note about I2C_C_CLEAR on error:
* The I2C_C_CLEAR on errors will take some time to resolve -- if you were in
@@ -262,7 +349,7 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
{
struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
unsigned long time_left;
- int i, ret;
+ int i;
for (i = 0; i < (num - 1); i++)
if (msgs[i].flags & I2C_M_RD) {
@@ -271,10 +358,6 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
return -EOPNOTSUPP;
}
- ret = bcm2835_i2c_set_divider(i2c_dev);
- if (ret)
- return ret;
-
i2c_dev->curr_msg = msgs;
i2c_dev->num_msgs = num;
reinit_completion(&i2c_dev->completion);
@@ -283,10 +366,12 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
time_left = wait_for_completion_timeout(&i2c_dev->completion,
adap->timeout);
+
+ bcm2835_i2c_finish_transfer(i2c_dev);
+
if (!time_left) {
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
BCM2835_I2C_C_CLEAR);
- dev_err(i2c_dev->dev, "i2c transfer timed out\n");
return -ETIMEDOUT;
}
@@ -307,13 +392,13 @@ static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm bcm2835_i2c_algo = {
- .master_xfer = bcm2835_i2c_xfer,
- .functionality = bcm2835_i2c_func,
+ .xfer = bcm2835_i2c_xfer,
+ .functionality = bcm2835_i2c_func,
};
/*
- * This HW was reported to have problems with clock stretching:
- * http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
+ * The BCM2835 was reported to have problems with clock stretching:
+ * https://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
* https://www.raspberrypi.org/forums/viewtopic.php?p=146272
*/
static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
@@ -323,9 +408,10 @@ static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
static int bcm2835_i2c_probe(struct platform_device *pdev)
{
struct bcm2835_i2c_dev *i2c_dev;
- struct resource *mem, *irq;
int ret;
struct i2c_adapter *adap;
+ struct clk *mclk;
+ u32 bus_clk_rate;
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
@@ -334,71 +420,102 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
i2c_dev->dev = &pdev->dev;
init_completion(&i2c_dev->completion);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem);
+ i2c_dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(i2c_dev->regs))
return PTR_ERR(i2c_dev->regs);
- i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2c_dev->clk)) {
- if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get clock\n");
- return PTR_ERR(i2c_dev->clk);
- }
+ mclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
+ "Could not get clock\n");
+
+ i2c_dev->bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk, i2c_dev);
+
+ if (IS_ERR(i2c_dev->bus_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->bus_clk),
+ "Could not register clock\n");
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
- &i2c_dev->bus_clk_rate);
+ &bus_clk_rate);
if (ret < 0) {
dev_warn(&pdev->dev,
"Could not read clock-frequency property\n");
- i2c_dev->bus_clk_rate = 100000;
+ bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
}
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(&pdev->dev, "No IRQ resource\n");
- return -ENODEV;
+ ret = clk_set_rate_exclusive(i2c_dev->bus_clk, bus_clk_rate);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "Could not set clock frequency\n");
+
+ ret = clk_prepare_enable(i2c_dev->bus_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't prepare clock");
+ goto err_put_exclusive_rate;
+ }
+
+ i2c_dev->irq = platform_get_irq(pdev, 0);
+ if (i2c_dev->irq < 0) {
+ ret = i2c_dev->irq;
+ goto err_disable_unprepare_clk;
}
- i2c_dev->irq = irq->start;
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Could not request IRQ\n");
- return -ENODEV;
+ goto err_disable_unprepare_clk;
}
adap = &i2c_dev->adapter;
i2c_set_adapdata(adap, i2c_dev);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
- strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
+ snprintf(adap->name, sizeof(adap->name), "bcm2835 (%s)",
+ of_node_full_name(pdev->dev.of_node));
adap->algo = &bcm2835_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
- adap->quirks = &bcm2835_i2c_quirks;
+ adap->quirks = of_device_get_match_data(&pdev->dev);
+ /*
+ * Disable the hardware clock stretching timeout. SMBUS
+ * specifies a limit for how long the device can stretch the
+ * clock, but core I2C doesn't.
+ */
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_CLKT, 0);
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
ret = i2c_add_adapter(adap);
if (ret)
- free_irq(i2c_dev->irq, i2c_dev);
+ goto err_free_irq;
+
+ return 0;
+
+err_free_irq:
+ free_irq(i2c_dev->irq, i2c_dev);
+err_disable_unprepare_clk:
+ clk_disable_unprepare(i2c_dev->bus_clk);
+err_put_exclusive_rate:
+ clk_rate_exclusive_put(i2c_dev->bus_clk);
return ret;
}
-static int bcm2835_i2c_remove(struct platform_device *pdev)
+static void bcm2835_i2c_remove(struct platform_device *pdev)
{
struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ clk_rate_exclusive_put(i2c_dev->bus_clk);
+ clk_disable_unprepare(i2c_dev->bus_clk);
+
free_irq(i2c_dev->irq, i2c_dev);
i2c_del_adapter(&i2c_dev->adapter);
-
- return 0;
}
static const struct of_device_id bcm2835_i2c_of_match[] = {
- { .compatible = "brcm,bcm2835-i2c" },
+ { .compatible = "brcm,bcm2711-i2c" },
+ { .compatible = "brcm,bcm2835-i2c", .data = &bcm2835_i2c_quirks },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
index 826d32049996..5fa30e8926c5 100644
--- a/drivers/i2c/busses/i2c-brcmstb.c
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2014 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014 Broadcom Corporation
#include <linux/clk.h>
#include <linux/delay.h>
@@ -22,7 +12,6 @@
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/version.h>
#define N_DATA_REGS 8
@@ -78,7 +67,7 @@
/* BSC block register map structure to cache fields to be written */
struct bsc_regs {
- u32 chip_address; /* slave address */
+ u32 chip_address; /* target address */
u32 data_in[N_DATA_REGS]; /* tx data buffer*/
u32 cnt_reg; /* rx/tx data length */
u32 ctl_reg; /* control register */
@@ -165,14 +154,13 @@ static const struct bsc_clk_param bsc_clk[] = {
struct brcmstb_i2c_dev {
struct device *device;
void __iomem *base;
- void __iomem *irq_base;
int irq;
struct bsc_regs *bsc_regmap;
struct i2c_adapter adapter;
struct completion done;
- bool is_suspended;
u32 clk_freq_hz;
int data_regsz;
+ bool atomic;
};
/* register accessors for both be and le cpu arch */
@@ -253,7 +241,7 @@ static int brcmstb_i2c_wait_for_completion(struct brcmstb_i2c_dev *dev)
int ret = 0;
unsigned long timeout = msecs_to_jiffies(I2C_TIMEOUT);
- if (dev->irq >= 0) {
+ if (dev->irq >= 0 && !dev->atomic) {
if (!wait_for_completion_timeout(&dev->done, timeout))
ret = -ETIMEDOUT;
} else {
@@ -300,7 +288,7 @@ static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev,
return rc;
/* only if we are in interrupt mode */
- if (dev->irq >= 0)
+ if (dev->irq >= 0 && !dev->atomic)
reinit_completion(&dev->done);
/* enable BSC CTL interrupt line */
@@ -318,7 +306,7 @@ static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev,
goto cmd_out;
}
- if ((CMD_RD || CMD_WR) &&
+ if ((cmd == CMD_RD || cmd == CMD_WR) &&
bsc_readl(dev, iic_enable) & BSC_IIC_EN_NOACK_MASK) {
rc = -EREMOTEIO;
dev_dbg(dev->device, "controller received NOACK intr for %s\n",
@@ -332,7 +320,7 @@ cmd_out:
return rc;
}
-/* Actual data transfer through the BSC master */
+/* Actual data transfer through the BSC controller */
static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
u8 *buf, unsigned int len,
struct i2c_msg *pmsg)
@@ -426,23 +414,22 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
if (msg->flags & I2C_M_TEN) {
/* First byte is 11110XX0 where XX is upper 2 bits */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7);
+ addr = i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD;
bsc_writel(dev, addr, chip_address);
/* Second byte is the remaining 8 bits */
- addr = msg->addr & 0xFF;
+ addr = i2c_10bit_addr_lo_from_msg(msg);
if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
return -EREMOTEIO;
if (msg->flags & I2C_M_RD) {
/* For read, send restart without stop condition */
- brcmstb_set_i2c_start_stop(dev, COND_RESTART
- | COND_NOSTOP);
+ brcmstb_set_i2c_start_stop(dev, COND_RESTART | COND_NOSTOP);
+
/* Then re-send the first byte with the read bit set */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01;
+ addr = i2c_10bit_addr_hi_from_msg(msg);
if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
return -EREMOTEIO;
-
}
} else {
addr = i2c_8bit_addr_from_msg(msg);
@@ -453,7 +440,6 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
return 0;
}
-/* Master transfer function */
static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
@@ -467,9 +453,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
int xfersz = brcmstb_i2c_get_xfersz(dev);
u32 cond, cond_per_msg;
- if (dev->is_suspended)
- return -EBUSY;
-
/* Loop through all messages */
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
@@ -488,7 +471,7 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
brcmstb_set_i2c_start_stop(dev, cond);
- /* Send slave address */
+ /* Send target address */
if (!(pmsg->flags & I2C_M_NOSTART)) {
rc = brcmstb_i2c_do_addr(dev, pmsg);
if (rc < 0) {
@@ -536,6 +519,23 @@ out:
}
+static int brcmstb_i2c_xfer_atomic(struct i2c_adapter *adapter,
+ struct i2c_msg msgs[], int num)
+{
+ struct brcmstb_i2c_dev *dev = i2c_get_adapdata(adapter);
+ int ret;
+
+ if (dev->irq >= 0)
+ disable_irq(dev->irq);
+ dev->atomic = true;
+ ret = brcmstb_i2c_xfer(adapter, msgs, num);
+ dev->atomic = false;
+ if (dev->irq >= 0)
+ enable_irq(dev->irq);
+
+ return ret;
+}
+
static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR
@@ -543,7 +543,8 @@ static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm brcmstb_i2c_algo = {
- .master_xfer = brcmstb_i2c_xfer,
+ .xfer = brcmstb_i2c_xfer,
+ .xfer_atomic = brcmstb_i2c_xfer_atomic,
.functionality = brcmstb_i2c_functionality,
};
@@ -585,13 +586,35 @@ static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
brcmstb_i2c_set_bus_speed(dev);
}
+#define AUTOI2C_CTRL0 0x26c
+#define AUTOI2C_CTRL0_RELEASE_BSC BIT(1)
+
+static int bcm2711_release_bsc(struct brcmstb_i2c_dev *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev->device);
+ void __iomem *autoi2c;
+
+ /* Map hardware registers */
+ autoi2c = devm_platform_ioremap_resource_byname(pdev, "auto-i2c");
+ if (IS_ERR(autoi2c))
+ return PTR_ERR(autoi2c);
+
+ writel(AUTOI2C_CTRL0_RELEASE_BSC, autoi2c + AUTOI2C_CTRL0);
+ devm_iounmap(&pdev->dev, autoi2c);
+
+ /* We need to reset the controller after the release */
+ dev->bsc_regmap->iic_enable = 0;
+ bsc_writel(dev, dev->bsc_regmap->iic_enable, iic_enable);
+
+ return 0;
+}
+
static int brcmstb_i2c_probe(struct platform_device *pdev)
{
- int rc = 0;
struct brcmstb_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *iomem;
const char *int_name;
+ int rc;
/* Allocate memory for private data structure */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -607,11 +630,15 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
init_completion(&dev->done);
/* Map hardware registers */
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->base = devm_ioremap_resource(dev->device, iomem);
- if (IS_ERR(dev->base)) {
- rc = -ENOMEM;
- goto probe_errorout;
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+
+ if (of_device_is_compatible(dev->device->of_node,
+ "brcm,bcm2711-hdmi-i2c")) {
+ rc = bcm2711_release_bsc(dev);
+ if (rc)
+ return rc;
}
rc = of_property_read_string(dev->device->of_node, "interrupt-names",
@@ -620,20 +647,22 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
int_name = NULL;
/* Get the interrupt number */
- dev->irq = platform_get_irq(pdev, 0);
+ dev->irq = platform_get_irq_optional(pdev, 0);
/* disable the bsc interrupt line */
brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
/* register the ISR handler */
- rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
- IRQF_SHARED,
- int_name ? int_name : pdev->name,
- dev);
-
- if (rc) {
- dev_dbg(dev->device, "falling back to polling mode");
- dev->irq = -1;
+ if (dev->irq >= 0) {
+ rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
+ IRQF_SHARED,
+ int_name ? int_name : pdev->name,
+ dev);
+
+ if (rc) {
+ dev_dbg(dev->device, "falling back to polling mode");
+ dev->irq = -1;
+ }
}
if (of_property_read_u32(dev->device->of_node,
@@ -645,7 +674,7 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
/* set the data in/out register size for compatible SoCs */
if (of_device_is_compatible(dev->device->of_node,
- "brcmstb,brcmper-i2c"))
+ "brcm,brcmper-i2c"))
dev->data_regsz = sizeof(u8);
else
dev->data_regsz = sizeof(u32);
@@ -656,43 +685,33 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- strlcpy(adap->name, "Broadcom STB : ", sizeof(adap->name));
- if (int_name)
- strlcat(adap->name, int_name, sizeof(adap->name));
+ strscpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
adap->algo = &brcmstb_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
rc = i2c_add_adapter(adap);
if (rc)
- goto probe_errorout;
+ return rc;
dev_info(dev->device, "%s@%dhz registered in %s mode\n",
int_name ? int_name : " ", dev->clk_freq_hz,
(dev->irq >= 0) ? "interrupt" : "polling");
return 0;
-
-probe_errorout:
- return rc;
}
-static int brcmstb_i2c_remove(struct platform_device *pdev)
+static void brcmstb_i2c_remove(struct platform_device *pdev)
{
struct brcmstb_i2c_dev *dev = platform_get_drvdata(pdev);
i2c_del_adapter(&dev->adapter);
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int brcmstb_i2c_suspend(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = true;
- i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
-
+ i2c_mark_adapter_suspended(&i2c_dev->adapter);
return 0;
}
@@ -700,21 +719,19 @@ static int brcmstb_i2c_resume(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
- i2c_dev->is_suspended = false;
- i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
+ i2c_mark_adapter_resumed(&i2c_dev->adapter);
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
- brcmstb_i2c_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
+ brcmstb_i2c_resume);
static const struct of_device_id brcmstb_i2c_of_match[] = {
{.compatible = "brcm,brcmstb-i2c"},
{.compatible = "brcm,brcmper-i2c"},
+ {.compatible = "brcm,bcm2711-hdmi-i2c"},
{},
};
MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
@@ -723,7 +740,7 @@ static struct platform_driver brcmstb_i2c_driver = {
.driver = {
.name = "brcmstb-i2c",
.of_match_table = brcmstb_i2c_of_match,
- .pm = &brcmstb_i2c_pm,
+ .pm = pm_sleep_ptr(&brcmstb_i2c_pm),
},
.probe = brcmstb_i2c_probe,
.remove = brcmstb_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index b13605718291..0fb728ade92e 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -1,13 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* I2C bus driver for the Cadence I2C controller.
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.
- *
- * This program is free software; you can redistribute it
- * and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation;
- * either version 2 of the License, or (at your option) any
- * later version.
*/
#include <linux/clk.h>
@@ -15,10 +10,13 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/reset.h>
/* Register offsets for the I2C device. */
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
@@ -28,6 +26,7 @@
#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */
#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */
#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */
+#define CDNS_I2C_IMR_OFFSET 0x20 /* IRQ Mask Register, RO */
#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */
#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */
@@ -45,9 +44,17 @@
#define CDNS_I2C_CR_DIVB_SHIFT 8
#define CDNS_I2C_CR_DIVB_MASK (0x3f << CDNS_I2C_CR_DIVB_SHIFT)
+#define CDNS_I2C_CR_MASTER_EN_MASK (CDNS_I2C_CR_NEA | \
+ CDNS_I2C_CR_ACK_EN | \
+ CDNS_I2C_CR_MS)
+
+#define CDNS_I2C_CR_SLAVE_EN_MASK ~CDNS_I2C_CR_MASTER_EN_MASK
+
/* Status Register Bit mask definitions */
#define CDNS_I2C_SR_BA BIT(8)
+#define CDNS_I2C_SR_TXDV BIT(6)
#define CDNS_I2C_SR_RXDV BIT(5)
+#define CDNS_I2C_SR_RXRW BIT(3)
/*
* I2C Address Register Bit mask definitions
@@ -96,32 +103,64 @@
CDNS_I2C_IXR_DATA | \
CDNS_I2C_IXR_COMP)
+#define CDNS_I2C_IXR_SLAVE_INTR_MASK (CDNS_I2C_IXR_RX_UNF | \
+ CDNS_I2C_IXR_TX_OVF | \
+ CDNS_I2C_IXR_RX_OVF | \
+ CDNS_I2C_IXR_TO | \
+ CDNS_I2C_IXR_NACK | \
+ CDNS_I2C_IXR_DATA | \
+ CDNS_I2C_IXR_COMP)
+
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
/* timeout for pm runtime autosuspend */
#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
-#define CDNS_I2C_FIFO_DEPTH 16
-/* FIFO depth at which the DATA interrupt occurs */
-#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2)
+#define CDNS_I2C_FIFO_DEPTH_DEFAULT 16
#define CDNS_I2C_MAX_TRANSFER_SIZE 255
/* Transfer size in multiples of data interrupt depth */
-#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
+#define CDNS_I2C_TRANSFER_SIZE(max) ((max) - 3)
#define DRIVER_NAME "cdns-i2c"
-#define CDNS_I2C_SPEED_MAX 400000
-#define CDNS_I2C_SPEED_DEFAULT 100000
-
#define CDNS_I2C_DIVA_MAX 4
#define CDNS_I2C_DIVB_MAX 64
#define CDNS_I2C_TIMEOUT_MAX 0xFF
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
+#define CDNS_I2C_POLL_US 100000
+#define CDNS_I2C_POLL_US_ATOMIC 10
+#define CDNS_I2C_TIMEOUT_US 500000
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+/**
+ * enum cdns_i2c_mode - I2C Controller current operating mode
+ *
+ * @CDNS_I2C_MODE_SLAVE: I2C controller operating in slave mode
+ * @CDNS_I2C_MODE_MASTER: I2C Controller operating in master mode
+ */
+enum cdns_i2c_mode {
+ CDNS_I2C_MODE_SLAVE,
+ CDNS_I2C_MODE_MASTER,
+};
+
+/**
+ * enum cdns_i2c_slave_state - Slave state when I2C is operating in slave mode
+ *
+ * @CDNS_I2C_SLAVE_STATE_IDLE: I2C slave idle
+ * @CDNS_I2C_SLAVE_STATE_SEND: I2C slave sending data to master
+ * @CDNS_I2C_SLAVE_STATE_RECV: I2C slave receiving data from master
+ */
+enum cdns_i2c_slave_state {
+ CDNS_I2C_SLAVE_STATE_IDLE,
+ CDNS_I2C_SLAVE_STATE_SEND,
+ CDNS_I2C_SLAVE_STATE_RECV,
+};
+#endif
+
/**
* struct cdns_i2c - I2C device private data structure
*
@@ -136,13 +175,23 @@
* @send_count: Number of bytes still expected to send
* @recv_count: Number of bytes still expected to receive
* @curr_recv_count: Number of bytes to be received in current transfer
- * @irq: IRQ number
* @input_clk: Input clock to I2C controller
* @i2c_clk: Maximum I2C clock speed
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
* @clk: Pointer to struct clk
* @clk_rate_change_nb: Notifier block for clock rate changes
+ * @reset: Reset control for the device
* @quirks: flag for broken hold bit usage in r1p10
+ * @ctrl_reg: Cached value of the control register.
+ * @rinfo: I2C GPIO recovery information
+ * @ctrl_reg_diva_divb: value of fields DIV_A and DIV_B from CR register
+ * @slave: Registered slave instance.
+ * @dev_mode: I2C operating role(master/slave).
+ * @slave_state: I2C Slave state(idle/read/write).
+ * @fifo_depth: The depth of the transfer FIFO
+ * @transfer_size: The maximum number of bytes in one transfer
+ * @atomic: Mode of transfer
+ * @err_status_atomic: Error status in atomic mode
*/
struct cdns_i2c {
struct device *dev;
@@ -156,13 +205,25 @@ struct cdns_i2c {
unsigned int send_count;
unsigned int recv_count;
unsigned int curr_recv_count;
- int irq;
unsigned long input_clk;
unsigned int i2c_clk;
unsigned int bus_hold_flag;
struct clk *clk;
struct notifier_block clk_rate_change_nb;
+ struct reset_control *reset;
u32 quirks;
+ u32 ctrl_reg;
+ struct i2c_bus_recovery_info rinfo;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ u16 ctrl_reg_diva_divb;
+ struct i2c_client *slave;
+ enum cdns_i2c_mode dev_mode;
+ enum cdns_i2c_slave_state slave_state;
+#endif
+ u32 fifo_depth;
+ unsigned int transfer_size;
+ bool atomic;
+ int err_status_atomic;
};
struct cdns_platform_data {
@@ -173,6 +234,66 @@ struct cdns_platform_data {
clk_rate_change_nb)
/**
+ * cdns_i2c_init - Controller initialisation
+ * @id: Device private data structure
+ *
+ * Initialise the i2c controller.
+ *
+ */
+static void cdns_i2c_init(struct cdns_i2c *id)
+{
+ cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
+ /*
+ * Cadence I2C controller has a bug wherein it generates
+ * invalid read transaction after HW timeout in master receiver mode.
+ * HW timeout is not used by this driver and the interrupt is disabled.
+ * But the feature itself cannot be disabled. Hence maximum value
+ * is written to this register to reduce the chances of error.
+ */
+ cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
+}
+
+/**
+ * cdns_i2c_runtime_suspend - Runtime suspend method for the driver
+ * @dev: Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ *
+ * Return: 0 always
+ */
+static int cdns_i2c_runtime_suspend(struct device *dev)
+{
+ struct cdns_i2c *xi2c = dev_get_drvdata(dev);
+
+ clk_disable(xi2c->clk);
+
+ return 0;
+}
+
+/**
+ * cdns_i2c_runtime_resume - Runtime resume
+ * @dev: Address of the platform_device structure
+ *
+ * Runtime resume callback.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int cdns_i2c_runtime_resume(struct device *dev)
+{
+ struct cdns_i2c *xi2c = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(xi2c->clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+ cdns_i2c_init(xi2c);
+
+ return 0;
+}
+
+/**
* cdns_i2c_clear_bus_hold - Clear bus hold bit
* @id: Pointer to driver data struct
*
@@ -188,24 +309,162 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround)
{
return (hold_wrkaround &&
- (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1));
+ (id->curr_recv_count == id->fifo_depth + 1));
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void cdns_i2c_set_mode(enum cdns_i2c_mode mode, struct cdns_i2c *id)
+{
+ /* Disable all interrupts */
+ cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
+
+ /* Clear FIFO and transfer size */
+ cdns_i2c_writereg(CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
+
+ /* Update device mode and state */
+ id->dev_mode = mode;
+ id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
+
+ switch (mode) {
+ case CDNS_I2C_MODE_MASTER:
+ /* Enable i2c master */
+ cdns_i2c_writereg(id->ctrl_reg_diva_divb |
+ CDNS_I2C_CR_MASTER_EN_MASK,
+ CDNS_I2C_CR_OFFSET);
+ /*
+ * This delay is needed to give the IP some time to switch to
+ * the master mode. With lower values(like 110 us) i2cdetect
+ * will not detect any slave and without this delay, the IP will
+ * trigger a timeout interrupt.
+ */
+ usleep_range(115, 125);
+ break;
+ case CDNS_I2C_MODE_SLAVE:
+ /* Enable i2c slave */
+ cdns_i2c_writereg(id->ctrl_reg_diva_divb &
+ CDNS_I2C_CR_SLAVE_EN_MASK,
+ CDNS_I2C_CR_OFFSET);
+
+ /* Setting slave address */
+ cdns_i2c_writereg(id->slave->addr & CDNS_I2C_ADDR_MASK,
+ CDNS_I2C_ADDR_OFFSET);
+
+ /* Enable slave send/receive interrupts */
+ cdns_i2c_writereg(CDNS_I2C_IXR_SLAVE_INTR_MASK,
+ CDNS_I2C_IER_OFFSET);
+ break;
+ }
+}
+
+static void cdns_i2c_slave_rcv_data(struct cdns_i2c *id)
+{
+ u8 bytes;
+ unsigned char data;
+
+ /* Prepare backend for data reception */
+ if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
+ id->slave_state = CDNS_I2C_SLAVE_STATE_RECV;
+ i2c_slave_event(id->slave, I2C_SLAVE_WRITE_REQUESTED, NULL);
+ }
+
+ /* Fetch number of bytes to receive */
+ bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+
+ /* Read data and send to backend */
+ while (bytes--) {
+ data = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
+ i2c_slave_event(id->slave, I2C_SLAVE_WRITE_RECEIVED, &data);
+ }
+}
+
+static void cdns_i2c_slave_send_data(struct cdns_i2c *id)
+{
+ u8 data;
+
+ /* Prepare backend for data transmission */
+ if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
+ id->slave_state = CDNS_I2C_SLAVE_STATE_SEND;
+ i2c_slave_event(id->slave, I2C_SLAVE_READ_REQUESTED, &data);
+ } else {
+ i2c_slave_event(id->slave, I2C_SLAVE_READ_PROCESSED, &data);
+ }
+
+ /* Send data over bus */
+ cdns_i2c_writereg(data, CDNS_I2C_DATA_OFFSET);
}
/**
- * cdns_i2c_isr - Interrupt handler for the I2C device
- * @irq: irq number for the I2C device
- * @ptr: void pointer to cdns_i2c structure
+ * cdns_i2c_slave_isr - Interrupt handler for the I2C device in slave role
+ * @ptr: Pointer to I2C device private data
+ *
+ * This function handles the data interrupt and transfer complete interrupt of
+ * the I2C device in slave role.
+ *
+ * Return: IRQ_HANDLED always
+ */
+static irqreturn_t cdns_i2c_slave_isr(void *ptr)
+{
+ struct cdns_i2c *id = ptr;
+ unsigned int isr_status, i2c_status;
+
+ /* Fetch the interrupt status */
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+
+ /* Ignore masked interrupts */
+ isr_status &= ~cdns_i2c_readreg(CDNS_I2C_IMR_OFFSET);
+
+ /* Fetch transfer mode (send/receive) */
+ i2c_status = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
+
+ /* Handle data send/receive */
+ if (i2c_status & CDNS_I2C_SR_RXRW) {
+ /* Send data to master */
+ if (isr_status & CDNS_I2C_IXR_DATA)
+ cdns_i2c_slave_send_data(id);
+
+ if (isr_status & CDNS_I2C_IXR_COMP) {
+ id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
+ i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
+ }
+ } else {
+ /* Receive data from master */
+ if (isr_status & CDNS_I2C_IXR_DATA)
+ cdns_i2c_slave_rcv_data(id);
+
+ if (isr_status & CDNS_I2C_IXR_COMP) {
+ cdns_i2c_slave_rcv_data(id);
+ id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
+ i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
+ }
+ }
+
+ /* Master indicated xfer stop or fifo underflow/overflow */
+ if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_RX_OVF |
+ CDNS_I2C_IXR_RX_UNF | CDNS_I2C_IXR_TX_OVF)) {
+ id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
+ i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
+ cdns_i2c_writereg(CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
+/**
+ * cdns_i2c_master_isr - Interrupt handler for the I2C device in master role
+ * @ptr: Pointer to I2C device private data
*
* This function handles the data interrupt, transfer complete interrupt and
- * the error interrupts of the I2C device.
+ * the error interrupts of the I2C device in master role.
*
* Return: IRQ_HANDLED always
*/
-static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
+static irqreturn_t cdns_i2c_master_isr(void *ptr)
{
- unsigned int isr_status, avail_bytes, updatetx;
+ unsigned int isr_status, avail_bytes;
unsigned int bytes_to_send;
- bool hold_quirk;
+ bool updatetx;
struct cdns_i2c *id = ptr;
/* Signal completion only after everything is updated */
int done_flag = 0;
@@ -213,6 +472,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+ id->err_status = 0;
/* Handling nack and arbitration lost interrupt */
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
@@ -224,11 +484,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
* Check if transfer size register needs to be updated again for a
* large data receive operation.
*/
- updatetx = 0;
- if (id->recv_count > id->curr_recv_count)
- updatetx = 1;
-
- hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
+ updatetx = id->recv_count > id->curr_recv_count;
/* When receiving, handle data interrupt and completion interrupt */
if (id->p_recv_buf &&
@@ -237,21 +493,29 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
/* Read data if receive data valid is set */
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
CDNS_I2C_SR_RXDV) {
- /*
- * Clear hold bit that was set for FIFO control if
- * RX data left is less than FIFO depth, unless
- * repeated start is selected.
- */
- if ((id->recv_count < CDNS_I2C_FIFO_DEPTH) &&
- !id->bus_hold_flag)
- cdns_i2c_clear_bus_hold(id);
+ if (id->recv_count > 0) {
+ *(id->p_recv_buf)++ =
+ cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
+ id->recv_count--;
+ id->curr_recv_count--;
+
+ /*
+ * Clear hold bit that was set for FIFO control
+ * if RX data left is less than or equal to
+ * FIFO DEPTH unless repeated start is selected
+ */
+ if (id->recv_count <= id->fifo_depth &&
+ !id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
- *(id->p_recv_buf)++ =
- cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
- id->recv_count--;
- id->curr_recv_count--;
+ } else {
+ dev_err(id->adap.dev.parent,
+ "xfer_size reg rollover. xfer aborted!\n");
+ id->err_status |= CDNS_I2C_IXR_TO;
+ break;
+ }
- if (cdns_is_holdquirk(id, hold_quirk))
+ if (cdns_is_holdquirk(id, updatetx))
break;
}
@@ -262,44 +526,28 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
* maintain transfer size non-zero while performing a large
* receive operation.
*/
- if (cdns_is_holdquirk(id, hold_quirk)) {
+ if (cdns_is_holdquirk(id, updatetx)) {
/* wait while fifo is full */
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
- (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
+ (id->curr_recv_count - id->fifo_depth))
;
/*
* Check number of bytes to be received against maximum
* transfer size and update register accordingly.
*/
- if (((int)(id->recv_count) - CDNS_I2C_FIFO_DEPTH) >
- CDNS_I2C_TRANSFER_SIZE) {
- cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ if (((int)(id->recv_count) - id->fifo_depth) >
+ id->transfer_size) {
+ cdns_i2c_writereg(id->transfer_size,
CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
- CDNS_I2C_FIFO_DEPTH;
+ id->curr_recv_count = id->transfer_size +
+ id->fifo_depth;
} else {
cdns_i2c_writereg(id->recv_count -
- CDNS_I2C_FIFO_DEPTH,
+ id->fifo_depth,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = id->recv_count;
}
- } else if (id->recv_count && !hold_quirk &&
- !id->curr_recv_count) {
-
- /* Set the slave address in address register*/
- cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
- CDNS_I2C_ADDR_OFFSET);
-
- if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
- cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
- CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
- } else {
- cdns_i2c_writereg(id->recv_count,
- CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = id->recv_count;
- }
}
/* Clear hold (if not repeated start) and signal completion */
@@ -319,7 +567,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
* space available in FIFO and fill with that many bytes.
*/
if (id->send_count) {
- avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ avail_bytes = id->fifo_depth -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
bytes_to_send = avail_bytes;
@@ -347,7 +595,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
}
/* Update the status for errors */
- id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
+ id->err_status |= isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
if (id->err_status)
status = IRQ_HANDLED;
@@ -358,6 +606,110 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
}
/**
+ * cdns_i2c_isr - Interrupt handler for the I2C device
+ * @irq: irq number for the I2C device
+ * @ptr: void pointer to cdns_i2c structure
+ *
+ * This function passes the control to slave/master based on current role of
+ * i2c controller.
+ *
+ * Return: IRQ_HANDLED always
+ */
+static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
+{
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ struct cdns_i2c *id = ptr;
+
+ if (id->dev_mode == CDNS_I2C_MODE_SLAVE)
+ return cdns_i2c_slave_isr(ptr);
+#endif
+ return cdns_i2c_master_isr(ptr);
+}
+
+static bool cdns_i2c_error_check(struct cdns_i2c *id)
+{
+ unsigned int isr_status;
+
+ id->err_status = 0;
+
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(isr_status & CDNS_I2C_IXR_ERR_INTR_MASK, CDNS_I2C_ISR_OFFSET);
+
+ id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
+
+ return !!id->err_status;
+}
+
+static void cdns_i2c_mrecv_atomic(struct cdns_i2c *id)
+{
+ while (id->recv_count > 0) {
+ bool updatetx;
+
+ /*
+ * Check if transfer size register needs to be updated again for a
+ * large data receive operation.
+ */
+ updatetx = id->recv_count > id->curr_recv_count;
+
+ while (id->curr_recv_count > 0) {
+ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) {
+ *id->p_recv_buf = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
+ id->p_recv_buf++;
+ id->recv_count--;
+ id->curr_recv_count--;
+
+ /*
+ * Clear the hold bit that was set for FIFO control,
+ * if the remaining RX data is less than or equal to
+ * the FIFO depth, unless a repeated start is selected.
+ */
+ if (id->recv_count <= id->fifo_depth && !id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
+ }
+ if (cdns_i2c_error_check(id))
+ return;
+ if (cdns_is_holdquirk(id, updatetx))
+ break;
+ }
+
+ /*
+ * The controller sends NACK to the slave/target when transfer size
+ * register reaches zero without considering the HOLD bit.
+ * This workaround is implemented for large data transfers to
+ * maintain transfer size non-zero while performing a large
+ * receive operation.
+ */
+ if (cdns_is_holdquirk(id, updatetx)) {
+ /* wait while fifo is full */
+ while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
+ (id->curr_recv_count - id->fifo_depth))
+ ;
+
+ /*
+ * Check number of bytes to be received against maximum
+ * transfer size and update register accordingly.
+ */
+ if ((id->recv_count - id->fifo_depth) >
+ id->transfer_size) {
+ cdns_i2c_writereg(id->transfer_size,
+ CDNS_I2C_XFER_SIZE_OFFSET);
+ id->curr_recv_count = id->transfer_size +
+ id->fifo_depth;
+ } else {
+ cdns_i2c_writereg(id->recv_count -
+ id->fifo_depth,
+ CDNS_I2C_XFER_SIZE_OFFSET);
+ id->curr_recv_count = id->recv_count;
+ }
+ }
+ }
+
+ /* Clear hold (if not repeated start) */
+ if (!id->recv_count && !id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
+}
+
+/**
* cdns_i2c_mrecv - Prepare and start a master receive operation
* @id: pointer to the i2c device structure
*/
@@ -365,6 +717,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
{
unsigned int ctrl_reg;
unsigned int isr_status;
+ unsigned long flags;
+ bool hold_clear = false;
+ bool irq_save = false;
+
+ u32 addr;
id->p_recv_buf = id->p_msg->buf;
id->recv_count = id->p_msg->len;
@@ -373,8 +730,13 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
+ /*
+ * Receive up to I2C_SMBUS_BLOCK_MAX data bytes, plus one message length
+ * byte, plus one checksum byte if PEC is enabled. p_msg->len will be 2 if
+ * PEC is enabled, otherwise 1.
+ */
if (id->p_msg->flags & I2C_M_RECV_LEN)
- id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
+ id->recv_count = I2C_SMBUS_BLOCK_MAX + id->p_msg->len;
id->curr_recv_count = id->recv_count;
@@ -382,7 +744,7 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
- if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
+ if (id->recv_count > id->fifo_depth)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
@@ -397,23 +759,78 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
* receive if it is less than transfer size and transfer size if
* it is more. Enable the interrupts.
*/
- if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
- cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ if (id->recv_count > id->transfer_size) {
+ cdns_i2c_writereg(id->transfer_size,
CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
+ id->curr_recv_count = id->transfer_size;
} else {
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
}
- /* Set the slave address in address register - triggers operation */
- cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
- CDNS_I2C_ADDR_OFFSET);
- /* Clear the bus hold flag if bytes to receive is less than FIFO size */
- if (!id->bus_hold_flag &&
- ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
- (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
- cdns_i2c_clear_bus_hold(id);
- cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+ /* Determine hold_clear based on number of bytes to receive and hold flag */
+ if (!id->bus_hold_flag && id->recv_count <= id->fifo_depth) {
+ if (ctrl_reg & CDNS_I2C_CR_HOLD) {
+ hold_clear = true;
+ if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
+ irq_save = true;
+ }
+ }
+
+ addr = id->p_msg->addr;
+ addr &= CDNS_I2C_ADDR_MASK;
+
+ if (hold_clear) {
+ ctrl_reg &= ~CDNS_I2C_CR_HOLD;
+ ctrl_reg &= ~CDNS_I2C_CR_CLR_FIFO;
+ /*
+ * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
+ * register reaches '0'. This is an IP bug which causes transfer size
+ * register overflow to 0xFF. To satisfy this timing requirement,
+ * disable the interrupts on current processor core between register
+ * writes to slave address register and control register.
+ */
+ if (irq_save)
+ local_irq_save(flags);
+
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+ /* Read it back to avoid bufferring and make sure write happens */
+ cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+ if (irq_save)
+ local_irq_restore(flags);
+ } else {
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ }
+
+ if (!id->atomic)
+ cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+ else
+ cdns_i2c_mrecv_atomic(id);
+}
+
+static void cdns_i2c_msend_rem_atomic(struct cdns_i2c *id)
+{
+ while (id->send_count) {
+ unsigned int avail_bytes;
+ unsigned int bytes_to_send;
+
+ avail_bytes = id->fifo_depth - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+ if (id->send_count > avail_bytes)
+ bytes_to_send = avail_bytes;
+ else
+ bytes_to_send = id->send_count;
+
+ while (bytes_to_send--) {
+ cdns_i2c_writereg((*id->p_send_buf++), CDNS_I2C_DATA_OFFSET);
+ id->send_count--;
+ }
+ if (cdns_i2c_error_check(id))
+ return;
+ }
+
+ if (!id->send_count && !id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
}
/**
@@ -440,7 +857,7 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
- if (id->send_count > CDNS_I2C_FIFO_DEPTH)
+ if (id->send_count > id->fifo_depth)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
@@ -453,7 +870,7 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
* against the space available, and fill the FIFO accordingly.
* Enable the interrupts.
*/
- avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ avail_bytes = id->fifo_depth -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
@@ -476,7 +893,10 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
- cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+ if (!id->atomic)
+ cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+ else if (id->send_count > 0)
+ cdns_i2c_msend_rem_atomic(id);
}
/**
@@ -500,7 +920,7 @@ static void cdns_i2c_master_reset(struct i2c_adapter *adap)
cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
/* Update the transfercount register to zero */
cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
- /* Clear the interupt status register */
+ /* Clear the interrupt status register */
regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET);
/* Clear the status register */
@@ -511,12 +931,13 @@ static void cdns_i2c_master_reset(struct i2c_adapter *adap)
static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
struct i2c_adapter *adap)
{
- unsigned long time_left;
+ unsigned long time_left, msg_timeout;
u32 reg;
id->p_msg = msg;
id->err_status = 0;
- reinit_completion(&id->xfer_done);
+ if (!id->atomic)
+ reinit_completion(&id->xfer_done);
/* Check for the TEN Bit mode on each msg */
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
@@ -536,12 +957,35 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
else
cdns_i2c_msend(id);
- /* Wait for the signal of completion */
- time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout);
+ /* Minimal time to execute this message */
+ msg_timeout = msecs_to_jiffies((1000 * msg->len * BITS_PER_BYTE) / id->i2c_clk);
+
+ /*
+ * Plus some wiggle room.
+ * For non-atomic contexts, 500 ms is added to the timeout.
+ * For atomic contexts, 2000 ms is added because transfers happen in polled
+ * mode, requiring more time to account for the polling overhead.
+ */
+ if (!id->atomic)
+ msg_timeout += msecs_to_jiffies(500);
+ else
+ msg_timeout += msecs_to_jiffies(2000);
+
+ if (msg_timeout < adap->timeout)
+ msg_timeout = adap->timeout;
+
+ if (!id->atomic) {
+ /* Wait for the signal of completion */
+ time_left = wait_for_completion_timeout(&id->xfer_done, msg_timeout);
+ } else {
+ /* 0 is success, -ETIMEDOUT is error */
+ time_left = !readl_poll_timeout_atomic(id->membase + CDNS_I2C_ISR_OFFSET,
+ reg, (reg & CDNS_I2C_IXR_COMP),
+ CDNS_I2C_POLL_US_ATOMIC, msg_timeout);
+ }
+
if (time_left == 0) {
cdns_i2c_master_reset(adap);
- dev_err(id->adap.dev.parent,
- "timeout waiting on completion\n");
return -ETIMEDOUT;
}
@@ -552,34 +996,37 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
if (id->err_status & CDNS_I2C_IXR_ARB_LOST)
return -EAGAIN;
+ if (msg->flags & I2C_M_RECV_LEN)
+ msg->len += min_t(unsigned int, msg->buf[0], I2C_SMBUS_BLOCK_MAX);
+
return 0;
}
-/**
- * cdns_i2c_master_xfer - The main i2c transfer function
- * @adap: pointer to the i2c adapter driver instance
- * @msgs: pointer to the i2c message structure
- * @num: the number of messages to transfer
- *
- * Initiates the send/recv activity based on the transfer message received.
- *
- * Return: number of msgs processed on success, negative error otherwise
- */
-static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
+static int cdns_i2c_master_common_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
{
int ret, count;
u32 reg;
struct cdns_i2c *id = adap->algo_data;
bool hold_quirk;
- ret = pm_runtime_get_sync(id->dev);
- if (ret < 0)
- return ret;
/* Check if the bus is free */
- if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) {
+ if (!id->atomic)
+ ret = readl_relaxed_poll_timeout(id->membase + CDNS_I2C_SR_OFFSET,
+ reg,
+ !(reg & CDNS_I2C_SR_BA),
+ CDNS_I2C_POLL_US, CDNS_I2C_TIMEOUT_US);
+ else
+ ret = readl_poll_timeout_atomic(id->membase + CDNS_I2C_SR_OFFSET,
+ reg,
+ !(reg & CDNS_I2C_SR_BA),
+ CDNS_I2C_POLL_US_ATOMIC, CDNS_I2C_TIMEOUT_US);
+ if (ret) {
ret = -EAGAIN;
- goto out;
+ if (id->adap.bus_recovery_info)
+ i2c_recover_bus(adap);
+ return ret;
}
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
@@ -599,8 +1046,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (msgs[count].flags & I2C_M_RD) {
dev_warn(adap->dev.parent,
"Can't do repeated start after a receive message\n");
- ret = -EOPNOTSUPP;
- goto out;
+ return -EOPNOTSUPP;
}
}
id->bus_hold_flag = 1;
@@ -618,29 +1064,110 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
ret = cdns_i2c_process_msg(id, msgs, adap);
if (ret)
- goto out;
+ return ret;
/* Report the other error interrupts to application */
- if (id->err_status) {
+ if (id->err_status || id->err_status_atomic) {
cdns_i2c_master_reset(adap);
- if (id->err_status & CDNS_I2C_IXR_NACK) {
- ret = -ENXIO;
- goto out;
- }
- ret = -EIO;
+ if (id->err_status & CDNS_I2C_IXR_NACK)
+ return -ENXIO;
+
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/**
+ * cdns_i2c_master_xfer - The main i2c transfer function
+ * @adap: pointer to the i2c adapter driver instance
+ * @msgs: pointer to the i2c message structure
+ * @num: the number of messages to transfer
+ *
+ * Initiates the send/recv activity based on the transfer message received.
+ *
+ * Return: number of msgs processed on success, negative error otherwise
+ */
+static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ int ret;
+ struct cdns_i2c *id = adap->algo_data;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ bool change_role = false;
+#endif
+
+ ret = pm_runtime_resume_and_get(id->dev);
+ if (ret < 0)
+ return ret;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* Check i2c operating mode and switch if possible */
+ if (id->dev_mode == CDNS_I2C_MODE_SLAVE) {
+ if (id->slave_state != CDNS_I2C_SLAVE_STATE_IDLE) {
+ ret = -EAGAIN;
goto out;
}
+
+ /* Set mode to master */
+ cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
+
+ /* Mark flag to change role once xfer is completed */
+ change_role = true;
}
+#endif
- ret = num;
+ ret = cdns_i2c_master_common_xfer(adap, msgs, num);
+ if (!ret)
+ ret = num;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
out:
- pm_runtime_mark_last_busy(id->dev);
+ /* Switch i2c mode to slave */
+ if (change_role)
+ cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, id);
+#endif
+
pm_runtime_put_autosuspend(id->dev);
return ret;
}
/**
+ * cdns_i2c_master_xfer_atomic - The i2c transfer function in atomic mode
+ * @adap: pointer to the i2c adapter driver instance
+ * @msgs: pointer to the i2c message structure
+ * @num: the number of messages to transfer
+ *
+ * Return: number of msgs processed on success, negative error otherwise
+ */
+static int cdns_i2c_master_xfer_atomic(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ int ret;
+ struct cdns_i2c *id = adap->algo_data;
+
+ ret = cdns_i2c_runtime_resume(id->dev);
+ if (ret)
+ return ret;
+
+ if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) {
+ dev_warn(id->adap.dev.parent,
+ "Atomic xfer not supported for version 1.0\n");
+ return 0;
+ }
+
+ id->atomic = true;
+ ret = cdns_i2c_master_common_xfer(adap, msgs, num);
+ if (!ret)
+ ret = num;
+
+ id->atomic = false;
+ cdns_i2c_runtime_suspend(id->dev);
+
+ return ret;
+}
+
+/**
* cdns_i2c_func - Returns the supported features of the I2C driver
* @adap: pointer to the i2c adapter structure
*
@@ -648,14 +1175,68 @@ out:
*/
static u32 cdns_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
- (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
- I2C_FUNC_SMBUS_BLOCK_DATA;
+ u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
+ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+ I2C_FUNC_SMBUS_BLOCK_DATA;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ func |= I2C_FUNC_SLAVE;
+#endif
+
+ return func;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int cdns_reg_slave(struct i2c_client *slave)
+{
+ int ret;
+ struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
+ adap);
+
+ if (id->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ ret = pm_runtime_resume_and_get(id->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Store slave information */
+ id->slave = slave;
+
+ /* Enable I2C slave */
+ cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, id);
+
+ return 0;
+}
+
+static int cdns_unreg_slave(struct i2c_client *slave)
+{
+ struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
+ adap);
+
+ pm_runtime_put(id->dev);
+
+ /* Remove slave information */
+ id->slave = NULL;
+
+ /* Enable I2C master */
+ cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
+
+ return 0;
}
+#endif
static const struct i2c_algorithm cdns_i2c_algo = {
- .master_xfer = cdns_i2c_master_xfer,
- .functionality = cdns_i2c_func,
+ .xfer = cdns_i2c_master_xfer,
+ .xfer_atomic = cdns_i2c_master_xfer_atomic,
+ .functionality = cdns_i2c_func,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = cdns_reg_slave,
+ .unreg_slave = cdns_unreg_slave,
+#endif
};
/**
@@ -700,8 +1281,7 @@ static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
if (actual_fscl > fscl)
continue;
- current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
- (fscl - actual_fscl));
+ current_error = fscl - actual_fscl;
if (last_error > current_error) {
calc_div_a = div_a;
@@ -745,12 +1325,16 @@ static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
if (ret)
return ret;
- ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ ctrl_reg = id->ctrl_reg;
ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
(div_b << CDNS_I2C_CR_DIVB_SHIFT));
+ id->ctrl_reg = ctrl_reg;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
-
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ id->ctrl_reg_diva_divb = ctrl_reg & (CDNS_I2C_CR_DIVA_MASK |
+ CDNS_I2C_CR_DIVB_MASK);
+#endif
return 0;
}
@@ -816,46 +1400,40 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
}
}
-/**
- * cdns_i2c_runtime_suspend - Runtime suspend method for the driver
- * @dev: Address of the platform_device structure
- *
- * Put the driver into low power mode.
- *
- * Return: 0 always
- */
-static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
+static int __maybe_unused cdns_i2c_suspend(struct device *dev)
{
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
- clk_disable(xi2c->clk);
+ i2c_mark_adapter_suspended(&xi2c->adap);
+
+ if (!pm_runtime_status_suspended(dev))
+ return cdns_i2c_runtime_suspend(dev);
return 0;
}
-/**
- * cdns_i2c_runtime_resume - Runtime resume
- * @dev: Address of the platform_device structure
- *
- * Runtime resume callback.
- *
- * Return: 0 on success and error value on error
- */
-static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
+static int __maybe_unused cdns_i2c_resume(struct device *dev)
{
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
- int ret;
+ int err;
- ret = clk_enable(xi2c->clk);
- if (ret) {
- dev_err(dev, "Cannot enable clock.\n");
- return ret;
+ err = cdns_i2c_runtime_resume(dev);
+ if (err)
+ return err;
+
+ if (pm_runtime_status_suspended(dev)) {
+ err = cdns_i2c_runtime_suspend(dev);
+ if (err)
+ return err;
}
+ i2c_mark_adapter_resumed(&xi2c->adap);
+
return 0;
}
static const struct dev_pm_ops cdns_i2c_dev_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_i2c_suspend, cdns_i2c_resume)
SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
cdns_i2c_runtime_resume, NULL)
};
@@ -872,6 +1450,37 @@ static const struct of_device_id cdns_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
/**
+ * cdns_i2c_detect_transfer_size - Detect the maximum transfer size supported
+ * @id: Device private data structure
+ *
+ * Detect the maximum transfer size that is supported by this instance of the
+ * Cadence I2C controller.
+ */
+static void cdns_i2c_detect_transfer_size(struct cdns_i2c *id)
+{
+ u32 val;
+
+ /*
+ * Writing to the transfer size register is only possible if these two bits
+ * are set in the control register.
+ */
+ cdns_i2c_writereg(CDNS_I2C_CR_MS | CDNS_I2C_CR_RW, CDNS_I2C_CR_OFFSET);
+
+ /*
+ * The number of writable bits of the transfer size register can be between
+ * 4 and 8. This is a controlled through a synthesis parameter of the IP
+ * core and can vary from instance to instance. The unused MSBs always read
+ * back as 0. Writing 0xff and then reading the value back will report the
+ * maximum supported transfer size.
+ */
+ cdns_i2c_writereg(CDNS_I2C_MAX_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET);
+ val = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+ id->transfer_size = CDNS_I2C_TRANSFER_SIZE(val);
+ cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
+ cdns_i2c_writereg(0, CDNS_I2C_CR_OFFSET);
+}
+
+/**
* cdns_i2c_probe - Platform registration call
* @pdev: Handle to the platform device structure
*
@@ -885,7 +1494,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
{
struct resource *r_mem;
struct cdns_i2c *id;
- int ret;
+ int ret, irq;
const struct of_device_id *match;
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
@@ -901,12 +1510,24 @@ static int cdns_i2c_probe(struct platform_device *pdev)
id->quirks = data->quirks;
}
- r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
+ id->rinfo.pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(id->rinfo.pinctrl)) {
+ int err = PTR_ERR(id->rinfo.pinctrl);
+
+ dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n");
+ if (err != -ENODEV)
+ return err;
+ } else {
+ id->adap.bus_recovery_info = &id->rinfo;
+ }
+
+ id->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &r_mem);
if (IS_ERR(id->membase))
return PTR_ERR(id->membase);
- id->irq = platform_get_irq(pdev, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
id->adap.owner = THIS_MODULE;
id->adap.dev.of_node = pdev->dev.of_node;
@@ -919,19 +1540,25 @@ static int cdns_i2c_probe(struct platform_device *pdev)
snprintf(id->adap.name, sizeof(id->adap.name),
"Cadence I2C at %08lx", (unsigned long)r_mem->start);
- id->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(id->clk)) {
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(id->clk);
- }
- ret = clk_prepare_enable(id->clk);
+ id->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(id->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(id->clk),
+ "input clock not found.\n");
+
+ id->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(id->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(id->reset),
+ "Failed to request reset.\n");
+
+ ret = reset_control_deassert(id->reset);
if (ret)
- dev_err(&pdev->dev, "Unable to enable clock.\n");
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to de-assert reset.\n");
- pm_runtime_enable(id->dev);
pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(id->dev);
pm_runtime_set_active(id->dev);
+ pm_runtime_enable(id->dev);
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
@@ -940,48 +1567,50 @@ static int cdns_i2c_probe(struct platform_device *pdev)
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&id->i2c_clk);
- if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))
- id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
+ if (ret || (id->i2c_clk > I2C_MAX_FAST_MODE_FREQ))
+ id->i2c_clk = I2C_MAX_STANDARD_MODE_FREQ;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* Set initial mode to master */
+ id->dev_mode = CDNS_I2C_MODE_MASTER;
+ id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
+#endif
+ id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
+
+ id->fifo_depth = CDNS_I2C_FIFO_DEPTH_DEFAULT;
+ of_property_read_u32(pdev->dev.of_node, "fifo-depth", &id->fifo_depth);
- cdns_i2c_writereg(CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS,
- CDNS_I2C_CR_OFFSET);
+ cdns_i2c_detect_transfer_size(id);
ret = cdns_i2c_setclk(id->input_clk, id);
if (ret) {
dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
ret = -EINVAL;
- goto err_clk_dis;
+ goto err_clk_notifier_unregister;
}
- ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
+ ret = devm_request_irq(&pdev->dev, irq, cdns_i2c_isr, 0,
DRIVER_NAME, id);
if (ret) {
- dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
- goto err_clk_dis;
+ dev_err(&pdev->dev, "cannot get irq %d\n", irq);
+ goto err_clk_notifier_unregister;
}
-
- /*
- * Cadence I2C controller has a bug wherein it generates
- * invalid read transaction after HW timeout in master receiver mode.
- * HW timeout is not used by this driver and the interrupt is disabled.
- * But the feature itself cannot be disabled. Hence maximum value
- * is written to this register to reduce the chances of error.
- */
- cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
+ cdns_i2c_init(id);
ret = i2c_add_adapter(&id->adap);
if (ret < 0)
- goto err_clk_dis;
+ goto err_clk_notifier_unregister;
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
- id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
+ id->i2c_clk / 1000, (unsigned long)r_mem->start, irq);
return 0;
-err_clk_dis:
- clk_disable_unprepare(id->clk);
- pm_runtime_set_suspended(&pdev->dev);
+err_clk_notifier_unregister:
+ clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ reset_control_assert(id->reset);
return ret;
}
@@ -993,16 +1622,17 @@ err_clk_dis:
*
* Return: 0 always
*/
-static int cdns_i2c_remove(struct platform_device *pdev)
+static void cdns_i2c_remove(struct platform_device *pdev)
{
struct cdns_i2c *id = platform_get_drvdata(pdev);
- i2c_del_adapter(&id->adap);
- clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
- clk_disable_unprepare(id->clk);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
- return 0;
+ i2c_del_adapter(&id->adap);
+ clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
+ reset_control_assert(id->reset);
}
static struct platform_driver cdns_i2c_drv = {
diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c
index b4f91e48948a..8065c7e4462e 100644
--- a/drivers/i2c/busses/i2c-cbus-gpio.c
+++ b/drivers/i2c/busses/i2c-cbus-gpio.c
@@ -18,16 +18,14 @@
#include <linux/io.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/i2c-cbus-gpio.h>
/*
* Bit counts are derived from Nokia implementation. These should be checked
@@ -39,9 +37,9 @@
struct cbus_host {
spinlock_t lock; /* host lock */
struct device *dev;
- int clk_gpio;
- int dat_gpio;
- int sel_gpio;
+ struct gpio_desc *clk;
+ struct gpio_desc *dat;
+ struct gpio_desc *sel;
};
/**
@@ -51,9 +49,9 @@ struct cbus_host {
*/
static void cbus_send_bit(struct cbus_host *host, unsigned bit)
{
- gpio_set_value(host->dat_gpio, bit ? 1 : 0);
- gpio_set_value(host->clk_gpio, 1);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->dat, bit ? 1 : 0);
+ gpiod_set_value(host->clk, 1);
+ gpiod_set_value(host->clk, 0);
}
/**
@@ -78,9 +76,9 @@ static int cbus_receive_bit(struct cbus_host *host)
{
int ret;
- gpio_set_value(host->clk_gpio, 1);
- ret = gpio_get_value(host->dat_gpio);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->clk, 1);
+ ret = gpiod_get_value(host->dat);
+ gpiod_set_value(host->clk, 0);
return ret;
}
@@ -123,10 +121,10 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
spin_lock_irqsave(&host->lock, flags);
/* Reset state and start of transfer, SEL stays down during transfer */
- gpio_set_value(host->sel_gpio, 0);
+ gpiod_set_value(host->sel, 0);
/* Set the DAT pin to output */
- gpio_direction_output(host->dat_gpio, 1);
+ gpiod_direction_output(host->dat, 1);
/* Send the device address */
cbus_send_data(host, dev, CBUS_ADDR_BITS);
@@ -141,12 +139,12 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
cbus_send_data(host, data, 16);
ret = 0;
} else {
- ret = gpio_direction_input(host->dat_gpio);
+ ret = gpiod_direction_input(host->dat);
if (ret) {
dev_dbg(host->dev, "failed setting direction\n");
goto out;
}
- gpio_set_value(host->clk_gpio, 1);
+ gpiod_set_value(host->clk, 1);
ret = cbus_receive_word(host);
if (ret < 0) {
@@ -156,9 +154,9 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
}
/* Indicate end of transfer, SEL goes up until next transfer */
- gpio_set_value(host->sel_gpio, 1);
- gpio_set_value(host->clk_gpio, 1);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->sel, 1);
+ gpiod_set_value(host->clk, 1);
+ gpiod_set_value(host->clk, 0);
out:
spin_unlock_irqrestore(&host->lock, flags);
@@ -197,24 +195,22 @@ static u32 cbus_i2c_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm cbus_i2c_algo = {
- .smbus_xfer = cbus_i2c_smbus_xfer,
- .functionality = cbus_i2c_func,
+ .smbus_xfer = cbus_i2c_smbus_xfer,
+ .smbus_xfer_atomic = cbus_i2c_smbus_xfer,
+ .functionality = cbus_i2c_func,
};
-static int cbus_i2c_remove(struct platform_device *pdev)
+static void cbus_i2c_remove(struct platform_device *pdev)
{
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
i2c_del_adapter(adapter);
-
- return 0;
}
static int cbus_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct cbus_host *chost;
- int ret;
adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter),
GFP_KERNEL);
@@ -225,22 +221,20 @@ static int cbus_i2c_probe(struct platform_device *pdev)
if (!chost)
return -ENOMEM;
- if (pdev->dev.of_node) {
- struct device_node *dnode = pdev->dev.of_node;
- if (of_gpio_count(dnode) != 3)
- return -ENODEV;
- chost->clk_gpio = of_get_gpio(dnode, 0);
- chost->dat_gpio = of_get_gpio(dnode, 1);
- chost->sel_gpio = of_get_gpio(dnode, 2);
- } else if (dev_get_platdata(&pdev->dev)) {
- struct i2c_cbus_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
- chost->clk_gpio = pdata->clk_gpio;
- chost->dat_gpio = pdata->dat_gpio;
- chost->sel_gpio = pdata->sel_gpio;
- } else {
+ if (gpiod_count(&pdev->dev, NULL) != 3)
return -ENODEV;
- }
+ chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW);
+ if (IS_ERR(chost->clk))
+ return PTR_ERR(chost->clk);
+ chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN);
+ if (IS_ERR(chost->dat))
+ return PTR_ERR(chost->dat);
+ chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH);
+ if (IS_ERR(chost->sel))
+ return PTR_ERR(chost->sel);
+ gpiod_set_consumer_name(chost->clk, "CBUS clk");
+ gpiod_set_consumer_name(chost->dat, "CBUS dat");
+ gpiod_set_consumer_name(chost->sel, "CBUS sel");
adapter->owner = THIS_MODULE;
adapter->class = I2C_CLASS_HWMON;
@@ -249,26 +243,11 @@ static int cbus_i2c_probe(struct platform_device *pdev)
adapter->nr = pdev->id;
adapter->timeout = HZ;
adapter->algo = &cbus_i2c_algo;
- strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name));
+ strscpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name));
spin_lock_init(&chost->lock);
chost->dev = &pdev->dev;
- ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio,
- GPIOF_OUT_INIT_LOW, "CBUS clk");
- if (ret)
- return ret;
-
- ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN,
- "CBUS data");
- if (ret)
- return ret;
-
- ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio,
- GPIOF_OUT_INIT_HIGH, "CBUS sel");
- if (ret)
- return ret;
-
i2c_set_adapdata(adapter, chost);
platform_set_drvdata(pdev, adapter);
@@ -285,7 +264,7 @@ MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids);
static struct platform_driver cbus_i2c_driver = {
.probe = cbus_i2c_probe,
- .remove = cbus_i2c_remove,
+ .remove = cbus_i2c_remove,
.driver = {
.name = "i2c-cbus-gpio",
.of_match_table = of_match_ptr(i2c_cbus_dt_ids),
diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.c b/drivers/i2c/busses/i2c-ccgx-ucsi.c
new file mode 100644
index 000000000000..d97233862a6c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ccgx-ucsi.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Instantiate UCSI device for Cypress CCGx Type-C controller.
+ * Derived from i2c-designware-pcidrv.c and i2c-nvidia-gpu.c.
+ */
+
+#include <linux/i2c.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include "i2c-ccgx-ucsi.h"
+
+struct software_node;
+
+struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
+ const struct software_node *swnode)
+{
+ struct i2c_board_info info = {};
+
+ strscpy(info.type, "ccgx-ucsi", sizeof(info.type));
+ info.addr = 0x08;
+ info.irq = irq;
+ info.swnode = swnode;
+
+ return i2c_new_client_device(adapter, &info);
+}
+EXPORT_SYMBOL_GPL(i2c_new_ccgx_ucsi);
+
+MODULE_DESCRIPTION("Instantiate UCSI device for Cypress CCGx Type-C controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.h b/drivers/i2c/busses/i2c-ccgx-ucsi.h
new file mode 100644
index 000000000000..739ac7a4b117
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ccgx-ucsi.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __I2C_CCGX_UCSI_H_
+#define __I2C_CCGX_UCSI_H_
+
+struct i2c_adapter;
+struct i2c_client;
+struct software_node;
+
+struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
+ const struct software_node *swnode);
+#endif /* __I2C_CCGX_UCSI_H_ */
diff --git a/drivers/i2c/busses/i2c-cgbc.c b/drivers/i2c/busses/i2c-cgbc.c
new file mode 100644
index 000000000000..25a74fa51aa0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cgbc.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Congatec Board Controller I2C busses driver
+ *
+ * Copyright (C) 2024 Bootlin
+ * Author: Thomas Richard <thomas.richard@bootlin.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/cgbc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CGBC_I2C_PRIMARY_BUS_ID 0
+#define CGBC_I2C_PM_BUS_ID 4
+
+#define CGBC_I2C_CMD_START 0x40
+#define CGBC_I2C_CMD_STAT 0x48
+#define CGBC_I2C_CMD_DATA 0x50
+#define CGBC_I2C_CMD_SPEED 0x58
+
+#define CGBC_I2C_STAT_IDL 0x00
+#define CGBC_I2C_STAT_DAT 0x01
+#define CGBC_I2C_STAT_BUSY 0x02
+
+#define CGBC_I2C_START 0x80
+#define CGBC_I2C_STOP 0x40
+
+#define CGBC_I2C_LAST_ACK 0x80 /* send ACK on last read byte */
+
+/*
+ * Reference code defines 1kHz as min freq and 6.1MHz as max freq.
+ * But in practice, the board controller limits the frequency to 1MHz, and the
+ * 1kHz is not functional (minimal working freq is 50kHz).
+ * So use these values as limits.
+ */
+#define CGBC_I2C_FREQ_MIN_HZ 50000 /* 50 kHz */
+#define CGBC_I2C_FREQ_MAX_HZ 1000000 /* 1 MHz */
+
+#define CGBC_I2C_FREQ_UNIT_1KHZ 0x40
+#define CGBC_I2C_FREQ_UNIT_10KHZ 0x80
+#define CGBC_I2C_FREQ_UNIT_100KHZ 0xC0
+
+#define CGBC_I2C_FREQ_UNIT_MASK 0xC0
+#define CGBC_I2C_FREQ_VALUE_MASK 0x3F
+
+#define CGBC_I2C_READ_MAX_LEN 31
+#define CGBC_I2C_WRITE_MAX_LEN 32
+
+#define CGBC_I2C_CMD_HEADER_SIZE 4
+#define CGBC_I2C_CMD_SIZE (CGBC_I2C_CMD_HEADER_SIZE + CGBC_I2C_WRITE_MAX_LEN)
+
+enum cgbc_i2c_state {
+ CGBC_I2C_STATE_DONE = 0,
+ CGBC_I2C_STATE_INIT,
+ CGBC_I2C_STATE_START,
+ CGBC_I2C_STATE_READ,
+ CGBC_I2C_STATE_WRITE,
+ CGBC_I2C_STATE_ERROR,
+};
+
+struct i2c_algo_cgbc_data {
+ u8 bus_id;
+ unsigned long read_maxtime_us;
+};
+
+struct cgbc_i2c_data {
+ struct device *dev;
+ struct cgbc_device_data *cgbc;
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+ int nmsgs;
+ int pos;
+ enum cgbc_i2c_state state;
+};
+
+struct cgbc_i2c_transfer {
+ u8 bus_id;
+ bool start;
+ bool stop;
+ bool last_ack;
+ u8 read;
+ u8 write;
+ u8 addr;
+ u8 data[CGBC_I2C_WRITE_MAX_LEN];
+};
+
+static u8 cgbc_i2c_freq_to_reg(unsigned int bus_frequency)
+{
+ u8 reg;
+
+ if (bus_frequency <= 10000)
+ reg = CGBC_I2C_FREQ_UNIT_1KHZ | (bus_frequency / 1000);
+ else if (bus_frequency <= 100000)
+ reg = CGBC_I2C_FREQ_UNIT_10KHZ | (bus_frequency / 10000);
+ else
+ reg = CGBC_I2C_FREQ_UNIT_100KHZ | (bus_frequency / 100000);
+
+ return reg;
+}
+
+static unsigned int cgbc_i2c_reg_to_freq(u8 reg)
+{
+ unsigned int freq = reg & CGBC_I2C_FREQ_VALUE_MASK;
+ u8 unit = reg & CGBC_I2C_FREQ_UNIT_MASK;
+
+ if (unit == CGBC_I2C_FREQ_UNIT_100KHZ)
+ return freq * 100000;
+ else if (unit == CGBC_I2C_FREQ_UNIT_10KHZ)
+ return freq * 10000;
+ else
+ return freq * 1000;
+}
+
+static int cgbc_i2c_get_status(struct i2c_adapter *adap)
+{
+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
+ struct cgbc_device_data *cgbc = i2c->cgbc;
+ u8 cmd = CGBC_I2C_CMD_STAT | algo_data->bus_id;
+ u8 status;
+ int ret;
+
+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), NULL, 0, &status);
+ if (ret)
+ return ret;
+
+ return status;
+}
+
+static int cgbc_i2c_set_frequency(struct i2c_adapter *adap,
+ unsigned int bus_frequency)
+{
+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
+ struct cgbc_device_data *cgbc = i2c->cgbc;
+ u8 cmd[2], data;
+ int ret;
+
+ if (bus_frequency > CGBC_I2C_FREQ_MAX_HZ ||
+ bus_frequency < CGBC_I2C_FREQ_MIN_HZ) {
+ dev_info(i2c->dev, "invalid frequency %u, using default\n", bus_frequency);
+ bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;
+ }
+
+ cmd[0] = CGBC_I2C_CMD_SPEED | algo_data->bus_id;
+ cmd[1] = cgbc_i2c_freq_to_reg(bus_frequency);
+
+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);
+ if (ret)
+ return dev_err_probe(i2c->dev, ret,
+ "Failed to initialize I2C bus %s",
+ adap->name);
+
+ cmd[1] = 0x00;
+
+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);
+ if (ret)
+ return dev_err_probe(i2c->dev, ret,
+ "Failed to get I2C bus frequency");
+
+ bus_frequency = cgbc_i2c_reg_to_freq(data);
+
+ dev_dbg(i2c->dev, "%s is running at %d Hz\n", adap->name, bus_frequency);
+
+ /*
+ * The read_maxtime_us variable represents the maximum time to wait
+ * for data during a read operation. The maximum amount of data that
+ * can be read by a command is CGBC_I2C_READ_MAX_LEN.
+ * Therefore, calculate the max time to properly size the timeout.
+ */
+ algo_data->read_maxtime_us = (BITS_PER_BYTE + 1) * CGBC_I2C_READ_MAX_LEN
+ * USEC_PER_SEC / bus_frequency;
+
+ return 0;
+}
+
+static unsigned int cgbc_i2c_xfer_to_cmd(struct cgbc_i2c_transfer xfer, u8 *cmd)
+{
+ int i = 0;
+
+ cmd[i++] = CGBC_I2C_CMD_START | xfer.bus_id;
+
+ cmd[i] = (xfer.start) ? CGBC_I2C_START : 0x00;
+ if (xfer.stop)
+ cmd[i] |= CGBC_I2C_STOP;
+ cmd[i++] |= (xfer.start) ? xfer.write + 1 : xfer.write;
+
+ cmd[i++] = (xfer.last_ack) ? (xfer.read | CGBC_I2C_LAST_ACK) : xfer.read;
+
+ if (xfer.start)
+ cmd[i++] = xfer.addr;
+
+ if (xfer.write > 0)
+ memcpy(&cmd[i], &xfer.data, xfer.write);
+
+ return i + xfer.write;
+}
+
+static int cgbc_i2c_xfer_msg(struct i2c_adapter *adap)
+{
+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
+ struct cgbc_device_data *cgbc = i2c->cgbc;
+ struct i2c_msg *msg = i2c->msg;
+ u8 cmd[CGBC_I2C_CMD_SIZE];
+ int ret, max_len, len, i;
+ unsigned int cmd_len;
+ u8 cmd_data;
+
+ struct cgbc_i2c_transfer xfer = {
+ .bus_id = algo_data->bus_id,
+ .addr = i2c_8bit_addr_from_msg(msg),
+ };
+
+ if (i2c->state == CGBC_I2C_STATE_DONE)
+ return 0;
+
+ ret = cgbc_i2c_get_status(adap);
+
+ if (ret == CGBC_I2C_STAT_BUSY)
+ return -EBUSY;
+ else if (ret < 0)
+ goto err;
+
+ if (i2c->state == CGBC_I2C_STATE_INIT ||
+ (i2c->state == CGBC_I2C_STATE_WRITE && msg->flags & I2C_M_RD))
+ xfer.start = true;
+
+ i2c->state = (msg->flags & I2C_M_RD) ? CGBC_I2C_STATE_READ : CGBC_I2C_STATE_WRITE;
+
+ max_len = (i2c->state == CGBC_I2C_STATE_READ) ?
+ CGBC_I2C_READ_MAX_LEN : CGBC_I2C_WRITE_MAX_LEN;
+
+ if (msg->len - i2c->pos > max_len) {
+ len = max_len;
+ } else {
+ len = msg->len - i2c->pos;
+
+ if (i2c->nmsgs == 1)
+ xfer.stop = true;
+ }
+
+ if (i2c->state == CGBC_I2C_STATE_WRITE) {
+ xfer.write = len;
+ xfer.read = 0;
+
+ for (i = 0; i < len; i++)
+ xfer.data[i] = msg->buf[i2c->pos + i];
+
+ cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);
+
+ ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);
+ if (ret)
+ goto err;
+ } else if (i2c->state == CGBC_I2C_STATE_READ) {
+ xfer.write = 0;
+ xfer.read = len;
+
+ if (i2c->nmsgs > 1 || msg->len - i2c->pos > max_len)
+ xfer.read |= CGBC_I2C_LAST_ACK;
+
+ cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);
+ ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);
+ if (ret)
+ goto err;
+
+ ret = read_poll_timeout(cgbc_i2c_get_status, ret,
+ ret != CGBC_I2C_STAT_BUSY, 0,
+ 2 * algo_data->read_maxtime_us, false, adap);
+ if (ret < 0)
+ goto err;
+
+ cmd_data = CGBC_I2C_CMD_DATA | algo_data->bus_id;
+ ret = cgbc_command(cgbc, &cmd_data, sizeof(cmd_data),
+ msg->buf + i2c->pos, len, NULL);
+ if (ret)
+ goto err;
+ }
+
+ if (len == (msg->len - i2c->pos)) {
+ i2c->msg++;
+ i2c->nmsgs--;
+ i2c->pos = 0;
+ } else {
+ i2c->pos += len;
+ }
+
+ if (i2c->nmsgs == 0)
+ i2c->state = CGBC_I2C_STATE_DONE;
+
+ return 0;
+
+err:
+ i2c->state = CGBC_I2C_STATE_ERROR;
+ return ret;
+}
+
+static int cgbc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
+ unsigned long timeout = jiffies + HZ;
+ int ret;
+
+ i2c->state = CGBC_I2C_STATE_INIT;
+ i2c->msg = msgs;
+ i2c->nmsgs = num;
+ i2c->pos = 0;
+
+ while (time_before(jiffies, timeout)) {
+ ret = cgbc_i2c_xfer_msg(adap);
+ if (i2c->state == CGBC_I2C_STATE_DONE)
+ return num;
+
+ if (i2c->state == CGBC_I2C_STATE_ERROR)
+ return ret;
+
+ if (ret == 0)
+ timeout = jiffies + HZ;
+ }
+
+ i2c->state = CGBC_I2C_STATE_ERROR;
+ return -ETIMEDOUT;
+}
+
+static u32 cgbc_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~(I2C_FUNC_SMBUS_QUICK));
+}
+
+static const struct i2c_algorithm cgbc_i2c_algorithm = {
+ .xfer = cgbc_i2c_xfer,
+ .functionality = cgbc_i2c_func,
+};
+
+static struct i2c_algo_cgbc_data cgbc_i2c_algo_data[] = {
+ { .bus_id = CGBC_I2C_PRIMARY_BUS_ID },
+ { .bus_id = CGBC_I2C_PM_BUS_ID },
+};
+
+static const struct i2c_adapter cgbc_i2c_adapter[] = {
+ {
+ .owner = THIS_MODULE,
+ .name = "Congatec General Purpose I2C adapter",
+ .class = I2C_CLASS_DEPRECATED,
+ .algo = &cgbc_i2c_algorithm,
+ .algo_data = &cgbc_i2c_algo_data[0],
+ .nr = -1,
+ },
+ {
+ .owner = THIS_MODULE,
+ .name = "Congatec Power Management I2C adapter",
+ .class = I2C_CLASS_DEPRECATED,
+ .algo = &cgbc_i2c_algorithm,
+ .algo_data = &cgbc_i2c_algo_data[1],
+ .nr = -1,
+ },
+};
+
+static int cgbc_i2c_probe(struct platform_device *pdev)
+{
+ struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
+ struct cgbc_i2c_data *i2c;
+ int ret;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->cgbc = cgbc;
+ i2c->dev = &pdev->dev;
+ i2c->adap = cgbc_i2c_adapter[pdev->id];
+ i2c->adap.dev.parent = i2c->dev;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ platform_set_drvdata(pdev, i2c);
+
+ ret = cgbc_i2c_set_frequency(&i2c->adap, I2C_MAX_STANDARD_MODE_FREQ);
+ if (ret)
+ return ret;
+
+ return i2c_add_numbered_adapter(&i2c->adap);
+}
+
+static void cgbc_i2c_remove(struct platform_device *pdev)
+{
+ struct cgbc_i2c_data *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+}
+
+static struct platform_driver cgbc_i2c_driver = {
+ .driver = {
+ .name = "cgbc-i2c",
+ },
+ .probe = cgbc_i2c_probe,
+ .remove = cgbc_i2c_remove,
+};
+
+module_platform_driver(cgbc_i2c_driver);
+
+MODULE_DESCRIPTION("Congatec Board Controller I2C Driver");
+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cgbc_i2c");
diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c
index c4d176f5ed79..606ac071cb80 100644
--- a/drivers/i2c/busses/i2c-cht-wc.c
+++ b/drivers/i2c/busses/i2c-cht-wc.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Intel CHT Whiskey Cove PMIC I2C Master driver
+ * Intel CHT Whiskey Cove PMIC I2C controller driver
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
*
* Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
* Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/acpi.h>
@@ -27,6 +18,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power/bq24190_charger.h>
+#include <linux/power/bq25890_charger.h>
#include <linux/slab.h>
#define CHT_WC_I2C_CTRL 0x5e24
@@ -108,20 +100,13 @@ static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data)
* interrupt handler as well, so running the client irq handler from
* this thread will cause things to lock up.
*/
- if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) {
- /*
- * generic_handle_irq expects local IRQs to be disabled
- * as normally it is called from interrupt context.
- */
- local_irq_disable();
- generic_handle_irq(adap->client_irq);
- local_irq_enable();
- }
+ if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ)
+ generic_handle_irq_safe(adap->client_irq);
return IRQ_HANDLED;
}
-static u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap)
+static u32 cht_wc_i2c_adap_func(struct i2c_adapter *adap)
{
/* This i2c adapter only supports SMBUS byte transfers */
return I2C_FUNC_SMBUS_BYTE_DATA;
@@ -183,10 +168,55 @@ static int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr,
}
static const struct i2c_algorithm cht_wc_i2c_adap_algo = {
- .functionality = cht_wc_i2c_adap_master_func,
+ .functionality = cht_wc_i2c_adap_func,
.smbus_xfer = cht_wc_i2c_adap_smbus_xfer,
};
+/*
+ * We are an i2c-adapter which itself is part of an i2c-client. This means that
+ * transfers done through us take adapter->bus_lock twice, once for our parent
+ * i2c-adapter and once to take our own bus_lock. Lockdep does not like this
+ * nested locking, to make lockdep happy in the case of busses with muxes, the
+ * i2c-core's i2c_adapter_lock_bus function calls:
+ * rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
+ *
+ * But i2c_adapter_depth only works when the direct parent of the adapter is
+ * another adapter, as it is only meant for muxes. In our case there is an
+ * i2c-client and MFD instantiated platform_device in the parent->child chain
+ * between the 2 devices.
+ *
+ * So we override the default i2c_lock_operations and pass a hardcoded
+ * depth of 1 to rt_mutex_lock_nested, to make lockdep happy.
+ *
+ * Note that if there were to be a mux attached to our adapter, this would
+ * break things again since the i2c-mux code expects the root-adapter to have
+ * a locking depth of 0. But we always have only 1 client directly attached
+ * in the form of the Charger IC paired with the CHT Whiskey Cove PMIC.
+ */
+static void cht_wc_i2c_adap_lock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ rt_mutex_lock_nested(&adapter->bus_lock, 1);
+}
+
+static int cht_wc_i2c_adap_trylock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ return rt_mutex_trylock(&adapter->bus_lock);
+}
+
+static void cht_wc_i2c_adap_unlock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ rt_mutex_unlock(&adapter->bus_lock);
+}
+
+static const struct i2c_lock_operations cht_wc_i2c_adap_lock_ops = {
+ .lock_bus = cht_wc_i2c_adap_lock_bus,
+ .trylock_bus = cht_wc_i2c_adap_trylock_bus,
+ .unlock_bus = cht_wc_i2c_adap_unlock_bus,
+};
+
/**** irqchip for the client connected to the extchgr i2c adapter ****/
static void cht_wc_i2c_irq_lock(struct irq_data *data)
{
@@ -234,6 +264,7 @@ static const struct irq_chip cht_wc_i2c_irq_chip = {
.name = "cht_wc_ext_chrg_irq_chip",
};
+/********** GPD Win / Pocket charger IC settings **********/
static const char * const bq24190_suppliers[] = {
"tcpm-source-psy-i2c-fusb302" };
@@ -244,6 +275,10 @@ static const struct property_entry bq24190_props[] = {
{ }
};
+static const struct software_node bq24190_node = {
+ .properties = bq24190_props,
+};
+
static struct regulator_consumer_supply fusb302_consumer = {
.supply = "vbus",
/* Must match fusb302 dev_name in intel_cht_int33fe.c */
@@ -264,24 +299,140 @@ static struct bq24190_platform_data bq24190_pdata = {
.regulator_init_data = &bq24190_vbus_init_data,
};
+static struct i2c_board_info gpd_win_board_info = {
+ .type = "bq24190",
+ .addr = 0x6b,
+ .dev_name = "bq24190",
+ .swnode = &bq24190_node,
+ .platform_data = &bq24190_pdata,
+};
+
+/********** Xiaomi Mi Pad 2 charger IC settings **********/
+static struct regulator_consumer_supply bq2589x_vbus_consumer = {
+ .supply = "vbus",
+ .dev_name = "cht_wcove_pwrsrc",
+};
+
+static const struct regulator_init_data bq2589x_vbus_init_data = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = &bq2589x_vbus_consumer,
+ .num_consumer_supplies = 1,
+};
+
+static struct bq25890_platform_data bq2589x_pdata = {
+ .regulator_init_data = &bq2589x_vbus_init_data,
+};
+
+static const struct property_entry xiaomi_mipad2_props[] = {
+ PROPERTY_ENTRY_BOOL("linux,skip-reset"),
+ PROPERTY_ENTRY_BOOL("linux,read-back-settings"),
+ { }
+};
+
+static const struct software_node xiaomi_mipad2_node = {
+ .properties = xiaomi_mipad2_props,
+};
+
+static struct i2c_board_info xiaomi_mipad2_board_info = {
+ .type = "bq25890",
+ .addr = 0x6a,
+ .dev_name = "bq25890",
+ .swnode = &xiaomi_mipad2_node,
+ .platform_data = &bq2589x_pdata,
+};
+
+/********** Lenovo Yogabook YB1-X90F/-X91F/-X91L charger settings **********/
+static const char * const lenovo_yb1_bq25892_suppliers[] = { "cht_wcove_pwrsrc" };
+
+static const struct property_entry lenovo_yb1_bq25892_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("supplied-from",
+ lenovo_yb1_bq25892_suppliers),
+ PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
+ PROPERTY_ENTRY_BOOL("linux,skip-reset"),
+ /*
+ * The firmware sets everything to the defaults, which leads to a
+ * somewhat low charge-current of 2048mA and worse to a battery-voltage
+ * of 4.2V instead of 4.35V (when booted without a charger connected).
+ * Use our own values instead of "linux,read-back-settings" to fix this.
+ */
+ PROPERTY_ENTRY_U32("ti,charge-current", 4224000),
+ PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
+ PROPERTY_ENTRY_U32("ti,termination-current", 256000),
+ PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
+ PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3500000),
+ PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
+ PROPERTY_ENTRY_U32("ti,boost-max-current", 1400000),
+ PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
+ { }
+};
+
+static const struct software_node lenovo_yb1_bq25892_node = {
+ .properties = lenovo_yb1_bq25892_props,
+};
+
+static struct i2c_board_info lenovo_yogabook1_board_info = {
+ .type = "bq25892",
+ .addr = 0x6b,
+ .dev_name = "bq25892",
+ .swnode = &lenovo_yb1_bq25892_node,
+ .platform_data = &bq2589x_pdata,
+};
+
+/********** Lenovo Yogabook YT3-X90F charger settings **********/
+static const char * const lenovo_yt3_bq25892_1_suppliers[] = { "cht_wcove_pwrsrc" };
+
+/*
+ * bq25892 charger settings for the round li-ion cells in the hinge,
+ * this is the main / biggest battery.
+ */
+static const struct property_entry lenovo_yt3_bq25892_1_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_1_suppliers),
+ PROPERTY_ENTRY_STRING("linux,secondary-charger-name", "bq25890-charger-0"),
+ PROPERTY_ENTRY_U32("linux,iinlim-percentage", 60),
+ PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
+ PROPERTY_ENTRY_BOOL("linux,skip-reset"),
+ /*
+ * The firmware sets everything to the defaults, leading to a low(ish)
+ * charge-current and battery-voltage of 2048mA resp 4.2V. Use the
+ * Android values instead of "linux,read-back-settings" to fix this.
+ */
+ PROPERTY_ENTRY_U32("ti,charge-current", 3072000),
+ PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
+ PROPERTY_ENTRY_U32("ti,termination-current", 128000),
+ PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
+ PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000),
+ PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
+ /* Set 5V boost current-limit to 1.2A (MAX/POR values are 2.45A/1.4A) */
+ PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
+ PROPERTY_ENTRY_U32("ti,boost-max-current", 1200000),
+ { }
+};
+
+static const struct software_node lenovo_yt3_bq25892_1_node = {
+ .properties = lenovo_yt3_bq25892_1_props,
+};
+
+/* bq25892 charger for the round li-ion cells in the hinge */
+static struct i2c_board_info lenovo_yoga_tab3_board_info = {
+ .type = "bq25892",
+ .addr = 0x6b,
+ .dev_name = "bq25892_1",
+ .swnode = &lenovo_yt3_bq25892_1_node,
+ .platform_data = &bq2589x_pdata,
+};
+
static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct i2c_board_info *board_info = NULL;
struct cht_wc_i2c_adap *adap;
- struct i2c_board_info board_info = {
- .type = "bq24190",
- .addr = 0x6b,
- .dev_name = "bq24190",
- .properties = bq24190_props,
- .platform_data = &bq24190_pdata,
- };
int ret, reg, irq;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Error missing irq resource\n");
- return -EINVAL;
- }
+ if (irq < 0)
+ return irq;
adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL);
if (!adap)
@@ -295,7 +446,8 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
adap->adapter.owner = THIS_MODULE;
adap->adapter.class = I2C_CLASS_HWMON;
adap->adapter.algo = &cht_wc_i2c_adap_algo;
- strlcpy(adap->adapter.name, "PMIC I2C Adapter",
+ adap->adapter.lock_ops = &cht_wc_i2c_adap_lock_ops;
+ strscpy(adap->adapter.name, "PMIC I2C Adapter",
sizeof(adap->adapter.name));
adap->adapter.dev.parent = &pdev->dev;
@@ -315,8 +467,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
return ret;
/* Alloc and register client IRQ */
- adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1,
- &irq_domain_simple_ops, NULL);
+ adap->irq_domain = irq_domain_create_linear(NULL, 1, &irq_domain_simple_ops, NULL);
if (!adap->irq_domain)
return -ENOMEM;
@@ -341,19 +492,29 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
if (ret)
goto remove_irq_domain;
- /*
- * Normally the Whiskey Cove PMIC is paired with a TI bq24292i charger,
- * connected to this i2c bus, and a max17047 fuel-gauge and a fusb302
- * USB Type-C controller connected to another i2c bus. In this setup
- * the max17047 and fusb302 devices are enumerated through an INT33FE
- * ACPI device. If this device is present register an i2c-client for
- * the TI bq24292i charger.
- */
- if (acpi_dev_present("INT33FE", NULL, -1)) {
- board_info.irq = adap->client_irq;
- adap->client = i2c_new_device(&adap->adapter, &board_info);
- if (!adap->client) {
- ret = -ENOMEM;
+ switch (pmic->cht_wc_model) {
+ case INTEL_CHT_WC_GPD_WIN_POCKET:
+ board_info = &gpd_win_board_info;
+ break;
+ case INTEL_CHT_WC_XIAOMI_MIPAD2:
+ board_info = &xiaomi_mipad2_board_info;
+ break;
+ case INTEL_CHT_WC_LENOVO_YOGABOOK1:
+ board_info = &lenovo_yogabook1_board_info;
+ break;
+ case INTEL_CHT_WC_LENOVO_YT3_X90:
+ board_info = &lenovo_yoga_tab3_board_info;
+ break;
+ default:
+ dev_warn(&pdev->dev, "Unknown model, not instantiating charger device\n");
+ break;
+ }
+
+ if (board_info) {
+ board_info->irq = adap->client_irq;
+ adap->client = i2c_new_client_device(&adap->adapter, board_info);
+ if (IS_ERR(adap->client)) {
+ ret = PTR_ERR(adap->client);
goto del_adapter;
}
}
@@ -368,16 +529,13 @@ remove_irq_domain:
return ret;
}
-static int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev)
+static void cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev)
{
struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev);
- if (adap->client)
- i2c_unregister_device(adap->client);
+ i2c_unregister_device(adap->client);
i2c_del_adapter(&adap->adapter);
irq_domain_remove(adap->irq_domain);
-
- return 0;
}
static const struct platform_device_id cht_wc_i2c_adap_id_table[] = {
@@ -396,6 +554,6 @@ static struct platform_driver cht_wc_i2c_adap_driver = {
};
module_platform_driver(cht_wc_i2c_adap_driver);
-MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver");
+MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C controller driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c
new file mode 100644
index 000000000000..e7720ea4045e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cp2615.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * i2c support for Silicon Labs' CP2615 Digital Audio Bridge
+ *
+ * (c) 2021, Bence Csókás <bence98@sch.bme.hu>
+ */
+
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+
+/** CP2615 I/O Protocol implementation */
+
+#define CP2615_VID 0x10c4
+#define CP2615_PID 0xeac1
+
+#define IOP_EP_IN 0x82
+#define IOP_EP_OUT 0x02
+#define IOP_IFN 1
+#define IOP_ALTSETTING 2
+
+#define MAX_IOP_SIZE 64
+#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6)
+#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4)
+
+enum cp2615_iop_msg_type {
+ iop_GetAccessoryInfo = 0xD100,
+ iop_AccessoryInfo = 0xA100,
+ iop_GetPortConfiguration = 0xD203,
+ iop_PortConfiguration = 0xA203,
+ iop_DoI2cTransfer = 0xD400,
+ iop_I2cTransferResult = 0xA400,
+ iop_GetSerialState = 0xD501,
+ iop_SerialState = 0xA501
+};
+
+struct __packed cp2615_iop_msg {
+ __be16 preamble, length, msg;
+ u8 data[MAX_IOP_PAYLOAD_SIZE];
+};
+
+#define PART_ID_A01 0x1400
+#define PART_ID_A02 0x1500
+
+struct __packed cp2615_iop_accessory_info {
+ __be16 part_id, option_id, proto_ver;
+};
+
+struct __packed cp2615_i2c_transfer {
+ u8 tag, i2caddr, read_len, write_len;
+ u8 data[MAX_I2C_SIZE];
+};
+
+/* Possible values for struct cp2615_i2c_transfer_result.status */
+enum cp2615_i2c_status {
+ /* Writing to the internal EEPROM failed, because it is locked */
+ CP2615_CFG_LOCKED = -6,
+ /* read_len or write_len out of range */
+ CP2615_INVALID_PARAM = -4,
+ /* I2C target did not ACK in time */
+ CP2615_TIMEOUT,
+ /* I2C bus busy */
+ CP2615_BUS_BUSY,
+ /* I2C bus error (ie. target NAK'd the request) */
+ CP2615_BUS_ERROR,
+ CP2615_SUCCESS
+};
+
+struct __packed cp2615_i2c_transfer_result {
+ u8 tag, i2caddr;
+ s8 status;
+ u8 read_len;
+ u8 data[MAX_I2C_SIZE];
+};
+
+static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg,
+ const void *data, size_t data_len)
+{
+ if (data_len > MAX_IOP_PAYLOAD_SIZE)
+ return -EFBIG;
+
+ if (!ret)
+ return -EINVAL;
+
+ ret->preamble = htons(0x2A2AU);
+ ret->length = htons(data_len + 6);
+ ret->msg = htons(msg);
+ if (data && data_len)
+ memcpy(&ret->data, data, data_len);
+ return 0;
+}
+
+static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data)
+{
+ return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len);
+}
+
+/* Translates status codes to Linux errno's */
+static int cp2615_check_status(enum cp2615_i2c_status status)
+{
+ switch (status) {
+ case CP2615_SUCCESS:
+ return 0;
+ case CP2615_BUS_ERROR:
+ return -ENXIO;
+ case CP2615_BUS_BUSY:
+ return -EAGAIN;
+ case CP2615_TIMEOUT:
+ return -ETIMEDOUT;
+ case CP2615_INVALID_PARAM:
+ return -EINVAL;
+ case CP2615_CFG_LOCKED:
+ return -EPERM;
+ }
+ /* Unknown error code */
+ return -EPROTO;
+}
+
+/** Driver code */
+
+static int
+cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w)
+{
+ struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int res = cp2615_init_i2c_msg(msg, i2c_w);
+
+ if (!res)
+ res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+ msg, ntohs(msg->length), NULL, 0);
+ kfree(msg);
+ return res;
+}
+
+static int
+cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf)
+{
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ struct cp2615_iop_msg *msg;
+ struct cp2615_i2c_transfer_result *i2c_r;
+ int res;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), msg,
+ sizeof(struct cp2615_iop_msg), NULL, 0);
+ if (res < 0) {
+ kfree(msg);
+ return res;
+ }
+
+ i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data;
+ if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) {
+ kfree(msg);
+ return -EIO;
+ }
+
+ res = cp2615_check_status(i2c_r->status);
+ if (!res)
+ memcpy(buf, &i2c_r->data, i2c_r->read_len);
+
+ kfree(msg);
+ return res;
+}
+
+/* Checks if the IOP is functional by querying the part's ID */
+static int cp2615_check_iop(struct usb_interface *usbif)
+{
+ struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data;
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0);
+
+ if (res)
+ goto out;
+
+ res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+ msg, ntohs(msg->length), NULL, 0);
+ if (res)
+ goto out;
+
+ res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
+ msg, sizeof(struct cp2615_iop_msg), NULL, 0);
+ if (res)
+ goto out;
+
+ if (msg->msg != htons(iop_AccessoryInfo)) {
+ res = -EIO;
+ goto out;
+ }
+
+ switch (ntohs(info->part_id)) {
+ case PART_ID_A01:
+ dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n");
+ break;
+ case PART_ID_A02:
+ dev_dbg(&usbif->dev, "Found good A02 part.\n");
+ break;
+ default:
+ dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id));
+ }
+
+out:
+ kfree(msg);
+ return res;
+}
+
+static int
+cp2615_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct usb_interface *usbif = adap->algo_data;
+ int i = 0, ret = 0;
+ struct i2c_msg *msg;
+ struct cp2615_i2c_transfer i2c_w = {0};
+
+ dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num);
+
+ for (; !ret && i < num; i++) {
+ msg = &msgs[i];
+
+ i2c_w.tag = 0xdd;
+ i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg);
+ if (msg->flags & I2C_M_RD) {
+ i2c_w.read_len = msg->len;
+ i2c_w.write_len = 0;
+ } else {
+ i2c_w.read_len = 0;
+ i2c_w.write_len = msg->len;
+ memcpy(&i2c_w.data, msg->buf, i2c_w.write_len);
+ }
+ ret = cp2615_i2c_send(usbif, &i2c_w);
+ if (ret)
+ break;
+ ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf);
+ }
+ if (ret < 0)
+ return ret;
+ return i;
+}
+
+static u32
+cp2615_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm cp2615_i2c_algo = {
+ .xfer = cp2615_i2c_xfer,
+ .functionality = cp2615_i2c_func,
+};
+
+/*
+ * This chip has some limitations: one is that the USB endpoint
+ * can only receive 64 bytes/transfer, that leaves 54 bytes for
+ * the I2C transfer. On top of that, EITHER read_len OR write_len
+ * may be zero, but not both. If both are non-zero, the adapter
+ * issues a write followed by a read. And the chip does not
+ * support repeated START between the write and read phases.
+ */
+static struct i2c_adapter_quirks cp2615_i2c_quirks = {
+ .max_write_len = MAX_I2C_SIZE,
+ .max_read_len = MAX_I2C_SIZE,
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
+ .max_comb_1st_msg_len = MAX_I2C_SIZE,
+ .max_comb_2nd_msg_len = MAX_I2C_SIZE
+};
+
+static void
+cp2615_i2c_remove(struct usb_interface *usbif)
+{
+ struct i2c_adapter *adap = usb_get_intfdata(usbif);
+
+ usb_set_intfdata(usbif, NULL);
+ i2c_del_adapter(adap);
+}
+
+static int
+cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct i2c_adapter *adap;
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+
+ ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING);
+ if (ret)
+ return ret;
+
+ ret = cp2615_check_iop(usbif);
+ if (ret)
+ return ret;
+
+ adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (!adap)
+ return -ENOMEM;
+
+ strscpy(adap->name, usbdev->serial, sizeof(adap->name));
+ adap->owner = THIS_MODULE;
+ adap->dev.parent = &usbif->dev;
+ adap->dev.of_node = usbif->dev.of_node;
+ adap->timeout = HZ;
+ adap->algo = &cp2615_i2c_algo;
+ adap->quirks = &cp2615_i2c_quirks;
+ adap->algo_data = usbif;
+
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ return ret;
+
+ usb_set_intfdata(usbif, adap);
+ return 0;
+}
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver cp2615_i2c_driver = {
+ .name = "i2c-cp2615",
+ .probe = cp2615_i2c_probe,
+ .disconnect = cp2615_i2c_remove,
+ .id_table = id_table,
+};
+
+module_usb_driver(cp2615_i2c_driver);
+
+MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>");
+MODULE_DESCRIPTION("CP2615 I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index 8a8ca945561b..260e1643c2cc 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale CPM1/CPM2 I2C interface.
* Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
@@ -13,16 +14,6 @@
*
* Converted to of_platform_device. Renamed to i2c-cpm.c.
* (C) 2007,2008 Jochen Friedrich <jochen@scram.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -35,10 +26,10 @@
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <sysdev/fsl_soc.h>
#include <asm/cpm.h>
@@ -74,6 +65,9 @@ struct i2c_ram {
char res1[4]; /* Reserved */
ushort rpbase; /* Relocation pointer */
char res2[2]; /* Reserved */
+ /* The following elements are only for CPM2 */
+ char res3[4]; /* Reserved */
+ uint sdmatmp; /* Internal */
};
#define I2COM_START 0x80
@@ -408,7 +402,7 @@ static u32 cpm_i2c_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
static const struct i2c_algorithm cpm_i2c_algo = {
- .master_xfer = cpm_i2c_xfer,
+ .xfer = cpm_i2c_xfer,
.functionality = cpm_i2c_func,
};
@@ -540,7 +534,9 @@ static int cpm_i2c_setup(struct cpm_i2c *cpm)
}
out_be32(&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] + 1) & ~1));
- cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL);
+ cpm->txbuf[i] = dma_alloc_coherent(&cpm->ofdev->dev,
+ CPM_MAX_READ + 1,
+ &cpm->txdma[i], GFP_KERNEL);
if (!cpm->txbuf[i]) {
ret = -ENOMEM;
goto out_muram;
@@ -574,7 +570,7 @@ static int cpm_i2c_setup(struct cpm_i2c *cpm)
out_8(&cpm->i2c_reg->i2brg, brg);
out_8(&cpm->i2c_reg->i2mod, 0x00);
- out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); /* Master mode */
+ out_8(&cpm->i2c_reg->i2com, I2COM_MASTER);
/* Disable interrupts. */
out_8(&cpm->i2c_reg->i2cmr, 0);
@@ -662,7 +658,7 @@ static int cpm_i2c_probe(struct platform_device *ofdev)
/* register new adapter to i2c module... */
data = of_get_property(ofdev->dev.of_node, "linux,i2c-index", &len);
- cpm->adap.nr = (data && len == 4) ? be32_to_cpup(data) : -1;
+ cpm->adap.nr = (data && len == 4) ? *data : -1;
result = i2c_add_numbered_adapter(&cpm->adap);
if (result < 0)
@@ -680,7 +676,7 @@ out_free:
return result;
}
-static int cpm_i2c_remove(struct platform_device *ofdev)
+static void cpm_i2c_remove(struct platform_device *ofdev)
{
struct cpm_i2c *cpm = platform_get_drvdata(ofdev);
@@ -689,8 +685,6 @@ static int cpm_i2c_remove(struct platform_device *ofdev)
cpm_i2c_shutdown(cpm);
kfree(cpm);
-
- return 0;
}
static const struct of_device_id cpm_i2c_match[] = {
diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
index 82bcd9a78759..208ce4f9e782 100644
--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -3,10 +3,11 @@
//
// Copyright (C) 2013 Google, Inc.
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -106,7 +107,7 @@ static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[],
/**
* ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response
*
- * @i2c_msgs: The i2c messages to to fill up.
+ * @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages expected.
*
* Returns the number of response bytes expeced.
@@ -130,7 +131,7 @@ static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num)
* We'll take the EC's response and copy it back into msgs.
*
* @buf: The buffer to parse.
- * @i2c_msgs: The i2c messages to to fill up.
+ * @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages; will be modified to include the actual
* number received.
*
@@ -234,19 +235,21 @@ static u32 ec_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm ec_i2c_algorithm = {
- .master_xfer = ec_i2c_xfer,
- .functionality = ec_i2c_functionality,
+ .xfer = ec_i2c_xfer,
+ .functionality = ec_i2c_functionality,
};
static int ec_i2c_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct ec_i2c_device *bus = NULL;
u32 remote_bus;
int err;
+ if (!ec)
+ return dev_err_probe(dev, -EPROBE_DEFER, "couldn't find parent EC device\n");
+
if (!ec->cmd_xfer) {
dev_err(dev, "Missing sendrecv\n");
return -EINVAL;
@@ -256,7 +259,7 @@ static int ec_i2c_probe(struct platform_device *pdev)
if (bus == NULL)
return -ENOMEM;
- err = of_property_read_u32(np, "google,remote-bus", &remote_bus);
+ err = device_property_read_u32(dev, "google,remote-bus", &remote_bus);
if (err) {
dev_err(dev, "Couldn't read remote-bus property\n");
return err;
@@ -267,12 +270,13 @@ static int ec_i2c_probe(struct platform_device *pdev)
bus->dev = dev;
bus->adap.owner = THIS_MODULE;
- strlcpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name));
+ strscpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name));
bus->adap.algo = &ec_i2c_algorithm;
bus->adap.algo_data = bus;
bus->adap.dev.parent = &pdev->dev;
- bus->adap.dev.of_node = np;
+ bus->adap.dev.of_node = pdev->dev.of_node;
bus->adap.retries = I2C_MAX_RETRIES;
+ ACPI_COMPANION_SET(&bus->adap.dev, ACPI_COMPANION(&pdev->dev));
err = i2c_add_adapter(&bus->adap);
if (err)
@@ -282,28 +286,31 @@ static int ec_i2c_probe(struct platform_device *pdev)
return err;
}
-static int ec_i2c_remove(struct platform_device *dev)
+static void ec_i2c_remove(struct platform_device *dev)
{
struct ec_i2c_device *bus = platform_get_drvdata(dev);
i2c_del_adapter(&bus->adap);
-
- return 0;
}
-#ifdef CONFIG_OF
-static const struct of_device_id cros_ec_i2c_of_match[] = {
+static const struct of_device_id cros_ec_i2c_of_match[] __maybe_unused = {
{ .compatible = "google,cros-ec-i2c-tunnel" },
{},
};
MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match);
-#endif
+
+static const struct acpi_device_id cros_ec_i2c_tunnel_acpi_id[] __maybe_unused = {
+ { "GOOG0012", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_tunnel_acpi_id);
static struct platform_driver ec_i2c_tunnel_driver = {
.probe = ec_i2c_probe,
.remove = ec_i2c_remove,
.driver = {
.name = "cros-ec-i2c-tunnel",
+ .acpi_match_table = ACPI_PTR(cros_ec_i2c_tunnel_acpi_id),
.of_match_table = of_match_ptr(cros_ec_i2c_of_match),
},
};
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 11caafa0e050..a773ba082321 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TI DAVINCI I2C adapter driver.
*
@@ -8,35 +9,25 @@
*
* ----------------------------------------------------------------------------
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
* ----------------------------------------------------------------------------
- *
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
+
#include <linux/clk.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/cpufreq.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of_device.h>
-#include <linux/platform_data/i2c-davinci.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
/* ----- global defines ----------------------------------------------- */
@@ -126,6 +117,8 @@
/* timeout for pm runtime autosuspend */
#define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */
+#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100
+
struct davinci_i2c_dev {
struct device *dev;
void __iomem *base;
@@ -141,13 +134,10 @@ struct davinci_i2c_dev {
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
- struct davinci_i2c_platform_data *pdata;
-};
-
-/* default platform data to use if not supplied in the platform_device */
-static struct davinci_i2c_platform_data davinci_i2c_platform_data_default = {
- .bus_freq = 100,
- .bus_delay = 0,
+ /* standard bus frequency (kHz) */
+ unsigned int bus_freq;
+ /* Chip has a ICPFUNC register */
+ bool has_pfunc;
};
static inline void davinci_i2c_write_reg(struct davinci_i2c_dev *i2c_dev,
@@ -177,14 +167,12 @@ static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev,
static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
{
- struct davinci_i2c_platform_data *pdata = dev->pdata;
u16 psc;
u32 clk;
u32 d;
u32 clkh;
u32 clkl;
u32 input_clock = clk_get_rate(dev->clk);
- struct device_node *of_node = dev->dev->of_node;
/* NOTE: I2C Clock divider programming info
* As per I2C specs the following formulas provide prescaler
@@ -218,19 +206,19 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
psc++; /* better to run under spec than over */
d = (psc >= 2) ? 5 : 7 - psc;
- if (of_node && of_device_is_compatible(of_node, "ti,keystone-i2c"))
+ if (device_is_compatible(dev->dev, "ti,keystone-i2c"))
d = 6;
- clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000));
+ clk = ((input_clock / (psc + 1)) / (dev->bus_freq * 1000));
/* Avoid driving the bus too fast because of rounding errors above */
- if (input_clock / (psc + 1) / clk > pdata->bus_freq * 1000)
+ if (input_clock / (psc + 1) / clk > dev->bus_freq * 1000)
clk++;
/*
* According to I2C-BUS Spec 2.1, in FAST-MODE LOW period should be at
* least 1.3uS, which is not the case with 50% duty cycle. Driving HIGH
* to LOW ratio as 1 to 2 is more safe.
*/
- if (pdata->bus_freq > 100)
+ if (dev->bus_freq > 100)
clkl = (clk << 1) / 3;
else
clkl = (clk >> 1);
@@ -264,15 +252,13 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
*/
static int i2c_davinci_init(struct davinci_i2c_dev *dev)
{
- struct davinci_i2c_platform_data *pdata = dev->pdata;
-
/* put I2C into reset */
davinci_i2c_reset_ctrl(dev, 0);
/* compute clock dividers */
i2c_davinci_calc_clk_dividers(dev);
- /* Respond at reserved "SMBus Host" slave address" (and zero);
+ /* Respond at reserved "SMBus Host" target address" (and zero);
* we seem to have no option to not respond...
*/
davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, DAVINCI_I2C_OWN_ADDRESS);
@@ -283,8 +269,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG));
dev_dbg(dev->dev, "CLKH = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG));
- dev_dbg(dev->dev, "bus_freq = %dkHz, bus_delay = %d\n",
- pdata->bus_freq, pdata->bus_delay);
+ dev_dbg(dev->dev, "bus_freq = %dkHz\n", dev->bus_freq);
/* Take the I2C module out of reset: */
@@ -318,12 +303,6 @@ static void davinci_i2c_unprepare_recovery(struct i2c_adapter *adap)
i2c_davinci_init(dev);
}
-static struct i2c_bus_recovery_info davinci_i2c_gpio_recovery_info = {
- .recover_bus = i2c_generic_scl_recovery,
- .prepare_recovery = davinci_i2c_prepare_recovery,
- .unprepare_recovery = davinci_i2c_unprepare_recovery,
-};
-
static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val)
{
struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
@@ -416,14 +395,13 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev)
}
/*
- * Low level master read/write transaction. This function is called
- * from i2c_davinci_xfer.
+ * Low level read/write transaction. This function is called from
+ * i2c_davinci_xfer.
*/
static int
i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
{
struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
- struct davinci_i2c_platform_data *pdata = dev->pdata;
u32 flag;
u16 w;
unsigned long time_left;
@@ -433,11 +411,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
return -EADDRNOTAVAIL;
}
- /* Introduce a delay, required for some boards (e.g Davinci EVM) */
- if (pdata->bus_delay)
- udelay(pdata->bus_delay);
-
- /* set the slave address */
+ /* set the target address */
davinci_i2c_write_reg(dev, DAVINCI_I2C_SAR_REG, msg->addr);
dev->buf = msg->buf;
@@ -449,10 +423,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
reinit_completion(&dev->cmd_complete);
dev->cmd_err = 0;
- /* Take I2C out of reset and configure it as master */
+ /* Take I2C out of reset and configure it as controller */
flag = DAVINCI_I2C_MDR_IRS | DAVINCI_I2C_MDR_MST;
- /* if the slave address is ten bit address, enable XA bit */
if (msg->flags & I2C_M_TEN)
flag |= DAVINCI_I2C_MDR_XA;
if (!(msg->flags & I2C_M_RD))
@@ -498,7 +471,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
time_left = wait_for_completion_timeout(&dev->cmd_complete,
dev->adapter.timeout);
if (!time_left) {
- dev_err(dev->dev, "controller timed out\n");
i2c_recover_bus(adap);
dev->buf_len = 0;
return -ETIMEDOUT;
@@ -548,10 +520,9 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
- ret = pm_runtime_get_sync(dev->dev);
+ ret = pm_runtime_resume_and_get(dev->dev);
if (ret < 0) {
dev_err(dev->dev, "Failed to runtime_get device: %d\n", ret);
- pm_runtime_put_noidle(dev->dev);
return ret;
}
@@ -572,7 +543,6 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = num;
out:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
@@ -580,7 +550,8 @@ out:
static u32 i2c_davinci_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_PROTOCOL_MANGLING;
}
static void terminate_read(struct davinci_i2c_dev *dev)
@@ -698,7 +669,7 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
break;
case DAVINCI_I2C_IVR_AAS:
- dev_dbg(dev->dev, "Address as slave interrupt\n");
+ dev_dbg(dev->dev, "Address as target interrupt\n");
break;
default:
@@ -755,8 +726,8 @@ static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev)
#endif
static const struct i2c_algorithm i2c_davinci_algo = {
- .master_xfer = i2c_davinci_xfer,
- .functionality = i2c_davinci_func,
+ .xfer = i2c_davinci_xfer,
+ .functionality = i2c_davinci_func,
};
static const struct of_device_id davinci_i2c_of_match[] = {
@@ -770,61 +741,36 @@ static int davinci_i2c_probe(struct platform_device *pdev)
{
struct davinci_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *mem;
- struct i2c_bus_recovery_info *rinfo;
int r, irq;
+ u32 prop;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- if (!irq)
- irq = -ENXIO;
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "can't get irq resource ret=%d\n", irq);
+ if (irq < 0)
return irq;
- }
- dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev),
- GFP_KERNEL);
- if (!dev) {
- dev_err(&pdev->dev, "Memory allocation failed\n");
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
return -ENOMEM;
- }
init_completion(&dev->cmd_complete);
dev->dev = &pdev->dev;
dev->irq = irq;
- dev->pdata = dev_get_platdata(&pdev->dev);
platform_set_drvdata(pdev, dev);
- if (!dev->pdata && pdev->dev.of_node) {
- u32 prop;
-
- dev->pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct davinci_i2c_platform_data), GFP_KERNEL);
- if (!dev->pdata)
- return -ENOMEM;
-
- memcpy(dev->pdata, &davinci_i2c_platform_data_default,
- sizeof(struct davinci_i2c_platform_data));
- if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
- &prop))
- dev->pdata->bus_freq = prop / 1000;
-
- dev->pdata->has_pfunc =
- of_property_read_bool(pdev->dev.of_node,
- "ti,has-pfunc");
- } else if (!dev->pdata) {
- dev->pdata = &davinci_i2c_platform_data_default;
- }
+ r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
+ if (r)
+ prop = DAVINCI_I2C_DEFAULT_BUS_FREQ;
+
+ dev->bus_freq = prop / 1000;
+
+ dev->has_pfunc = device_property_present(&pdev->dev, "ti,has-pfunc");
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk))
return PTR_ERR(dev->clk);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->base = devm_ioremap_resource(&pdev->dev, mem);
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dev->base)) {
return PTR_ERR(dev->base);
}
@@ -835,11 +781,10 @@ static int davinci_i2c_probe(struct platform_device *pdev)
pm_runtime_enable(dev->dev);
- r = pm_runtime_get_sync(dev->dev);
+ r = pm_runtime_resume_and_get(dev->dev);
if (r < 0) {
dev_err(dev->dev, "failed to runtime_get device: %d\n", r);
- pm_runtime_put_noidle(dev->dev);
- return r;
+ goto err_pm;
}
i2c_davinci_init(dev);
@@ -861,36 +806,20 @@ static int davinci_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
- strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
+ strscpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
adap->algo = &i2c_davinci_algo;
adap->dev.parent = &pdev->dev;
adap->timeout = DAVINCI_I2C_TIMEOUT;
- adap->dev.of_node = pdev->dev.of_node;
+ adap->dev.of_node = dev_of_node(&pdev->dev);
- if (dev->pdata->has_pfunc)
+ if (dev->has_pfunc)
adap->bus_recovery_info = &davinci_i2c_scl_recovery_info;
- else if (dev->pdata->gpio_recovery) {
- rinfo = &davinci_i2c_gpio_recovery_info;
- adap->bus_recovery_info = rinfo;
- rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl",
- GPIOD_OUT_HIGH_OPEN_DRAIN);
- if (IS_ERR(rinfo->scl_gpiod)) {
- r = PTR_ERR(rinfo->scl_gpiod);
- goto err_unuse_clocks;
- }
- rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN);
- if (IS_ERR(rinfo->sda_gpiod)) {
- r = PTR_ERR(rinfo->sda_gpiod);
- goto err_unuse_clocks;
- }
- }
adap->nr = pdev->id;
r = i2c_add_numbered_adapter(adap);
if (r)
goto err_unuse_clocks;
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
@@ -898,12 +827,13 @@ static int davinci_i2c_probe(struct platform_device *pdev)
err_unuse_clocks:
pm_runtime_dont_use_autosuspend(dev->dev);
pm_runtime_put_sync(dev->dev);
+err_pm:
pm_runtime_disable(dev->dev);
return r;
}
-static int davinci_i2c_remove(struct platform_device *pdev)
+static void davinci_i2c_remove(struct platform_device *pdev)
{
struct davinci_i2c_dev *dev = platform_get_drvdata(pdev);
int ret;
@@ -913,21 +843,16 @@ static int davinci_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&pdev->dev);
- return ret;
- }
-
- davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to resume device\n");
+ else
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0);
pm_runtime_dont_use_autosuspend(dev->dev);
pm_runtime_put_sync(dev->dev);
pm_runtime_disable(dev->dev);
-
- return 0;
}
-#ifdef CONFIG_PM
static int davinci_i2c_suspend(struct device *dev)
{
struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev);
@@ -951,24 +876,23 @@ static int davinci_i2c_resume(struct device *dev)
static const struct dev_pm_ops davinci_i2c_pm = {
.suspend = davinci_i2c_suspend,
.resume = davinci_i2c_resume,
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
-#define davinci_i2c_pm_ops (&davinci_i2c_pm)
-#else
-#define davinci_i2c_pm_ops NULL
-#endif
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:i2c_davinci");
+static const struct platform_device_id davinci_i2c_driver_ids[] = {
+ { .name = "i2c_davinci", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, davinci_i2c_driver_ids);
static struct platform_driver davinci_i2c_driver = {
.probe = davinci_i2c_probe,
.remove = davinci_i2c_remove,
+ .id_table = davinci_i2c_driver_ids,
.driver = {
.name = "i2c_davinci",
- .pm = davinci_i2c_pm_ops,
+ .pm = pm_sleep_ptr(&davinci_i2c_pm),
.of_match_table = davinci_i2c_of_match,
},
};
diff --git a/drivers/i2c/busses/i2c-designware-amdisp.c b/drivers/i2c/busses/i2c-designware-amdisp.c
new file mode 100644
index 000000000000..450793d5f839
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-amdisp.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Based on Synopsys DesignWare I2C adapter driver.
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/amd/isp4_misc.h>
+
+#include "i2c-designware-core.h"
+
+#define DRV_NAME "amd_isp_i2c_designware"
+#define AMD_ISP_I2C_INPUT_CLK 100 /* Mhz */
+
+static void amd_isp_dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *i2c_dev)
+{
+ pm_runtime_disable(i2c_dev->dev);
+
+ if (i2c_dev->shared_with_punit)
+ pm_runtime_put_noidle(i2c_dev->dev);
+}
+
+static inline u32 amd_isp_dw_i2c_get_clk_rate(struct dw_i2c_dev *i2c_dev)
+{
+ return AMD_ISP_I2C_INPUT_CLK * 1000;
+}
+
+static int amd_isp_dw_i2c_plat_probe(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *isp_i2c_dev;
+ struct i2c_adapter *adap;
+ int ret;
+
+ isp_i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c_dev), GFP_KERNEL);
+ if (!isp_i2c_dev)
+ return -ENOMEM;
+ isp_i2c_dev->dev = &pdev->dev;
+
+ pdev->dev.init_name = DRV_NAME;
+
+ /*
+ * Use the polling mode to send/receive the data, because
+ * no IRQ connection from ISP I2C
+ */
+ isp_i2c_dev->flags |= ACCESS_POLLING;
+ platform_set_drvdata(pdev, isp_i2c_dev);
+
+ isp_i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(isp_i2c_dev->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(isp_i2c_dev->base),
+ "failed to get IOMEM resource\n");
+
+ isp_i2c_dev->get_clk_rate_khz = amd_isp_dw_i2c_get_clk_rate;
+ ret = i2c_dw_fw_parse_and_configure(isp_i2c_dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to parse i2c dw fwnode and configure\n");
+
+ i2c_dw_configure(isp_i2c_dev);
+
+ adap = &isp_i2c_dev->adapter;
+ adap->owner = THIS_MODULE;
+ scnprintf(adap->name, sizeof(adap->name), AMDISP_I2C_ADAP_NAME);
+ ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
+ adap->dev.of_node = pdev->dev.of_node;
+ /* use dynamically allocated adapter id */
+ adap->nr = -1;
+
+ if (isp_i2c_dev->flags & ACCESS_NO_IRQ_SUSPEND)
+ dev_pm_set_driver_flags(&pdev->dev,
+ DPM_FLAG_SMART_PREPARE);
+ else
+ dev_pm_set_driver_flags(&pdev->dev,
+ DPM_FLAG_SMART_PREPARE |
+ DPM_FLAG_SMART_SUSPEND);
+
+ device_enable_async_suspend(&pdev->dev);
+
+ if (isp_i2c_dev->shared_with_punit)
+ pm_runtime_get_noresume(&pdev->dev);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ ret = i2c_dw_probe(isp_i2c_dev);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "i2c_dw_probe failed\n");
+ goto error_release_rpm;
+ }
+
+ pm_runtime_put_sync(&pdev->dev);
+
+ return 0;
+
+error_release_rpm:
+ amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
+ pm_runtime_put_sync(&pdev->dev);
+ return ret;
+}
+
+static void amd_isp_dw_i2c_plat_remove(struct platform_device *pdev)
+{
+ struct dw_i2c_dev *isp_i2c_dev = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+
+ i2c_del_adapter(&isp_i2c_dev->adapter);
+
+ i2c_dw_disable(isp_i2c_dev);
+
+ pm_runtime_put_sync(&pdev->dev);
+ amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
+}
+
+static int amd_isp_dw_i2c_plat_prepare(struct device *dev)
+{
+ /*
+ * If the ACPI companion device object is present for this device, it
+ * may be accessed during suspend and resume of other devices via I2C
+ * operation regions, so tell the PM core and middle layers to avoid
+ * skipping system suspend/resume callbacks for it in that case.
+ */
+ return !has_acpi_companion(dev);
+}
+
+static int amd_isp_dw_i2c_plat_runtime_suspend(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ if (i_dev->shared_with_punit)
+ return 0;
+
+ i2c_dw_disable(i_dev);
+ i2c_dw_prepare_clk(i_dev, false);
+
+ return 0;
+}
+
+static int amd_isp_dw_i2c_plat_suspend(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!i_dev)
+ return -ENODEV;
+
+ ret = amd_isp_dw_i2c_plat_runtime_suspend(dev);
+ if (!ret)
+ i2c_mark_adapter_suspended(&i_dev->adapter);
+
+ return ret;
+}
+
+static int amd_isp_dw_i2c_plat_runtime_resume(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ if (!i_dev)
+ return -ENODEV;
+
+ if (!i_dev->shared_with_punit)
+ i2c_dw_prepare_clk(i_dev, true);
+ if (i_dev->init)
+ i_dev->init(i_dev);
+
+ return 0;
+}
+
+static int amd_isp_dw_i2c_plat_resume(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ amd_isp_dw_i2c_plat_runtime_resume(dev);
+ i2c_mark_adapter_resumed(&i_dev->adapter);
+
+ return 0;
+}
+
+static const struct dev_pm_ops amd_isp_dw_i2c_dev_pm_ops = {
+ .prepare = pm_sleep_ptr(amd_isp_dw_i2c_plat_prepare),
+ LATE_SYSTEM_SLEEP_PM_OPS(amd_isp_dw_i2c_plat_suspend, amd_isp_dw_i2c_plat_resume)
+ RUNTIME_PM_OPS(amd_isp_dw_i2c_plat_runtime_suspend, amd_isp_dw_i2c_plat_runtime_resume, NULL)
+};
+
+/* Work with hotplug and coldplug */
+MODULE_ALIAS("platform:amd_isp_i2c_designware");
+
+static struct platform_driver amd_isp_dw_i2c_driver = {
+ .probe = amd_isp_dw_i2c_plat_probe,
+ .remove = amd_isp_dw_i2c_plat_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = pm_ptr(&amd_isp_dw_i2c_dev_pm_ops),
+ },
+};
+module_platform_driver(amd_isp_dw_i2c_driver);
+
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter in AMD ISP");
+MODULE_IMPORT_NS("I2C_DW");
+MODULE_IMPORT_NS("I2C_DW_COMMON");
+MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vengutta@amd.com>");
+MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
+MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c
new file mode 100644
index 000000000000..404571ad61a8
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-amdpsp.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/psp-platform-access.h>
+#include <linux/psp.h>
+#include <linux/workqueue.h>
+
+#include "i2c-designware-core.h"
+
+#define PSP_I2C_RESERVATION_TIME_MS 100
+
+#define PSP_I2C_REQ_RETRY_CNT 400
+#define PSP_I2C_REQ_RETRY_DELAY_US (25 * USEC_PER_MSEC)
+#define PSP_I2C_REQ_STS_OK 0x0
+#define PSP_I2C_REQ_STS_BUS_BUSY 0x1
+#define PSP_I2C_REQ_STS_INV_PARAM 0x3
+
+enum psp_i2c_req_type {
+ PSP_I2C_REQ_ACQUIRE,
+ PSP_I2C_REQ_RELEASE,
+ PSP_I2C_REQ_MAX
+};
+
+struct psp_i2c_req {
+ struct psp_req_buffer_hdr hdr;
+ enum psp_i2c_req_type type;
+};
+
+static DEFINE_MUTEX(psp_i2c_access_mutex);
+static unsigned long psp_i2c_sem_acquired;
+static u32 psp_i2c_access_count;
+static bool psp_i2c_mbox_fail;
+static struct device *psp_i2c_dev;
+
+static int (*_psp_send_i2c_req)(struct psp_i2c_req *req);
+
+/* Helper to verify status returned by PSP */
+static int check_i2c_req_sts(struct psp_i2c_req *req)
+{
+ u32 status;
+
+ /* Status field in command-response buffer is updated by PSP */
+ status = READ_ONCE(req->hdr.status);
+
+ switch (status) {
+ case PSP_I2C_REQ_STS_OK:
+ return 0;
+ case PSP_I2C_REQ_STS_BUS_BUSY:
+ return -EBUSY;
+ case PSP_I2C_REQ_STS_INV_PARAM:
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Errors in x86-PSP i2c-arbitration protocol may occur at two levels:
+ * 1. mailbox communication - PSP is not operational or some IO errors with
+ * basic communication had happened.
+ * 2. i2c-requests - PSP refuses to grant i2c arbitration to x86 for too long.
+ *
+ * In order to distinguish between these in error handling code all mailbox
+ * communication errors on the first level (from CCP symbols) will be passed
+ * up and if -EIO is returned the second level will be checked.
+ */
+static int psp_send_i2c_req_cezanne(struct psp_i2c_req *req)
+{
+ int ret;
+
+ ret = psp_send_platform_access_msg(PSP_I2C_REQ_BUS_CMD, (struct psp_request *)req);
+ if (ret == -EIO)
+ return check_i2c_req_sts(req);
+
+ return ret;
+}
+
+static int psp_send_i2c_req_doorbell(struct psp_i2c_req *req)
+{
+ int ret;
+
+ ret = psp_ring_platform_doorbell(req->type, &req->hdr.status);
+ if (ret == -EIO)
+ return check_i2c_req_sts(req);
+
+ return ret;
+}
+
+static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type)
+{
+ struct psp_i2c_req *req;
+ unsigned long start;
+ int status, ret;
+
+ /* Allocate command-response buffer */
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->hdr.payload_size = sizeof(*req);
+ req->type = i2c_req_type;
+
+ start = jiffies;
+ ret = read_poll_timeout(_psp_send_i2c_req, status,
+ (status != -EBUSY),
+ PSP_I2C_REQ_RETRY_DELAY_US,
+ PSP_I2C_REQ_RETRY_CNT * PSP_I2C_REQ_RETRY_DELAY_US,
+ 0, req);
+ if (ret) {
+ dev_err(psp_i2c_dev, "Timed out waiting for PSP to %s I2C bus\n",
+ (i2c_req_type == PSP_I2C_REQ_ACQUIRE) ?
+ "release" : "acquire");
+ goto cleanup;
+ }
+
+ ret = status;
+ if (ret) {
+ dev_err(psp_i2c_dev, "PSP communication error\n");
+ goto cleanup;
+ }
+
+ dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n",
+ jiffies_to_msecs(jiffies - start));
+
+cleanup:
+ if (ret) {
+ dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n");
+ psp_i2c_mbox_fail = true;
+ }
+
+ kfree(req);
+ return ret;
+}
+
+static void release_bus(void)
+{
+ int status;
+
+ if (!psp_i2c_sem_acquired)
+ return;
+
+ status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE);
+ if (status)
+ return;
+
+ dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n",
+ jiffies_to_msecs(jiffies - psp_i2c_sem_acquired));
+
+ psp_i2c_sem_acquired = 0;
+}
+
+static void psp_release_i2c_bus_deferred(struct work_struct *work)
+{
+ guard(mutex)(&psp_i2c_access_mutex);
+
+ /*
+ * If there is any pending transaction, cannot release the bus here.
+ * psp_release_i2c_bus() will take care of this later.
+ */
+ if (psp_i2c_access_count)
+ return;
+
+ release_bus();
+}
+static DECLARE_DELAYED_WORK(release_queue, psp_release_i2c_bus_deferred);
+
+static int psp_acquire_i2c_bus(void)
+{
+ int status;
+
+ guard(mutex)(&psp_i2c_access_mutex);
+
+ /* Return early if mailbox malfunctioned */
+ if (psp_i2c_mbox_fail)
+ return 0;
+
+ psp_i2c_access_count++;
+
+ /*
+ * No need to request bus arbitration once we are inside semaphore
+ * reservation period.
+ */
+ if (psp_i2c_sem_acquired)
+ return 0;
+
+ status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE);
+ if (status)
+ return 0;
+
+ psp_i2c_sem_acquired = jiffies;
+
+ schedule_delayed_work(&release_queue,
+ msecs_to_jiffies(PSP_I2C_RESERVATION_TIME_MS));
+
+ /*
+ * In case of errors with PSP arbitrator psp_i2c_mbox_fail variable is
+ * set above. As a consequence consecutive calls to acquire will bypass
+ * communication with PSP. At any case i2c bus is granted to the caller,
+ * thus always return success.
+ */
+ return 0;
+}
+
+static void psp_release_i2c_bus(void)
+{
+ guard(mutex)(&psp_i2c_access_mutex);
+
+ /* Return early if mailbox was malfunctioned */
+ if (psp_i2c_mbox_fail)
+ return;
+
+ /*
+ * If we are last owner of PSP semaphore, need to release arbitration
+ * via mailbox.
+ */
+ psp_i2c_access_count--;
+ if (psp_i2c_access_count)
+ return;
+
+ /*
+ * Send a release command to PSP if the semaphore reservation timeout
+ * elapsed but x86 still owns the controller.
+ */
+ if (!delayed_work_pending(&release_queue))
+ release_bus();
+}
+
+/*
+ * Locking methods are based on the default implementation from
+ * drivers/i2c/i2c-core-base.c, but with PSP acquire and release operations
+ * added. With this in place we can ensure that i2c clients on the bus shared
+ * with PSP are able to lock HW access to the bus for arbitrary number of
+ * operations - that is e.g. write-wait-read.
+ */
+static void i2c_adapter_dw_psp_lock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ psp_acquire_i2c_bus();
+ rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
+}
+
+static int i2c_adapter_dw_psp_trylock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = rt_mutex_trylock(&adapter->bus_lock);
+ if (ret)
+ return ret;
+
+ psp_acquire_i2c_bus();
+
+ return ret;
+}
+
+static void i2c_adapter_dw_psp_unlock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ psp_release_i2c_bus();
+ rt_mutex_unlock(&adapter->bus_lock);
+}
+
+static const struct i2c_lock_operations i2c_dw_psp_lock_ops = {
+ .lock_bus = i2c_adapter_dw_psp_lock_bus,
+ .trylock_bus = i2c_adapter_dw_psp_trylock_bus,
+ .unlock_bus = i2c_adapter_dw_psp_unlock_bus,
+};
+
+int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev)
+{
+ struct pci_dev *rdev;
+
+ if (!IS_REACHABLE(CONFIG_CRYPTO_DEV_CCP_DD))
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (!(dev->flags & ARBITRATION_SEMAPHORE))
+ return -ENODEV;
+
+ /* Allow to bind only one instance of a driver */
+ if (psp_i2c_dev)
+ return -EEXIST;
+
+ /* Cezanne uses platform mailbox, Mendocino and later use doorbell */
+ rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
+ if (rdev->device == 0x1630)
+ _psp_send_i2c_req = psp_send_i2c_req_cezanne;
+ else
+ _psp_send_i2c_req = psp_send_i2c_req_doorbell;
+ pci_dev_put(rdev);
+
+ if (psp_check_platform_access_status())
+ return -EPROBE_DEFER;
+
+ psp_i2c_dev = dev->dev;
+
+ dev_info(psp_i2c_dev, "I2C bus managed by AMD PSP\n");
+
+ /*
+ * Install global locking callbacks for adapter as well as internal i2c
+ * controller locks.
+ */
+ dev->adapter.lock_ops = &i2c_dw_psp_lock_ops;
+ dev->acquire_lock = psp_acquire_i2c_bus;
+ dev->release_lock = psp_release_i2c_bus;
+
+ return 0;
+}
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c
index 33da07d64494..45774aa47c28 100644
--- a/drivers/i2c/busses/i2c-designware-baytrail.c
+++ b/drivers/i2c/busses/i2c-designware-baytrail.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Intel BayTrail PMIC I2C bus semaphore implementaion
+ * Intel BayTrail PMIC I2C bus semaphore implementation
* Copyright (c) 2014, Intel Corporation.
*/
#include <linux/device.h>
@@ -12,25 +12,25 @@
#include "i2c-designware-core.h"
-int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
+int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev)
{
acpi_status status;
unsigned long long shared_host = 0;
acpi_handle handle;
- if (!dev || !dev->dev)
- return 0;
+ if (!dev)
+ return -ENODEV;
handle = ACPI_HANDLE(dev->dev);
if (!handle)
- return 0;
+ return -ENODEV;
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
if (ACPI_FAILURE(status))
- return 0;
+ return -ENODEV;
if (!shared_host)
- return 0;
+ return -ENODEV;
if (!iosf_mbi_available())
return -EPROBE_DEFER;
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index a4730111d290..5b1e8f74c4ac 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -8,21 +8,33 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
+
+#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW_COMMON"
+
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/errno.h>
+#include <linux/device.h>
#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/units.h>
#include "i2c-designware-core.h"
-static char *abort_sources[] = {
+static const char *const abort_sources[] = {
[ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
[ABRT_10ADDR1_NOACK] =
@@ -53,110 +65,384 @@ static char *abort_sources[] = {
"incorrect slave-transmitter mode configuration",
};
-u32 dw_readl(struct dw_i2c_dev *dev, int offset)
+static int dw_reg_read(void *context, unsigned int reg, unsigned int *val)
{
- u32 value;
+ struct dw_i2c_dev *dev = context;
- if (dev->flags & ACCESS_16BIT)
- value = readw_relaxed(dev->base + offset) |
- (readw_relaxed(dev->base + offset + 2) << 16);
- else
- value = readl_relaxed(dev->base + offset);
+ *val = readl(dev->base + reg);
- if (dev->flags & ACCESS_SWAP)
- return swab32(value);
- else
- return value;
+ return 0;
}
-void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
+static int dw_reg_write(void *context, unsigned int reg, unsigned int val)
{
- if (dev->flags & ACCESS_SWAP)
- b = swab32(b);
+ struct dw_i2c_dev *dev = context;
- if (dev->flags & ACCESS_16BIT) {
- writew_relaxed((u16)b, dev->base + offset);
- writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
- } else {
- writel_relaxed(b, dev->base + offset);
- }
+ writel(val, dev->base + reg);
+
+ return 0;
+}
+
+static int dw_reg_read_swab(void *context, unsigned int reg, unsigned int *val)
+{
+ struct dw_i2c_dev *dev = context;
+
+ *val = swab32(readl(dev->base + reg));
+
+ return 0;
+}
+
+static int dw_reg_write_swab(void *context, unsigned int reg, unsigned int val)
+{
+ struct dw_i2c_dev *dev = context;
+
+ writel(swab32(val), dev->base + reg);
+
+ return 0;
+}
+
+static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val)
+{
+ struct dw_i2c_dev *dev = context;
+
+ *val = readw(dev->base + reg) |
+ (readw(dev->base + reg + 2) << 16);
+
+ return 0;
+}
+
+static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val)
+{
+ struct dw_i2c_dev *dev = context;
+
+ writew(val, dev->base + reg);
+ writew(val >> 16, dev->base + reg + 2);
+
+ return 0;
}
/**
- * i2c_dw_set_reg_access() - Set register access flags
+ * i2c_dw_init_regmap() - Initialize registers map
* @dev: device private data
*
- * Autodetects needed register access mode and sets access flags accordingly.
- * This must be called before doing any other register access.
+ * Autodetects needed register access mode and creates the regmap with
+ * corresponding read/write callbacks. This must be called before doing any
+ * other register access.
+ *
+ * Return: 0 on success, or negative errno otherwise.
*/
-int i2c_dw_set_reg_access(struct dw_i2c_dev *dev)
+int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
{
+ struct regmap_config map_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .disable_locking = true,
+ .reg_read = dw_reg_read,
+ .reg_write = dw_reg_write,
+ .max_register = DW_IC_COMP_TYPE,
+ };
u32 reg;
int ret;
+ /*
+ * Skip detecting the registers map configuration if the regmap has
+ * already been provided by a higher code.
+ */
+ if (dev->map)
+ return 0;
+
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
- reg = dw_readl(dev, DW_IC_COMP_TYPE);
+ reg = readl(dev->base + DW_IC_COMP_TYPE);
i2c_dw_release_lock(dev);
+ if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+ map_cfg.max_register = AMD_UCSI_INTR_REG;
+
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
- /* Configure register endianess access */
- dev->flags |= ACCESS_SWAP;
+ map_cfg.reg_read = dw_reg_read_swab;
+ map_cfg.reg_write = dw_reg_write_swab;
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
- /* Configure register access mode 16bit */
- dev->flags |= ACCESS_16BIT;
+ map_cfg.reg_read = dw_reg_read_word;
+ map_cfg.reg_write = dw_reg_write_word;
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
dev_err(dev->dev,
"Unknown Synopsys component type: 0x%08x\n", reg);
return -ENODEV;
}
+ /*
+ * Note we'll check the return value of the regmap IO accessors only
+ * at the probe stage. The rest of the code won't do this because
+ * basically we have MMIO-based regmap, so none of the read/write methods
+ * can fail.
+ */
+ dev->map = devm_regmap_init(dev->dev, NULL, dev, &map_cfg);
+ if (IS_ERR(dev->map)) {
+ dev_err(dev->dev, "Failed to init the registers map\n");
+ return PTR_ERR(dev->map);
+ }
+
return 0;
}
-u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
+static const u32 supported_speeds[] = {
+ I2C_MAX_HIGH_SPEED_MODE_FREQ,
+ I2C_MAX_FAST_MODE_PLUS_FREQ,
+ I2C_MAX_FAST_MODE_FREQ,
+ I2C_MAX_STANDARD_MODE_FREQ,
+};
+
+static int i2c_dw_validate_speed(struct dw_i2c_dev *dev)
{
+ struct i2c_timings *t = &dev->timings;
+ unsigned int i;
+
/*
- * DesignWare I2C core doesn't seem to have solid strategy to meet
- * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
- * will result in violation of the tHD;STA spec.
+ * Only standard mode at 100kHz, fast mode at 400kHz,
+ * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
*/
- if (cond)
- /*
- * Conditional expression:
- *
- * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
- *
- * This is based on the DW manuals, and represents an ideal
- * configuration. The resulting I2C bus speed will be
- * faster than any of the others.
- *
- * If your hardware is free from tHD;STA issue, try this one.
- */
- return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
+ for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) {
+ if (t->bus_freq_hz == supported_speeds[i])
+ return 0;
+ }
+
+ dev_err(dev->dev,
+ "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
+ t->bus_freq_hz);
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+#include <linux/platform_device.h>
+
+#define MSCC_ICPU_CFG_TWI_DELAY 0x0
+#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
+#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
+
+static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
+{
+ writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
+ dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
+
+ return 0;
+}
+
+static void i2c_dw_of_configure(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_MSCC_OCELOT:
+ dev->ext = devm_platform_ioremap_resource(pdev, 1);
+ if (!IS_ERR(dev->ext))
+ dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
+ break;
+ default:
+ break;
+ }
+}
+
+#else /* CONFIG_OF */
+
+static inline void i2c_dw_of_configure(struct device *device) { }
+
+#endif /* CONFIG_OF */
+
+#ifdef CONFIG_ACPI
+
+#include <linux/dmi.h>
+
+/*
+ * The HCNT/LCNT information coming from ACPI should be the most accurate
+ * for given platform. However, some systems get it wrong. On such systems
+ * we get better results by calculating those based on the input clock.
+ */
+static const struct dmi_system_id i2c_dw_no_acpi_params[] = {
+ {
+ .ident = "Dell Inspiron 7348",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7348"),
+ },
+ },
+ {}
+};
+
+static void i2c_dw_acpi_params(struct device *device, char method[],
+ u16 *hcnt, u16 *lcnt, u32 *sda_hold)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ acpi_handle handle = ACPI_HANDLE(device);
+ union acpi_object *obj;
+
+ if (dmi_check_system(i2c_dw_no_acpi_params))
+ return;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf)))
+ return;
+
+ obj = (union acpi_object *)buf.pointer;
+ if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) {
+ const union acpi_object *objs = obj->package.elements;
+
+ *hcnt = (u16)objs[0].integer.value;
+ *lcnt = (u16)objs[1].integer.value;
+ *sda_hold = (u32)objs[2].integer.value;
+ }
+
+ kfree(buf.pointer);
+}
+
+static void i2c_dw_acpi_configure(struct device *device)
+{
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+ struct i2c_timings *t = &dev->timings;
+ u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
+
+ /*
+ * Try to get SDA hold time and *CNT values from an ACPI method for
+ * selected speed modes.
+ */
+ i2c_dw_acpi_params(device, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht);
+ i2c_dw_acpi_params(device, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
+ i2c_dw_acpi_params(device, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht);
+ i2c_dw_acpi_params(device, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
+
+ switch (t->bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ dev->sda_hold_time = ss_ht;
+ break;
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ dev->sda_hold_time = fp_ht;
+ break;
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ dev->sda_hold_time = hs_ht;
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ default:
+ dev->sda_hold_time = fs_ht;
+ break;
+ }
+}
+
+static u32 i2c_dw_acpi_round_bus_speed(struct device *device)
+{
+ u32 acpi_speed;
+ int i;
+
+ acpi_speed = i2c_acpi_find_bus_speed(device);
+ /*
+ * Some DSDTs use a non standard speed, round down to the lowest
+ * standard speed.
+ */
+ for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) {
+ if (acpi_speed >= supported_speeds[i])
+ return supported_speeds[i];
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_ACPI */
+
+static inline void i2c_dw_acpi_configure(struct device *device) { }
+
+static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0; }
+
+#endif /* CONFIG_ACPI */
+
+static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
+{
+ u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev);
+ struct i2c_timings *t = &dev->timings;
+
+ /*
+ * Find bus speed from the "clock-frequency" device property, ACPI
+ * or by using fast mode if neither is set.
+ */
+ if (acpi_speed && t->bus_freq_hz)
+ t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
+ else if (acpi_speed || t->bus_freq_hz)
+ t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
else
- /*
- * Conditional expression:
- *
- * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
- *
- * This is just experimental rule; the tHD;STA period turned
- * out to be proportinal to (_HCNT + 3). With this setting,
- * we could meet both tHIGH and tHD;STA timing specs.
- *
- * If unsure, you'd better to take this alternative.
- *
- * The reason why we need to take into account "tf" here,
- * is the same as described in i2c_dw_scl_lcnt().
- */
- return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000
- - 3 + offset;
+ t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
}
-u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
+int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev)
{
+ struct i2c_timings *t = &dev->timings;
+ struct device *device = dev->dev;
+ struct fwnode_handle *fwnode = dev_fwnode(device);
+
+ i2c_parse_fw_timings(device, t, false);
+
+ if (device_property_read_u32(device, "snps,bus-capacitance-pf", &dev->bus_capacitance_pF))
+ dev->bus_capacitance_pF = 100;
+
+ dev->clk_freq_optimized = device_property_read_bool(device, "snps,clk-freq-optimized");
+
+ i2c_dw_adjust_bus_speed(dev);
+
+ if (is_of_node(fwnode))
+ i2c_dw_of_configure(device);
+ else if (is_acpi_node(fwnode))
+ i2c_dw_acpi_configure(device);
+
+ return i2c_dw_validate_speed(dev);
+}
+EXPORT_SYMBOL_GPL(i2c_dw_fw_parse_and_configure);
+
+static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg)
+{
+ u32 val;
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return 0;
+
+ ret = regmap_read(dev->map, reg, &val);
+ i2c_dw_release_lock(dev);
+
+ return ret ? 0 : val;
+}
+
+u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
+ u32 tSYMBOL, u32 tf, int offset)
+{
+ if (!ic_clk)
+ return i2c_dw_read_scl_reg(dev, reg);
+
+ /*
+ * Conditional expression:
+ *
+ * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
+ *
+ * This is just experimental rule; the tHD;STA period turned
+ * out to be proportinal to (_HCNT + 3). With this setting,
+ * we could meet both tHIGH and tHD;STA timing specs.
+ *
+ * If unsure, you'd better to take this alternative.
+ *
+ * The reason why we need to take into account "tf" here,
+ * is the same as described in i2c_dw_scl_lcnt().
+ */
+ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tSYMBOL + tf), MICRO) - 3 + offset;
+}
+
+u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
+ u32 tLOW, u32 tf, int offset)
+{
+ if (!ic_clk)
+ return i2c_dw_read_scl_reg(dev, reg);
+
/*
* Conditional expression:
*
@@ -168,12 +454,12 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
- return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
+ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) - 1 + offset;
}
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
{
- u32 reg;
+ unsigned int reg;
int ret;
ret = i2c_dw_acquire_lock(dev);
@@ -181,19 +467,25 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
return ret;
/* Configure SDA Hold Time if required */
- reg = dw_readl(dev, DW_IC_COMP_VERSION);
+ ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &reg);
+ if (ret)
+ goto err_release_lock;
+
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
if (!dev->sda_hold_time) {
/* Keep previous hold time setting if no one set it */
- dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
+ ret = regmap_read(dev->map, DW_IC_SDA_HOLD,
+ &dev->sda_hold_time);
+ if (ret)
+ goto err_release_lock;
}
/*
* Workaround for avoiding TX arbitration lost in case I2C
- * slave pulls SDA down "too quickly" after falling egde of
+ * slave pulls SDA down "too quickly" after falling edge of
* SCL by enabling non-zero SDA RX hold. Specification says it
* extends incoming SDA low to high transition while SCL is
- * high but it apprears to help also above issue.
+ * high but it appears to help also above issue.
*/
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
@@ -209,14 +501,49 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
dev->sda_hold_time = 0;
}
+err_release_lock:
i2c_dw_release_lock(dev);
- return 0;
+ return ret;
}
void __i2c_dw_disable(struct dw_i2c_dev *dev)
{
+ struct i2c_timings *t = &dev->timings;
+ unsigned int raw_intr_stats, ic_stats;
+ unsigned int enable;
int timeout = 100;
+ bool abort_needed;
+ unsigned int status;
+ int ret;
+
+ regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &raw_intr_stats);
+ regmap_read(dev->map, DW_IC_STATUS, &ic_stats);
+ regmap_read(dev->map, DW_IC_ENABLE, &enable);
+
+ abort_needed = (raw_intr_stats & DW_IC_INTR_MST_ON_HOLD) ||
+ (ic_stats & DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY);
+ if (abort_needed) {
+ if (!(enable & DW_IC_ENABLE_ENABLE)) {
+ regmap_write(dev->map, DW_IC_ENABLE, DW_IC_ENABLE_ENABLE);
+ /*
+ * Wait 10 times the signaling period of the highest I2C
+ * transfer supported by the driver (for 400KHz this is
+ * 25us) to ensure the I2C ENABLE bit is already set
+ * as described in the DesignWare I2C databook.
+ */
+ fsleep(DIV_ROUND_CLOSEST_ULL(10 * MICRO, t->bus_freq_hz));
+ /* Set ENABLE bit before setting ABORT */
+ enable |= DW_IC_ENABLE_ENABLE;
+ }
+
+ regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT);
+ ret = regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, enable,
+ !(enable & DW_IC_ENABLE_ABORT), 10,
+ 100);
+ if (ret)
+ dev_err(dev->dev, "timeout while trying to abort current transfer\n");
+ }
do {
__i2c_dw_disable_nowait(dev);
@@ -224,12 +551,13 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev)
* The enable status register may be unimplemented, but
* in that case this test reads zero and exits the loop.
*/
- if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == 0)
+ regmap_read(dev->map, DW_IC_ENABLE_STATUS, &status);
+ if ((status & 1) == 0)
return;
/*
* Wait 10 times the signaling period of the highest I2C
- * transfer supported by the driver (for 400KHz this is
+ * transfer supported by the driver (for 400kHz this is
* 25us) as described in the DesignWare I2C databook.
*/
usleep_range(25, 250);
@@ -238,26 +566,39 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev)
dev_warn(dev->dev, "timeout in disabling adapter\n");
}
-unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
+u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev)
{
/*
* Clock is not necessary if we got LCNT/HCNT values directly from
* the platform code.
*/
- if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
+ if (!dev->get_clk_rate_khz) {
+ dev_dbg_once(dev->dev, "Callback get_clk_rate_khz() is not defined\n");
return 0;
+ }
return dev->get_clk_rate_khz(dev);
}
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare)
{
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
+ int ret;
+
+ if (prepare) {
+ /* Optional interface clock */
+ ret = clk_prepare_enable(dev->pclk);
+ if (ret)
+ return ret;
- if (prepare)
- return clk_prepare_enable(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret)
+ clk_disable_unprepare(dev->pclk);
+
+ return ret;
+ }
clk_disable_unprepare(dev->clk);
+ clk_disable_unprepare(dev->pclk);
+
return 0;
}
EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk);
@@ -289,22 +630,23 @@ void i2c_dw_release_lock(struct dw_i2c_dev *dev)
*/
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
- int timeout = TIMEOUT;
+ unsigned int status;
+ int ret;
- while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
- if (timeout <= 0) {
- dev_warn(dev->dev, "timeout waiting for bus ready\n");
- i2c_recover_bus(&dev->adapter);
+ ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
+ !(status & DW_IC_STATUS_ACTIVITY),
+ 1100, 20000);
+ if (ret) {
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
- if (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY)
- return -ETIMEDOUT;
- return 0;
- }
- timeout--;
- usleep_range(1000, 1100);
+ i2c_recover_bus(&dev->adapter);
+
+ regmap_read(dev->map, DW_IC_STATUS, &status);
+ if (!(status & DW_IC_STATUS_ACTIVITY))
+ ret = 0;
}
- return 0;
+ return ret;
}
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
@@ -324,10 +666,52 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
if (abort_source & DW_IC_TX_ARB_LOST)
return -EAGAIN;
- else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
+ if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
return -EINVAL; /* wrong msgs[] data */
- else
- return -EIO;
+
+ return -EIO;
+}
+
+int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
+{
+ u32 tx_fifo_depth, rx_fifo_depth;
+ unsigned int param;
+ int ret;
+
+ /* DW_IC_COMP_PARAM_1 not implement for IP issue */
+ if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP) {
+ dev->tx_fifo_depth = TXGBE_TX_FIFO_DEPTH;
+ dev->rx_fifo_depth = TXGBE_RX_FIFO_DEPTH;
+
+ return 0;
+ }
+
+ /*
+ * Try to detect the FIFO depth if not set by interface driver,
+ * the depth could be from 2 to 256 from HW spec.
+ */
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &param);
+ i2c_dw_release_lock(dev);
+ if (ret)
+ return ret;
+
+ tx_fifo_depth = ((param >> 16) & 0xff) + 1;
+ rx_fifo_depth = ((param >> 8) & 0xff) + 1;
+ if (!dev->tx_fifo_depth) {
+ dev->tx_fifo_depth = tx_fifo_depth;
+ dev->rx_fifo_depth = rx_fifo_depth;
+ } else if (tx_fifo_depth >= 2) {
+ dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth,
+ tx_fifo_depth);
+ dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth,
+ rx_fifo_depth);
+ }
+
+ return 0;
}
u32 i2c_dw_func(struct i2c_adapter *adap)
@@ -339,24 +723,100 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
+ unsigned int dummy;
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return;
+
/* Disable controller */
__i2c_dw_disable(dev);
- /* Disable all interupts */
- dw_writel(dev, 0, DW_IC_INTR_MASK);
- dw_readl(dev, DW_IC_CLR_INTR);
+ /* Disable all interrupts */
+ __i2c_dw_write_intr_mask(dev, 0);
+ regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+
+ i2c_dw_release_lock(dev);
}
+EXPORT_SYMBOL_GPL(i2c_dw_disable);
-void i2c_dw_disable_int(struct dw_i2c_dev *dev)
+int i2c_dw_probe(struct dw_i2c_dev *dev)
{
- dw_writel(dev, 0, DW_IC_INTR_MASK);
+ device_set_node(&dev->adapter.dev, dev_fwnode(dev->dev));
+
+ switch (dev->mode) {
+ case DW_IC_SLAVE:
+ return i2c_dw_probe_slave(dev);
+ case DW_IC_MASTER:
+ return i2c_dw_probe_master(dev);
+ default:
+ dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode);
+ return -EINVAL;
+ }
}
+EXPORT_SYMBOL_GPL(i2c_dw_probe);
-u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
+static int i2c_dw_prepare(struct device *device)
{
- return dw_readl(dev, DW_IC_COMP_PARAM_1);
+ /*
+ * If the ACPI companion device object is present for this device,
+ * it may be accessed during suspend and resume of other devices via
+ * I2C operation regions, so tell the PM core and middle layers to
+ * avoid skipping system suspend/resume callbacks for it in that case.
+ */
+ return !has_acpi_companion(device);
}
-EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
+
+static int i2c_dw_runtime_suspend(struct device *device)
+{
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+
+ if (dev->shared_with_punit)
+ return 0;
+
+ i2c_dw_disable(dev);
+ i2c_dw_prepare_clk(dev, false);
+
+ return 0;
+}
+
+static int i2c_dw_suspend(struct device *device)
+{
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+
+ i2c_mark_adapter_suspended(&dev->adapter);
+
+ return i2c_dw_runtime_suspend(device);
+}
+
+static int i2c_dw_runtime_resume(struct device *device)
+{
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+
+ if (!dev->shared_with_punit)
+ i2c_dw_prepare_clk(dev, true);
+
+ dev->init(dev);
+
+ return 0;
+}
+
+static int i2c_dw_resume(struct device *device)
+{
+ struct dw_i2c_dev *dev = dev_get_drvdata(device);
+
+ i2c_dw_runtime_resume(device);
+ i2c_mark_adapter_resumed(&dev->adapter);
+
+ return 0;
+}
+
+EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = {
+ .prepare = pm_sleep_ptr(i2c_dw_prepare),
+ LATE_SYSTEM_SLEEP_PM_OPS(i2c_dw_suspend, i2c_dw_resume)
+ RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL)
+};
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index b4a0b2b99a78..bb5ce0a382f9 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -9,191 +9,217 @@
* Copyright (C) 2009 Provigent Ltd.
*/
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
-#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
- I2C_FUNC_SMBUS_BYTE | \
- I2C_FUNC_SMBUS_BYTE_DATA | \
- I2C_FUNC_SMBUS_WORD_DATA | \
- I2C_FUNC_SMBUS_BLOCK_DATA | \
- I2C_FUNC_SMBUS_I2C_BLOCK)
-
-#define DW_IC_CON_MASTER 0x1
-#define DW_IC_CON_SPEED_STD 0x2
-#define DW_IC_CON_SPEED_FAST 0x4
-#define DW_IC_CON_SPEED_HIGH 0x6
-#define DW_IC_CON_SPEED_MASK 0x6
-#define DW_IC_CON_10BITADDR_SLAVE 0x8
-#define DW_IC_CON_10BITADDR_MASTER 0x10
-#define DW_IC_CON_RESTART_EN 0x20
-#define DW_IC_CON_SLAVE_DISABLE 0x40
-#define DW_IC_CON_STOP_DET_IFADDRESSED 0x80
-#define DW_IC_CON_TX_EMPTY_CTRL 0x100
-#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
+#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
+ I2C_FUNC_SMBUS_BYTE | \
+ I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | \
+ I2C_FUNC_SMBUS_BLOCK_DATA | \
+ I2C_FUNC_SMBUS_I2C_BLOCK)
+
+#define DW_IC_CON_MASTER BIT(0)
+#define DW_IC_CON_SPEED_STD (1 << 1)
+#define DW_IC_CON_SPEED_FAST (2 << 1)
+#define DW_IC_CON_SPEED_HIGH (3 << 1)
+#define DW_IC_CON_SPEED_MASK GENMASK(2, 1)
+#define DW_IC_CON_10BITADDR_SLAVE BIT(3)
+#define DW_IC_CON_10BITADDR_MASTER BIT(4)
+#define DW_IC_CON_RESTART_EN BIT(5)
+#define DW_IC_CON_SLAVE_DISABLE BIT(6)
+#define DW_IC_CON_STOP_DET_IFADDRESSED BIT(7)
+#define DW_IC_CON_TX_EMPTY_CTRL BIT(8)
+#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9)
+#define DW_IC_CON_BUS_CLEAR_CTRL BIT(11)
+
+#define DW_IC_DATA_CMD_DAT GENMASK(7, 0)
+#define DW_IC_DATA_CMD_FIRST_DATA_BYTE BIT(11)
/*
* Registers offset
*/
-#define DW_IC_CON 0x0
-#define DW_IC_TAR 0x4
-#define DW_IC_SAR 0x8
-#define DW_IC_DATA_CMD 0x10
-#define DW_IC_SS_SCL_HCNT 0x14
-#define DW_IC_SS_SCL_LCNT 0x18
-#define DW_IC_FS_SCL_HCNT 0x1c
-#define DW_IC_FS_SCL_LCNT 0x20
-#define DW_IC_HS_SCL_HCNT 0x24
-#define DW_IC_HS_SCL_LCNT 0x28
-#define DW_IC_INTR_STAT 0x2c
-#define DW_IC_INTR_MASK 0x30
-#define DW_IC_RAW_INTR_STAT 0x34
-#define DW_IC_RX_TL 0x38
-#define DW_IC_TX_TL 0x3c
-#define DW_IC_CLR_INTR 0x40
-#define DW_IC_CLR_RX_UNDER 0x44
-#define DW_IC_CLR_RX_OVER 0x48
-#define DW_IC_CLR_TX_OVER 0x4c
-#define DW_IC_CLR_RD_REQ 0x50
-#define DW_IC_CLR_TX_ABRT 0x54
-#define DW_IC_CLR_RX_DONE 0x58
-#define DW_IC_CLR_ACTIVITY 0x5c
-#define DW_IC_CLR_STOP_DET 0x60
-#define DW_IC_CLR_START_DET 0x64
-#define DW_IC_CLR_GEN_CALL 0x68
-#define DW_IC_ENABLE 0x6c
-#define DW_IC_STATUS 0x70
-#define DW_IC_TXFLR 0x74
-#define DW_IC_RXFLR 0x78
-#define DW_IC_SDA_HOLD 0x7c
-#define DW_IC_TX_ABRT_SOURCE 0x80
-#define DW_IC_ENABLE_STATUS 0x9c
-#define DW_IC_CLR_RESTART_DET 0xa8
-#define DW_IC_COMP_PARAM_1 0xf4
-#define DW_IC_COMP_VERSION 0xf8
-#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A
-#define DW_IC_COMP_TYPE 0xfc
-#define DW_IC_COMP_TYPE_VALUE 0x44570140
-
-#define DW_IC_INTR_RX_UNDER 0x001
-#define DW_IC_INTR_RX_OVER 0x002
-#define DW_IC_INTR_RX_FULL 0x004
-#define DW_IC_INTR_TX_OVER 0x008
-#define DW_IC_INTR_TX_EMPTY 0x010
-#define DW_IC_INTR_RD_REQ 0x020
-#define DW_IC_INTR_TX_ABRT 0x040
-#define DW_IC_INTR_RX_DONE 0x080
-#define DW_IC_INTR_ACTIVITY 0x100
-#define DW_IC_INTR_STOP_DET 0x200
-#define DW_IC_INTR_START_DET 0x400
-#define DW_IC_INTR_GEN_CALL 0x800
-#define DW_IC_INTR_RESTART_DET 0x1000
-
-#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
- DW_IC_INTR_TX_ABRT | \
- DW_IC_INTR_STOP_DET)
-#define DW_IC_INTR_MASTER_MASK (DW_IC_INTR_DEFAULT_MASK | \
- DW_IC_INTR_TX_EMPTY)
-#define DW_IC_INTR_SLAVE_MASK (DW_IC_INTR_DEFAULT_MASK | \
- DW_IC_INTR_RX_DONE | \
- DW_IC_INTR_RX_UNDER | \
- DW_IC_INTR_RD_REQ)
-
-#define DW_IC_STATUS_ACTIVITY 0x1
-#define DW_IC_STATUS_TFE BIT(2)
-#define DW_IC_STATUS_MASTER_ACTIVITY BIT(5)
-#define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6)
-
-#define DW_IC_SDA_HOLD_RX_SHIFT 16
-#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT)
-
-#define DW_IC_ERR_TX_ABRT 0x1
-
-#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
+#define DW_IC_CON 0x00
+#define DW_IC_TAR 0x04
+#define DW_IC_SAR 0x08
+#define DW_IC_DATA_CMD 0x10
+#define DW_IC_SS_SCL_HCNT 0x14
+#define DW_IC_SS_SCL_LCNT 0x18
+#define DW_IC_FS_SCL_HCNT 0x1c
+#define DW_IC_FS_SCL_LCNT 0x20
+#define DW_IC_HS_SCL_HCNT 0x24
+#define DW_IC_HS_SCL_LCNT 0x28
+#define DW_IC_INTR_STAT 0x2c
+#define DW_IC_INTR_MASK 0x30
+#define DW_IC_RAW_INTR_STAT 0x34
+#define DW_IC_RX_TL 0x38
+#define DW_IC_TX_TL 0x3c
+#define DW_IC_CLR_INTR 0x40
+#define DW_IC_CLR_RX_UNDER 0x44
+#define DW_IC_CLR_RX_OVER 0x48
+#define DW_IC_CLR_TX_OVER 0x4c
+#define DW_IC_CLR_RD_REQ 0x50
+#define DW_IC_CLR_TX_ABRT 0x54
+#define DW_IC_CLR_RX_DONE 0x58
+#define DW_IC_CLR_ACTIVITY 0x5c
+#define DW_IC_CLR_STOP_DET 0x60
+#define DW_IC_CLR_START_DET 0x64
+#define DW_IC_CLR_GEN_CALL 0x68
+#define DW_IC_ENABLE 0x6c
+#define DW_IC_STATUS 0x70
+#define DW_IC_TXFLR 0x74
+#define DW_IC_RXFLR 0x78
+#define DW_IC_SDA_HOLD 0x7c
+#define DW_IC_TX_ABRT_SOURCE 0x80
+#define DW_IC_ENABLE_STATUS 0x9c
+#define DW_IC_CLR_RESTART_DET 0xa8
+#define DW_IC_SMBUS_INTR_MASK 0xcc
+#define DW_IC_COMP_PARAM_1 0xf4
+#define DW_IC_COMP_VERSION 0xf8
+#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */
+#define DW_IC_COMP_TYPE 0xfc
+#define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */
+
+#define DW_IC_INTR_RX_UNDER BIT(0)
+#define DW_IC_INTR_RX_OVER BIT(1)
+#define DW_IC_INTR_RX_FULL BIT(2)
+#define DW_IC_INTR_TX_OVER BIT(3)
+#define DW_IC_INTR_TX_EMPTY BIT(4)
+#define DW_IC_INTR_RD_REQ BIT(5)
+#define DW_IC_INTR_TX_ABRT BIT(6)
+#define DW_IC_INTR_RX_DONE BIT(7)
+#define DW_IC_INTR_ACTIVITY BIT(8)
+#define DW_IC_INTR_STOP_DET BIT(9)
+#define DW_IC_INTR_START_DET BIT(10)
+#define DW_IC_INTR_GEN_CALL BIT(11)
+#define DW_IC_INTR_RESTART_DET BIT(12)
+#define DW_IC_INTR_MST_ON_HOLD BIT(13)
+
+#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
+ DW_IC_INTR_TX_ABRT | \
+ DW_IC_INTR_STOP_DET)
+#define DW_IC_INTR_MASTER_MASK (DW_IC_INTR_DEFAULT_MASK | \
+ DW_IC_INTR_TX_EMPTY)
+#define DW_IC_INTR_SLAVE_MASK (DW_IC_INTR_DEFAULT_MASK | \
+ DW_IC_INTR_RX_UNDER | \
+ DW_IC_INTR_RD_REQ)
+
+#define DW_IC_ENABLE_ENABLE BIT(0)
+#define DW_IC_ENABLE_ABORT BIT(1)
+
+#define DW_IC_STATUS_ACTIVITY BIT(0)
+#define DW_IC_STATUS_TFE BIT(2)
+#define DW_IC_STATUS_RFNE BIT(3)
+#define DW_IC_STATUS_MASTER_ACTIVITY BIT(5)
+#define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6)
+#define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7)
+
+#define DW_IC_SDA_HOLD_RX_SHIFT 16
+#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16)
+
+#define DW_IC_ERR_TX_ABRT 0x1
+
+#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2)
/*
- * status codes
+ * Sofware status flags
*/
-#define STATUS_IDLE 0x0
-#define STATUS_WRITE_IN_PROGRESS 0x1
-#define STATUS_READ_IN_PROGRESS 0x2
-
-#define TIMEOUT 20 /* ms */
+#define STATUS_ACTIVE BIT(0)
+#define STATUS_WRITE_IN_PROGRESS BIT(1)
+#define STATUS_READ_IN_PROGRESS BIT(2)
+#define STATUS_MASK GENMASK(2, 0)
/*
* operation modes
*/
-#define DW_IC_MASTER 0
-#define DW_IC_SLAVE 1
+#define DW_IC_MASTER 0
+#define DW_IC_SLAVE 1
/*
- * Hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
+ * Hardware abort codes from the DW_IC_TX_ABRT_SOURCE register.
*
- * Only expected abort codes are listed here
- * refer to the datasheet for the full list
+ * Only expected abort codes are listed here,
+ * refer to the datasheet for the full list.
*/
-#define ABRT_7B_ADDR_NOACK 0
-#define ABRT_10ADDR1_NOACK 1
-#define ABRT_10ADDR2_NOACK 2
-#define ABRT_TXDATA_NOACK 3
-#define ABRT_GCALL_NOACK 4
-#define ABRT_GCALL_READ 5
-#define ABRT_SBYTE_ACKDET 7
-#define ABRT_SBYTE_NORSTRT 9
-#define ABRT_10B_RD_NORSTRT 10
-#define ABRT_MASTER_DIS 11
-#define ARB_LOST 12
-#define ABRT_SLAVE_FLUSH_TXFIFO 13
-#define ABRT_SLAVE_ARBLOST 14
-#define ABRT_SLAVE_RD_INTX 15
-
-#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
-#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
-#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK)
-#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK)
-#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK)
-#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ)
-#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET)
-#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT)
-#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
-#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
-#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
-#define DW_IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX)
-#define DW_IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST)
-#define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO)
-
-#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
- DW_IC_TX_ABRT_10ADDR1_NOACK | \
- DW_IC_TX_ABRT_10ADDR2_NOACK | \
- DW_IC_TX_ABRT_TXDATA_NOACK | \
- DW_IC_TX_ABRT_GCALL_NOACK)
+#define ABRT_7B_ADDR_NOACK 0
+#define ABRT_10ADDR1_NOACK 1
+#define ABRT_10ADDR2_NOACK 2
+#define ABRT_TXDATA_NOACK 3
+#define ABRT_GCALL_NOACK 4
+#define ABRT_GCALL_READ 5
+#define ABRT_SBYTE_ACKDET 7
+#define ABRT_SBYTE_NORSTRT 9
+#define ABRT_10B_RD_NORSTRT 10
+#define ABRT_MASTER_DIS 11
+#define ARB_LOST 12
+#define ABRT_SLAVE_FLUSH_TXFIFO 13
+#define ABRT_SLAVE_ARBLOST 14
+#define ABRT_SLAVE_RD_INTX 15
+#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(ABRT_7B_ADDR_NOACK)
+#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(ABRT_10ADDR1_NOACK)
+#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(ABRT_10ADDR2_NOACK)
+#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(ABRT_TXDATA_NOACK)
+#define DW_IC_TX_ABRT_GCALL_NOACK BIT(ABRT_GCALL_NOACK)
+#define DW_IC_TX_ABRT_GCALL_READ BIT(ABRT_GCALL_READ)
+#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(ABRT_SBYTE_ACKDET)
+#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(ABRT_SBYTE_NORSTRT)
+#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(ABRT_10B_RD_NORSTRT)
+#define DW_IC_TX_ABRT_MASTER_DIS BIT(ABRT_MASTER_DIS)
+#define DW_IC_TX_ARB_LOST BIT(ARB_LOST)
+#define DW_IC_RX_ABRT_SLAVE_RD_INTX BIT(ABRT_SLAVE_RD_INTX)
+#define DW_IC_RX_ABRT_SLAVE_ARBLOST BIT(ABRT_SLAVE_ARBLOST)
+#define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO BIT(ABRT_SLAVE_FLUSH_TXFIFO)
+
+#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
+ DW_IC_TX_ABRT_10ADDR1_NOACK | \
+ DW_IC_TX_ABRT_10ADDR2_NOACK | \
+ DW_IC_TX_ABRT_TXDATA_NOACK | \
+ DW_IC_TX_ABRT_GCALL_NOACK)
+
+struct clk;
+struct device;
+struct reset_control;
/**
* struct dw_i2c_dev - private i2c-designware data
* @dev: driver model device node
+ * @map: IO registers map
+ * @sysmap: System controller registers map
* @base: IO registers pointer
+ * @ext: Extended IO registers pointer
* @cmd_complete: tx completion indicator
* @clk: input reference clock
+ * @pclk: clock required to access the registers
+ * @rst: optional reset for the controller
* @slave: represent an I2C slave device
- * @cmd_err: run time hadware error code
+ * @get_clk_rate_khz: callback to retrieve IP specific bus speed
+ * @cmd_err: run time hardware error code
* @msgs: points to an array of messages currently being transferred
* @msgs_num: the number of elements in msgs
- * @msg_write_idx: the element index of the current tx message in the msgs
- * array
+ * @msg_write_idx: the element index of the current tx message in the msgs array
* @tx_buf_len: the length of the current tx buffer
* @tx_buf: the current tx buffer
- * @msg_read_idx: the element index of the current rx message in the msgs
- * array
+ * @msg_read_idx: the element index of the current rx message in the msgs array
* @rx_buf_len: the length of the current rx buffer
* @rx_buf: the current rx buffer
* @msg_err: error status of the current transfer
* @status: i2c master status, one of STATUS_*
* @abort_source: copy of the TX_ABRT_SOURCE register
+ * @sw_mask: SW mask of DW_IC_INTR_MASK used in polling mode
* @irq: interrupt number for the i2c master
+ * @flags: platform specific flags like type of IO accessors or model
* @adapter: i2c subsystem adapter node
+ * @functionality: I2C_FUNC_* ORed bits to reflect what controller does support
+ * @master_cfg: configuration for the master device
* @slave_cfg: configuration for the slave device
* @tx_fifo_depth: depth of the hardware tx fifo
* @rx_fifo_depth: depth of the hardware rx fifo
@@ -210,11 +236,17 @@
* @hs_lcnt: high speed LCNT value
* @acquire_lock: function to acquire a hardware lock on the bus
* @release_lock: function to release a hardware lock on the bus
- * @shared_with_punit: true if this bus is shared with the SoCs PUNIT
- * @disable: function to disable the controller
- * @disable_int: function to disable all interrupts
+ * @semaphore_idx: Index of table with semaphore type attached to the bus. It's
+ * -1 if there is no semaphore.
+ * @shared_with_punit: true if this bus is shared with the SoC's PUNIT
* @init: function to initialize the I2C hardware
+ * @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
+ * @rinfo: I²C GPIO recovery information
+ * @bus_capacitance_pF: bus capacitance in picofarads
+ * @clk_freq_optimized: if this value is true, it means the hardware reduces
+ * its internal clock frequency by reducing the internal latency required
+ * to generate the high period and low period of SCL line.
*
* HCNT and LCNT parameters can be used if the platform knows more accurate
* values than the one computed based only on the input clock frequency.
@@ -222,14 +254,16 @@
*/
struct dw_i2c_dev {
struct device *dev;
+ struct regmap *map;
+ struct regmap *sysmap;
void __iomem *base;
void __iomem *ext;
struct completion cmd_complete;
struct clk *clk;
+ struct clk *pclk;
struct reset_control *rst;
- struct i2c_client *slave;
+ struct i2c_client *slave;
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
- struct dw_pci_controller *controller;
int cmd_err;
struct i2c_msg *msgs;
int msgs_num;
@@ -241,7 +275,8 @@ struct dw_i2c_dev {
u8 *rx_buf;
int msg_err;
unsigned int status;
- u32 abort_source;
+ unsigned int abort_source;
+ unsigned int sw_mask;
int irq;
u32 flags;
struct i2c_adapter adapter;
@@ -263,62 +298,118 @@ struct dw_i2c_dev {
u16 hs_lcnt;
int (*acquire_lock)(void);
void (*release_lock)(void);
+ int semaphore_idx;
bool shared_with_punit;
- void (*disable)(struct dw_i2c_dev *dev);
- void (*disable_int)(struct dw_i2c_dev *dev);
int (*init)(struct dw_i2c_dev *dev);
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
int mode;
struct i2c_bus_recovery_info rinfo;
+ u32 bus_capacitance_pF;
+ bool clk_freq_optimized;
};
-#define ACCESS_SWAP 0x00000001
-#define ACCESS_16BIT 0x00000002
-#define ACCESS_INTR_MASK 0x00000004
-#define ACCESS_NO_IRQ_SUSPEND 0x00000008
+#define ACCESS_INTR_MASK BIT(0)
+#define ACCESS_NO_IRQ_SUSPEND BIT(1)
+#define ARBITRATION_SEMAPHORE BIT(2)
+#define ACCESS_POLLING BIT(3)
+
+#define MODEL_MSCC_OCELOT BIT(8)
+#define MODEL_BAIKAL_BT1 BIT(9)
+#define MODEL_AMD_NAVI_GPU BIT(10)
+#define MODEL_WANGXUN_SP BIT(11)
+#define MODEL_MASK GENMASK(11, 8)
-#define MODEL_CHERRYTRAIL 0x00000100
-#define MODEL_MSCC_OCELOT 0x00000200
-#define MODEL_MASK 0x00000f00
+/*
+ * Enable UCSI interrupt by writing 0xd at register
+ * offset 0x474 specified in hardware specification.
+ */
+#define AMD_UCSI_INTR_REG 0x474
+#define AMD_UCSI_INTR_EN 0xd
-u32 dw_readl(struct dw_i2c_dev *dev, int offset);
-void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
-int i2c_dw_set_reg_access(struct dw_i2c_dev *dev);
-u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
-u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
+#define TXGBE_TX_FIFO_DEPTH 4
+#define TXGBE_RX_FIFO_DEPTH 1
+
+struct i2c_dw_semaphore_callbacks {
+ int (*probe)(struct dw_i2c_dev *dev);
+};
+
+int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
+u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
+ u32 tSYMBOL, u32 tf, int offset);
+u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
+ u32 tLOW, u32 tf, int offset);
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
-unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
+u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev);
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
void i2c_dw_release_lock(struct dw_i2c_dev *dev);
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev);
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
+int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev);
u32 i2c_dw_func(struct i2c_adapter *adap);
-void i2c_dw_disable(struct dw_i2c_dev *dev);
-void i2c_dw_disable_int(struct dw_i2c_dev *dev);
+
+extern const struct dev_pm_ops i2c_dw_dev_pm_ops;
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
{
- dw_writel(dev, 1, DW_IC_ENABLE);
+ dev->status |= STATUS_ACTIVE;
+ regmap_write(dev->map, DW_IC_ENABLE, 1);
}
static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev)
{
- dw_writel(dev, 0, DW_IC_ENABLE);
+ regmap_write(dev->map, DW_IC_ENABLE, 0);
+ dev->status &= ~STATUS_ACTIVE;
+}
+
+static inline void __i2c_dw_write_intr_mask(struct dw_i2c_dev *dev,
+ unsigned int intr_mask)
+{
+ unsigned int val = dev->flags & ACCESS_POLLING ? 0 : intr_mask;
+
+ regmap_write(dev->map, DW_IC_INTR_MASK, val);
+ dev->sw_mask = intr_mask;
+}
+
+static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev,
+ unsigned int *intr_mask)
+{
+ if (!(dev->flags & ACCESS_POLLING))
+ regmap_read(dev->map, DW_IC_INTR_MASK, intr_mask);
+ else
+ *intr_mask = dev->sw_mask;
}
void __i2c_dw_disable(struct dw_i2c_dev *dev);
+void i2c_dw_disable(struct dw_i2c_dev *dev);
+
+extern void i2c_dw_configure_master(struct dw_i2c_dev *dev);
+extern int i2c_dw_probe_master(struct dw_i2c_dev *dev);
-extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
-extern int i2c_dw_probe(struct dw_i2c_dev *dev);
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_SLAVE)
+extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev);
extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev);
#else
+static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { }
static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; }
#endif
+static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
+{
+ if (i2c_detect_slave_mode(dev->dev))
+ i2c_dw_configure_slave(dev);
+ else
+ i2c_dw_configure_master(dev);
+}
+
+int i2c_dw_probe(struct dw_i2c_dev *dev);
+
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
-extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev);
-#else
-static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; }
+int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
+#endif
+
+#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_AMDPSP)
+int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev);
#endif
+
+int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 8d1bc44d2530..45bfca05bb30 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -8,6 +8,9 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
+
+#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW"
+
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -17,35 +20,44 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include "i2c-designware-core.h"
+#define AMD_TIMEOUT_MIN_US 25
+#define AMD_TIMEOUT_MAX_US 250
+#define AMD_MASTERCFG_MASK GENMASK(15, 0)
+
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
{
/* Configure Tx/Rx FIFO threshold levels */
- dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
- dw_writel(dev, 0, DW_IC_RX_TL);
+ regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);
+ regmap_write(dev->map, DW_IC_RX_TL, 0);
/* Configure the I2C master */
- dw_writel(dev, dev->master_cfg, DW_IC_CON);
+ regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
}
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
{
- const char *mode_str, *fp_str = "";
- u32 comp_param1;
+ unsigned int comp_param1;
u32 sda_falling_time, scl_falling_time;
struct i2c_timings *t = &dev->timings;
+ const char *fp_str = "";
u32 ic_clk;
int ret;
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
- comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
+
+ ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &comp_param1);
i2c_dw_release_lock(dev);
+ if (ret)
+ return ret;
/* Set standard and fast speed dividers for high/low periods */
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
@@ -55,13 +67,16 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
if (!dev->ss_hcnt || !dev->ss_lcnt) {
ic_clk = i2c_dw_clk_rate(dev);
dev->ss_hcnt =
- i2c_dw_scl_hcnt(ic_clk,
+ i2c_dw_scl_hcnt(dev,
+ DW_IC_SS_SCL_HCNT,
+ ic_clk,
4000, /* tHD;STA = tHIGH = 4.0 us */
sda_falling_time,
- 0, /* 0: DW default, 1: Ideal */
0); /* No offset */
dev->ss_lcnt =
- i2c_dw_scl_lcnt(ic_clk,
+ i2c_dw_scl_lcnt(dev,
+ DW_IC_SS_SCL_LCNT,
+ ic_clk,
4700, /* tLOW = 4.7 us */
scl_falling_time,
0); /* No offset */
@@ -74,16 +89,32 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
* difference is the timing parameter values since the registers are
* the same.
*/
- if (t->bus_freq_hz == 1000000) {
+ if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) {
/*
- * Check are fast mode plus parameters available and use
- * fast mode if not.
+ * Check are Fast Mode Plus parameters available. Calculate
+ * SCL timing parameters for Fast Mode Plus if not set.
*/
if (dev->fp_hcnt && dev->fp_lcnt) {
dev->fs_hcnt = dev->fp_hcnt;
dev->fs_lcnt = dev->fp_lcnt;
- fp_str = " Plus";
+ } else {
+ ic_clk = i2c_dw_clk_rate(dev);
+ dev->fs_hcnt =
+ i2c_dw_scl_hcnt(dev,
+ DW_IC_FS_SCL_HCNT,
+ ic_clk,
+ 260, /* tHIGH = 260 ns */
+ sda_falling_time,
+ 0); /* No offset */
+ dev->fs_lcnt =
+ i2c_dw_scl_lcnt(dev,
+ DW_IC_FS_SCL_LCNT,
+ ic_clk,
+ 500, /* tLOW = 500 ns */
+ scl_falling_time,
+ 0); /* No offset */
}
+ fp_str = " Plus";
}
/*
* Calculate SCL timing parameters for fast mode if not set. They are
@@ -92,13 +123,16 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
if (!dev->fs_hcnt || !dev->fs_lcnt) {
ic_clk = i2c_dw_clk_rate(dev);
dev->fs_hcnt =
- i2c_dw_scl_hcnt(ic_clk,
+ i2c_dw_scl_hcnt(dev,
+ DW_IC_FS_SCL_HCNT,
+ ic_clk,
600, /* tHD;STA = tHIGH = 0.6 us */
sda_falling_time,
- 0, /* 0: DW default, 1: Ideal */
0); /* No offset */
dev->fs_lcnt =
- i2c_dw_scl_lcnt(ic_clk,
+ i2c_dw_scl_lcnt(dev,
+ DW_IC_FS_SCL_LCNT,
+ ic_clk,
1300, /* tLOW = 1.3 us */
scl_falling_time,
0); /* No offset */
@@ -112,43 +146,68 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
dev_err(dev->dev, "High Speed not supported!\n");
+ t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
dev->master_cfg &= ~DW_IC_CON_SPEED_MASK;
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
dev->hs_hcnt = 0;
dev->hs_lcnt = 0;
- } else if (dev->hs_hcnt && dev->hs_lcnt) {
- dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n",
- dev->hs_hcnt, dev->hs_lcnt);
+ } else if (!dev->hs_hcnt || !dev->hs_lcnt) {
+ u32 t_high, t_low;
+
+ /*
+ * The legal values stated in the databook for bus
+ * capacitance are only 100pF and 400pF.
+ * If dev->bus_capacitance_pF is greater than or equals
+ * to 400, t_high and t_low are assumed to be
+ * appropriate values for 400pF, otherwise 100pF.
+ */
+ if (dev->bus_capacitance_pF >= 400) {
+ /* assume bus capacitance is 400pF */
+ t_high = dev->clk_freq_optimized ? 160 : 120;
+ t_low = 320;
+ } else {
+ /* assume bus capacitance is 100pF */
+ t_high = 60;
+ t_low = dev->clk_freq_optimized ? 120 : 160;
+ }
+
+ ic_clk = i2c_dw_clk_rate(dev);
+ dev->hs_hcnt =
+ i2c_dw_scl_hcnt(dev,
+ DW_IC_HS_SCL_HCNT,
+ ic_clk,
+ t_high,
+ sda_falling_time,
+ 0); /* No offset */
+ dev->hs_lcnt =
+ i2c_dw_scl_lcnt(dev,
+ DW_IC_HS_SCL_LCNT,
+ ic_clk,
+ t_low,
+ scl_falling_time,
+ 0); /* No offset */
}
+ dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n",
+ dev->hs_hcnt, dev->hs_lcnt);
}
ret = i2c_dw_set_sda_hold(dev);
if (ret)
- goto out;
-
- switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) {
- case DW_IC_CON_SPEED_STD:
- mode_str = "Standard Mode";
- break;
- case DW_IC_CON_SPEED_HIGH:
- mode_str = "High Speed Mode";
- break;
- default:
- mode_str = "Fast Mode";
- }
- dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str);
+ return ret;
-out:
- return ret;
+ dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz));
+ return 0;
}
/**
- * i2c_dw_init() - Initialize the designware I2C master hardware
+ * i2c_dw_init_master() - Initialize the DesignWare I2C master hardware
* @dev: device private data
*
* This functions configures and enables the I2C master.
* This function is called during I2C init function, and in case of timeout at
* run time.
+ *
+ * Return: 0 on success, or negative errno otherwise.
*/
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
{
@@ -161,23 +220,30 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
/* Disable the adapter */
__i2c_dw_disable(dev);
+ /*
+ * Mask SMBus interrupts to block storms from broken
+ * firmware that leaves IC_SMBUS=1; the handler never
+ * services them.
+ */
+ regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
+
/* Write standard speed timing parameters */
- dw_writel(dev, dev->ss_hcnt, DW_IC_SS_SCL_HCNT);
- dw_writel(dev, dev->ss_lcnt, DW_IC_SS_SCL_LCNT);
+ regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
+ regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
/* Write fast mode/fast mode plus timing parameters */
- dw_writel(dev, dev->fs_hcnt, DW_IC_FS_SCL_HCNT);
- dw_writel(dev, dev->fs_lcnt, DW_IC_FS_SCL_LCNT);
+ regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
+ regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
/* Write high speed timing parameters if supported */
if (dev->hs_hcnt && dev->hs_lcnt) {
- dw_writel(dev, dev->hs_hcnt, DW_IC_HS_SCL_HCNT);
- dw_writel(dev, dev->hs_lcnt, DW_IC_HS_SCL_LCNT);
+ regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt);
+ regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
}
/* Write SDA hold time if supported */
if (dev->sda_hold_time)
- dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
+ regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
i2c_dw_configure_fifo_master(dev);
i2c_dw_release_lock(dev);
@@ -188,15 +254,15 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
- u32 ic_con, ic_tar = 0;
+ u32 ic_con = 0, ic_tar = 0;
+ unsigned int dummy;
/* Disable the adapter */
__i2c_dw_disable(dev);
/* If the slave address is ten bit address, enable 10BITADDR */
- ic_con = dw_readl(dev, DW_IC_CON);
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
- ic_con |= DW_IC_CON_10BITADDR_MASTER;
+ ic_con = DW_IC_CON_10BITADDR_MASTER;
/*
* If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
* mode has to be enabled via bit 12 of IC_TAR register.
@@ -204,35 +270,165 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
* detected from registers.
*/
ic_tar = DW_IC_TAR_10BITADDR_MASTER;
- } else {
- ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
}
- dw_writel(dev, ic_con, DW_IC_CON);
+ regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
+ ic_con);
/*
* Set the slave (target) address and enable 10-bit addressing mode
* if applicable.
*/
- dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
+ regmap_write(dev->map, DW_IC_TAR,
+ msgs[dev->msg_write_idx].addr | ic_tar);
/* Enforce disabled interrupts (due to HW issues) */
- i2c_dw_disable_int(dev);
+ __i2c_dw_write_intr_mask(dev, 0);
/* Enable the adapter */
__i2c_dw_enable(dev);
/* Dummy read to avoid the register getting stuck on Bay Trail */
- dw_readl(dev, DW_IC_ENABLE_STATUS);
+ regmap_read(dev->map, DW_IC_ENABLE_STATUS, &dummy);
/* Clear and enable interrupts */
- dw_readl(dev, DW_IC_CLR_INTR);
- dw_writel(dev, DW_IC_INTR_MASTER_MASK, DW_IC_INTR_MASK);
+ regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+ __i2c_dw_write_intr_mask(dev, DW_IC_INTR_MASTER_MASK);
+}
+
+/*
+ * This function waits for the controller to be idle before disabling I2C
+ * When the controller is not in the IDLE state, the MST_ACTIVITY bit
+ * (IC_STATUS[5]) is set.
+ *
+ * Values:
+ * 0x1 (ACTIVE): Controller not idle
+ * 0x0 (IDLE): Controller is idle
+ *
+ * The function is called after completing the current transfer.
+ *
+ * Returns:
+ * False when the controller is in the IDLE state.
+ * True when the controller is in the ACTIVE state.
+ */
+static bool i2c_dw_is_controller_active(struct dw_i2c_dev *dev)
+{
+ u32 status;
+
+ regmap_read(dev->map, DW_IC_STATUS, &status);
+ if (!(status & DW_IC_STATUS_MASTER_ACTIVITY))
+ return false;
+
+ return regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
+ !(status & DW_IC_STATUS_MASTER_ACTIVITY),
+ 1100, 20000) != 0;
+}
+
+static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val,
+ !(val & DW_IC_INTR_STOP_DET),
+ 1100, 20000);
+ if (ret)
+ dev_err(dev->dev, "i2c timeout error %d\n", ret);
+
+ return ret;
+}
+
+static int i2c_dw_status(struct dw_i2c_dev *dev)
+{
+ int status;
+
+ status = i2c_dw_wait_bus_not_busy(dev);
+ if (status)
+ return status;
+
+ return i2c_dw_check_stopbit(dev);
+}
+
+/*
+ * Initiate and continue master read/write transaction with polling
+ * based transfer routine afterward write messages into the Tx buffer.
+ */
+static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
+ int cmd = 0, status;
+ u8 *tx_buf;
+ unsigned int val;
+
+ /*
+ * In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
+ * it is mandatory to set the right value in specific register
+ * (offset:0x474) as per the hardware IP specification.
+ */
+ regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN);
+
+ dev->msgs = msgs;
+ dev->msgs_num = num_msgs;
+ dev->msg_write_idx = 0;
+ i2c_dw_xfer_init(dev);
+
+ /* Initiate messages read/write transaction */
+ for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) {
+ tx_buf = msgs[msg_wrt_idx].buf;
+ buf_len = msgs[msg_wrt_idx].len;
+
+ if (!(msgs[msg_wrt_idx].flags & I2C_M_RD))
+ regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1);
+ /*
+ * Initiate the i2c read/write transaction of buffer length,
+ * and poll for bus busy status. For the last message transfer,
+ * update the command with stop bit enable.
+ */
+ for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
+ if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
+ cmd |= BIT(9);
+
+ if (msgs[msg_wrt_idx].flags & I2C_M_RD) {
+ /* Due to hardware bug, need to write the same command twice. */
+ regmap_write(dev->map, DW_IC_DATA_CMD, 0x100);
+ regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd);
+ if (cmd) {
+ regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1));
+ regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1));
+ /*
+ * Need to check the stop bit. However, it cannot be
+ * detected from the registers so we check it always
+ * when read/write the last byte.
+ */
+ status = i2c_dw_status(dev);
+ if (status)
+ return status;
+
+ for (data_idx = 0; data_idx < buf_len; data_idx++) {
+ regmap_read(dev->map, DW_IC_DATA_CMD, &val);
+ tx_buf[data_idx] = val;
+ }
+ status = i2c_dw_check_stopbit(dev);
+ if (status)
+ return status;
+ }
+ } else {
+ regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd);
+ usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US);
+ }
+ }
+ status = i2c_dw_check_stopbit(dev);
+ if (status)
+ return status;
+ }
+
+ return 0;
}
/*
* Initiate (and continue) low level master read/write transaction.
- * This function is only called from i2c_dw_isr, and pumping i2c_msg
+ * This function is only called from i2c_dw_isr(), and pumping i2c_msg
* messages into the tx buffer. Even if the size of i2c_msg data is
* longer than the size of the tx buffer, it handles everything.
*/
@@ -246,6 +442,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
u32 buf_len = dev->tx_buf_len;
u8 *buf = dev->tx_buf;
bool need_restart = false;
+ unsigned int flr;
intr_mask = DW_IC_INTR_MASTER_MASK;
@@ -269,7 +466,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
buf = msgs[dev->msg_write_idx].buf;
buf_len = msgs[dev->msg_write_idx].len;
- /* If both IC_EMPTYFIFO_HOLD_MASTER_EN and
+ /*
+ * If both IC_EMPTYFIFO_HOLD_MASTER_EN and
* IC_RESTART_EN are set, we must manually
* set restart bit between messages.
*/
@@ -278,8 +476,11 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
need_restart = true;
}
- tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
- rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
+ regmap_read(dev->map, DW_IC_TXFLR, &flr);
+ tx_limit = dev->tx_fifo_depth - flr;
+
+ regmap_read(dev->map, DW_IC_RXFLR, &flr);
+ rx_limit = dev->rx_fifo_depth - flr;
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
u32 cmd = 0;
@@ -312,11 +513,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->rx_outstanding >= dev->rx_fifo_depth)
break;
- dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
+ regmap_write(dev->map, DW_IC_DATA_CMD,
+ cmd | 0x100);
rx_limit--;
dev->rx_outstanding++;
- } else
- dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
+ } else {
+ regmap_write(dev->map, DW_IC_DATA_CMD,
+ cmd | *buf++);
+ }
tx_limit--; buf_len--;
}
@@ -325,10 +529,16 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
/*
* Because we don't know the buffer length in the
- * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop
- * the transaction here.
+ * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop the
+ * transaction here. Also disable the TX_EMPTY IRQ
+ * while waiting for the data length byte to avoid the
+ * bogus interrupts flood.
*/
- if (buf_len > 0 || flags & I2C_M_RECV_LEN) {
+ if (flags & I2C_M_RECV_LEN) {
+ dev->status |= STATUS_WRITE_IN_PROGRESS;
+ intr_mask &= ~DW_IC_INTR_TX_EMPTY;
+ break;
+ } else if (buf_len > 0) {
/* more bytes to be written */
dev->status |= STATUS_WRITE_IN_PROGRESS;
break;
@@ -346,7 +556,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->msg_err)
intr_mask = 0;
- dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
+ __i2c_dw_write_intr_mask(dev, intr_mask);
}
static u8
@@ -354,6 +564,7 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
{
struct i2c_msg *msgs = dev->msgs;
u32 flags = msgs[dev->msg_read_idx].flags;
+ unsigned int intr_mask;
/*
* Adjust the buffer length and mask the flag
@@ -364,6 +575,14 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
msgs[dev->msg_read_idx].len = len;
msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
+ /*
+ * Received buffer length, re-enable TX_EMPTY interrupt
+ * to resume the SMBUS transaction.
+ */
+ __i2c_dw_read_intr_mask(dev, &intr_mask);
+ intr_mask |= DW_IC_INTR_TX_EMPTY;
+ __i2c_dw_write_intr_mask(dev, intr_mask);
+
return len;
}
@@ -371,9 +590,10 @@ static void
i2c_dw_read(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
- int rx_valid;
+ unsigned int rx_valid;
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
+ unsigned int tmp;
u32 len;
u8 *buf;
@@ -388,18 +608,30 @@ i2c_dw_read(struct dw_i2c_dev *dev)
buf = dev->rx_buf;
}
- rx_valid = dw_readl(dev, DW_IC_RXFLR);
+ regmap_read(dev->map, DW_IC_RXFLR, &rx_valid);
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
u32 flags = msgs[dev->msg_read_idx].flags;
- *buf = dw_readl(dev, DW_IC_DATA_CMD);
+ regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
+ tmp &= DW_IC_DATA_CMD_DAT;
/* Ensure length byte is a valid value */
- if (flags & I2C_M_RECV_LEN &&
- *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) {
- len = i2c_dw_recv_len(dev, *buf);
+ if (flags & I2C_M_RECV_LEN) {
+ /*
+ * if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be
+ * detected from the registers, the controller can be
+ * disabled if the STOP bit is set. But it is only set
+ * after receiving block data response length in
+ * I2C_FUNC_SMBUS_BLOCK_DATA case. That needs to read
+ * another byte with STOP bit set when the block data
+ * response length is invalid to complete the transaction.
+ */
+ if (!tmp || tmp > I2C_SMBUS_BLOCK_MAX)
+ tmp = 1;
+
+ len = i2c_dw_recv_len(dev, tmp);
}
- buf++;
+ *buf++ = tmp;
dev->rx_outstanding--;
}
@@ -413,6 +645,169 @@ i2c_dw_read(struct dw_i2c_dev *dev)
}
}
+static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+{
+ unsigned int stat, dummy;
+
+ /*
+ * The IC_INTR_STAT register just indicates "enabled" interrupts.
+ * The unmasked raw version of interrupt status bits is available
+ * in the IC_RAW_INTR_STAT register.
+ *
+ * That is,
+ * stat = readl(IC_INTR_STAT);
+ * equals to,
+ * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
+ *
+ * The raw version might be useful for debugging purposes.
+ */
+ if (!(dev->flags & ACCESS_POLLING)) {
+ regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
+ } else {
+ regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat);
+ stat &= dev->sw_mask;
+ }
+
+ /*
+ * Do not use the IC_CLR_INTR register to clear interrupts, or
+ * you'll miss some interrupts, triggered during the period from
+ * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
+ *
+ * Instead, use the separately-prepared IC_CLR_* registers.
+ */
+ if (stat & DW_IC_INTR_RX_UNDER)
+ regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
+ if (stat & DW_IC_INTR_RX_OVER)
+ regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
+ if (stat & DW_IC_INTR_TX_OVER)
+ regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
+ if (stat & DW_IC_INTR_RD_REQ)
+ regmap_read(dev->map, DW_IC_CLR_RD_REQ, &dummy);
+ if (stat & DW_IC_INTR_TX_ABRT) {
+ /*
+ * The IC_TX_ABRT_SOURCE register is cleared whenever
+ * the IC_CLR_TX_ABRT is read. Preserve it beforehand.
+ */
+ regmap_read(dev->map, DW_IC_TX_ABRT_SOURCE, &dev->abort_source);
+ regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
+ }
+ if (stat & DW_IC_INTR_RX_DONE)
+ regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
+ if (stat & DW_IC_INTR_ACTIVITY)
+ regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
+ if ((stat & DW_IC_INTR_STOP_DET) &&
+ ((dev->rx_outstanding == 0) || (stat & DW_IC_INTR_RX_FULL)))
+ regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
+ if (stat & DW_IC_INTR_START_DET)
+ regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
+ if (stat & DW_IC_INTR_GEN_CALL)
+ regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
+
+ return stat;
+}
+
+static void i2c_dw_process_transfer(struct dw_i2c_dev *dev, unsigned int stat)
+{
+ if (stat & DW_IC_INTR_TX_ABRT) {
+ dev->cmd_err |= DW_IC_ERR_TX_ABRT;
+ dev->status &= ~STATUS_MASK;
+ dev->rx_outstanding = 0;
+
+ /*
+ * Anytime TX_ABRT is set, the contents of the tx/rx
+ * buffers are flushed. Make sure to skip them.
+ */
+ __i2c_dw_write_intr_mask(dev, 0);
+ goto tx_aborted;
+ }
+
+ if (stat & DW_IC_INTR_RX_FULL)
+ i2c_dw_read(dev);
+
+ if (stat & DW_IC_INTR_TX_EMPTY)
+ i2c_dw_xfer_msg(dev);
+
+ /*
+ * No need to modify or disable the interrupt mask here.
+ * i2c_dw_xfer_msg() will take care of it according to
+ * the current transmit status.
+ */
+
+tx_aborted:
+ if (((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) &&
+ (dev->rx_outstanding == 0))
+ complete(&dev->cmd_complete);
+ else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
+ /* Workaround to trigger pending interrupt */
+ __i2c_dw_read_intr_mask(dev, &stat);
+ __i2c_dw_write_intr_mask(dev, 0);
+ __i2c_dw_write_intr_mask(dev, stat);
+ }
+}
+
+/*
+ * Interrupt service routine. This gets called whenever an I2C master interrupt
+ * occurs.
+ */
+static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+{
+ struct dw_i2c_dev *dev = dev_id;
+ unsigned int stat, enabled;
+
+ regmap_read(dev->map, DW_IC_ENABLE, &enabled);
+ regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat);
+ if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
+ return IRQ_NONE;
+ if (pm_runtime_suspended(dev->dev) || stat == GENMASK(31, 0))
+ return IRQ_NONE;
+ dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
+
+ stat = i2c_dw_read_clear_intrbits(dev);
+
+ if (!(dev->status & STATUS_ACTIVE)) {
+ /*
+ * Unexpected interrupt in driver point of view. State
+ * variables are either unset or stale so acknowledge and
+ * disable interrupts for suppressing further interrupts if
+ * interrupt really came from this HW (E.g. firmware has left
+ * the HW active).
+ */
+ __i2c_dw_write_intr_mask(dev, 0);
+ return IRQ_HANDLED;
+ }
+
+ i2c_dw_process_transfer(dev, stat);
+
+ return IRQ_HANDLED;
+}
+
+static int i2c_dw_wait_transfer(struct dw_i2c_dev *dev)
+{
+ unsigned long timeout = dev->adapter.timeout;
+ unsigned int stat;
+ int ret;
+
+ if (!(dev->flags & ACCESS_POLLING)) {
+ ret = wait_for_completion_timeout(&dev->cmd_complete, timeout);
+ } else {
+ timeout += jiffies;
+ do {
+ ret = try_wait_for_completion(&dev->cmd_complete);
+ if (ret)
+ break;
+
+ stat = i2c_dw_read_clear_intrbits(dev);
+ if (stat)
+ i2c_dw_process_transfer(dev, stat);
+ else
+ /* Try save some power */
+ usleep_range(3, 25);
+ } while (time_before(jiffies, timeout));
+ }
+
+ return ret ? 0 : -ETIMEDOUT;
+}
+
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg.
*/
@@ -426,6 +821,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
pm_runtime_get_sync(dev->dev);
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_AMD_NAVI_GPU:
+ ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
+ goto done_nolock;
+ default:
+ break;
+ }
+
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -433,7 +836,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev->msg_write_idx = 0;
dev->msg_read_idx = 0;
dev->msg_err = 0;
- dev->status = STATUS_IDLE;
+ dev->status = 0;
dev->abort_source = 0;
dev->rx_outstanding = 0;
@@ -449,20 +852,30 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
i2c_dw_xfer_init(dev);
/* Wait for tx to complete */
- if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
+ ret = i2c_dw_wait_transfer(dev);
+ if (ret) {
dev_err(dev->dev, "controller timed out\n");
- /* i2c_dw_init implicitly disables the adapter */
+ /* i2c_dw_init_master() implicitly disables the adapter */
i2c_recover_bus(&dev->adapter);
i2c_dw_init_master(dev);
- ret = -ETIMEDOUT;
goto done;
}
/*
+ * This happens rarely (~1:500) and is hard to reproduce. Debug trace
+ * showed that IC_STATUS had value of 0x23 when STOP_DET occurred,
+ * if disable IC_ENABLE.ENABLE immediately that can result in
+ * IC_RAW_INTR_STAT.MASTER_ON_HOLD holding SCL low. Check if
+ * controller is still ACTIVE before disabling I2C.
+ */
+ if (i2c_dw_is_controller_active(dev))
+ dev_err(dev->dev, "controller active\n");
+
+ /*
* We must disable the adapter before returning and signaling the end
* of the current transfer. Otherwise the hardware might continue
* generating interrupts which in turn causes a race condition with
- * the following transfer. Needs some more investigation if the
+ * the following transfer. Needs some more investigation if the
* additional interrupts are a hardware bug or this driver doesn't
* handle them correctly yet.
*/
@@ -495,14 +908,13 @@ done:
i2c_dw_release_lock(dev);
done_nolock:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
}
static const struct i2c_algorithm i2c_dw_algo = {
- .master_xfer = i2c_dw_xfer,
+ .xfer = i2c_dw_xfer,
.functionality = i2c_dw_func,
};
@@ -510,122 +922,29 @@ static const struct i2c_adapter_quirks i2c_dw_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
};
-static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{
- u32 stat;
-
- /*
- * The IC_INTR_STAT register just indicates "enabled" interrupts.
- * Ths unmasked raw version of interrupt status bits are available
- * in the IC_RAW_INTR_STAT register.
- *
- * That is,
- * stat = dw_readl(IC_INTR_STAT);
- * equals to,
- * stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
- *
- * The raw version might be useful for debugging purposes.
- */
- stat = dw_readl(dev, DW_IC_INTR_STAT);
-
- /*
- * Do not use the IC_CLR_INTR register to clear interrupts, or
- * you'll miss some interrupts, triggered during the period from
- * dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
- *
- * Instead, use the separately-prepared IC_CLR_* registers.
- */
- if (stat & DW_IC_INTR_RX_UNDER)
- dw_readl(dev, DW_IC_CLR_RX_UNDER);
- if (stat & DW_IC_INTR_RX_OVER)
- dw_readl(dev, DW_IC_CLR_RX_OVER);
- if (stat & DW_IC_INTR_TX_OVER)
- dw_readl(dev, DW_IC_CLR_TX_OVER);
- if (stat & DW_IC_INTR_RD_REQ)
- dw_readl(dev, DW_IC_CLR_RD_REQ);
- if (stat & DW_IC_INTR_TX_ABRT) {
- /*
- * The IC_TX_ABRT_SOURCE register is cleared whenever
- * the IC_CLR_TX_ABRT is read. Preserve it beforehand.
- */
- dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE);
- dw_readl(dev, DW_IC_CLR_TX_ABRT);
- }
- if (stat & DW_IC_INTR_RX_DONE)
- dw_readl(dev, DW_IC_CLR_RX_DONE);
- if (stat & DW_IC_INTR_ACTIVITY)
- dw_readl(dev, DW_IC_CLR_ACTIVITY);
- if (stat & DW_IC_INTR_STOP_DET)
- dw_readl(dev, DW_IC_CLR_STOP_DET);
- if (stat & DW_IC_INTR_START_DET)
- dw_readl(dev, DW_IC_CLR_START_DET);
- if (stat & DW_IC_INTR_GEN_CALL)
- dw_readl(dev, DW_IC_CLR_GEN_CALL);
-
- return stat;
-}
+ struct i2c_timings *t = &dev->timings;
-/*
- * Interrupt service routine. This gets called whenever an I2C master interrupt
- * occurs.
- */
-static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
-{
- u32 stat;
+ dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
- stat = i2c_dw_read_clear_intrbits(dev);
- if (stat & DW_IC_INTR_TX_ABRT) {
- dev->cmd_err |= DW_IC_ERR_TX_ABRT;
- dev->status = STATUS_IDLE;
+ dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ DW_IC_CON_RESTART_EN;
- /*
- * Anytime TX_ABRT is set, the contents of the tx/rx
- * buffers are flushed. Make sure to skip them.
- */
- dw_writel(dev, 0, DW_IC_INTR_MASK);
- goto tx_aborted;
- }
+ dev->mode = DW_IC_MASTER;
- if (stat & DW_IC_INTR_RX_FULL)
- i2c_dw_read(dev);
-
- if (stat & DW_IC_INTR_TX_EMPTY)
- i2c_dw_xfer_msg(dev);
-
- /*
- * No need to modify or disable the interrupt mask here.
- * i2c_dw_xfer_msg() will take care of it according to
- * the current transmit status.
- */
-
-tx_aborted:
- if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
- complete(&dev->cmd_complete);
- else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
- /* Workaround to trigger pending interrupt */
- stat = dw_readl(dev, DW_IC_INTR_MASK);
- i2c_dw_disable_int(dev);
- dw_writel(dev, stat, DW_IC_INTR_MASK);
+ switch (t->bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ dev->master_cfg |= DW_IC_CON_SPEED_STD;
+ break;
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
+ break;
+ default:
+ dev->master_cfg |= DW_IC_CON_SPEED_FAST;
}
-
- return 0;
-}
-
-static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
-{
- struct dw_i2c_dev *dev = dev_id;
- u32 stat, enabled;
-
- enabled = dw_readl(dev, DW_IC_ENABLE);
- stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
- dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
- if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
- return IRQ_NONE;
-
- i2c_dw_irq_handler_master(dev);
-
- return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(i2c_dw_configure_master);
static void i2c_dw_prepare_recovery(struct i2c_adapter *adap)
{
@@ -650,15 +969,11 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
struct i2c_adapter *adap = &dev->adapter;
struct gpio_desc *gpio;
- int r;
-
- gpio = devm_gpiod_get(dev->dev, "scl", GPIOD_OUT_HIGH);
- if (IS_ERR(gpio)) {
- r = PTR_ERR(gpio);
- if (r == -ENOENT || r == -ENOSYS)
- return 0;
- return r;
- }
+
+ gpio = devm_gpiod_get_optional(dev->dev, "scl", GPIOD_OUT_HIGH);
+ if (IS_ERR_OR_NULL(gpio))
+ return PTR_ERR_OR_ZERO(gpio);
+
rinfo->scl_gpiod = gpio;
gpio = devm_gpiod_get_optional(dev->dev, "sda", GPIOD_IN);
@@ -666,30 +981,40 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
return PTR_ERR(gpio);
rinfo->sda_gpiod = gpio;
+ rinfo->pinctrl = devm_pinctrl_get(dev->dev);
+ if (IS_ERR(rinfo->pinctrl)) {
+ if (PTR_ERR(rinfo->pinctrl) == -EPROBE_DEFER)
+ return PTR_ERR(rinfo->pinctrl);
+
+ rinfo->pinctrl = NULL;
+ dev_err(dev->dev, "getting pinctrl info failed: bus recovery might not work\n");
+ } else if (!rinfo->pinctrl) {
+ dev_dbg(dev->dev, "pinctrl is disabled, bus recovery might not work\n");
+ }
+
rinfo->recover_bus = i2c_generic_scl_recovery;
rinfo->prepare_recovery = i2c_dw_prepare_recovery;
rinfo->unprepare_recovery = i2c_dw_unprepare_recovery;
adap->bus_recovery_info = rinfo;
- dev_info(dev->dev, "running with gpio recovery mode! scl%s",
+ dev_info(dev->dev, "running with GPIO recovery mode! scl%s",
rinfo->sda_gpiod ? ",sda" : "");
return 0;
}
-int i2c_dw_probe(struct dw_i2c_dev *dev)
+int i2c_dw_probe_master(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
unsigned long irq_flags;
+ unsigned int ic_con;
int ret;
init_completion(&dev->cmd_complete);
dev->init = i2c_dw_init_master;
- dev->disable = i2c_dw_disable;
- dev->disable_int = i2c_dw_disable_int;
- ret = i2c_dw_set_reg_access(dev);
+ ret = i2c_dw_init_regmap(dev);
if (ret)
return ret;
@@ -697,12 +1022,36 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
if (ret)
return ret;
+ ret = i2c_dw_set_fifo_size(dev);
+ if (ret)
+ return ret;
+
+ /* Lock the bus for accessing DW_IC_CON */
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * On AMD platforms BIOS advertises the bus clear feature
+ * and enables the SCL/SDA stuck low. SMU FW does the
+ * bus recovery process. Driver should not ignore this BIOS
+ * advertisement of bus clear feature.
+ */
+ ret = regmap_read(dev->map, DW_IC_CON, &ic_con);
+ i2c_dw_release_lock(dev);
+ if (ret)
+ return ret;
+
+ if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL)
+ dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL;
+
ret = dev->init(dev);
if (ret)
return ret;
- snprintf(adap->name, sizeof(adap->name),
- "Synopsys DesignWare I2C adapter");
+ if (!adap->name[0])
+ scnprintf(adap->name, sizeof(adap->name),
+ "Synopsys DesignWare I2C adapter");
adap->retries = 3;
adap->algo = &i2c_dw_algo;
adap->quirks = &i2c_dw_quirks;
@@ -715,13 +1064,20 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
}
- i2c_dw_disable_int(dev);
- ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
- dev_name(dev->dev), dev);
- if (ret) {
- dev_err(dev->dev, "failure requesting irq %i: %d\n",
- dev->irq, ret);
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
return ret;
+
+ __i2c_dw_write_intr_mask(dev, 0);
+ i2c_dw_release_lock(dev);
+
+ if (!(dev->flags & ACCESS_POLLING)) {
+ ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr,
+ irq_flags, dev_name(dev->dev), dev);
+ if (ret)
+ return dev_err_probe(dev->dev, ret,
+ "failure requesting irq %i: %d\n",
+ dev->irq, ret);
}
ret = i2c_dw_init_recovery_info(dev);
@@ -742,7 +1098,8 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
return ret;
}
-EXPORT_SYMBOL_GPL(i2c_dw_probe);
+EXPORT_SYMBOL_GPL(i2c_dw_probe_master);
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus master adapter");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("I2C_DW_COMMON");
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index d50f80487214..f21f9877c040 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -9,7 +9,6 @@
* Copyright (C) 2009 Provigent Ltd.
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
*/
-#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -19,11 +18,14 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "i2c-designware-core.h"
+#include "i2c-ccgx-ucsi.h"
#define DRIVER_NAME "i2c-designware-pci"
@@ -33,32 +35,33 @@ enum dw_pci_ctl_id_t {
baytrail,
cherrytrail,
haswell,
+ elkhartlake,
+ navi_amd,
};
+/*
+ * This is a legacy structure to describe the hardware counters
+ * to configure signal timings on the bus. For Device Tree platforms
+ * one should use the respective properties and for ACPI there is
+ * a set of ACPI methods that provide these counters. No new
+ * platform should use this structure.
+ */
struct dw_scl_sda_cfg {
- u32 ss_hcnt;
- u32 fs_hcnt;
- u32 ss_lcnt;
- u32 fs_lcnt;
- u32 sda_hold;
+ u16 ss_hcnt;
+ u16 fs_hcnt;
+ u16 ss_lcnt;
+ u16 fs_lcnt;
+ u32 sda_hold_time;
};
struct dw_pci_controller {
u32 bus_num;
- u32 bus_cfg;
- u32 tx_fifo_depth;
- u32 rx_fifo_depth;
- u32 clk_khz;
- u32 functionality;
u32 flags;
struct dw_scl_sda_cfg *scl_sda_cfg;
int (*setup)(struct pci_dev *pdev, struct dw_pci_controller *c);
+ u32 (*get_clk_rate_khz)(struct dw_i2c_dev *dev);
};
-#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
- DW_IC_CON_SLAVE_DISABLE | \
- DW_IC_CON_RESTART_EN)
-
/* Merrifield HCNT/LCNT/SDA hold time */
static struct dw_scl_sda_cfg mrfld_config = {
.ss_hcnt = 0x2f8,
@@ -73,7 +76,7 @@ static struct dw_scl_sda_cfg byt_config = {
.fs_hcnt = 0x55,
.ss_lcnt = 0x200,
.fs_lcnt = 0x99,
- .sda_hold = 0x6,
+ .sda_hold_time = 0x6,
};
/* Haswell HCNT/LCNT/SDA hold time */
@@ -82,16 +85,29 @@ static struct dw_scl_sda_cfg hsw_config = {
.fs_hcnt = 0x48,
.ss_lcnt = 0x01fb,
.fs_lcnt = 0xa0,
- .sda_hold = 0x9,
+ .sda_hold_time = 0x9,
+};
+
+/* NAVI-AMD HCNT/LCNT/SDA hold time */
+static struct dw_scl_sda_cfg navi_amd_config = {
+ .ss_hcnt = 0x1ae,
+ .ss_lcnt = 0x23a,
+ .sda_hold_time = 0x9,
};
+static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return 25000;
+}
+
static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
{
+ struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
+
switch (pdev->device) {
case 0x0817:
- c->bus_cfg &= ~DW_IC_CON_SPEED_MASK;
- c->bus_cfg |= DW_IC_CON_SPEED_STD;
- /* fall through */
+ dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ fallthrough;
case 0x0818:
case 0x0819:
c->bus_num = pdev->device - 0x817 + 3;
@@ -108,7 +124,7 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
{
/*
- * On Intel Merrifield the user visible i2c busses are enumerated
+ * On Intel Merrifield the user visible i2c buses are enumerated
* [1..7]. So, we add 1 to shift the default range. Besides that the
* first PCI slot provides 4 functions, that's why we have to add 0 to
* the first slot and 4 to the next one.
@@ -124,160 +140,155 @@ static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
return -ENODEV;
}
+static u32 ehl_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return 100000;
+}
+
+static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+ return 100000;
+}
+
+static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
+{
+ struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
+
+ dev->flags |= MODEL_AMD_NAVI_GPU | ACCESS_POLLING;
+ dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ return 0;
+}
+
static struct dw_pci_controller dw_pci_controllers[] = {
[medfield] = {
.bus_num = -1,
- .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
- .tx_fifo_depth = 32,
- .rx_fifo_depth = 32,
- .functionality = I2C_FUNC_10BIT_ADDR,
- .clk_khz = 25000,
.setup = mfld_setup,
+ .get_clk_rate_khz = mfld_get_clk_rate_khz,
},
[merrifield] = {
.bus_num = -1,
- .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
- .tx_fifo_depth = 64,
- .rx_fifo_depth = 64,
- .functionality = I2C_FUNC_10BIT_ADDR,
.scl_sda_cfg = &mrfld_config,
.setup = mrfld_setup,
},
[baytrail] = {
.bus_num = -1,
- .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
- .tx_fifo_depth = 32,
- .rx_fifo_depth = 32,
- .functionality = I2C_FUNC_10BIT_ADDR,
.scl_sda_cfg = &byt_config,
},
[haswell] = {
.bus_num = -1,
- .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
- .tx_fifo_depth = 32,
- .rx_fifo_depth = 32,
- .functionality = I2C_FUNC_10BIT_ADDR,
.scl_sda_cfg = &hsw_config,
},
[cherrytrail] = {
.bus_num = -1,
- .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
- .tx_fifo_depth = 32,
- .rx_fifo_depth = 32,
- .functionality = I2C_FUNC_10BIT_ADDR,
- .flags = MODEL_CHERRYTRAIL,
.scl_sda_cfg = &byt_config,
},
+ [elkhartlake] = {
+ .bus_num = -1,
+ .get_clk_rate_khz = ehl_get_clk_rate_khz,
+ },
+ [navi_amd] = {
+ .bus_num = -1,
+ .scl_sda_cfg = &navi_amd_config,
+ .setup = navi_amd_setup,
+ .get_clk_rate_khz = navi_amd_get_clk_rate_khz,
+ },
};
-#ifdef CONFIG_PM
-static int i2c_dw_pci_suspend(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
-
- i_dev->disable(i_dev);
-
- return 0;
-}
-
-static int i2c_dw_pci_resume(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
-
- return i_dev->init(i_dev);
-}
-#endif
-
-static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
- i2c_dw_pci_resume, NULL);
+static const struct property_entry dgpu_properties[] = {
+ /* USB-C doesn't power the system */
+ PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE),
+ {}
+};
-static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
-{
- return dev->controller->clk_khz;
-}
+static const struct software_node dgpu_node = {
+ .properties = dgpu_properties,
+};
static int i2c_dw_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ struct device *device = &pdev->dev;
struct dw_i2c_dev *dev;
struct i2c_adapter *adap;
int r;
struct dw_pci_controller *controller;
struct dw_scl_sda_cfg *cfg;
- if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
- dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__,
- id->driver_data);
- return -EINVAL;
- }
+ if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers))
+ return dev_err_probe(device, -EINVAL, "Invalid driver data %ld\n",
+ id->driver_data);
controller = &dw_pci_controllers[id->driver_data];
r = pcim_enable_device(pdev);
- if (r) {
- dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
- r);
- return r;
- }
+ if (r)
+ return dev_err_probe(device, r, "Failed to enable I2C PCI device\n");
+
+ pci_set_master(pdev);
r = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
- if (r) {
- dev_err(&pdev->dev, "I/O memory remapping failed\n");
- return r;
- }
+ if (r)
+ return dev_err_probe(device, r, "I/O memory remapping failed\n");
- dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
- dev->clk = NULL;
- dev->controller = controller;
- dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
+ r = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (r < 0)
+ return r;
+
+ dev->get_clk_rate_khz = controller->get_clk_rate_khz;
dev->base = pcim_iomap_table(pdev)[0];
- dev->dev = &pdev->dev;
- dev->irq = pdev->irq;
+ dev->dev = device;
+ dev->irq = pci_irq_vector(pdev, 0);
dev->flags |= controller->flags;
+ pci_set_drvdata(pdev, dev);
+
if (controller->setup) {
r = controller->setup(pdev, controller);
if (r)
return r;
}
- dev->functionality = controller->functionality |
- DW_IC_DEFAULT_FUNCTIONALITY;
+ r = i2c_dw_fw_parse_and_configure(dev);
+ if (r)
+ return r;
+
+ i2c_dw_configure(dev);
- dev->master_cfg = controller->bus_cfg;
if (controller->scl_sda_cfg) {
cfg = controller->scl_sda_cfg;
dev->ss_hcnt = cfg->ss_hcnt;
dev->fs_hcnt = cfg->fs_hcnt;
dev->ss_lcnt = cfg->ss_lcnt;
dev->fs_lcnt = cfg->fs_lcnt;
- dev->sda_hold_time = cfg->sda_hold;
+ dev->sda_hold_time = cfg->sda_hold_time;
}
- pci_set_drvdata(pdev, dev);
-
- dev->tx_fifo_depth = controller->tx_fifo_depth;
- dev->rx_fifo_depth = controller->rx_fifo_depth;
-
adap = &dev->adapter;
adap->owner = THIS_MODULE;
adap->class = 0;
- ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->nr = controller->bus_num;
r = i2c_dw_probe(dev);
if (r)
return r;
- pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_put_autosuspend(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
+ if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
+ dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node);
+ if (IS_ERR(dev->slave)) {
+ i2c_del_adapter(&dev->adapter);
+ return dev_err_probe(device, PTR_ERR(dev->slave),
+ "register UCSI failed\n");
+ }
+ }
+
+ pm_runtime_set_autosuspend_delay(device, 1000);
+ pm_runtime_use_autosuspend(device);
+ pm_runtime_put_autosuspend(device);
+ pm_runtime_allow(device);
return 0;
}
@@ -285,18 +296,17 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
static void i2c_dw_pci_remove(struct pci_dev *pdev)
{
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
+ struct device *device = &pdev->dev;
- dev->disable(dev);
- pm_runtime_forbid(&pdev->dev);
- pm_runtime_get_noresume(&pdev->dev);
+ i2c_dw_disable(dev);
+
+ pm_runtime_forbid(device);
+ pm_runtime_get_noresume(device);
i2c_del_adapter(&dev->adapter);
}
-/* work with hotplug and coldplug */
-MODULE_ALIAS("i2c_designware-pci");
-
-static const struct pci_device_id i2_designware_pci_ids[] = {
+static const struct pci_device_id i2c_designware_pci_ids[] = {
/* Medfield */
{ PCI_VDEVICE(INTEL, 0x0817), medfield },
{ PCI_VDEVICE(INTEL, 0x0818), medfield },
@@ -326,22 +336,39 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x22C5), cherrytrail },
{ PCI_VDEVICE(INTEL, 0x22C6), cherrytrail },
{ PCI_VDEVICE(INTEL, 0x22C7), cherrytrail },
- { 0,}
+ /* Elkhart Lake (PSE I2C) */
+ { PCI_VDEVICE(INTEL, 0x4bb9), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bba), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bbb), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bbc), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bbd), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake },
+ { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake },
+ /* AMD NAVI */
+ { PCI_VDEVICE(ATI, 0x7314), navi_amd },
+ { PCI_VDEVICE(ATI, 0x73a4), navi_amd },
+ { PCI_VDEVICE(ATI, 0x73e4), navi_amd },
+ { PCI_VDEVICE(ATI, 0x73c4), navi_amd },
+ { PCI_VDEVICE(ATI, 0x7444), navi_amd },
+ { PCI_VDEVICE(ATI, 0x7464), navi_amd },
+ {}
};
-MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
+MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids);
static struct pci_driver dw_i2c_driver = {
.name = DRIVER_NAME,
- .id_table = i2_designware_pci_ids,
.probe = i2c_dw_pci_probe,
.remove = i2c_dw_pci_remove,
.driver = {
- .pm = &i2c_dw_pm_ops,
+ .pm = pm_ptr(&i2c_dw_dev_pm_ops),
},
+ .id_table = i2c_designware_pci_ids,
};
-
module_pci_driver(dw_i2c_driver);
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("I2C_DW");
+MODULE_IMPORT_NS("I2C_DW_COMMON");
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 9eaac3be1f63..7be99656a67d 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -8,7 +8,6 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
-#include <linux/acpi.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -19,335 +18,252 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_data/i2c-designware.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/suspend.h>
+#include <linux/units.h>
#include "i2c-designware-core.h"
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
{
- return clk_get_rate(dev->clk)/1000;
+ return clk_get_rate(dev->clk) / HZ_PER_KHZ;
}
-#ifdef CONFIG_ACPI
-/*
- * The HCNT/LCNT information coming from ACPI should be the most accurate
- * for given platform. However, some systems get it wrong. On such systems
- * we get better results by calculating those based on the input clock.
- */
-static const struct dmi_system_id dw_i2c_no_acpi_params[] = {
- {
- .ident = "Dell Inspiron 7348",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7348"),
- },
- },
- { }
-};
-
-static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
- u16 *hcnt, u16 *lcnt, u32 *sda_hold)
+#ifdef CONFIG_OF
+#define BT1_I2C_CTL 0x100
+#define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0)
+#define BT1_I2C_CTL_WR BIT(8)
+#define BT1_I2C_CTL_GO BIT(31)
+#define BT1_I2C_DI 0x104
+#define BT1_I2C_DO 0x108
+
+static int bt1_i2c_read(void *context, unsigned int reg, unsigned int *val)
{
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
- acpi_handle handle = ACPI_HANDLE(&pdev->dev);
- union acpi_object *obj;
-
- if (dmi_check_system(dw_i2c_no_acpi_params))
- return;
-
- if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf)))
- return;
-
- obj = (union acpi_object *)buf.pointer;
- if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) {
- const union acpi_object *objs = obj->package.elements;
+ struct dw_i2c_dev *dev = context;
+ int ret;
- *hcnt = (u16)objs[0].integer.value;
- *lcnt = (u16)objs[1].integer.value;
- *sda_hold = (u32)objs[2].integer.value;
- }
+ /*
+ * Note these methods shouldn't ever fail because the system controller
+ * registers are memory mapped. We check the return value just in case.
+ */
+ ret = regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | (reg & BT1_I2C_CTL_ADDR_MASK));
+ if (ret)
+ return ret;
- kfree(buf.pointer);
+ return regmap_read(dev->sysmap, BT1_I2C_DO, val);
}
-static int dw_i2c_acpi_configure(struct platform_device *pdev)
+static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)
{
- struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
- struct i2c_timings *t = &dev->timings;
- u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
-
- dev->adapter.nr = -1;
- dev->tx_fifo_depth = 32;
- dev->rx_fifo_depth = 32;
+ struct dw_i2c_dev *dev = context;
+ int ret;
- /*
- * Try to get SDA hold time and *CNT values from an ACPI method for
- * selected speed modes.
- */
- dw_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht);
- dw_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht);
- dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
- dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
-
- switch (t->bus_freq_hz) {
- case 100000:
- dev->sda_hold_time = ss_ht;
- break;
- case 1000000:
- dev->sda_hold_time = fp_ht;
- break;
- case 3400000:
- dev->sda_hold_time = hs_ht;
- break;
- case 400000:
- default:
- dev->sda_hold_time = fs_ht;
- break;
- }
+ ret = regmap_write(dev->sysmap, BT1_I2C_DI, val);
+ if (ret)
+ return ret;
- return 0;
+ return regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
}
-static const struct acpi_device_id dw_i2c_acpi_match[] = {
- { "INT33C2", 0 },
- { "INT33C3", 0 },
- { "INT3432", 0 },
- { "INT3433", 0 },
- { "80860F41", ACCESS_NO_IRQ_SUSPEND },
- { "808622C1", ACCESS_NO_IRQ_SUSPEND | MODEL_CHERRYTRAIL },
- { "AMD0010", ACCESS_INTR_MASK },
- { "AMDI0010", ACCESS_INTR_MASK },
- { "AMDI0510", 0 },
- { "APMC0D0F", 0 },
- { "HISI02A1", 0 },
- { "HISI02A2", 0 },
- { }
+static const struct regmap_config bt1_i2c_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+ .reg_read = bt1_i2c_read,
+ .reg_write = bt1_i2c_write,
+ .max_register = DW_IC_COMP_TYPE,
};
-MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
+
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
+{
+ dev->sysmap = syscon_node_to_regmap(dev->dev->of_node->parent);
+ if (IS_ERR(dev->sysmap))
+ return PTR_ERR(dev->sysmap);
+
+ dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);
+ return PTR_ERR_OR_ZERO(dev->map);
+}
#else
-static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
{
return -ENODEV;
}
#endif
-#ifdef CONFIG_OF
-#define MSCC_ICPU_CFG_TWI_DELAY 0x0
-#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
-#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
-
-static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
+static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev)
{
- writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
- dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
+ dev->map = dev_get_regmap(dev->dev->parent, NULL);
+ if (!dev->map)
+ return -ENODEV;
return 0;
}
-static int dw_i2c_of_configure(struct platform_device *pdev)
+static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
- struct resource *mem;
-
- switch (dev->flags & MODEL_MASK) {
- case MODEL_MSCC_OCELOT:
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- dev->ext = devm_ioremap_resource(&pdev->dev, mem);
- if (!IS_ERR(dev->ext))
- dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
- break;
- default:
- break;
- }
-
- return 0;
-}
+ pm_runtime_disable(dev->dev);
-static const struct of_device_id dw_i2c_of_match[] = {
- { .compatible = "snps,designware-i2c", },
- { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
- {},
-};
-MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
-#else
-static inline int dw_i2c_of_configure(struct platform_device *pdev)
-{
- return -ENODEV;
+ if (dev->shared_with_punit)
+ pm_runtime_put_noidle(dev->dev);
}
-#endif
-static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
+static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
{
- struct i2c_timings *t = &dev->timings;
+ struct platform_device *pdev = to_platform_device(dev->dev);
+ int ret;
- dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
+ if (device_is_compatible(dev->dev, "intel,xe-i2c"))
+ return dw_i2c_get_parent_regmap(dev);
- dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
- DW_IC_CON_RESTART_EN;
-
- dev->mode = DW_IC_MASTER;
-
- switch (t->bus_freq_hz) {
- case 100000:
- dev->master_cfg |= DW_IC_CON_SPEED_STD;
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_BAIKAL_BT1:
+ ret = bt1_i2c_request_regs(dev);
break;
- case 3400000:
- dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
+ case MODEL_WANGXUN_SP:
+ ret = dw_i2c_get_parent_regmap(dev);
break;
default:
- dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
+ ret = PTR_ERR_OR_ZERO(dev->base);
+ break;
}
-}
-
-static void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
-{
- dev->functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY;
-
- dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
- DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
- dev->mode = DW_IC_SLAVE;
+ return ret;
}
-static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
-{
- u32 param, tx_fifo_depth, rx_fifo_depth;
+static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
+ {
+ .ident = "Qtechnology QT5222",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Qtechnology"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "QT5222"),
+ },
+ },
+ { } /* terminate list */
+};
- /*
- * Try to detect the FIFO depth if not set by interface driver,
- * the depth could be from 2 to 256 from HW spec.
- */
- param = i2c_dw_read_comp_param(dev);
- tx_fifo_depth = ((param >> 16) & 0xff) + 1;
- rx_fifo_depth = ((param >> 8) & 0xff) + 1;
- if (!dev->tx_fifo_depth) {
- dev->tx_fifo_depth = tx_fifo_depth;
- dev->rx_fifo_depth = rx_fifo_depth;
- dev->adapter.nr = id;
- } else if (tx_fifo_depth >= 2) {
- dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth,
- tx_fifo_depth);
- dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth,
- rx_fifo_depth);
- }
-}
+static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = {
+#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL
+ {
+ .probe = i2c_dw_baytrail_probe_lock_support,
+ },
+#endif
+#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP
+ {
+ .probe = i2c_dw_amdpsp_probe_lock_support,
+ },
+#endif
+ {}
+};
-static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
+static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
{
- pm_runtime_disable(dev->dev);
+ const struct i2c_dw_semaphore_callbacks *ptr;
+ int i = 0;
+ int ret;
+
+ dev->semaphore_idx = -1;
+
+ for (ptr = i2c_dw_semaphore_cb_table; ptr->probe; ptr++) {
+ ret = ptr->probe(dev);
+ if (ret) {
+ /*
+ * If there is no semaphore device attached to this
+ * controller, we shouldn't abort general i2c_controller
+ * probe.
+ */
+ if (ret != -ENODEV)
+ return ret;
+
+ i++;
+ continue;
+ }
+
+ dev->semaphore_idx = i;
+ break;
+ }
- if (dev->shared_with_punit)
- pm_runtime_put_noidle(dev->dev);
+ return 0;
}
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
- struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
+ struct device *device = &pdev->dev;
struct i2c_adapter *adap;
struct dw_i2c_dev *dev;
- struct i2c_timings *t;
- u32 acpi_speed;
- struct resource *mem;
- int i, irq, ret;
- static const int supported_speeds[] = {
- 0, 100000, 400000, 1000000, 3400000
- };
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
+ int irq, ret;
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq == -ENXIO)
+ flags |= ACCESS_POLLING;
+ else if (irq < 0)
return irq;
- dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
+ dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(dev->base))
- return PTR_ERR(dev->base);
+ if (device_property_present(device, "wx,i2c-snps-model"))
+ flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
- dev->dev = &pdev->dev;
+ dev->dev = device;
dev->irq = irq;
+ dev->flags = flags;
platform_set_drvdata(pdev, dev);
- dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
- if (IS_ERR(dev->rst)) {
- if (PTR_ERR(dev->rst) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- } else {
- reset_control_deassert(dev->rst);
- }
+ ret = dw_i2c_plat_request_regs(dev);
+ if (ret)
+ return ret;
- t = &dev->timings;
- if (pdata)
- t->bus_freq_hz = pdata->i2c_scl_freq;
- else
- i2c_parse_fw_timings(&pdev->dev, t, false);
+ dev->rst = devm_reset_control_get_optional_exclusive(device, NULL);
+ if (IS_ERR(dev->rst))
+ return dev_err_probe(device, PTR_ERR(dev->rst), "failed to acquire reset\n");
- acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
- /*
- * Some DSTDs use a non standard speed, round down to the lowest
- * standard speed.
- */
- for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
- if (acpi_speed < supported_speeds[i])
- break;
- }
- acpi_speed = supported_speeds[i - 1];
+ reset_control_deassert(dev->rst);
- /*
- * Find bus speed from the "clock-frequency" device property, ACPI
- * or by using fast mode if neither is set.
- */
- if (acpi_speed && t->bus_freq_hz)
- t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
- else if (acpi_speed || t->bus_freq_hz)
- t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
- else
- t->bus_freq_hz = 400000;
+ ret = i2c_dw_fw_parse_and_configure(dev);
+ if (ret)
+ goto exit_reset;
- dev->flags |= (uintptr_t)device_get_match_data(&pdev->dev);
+ ret = i2c_dw_probe_lock_support(dev);
+ if (ret) {
+ dev_err_probe(device, ret, "failed to probe lock support\n");
+ goto exit_reset;
+ }
- if (pdev->dev.of_node)
- dw_i2c_of_configure(pdev);
+ i2c_dw_configure(dev);
- if (has_acpi_companion(&pdev->dev))
- dw_i2c_acpi_configure(pdev);
+ /* Optional interface clock */
+ dev->pclk = devm_clk_get_optional(device, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ ret = dev_err_probe(device, PTR_ERR(dev->pclk), "failed to acquire pclk\n");
+ goto exit_reset;
+ }
- /*
- * Only standard mode at 100kHz, fast mode at 400kHz,
- * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
- */
- if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 &&
- t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) {
- dev_err(&pdev->dev,
- "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
- t->bus_freq_hz);
- ret = -EINVAL;
+ dev->clk = devm_clk_get_optional(device, NULL);
+ if (IS_ERR(dev->clk)) {
+ ret = dev_err_probe(device, PTR_ERR(dev->clk), "failed to acquire clock\n");
goto exit_reset;
}
- ret = i2c_dw_probe_lock_support(dev);
+ ret = i2c_dw_prepare_clk(dev, true);
if (ret)
goto exit_reset;
- if (i2c_detect_slave_mode(&pdev->dev))
- i2c_dw_configure_slave(dev);
- else
- i2c_dw_configure_master(dev);
-
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (!i2c_dw_prepare_clk(dev, true)) {
+ if (dev->clk) {
+ struct i2c_timings *t = &dev->timings;
u64 clk_khz;
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
@@ -355,39 +271,35 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
if (!dev->sda_hold_time && t->sda_hold_ns)
dev->sda_hold_time =
- div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
+ DIV_S64_ROUND_CLOSEST(clk_khz * t->sda_hold_ns, MICRO);
}
- dw_i2c_set_fifo_size(dev, pdev->id);
-
adap = &dev->adapter;
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_DEPRECATED;
- ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
- adap->dev.of_node = pdev->dev.of_node;
+ adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ?
+ I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED;
+ adap->nr = -1;
- dev_pm_set_driver_flags(&pdev->dev,
- DPM_FLAG_SMART_PREPARE |
- DPM_FLAG_SMART_SUSPEND |
- DPM_FLAG_LEAVE_SUSPENDED);
+ if (dev->flags & ACCESS_NO_IRQ_SUSPEND)
+ dev_pm_set_driver_flags(device, DPM_FLAG_SMART_PREPARE);
+ else
+ dev_pm_set_driver_flags(device, DPM_FLAG_SMART_PREPARE | DPM_FLAG_SMART_SUSPEND);
+
+ device_enable_async_suspend(device);
/* The code below assumes runtime PM to be disabled. */
- WARN_ON(pm_runtime_enabled(&pdev->dev));
+ WARN_ON(pm_runtime_enabled(device));
- pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_active(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(device, 1000);
+ pm_runtime_use_autosuspend(device);
+ pm_runtime_set_active(device);
if (dev->shared_with_punit)
- pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_get_noresume(device);
- pm_runtime_enable(&pdev->dev);
-
- if (dev->mode == DW_IC_SLAVE)
- ret = i2c_dw_probe_slave(dev);
- else
- ret = i2c_dw_probe(dev);
+ pm_runtime_enable(device);
+ ret = i2c_dw_probe(dev);
if (ret)
goto exit_probe;
@@ -395,110 +307,79 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
exit_probe:
dw_i2c_plat_pm_cleanup(dev);
+ i2c_dw_prepare_clk(dev, false);
exit_reset:
- if (!IS_ERR_OR_NULL(dev->rst))
- reset_control_assert(dev->rst);
+ reset_control_assert(dev->rst);
return ret;
}
-static int dw_i2c_plat_remove(struct platform_device *pdev)
+static void dw_i2c_plat_remove(struct platform_device *pdev)
{
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct device *device = &pdev->dev;
- pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_sync(device);
i2c_del_adapter(&dev->adapter);
- dev->disable(dev);
+ i2c_dw_disable(dev);
- pm_runtime_dont_use_autosuspend(&pdev->dev);
- pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(device);
+ pm_runtime_put_noidle(device);
dw_i2c_plat_pm_cleanup(dev);
- if (!IS_ERR_OR_NULL(dev->rst))
- reset_control_assert(dev->rst);
+ i2c_dw_prepare_clk(dev, false);
- return 0;
+ reset_control_assert(dev->rst);
}
-#ifdef CONFIG_PM_SLEEP
-static int dw_i2c_plat_prepare(struct device *dev)
-{
- /*
- * If the ACPI companion device object is present for this device, it
- * may be accessed during suspend and resume of other devices via I2C
- * operation regions, so tell the PM core and middle layers to avoid
- * skipping system suspend/resume callbacks for it in that case.
- */
- return !has_acpi_companion(dev);
-}
-
-static void dw_i2c_plat_complete(struct device *dev)
-{
- /*
- * The device can only be in runtime suspend at this point if it has not
- * been resumed throughout the ending system suspend/resume cycle, so if
- * the platform firmware might mess up with it, request the runtime PM
- * framework to resume it.
- */
- if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
- pm_request_resume(dev);
-}
-#else
-#define dw_i2c_plat_prepare NULL
-#define dw_i2c_plat_complete NULL
-#endif
-
-#ifdef CONFIG_PM
-static int dw_i2c_plat_suspend(struct device *dev)
-{
- struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
-
- if (i_dev->shared_with_punit)
- return 0;
-
- i_dev->disable(i_dev);
- i2c_dw_prepare_clk(i_dev, false);
-
- return 0;
-}
-
-static int dw_i2c_plat_resume(struct device *dev)
-{
- struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
-
- if (!i_dev->shared_with_punit)
- i2c_dw_prepare_clk(i_dev, true);
-
- i_dev->init(i_dev);
-
- return 0;
-}
-
-static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
- .prepare = dw_i2c_plat_prepare,
- .complete = dw_i2c_plat_complete,
- SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
- SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
+static const struct of_device_id dw_i2c_of_match[] = {
+ { .compatible = "snps,designware-i2c", },
+ { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
+ { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
+ {}
};
+MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
-#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
-#else
-#define DW_I2C_DEV_PMOPS NULL
-#endif
+static const struct acpi_device_id dw_i2c_acpi_match[] = {
+ { "80860F41", ACCESS_NO_IRQ_SUSPEND },
+ { "808622C1", ACCESS_NO_IRQ_SUSPEND },
+ { "AMD0010", ACCESS_INTR_MASK },
+ { "AMDI0010", ACCESS_INTR_MASK },
+ { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
+ { "AMDI0510", 0 },
+ { "APMC0D0F", 0 },
+ { "FUJI200B", 0 },
+ { "HISI02A1", 0 },
+ { "HISI02A2", 0 },
+ { "HISI02A3", 0 },
+ { "HJMC3001", 0 },
+ { "HYGO0010", ACCESS_INTR_MASK },
+ { "INT33C2", 0 },
+ { "INT33C3", 0 },
+ { "INT3432", 0 },
+ { "INT3433", 0 },
+ { "INTC10EF", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
-/* Work with hotplug and coldplug */
-MODULE_ALIAS("platform:i2c_designware");
+static const struct platform_device_id dw_i2c_platform_ids[] = {
+ { "i2c_designware" },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids);
static struct platform_driver dw_i2c_driver = {
.probe = dw_i2c_plat_probe,
.remove = dw_i2c_plat_remove,
.driver = {
.name = "i2c_designware",
- .of_match_table = of_match_ptr(dw_i2c_of_match),
- .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
- .pm = DW_I2C_DEV_PMOPS,
+ .of_match_table = dw_i2c_of_match,
+ .acpi_match_table = dw_i2c_acpi_match,
+ .pm = pm_ptr(&i2c_dw_dev_pm_ops),
},
+ .id_table = dw_i2c_platform_ids,
};
static int __init dw_i2c_init_driver(void)
@@ -516,3 +397,5 @@ module_exit(dw_i2c_exit_driver);
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("I2C_DW");
+MODULE_IMPORT_NS("I2C_DW_COMMON");
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index e7f9305b2dd9..6eb16b7d75a6 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -6,6 +6,9 @@
*
* Copyright (C) 2016 Synopsys Inc.
*/
+
+#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW"
+
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -14,27 +17,30 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include "i2c-designware-core.h"
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
{
/* Configure Tx/Rx FIFO threshold levels. */
- dw_writel(dev, 0, DW_IC_TX_TL);
- dw_writel(dev, 0, DW_IC_RX_TL);
+ regmap_write(dev->map, DW_IC_TX_TL, 0);
+ regmap_write(dev->map, DW_IC_RX_TL, 0);
/* Configure the I2C slave. */
- dw_writel(dev, dev->slave_cfg, DW_IC_CON);
- dw_writel(dev, DW_IC_INTR_SLAVE_MASK, DW_IC_INTR_MASK);
+ regmap_write(dev->map, DW_IC_CON, dev->slave_cfg);
+ regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK);
}
/**
- * i2c_dw_init_slave() - Initialize the designware i2c slave hardware
+ * i2c_dw_init_slave() - Initialize the DesignWare i2c slave hardware
* @dev: device private data
*
* This function configures and enables the I2C in slave mode.
* This function is called during I2C init function, and in case of timeout at
* run time.
+ *
+ * Return: 0 on success, or negative errno otherwise.
*/
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
{
@@ -49,7 +55,7 @@ static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
/* Write SDA hold time if supported */
if (dev->sda_hold_time)
- dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
+ regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
i2c_dw_configure_fifo_slave(dev);
i2c_dw_release_lock(dev);
@@ -72,18 +78,12 @@ static int i2c_dw_reg_slave(struct i2c_client *slave)
* the address to which the DW_apb_i2c responds.
*/
__i2c_dw_disable_nowait(dev);
- dw_writel(dev, slave->addr, DW_IC_SAR);
+ regmap_write(dev->map, DW_IC_SAR, slave->addr);
dev->slave = slave;
__i2c_dw_enable(dev);
- dev->cmd_err = 0;
- dev->msg_write_idx = 0;
- dev->msg_read_idx = 0;
- dev->msg_err = 0;
- dev->status = STATUS_IDLE;
- dev->abort_source = 0;
- dev->rx_outstanding = 0;
+ dev->status = 0;
return 0;
}
@@ -92,57 +92,58 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
- dev->disable_int(dev);
- dev->disable(dev);
+ regmap_write(dev->map, DW_IC_INTR_MASK, 0);
+ i2c_dw_disable(dev);
+ synchronize_irq(dev->irq);
dev->slave = NULL;
- pm_runtime_put(dev->dev);
+ pm_runtime_put_sync_suspend(dev->dev);
return 0;
}
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
{
- u32 stat;
+ unsigned int stat, dummy;
/*
* The IC_INTR_STAT register just indicates "enabled" interrupts.
- * Ths unmasked raw version of interrupt status bits are available
+ * The unmasked raw version of interrupt status bits is available
* in the IC_RAW_INTR_STAT register.
*
* That is,
- * stat = dw_readl(IC_INTR_STAT);
+ * stat = readl(IC_INTR_STAT);
* equals to,
- * stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
+ * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
*
* The raw version might be useful for debugging purposes.
*/
- stat = dw_readl(dev, DW_IC_INTR_STAT);
+ regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
/*
* Do not use the IC_CLR_INTR register to clear interrupts, or
* you'll miss some interrupts, triggered during the period from
- * dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
+ * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
*
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_TX_ABRT)
- dw_readl(dev, DW_IC_CLR_TX_ABRT);
+ regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
if (stat & DW_IC_INTR_RX_UNDER)
- dw_readl(dev, DW_IC_CLR_RX_UNDER);
+ regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
if (stat & DW_IC_INTR_RX_OVER)
- dw_readl(dev, DW_IC_CLR_RX_OVER);
+ regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
if (stat & DW_IC_INTR_TX_OVER)
- dw_readl(dev, DW_IC_CLR_TX_OVER);
+ regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
if (stat & DW_IC_INTR_RX_DONE)
- dw_readl(dev, DW_IC_CLR_RX_DONE);
+ regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
if (stat & DW_IC_INTR_ACTIVITY)
- dw_readl(dev, DW_IC_CLR_ACTIVITY);
+ regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
if (stat & DW_IC_INTR_STOP_DET)
- dw_readl(dev, DW_IC_CLR_STOP_DET);
+ regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
if (stat & DW_IC_INTR_START_DET)
- dw_readl(dev, DW_IC_CLR_START_DET);
+ regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
if (stat & DW_IC_INTR_GEN_CALL)
- dw_readl(dev, DW_IC_CLR_GEN_CALL);
+ regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
return stat;
}
@@ -151,87 +152,69 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
* Interrupt service routine. This gets called whenever an I2C slave interrupt
* occurs.
*/
-
-static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
+static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
{
- u32 raw_stat, stat, enabled;
- u8 val, slave_activity;
+ struct dw_i2c_dev *dev = dev_id;
+ unsigned int raw_stat, stat, enabled, tmp;
+ u8 val = 0, slave_activity;
- stat = dw_readl(dev, DW_IC_INTR_STAT);
- enabled = dw_readl(dev, DW_IC_ENABLE);
- raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
- slave_activity = ((dw_readl(dev, DW_IC_STATUS) &
- DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);
+ regmap_read(dev->map, DW_IC_ENABLE, &enabled);
+ regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &raw_stat);
+ regmap_read(dev->map, DW_IC_STATUS, &tmp);
+ slave_activity = ((tmp & DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);
if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY) || !dev->slave)
- return 0;
+ return IRQ_NONE;
+ stat = i2c_dw_read_clear_intrbits_slave(dev);
dev_dbg(dev->dev,
"%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",
enabled, slave_activity, raw_stat, stat);
- if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET))
- i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
+ if (stat & DW_IC_INTR_RX_FULL) {
+ if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
+ dev->status |= STATUS_WRITE_IN_PROGRESS;
+ dev->status &= ~STATUS_READ_IN_PROGRESS;
+ i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED,
+ &val);
+ }
+
+ do {
+ regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
+ if (tmp & DW_IC_DATA_CMD_FIRST_DATA_BYTE)
+ i2c_slave_event(dev->slave,
+ I2C_SLAVE_WRITE_REQUESTED,
+ &val);
+ val = tmp;
+ i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
+ &val);
+ regmap_read(dev->map, DW_IC_STATUS, &tmp);
+ } while (tmp & DW_IC_STATUS_RFNE);
+ }
if (stat & DW_IC_INTR_RD_REQ) {
if (slave_activity) {
- if (stat & DW_IC_INTR_RX_FULL) {
- val = dw_readl(dev, DW_IC_DATA_CMD);
-
- if (!i2c_slave_event(dev->slave,
- I2C_SLAVE_WRITE_RECEIVED,
- &val)) {
- dev_vdbg(dev->dev, "Byte %X acked!",
- val);
- }
- dw_readl(dev, DW_IC_CLR_RD_REQ);
- stat = i2c_dw_read_clear_intrbits_slave(dev);
+ regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
+
+ if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
+ i2c_slave_event(dev->slave,
+ I2C_SLAVE_READ_REQUESTED,
+ &val);
+ dev->status |= STATUS_READ_IN_PROGRESS;
+ dev->status &= ~STATUS_WRITE_IN_PROGRESS;
} else {
- dw_readl(dev, DW_IC_CLR_RD_REQ);
- dw_readl(dev, DW_IC_CLR_RX_UNDER);
- stat = i2c_dw_read_clear_intrbits_slave(dev);
+ i2c_slave_event(dev->slave,
+ I2C_SLAVE_READ_PROCESSED,
+ &val);
}
- if (!i2c_slave_event(dev->slave,
- I2C_SLAVE_READ_REQUESTED,
- &val))
- dw_writel(dev, val, DW_IC_DATA_CMD);
+ regmap_write(dev->map, DW_IC_DATA_CMD, val);
}
}
- if (stat & DW_IC_INTR_RX_DONE) {
- if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
- &val))
- dw_readl(dev, DW_IC_CLR_RX_DONE);
-
- i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
- stat = i2c_dw_read_clear_intrbits_slave(dev);
- return 1;
- }
-
- if (stat & DW_IC_INTR_RX_FULL) {
- val = dw_readl(dev, DW_IC_DATA_CMD);
- if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
- &val))
- dev_vdbg(dev->dev, "Byte %X acked!", val);
- } else {
+ if (stat & DW_IC_INTR_STOP_DET)
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
- stat = i2c_dw_read_clear_intrbits_slave(dev);
- }
- return 1;
-}
-
-static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
-{
- struct dw_i2c_dev *dev = dev_id;
- int ret;
-
- i2c_dw_read_clear_intrbits_slave(dev);
- ret = i2c_dw_irq_handler_slave(dev);
- if (ret > 0)
- complete(&dev->cmd_complete);
-
- return IRQ_RETVAL(ret);
+ return IRQ_HANDLED;
}
static const struct i2c_algorithm i2c_dw_algo = {
@@ -240,18 +223,25 @@ static const struct i2c_algorithm i2c_dw_algo = {
.unreg_slave = i2c_dw_unreg_slave,
};
+void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
+{
+ dev->functionality = I2C_FUNC_SLAVE;
+
+ dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
+ DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
+
+ dev->mode = DW_IC_SLAVE;
+}
+EXPORT_SYMBOL_GPL(i2c_dw_configure_slave);
+
int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
int ret;
- init_completion(&dev->cmd_complete);
-
dev->init = i2c_dw_init_slave;
- dev->disable = i2c_dw_disable;
- dev->disable_int = i2c_dw_disable_int;
- ret = i2c_dw_set_reg_access(dev);
+ ret = i2c_dw_init_regmap(dev);
if (ret)
return ret;
@@ -259,6 +249,10 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
if (ret)
return ret;
+ ret = i2c_dw_set_fifo_size(dev);
+ if (ret)
+ return ret;
+
ret = dev->init(dev);
if (ret)
return ret;
@@ -272,11 +266,10 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr_slave,
IRQF_SHARED, dev_name(dev->dev), dev);
- if (ret) {
- dev_err(dev->dev, "failure requesting irq %i: %d\n",
- dev->irq, ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev->dev, ret,
+ "failure requesting IRQ %i: %d\n",
+ dev->irq, ret);
ret = i2c_add_numbered_adapter(adap);
if (ret)
@@ -289,3 +282,4 @@ EXPORT_SYMBOL_GPL(i2c_dw_probe_slave);
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("I2C_DW_COMMON");
diff --git a/drivers/i2c/busses/i2c-digicolor.c b/drivers/i2c/busses/i2c-digicolor.c
index 50813a24c541..38d7f31aee79 100644
--- a/drivers/i2c/busses/i2c-digicolor.c
+++ b/drivers/i2c/busses/i2c-digicolor.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C bus driver for Conexant Digicolor SoCs
*
* Author: Baruch Siach <baruch@tkos.co.il>
*
* Copyright (C) 2015 Paradox Innovation Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -21,7 +18,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
-#define DEFAULT_FREQ 100000
#define TIMEOUT_MS 100
#define II_CONTROL 0x0
@@ -164,12 +160,11 @@ static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
{
struct dc_i2c *i2c = dev_id;
int cmd_status = dc_i2c_cmd_status(i2c);
- unsigned long flags;
u8 addr_cmd;
writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR);
- spin_lock_irqsave(&i2c->lock, flags);
+ spin_lock(&i2c->lock);
if (cmd_status == II_CMD_STATUS_ACK_BAD
|| cmd_status == II_CMD_STATUS_ABORT) {
@@ -191,7 +186,7 @@ static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
break;
}
i2c->state = STATE_WRITE;
- /* fall through */
+ fallthrough;
case STATE_WRITE:
if (i2c->msgbuf_ptr < i2c->msg->len)
dc_i2c_write_buf(i2c);
@@ -211,14 +206,14 @@ static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
}
out:
- spin_unlock_irqrestore(&i2c->lock, flags);
+ spin_unlock(&i2c->lock);
return IRQ_HANDLED;
}
static int dc_i2c_xfer_msg(struct dc_i2c *i2c, struct i2c_msg *msg, int first,
int last)
{
- unsigned long timeout = msecs_to_jiffies(TIMEOUT_MS);
+ unsigned long time_left = msecs_to_jiffies(TIMEOUT_MS);
unsigned long flags;
spin_lock_irqsave(&i2c->lock, flags);
@@ -232,9 +227,9 @@ static int dc_i2c_xfer_msg(struct dc_i2c *i2c, struct i2c_msg *msg, int first,
dc_i2c_start_msg(i2c, first);
spin_unlock_irqrestore(&i2c->lock, flags);
- timeout = wait_for_completion_timeout(&i2c->done, timeout);
+ time_left = wait_for_completion_timeout(&i2c->done, time_left);
dc_i2c_set_irq(i2c, 0);
- if (timeout == 0) {
+ if (time_left == 0) {
i2c->state = STATE_IDLE;
return -ETIMEDOUT;
}
@@ -286,15 +281,14 @@ static u32 dc_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm dc_i2c_algorithm = {
- .master_xfer = dc_i2c_xfer,
- .functionality = dc_i2c_func,
+ .xfer = dc_i2c_xfer,
+ .functionality = dc_i2c_func,
};
static int dc_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct dc_i2c *i2c;
- struct resource *r;
int ret = 0, irq;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct dc_i2c), GFP_KERNEL);
@@ -303,7 +297,7 @@ static int dc_i2c_probe(struct platform_device *pdev)
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&i2c->frequency))
- i2c->frequency = DEFAULT_FREQ;
+ i2c->frequency = I2C_MAX_STANDARD_MODE_FREQ;
i2c->dev = &pdev->dev;
platform_set_drvdata(pdev, i2c);
@@ -315,8 +309,7 @@ static int dc_i2c_probe(struct platform_device *pdev)
if (IS_ERR(i2c->clk))
return PTR_ERR(i2c->clk);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, r);
+ i2c->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
@@ -329,7 +322,7 @@ static int dc_i2c_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- strlcpy(i2c->adap.name, "Conexant Digicolor I2C adapter",
+ strscpy(i2c->adap.name, "Conexant Digicolor I2C adapter",
sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &dc_i2c_algorithm;
@@ -354,25 +347,23 @@ static int dc_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int dc_i2c_remove(struct platform_device *pdev)
+static void dc_i2c_remove(struct platform_device *pdev)
{
struct dc_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
clk_disable_unprepare(i2c->clk);
-
- return 0;
}
static const struct of_device_id dc_i2c_match[] = {
{ .compatible = "cnxt,cx92755-i2c" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, dc_i2c_match);
static struct platform_driver dc_i2c_driver = {
.probe = dc_i2c_probe,
- .remove = dc_i2c_remove,
+ .remove = dc_i2c_remove,
.driver = {
.name = "digicolor-i2c",
.of_match_table = dc_i2c_match,
@@ -381,5 +372,5 @@ static struct platform_driver dc_i2c_driver = {
module_platform_driver(dc_i2c_driver);
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
-MODULE_DESCRIPTION("Conexant Digicolor I2C master driver");
+MODULE_DESCRIPTION("Conexant Digicolor I2C controller driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c
index 3f28317cde39..c02459405b26 100644
--- a/drivers/i2c/busses/i2c-diolan-u2c.c
+++ b/drivers/i2c/busses/i2c-diolan-u2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Diolan u2c-12 USB-I2C adapter
*
@@ -6,10 +7,6 @@
* Derived from:
* i2c-tiny-usb.c
* Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
@@ -67,8 +64,6 @@
#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */
#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1)
-#define U2C_I2C_FREQ_FAST 400000
-#define U2C_I2C_FREQ_STD 100000
#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10))
#define DIOLAN_USB_TIMEOUT 100 /* in ms */
@@ -90,7 +85,7 @@ struct i2c_diolan_u2c {
int ocount; /* Number of enqueued messages */
};
-static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */
+static uint frequency = I2C_MAX_STANDARD_MODE_FREQ; /* I2C clock frequency in Hz */
module_param(frequency, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz");
@@ -302,12 +297,12 @@ static int diolan_init(struct i2c_diolan_u2c *dev)
{
int speed, ret;
- if (frequency >= 200000) {
+ if (frequency >= 2 * I2C_MAX_STANDARD_MODE_FREQ) {
speed = U2C_I2C_SPEED_FAST;
- frequency = U2C_I2C_FREQ_FAST;
- } else if (frequency >= 100000 || frequency == 0) {
+ frequency = I2C_MAX_FAST_MODE_FREQ;
+ } else if (frequency >= I2C_MAX_STANDARD_MODE_FREQ || frequency == 0) {
speed = U2C_I2C_SPEED_STD;
- frequency = U2C_I2C_FREQ_STD;
+ frequency = I2C_MAX_STANDARD_MODE_FREQ;
} else {
speed = U2C_I2C_SPEED(frequency);
if (speed > U2C_I2C_SPEED_2KHZ)
@@ -419,7 +414,7 @@ static u32 diolan_usb_func(struct i2c_adapter *a)
}
static const struct i2c_algorithm diolan_usb_algorithm = {
- .master_xfer = diolan_usb_xfer,
+ .xfer = diolan_usb_xfer,
.functionality = diolan_usb_func,
};
diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c
index 69075a32073e..bde2ef098862 100644
--- a/drivers/i2c/busses/i2c-dln2.c
+++ b/drivers/i2c/busses/i2c-dln2.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Diolan DLN-2 USB-I2C adapter
*
@@ -6,10 +7,6 @@
* Derived from:
* i2c-diolan-u2c.c
* Copyright (c) 2010-2011 Ericsson AB
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
@@ -178,7 +175,7 @@ static u32 dln2_i2c_func(struct i2c_adapter *a)
}
static const struct i2c_algorithm dln2_i2c_usb_algorithm = {
- .master_xfer = dln2_i2c_xfer,
+ .xfer = dln2_i2c_xfer,
.functionality = dln2_i2c_func,
};
@@ -221,10 +218,8 @@ static int dln2_i2c_probe(struct platform_device *pdev)
/* initialize the i2c interface */
ret = dln2_i2c_enable(dln2, true);
- if (ret < 0) {
- dev_err(dev, "failed to initialize adapter: %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to initialize adapter\n");
/* and finally attach to i2c layer */
ret = i2c_add_adapter(&dln2->adapter);
@@ -239,14 +234,12 @@ out_disable:
return ret;
}
-static int dln2_i2c_remove(struct platform_device *pdev)
+static void dln2_i2c_remove(struct platform_device *pdev)
{
struct dln2_i2c *dln2 = platform_get_drvdata(pdev);
i2c_del_adapter(&dln2->adapter);
dln2_i2c_enable(dln2, false);
-
- return 0;
}
static struct platform_driver dln2_i2c_driver = {
@@ -258,6 +251,6 @@ static struct platform_driver dln2_i2c_driver = {
module_platform_driver(dln2_i2c_driver);
MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
-MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C controller interface");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dln2-i2c");
diff --git a/drivers/i2c/busses/i2c-efm32.c b/drivers/i2c/busses/i2c-efm32.c
deleted file mode 100644
index 5f2bab878b2c..000000000000
--- a/drivers/i2c/busses/i2c-efm32.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright (C) 2014 Uwe Kleine-Koenig for Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
- */
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-
-#define DRIVER_NAME "efm32-i2c"
-
-#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
-
-#define REG_CTRL 0x00
-#define REG_CTRL_EN 0x00001
-#define REG_CTRL_SLAVE 0x00002
-#define REG_CTRL_AUTOACK 0x00004
-#define REG_CTRL_AUTOSE 0x00008
-#define REG_CTRL_AUTOSN 0x00010
-#define REG_CTRL_ARBDIS 0x00020
-#define REG_CTRL_GCAMEN 0x00040
-#define REG_CTRL_CLHR__MASK 0x00300
-#define REG_CTRL_BITO__MASK 0x03000
-#define REG_CTRL_BITO_OFF 0x00000
-#define REG_CTRL_BITO_40PCC 0x01000
-#define REG_CTRL_BITO_80PCC 0x02000
-#define REG_CTRL_BITO_160PCC 0x03000
-#define REG_CTRL_GIBITO 0x08000
-#define REG_CTRL_CLTO__MASK 0x70000
-#define REG_CTRL_CLTO_OFF 0x00000
-
-#define REG_CMD 0x04
-#define REG_CMD_START 0x00001
-#define REG_CMD_STOP 0x00002
-#define REG_CMD_ACK 0x00004
-#define REG_CMD_NACK 0x00008
-#define REG_CMD_CONT 0x00010
-#define REG_CMD_ABORT 0x00020
-#define REG_CMD_CLEARTX 0x00040
-#define REG_CMD_CLEARPC 0x00080
-
-#define REG_STATE 0x08
-#define REG_STATE_BUSY 0x00001
-#define REG_STATE_MASTER 0x00002
-#define REG_STATE_TRANSMITTER 0x00004
-#define REG_STATE_NACKED 0x00008
-#define REG_STATE_BUSHOLD 0x00010
-#define REG_STATE_STATE__MASK 0x000e0
-#define REG_STATE_STATE_IDLE 0x00000
-#define REG_STATE_STATE_WAIT 0x00020
-#define REG_STATE_STATE_START 0x00040
-#define REG_STATE_STATE_ADDR 0x00060
-#define REG_STATE_STATE_ADDRACK 0x00080
-#define REG_STATE_STATE_DATA 0x000a0
-#define REG_STATE_STATE_DATAACK 0x000c0
-
-#define REG_STATUS 0x0c
-#define REG_STATUS_PSTART 0x00001
-#define REG_STATUS_PSTOP 0x00002
-#define REG_STATUS_PACK 0x00004
-#define REG_STATUS_PNACK 0x00008
-#define REG_STATUS_PCONT 0x00010
-#define REG_STATUS_PABORT 0x00020
-#define REG_STATUS_TXC 0x00040
-#define REG_STATUS_TXBL 0x00080
-#define REG_STATUS_RXDATAV 0x00100
-
-#define REG_CLKDIV 0x10
-#define REG_CLKDIV_DIV__MASK 0x001ff
-#define REG_CLKDIV_DIV(div) MASK_VAL(REG_CLKDIV_DIV__MASK, (div))
-
-#define REG_SADDR 0x14
-#define REG_SADDRMASK 0x18
-#define REG_RXDATA 0x1c
-#define REG_RXDATAP 0x20
-#define REG_TXDATA 0x24
-#define REG_IF 0x28
-#define REG_IF_START 0x00001
-#define REG_IF_RSTART 0x00002
-#define REG_IF_ADDR 0x00004
-#define REG_IF_TXC 0x00008
-#define REG_IF_TXBL 0x00010
-#define REG_IF_RXDATAV 0x00020
-#define REG_IF_ACK 0x00040
-#define REG_IF_NACK 0x00080
-#define REG_IF_MSTOP 0x00100
-#define REG_IF_ARBLOST 0x00200
-#define REG_IF_BUSERR 0x00400
-#define REG_IF_BUSHOLD 0x00800
-#define REG_IF_TXOF 0x01000
-#define REG_IF_RXUF 0x02000
-#define REG_IF_BITO 0x04000
-#define REG_IF_CLTO 0x08000
-#define REG_IF_SSTOP 0x10000
-
-#define REG_IFS 0x2c
-#define REG_IFC 0x30
-#define REG_IFC__MASK 0x1ffcf
-
-#define REG_IEN 0x34
-
-#define REG_ROUTE 0x38
-#define REG_ROUTE_SDAPEN 0x00001
-#define REG_ROUTE_SCLPEN 0x00002
-#define REG_ROUTE_LOCATION__MASK 0x00700
-#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
-
-struct efm32_i2c_ddata {
- struct i2c_adapter adapter;
-
- struct clk *clk;
- void __iomem *base;
- unsigned int irq;
- u8 location;
- unsigned long frequency;
-
- /* transfer data */
- struct completion done;
- struct i2c_msg *msgs;
- size_t num_msgs;
- size_t current_word, current_msg;
- int retval;
-};
-
-static u32 efm32_i2c_read32(struct efm32_i2c_ddata *ddata, unsigned offset)
-{
- return readl(ddata->base + offset);
-}
-
-static void efm32_i2c_write32(struct efm32_i2c_ddata *ddata,
- unsigned offset, u32 value)
-{
- writel(value, ddata->base + offset);
-}
-
-static void efm32_i2c_send_next_msg(struct efm32_i2c_ddata *ddata)
-{
- struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
-
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
- efm32_i2c_write32(ddata, REG_TXDATA, i2c_8bit_addr_from_msg(cur_msg));
-}
-
-static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
-{
- struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
-
- if (ddata->current_word >= cur_msg->len) {
- /* cur_msg completely transferred */
- ddata->current_word = 0;
- ddata->current_msg += 1;
-
- if (ddata->current_msg >= ddata->num_msgs) {
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
- complete(&ddata->done);
- } else {
- efm32_i2c_send_next_msg(ddata);
- }
- } else {
- efm32_i2c_write32(ddata, REG_TXDATA,
- cur_msg->buf[ddata->current_word++]);
- }
-}
-
-static void efm32_i2c_recv_next_byte(struct efm32_i2c_ddata *ddata)
-{
- struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
-
- cur_msg->buf[ddata->current_word] = efm32_i2c_read32(ddata, REG_RXDATA);
- ddata->current_word += 1;
- if (ddata->current_word >= cur_msg->len) {
- /* cur_msg completely transferred */
- ddata->current_word = 0;
- ddata->current_msg += 1;
-
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_NACK);
-
- if (ddata->current_msg >= ddata->num_msgs) {
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
- complete(&ddata->done);
- } else {
- efm32_i2c_send_next_msg(ddata);
- }
- } else {
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ACK);
- }
-}
-
-static irqreturn_t efm32_i2c_irq(int irq, void *dev_id)
-{
- struct efm32_i2c_ddata *ddata = dev_id;
- struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
- u32 irqflag = efm32_i2c_read32(ddata, REG_IF);
- u32 state = efm32_i2c_read32(ddata, REG_STATE);
-
- efm32_i2c_write32(ddata, REG_IFC, irqflag & REG_IFC__MASK);
-
- switch (state & REG_STATE_STATE__MASK) {
- case REG_STATE_STATE_IDLE:
- /* arbitration lost? */
- ddata->retval = -EAGAIN;
- complete(&ddata->done);
- break;
- case REG_STATE_STATE_WAIT:
- /*
- * huh, this shouldn't happen.
- * Reset hardware state and get out
- */
- ddata->retval = -EIO;
- efm32_i2c_write32(ddata, REG_CMD,
- REG_CMD_STOP | REG_CMD_ABORT |
- REG_CMD_CLEARTX | REG_CMD_CLEARPC);
- complete(&ddata->done);
- break;
- case REG_STATE_STATE_START:
- /* "caller" is expected to send an address */
- break;
- case REG_STATE_STATE_ADDR:
- /* wait for Ack or NAck of slave */
- break;
- case REG_STATE_STATE_ADDRACK:
- if (state & REG_STATE_NACKED) {
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
- ddata->retval = -ENXIO;
- complete(&ddata->done);
- } else if (cur_msg->flags & I2C_M_RD) {
- /* wait for slave to send first data byte */
- } else {
- efm32_i2c_send_next_byte(ddata);
- }
- break;
- case REG_STATE_STATE_DATA:
- if (cur_msg->flags & I2C_M_RD) {
- efm32_i2c_recv_next_byte(ddata);
- } else {
- /* wait for Ack or Nack of slave */
- }
- break;
- case REG_STATE_STATE_DATAACK:
- if (state & REG_STATE_NACKED) {
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
- complete(&ddata->done);
- } else {
- efm32_i2c_send_next_byte(ddata);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static int efm32_i2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- struct efm32_i2c_ddata *ddata = i2c_get_adapdata(adap);
- int ret;
-
- if (ddata->msgs)
- return -EBUSY;
-
- ddata->msgs = msgs;
- ddata->num_msgs = num;
- ddata->current_word = 0;
- ddata->current_msg = 0;
- ddata->retval = -EIO;
-
- reinit_completion(&ddata->done);
-
- dev_dbg(&ddata->adapter.dev, "state: %08x, status: %08x\n",
- efm32_i2c_read32(ddata, REG_STATE),
- efm32_i2c_read32(ddata, REG_STATUS));
-
- efm32_i2c_send_next_msg(ddata);
-
- wait_for_completion(&ddata->done);
-
- if (ddata->current_msg >= ddata->num_msgs)
- ret = ddata->num_msgs;
- else
- ret = ddata->retval;
-
- return ret;
-}
-
-static u32 efm32_i2c_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm efm32_i2c_algo = {
- .master_xfer = efm32_i2c_master_xfer,
- .functionality = efm32_i2c_functionality,
-};
-
-static u32 efm32_i2c_get_configured_location(struct efm32_i2c_ddata *ddata)
-{
- u32 reg = efm32_i2c_read32(ddata, REG_ROUTE);
-
- return (reg & REG_ROUTE_LOCATION__MASK) >>
- __ffs(REG_ROUTE_LOCATION__MASK);
-}
-
-static int efm32_i2c_probe(struct platform_device *pdev)
-{
- struct efm32_i2c_ddata *ddata;
- struct resource *res;
- unsigned long rate;
- struct device_node *np = pdev->dev.of_node;
- u32 location, frequency;
- int ret;
- u32 clkdiv;
-
- if (!np)
- return -EINVAL;
-
- ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
- if (!ddata)
- return -ENOMEM;
- platform_set_drvdata(pdev, ddata);
-
- init_completion(&ddata->done);
- strlcpy(ddata->adapter.name, pdev->name, sizeof(ddata->adapter.name));
- ddata->adapter.owner = THIS_MODULE;
- ddata->adapter.algo = &efm32_i2c_algo;
- ddata->adapter.dev.parent = &pdev->dev;
- ddata->adapter.dev.of_node = pdev->dev.of_node;
- i2c_set_adapdata(&ddata->adapter, ddata);
-
- ddata->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ddata->clk)) {
- ret = PTR_ERR(ddata->clk);
- dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to determine base address\n");
- return -ENODEV;
- }
-
- if (resource_size(res) < 0x42) {
- dev_err(&pdev->dev, "memory resource too small\n");
- return -EINVAL;
- }
-
- ddata->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ddata->base))
- return PTR_ERR(ddata->base);
-
- ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "failed to get irq (%d)\n", ret);
- if (!ret)
- ret = -EINVAL;
- return ret;
- }
-
- ddata->irq = ret;
-
- ret = clk_prepare_enable(ddata->clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
- return ret;
- }
-
-
- ret = of_property_read_u32(np, "energymicro,location", &location);
-
- if (ret)
- /* fall back to wrongly namespaced property */
- ret = of_property_read_u32(np, "efm32,location", &location);
-
- if (!ret) {
- dev_dbg(&pdev->dev, "using location %u\n", location);
- } else {
- /* default to location configured in hardware */
- location = efm32_i2c_get_configured_location(ddata);
-
- dev_info(&pdev->dev, "fall back to location %u\n", location);
- }
-
- ddata->location = location;
-
- ret = of_property_read_u32(np, "clock-frequency", &frequency);
- if (!ret) {
- dev_dbg(&pdev->dev, "using frequency %u\n", frequency);
- } else {
- frequency = 100000;
- dev_info(&pdev->dev, "defaulting to 100 kHz\n");
- }
- ddata->frequency = frequency;
-
- rate = clk_get_rate(ddata->clk);
- if (!rate) {
- dev_err(&pdev->dev, "there is no input clock available\n");
- ret = -EINVAL;
- goto err_disable_clk;
- }
- clkdiv = DIV_ROUND_UP(rate, 8 * ddata->frequency) - 1;
- if (clkdiv >= 0x200) {
- dev_err(&pdev->dev,
- "input clock too fast (%lu) to divide down to bus freq (%lu)",
- rate, ddata->frequency);
- ret = -EINVAL;
- goto err_disable_clk;
- }
-
- dev_dbg(&pdev->dev, "input clock = %lu, bus freq = %lu, clkdiv = %lu\n",
- rate, ddata->frequency, (unsigned long)clkdiv);
- efm32_i2c_write32(ddata, REG_CLKDIV, REG_CLKDIV_DIV(clkdiv));
-
- efm32_i2c_write32(ddata, REG_ROUTE, REG_ROUTE_SDAPEN |
- REG_ROUTE_SCLPEN |
- REG_ROUTE_LOCATION(ddata->location));
-
- efm32_i2c_write32(ddata, REG_CTRL, REG_CTRL_EN |
- REG_CTRL_BITO_160PCC | 0 * REG_CTRL_GIBITO);
-
- efm32_i2c_write32(ddata, REG_IFC, REG_IFC__MASK);
- efm32_i2c_write32(ddata, REG_IEN, REG_IF_TXC | REG_IF_ACK | REG_IF_NACK
- | REG_IF_ARBLOST | REG_IF_BUSERR | REG_IF_RXDATAV);
-
- /* to make bus idle */
- efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ABORT);
-
- ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
- goto err_disable_clk;
- }
-
- ret = i2c_add_adapter(&ddata->adapter);
- if (ret) {
- free_irq(ddata->irq, ddata);
-
-err_disable_clk:
- clk_disable_unprepare(ddata->clk);
- }
- return ret;
-}
-
-static int efm32_i2c_remove(struct platform_device *pdev)
-{
- struct efm32_i2c_ddata *ddata = platform_get_drvdata(pdev);
-
- i2c_del_adapter(&ddata->adapter);
- free_irq(ddata->irq, ddata);
- clk_disable_unprepare(ddata->clk);
-
- return 0;
-}
-
-static const struct of_device_id efm32_i2c_dt_ids[] = {
- {
- .compatible = "energymicro,efm32-i2c",
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(of, efm32_i2c_dt_ids);
-
-static struct platform_driver efm32_i2c_driver = {
- .probe = efm32_i2c_probe,
- .remove = efm32_i2c_remove,
-
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = efm32_i2c_dt_ids,
- },
-};
-module_platform_driver(efm32_i2c_driver);
-
-MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
-MODULE_DESCRIPTION("EFM32 i2c driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 835d54ac2971..27ea3c130a16 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -56,8 +48,6 @@
#define BUS_IDLE_TIMEOUT 20
#define PCH_I2CCTL_I2CMEN 0x0080
-#define TEN_BIT_ADDR_DEFAULT 0xF000
-#define TEN_BIT_ADDR_MASK 0xF0
#define PCH_START 0x0020
#define PCH_RESTART 0x0004
#define PCH_ESR_START 0x0001
@@ -66,7 +56,6 @@
#define PCH_ACK 0x0008
#define PCH_GETACK 0x0001
#define CLR_REG 0x0
-#define I2C_RD 0x1
#define I2CMCF_BIT 0x0080
#define I2CMIF_BIT 0x0002
#define I2CMAL_BIT 0x0010
@@ -84,8 +73,6 @@
#define I2CMBB_BIT 0x0020
#define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \
I2CBMTO_BIT | I2CBMIS_BIT)
-#define I2C_ADDR_MSK 0xFF
-#define I2C_MSB_2B_MSK 0x300
#define FAST_MODE_CLK 400
#define FAST_MODE_EN 0x0001
#define SUB_ADDR_LEN_MAX 4
@@ -156,7 +143,7 @@ struct i2c_algo_pch_data {
/**
* struct adapter_info - This structure holds the adapter information for the
- PCH i2c controller
+ * PCH i2c controller
* @pch_data: stores a list of i2c_algo_pch_data
* @pch_i2c_suspended: specifies whether the system is suspended or not
* perhaps with more lines and words.
@@ -177,7 +164,6 @@ static wait_queue_head_t pch_event;
static DEFINE_MUTEX(pch_mutex);
/* Definition for ML7213 by LAPIS Semiconductor */
-#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
#define PCI_DEVICE_ID_ML7831_I2C 0x8817
@@ -189,6 +175,7 @@ static const struct pci_device_id pch_pcidev_id[] = {
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, },
{0,}
};
+MODULE_DEVICE_TABLE(pci, pch_pcidev_id);
static irqreturn_t pch_i2c_handler(int irq, void *pData);
@@ -366,6 +353,7 @@ static void pch_i2c_repstart(struct i2c_algo_pch_data *adap)
/**
* pch_i2c_writebytes() - write data to I2C bus in normal mode
* @i2c_adap: Pointer to the struct i2c_adapter.
+ * @msgs: Pointer to the i2c message structure.
* @last: specifies whether last message or not.
* In the case of compound mode it will be 1 for last message,
* otherwise 0.
@@ -378,16 +366,12 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
u8 *buf;
u32 length;
- u32 addr;
- u32 addr_2_msb;
- u32 addr_8_lsb;
s32 wrcount;
s32 rtn;
void __iomem *p = adap->pch_base_address;
length = msgs->len;
buf = msgs->buf;
- addr = msgs->addr;
/* enable master tx */
pch_setbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE);
@@ -401,8 +385,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06;
- iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+ iowrite32(i2c_10bit_addr_hi_from_msg(msgs), p + PCH_I2CDR);
if (first)
pch_i2c_start(adap);
@@ -410,8 +393,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
if (rtn)
return rtn;
- addr_8_lsb = (addr & I2C_ADDR_MSK);
- iowrite32(addr_8_lsb, p + PCH_I2CDR);
+ iowrite32(i2c_10bit_addr_lo_from_msg(msgs), p + PCH_I2CDR);
} else {
/* set 7 bit slave address and R/W bit as 0 */
iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR);
@@ -497,15 +479,11 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u8 *buf;
u32 count;
u32 length;
- u32 addr;
- u32 addr_2_msb;
- u32 addr_8_lsb;
void __iomem *p = adap->pch_base_address;
s32 rtn;
length = msgs->len;
buf = msgs->buf;
- addr = msgs->addr;
/* enable master reception */
pch_clrbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE);
@@ -516,8 +494,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
}
if (msgs->flags & I2C_M_TEN) {
- addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
- iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+ iowrite32(i2c_10bit_addr_hi_from_msg(msgs) & ~I2C_M_RD, p + PCH_I2CDR);
if (first)
pch_i2c_start(adap);
@@ -525,8 +502,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
if (rtn)
return rtn;
- addr_8_lsb = (addr & I2C_ADDR_MSK);
- iowrite32(addr_8_lsb, p + PCH_I2CDR);
+ iowrite32(i2c_10bit_addr_lo_from_msg(msgs), p + PCH_I2CDR);
pch_i2c_restart(adap);
@@ -534,8 +510,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
if (rtn)
return rtn;
- addr_2_msb |= I2C_RD;
- iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+ iowrite32(i2c_10bit_addr_hi_from_msg(msgs), p + PCH_I2CDR);
} else {
/* 7 address bits + R/W bit */
iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR);
@@ -715,7 +690,7 @@ static u32 pch_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm pch_algorithm = {
- .master_xfer = pch_i2c_xfer,
+ .xfer = pch_i2c_xfer,
.functionality = pch_i2c_func
};
@@ -780,7 +755,7 @@ static int pch_i2c_probe(struct pci_dev *pdev,
pch_adap->owner = THIS_MODULE;
pch_adap->class = I2C_CLASS_HWMON;
- strlcpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name));
+ strscpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name));
pch_adap->algo = &pch_algorithm;
pch_adap->algo_data = &adap_info->pch_data[i];
@@ -854,11 +829,10 @@ static void pch_i2c_remove(struct pci_dev *pdev)
kfree(adap_info);
}
-#ifdef CONFIG_PM
-static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused pch_i2c_suspend(struct device *dev)
{
- int ret;
int i;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct adapter_info *adap_info = pci_get_drvdata(pdev);
void __iomem *p = adap_info->pch_data[0].pch_base_address;
@@ -880,34 +854,13 @@ static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
ioread32(p + PCH_I2CSR), ioread32(p + PCH_I2CBUFSTA),
ioread32(p + PCH_I2CESRSTA));
- ret = pci_save_state(pdev);
-
- if (ret) {
- pch_pci_err(pdev, "pci_save_state\n");
- return ret;
- }
-
- pci_enable_wake(pdev, PCI_D3hot, 0);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
return 0;
}
-static int pch_i2c_resume(struct pci_dev *pdev)
+static int __maybe_unused pch_i2c_resume(struct device *dev)
{
int i;
- struct adapter_info *adap_info = pci_get_drvdata(pdev);
-
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- if (pci_enable_device(pdev) < 0) {
- pch_pci_err(pdev, "pch_i2c_resume:pci_enable_device FAILED\n");
- return -EIO;
- }
-
- pci_enable_wake(pdev, PCI_D3hot, 0);
+ struct adapter_info *adap_info = dev_get_drvdata(dev);
for (i = 0; i < adap_info->ch_num; i++)
pch_i2c_init(&adap_info->pch_data[i]);
@@ -916,18 +869,15 @@ static int pch_i2c_resume(struct pci_dev *pdev)
return 0;
}
-#else
-#define pch_i2c_suspend NULL
-#define pch_i2c_resume NULL
-#endif
+
+static SIMPLE_DEV_PM_OPS(pch_i2c_pm_ops, pch_i2c_suspend, pch_i2c_resume);
static struct pci_driver pch_pcidriver = {
.name = KBUILD_MODNAME,
.id_table = pch_pcidev_id,
.probe = pch_i2c_probe,
.remove = pch_i2c_remove,
- .suspend = pch_i2c_suspend,
- .resume = pch_i2c_resume
+ .driver.pm = &pch_i2c_pm_ops,
};
module_pci_driver(pch_pcidriver);
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index 5416003e0605..cfe8665cacd2 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* ------------------------------------------------------------------------- */
/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-97 Simon G. Vogl
1998-99 Hans Berglund
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details. */
+ */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
@@ -56,7 +49,7 @@ static int mmapped;
static wait_queue_head_t pcf_wait;
static int pcf_pending;
-static spinlock_t lock;
+static DEFINE_SPINLOCK(lock);
static struct i2c_adapter pcf_isa_ops;
@@ -139,7 +132,6 @@ static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) {
static int pcf_isa_init(void)
{
- spin_lock_init(&lock);
if (!mmapped) {
if (!request_region(base, 2, pcf_isa_ops.name)) {
printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
@@ -196,7 +188,7 @@ static struct i2c_algo_pcf_data pcf_isa_data = {
static struct i2c_adapter pcf_isa_ops = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo_data = &pcf_isa_data,
.name = "i2c-elektor",
};
@@ -289,7 +281,7 @@ static int elektor_probe(struct device *dev, unsigned int id)
return -ENODEV;
}
-static int elektor_remove(struct device *dev, unsigned int id)
+static void elektor_remove(struct device *dev, unsigned int id)
{
i2c_del_adapter(&pcf_isa_ops);
@@ -305,8 +297,6 @@ static int elektor_remove(struct device *dev, unsigned int id)
iounmap(base_iomem);
release_mem_region(base, 2);
}
-
- return 0;
}
static struct isa_driver i2c_elektor_driver = {
diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c
index 35b302d983e0..ece019b3d066 100644
--- a/drivers/i2c/busses/i2c-emev2.c
+++ b/drivers/i2c/busses/i2c-emev2.c
@@ -16,7 +16,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
@@ -67,8 +67,8 @@ struct em_i2c_device {
void __iomem *base;
struct i2c_adapter adap;
struct completion msg_done;
- struct clk *sclk;
struct i2c_client *slave;
+ int irq;
};
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
@@ -339,42 +339,43 @@ static int em_i2c_unreg_slave(struct i2c_client *slave)
writeb(0, priv->base + I2C_OFS_SVA0);
+ /*
+ * Wait for interrupt to finish. New slave irqs cannot happen because we
+ * cleared the slave address and, thus, only extension codes will be
+ * detected which do not use the slave ptr.
+ */
+ synchronize_irq(priv->irq);
priv->slave = NULL;
return 0;
}
static const struct i2c_algorithm em_i2c_algo = {
- .master_xfer = em_i2c_xfer,
+ .xfer = em_i2c_xfer,
.functionality = em_i2c_func,
- .reg_slave = em_i2c_reg_slave,
- .unreg_slave = em_i2c_unreg_slave,
+ .reg_slave = em_i2c_reg_slave,
+ .unreg_slave = em_i2c_unreg_slave,
};
static int em_i2c_probe(struct platform_device *pdev)
{
struct em_i2c_device *priv;
- struct resource *r;
- int irq, ret;
+ struct clk *sclk;
+ int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, r);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
- strlcpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name));
+ strscpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name));
- priv->sclk = devm_clk_get(&pdev->dev, "sclk");
- if (IS_ERR(priv->sclk))
- return PTR_ERR(priv->sclk);
-
- ret = clk_prepare_enable(priv->sclk);
- if (ret)
- return ret;
+ sclk = devm_clk_get_enabled(&pdev->dev, "sclk");
+ if (IS_ERR(sclk))
+ return PTR_ERR(sclk);
priv->adap.timeout = msecs_to_jiffies(100);
priv->adap.retries = 5;
@@ -390,34 +391,31 @@ static int em_i2c_probe(struct platform_device *pdev)
em_i2c_reset(&priv->adap);
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_irq(&pdev->dev, irq, em_i2c_irq_handler, 0,
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ priv->irq = ret;
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0,
"em_i2c", priv);
if (ret)
- goto err_clk;
+ return ret;
ret = i2c_add_adapter(&priv->adap);
-
if (ret)
- goto err_clk;
+ return ret;
- dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, irq);
+ dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr,
+ priv->irq);
return 0;
-
-err_clk:
- clk_disable_unprepare(priv->sclk);
- return ret;
}
-static int em_i2c_remove(struct platform_device *dev)
+static void em_i2c_remove(struct platform_device *dev)
{
struct em_i2c_device *priv = platform_get_drvdata(dev);
i2c_del_adapter(&priv->adap);
- clk_disable_unprepare(priv->sclk);
-
- return 0;
}
static const struct of_device_id em_i2c_ids[] = {
@@ -436,6 +434,7 @@ static struct platform_driver em_i2c_driver = {
module_platform_driver(em_i2c_driver);
MODULE_DESCRIPTION("EMEV2 I2C bus driver");
-MODULE_AUTHOR("Ian Molton and Wolfram Sang <wsa@sang-engineering.com>");
+MODULE_AUTHOR("Ian Molton");
+MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(of, em_i2c_ids);
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index c1ce2299a76e..9c1c5f3c09f6 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -1,11 +1,8 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* i2c-exynos5.c - Samsung Exynos5 I2C Controller Driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -21,9 +18,7 @@
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
#include <linux/spinlock.h>
/*
@@ -167,23 +162,17 @@
#define HSI2C_MASTER_ID(x) ((x & 0xff) << 24)
#define MASTER_ID(x) ((x & 0x7) + 0x08)
-/*
- * Controller operating frequency, timing values for operation
- * are calculated against this frequency
- */
-#define HSI2C_HS_TX_CLOCK 1000000
-#define HSI2C_FS_TX_CLOCK 100000
-
#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100))
enum i2c_type_exynos {
I2C_TYPE_EXYNOS5,
I2C_TYPE_EXYNOS7,
+ I2C_TYPE_EXYNOSAUTOV9,
+ I2C_TYPE_EXYNOS8895,
};
struct exynos5_i2c {
struct i2c_adapter adap;
- unsigned int suspended:1;
struct i2c_msg *msg;
struct completion msg_complete;
@@ -192,7 +181,8 @@ struct exynos5_i2c {
unsigned int irq;
void __iomem *regs;
- struct clk *clk;
+ struct clk *clk; /* operating clock */
+ struct clk *pclk; /* bus clock */
struct device *dev;
int state;
@@ -205,6 +195,11 @@ struct exynos5_i2c {
*/
int trans_done;
+ /*
+ * Called from atomic context, don't use interrupts.
+ */
+ unsigned int atomic;
+
/* Controller operating frequency */
unsigned int op_clock;
@@ -241,6 +236,16 @@ static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
.hw = I2C_TYPE_EXYNOS7,
};
+static const struct exynos_hsi2c_variant exynosautov9_hsi2c_data = {
+ .fifo_depth = 64,
+ .hw = I2C_TYPE_EXYNOSAUTOV9,
+};
+
+static const struct exynos_hsi2c_variant exynos8895_hsi2c_data = {
+ .fifo_depth = 64,
+ .hw = I2C_TYPE_EXYNOS8895,
+};
+
static const struct of_device_id exynos5_i2c_match[] = {
{
.compatible = "samsung,exynos5-hsi2c",
@@ -254,6 +259,12 @@ static const struct of_device_id exynos5_i2c_match[] = {
}, {
.compatible = "samsung,exynos7-hsi2c",
.data = &exynos7_hsi2c_data
+ }, {
+ .compatible = "samsung,exynosautov9-hsi2c",
+ .data = &exynosautov9_hsi2c_data
+ }, {
+ .compatible = "samsung,exynos8895-hsi2c",
+ .data = &exynos8895_hsi2c_data
}, {},
};
MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
@@ -268,6 +279,9 @@ static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c)
* exynos5_i2c_set_timing: updates the registers with appropriate
* timing values calculated
*
+ * Timing values for operation are calculated against 100kHz, 400kHz
+ * or 1MHz controller operating frequency.
+ *
* Returns 0 on success, -EINVAL if the cycle length cannot
* be calculated.
*/
@@ -285,11 +299,36 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
unsigned int t_ftl_cycle;
unsigned int clkin = clk_get_rate(i2c->clk);
unsigned int op_clk = hs_timings ? i2c->op_clock :
- (i2c->op_clock >= HSI2C_HS_TX_CLOCK) ? HSI2C_FS_TX_CLOCK :
+ (i2c->op_clock >= I2C_MAX_FAST_MODE_PLUS_FREQ) ? I2C_MAX_STANDARD_MODE_FREQ :
i2c->op_clock;
int div, clk_cycle, temp;
/*
+ * In case of HSI2C controllers in ExynosAutoV9:
+ *
+ * FSCL = IPCLK / ((CLK_DIV + 1) * 16)
+ * T_SCL_LOW = IPCLK * (CLK_DIV + 1) * (N + M)
+ * [N : number of 0's in the TSCL_H_HS]
+ * [M : number of 0's in the TSCL_L_HS]
+ * T_SCL_HIGH = IPCLK * (CLK_DIV + 1) * (N + M)
+ * [N : number of 1's in the TSCL_H_HS]
+ * [M : number of 1's in the TSCL_L_HS]
+ *
+ * Result of (N + M) is always 8.
+ * In general case, we don't need to control timing_s1 and timing_s2.
+ */
+ if (i2c->variant->hw == I2C_TYPE_EXYNOSAUTOV9) {
+ div = ((clkin / (16 * i2c->op_clock)) - 1);
+ i2c_timing_s3 = div << 16;
+ if (hs_timings)
+ writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
+ else
+ writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
+
+ return 0;
+ }
+
+ /*
* In case of HSI2C controller in Exynos5 series
* FPCLK / FI2C =
* (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
@@ -301,23 +340,67 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
* clk_cycle := TSCLK_L + TSCLK_H
* temp := (CLK_DIV + 1) * (clk_cycle + 2)
*
+ * In case of HSI2C controllers in Exynos8895
+ * FPCLK / FI2C =
+ * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) +
+ * 2 * ((FLT_CYCLE + 3) - (FLT_CYCLE + 3) % (CLK_DIV + 1))
+ *
+ * clk_cycle := TSCLK_L + TSCLK_H
+ * temp := (FPCLK / FI2C) - (FLT_CYCLE + 3) * 2
+ *
* Constraints: 4 <= temp, 0 <= CLK_DIV < 256, 2 <= clk_cycle <= 510
*
+ * To split SCL clock into low, high periods appropriately, one
+ * proportion factor for each I2C mode is used, which is calculated
+ * using this formula.
+ * ```
+ * ((t_low_min + (scl_clock - t_low_min - t_high_min) / 2) / scl_clock)
+ * ```
+ * where:
+ * t_low_min is the minimal value of low period of the SCL clock in us;
+ * t_high_min is the minimal value of high period of the SCL clock in us;
+ * scl_clock is converted from SCL clock frequency into us.
+ *
+ * Below are the proportion factors for these I2C modes:
+ * t_low_min, t_high_min, scl_clock, proportion
+ * Standard Mode: 4.7us, 4.0us, 10us, 0.535
+ * Fast Mode: 1.3us, 0.6us, 2.5us, 0.64
+ * Fast-Plus Mode: 0.5us, 0.26us, 1us, 0.62
+ *
*/
t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
- temp = clkin / op_clk - 8 - t_ftl_cycle;
- if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
- temp -= t_ftl_cycle;
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS8895)
+ temp = clkin / op_clk - (t_ftl_cycle + 3) * 2;
+ else if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
+ temp = clkin / op_clk - 8 - t_ftl_cycle;
+ else
+ temp = clkin / op_clk - 8 - (t_ftl_cycle * 2);
div = temp / 512;
- clk_cycle = temp / (div + 1) - 2;
+
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS8895)
+ clk_cycle = (temp + ((t_ftl_cycle + 3) % (div + 1)) * 2) /
+ (div + 1) - 2;
+ else
+ clk_cycle = temp / (div + 1) - 2;
if (temp < 4 || div >= 256 || clk_cycle < 2) {
dev_err(i2c->dev, "%s clock set-up failed\n",
hs_timings ? "HS" : "FS");
return -EINVAL;
}
- t_scl_l = clk_cycle / 2;
- t_scl_h = clk_cycle / 2;
+ /*
+ * Scale clk_cycle to get t_scl_l using the proption factors for individual I2C modes.
+ */
+ if (op_clk <= I2C_MAX_STANDARD_MODE_FREQ)
+ t_scl_l = clk_cycle * 535 / 1000;
+ else if (op_clk <= I2C_MAX_FAST_MODE_FREQ)
+ t_scl_l = clk_cycle * 64 / 100;
+ else
+ t_scl_l = clk_cycle * 62 / 100;
+
+ if (t_scl_l > 0xFF)
+ t_scl_l = 0xFF;
+ t_scl_h = clk_cycle - t_scl_l;
t_start_su = t_scl_l;
t_start_hd = t_scl_l;
t_stop_su = t_scl_l;
@@ -357,7 +440,7 @@ static int exynos5_hsi2c_clock_setup(struct exynos5_i2c *i2c)
/* always set Fast Speed timings */
int ret = exynos5_i2c_set_timing(i2c, false);
- if (ret < 0 || i2c->op_clock < HSI2C_HS_TX_CLOCK)
+ if (ret < 0 || i2c->op_clock < I2C_MAX_FAST_MODE_PLUS_FREQ)
return ret;
return exynos5_i2c_set_timing(i2c, true);
@@ -380,7 +463,7 @@ static void exynos5_i2c_init(struct exynos5_i2c *i2c)
i2c->regs + HSI2C_CTL);
writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL);
- if (i2c->op_clock >= HSI2C_HS_TX_CLOCK) {
+ if (i2c->op_clock >= I2C_MAX_FAST_MODE_PLUS_FREQ) {
writel(HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr)),
i2c->regs + HSI2C_ADDR);
i2c_conf |= HSI2C_HS_MODE;
@@ -430,7 +513,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
/* handle interrupt related to the transfer status */
- if (i2c->variant->hw == I2C_TYPE_EXYNOS7) {
+ switch (i2c->variant->hw) {
+ case I2C_TYPE_EXYNOSAUTOV9:
+ fallthrough;
+ case I2C_TYPE_EXYNOS8895:
+ fallthrough;
+ case I2C_TYPE_EXYNOS7:
if (int_status & HSI2C_INT_TRANS_DONE) {
i2c->trans_done = 1;
i2c->state = 0;
@@ -451,7 +539,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
i2c->state = -ETIMEDOUT;
goto stop;
}
- } else if (int_status & HSI2C_INT_I2C) {
+
+ break;
+ case I2C_TYPE_EXYNOS5:
+ if (!(int_status & HSI2C_INT_I2C))
+ break;
+
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
if (trans_status & HSI2C_NO_DEV_ACK) {
dev_dbg(i2c->dev, "No ACK from device\n");
@@ -473,6 +566,8 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
i2c->trans_done = 1;
i2c->state = 0;
}
+
+ break;
}
if ((i2c->msg->flags & I2C_M_RD) && (int_status &
@@ -577,13 +672,13 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
{
unsigned long timeout;
- if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
return;
/*
- * HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction
- * indicates that bus is stuck (SDA is low). In such case bus recovery
- * can be performed.
+ * HSI2C_MASTER_ST_LOSE state (in Exynos7 and ExynosAutoV9 variants)
+ * before transaction indicates that bus is stuck (SDA is low).
+ * In such case bus recovery can be performed.
*/
timeout = jiffies + msecs_to_jiffies(100);
for (;;) {
@@ -614,14 +709,15 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
u32 i2c_ctl;
u32 int_en = 0;
u32 i2c_auto_conf = 0;
+ u32 i2c_addr = 0;
u32 fifo_ctl;
unsigned long flags;
unsigned short trig_lvl;
- if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
- int_en |= HSI2C_INT_I2C_TRANS;
- else
+ if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
int_en |= HSI2C_INT_I2C;
+ else
+ int_en |= HSI2C_INT_I2C_TRANS;
i2c_ctl = readl(i2c->regs + HSI2C_CTL);
i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
@@ -648,7 +744,12 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
}
- writel(HSI2C_SLV_ADDR_MAS(i2c->msg->addr), i2c->regs + HSI2C_ADDR);
+ i2c_addr = HSI2C_SLV_ADDR_MAS(i2c->msg->addr);
+
+ if (i2c->op_clock >= I2C_MAX_FAST_MODE_PLUS_FREQ)
+ i2c_addr |= HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr));
+
+ writel(i2c_addr, i2c->regs + HSI2C_ADDR);
writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
writel(i2c_ctl, i2c->regs + HSI2C_CTL);
@@ -670,10 +771,26 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
spin_unlock_irqrestore(&i2c->lock, flags);
}
+static bool exynos5_i2c_poll_irqs_timeout(struct exynos5_i2c *i2c,
+ unsigned long timeout)
+{
+ unsigned long time_left = jiffies + timeout;
+
+ while (time_before(jiffies, time_left) &&
+ !((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
+ (i2c->state < 0))) {
+ while (readl(i2c->regs + HSI2C_INT_ENABLE) &
+ readl(i2c->regs + HSI2C_INT_STATUS))
+ exynos5_i2c_irq(i2c->irq, i2c);
+ usleep_range(100, 200);
+ }
+ return time_before(jiffies, time_left);
+}
+
static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
struct i2c_msg *msgs, int stop)
{
- unsigned long timeout;
+ unsigned long time_left;
int ret;
i2c->msg = msgs;
@@ -684,15 +801,20 @@ static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
exynos5_i2c_message_start(i2c, stop);
- timeout = wait_for_completion_timeout(&i2c->msg_complete,
- EXYNOS5_I2C_TIMEOUT);
- if (timeout == 0)
+ if (!i2c->atomic)
+ time_left = wait_for_completion_timeout(&i2c->msg_complete,
+ EXYNOS5_I2C_TIMEOUT);
+ else
+ time_left = exynos5_i2c_poll_irqs_timeout(i2c,
+ EXYNOS5_I2C_TIMEOUT);
+
+ if (time_left == 0)
ret = -ETIMEDOUT;
else
ret = i2c->state;
/*
- * If this is the last message to be transfered (stop == 1)
+ * If this is the last message to be transferred (stop == 1)
* Then check if the bus can be brought back to idle.
*/
if (ret == 0 && stop)
@@ -715,14 +837,13 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
struct exynos5_i2c *i2c = adap->algo_data;
int i, ret;
- if (i2c->suspended) {
- dev_err(i2c->dev, "HS-I2C is not initialized.\n");
- return -EIO;
- }
+ ret = clk_enable(i2c->pclk);
+ if (ret)
+ return ret;
ret = clk_enable(i2c->clk);
if (ret)
- return ret;
+ goto err_pclk;
for (i = 0; i < num; ++i) {
ret = exynos5_i2c_xfer_msg(i2c, msgs + i, i + 1 == num);
@@ -731,25 +852,42 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
}
clk_disable(i2c->clk);
+err_pclk:
+ clk_disable(i2c->pclk);
return ret ?: num;
}
+static int exynos5_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct exynos5_i2c *i2c = adap->algo_data;
+ int ret;
+
+ disable_irq(i2c->irq);
+ i2c->atomic = true;
+ ret = exynos5_i2c_xfer(adap, msgs, num);
+ i2c->atomic = false;
+ enable_irq(i2c->irq);
+
+ return ret;
+}
+
static u32 exynos5_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm exynos5_i2c_algorithm = {
- .master_xfer = exynos5_i2c_xfer,
- .functionality = exynos5_i2c_func,
+ .xfer = exynos5_i2c_xfer,
+ .xfer_atomic = exynos5_i2c_xfer_atomic,
+ .functionality = exynos5_i2c_func,
};
static int exynos5_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct exynos5_i2c *i2c;
- struct resource *mem;
int ret;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
@@ -757,9 +895,9 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock))
- i2c->op_clock = HSI2C_FS_TX_CLOCK;
+ i2c->op_clock = I2C_MAX_STANDARD_MODE_FREQ;
- strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &exynos5_i2c_algorithm;
i2c->adap.retries = 3;
@@ -771,12 +909,21 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
return -ENOENT;
}
- ret = clk_prepare_enable(i2c->clk);
+ i2c->pclk = devm_clk_get_optional(&pdev->dev, "hsi2c_pclk");
+ if (IS_ERR(i2c->pclk)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
+ "cannot get pclk");
+ }
+
+ ret = clk_prepare_enable(i2c->pclk);
if (ret)
return ret;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ goto err_pclk;
+
+ i2c->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->regs)) {
ret = PTR_ERR(i2c->regs);
goto err_clk;
@@ -793,16 +940,11 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
init_completion(&i2c->msg_complete);
i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
- ret = -EINVAL;
+ if (ret < 0)
goto err_clk;
- }
ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
- IRQF_NO_SUSPEND | IRQF_ONESHOT,
- dev_name(&pdev->dev), i2c);
-
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", i2c->irq);
goto err_clk;
@@ -823,33 +965,35 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2c);
clk_disable(i2c->clk);
+ clk_disable(i2c->pclk);
return 0;
err_clk:
clk_disable_unprepare(i2c->clk);
+
+ err_pclk:
+ clk_disable_unprepare(i2c->pclk);
return ret;
}
-static int exynos5_i2c_remove(struct platform_device *pdev)
+static void exynos5_i2c_remove(struct platform_device *pdev)
{
struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
clk_unprepare(i2c->clk);
-
- return 0;
+ clk_unprepare(i2c->pclk);
}
-#ifdef CONFIG_PM_SLEEP
static int exynos5_i2c_suspend_noirq(struct device *dev)
{
struct exynos5_i2c *i2c = dev_get_drvdata(dev);
- i2c->suspended = 1;
-
+ i2c_mark_adapter_suspended(&i2c->adap);
clk_unprepare(i2c->clk);
+ clk_unprepare(i2c->pclk);
return 0;
}
@@ -859,27 +1003,35 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
struct exynos5_i2c *i2c = dev_get_drvdata(dev);
int ret = 0;
- ret = clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->pclk);
if (ret)
return ret;
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ goto err_pclk;
+
ret = exynos5_hsi2c_clock_setup(i2c);
- if (ret) {
- clk_disable_unprepare(i2c->clk);
- return ret;
- }
+ if (ret)
+ goto err_clk;
exynos5_i2c_init(i2c);
clk_disable(i2c->clk);
- i2c->suspended = 0;
+ clk_disable(i2c->pclk);
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
+
+err_clk:
+ clk_disable_unprepare(i2c->clk);
+err_pclk:
+ clk_disable_unprepare(i2c->pclk);
+ return ret;
}
-#endif
static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq,
- exynos5_i2c_resume_noirq)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq,
+ exynos5_i2c_resume_noirq)
};
static struct platform_driver exynos5_i2c_driver = {
@@ -887,7 +1039,7 @@ static struct platform_driver exynos5_i2c_driver = {
.remove = exynos5_i2c_remove,
.driver = {
.name = "exynos5-hsi2c",
- .pm = &exynos5_i2c_dev_pm_ops,
+ .pm = pm_sleep_ptr(&exynos5_i2c_dev_pm_ops),
.of_match_table = exynos5_i2c_match,
},
};
@@ -895,6 +1047,6 @@ static struct platform_driver exynos5_i2c_driver = {
module_platform_driver(exynos5_i2c_driver);
MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
-MODULE_AUTHOR("Naveen Krishna Chatradhi, <ch.naveen@samsung.com>");
-MODULE_AUTHOR("Taekgyun Ko, <taeggyun.ko@samsung.com>");
+MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
+MODULE_AUTHOR("Taekgyun Ko <taeggyun.ko@samsung.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 1e2be2219a60..ae016a9431da 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * FSI-attached I2C master algorithm
+ * FSI-attached I2C controller algorithm
*
* Copyright 2018 IBM Corporation
*
@@ -98,7 +98,7 @@
#define I2C_STAT_DAT_REQ BIT(25)
#define I2C_STAT_CMD_COMP BIT(24)
#define I2C_STAT_STOP_ERR BIT(23)
-#define I2C_STAT_MAX_PORT GENMASK(19, 16)
+#define I2C_STAT_MAX_PORT GENMASK(22, 16)
#define I2C_STAT_ANY_INT BIT(15)
#define I2C_STAT_SCL_IN BIT(11)
#define I2C_STAT_SDA_IN BIT(10)
@@ -145,7 +145,7 @@
/* choose timeout length from legacy driver; it's well tested */
#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100)
-struct fsi_i2c_master {
+struct fsi_i2c_ctrl {
struct fsi_device *fsi;
u8 fifo_size;
struct list_head ports;
@@ -155,7 +155,7 @@ struct fsi_i2c_master {
struct fsi_i2c_port {
struct list_head list;
struct i2c_adapter adapter;
- struct fsi_i2c_master *master;
+ struct fsi_i2c_ctrl *ctrl;
u16 port;
u16 xfrd;
};
@@ -183,7 +183,7 @@ static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
return fsi_device_write(fsi, reg, &data_be, sizeof(data_be));
}
-static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+static int fsi_i2c_dev_init(struct fsi_i2c_ctrl *i2c)
{
int rc;
u32 mode = I2C_MODE_ENHANCED, extended_status, watermark;
@@ -214,7 +214,7 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
static int fsi_i2c_set_port(struct fsi_i2c_port *port)
{
int rc;
- struct fsi_device *fsi = port->master->fsi;
+ struct fsi_device *fsi = port->ctrl->fsi;
u32 mode, dummy = 0;
rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
@@ -236,7 +236,7 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
bool stop)
{
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
port->xfrd = 0;
@@ -268,7 +268,7 @@ static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
{
int write;
int rc;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
int bytes_to_write = i2c->fifo_size - fifo_count;
int bytes_remaining = msg->len - port->xfrd;
@@ -294,7 +294,7 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
{
int read;
int rc;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
int bytes_to_read;
int xfr_remaining = msg->len - port->xfrd;
u32 dummy;
@@ -330,7 +330,7 @@ static int fsi_i2c_get_scl(struct i2c_adapter *adap)
{
u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
@@ -341,7 +341,7 @@ static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
{
u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
if (val)
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
@@ -353,7 +353,7 @@ static int fsi_i2c_get_sda(struct i2c_adapter *adap)
{
u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
@@ -364,7 +364,7 @@ static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
{
u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
if (val)
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
@@ -377,7 +377,7 @@ static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
int rc;
u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
if (rc)
@@ -392,7 +392,7 @@ static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
int rc;
u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
if (rc)
@@ -402,7 +402,7 @@ static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
}
-static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
+static int fsi_i2c_reset_bus(struct fsi_i2c_ctrl *i2c,
struct fsi_i2c_port *port)
{
int rc;
@@ -435,7 +435,7 @@ static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
return fsi_i2c_dev_init(i2c);
}
-static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
+static int fsi_i2c_reset_engine(struct fsi_i2c_ctrl *i2c, u16 port)
{
int rc;
u32 mode, dummy = 0;
@@ -478,7 +478,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
unsigned long start;
u32 cmd = I2C_CMD_WITH_STOP;
u32 stat;
- struct fsi_i2c_master *i2c = port->master;
+ struct fsi_i2c_ctrl *i2c = port->ctrl;
struct fsi_device *fsi = i2c->fsi;
rc = fsi_i2c_reset_engine(i2c, port->port);
@@ -505,7 +505,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
if (rc)
return rc;
- /* wait until we see command complete in the master */
+ /* wait until we see command complete in the controller */
start = jiffies;
do {
@@ -579,7 +579,7 @@ static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
unsigned long start = jiffies;
do {
- rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
+ rc = fsi_i2c_read_reg(port->ctrl->fsi, I2C_FSI_STAT,
&status);
if (rc)
return rc;
@@ -609,10 +609,10 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int i, rc;
unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *master = port->master;
+ struct fsi_i2c_ctrl *ctrl = port->ctrl;
struct i2c_msg *msg;
- mutex_lock(&master->lock);
+ mutex_lock(&ctrl->lock);
rc = fsi_i2c_set_port(port);
if (rc)
@@ -633,7 +633,7 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}
unlock:
- mutex_unlock(&master->lock);
+ mutex_unlock(&ctrl->lock);
return rc ? : num;
}
@@ -654,17 +654,33 @@ static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
};
static const struct i2c_algorithm fsi_i2c_algorithm = {
- .master_xfer = fsi_i2c_xfer,
+ .xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality,
};
+static struct device_node *fsi_i2c_find_port_of_node(struct device_node *fsi,
+ int port)
+{
+ struct device_node *np;
+ u32 port_no;
+ int rc;
+
+ for_each_child_of_node(fsi, np) {
+ rc = of_property_read_u32(np, "reg", &port_no);
+ if (!rc && port_no == port)
+ return np;
+ }
+
+ return NULL;
+}
+
static int fsi_i2c_probe(struct device *dev)
{
- struct fsi_i2c_master *i2c;
+ struct fsi_i2c_ctrl *i2c;
struct fsi_i2c_port *port;
struct device_node *np;
+ u32 port_no, ports, stat;
int rc;
- u32 port_no;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
@@ -678,17 +694,25 @@ static int fsi_i2c_probe(struct device *dev)
if (rc)
return rc;
- /* Add adapter for each i2c port of the master. */
- for_each_available_child_of_node(dev->of_node, np) {
- rc = of_property_read_u32(np, "reg", &port_no);
- if (rc || port_no > USHRT_MAX)
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ if (rc)
+ return rc;
+
+ ports = FIELD_GET(I2C_STAT_MAX_PORT, stat) + 1;
+ dev_dbg(dev, "I2C controller has %d ports\n", ports);
+
+ for (port_no = 0; port_no < ports; port_no++) {
+ np = fsi_i2c_find_port_of_node(dev->of_node, port_no);
+ if (!of_device_is_available(np))
continue;
port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (!port)
+ if (!port) {
+ of_node_put(np);
break;
+ }
- port->master = i2c;
+ port->ctrl = i2c;
port->port = port_no;
port->adapter.owner = THIS_MODULE;
@@ -718,7 +742,7 @@ static int fsi_i2c_probe(struct device *dev)
static int fsi_i2c_remove(struct device *dev)
{
- struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+ struct fsi_i2c_ctrl *i2c = dev_get_drvdata(dev);
struct fsi_i2c_port *port, *tmp;
list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
@@ -748,5 +772,5 @@ static struct fsi_driver fsi_i2c_driver = {
module_fsi_driver(fsi_i2c_driver);
MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
-MODULE_DESCRIPTION("FSI attached I2C master");
+MODULE_DESCRIPTION("FSI attached I2C controller");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index c008d209f0b8..f4355b17bfbf 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -1,23 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Bitbanging I2C bus driver using the GPIO API
*
* Copyright (C) 2007 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
-#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c-algo-bit.h>
-#include <linux/platform_data/i2c-gpio.h>
+#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/slab.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/platform_device.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/slab.h>
struct i2c_gpio_private_data {
struct gpio_desc *sda;
@@ -26,7 +25,9 @@ struct i2c_gpio_private_data {
struct i2c_algo_bit_data bit_data;
struct i2c_gpio_platform_data pdata;
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
- struct dentry *debug_dir;
+ /* these must be protected by bus lock */
+ struct completion scl_irq_completion;
+ u64 scl_irq_data;
#endif
};
@@ -70,7 +71,6 @@ static int i2c_gpio_getscl(void *data)
}
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
-static struct dentry *i2c_gpio_debug_dir;
#define setsda(bd, val) ((bd)->setsda((bd)->data, val))
#define setscl(bd, val) ((bd)->setscl((bd)->data, val))
@@ -162,60 +162,142 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr)
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
-static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
+static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
+ irqreturn_t handler(int, void*))
{
- struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev);
+ int ret, irq = gpiod_to_irq(priv->scl);
+
+ if (irq < 0)
+ return irq;
+
+ i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ ret = gpiod_direction_input(priv->scl);
+ if (ret)
+ goto unlock;
+
+ reinit_completion(&priv->scl_irq_completion);
+ ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING,
+ "i2c_gpio_fault_injector_scl_irq", priv);
+ if (ret)
+ goto output;
+
+ wait_for_completion_interruptible(&priv->scl_irq_completion);
+
+ free_irq(irq, priv);
+ output:
+ ret = gpiod_direction_output(priv->scl, 1) ?: ret;
+ unlock:
+ i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return ret;
+}
+
+static irqreturn_t lose_arbitration_irq(int irq, void *dev_id)
+{
+ struct i2c_gpio_private_data *priv = dev_id;
+
+ setsda(&priv->bit_data, 0);
+ udelay(priv->scl_irq_data);
+ setsda(&priv->bit_data, 1);
+
+ complete(&priv->scl_irq_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int fops_lose_arbitration_set(void *data, u64 duration)
+{
+ struct i2c_gpio_private_data *priv = data;
+
+ if (duration > 100 * 1000)
+ return -EINVAL;
+
+ priv->scl_irq_data = duration;
/*
- * If there will be a debugfs-dir per i2c adapter somewhen, put the
- * 'fault-injector' dir there. Until then, we have a global dir with
- * all adapters as subdirs.
+ * Interrupt on falling SCL. This ensures that the controller under test
+ * has really started the transfer. Interrupt on falling SDA did only
+ * exercise 'bus busy' detection on some HW but not 'arbitration lost'.
+ * Note that the interrupt latency may cause the first bits to be
+ * transmitted correctly.
*/
- if (!i2c_gpio_debug_dir) {
- i2c_gpio_debug_dir = debugfs_create_dir("i2c-fault-injector", NULL);
- if (!i2c_gpio_debug_dir)
- return;
- }
+ return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n");
- priv->debug_dir = debugfs_create_dir(pdev->name, i2c_gpio_debug_dir);
- if (!priv->debug_dir)
- return;
+static irqreturn_t inject_panic_irq(int irq, void *dev_id)
+{
+ struct i2c_gpio_private_data *priv = dev_id;
- debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
- debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
- debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
- priv, &fops_incomplete_addr_phase);
- debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
- priv, &fops_incomplete_write_byte);
+ udelay(priv->scl_irq_data);
+ panic("I2C fault injector induced panic");
+
+ return IRQ_HANDLED;
+}
+
+static int fops_inject_panic_set(void *data, u64 duration)
+{
+ struct i2c_gpio_private_data *priv = data;
+
+ if (duration > 100 * 1000)
+ return -EINVAL;
+
+ priv->scl_irq_data = duration;
+ /*
+ * Interrupt on falling SCL. This ensures that the controller under test
+ * has really started the transfer.
+ */
+ return i2c_gpio_fi_act_on_scl_irq(priv, inject_panic_irq);
}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_inject_panic, NULL, fops_inject_panic_set, "%llu\n");
-static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
+static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
{
struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev);
- debugfs_remove_recursive(priv->debug_dir);
+ init_completion(&priv->scl_irq_completion);
+
+ debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->adap.debugfs,
+ priv, &fops_incomplete_addr_phase);
+ debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->adap.debugfs,
+ priv, &fops_incomplete_write_byte);
+ if (priv->bit_data.getscl) {
+ debugfs_create_file_unsafe("inject_panic", 0200, priv->adap.debugfs,
+ priv, &fops_inject_panic);
+ debugfs_create_file_unsafe("lose_arbitration", 0200, priv->adap.debugfs,
+ priv, &fops_lose_arbitration);
+ }
+ debugfs_create_file_unsafe("scl", 0600, priv->adap.debugfs, priv, &fops_scl);
+ debugfs_create_file_unsafe("sda", 0600, priv->adap.debugfs, priv, &fops_sda);
}
#else
static inline void i2c_gpio_fault_injector_init(struct platform_device *pdev) {}
-static inline void i2c_gpio_fault_injector_exit(struct platform_device *pdev) {}
#endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/
-static void of_i2c_gpio_get_props(struct device_node *np,
- struct i2c_gpio_platform_data *pdata)
+/* Get i2c-gpio properties from DT or ACPI table */
+static void i2c_gpio_get_properties(struct device *dev,
+ struct i2c_gpio_platform_data *pdata)
{
u32 reg;
- of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
+ device_property_read_u32(dev, "i2c-gpio,delay-us", &pdata->udelay);
- if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", &reg))
+ if (!device_property_read_u32(dev, "i2c-gpio,timeout-ms", &reg))
pdata->timeout = msecs_to_jiffies(reg);
pdata->sda_is_open_drain =
- of_property_read_bool(np, "i2c-gpio,sda-open-drain");
+ device_property_read_bool(dev, "i2c-gpio,sda-open-drain");
pdata->scl_is_open_drain =
- of_property_read_bool(np, "i2c-gpio,scl-open-drain");
+ device_property_read_bool(dev, "i2c-gpio,scl-open-drain");
pdata->scl_is_output_only =
- of_property_read_bool(np, "i2c-gpio,scl-output-only");
+ device_property_read_bool(dev, "i2c-gpio,scl-output-only");
+ pdata->sda_is_output_only =
+ device_property_read_bool(dev, "i2c-gpio,sda-output-only");
+ pdata->sda_has_no_pullup =
+ device_property_read_bool(dev, "i2c-gpio,sda-has-no-pullup");
+ pdata->scl_has_no_pullup =
+ device_property_read_bool(dev, "i2c-gpio,scl-has-no-pullup");
}
static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
@@ -248,7 +330,7 @@ static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
if (ret == -ENOENT)
retdesc = ERR_PTR(-EPROBE_DEFER);
- if (ret != -EPROBE_DEFER)
+ if (PTR_ERR(retdesc) != -EPROBE_DEFER)
dev_err(dev, "error trying to get descriptor: %d\n", ret);
return retdesc;
@@ -261,7 +343,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
enum gpiod_flags gflags;
int ret;
@@ -273,8 +355,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
bit_data = &priv->bit_data;
pdata = &priv->pdata;
- if (np) {
- of_i2c_gpio_get_props(np, pdata);
+ if (fwnode) {
+ i2c_gpio_get_properties(dev, pdata);
} else {
/*
* If all platform data settings are zero it is OK
@@ -286,13 +368,13 @@ static int i2c_gpio_probe(struct platform_device *pdev)
/*
* First get the GPIO pins; if it fails, we'll defer the probe.
- * If the SDA line is marked from platform data or device tree as
- * "open drain" it means something outside of our control is making
- * this line being handled as open drain, and we should just handle
- * it as any other output. Else we enforce open drain as this is
- * required for an I2C bus.
+ * If the SCL/SDA lines are marked "open drain" by platform data or
+ * device tree then this means that something outside of our control is
+ * marking these lines to be handled as open drain, and we should just
+ * handle them as we handle any other output. Else we enforce open
+ * drain as this is required for an I2C bus.
*/
- if (pdata->sda_is_open_drain)
+ if (pdata->sda_is_open_drain || pdata->sda_has_no_pullup)
gflags = GPIOD_OUT_HIGH;
else
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
@@ -300,14 +382,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
if (IS_ERR(priv->sda))
return PTR_ERR(priv->sda);
- /*
- * If the SCL line is marked from platform data or device tree as
- * "open drain" it means something outside of our control is making
- * this line being handled as open drain, and we should just handle
- * it as any other output. Else we enforce open drain as this is
- * required for an I2C bus.
- */
- if (pdata->scl_is_open_drain)
+ if (pdata->scl_is_open_drain || pdata->scl_has_no_pullup)
gflags = GPIOD_OUT_HIGH;
else
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
@@ -317,13 +392,16 @@ static int i2c_gpio_probe(struct platform_device *pdev)
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
+ else
+ bit_data->can_do_atomic = true;
bit_data->setsda = i2c_gpio_setsda_val;
bit_data->setscl = i2c_gpio_setscl_val;
if (!pdata->scl_is_output_only)
bit_data->getscl = i2c_gpio_getscl;
- bit_data->getsda = i2c_gpio_getsda;
+ if (!pdata->sda_is_output_only)
+ bit_data->getsda = i2c_gpio_getsda;
if (pdata->udelay)
bit_data->udelay = pdata->udelay;
@@ -340,15 +418,15 @@ static int i2c_gpio_probe(struct platform_device *pdev)
bit_data->data = priv;
adap->owner = THIS_MODULE;
- if (np)
- strlcpy(adap->name, dev_name(dev), sizeof(adap->name));
+ if (fwnode)
+ strscpy(adap->name, dev_name(dev), sizeof(adap->name));
else
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
adap->algo_data = bit_data;
- adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->class = I2C_CLASS_HWMON;
adap->dev.parent = dev;
- adap->dev.of_node = np;
+ device_set_node(&adap->dev, fwnode);
adap->nr = pdev->id;
ret = i2c_bit_add_numbered_bus(adap);
@@ -372,34 +450,35 @@ static int i2c_gpio_probe(struct platform_device *pdev)
return 0;
}
-static int i2c_gpio_remove(struct platform_device *pdev)
+static void i2c_gpio_remove(struct platform_device *pdev)
{
struct i2c_gpio_private_data *priv;
struct i2c_adapter *adap;
- i2c_gpio_fault_injector_exit(pdev);
-
priv = platform_get_drvdata(pdev);
adap = &priv->adap;
i2c_del_adapter(adap);
-
- return 0;
}
-#if defined(CONFIG_OF)
static const struct of_device_id i2c_gpio_dt_ids[] = {
{ .compatible = "i2c-gpio", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
-#endif
+
+static const struct acpi_device_id i2c_gpio_acpi_match[] = {
+ { "LOON0005" }, /* LoongArch */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, i2c_gpio_acpi_match);
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio",
- .of_match_table = of_match_ptr(i2c_gpio_dt_ids),
+ .of_match_table = i2c_gpio_dt_ids,
+ .acpi_match_table = i2c_gpio_acpi_match,
},
.probe = i2c_gpio_probe,
.remove = i2c_gpio_remove,
@@ -425,5 +504,5 @@ module_exit(i2c_gpio_exit);
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c-gpio");
diff --git a/drivers/i2c/busses/i2c-gxp.c b/drivers/i2c/busses/i2c-gxp.c
new file mode 100644
index 000000000000..2d117e7e3cb6
--- /dev/null
+++ b/drivers/i2c/busses/i2c-gxp.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define GXP_MAX_I2C_ENGINE 10
+static const char * const gxp_i2c_name[] = {
+ "gxp-i2c0", "gxp-i2c1", "gxp-i2c2", "gxp-i2c3",
+ "gxp-i2c4", "gxp-i2c5", "gxp-i2c6", "gxp-i2c7",
+ "gxp-i2c8", "gxp-i2c9" };
+
+/* GXP I2C Global interrupt status/enable register*/
+#define GXP_I2CINTSTAT 0x00
+#define GXP_I2CINTEN 0x04
+
+/* GXP I2C registers */
+#define GXP_I2CSTAT 0x00
+#define MASK_STOP_EVENT 0x20
+#define MASK_ACK 0x08
+#define MASK_RW 0x04
+#define GXP_I2CEVTERR 0x01
+#define MASK_SLAVE_CMD_EVENT 0x01
+#define MASK_SLAVE_DATA_EVENT 0x02
+#define MASK_MASTER_EVENT 0x10
+#define GXP_I2CSNPDAT 0x02
+#define GXP_I2CMCMD 0x04
+#define GXP_I2CSCMD 0x06
+#define GXP_I2CSNPAA 0x09
+#define GXP_I2CADVFEAT 0x0A
+#define GXP_I2COWNADR 0x0B
+#define GXP_I2CFREQDIV 0x0C
+#define GXP_I2CFLTFAIR 0x0D
+#define GXP_I2CTMOEDG 0x0E
+#define GXP_I2CCYCTIM 0x0F
+
+/* I2CSCMD Bits */
+#define SNOOP_EVT_CLR 0x80
+#define SLAVE_EVT_CLR 0x40
+#define SNOOP_EVT_MASK 0x20
+#define SLAVE_EVT_MASK 0x10
+#define SLAVE_ACK_ENAB 0x08
+#define SLAVE_EVT_STALL 0x01
+
+/* I2CMCMD Bits */
+#define MASTER_EVT_CLR 0x80
+#define MASTER_ACK_ENAB 0x08
+#define RW_CMD 0x04
+#define STOP_CMD 0x02
+#define START_CMD 0x01
+
+/* I2CTMOEDG value */
+#define GXP_DATA_EDGE_RST_CTRL 0x0a /* 30ns */
+
+/* I2CFLTFAIR Bits */
+#define FILTER_CNT 0x30
+#define FAIRNESS_CNT 0x02
+
+enum {
+ GXP_I2C_IDLE = 0,
+ GXP_I2C_ADDR_PHASE,
+ GXP_I2C_RDATA_PHASE,
+ GXP_I2C_WDATA_PHASE,
+ GXP_I2C_ADDR_NACK,
+ GXP_I2C_DATA_NACK,
+ GXP_I2C_ERROR,
+ GXP_I2C_COMP
+};
+
+struct gxp_i2c_drvdata {
+ struct device *dev;
+ void __iomem *base;
+ struct i2c_timings t;
+ u32 engine;
+ int irq;
+ struct completion completion;
+ struct i2c_adapter adapter;
+ struct i2c_msg *curr_msg;
+ int msgs_remaining;
+ int msgs_num;
+ u8 *buf;
+ size_t buf_remaining;
+ unsigned char state;
+ struct i2c_client *slave;
+ unsigned char stopped;
+};
+
+static struct regmap *i2cg_map;
+
+static void gxp_i2c_start(struct gxp_i2c_drvdata *drvdata)
+{
+ u16 value;
+
+ drvdata->buf = drvdata->curr_msg->buf;
+ drvdata->buf_remaining = drvdata->curr_msg->len;
+
+ /* Note: Address in struct i2c_msg is 7 bits */
+ value = drvdata->curr_msg->addr << 9;
+
+ /* Read or Write */
+ value |= drvdata->curr_msg->flags & I2C_M_RD ? RW_CMD | START_CMD : START_CMD;
+
+ drvdata->state = GXP_I2C_ADDR_PHASE;
+ writew(value, drvdata->base + GXP_I2CMCMD);
+}
+
+static int gxp_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ int ret;
+ struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(adapter);
+ unsigned long time_left;
+
+ drvdata->msgs_remaining = num;
+ drvdata->curr_msg = msgs;
+ drvdata->msgs_num = num;
+ reinit_completion(&drvdata->completion);
+
+ gxp_i2c_start(drvdata);
+
+ time_left = wait_for_completion_timeout(&drvdata->completion,
+ adapter->timeout);
+ ret = num - drvdata->msgs_remaining;
+ if (time_left == 0)
+ return -ETIMEDOUT;
+
+ if (drvdata->state == GXP_I2C_ADDR_NACK)
+ return -ENXIO;
+
+ if (drvdata->state == GXP_I2C_DATA_NACK)
+ return -EIO;
+
+ return ret;
+}
+
+static u32 gxp_i2c_func(struct i2c_adapter *adap)
+{
+ if (IS_ENABLED(CONFIG_I2C_SLAVE))
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
+
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int gxp_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter);
+
+ if (drvdata->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ drvdata->slave = slave;
+
+ writeb(slave->addr << 1, drvdata->base + GXP_I2COWNADR);
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | SLAVE_ACK_ENAB |
+ SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
+
+ return 0;
+}
+
+static int gxp_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter);
+
+ WARN_ON(!drvdata->slave);
+
+ writeb(0x00, drvdata->base + GXP_I2COWNADR);
+ writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD);
+
+ drvdata->slave = NULL;
+
+ return 0;
+}
+#endif
+
+static const struct i2c_algorithm gxp_i2c_algo = {
+ .xfer = gxp_i2c_master_xfer,
+ .functionality = gxp_i2c_func,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = gxp_i2c_reg_slave,
+ .unreg_slave = gxp_i2c_unreg_slave,
+#endif
+};
+
+static void gxp_i2c_stop(struct gxp_i2c_drvdata *drvdata)
+{
+ /* Clear event and send stop */
+ writeb(MASTER_EVT_CLR | STOP_CMD, drvdata->base + GXP_I2CMCMD);
+
+ complete(&drvdata->completion);
+}
+
+static void gxp_i2c_restart(struct gxp_i2c_drvdata *drvdata)
+{
+ u16 value;
+
+ drvdata->buf = drvdata->curr_msg->buf;
+ drvdata->buf_remaining = drvdata->curr_msg->len;
+
+ value = drvdata->curr_msg->addr << 9;
+
+ if (drvdata->curr_msg->flags & I2C_M_RD) {
+ /* Read and clear master event */
+ value |= MASTER_EVT_CLR | RW_CMD | START_CMD;
+ } else {
+ /* Write and clear master event */
+ value |= MASTER_EVT_CLR | START_CMD;
+ }
+
+ drvdata->state = GXP_I2C_ADDR_PHASE;
+
+ writew(value, drvdata->base + GXP_I2CMCMD);
+}
+
+static void gxp_i2c_chk_addr_ack(struct gxp_i2c_drvdata *drvdata)
+{
+ u16 value;
+
+ value = readb(drvdata->base + GXP_I2CSTAT);
+ if (!(value & MASK_ACK)) {
+ /* Got no ack, stop */
+ drvdata->state = GXP_I2C_ADDR_NACK;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+
+ if (drvdata->curr_msg->flags & I2C_M_RD) {
+ /* Start to read data from slave */
+ if (drvdata->buf_remaining == 0) {
+ /* No more data to read, stop */
+ drvdata->msgs_remaining--;
+ drvdata->state = GXP_I2C_COMP;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+ drvdata->state = GXP_I2C_RDATA_PHASE;
+
+ if (drvdata->buf_remaining == 1) {
+ /* The last data, do not ack */
+ writeb(MASTER_EVT_CLR | RW_CMD,
+ drvdata->base + GXP_I2CMCMD);
+ } else {
+ /* Read data and ack it */
+ writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB |
+ RW_CMD, drvdata->base + GXP_I2CMCMD);
+ }
+ } else {
+ /* Start to write first data to slave */
+ if (drvdata->buf_remaining == 0) {
+ /* No more data to write, stop */
+ drvdata->msgs_remaining--;
+ drvdata->state = GXP_I2C_COMP;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+ value = *drvdata->buf;
+ value = value << 8;
+ /* Clear master event */
+ value |= MASTER_EVT_CLR;
+ drvdata->buf++;
+ drvdata->buf_remaining--;
+ drvdata->state = GXP_I2C_WDATA_PHASE;
+ writew(value, drvdata->base + GXP_I2CMCMD);
+ }
+}
+
+static void gxp_i2c_ack_data(struct gxp_i2c_drvdata *drvdata)
+{
+ u8 value;
+
+ /* Store the data returned */
+ value = readb(drvdata->base + GXP_I2CSNPDAT);
+ *drvdata->buf = value;
+ drvdata->buf++;
+ drvdata->buf_remaining--;
+
+ if (drvdata->buf_remaining == 0) {
+ /* No more data, this message is completed. */
+ drvdata->msgs_remaining--;
+
+ if (drvdata->msgs_remaining == 0) {
+ /* No more messages, stop */
+ drvdata->state = GXP_I2C_COMP;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+ /* Move to next message and start transfer */
+ drvdata->curr_msg++;
+ gxp_i2c_restart(drvdata);
+ return;
+ }
+
+ /* Ack the slave to make it send next byte */
+ drvdata->state = GXP_I2C_RDATA_PHASE;
+ if (drvdata->buf_remaining == 1) {
+ /* The last data, do not ack */
+ writeb(MASTER_EVT_CLR | RW_CMD,
+ drvdata->base + GXP_I2CMCMD);
+ } else {
+ /* Read data and ack it */
+ writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB |
+ RW_CMD, drvdata->base + GXP_I2CMCMD);
+ }
+}
+
+static void gxp_i2c_chk_data_ack(struct gxp_i2c_drvdata *drvdata)
+{
+ u16 value;
+
+ value = readb(drvdata->base + GXP_I2CSTAT);
+ if (!(value & MASK_ACK)) {
+ /* Received No ack, stop */
+ drvdata->state = GXP_I2C_DATA_NACK;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+
+ /* Got ack, check if there is more data to write */
+ if (drvdata->buf_remaining == 0) {
+ /* No more data, this message is completed */
+ drvdata->msgs_remaining--;
+
+ if (drvdata->msgs_remaining == 0) {
+ /* No more messages, stop */
+ drvdata->state = GXP_I2C_COMP;
+ gxp_i2c_stop(drvdata);
+ return;
+ }
+ /* Move to next message and start transfer */
+ drvdata->curr_msg++;
+ gxp_i2c_restart(drvdata);
+ return;
+ }
+
+ /* Write data to slave */
+ value = *drvdata->buf;
+ value = value << 8;
+
+ /* Clear master event */
+ value |= MASTER_EVT_CLR;
+ drvdata->buf++;
+ drvdata->buf_remaining--;
+ drvdata->state = GXP_I2C_WDATA_PHASE;
+ writew(value, drvdata->base + GXP_I2CMCMD);
+}
+
+static bool gxp_i2c_slave_irq_handler(struct gxp_i2c_drvdata *drvdata)
+{
+ u8 value;
+ u8 buf;
+ int ret;
+
+ value = readb(drvdata->base + GXP_I2CEVTERR);
+
+ /* Received start or stop event */
+ if (value & MASK_SLAVE_CMD_EVENT) {
+ value = readb(drvdata->base + GXP_I2CSTAT);
+ /* Master sent stop */
+ if (value & MASK_STOP_EVENT) {
+ if (drvdata->stopped == 0)
+ i2c_slave_event(drvdata->slave, I2C_SLAVE_STOP, &buf);
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_ACK_ENAB | SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
+ drvdata->stopped = 1;
+ } else {
+ /* Master sent start and wants to read */
+ drvdata->stopped = 0;
+ if (value & MASK_RW) {
+ i2c_slave_event(drvdata->slave,
+ I2C_SLAVE_READ_REQUESTED, &buf);
+ value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_STALL);
+ writew(value, drvdata->base + GXP_I2CSCMD);
+ } else {
+ /* Master wants to write to us */
+ ret = i2c_slave_event(drvdata->slave,
+ I2C_SLAVE_WRITE_REQUESTED, &buf);
+ if (!ret) {
+ /* Ack next byte from master */
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
+ drvdata->base + GXP_I2CSCMD);
+ } else {
+ /* Nack next byte from master */
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
+ }
+ }
+ }
+ } else if (value & MASK_SLAVE_DATA_EVENT) {
+ value = readb(drvdata->base + GXP_I2CSTAT);
+ /* Master wants to read */
+ if (value & MASK_RW) {
+ /* Master wants another byte */
+ if (value & MASK_ACK) {
+ i2c_slave_event(drvdata->slave,
+ I2C_SLAVE_READ_PROCESSED, &buf);
+ value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_STALL);
+ writew(value, drvdata->base + GXP_I2CSCMD);
+ } else {
+ /* No more bytes needed */
+ writew(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
+ drvdata->base + GXP_I2CSCMD);
+ }
+ } else {
+ /* Master wants to write to us */
+ value = readb(drvdata->base + GXP_I2CSNPDAT);
+ buf = (uint8_t)value;
+ ret = i2c_slave_event(drvdata->slave,
+ I2C_SLAVE_WRITE_RECEIVED, &buf);
+ if (!ret) {
+ /* Ack next byte from master */
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
+ drvdata->base + GXP_I2CSCMD);
+ } else {
+ /* Nack next byte from master */
+ writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static irqreturn_t gxp_i2c_irq_handler(int irq, void *_drvdata)
+{
+ struct gxp_i2c_drvdata *drvdata = (struct gxp_i2c_drvdata *)_drvdata;
+ u32 value;
+
+ /* Check if the interrupt is for the current engine */
+ regmap_read(i2cg_map, GXP_I2CINTSTAT, &value);
+ if (!(value & BIT(drvdata->engine)))
+ return IRQ_NONE;
+
+ value = readb(drvdata->base + GXP_I2CEVTERR);
+
+ /* Error */
+ if (value & ~(MASK_MASTER_EVENT | MASK_SLAVE_CMD_EVENT |
+ MASK_SLAVE_DATA_EVENT)) {
+ /* Clear all events */
+ writeb(0x00, drvdata->base + GXP_I2CEVTERR);
+ drvdata->state = GXP_I2C_ERROR;
+ gxp_i2c_stop(drvdata);
+ return IRQ_HANDLED;
+ }
+
+ if (IS_ENABLED(CONFIG_I2C_SLAVE)) {
+ /* Slave mode */
+ if (value & (MASK_SLAVE_CMD_EVENT | MASK_SLAVE_DATA_EVENT)) {
+ if (gxp_i2c_slave_irq_handler(drvdata))
+ return IRQ_HANDLED;
+ return IRQ_NONE;
+ }
+ }
+
+ /* Master mode */
+ switch (drvdata->state) {
+ case GXP_I2C_ADDR_PHASE:
+ gxp_i2c_chk_addr_ack(drvdata);
+ break;
+
+ case GXP_I2C_RDATA_PHASE:
+ gxp_i2c_ack_data(drvdata);
+ break;
+
+ case GXP_I2C_WDATA_PHASE:
+ gxp_i2c_chk_data_ack(drvdata);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void gxp_i2c_init(struct gxp_i2c_drvdata *drvdata)
+{
+ drvdata->state = GXP_I2C_IDLE;
+ writeb(2000000 / drvdata->t.bus_freq_hz,
+ drvdata->base + GXP_I2CFREQDIV);
+ writeb(FILTER_CNT | FAIRNESS_CNT,
+ drvdata->base + GXP_I2CFLTFAIR);
+ writeb(GXP_DATA_EDGE_RST_CTRL, drvdata->base + GXP_I2CTMOEDG);
+ writeb(0x00, drvdata->base + GXP_I2CCYCTIM);
+ writeb(0x00, drvdata->base + GXP_I2CSNPAA);
+ writeb(0x00, drvdata->base + GXP_I2CADVFEAT);
+ writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK |
+ SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD);
+ writeb(MASTER_EVT_CLR, drvdata->base + GXP_I2CMCMD);
+ writeb(0x00, drvdata->base + GXP_I2CEVTERR);
+ writeb(0x00, drvdata->base + GXP_I2COWNADR);
+}
+
+static int gxp_i2c_probe(struct platform_device *pdev)
+{
+ struct gxp_i2c_drvdata *drvdata;
+ int rc;
+ struct i2c_adapter *adapter;
+
+ if (!i2cg_map) {
+ i2cg_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "hpe,sysreg");
+ if (IS_ERR(i2cg_map)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2cg_map),
+ "failed to map i2cg_handle\n");
+ }
+
+ /* Disable interrupt */
+ regmap_update_bits(i2cg_map, GXP_I2CINTEN, 0x00000FFF, 0);
+ }
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, drvdata);
+ drvdata->dev = &pdev->dev;
+ init_completion(&drvdata->completion);
+
+ drvdata->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(drvdata->base))
+ return PTR_ERR(drvdata->base);
+
+ /* Use physical memory address to determine which I2C engine this is. */
+ drvdata->engine = ((size_t)drvdata->base & 0xf00) >> 8;
+
+ if (drvdata->engine >= GXP_MAX_I2C_ENGINE) {
+ return dev_err_probe(&pdev->dev, -EINVAL, "i2c engine% is unsupported\n",
+ drvdata->engine);
+ }
+
+ rc = platform_get_irq(pdev, 0);
+ if (rc < 0)
+ return rc;
+
+ drvdata->irq = rc;
+ rc = devm_request_irq(&pdev->dev, drvdata->irq, gxp_i2c_irq_handler,
+ IRQF_SHARED, gxp_i2c_name[drvdata->engine], drvdata);
+ if (rc < 0)
+ return dev_err_probe(&pdev->dev, rc, "irq request failed\n");
+
+ i2c_parse_fw_timings(&pdev->dev, &drvdata->t, true);
+
+ gxp_i2c_init(drvdata);
+
+ /* Enable interrupt */
+ regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine),
+ BIT(drvdata->engine));
+
+ adapter = &drvdata->adapter;
+ i2c_set_adapdata(adapter, drvdata);
+
+ adapter->owner = THIS_MODULE;
+ strscpy(adapter->name, "HPE GXP I2C adapter", sizeof(adapter->name));
+ adapter->algo = &gxp_i2c_algo;
+ adapter->dev.parent = &pdev->dev;
+ adapter->dev.of_node = pdev->dev.of_node;
+
+ rc = i2c_add_adapter(adapter);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc, "i2c add adapter failed\n");
+
+ return 0;
+}
+
+static void gxp_i2c_remove(struct platform_device *pdev)
+{
+ struct gxp_i2c_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ /* Disable interrupt */
+ regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine), 0);
+ i2c_del_adapter(&drvdata->adapter);
+}
+
+static const struct of_device_id gxp_i2c_of_match[] = {
+ { .compatible = "hpe,gxp-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gxp_i2c_of_match);
+
+static struct platform_driver gxp_i2c_driver = {
+ .probe = gxp_i2c_probe,
+ .remove = gxp_i2c_remove,
+ .driver = {
+ .name = "gxp-i2c",
+ .of_match_table = gxp_i2c_of_match,
+ },
+};
+module_platform_driver(gxp_i2c_driver);
+
+MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
+MODULE_DESCRIPTION("HPE GXP I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c
index ff340d7ae2e5..78c5845e0877 100644
--- a/drivers/i2c/busses/i2c-highlander.c
+++ b/drivers/i2c/busses/i2c-highlander.c
@@ -322,7 +322,7 @@ static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
tmp |= (SMMR_MODE0 | SMMR_MODE1);
break;
default:
- dev_err(dev->dev, "unsupported xfer size %d\n", dev->buf_len);
+ dev_err(dev->dev, "unsupported xfer size %zu\n", dev->buf_len);
return -EINVAL;
}
@@ -331,7 +331,7 @@ static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
/* Ensure we're in a sane state */
highlander_i2c_done(dev);
- /* Set slave address */
+ /* Set target address */
iowrite16((addr << 1) | read_write, dev->base + SMSMADR);
highlander_i2c_command(dev, command, dev->buf_len);
@@ -369,7 +369,7 @@ static int highlander_i2c_probe(struct platform_device *pdev)
if (unlikely(!dev))
return -ENOMEM;
- dev->base = ioremap_nocache(res->start, resource_size(res));
+ dev->base = ioremap(res->start, resource_size(res));
if (unlikely(!dev->base)) {
ret = -ENXIO;
goto err;
@@ -379,7 +379,7 @@ static int highlander_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
dev->irq = platform_get_irq(pdev, 0);
- if (iic_force_poll)
+ if (dev->irq < 0 || iic_force_poll)
dev->irq = 0;
if (dev->irq) {
@@ -402,7 +402,7 @@ static int highlander_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON;
- strlcpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name));
+ strscpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name));
adap->algo = &highlander_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->nr = pdev->id;
@@ -435,7 +435,7 @@ err:
return ret;
}
-static int highlander_i2c_remove(struct platform_device *pdev)
+static void highlander_i2c_remove(struct platform_device *pdev)
{
struct highlander_i2c_dev *dev = platform_get_drvdata(pdev);
@@ -446,8 +446,6 @@ static int highlander_i2c_remove(struct platform_device *pdev)
iounmap(dev->base);
kfree(dev);
-
- return 0;
}
static struct platform_driver highlander_i2c_driver = {
diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c
new file mode 100644
index 000000000000..4b735ad9e193
--- /dev/null
+++ b/drivers/i2c/busses/i2c-hisi.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HiSilicon I2C Controller Driver for Kunpeng SoC
+ *
+ * Copyright (c) 2021 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/units.h>
+
+#define HISI_I2C_FRAME_CTRL 0x0000
+#define HISI_I2C_FRAME_CTRL_SPEED_MODE GENMASK(1, 0)
+#define HISI_I2C_FRAME_CTRL_ADDR_TEN BIT(2)
+#define HISI_I2C_SLV_ADDR 0x0004
+#define HISI_I2C_SLV_ADDR_VAL GENMASK(9, 0)
+#define HISI_I2C_SLV_ADDR_GC_S_MODE BIT(10)
+#define HISI_I2C_SLV_ADDR_GC_S_EN BIT(11)
+#define HISI_I2C_CMD_TXDATA 0x0008
+#define HISI_I2C_CMD_TXDATA_DATA GENMASK(7, 0)
+#define HISI_I2C_CMD_TXDATA_RW BIT(8)
+#define HISI_I2C_CMD_TXDATA_P_EN BIT(9)
+#define HISI_I2C_CMD_TXDATA_SR_EN BIT(10)
+#define HISI_I2C_RXDATA 0x000c
+#define HISI_I2C_RXDATA_DATA GENMASK(7, 0)
+#define HISI_I2C_SS_SCL_HCNT 0x0010
+#define HISI_I2C_SS_SCL_LCNT 0x0014
+#define HISI_I2C_FS_SCL_HCNT 0x0018
+#define HISI_I2C_FS_SCL_LCNT 0x001c
+#define HISI_I2C_HS_SCL_HCNT 0x0020
+#define HISI_I2C_HS_SCL_LCNT 0x0024
+#define HISI_I2C_FIFO_CTRL 0x0028
+#define HISI_I2C_FIFO_RX_CLR BIT(0)
+#define HISI_I2C_FIFO_TX_CLR BIT(1)
+#define HISI_I2C_FIFO_RX_AF_THRESH GENMASK(7, 2)
+#define HISI_I2C_FIFO_TX_AE_THRESH GENMASK(13, 8)
+#define HISI_I2C_FIFO_STATE 0x002c
+#define HISI_I2C_FIFO_STATE_RX_RERR BIT(0)
+#define HISI_I2C_FIFO_STATE_RX_WERR BIT(1)
+#define HISI_I2C_FIFO_STATE_RX_EMPTY BIT(3)
+#define HISI_I2C_FIFO_STATE_TX_RERR BIT(6)
+#define HISI_I2C_FIFO_STATE_TX_WERR BIT(7)
+#define HISI_I2C_FIFO_STATE_TX_FULL BIT(11)
+#define HISI_I2C_SDA_HOLD 0x0030
+#define HISI_I2C_SDA_HOLD_TX GENMASK(15, 0)
+#define HISI_I2C_SDA_HOLD_RX GENMASK(23, 16)
+#define HISI_I2C_FS_SPK_LEN 0x0038
+#define HISI_I2C_FS_SPK_LEN_CNT GENMASK(7, 0)
+#define HISI_I2C_HS_SPK_LEN 0x003c
+#define HISI_I2C_HS_SPK_LEN_CNT GENMASK(7, 0)
+#define HISI_I2C_TX_INT_CLR 0x0040
+#define HISI_I2C_TX_AEMPTY_INT BIT(0)
+#define HISI_I2C_INT_MSTAT 0x0044
+#define HISI_I2C_INT_CLR 0x0048
+#define HISI_I2C_INT_MASK 0x004C
+#define HISI_I2C_TRANS_STATE 0x0050
+#define HISI_I2C_TRANS_ERR 0x0054
+#define HISI_I2C_VERSION 0x0058
+
+#define HISI_I2C_INT_ALL GENMASK(4, 0)
+#define HISI_I2C_INT_TRANS_CPLT BIT(0)
+#define HISI_I2C_INT_TRANS_ERR BIT(1)
+#define HISI_I2C_INT_FIFO_ERR BIT(2)
+#define HISI_I2C_INT_RX_FULL BIT(3)
+#define HISI_I2C_INT_TX_EMPTY BIT(4)
+#define HISI_I2C_INT_ERR \
+ (HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR)
+
+#define HISI_I2C_STD_SPEED_MODE 0
+#define HISI_I2C_FAST_SPEED_MODE 1
+#define HISI_I2C_HIGH_SPEED_MODE 2
+
+#define HISI_I2C_TX_FIFO_DEPTH 64
+#define HISI_I2C_RX_FIFO_DEPTH 64
+#define HISI_I2C_TX_F_AE_THRESH 1
+#define HISI_I2C_RX_F_AF_THRESH 60
+
+#define NSEC_TO_CYCLES(ns, clk_rate_khz) \
+ DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC)
+
+struct hisi_i2c_controller {
+ struct i2c_adapter adapter;
+ void __iomem *iobase;
+ struct device *dev;
+ struct clk *clk;
+ int irq;
+
+ /* Intermediates for recording the transfer process */
+ struct completion *completion;
+ struct i2c_msg *msgs;
+ int msg_num;
+ int msg_tx_idx;
+ int buf_tx_idx;
+ int msg_rx_idx;
+ int buf_rx_idx;
+ u16 tar_addr;
+ u32 xfer_err;
+
+ /* I2C bus configuration */
+ struct i2c_timings t;
+ u32 clk_rate_khz;
+ u32 spk_len;
+};
+
+static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+ writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK);
+}
+
+static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+ writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK);
+}
+
+static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+ writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR);
+}
+
+static void hisi_i2c_clear_tx_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+ writel_relaxed(mask, ctlr->iobase + HISI_I2C_TX_INT_CLR);
+}
+
+static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr)
+{
+ u32 int_err = ctlr->xfer_err, reg;
+
+ if (int_err & HISI_I2C_INT_FIFO_ERR) {
+ reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+
+ if (reg & HISI_I2C_FIFO_STATE_RX_RERR)
+ dev_err(ctlr->dev, "rx fifo error read\n");
+
+ if (reg & HISI_I2C_FIFO_STATE_RX_WERR)
+ dev_err(ctlr->dev, "rx fifo error write\n");
+
+ if (reg & HISI_I2C_FIFO_STATE_TX_RERR)
+ dev_err(ctlr->dev, "tx fifo error read\n");
+
+ if (reg & HISI_I2C_FIFO_STATE_TX_WERR)
+ dev_err(ctlr->dev, "tx fifo error write\n");
+ }
+}
+
+static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr)
+{
+ struct i2c_msg *msg = ctlr->msgs;
+ u32 reg;
+
+ reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
+ reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN;
+ if (msg->flags & I2C_M_TEN)
+ reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN;
+ writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
+
+ reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR);
+ reg &= ~HISI_I2C_SLV_ADDR_VAL;
+ reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr);
+ writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR);
+
+ reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL);
+ reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR;
+ writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+ reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR);
+ writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+
+ hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
+ hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT);
+ hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL);
+
+ return 0;
+}
+
+static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr)
+{
+ ctlr->msg_num = 0;
+ ctlr->xfer_err = 0;
+ ctlr->msg_tx_idx = 0;
+ ctlr->msg_rx_idx = 0;
+ ctlr->buf_tx_idx = 0;
+ ctlr->buf_rx_idx = 0;
+}
+
+/*
+ * Initialize the transfer information and start the I2C bus transfer.
+ * We only configure the transfer and do some pre/post works here, and
+ * wait for the transfer done. The major transfer process is performed
+ * in the IRQ handler.
+ */
+static int hisi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap);
+ DECLARE_COMPLETION_ONSTACK(done);
+ int ret = num;
+
+ hisi_i2c_reset_xfer(ctlr);
+ ctlr->completion = &done;
+ ctlr->msg_num = num;
+ ctlr->msgs = msgs;
+
+ hisi_i2c_start_xfer(ctlr);
+
+ if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) {
+ hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+ synchronize_irq(ctlr->irq);
+ i2c_recover_bus(&ctlr->adapter);
+ dev_err(ctlr->dev, "bus transfer timeout\n");
+ ret = -EIO;
+ }
+
+ if (ctlr->xfer_err) {
+ hisi_i2c_handle_errors(ctlr);
+ ret = -EIO;
+ }
+
+ hisi_i2c_reset_xfer(ctlr);
+ ctlr->completion = NULL;
+
+ return ret;
+}
+
+static u32 hisi_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm hisi_i2c_algo = {
+ .xfer = hisi_i2c_xfer,
+ .functionality = hisi_i2c_functionality,
+};
+
+static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr)
+{
+ struct i2c_msg *cur_msg;
+ u32 fifo_state;
+
+ while (ctlr->msg_rx_idx < ctlr->msg_num) {
+ cur_msg = ctlr->msgs + ctlr->msg_rx_idx;
+
+ if (!(cur_msg->flags & I2C_M_RD)) {
+ ctlr->msg_rx_idx++;
+ continue;
+ }
+
+ fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+ while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) &&
+ ctlr->buf_rx_idx < cur_msg->len) {
+ cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA);
+ fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+ }
+
+ if (ctlr->buf_rx_idx == cur_msg->len) {
+ ctlr->buf_rx_idx = 0;
+ ctlr->msg_rx_idx++;
+ }
+
+ if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY)
+ break;
+ }
+
+ return 0;
+}
+
+static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr)
+{
+ int max_write = HISI_I2C_TX_FIFO_DEPTH - HISI_I2C_TX_F_AE_THRESH;
+ bool need_restart = false, last_msg;
+ struct i2c_msg *cur_msg;
+ u32 cmd, fifo_state;
+
+ while (ctlr->msg_tx_idx < ctlr->msg_num) {
+ cur_msg = ctlr->msgs + ctlr->msg_tx_idx;
+ last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1);
+
+ /* Signal the SR bit when we start transferring a new message */
+ if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx)
+ need_restart = true;
+
+ fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+ while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) &&
+ ctlr->buf_tx_idx < cur_msg->len && max_write) {
+ cmd = 0;
+
+ if (need_restart) {
+ cmd |= HISI_I2C_CMD_TXDATA_SR_EN;
+ need_restart = false;
+ }
+
+ /* Signal the STOP bit at the last frame of the last message */
+ if (ctlr->buf_tx_idx == cur_msg->len - 1 && last_msg)
+ cmd |= HISI_I2C_CMD_TXDATA_P_EN;
+
+ if (cur_msg->flags & I2C_M_RD)
+ cmd |= HISI_I2C_CMD_TXDATA_RW;
+ else
+ cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA,
+ cur_msg->buf[ctlr->buf_tx_idx]);
+
+ writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA);
+ ctlr->buf_tx_idx++;
+ max_write--;
+
+ fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+ }
+
+ /* Update the transfer index after per message transfer is done. */
+ if (ctlr->buf_tx_idx == cur_msg->len) {
+ ctlr->buf_tx_idx = 0;
+ ctlr->msg_tx_idx++;
+ }
+
+ if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) ||
+ max_write == 0)
+ break;
+ }
+
+ /*
+ * Disable the TX_EMPTY interrupt after finishing all the messages to
+ * avoid overwhelming the CPU.
+ */
+ if (ctlr->msg_tx_idx == ctlr->msg_num)
+ hisi_i2c_disable_int(ctlr, HISI_I2C_INT_TX_EMPTY);
+
+ hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT);
+}
+
+static irqreturn_t hisi_i2c_irq(int irq, void *context)
+{
+ struct hisi_i2c_controller *ctlr = context;
+ u32 int_stat;
+
+ /*
+ * Don't handle the interrupt if cltr->completion is NULL. We may
+ * reach here because the interrupt is spurious or the transfer is
+ * started by another port (e.g. firmware) rather than us.
+ */
+ if (!ctlr->completion)
+ return IRQ_NONE;
+
+ int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT);
+ hisi_i2c_clear_int(ctlr, int_stat);
+ if (!(int_stat & HISI_I2C_INT_ALL))
+ return IRQ_NONE;
+
+ if (int_stat & HISI_I2C_INT_TX_EMPTY)
+ hisi_i2c_xfer_msg(ctlr);
+
+ if (int_stat & HISI_I2C_INT_ERR) {
+ ctlr->xfer_err = int_stat;
+ goto out;
+ }
+
+ /* Drain the rx fifo before finish the transfer */
+ if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL))
+ hisi_i2c_read_rx_fifo(ctlr);
+
+out:
+ /*
+ * Only use TRANS_CPLT to indicate the completion. On error cases we'll
+ * get two interrupts, INT_ERR first then TRANS_CPLT.
+ */
+ if (int_stat & HISI_I2C_INT_TRANS_CPLT) {
+ hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+ hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
+ hisi_i2c_clear_tx_int(ctlr, HISI_I2C_TX_AEMPTY_INT);
+ complete(ctlr->completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Helper function for calculating and configuring the HIGH and LOW
+ * periods of SCL clock. The caller will pass the ratio of the
+ * counts (divide / divisor) according to the target speed mode,
+ * and the target registers.
+ */
+static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr,
+ u32 divide, u32 divisor,
+ u32 reg_hcnt, u32 reg_lcnt)
+{
+ u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt;
+ u32 scl_hcnt, scl_lcnt;
+
+ /* Total SCL clock cycles per speed period */
+ total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * HZ_PER_KHZ, ctlr->t.bus_freq_hz);
+ /* Total HIGH level SCL clock cycles including edges */
+ t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor);
+ /* Total LOW level SCL clock cycles including edges */
+ t_scl_lcnt = total_cnt - t_scl_hcnt;
+ /* Fall edge SCL clock cycles */
+ scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz);
+ /* Rise edge SCL clock cycles */
+ scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz);
+
+ /* Calculated HIGH and LOW periods of SCL clock */
+ scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt;
+ scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt;
+
+ writel(scl_hcnt, ctlr->iobase + reg_hcnt);
+ writel(scl_lcnt, ctlr->iobase + reg_lcnt);
+}
+
+static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr)
+{
+ u32 reg, sda_hold_cnt, speed_mode;
+
+ i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true);
+ ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz);
+
+ switch (ctlr->t.bus_freq_hz) {
+ case I2C_MAX_FAST_MODE_FREQ:
+ speed_mode = HISI_I2C_FAST_SPEED_MODE;
+ hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT);
+ break;
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ speed_mode = HISI_I2C_HIGH_SPEED_MODE;
+ hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT);
+ break;
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ default:
+ speed_mode = HISI_I2C_STD_SPEED_MODE;
+
+ /* For default condition force the bus speed to standard mode. */
+ ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT);
+ break;
+ }
+
+ reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
+ reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE;
+ reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode);
+ writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
+
+ sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz);
+
+ reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt);
+ writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD);
+
+ writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN);
+
+ reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH);
+ reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH);
+ writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+}
+
+static int hisi_i2c_probe(struct platform_device *pdev)
+{
+ struct hisi_i2c_controller *ctlr;
+ struct device *dev = &pdev->dev;
+ struct i2c_adapter *adapter;
+ u64 clk_rate_hz;
+ u32 hw_version;
+ int ret;
+
+ ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL);
+ if (!ctlr)
+ return -ENOMEM;
+
+ ctlr->iobase = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctlr->iobase))
+ return PTR_ERR(ctlr->iobase);
+
+ ctlr->irq = platform_get_irq(pdev, 0);
+ if (ctlr->irq < 0)
+ return ctlr->irq;
+
+ ctlr->dev = dev;
+
+ hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+
+ ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq handler\n");
+
+ ctlr->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ if (IS_ERR_OR_NULL(ctlr->clk)) {
+ ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get clock frequency\n");
+ } else {
+ clk_rate_hz = clk_get_rate(ctlr->clk);
+ }
+
+ ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(clk_rate_hz, HZ_PER_KHZ);
+
+ hisi_i2c_configure_bus(ctlr);
+
+ adapter = &ctlr->adapter;
+ snprintf(adapter->name, sizeof(adapter->name),
+ "HiSilicon I2C Controller %s", dev_name(dev));
+ adapter->owner = THIS_MODULE;
+ adapter->algo = &hisi_i2c_algo;
+ adapter->dev.parent = dev;
+ i2c_set_adapdata(adapter, ctlr);
+
+ ret = devm_i2c_add_adapter(dev, adapter);
+ if (ret)
+ return ret;
+
+ hw_version = readl(ctlr->iobase + HISI_I2C_VERSION);
+ dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n",
+ i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version);
+
+ return 0;
+}
+
+static const struct acpi_device_id hisi_i2c_acpi_ids[] = {
+ { "HISI03D1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids);
+
+static const struct of_device_id hisi_i2c_dts_ids[] = {
+ { .compatible = "hisilicon,ascend910-i2c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hisi_i2c_dts_ids);
+
+static struct platform_driver hisi_i2c_driver = {
+ .probe = hisi_i2c_probe,
+ .driver = {
+ .name = "hisi-i2c",
+ .acpi_match_table = hisi_i2c_acpi_ids,
+ .of_match_table = hisi_i2c_dts_ids,
+ },
+};
+module_platform_driver(hisi_i2c_driver);
+
+MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
+MODULE_DESCRIPTION("HiSilicon I2C Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c
index 061a4bfb03f4..95ab910b80c0 100644
--- a/drivers/i2c/busses/i2c-hix5hd2.c
+++ b/drivers/i2c/busses/i2c-hix5hd2.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2014 Linaro Ltd.
- * Copyright (c) 2014 Hisilicon Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Copyright (c) 2014 HiSilicon Limited.
*
* Now only support 7 bit address.
*/
@@ -72,8 +68,6 @@
#define I2C_ARBITRATE_INTR BIT(1)
#define I2C_OVER_INTR BIT(0)
-#define HIX5I2C_MAX_FREQ 400000 /* 400k */
-
enum hix5hd2_i2c_state {
HIX5I2C_STAT_RW_ERR = -1,
HIX5I2C_STAT_INIT,
@@ -206,7 +200,7 @@ static void hix5hd2_read_handle(struct hix5hd2_i2c_priv *priv)
/* the last byte don't need send ACK */
writel_relaxed(I2C_READ | I2C_NO_ACK, priv->regs + HIX5I2C_COM);
} else if (priv->msg_len > 1) {
- /* if i2c master receive data will send ACK */
+ /* if i2c controller receive data will send ACK */
writel_relaxed(I2C_READ, priv->regs + HIX5I2C_COM);
} else {
hix5hd2_rw_handle_stop(priv);
@@ -320,7 +314,7 @@ static void hix5hd2_i2c_message_start(struct hix5hd2_i2c_priv *priv, int stop)
static int hix5hd2_i2c_xfer_msg(struct hix5hd2_i2c_priv *priv,
struct i2c_msg *msgs, int stop)
{
- unsigned long timeout;
+ unsigned long time_left;
int ret;
priv->msg = msgs;
@@ -333,9 +327,9 @@ static int hix5hd2_i2c_xfer_msg(struct hix5hd2_i2c_priv *priv,
reinit_completion(&priv->msg_complete);
hix5hd2_i2c_message_start(priv, stop);
- timeout = wait_for_completion_timeout(&priv->msg_complete,
- priv->adap.timeout);
- if (timeout == 0) {
+ time_left = wait_for_completion_timeout(&priv->msg_complete,
+ priv->adap.timeout);
+ if (time_left == 0) {
priv->state = HIX5I2C_STAT_RW_ERR;
priv->err = -ETIMEDOUT;
dev_warn(priv->dev, "%s timeout=%d\n",
@@ -345,7 +339,7 @@ static int hix5hd2_i2c_xfer_msg(struct hix5hd2_i2c_priv *priv,
ret = priv->state;
/*
- * If this is the last message to be transfered (stop == 1)
+ * If this is the last message to be transferred (stop == 1)
* Then check if the bus can be brought back to idle.
*/
if (priv->state == HIX5I2C_STAT_RW_SUCCESS && stop)
@@ -366,7 +360,11 @@ static int hix5hd2_i2c_xfer(struct i2c_adapter *adap,
pm_runtime_get_sync(priv->dev);
for (i = 0; i < num; i++, msgs++) {
- stop = (i == num - 1);
+ if ((i == num - 1) || (msgs->flags & I2C_M_STOP))
+ stop = 1;
+ else
+ stop = 0;
+
ret = hix5hd2_i2c_xfer_msg(priv, msgs, stop);
if (ret < 0)
goto out;
@@ -375,7 +373,6 @@ static int hix5hd2_i2c_xfer(struct i2c_adapter *adap,
ret = num;
out:
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return ret;
}
@@ -386,15 +383,14 @@ static u32 hix5hd2_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm hix5hd2_i2c_algorithm = {
- .master_xfer = hix5hd2_i2c_xfer,
- .functionality = hix5hd2_i2c_func,
+ .xfer = hix5hd2_i2c_xfer,
+ .functionality = hix5hd2_i2c_func,
};
static int hix5hd2_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct hix5hd2_i2c_priv *priv;
- struct resource *mem;
unsigned int freq;
int irq, ret;
@@ -404,36 +400,32 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "clock-frequency", &freq)) {
/* use 100k as default value */
- priv->freq = 100000;
+ priv->freq = I2C_MAX_STANDARD_MODE_FREQ;
} else {
- if (freq > HIX5I2C_MAX_FREQ) {
- priv->freq = HIX5I2C_MAX_FREQ;
+ if (freq > I2C_MAX_FAST_MODE_FREQ) {
+ priv->freq = I2C_MAX_FAST_MODE_FREQ;
dev_warn(priv->dev, "use max freq %d instead\n",
- HIX5I2C_MAX_FREQ);
+ I2C_MAX_FAST_MODE_FREQ);
} else {
priv->freq = freq;
}
}
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = devm_ioremap_resource(&pdev->dev, mem);
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
+ if (irq < 0)
return irq;
- }
- priv->clk = devm_clk_get(&pdev->dev, NULL);
+ priv->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
+ dev_err(&pdev->dev, "cannot enable clock\n");
return PTR_ERR(priv->clk);
}
- clk_prepare_enable(priv->clk);
- strlcpy(priv->adap.name, "hix5hd2-i2c", sizeof(priv->adap.name));
+ strscpy(priv->adap.name, "hix5hd2-i2c", sizeof(priv->adap.name));
priv->dev = &pdev->dev;
priv->adap.owner = THIS_MODULE;
priv->adap.algo = &hix5hd2_i2c_algorithm;
@@ -449,11 +441,10 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev)
hix5hd2_i2c_init(priv);
ret = devm_request_irq(&pdev->dev, irq, hix5hd2_i2c_irq,
- IRQF_NO_SUSPEND | IRQF_ONESHOT,
- dev_name(&pdev->dev), priv);
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), priv);
if (ret != 0) {
dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", irq);
- goto err_clk;
+ return ret;
}
pm_runtime_set_autosuspend_delay(priv->dev, MSEC_PER_SEC);
@@ -470,23 +461,19 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev)
err_runtime:
pm_runtime_disable(priv->dev);
pm_runtime_set_suspended(priv->dev);
-err_clk:
- clk_disable_unprepare(priv->clk);
+
return ret;
}
-static int hix5hd2_i2c_remove(struct platform_device *pdev)
+static void hix5hd2_i2c_remove(struct platform_device *pdev)
{
struct hix5hd2_i2c_priv *priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
pm_runtime_disable(priv->dev);
pm_runtime_set_suspended(priv->dev);
-
- return 0;
}
-#ifdef CONFIG_PM
static int hix5hd2_i2c_runtime_suspend(struct device *dev)
{
struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev);
@@ -505,12 +492,11 @@ static int hix5hd2_i2c_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops hix5hd2_i2c_pm_ops = {
- SET_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,
- hix5hd2_i2c_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,
+ hix5hd2_i2c_runtime_resume,
+ NULL)
};
static const struct of_device_id hix5hd2_i2c_match[] = {
@@ -524,7 +510,7 @@ static struct platform_driver hix5hd2_i2c_driver = {
.remove = hix5hd2_i2c_remove,
.driver = {
.name = "hix5hd2-i2c",
- .pm = &hix5hd2_i2c_pm_ops,
+ .pm = pm_ptr(&hix5hd2_i2c_pm_ops),
.of_match_table = hix5hd2_i2c_match,
},
};
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index b7864cf42a72..c60b73e933cf 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
i2c Support for the Apple `Hydra' Mac I/O
@@ -6,15 +7,6 @@
Based on i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index c91e145ef5a5..81e6e2d7ad3d 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
@@ -6,15 +7,6 @@
Copyright (C) 2010 Intel Corporation,
David Woodhouse <dwmw2@infradead.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -62,6 +54,7 @@
* Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
* Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes
* DNV (SOC) 0x19df 32 hard yes yes yes
+ * Emmitsburg (PCH) 0x1bc9 32 hard yes yes yes
* Broxton (SOC) 0x5ad4 32 hard yes yes yes
* Lewisburg (PCH) 0xa1a3 32 hard yes yes yes
* Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes
@@ -71,20 +64,43 @@
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
* Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
+ * Ice Lake-N (PCH) 0x38a3 32 hard yes yes yes
+ * Comet Lake (PCH) 0x02a3 32 hard yes yes yes
+ * Comet Lake-H (PCH) 0x06a3 32 hard yes yes yes
+ * Elkhart Lake (PCH) 0x4b23 32 hard yes yes yes
+ * Tiger Lake-LP (PCH) 0xa0a3 32 hard yes yes yes
+ * Tiger Lake-H (PCH) 0x43a3 32 hard yes yes yes
+ * Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
+ * Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
+ * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
+ * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
+ * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes
+ * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes
+ * Meteor Lake-P (SOC) 0x7e22 32 hard yes yes yes
+ * Meteor Lake SoC-S (SOC) 0xae22 32 hard yes yes yes
+ * Meteor Lake PCH-S (PCH) 0x7f23 32 hard yes yes yes
+ * Birch Stream (SOC) 0x5796 32 hard yes yes yes
+ * Arrow Lake-H (SOC) 0x7722 32 hard yes yes yes
+ * Panther Lake-H (SOC) 0xe322 32 hard yes yes yes
+ * Panther Lake-P (SOC) 0xe422 32 hard yes yes yes
+ * Wildcat Lake-U (SOC) 0x4d22 32 hard yes yes yes
+ * Diamond Rapids (SOC) 0x5827 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
* Hardware PEC yes
* Block buffer yes
- * Block process call transaction no
+ * Block process call transaction yes
* I2C block read transaction yes (doesn't use the block buffer)
- * Slave mode no
+ * Target mode no
* SMBus Host Notify yes
* Interrupt processing yes
*
- * See the file Documentation/i2c/busses/i2c-i801 for details.
+ * See the file Documentation/i2c/busses/i2c-i801.rst for details.
*/
+#define DRV_NAME "i801_smbus"
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -94,19 +110,23 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
#include <linux/i2c-smbus.h>
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/dmi.h>
#include <linux/slab.h>
-#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/completion.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>
+#include <linux/platform_data/x86/p2sb.h>
#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
-#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
-#include <linux/gpio.h>
+#ifdef CONFIG_I2C_I801_MUX
+#include <linux/gpio/machine.h>
#include <linux/platform_data/i2c-mux-gpio.h>
#endif
@@ -126,28 +146,15 @@
#define SMBNTFDADD(p) (20 + (p)->smba) /* ICH3 and later */
/* PCI Address Constants */
+#define SMBBAR_MMIO 0
#define SMBBAR 4
-#define SMBPCICTL 0x004
-#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
#define TCOBASE 0x050
#define TCOCTL 0x054
-#define ACPIBASE 0x040
-#define ACPIBASE_SMI_OFF 0x030
-#define ACPICTRL 0x044
-#define ACPICTRL_EN 0x080
-
-#define SBREG_BAR 0x10
#define SBREG_SMBCTRL 0xc6000c
#define SBREG_SMBCTRL_DNV 0xcf000c
-/* Host status bits for SMBPCISTS */
-#define SMBPCISTS_INTS BIT(3)
-
-/* Control bits for SMBPCICTL */
-#define SMBPCICTL_INTDIS BIT(10)
-
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN BIT(0)
#define SMBHSTCFG_SMB_SMI_EN BIT(1)
@@ -165,17 +172,15 @@
#define SMBAUXCTL_CRC BIT(0)
#define SMBAUXCTL_E32B BIT(1)
-/* Other settings */
-#define MAX_RETRIES 400
-
/* I801 command constants */
#define I801_QUICK 0x00
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
-#define I801_PROC_CALL 0x10 /* unimplemented */
+#define I801_PROC_CALL 0x10
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
+#define I801_BLOCK_PROC_CALL 0x1C
/* I801 Host Control register bits */
#define SMBHSTCNT_INTREN BIT(0)
@@ -198,6 +203,7 @@
#define SMBSLVSTS_HST_NTFY_STS BIT(0)
/* Host Notify Command register bits */
+#define SMBSLVCMD_SMBALERT_DISABLE BIT(2)
#define SMBSLVCMD_HST_NTFY_INTREN BIT(0)
#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
@@ -206,10 +212,15 @@
#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | \
STATUS_ERROR_FLAGS)
+#define SMBUS_LEN_SENTINEL (I2C_SMBUS_BLOCK_MAX + 1)
+
/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS 0x02a3
+#define PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS 0x06a3
#define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12
#define PCI_DEVICE_ID_INTEL_CDF_SMBUS 0x18df
#define PCI_DEVICE_ID_INTEL_DNV_SMBUS 0x19df
+#define PCI_DEVICE_ID_INTEL_EBG_SMBUS 0x1bc9
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
@@ -223,8 +234,22 @@
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
#define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3
+#define PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS 0x38a3
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
+#define PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS 0x43a3
+#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
+#define PCI_DEVICE_ID_INTEL_WILDCAT_LAKE_U_SMBUS 0x4d22
+#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
+#define PCI_DEVICE_ID_INTEL_BIRCH_STREAM_SMBUS 0x5796
+#define PCI_DEVICE_ID_INTEL_DIAMOND_RAPIDS_SMBUS 0x5827
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
+#define PCI_DEVICE_ID_INTEL_ARROW_LAKE_H_SMBUS 0x7722
+#define PCI_DEVICE_ID_INTEL_RAPTOR_LAKE_S_SMBUS 0x7a23
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
+#define PCI_DEVICE_ID_INTEL_METEOR_LAKE_P_SMBUS 0x7e22
+#define PCI_DEVICE_ID_INTEL_METEOR_LAKE_PCH_S_SMBUS 0x7f23
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
@@ -235,31 +260,36 @@
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS 0x9da3
+#define PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS 0xa0a3
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS 0xa1a3
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223
#define PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS 0xa2a3
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS 0xa323
+#define PCI_DEVICE_ID_INTEL_COMETLAKE_V_SMBUS 0xa3a3
+#define PCI_DEVICE_ID_INTEL_METEOR_LAKE_SOC_S_SMBUS 0xae22
+#define PCI_DEVICE_ID_INTEL_PANTHER_LAKE_H_SMBUS 0xe322
+#define PCI_DEVICE_ID_INTEL_PANTHER_LAKE_P_SMBUS 0xe422
struct i801_mux_config {
char *gpio_chip;
unsigned values[3];
int n_values;
- unsigned classes[3];
unsigned gpios[2]; /* Relative to gpio_chip->base */
int n_gpios;
};
struct i801_priv {
struct i2c_adapter adapter;
- unsigned long smba;
+ void __iomem *smba;
unsigned char original_hstcfg;
+ unsigned char original_hstcnt;
unsigned char original_slvcmd;
struct pci_dev *pci_dev;
unsigned int features;
/* isr processing */
- wait_queue_head_t waitq;
+ struct completion done;
u8 status;
/* Command state used by isr for byte-by-byte block transactions */
@@ -269,18 +299,18 @@ struct i801_priv {
int len;
u8 *data;
-#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
- const struct i801_mux_config *mux_drvdata;
+#ifdef CONFIG_I2C_I801_MUX
struct platform_device *mux_pdev;
+ struct gpiod_lookup_table *lookup;
+ struct notifier_block mux_notifier_block;
#endif
struct platform_device *tco_pdev;
/*
* If set to true the host controller registers are reserved for
- * ACPI AML use. Protected by acpi_lock.
+ * ACPI AML use.
*/
bool acpi_reserved;
- struct mutex acpi_lock;
};
#define FEATURE_SMBUS_PEC BIT(0)
@@ -291,7 +321,8 @@ struct i801_priv {
#define FEATURE_HOST_NOTIFY BIT(5)
/* Not really a feature, but it's convenient to handle it as such */
#define FEATURE_IDF BIT(15)
-#define FEATURE_TCO BIT(16)
+#define FEATURE_TCO_SPT BIT(16)
+#define FEATURE_TCO_CNL BIT(17)
static const char *i801_feature_names[] = {
"SMBus PEC",
@@ -311,30 +342,84 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n"
"\t\t 0x10 don't use interrupts\n"
"\t\t 0x20 disable SMBus Host Notify ");
+/* Wait for BUSY being cleared and either INTR or an error flag being set */
+static int i801_wait_intr(struct i801_priv *priv)
+{
+ unsigned long timeout = jiffies + priv->adapter.timeout;
+ int status, busy;
+
+ do {
+ usleep_range(250, 500);
+ status = ioread8(SMBHSTSTS(priv));
+ busy = status & SMBHSTSTS_HOST_BUSY;
+ status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
+ if (!busy && status)
+ return status & STATUS_ERROR_FLAGS;
+ } while (time_is_after_eq_jiffies(timeout));
+
+ return -ETIMEDOUT;
+}
+
+/* Wait for either BYTE_DONE or an error flag being set */
+static int i801_wait_byte_done(struct i801_priv *priv)
+{
+ unsigned long timeout = jiffies + priv->adapter.timeout;
+ int status;
+
+ do {
+ usleep_range(250, 500);
+ status = ioread8(SMBHSTSTS(priv));
+ if (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE))
+ return status & STATUS_ERROR_FLAGS;
+ } while (time_is_after_eq_jiffies(timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int i801_get_block_len(struct i801_priv *priv)
+{
+ u8 len = ioread8(SMBHSTDAT0(priv));
+
+ if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
+ pci_err(priv->pci_dev, "Illegal SMBus block read size %u\n", len);
+ return -EPROTO;
+ }
+
+ return len;
+}
+
+static int i801_check_and_clear_pec_error(struct i801_priv *priv)
+{
+ u8 status;
+
+ if (!(priv->features & FEATURE_SMBUS_PEC))
+ return 0;
+
+ status = ioread8(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
+ if (status) {
+ iowrite8(status, SMBAUXSTS(priv));
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
/* Make sure the SMBus host is ready to start transmitting.
Return 0 if it is, -EBUSY if it is not. */
static int i801_check_pre(struct i801_priv *priv)
{
- int status;
+ int status, result;
- status = inb_p(SMBHSTSTS(priv));
+ status = ioread8(SMBHSTSTS(priv));
if (status & SMBHSTSTS_HOST_BUSY) {
- dev_err(&priv->pci_dev->dev, "SMBus is busy, can't use it!\n");
+ pci_err(priv->pci_dev, "SMBus is busy, can't use it!\n");
return -EBUSY;
}
status &= STATUS_FLAGS;
if (status) {
- dev_dbg(&priv->pci_dev->dev, "Clearing status flags (%02x)\n",
- status);
- outb_p(status, SMBHSTSTS(priv));
- status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
- if (status) {
- dev_err(&priv->pci_dev->dev,
- "Failed clearing status flags (%02x)\n",
- status);
- return -EBUSY;
- }
+ pci_dbg(priv->pci_dev, "Clearing status flags (%02x)\n", status);
+ iowrite8(status, SMBHSTSTS(priv));
}
/*
@@ -344,63 +429,35 @@ static int i801_check_pre(struct i801_priv *priv)
* the hardware was already in this state when the driver
* started.
*/
- if (priv->features & FEATURE_SMBUS_PEC) {
- status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
- if (status) {
- dev_dbg(&priv->pci_dev->dev,
- "Clearing aux status flags (%02x)\n", status);
- outb_p(status, SMBAUXSTS(priv));
- status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
- if (status) {
- dev_err(&priv->pci_dev->dev,
- "Failed clearing aux status flags (%02x)\n",
- status);
- return -EBUSY;
- }
- }
- }
+ result = i801_check_and_clear_pec_error(priv);
+ if (result)
+ pci_dbg(priv->pci_dev, "Clearing aux status flag CRCE\n");
return 0;
}
-/*
- * Convert the status register to an error code, and clear it.
- * Note that status only contains the bits we want to clear, not the
- * actual register value.
- */
static int i801_check_post(struct i801_priv *priv, int status)
{
int result = 0;
/*
* If the SMBus is still busy, we give up
- * Note: This timeout condition only happens when using polling
- * transactions. For interrupt operation, NAK/timeout is indicated by
- * DEV_ERR.
*/
if (unlikely(status < 0)) {
- dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
/* try to stop the current command */
- dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n");
- outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL,
- SMBHSTCNT(priv));
- usleep_range(1000, 2000);
- outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL),
- SMBHSTCNT(priv));
+ iowrite8(SMBHSTCNT_KILL, SMBHSTCNT(priv));
+ status = i801_wait_intr(priv);
+ iowrite8(0, SMBHSTCNT(priv));
/* Check if it worked */
- status = inb_p(SMBHSTSTS(priv));
- if ((status & SMBHSTSTS_HOST_BUSY) ||
- !(status & SMBHSTSTS_FAILED))
- dev_err(&priv->pci_dev->dev,
- "Failed terminating the transaction\n");
- outb_p(STATUS_FLAGS, SMBHSTSTS(priv));
+ if (status < 0 || !(status & SMBHSTSTS_FAILED))
+ pci_dbg(priv->pci_dev, "Failed terminating the transaction\n");
return -ETIMEDOUT;
}
if (status & SMBHSTSTS_FAILED) {
result = -EIO;
- dev_err(&priv->pci_dev->dev, "Transaction failed\n");
+ pci_err(priv->pci_dev, "Transaction failed\n");
}
if (status & SMBHSTSTS_DEV_ERR) {
/*
@@ -418,181 +475,126 @@ static int i801_check_post(struct i801_priv *priv, int status)
* bit is harmless as long as it's cleared before
* the next operation.
*/
- if ((priv->features & FEATURE_SMBUS_PEC) &&
- (inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) {
- outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv));
- result = -EBADMSG;
- dev_dbg(&priv->pci_dev->dev, "PEC error\n");
+ result = i801_check_and_clear_pec_error(priv);
+ if (result) {
+ pci_dbg(priv->pci_dev, "PEC error\n");
} else {
result = -ENXIO;
- dev_dbg(&priv->pci_dev->dev, "No response\n");
+ pci_dbg(priv->pci_dev, "No response\n");
}
}
if (status & SMBHSTSTS_BUS_ERR) {
result = -EAGAIN;
- dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n");
+ pci_dbg(priv->pci_dev, "Lost arbitration\n");
}
- /* Clear status flags except BYTE_DONE, to be cleared by caller */
- outb_p(status, SMBHSTSTS(priv));
-
return result;
}
-/* Wait for BUSY being cleared and either INTR or an error flag being set */
-static int i801_wait_intr(struct i801_priv *priv)
-{
- int timeout = 0;
- int status;
-
- /* We will always wait for a fraction of a second! */
- do {
- usleep_range(250, 500);
- status = inb_p(SMBHSTSTS(priv));
- } while (((status & SMBHSTSTS_HOST_BUSY) ||
- !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
- (timeout++ < MAX_RETRIES));
-
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
-}
-
-/* Wait for either BYTE_DONE or an error flag being set */
-static int i801_wait_byte_done(struct i801_priv *priv)
-{
- int timeout = 0;
- int status;
-
- /* We will always wait for a fraction of a second! */
- do {
- usleep_range(250, 500);
- status = inb_p(SMBHSTSTS(priv));
- } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
- (timeout++ < MAX_RETRIES));
-
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & STATUS_ERROR_FLAGS;
-}
-
static int i801_transaction(struct i801_priv *priv, int xact)
{
- int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
-
if (priv->features & FEATURE_IRQ) {
- outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
+ reinit_completion(&priv->done);
+ iowrite8(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return result ? priv->status : -ETIMEDOUT;
}
- /* the current contents of SMBHSTCNT can be overwritten, since PEC,
- * SMBSCMD are passed in xact */
- outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
+ iowrite8(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
- status = i801_wait_intr(priv);
- return i801_check_post(priv, status);
+ return i801_wait_intr(priv);
}
static int i801_block_transaction_by_block(struct i801_priv *priv,
union i2c_smbus_data *data,
- char read_write, int hwpec)
+ char read_write, int command)
{
- int i, len;
- int status;
+ int len, status, xact;
- inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */
+ switch (command) {
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ xact = I801_BLOCK_PROC_CALL;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ xact = I801_BLOCK_DATA;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Set block buffer mode */
+ iowrite8(ioread8(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv));
- /* Use 32-byte buffer to process this transaction */
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
- outb_p(len, SMBHSTDAT0(priv));
- for (i = 0; i < len; i++)
- outb_p(data->block[i+1], SMBBLKDAT(priv));
+ iowrite8(len, SMBHSTDAT0(priv));
+ ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */
+ iowrite8_rep(SMBBLKDAT(priv), data->block + 1, len);
}
- status = i801_transaction(priv, I801_BLOCK_DATA |
- (hwpec ? SMBHSTCNT_PEC_EN : 0));
+ status = i801_transaction(priv, xact);
if (status)
- return status;
+ goto out;
- if (read_write == I2C_SMBUS_READ) {
- len = inb_p(SMBHSTDAT0(priv));
- if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
- return -EPROTO;
+ if (read_write == I2C_SMBUS_READ ||
+ command == I2C_SMBUS_BLOCK_PROC_CALL) {
+ len = i801_get_block_len(priv);
+ if (len < 0) {
+ status = len;
+ goto out;
+ }
data->block[0] = len;
- for (i = 0; i < len; i++)
- data->block[i + 1] = inb_p(SMBBLKDAT(priv));
+ ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */
+ ioread8_rep(SMBBLKDAT(priv), data->block + 1, len);
}
- return 0;
+out:
+ iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_E32B, SMBAUXCTL(priv));
+ return status;
}
static void i801_isr_byte_done(struct i801_priv *priv)
{
if (priv->is_read) {
- /* For SMBus block reads, length is received with first byte */
- if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) &&
- (priv->count == 0)) {
- priv->len = inb_p(SMBHSTDAT0(priv));
- if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&priv->pci_dev->dev,
- "Illegal SMBus block read size %d\n",
- priv->len);
+ /*
+ * At transfer start i801_smbus_block_transaction() marks
+ * the block length as invalid. Check for this sentinel value
+ * and read the block length from SMBHSTDAT0.
+ */
+ if (priv->len == SMBUS_LEN_SENTINEL) {
+ priv->len = i801_get_block_len(priv);
+ if (priv->len < 0)
/* FIXME: Recover */
priv->len = I2C_SMBUS_BLOCK_MAX;
- } else {
- dev_dbg(&priv->pci_dev->dev,
- "SMBus block read size is %d\n",
- priv->len);
- }
+
priv->data[-1] = priv->len;
}
/* Read next byte */
if (priv->count < priv->len)
- priv->data[priv->count++] = inb(SMBBLKDAT(priv));
+ priv->data[priv->count++] = ioread8(SMBBLKDAT(priv));
else
- dev_dbg(&priv->pci_dev->dev,
- "Discarding extra byte on block read\n");
+ pci_dbg(priv->pci_dev, "Discarding extra byte on block read\n");
/* Set LAST_BYTE for last byte of read transaction */
if (priv->count == priv->len - 1)
- outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE,
+ iowrite8(priv->cmd | SMBHSTCNT_LAST_BYTE,
SMBHSTCNT(priv));
} else if (priv->count < priv->len - 1) {
/* Write next byte, except for IRQ after last byte */
- outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
+ iowrite8(priv->data[++priv->count], SMBBLKDAT(priv));
}
-
- /* Clear BYTE_DONE to continue with next byte */
- outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
}
static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
{
unsigned short addr;
- addr = inb_p(SMBNTFDADD(priv)) >> 1;
+ addr = ioread8(SMBNTFDADD(priv)) >> 1;
/*
* With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
@@ -602,7 +604,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
i2c_handle_smbus_host_notify(&priv->adapter, addr);
/* clear Host Notify bit and return */
- outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
+ iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
return IRQ_HANDLED;
}
@@ -614,8 +616,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
* DEV_ERR - Invalid command, NAK or communication timeout
* BUS_ERR - SMI# transaction collision
* FAILED - transaction was canceled due to a KILL request
- * When any of these occur, update ->status and wake up the waitq.
- * ->status must be cleared before kicking off the next transaction.
+ * When any of these occur, update ->status and signal completion.
*
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
* occurs for each byte of a byte-by-byte to prepare the next byte.
@@ -629,29 +630,32 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
u8 status;
/* Confirm this is our interrupt */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (!(pcists & SMBPCISTS_INTS))
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (!(pcists & PCI_STATUS_INTERRUPT))
return IRQ_NONE;
if (priv->features & FEATURE_HOST_NOTIFY) {
- status = inb_p(SMBSLVSTS(priv));
+ status = ioread8(SMBSLVSTS(priv));
if (status & SMBSLVSTS_HST_NTFY_STS)
return i801_host_notify_isr(priv);
}
- status = inb_p(SMBHSTSTS(priv));
- if (status & SMBHSTSTS_BYTE_DONE)
+ status = ioread8(SMBHSTSTS(priv));
+ if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE)
i801_isr_byte_done(priv);
/*
- * Clear irq sources and report transaction result.
- * ->status must be cleared before the next transaction is started.
+ * Clear IRQ sources: SMB_ALERT status is set after signal assertion
+ * independently of the interrupt generation being blocked or not
+ * so clear it always when the status is set.
*/
- status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS;
+ status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS;
+ iowrite8(status, SMBHSTSTS(priv));
+
+ status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
if (status) {
- outb_p(status, SMBHSTSTS(priv));
- priv->status = status;
- wake_up(&priv->waitq);
+ priv->status = status & STATUS_ERROR_FLAGS;
+ complete(&priv->done);
}
return IRQ_HANDLED;
@@ -664,24 +668,22 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
*/
static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
union i2c_smbus_data *data,
- char read_write, int command,
- int hwpec)
+ char read_write, int command)
{
int i, len;
int smbcmd;
int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
+ if (command == I2C_SMBUS_BLOCK_PROC_CALL)
+ return -EOPNOTSUPP;
len = data->block[0];
if (read_write == I2C_SMBUS_WRITE) {
- outb_p(len, SMBHSTDAT0(priv));
- outb_p(data->block[1], SMBBLKDAT(priv));
+ iowrite8(len, SMBHSTDAT0(priv));
+ iowrite8(data->block[1], SMBBLKDAT(priv));
}
if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
@@ -699,122 +701,186 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
priv->count = 0;
priv->data = &data->block[1];
- outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ reinit_completion(&priv->done);
+ iowrite8(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return result ? priv->status : -ETIMEDOUT;
}
- for (i = 1; i <= len; i++) {
- if (i == len && read_write == I2C_SMBUS_READ)
- smbcmd |= SMBHSTCNT_LAST_BYTE;
- outb_p(smbcmd, SMBHSTCNT(priv));
-
- if (i == 1)
- outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START,
- SMBHSTCNT(priv));
+ if (len == 1 && read_write == I2C_SMBUS_READ)
+ smbcmd |= SMBHSTCNT_LAST_BYTE;
+ iowrite8(smbcmd | SMBHSTCNT_START, SMBHSTCNT(priv));
+ for (i = 1; i <= len; i++) {
status = i801_wait_byte_done(priv);
if (status)
- goto exit;
-
- if (i == 1 && read_write == I2C_SMBUS_READ
- && command != I2C_SMBUS_I2C_BLOCK_DATA) {
- len = inb_p(SMBHSTDAT0(priv));
- if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&priv->pci_dev->dev,
- "Illegal SMBus block read size %d\n",
- len);
+ return status;
+
+ /*
+ * At transfer start i801_smbus_block_transaction() marks
+ * the block length as invalid. Check for this sentinel value
+ * and read the block length from SMBHSTDAT0.
+ */
+ if (len == SMBUS_LEN_SENTINEL) {
+ len = i801_get_block_len(priv);
+ if (len < 0) {
/* Recover */
- while (inb_p(SMBHSTSTS(priv)) &
+ while (ioread8(SMBHSTSTS(priv)) &
SMBHSTSTS_HOST_BUSY)
- outb_p(SMBHSTSTS_BYTE_DONE,
+ iowrite8(SMBHSTSTS_BYTE_DONE,
SMBHSTSTS(priv));
- outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv));
+ iowrite8(SMBHSTSTS_INTR, SMBHSTSTS(priv));
return -EPROTO;
}
data->block[0] = len;
}
- /* Retrieve/store value in SMBBLKDAT */
- if (read_write == I2C_SMBUS_READ)
- data->block[i] = inb_p(SMBBLKDAT(priv));
+ if (read_write == I2C_SMBUS_READ) {
+ data->block[i] = ioread8(SMBBLKDAT(priv));
+ if (i == len - 1)
+ iowrite8(smbcmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv));
+ }
+
if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
- outb_p(data->block[i+1], SMBBLKDAT(priv));
+ iowrite8(data->block[i+1], SMBBLKDAT(priv));
/* signals SMBBLKDAT ready */
- outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
+ iowrite8(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
}
- status = i801_wait_intr(priv);
-exit:
- return i801_check_post(priv, status);
+ return i801_wait_intr(priv);
}
-static int i801_set_block_buffer_mode(struct i801_priv *priv)
+static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
{
- outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv));
- if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0)
- return -EIO;
- return 0;
+ iowrite8((addr << 1) | (read_write & 0x01), SMBHSTADD(priv));
}
-/* Block transaction function */
-static int i801_block_transaction(struct i801_priv *priv,
- union i2c_smbus_data *data, char read_write,
- int command, int hwpec)
+/* Single value transaction function */
+static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ u8 addr, u8 hstcmd, char read_write, int command)
{
- int result = 0;
- unsigned char hostc;
+ int xact, ret;
- if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+ switch (command) {
+ case I2C_SMBUS_QUICK:
+ i801_set_hstadd(priv, addr, read_write);
+ xact = I801_QUICK;
+ break;
+ case I2C_SMBUS_BYTE:
+ i801_set_hstadd(priv, addr, read_write);
+ if (read_write == I2C_SMBUS_WRITE)
+ iowrite8(hstcmd, SMBHSTCMD(priv));
+ xact = I801_BYTE;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ i801_set_hstadd(priv, addr, read_write);
+ if (read_write == I2C_SMBUS_WRITE)
+ iowrite8(data->byte, SMBHSTDAT0(priv));
+ iowrite8(hstcmd, SMBHSTCMD(priv));
+ xact = I801_BYTE_DATA;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ i801_set_hstadd(priv, addr, read_write);
if (read_write == I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(priv->pci_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
- dev_err(&priv->pci_dev->dev,
- "I2C block read is unsupported!\n");
- return -EOPNOTSUPP;
+ iowrite8(data->word & 0xff, SMBHSTDAT0(priv));
+ iowrite8((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
}
+ iowrite8(hstcmd, SMBHSTCMD(priv));
+ xact = I801_WORD_DATA;
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
+ iowrite8(data->word & 0xff, SMBHSTDAT0(priv));
+ iowrite8((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
+ iowrite8(hstcmd, SMBHSTCMD(priv));
+ read_write = I2C_SMBUS_READ;
+ xact = I801_PROC_CALL;
+ break;
+ default:
+ pci_err(priv->pci_dev, "Unsupported transaction %d\n", command);
+ return -EOPNOTSUPP;
}
- if (read_write == I2C_SMBUS_WRITE
- || command == I2C_SMBUS_I2C_BLOCK_DATA) {
- if (data->block[0] < 1)
- data->block[0] = 1;
- if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
- data->block[0] = I2C_SMBUS_BLOCK_MAX;
- } else {
- data->block[0] = 32; /* max for SMBus block reads */
+ ret = i801_transaction(priv, xact);
+ if (ret || read_write == I2C_SMBUS_WRITE)
+ return ret;
+
+ switch (command) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ data->byte = ioread8(SMBHSTDAT0(priv));
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ data->word = ioread8(SMBHSTDAT0(priv)) +
+ (ioread8(SMBHSTDAT1(priv)) << 8);
+ break;
}
- /* Experience has shown that the block buffer can only be used for
- SMBus (not I2C) block transactions, even though the datasheet
- doesn't mention this limitation. */
- if ((priv->features & FEATURE_BLOCK_BUFFER)
- && command != I2C_SMBUS_I2C_BLOCK_DATA
- && i801_set_block_buffer_mode(priv) == 0)
- result = i801_block_transaction_by_block(priv, data,
- read_write, hwpec);
+ return 0;
+}
+
+static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ u8 addr, u8 hstcmd, char read_write, int command)
+{
+ if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA)
+ /* Mark block length as invalid */
+ data->block[0] = SMBUS_LEN_SENTINEL;
+ else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+
+ if (command == I2C_SMBUS_BLOCK_PROC_CALL)
+ /* Needs to be flagged as write transaction */
+ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
else
- result = i801_block_transaction_byte_by_byte(priv, data,
- read_write,
- command, hwpec);
+ i801_set_hstadd(priv, addr, read_write);
+ iowrite8(hstcmd, SMBHSTCMD(priv));
- if (command == I2C_SMBUS_I2C_BLOCK_DATA
- && read_write == I2C_SMBUS_WRITE) {
- /* restore saved configuration register value */
- pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc);
+ if (priv->features & FEATURE_BLOCK_BUFFER)
+ return i801_block_transaction_by_block(priv, data, read_write, command);
+ else
+ return i801_block_transaction_byte_by_byte(priv, data, read_write, command);
+}
+
+static int i801_i2c_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ u8 addr, u8 hstcmd, char read_write, int command)
+{
+ int result;
+ u8 hostc;
+
+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+ /*
+ * NB: page 240 of ICH5 datasheet shows that the R/#W bit should be cleared here,
+ * even when reading. However if SPD Write Disable is set (Lynx Point and later),
+ * the read will fail if we don't set the R/#W bit.
+ */
+ i801_set_hstadd(priv, addr,
+ priv->original_hstcfg & SMBHSTCFG_SPD_WD ? read_write : I2C_SMBUS_WRITE);
+
+ /* NB: page 240 of ICH5 datasheet shows that DATA1 is the cmd field when reading */
+ if (read_write == I2C_SMBUS_READ)
+ iowrite8(hstcmd, SMBHSTDAT1(priv));
+ else
+ iowrite8(hstcmd, SMBHSTCMD(priv));
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
+ pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN);
+ } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
+ pci_err(priv->pci_dev, "I2C block read is unsupported!\n");
+ return -EOPNOTSUPP;
}
+
+ /* Block buffer isn't supported for I2C block transactions */
+ result = i801_block_transaction_byte_by_byte(priv, data, read_write, command);
+
+ /* restore saved configuration register value */
+ if (read_write == I2C_SMBUS_WRITE)
+ pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc);
+
return result;
}
@@ -823,127 +889,50 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
- int hwpec;
- int block = 0;
- int ret = 0, xact = 0;
+ int hwpec, ret;
struct i801_priv *priv = i2c_get_adapdata(adap);
- mutex_lock(&priv->acpi_lock);
- if (priv->acpi_reserved) {
- mutex_unlock(&priv->acpi_lock);
+ if (priv->acpi_reserved)
return -EBUSY;
- }
pm_runtime_get_sync(&priv->pci_dev->dev);
+ ret = i801_check_pre(priv);
+ if (ret)
+ goto out;
+
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
&& size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
- switch (size) {
- case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
- xact = I801_QUICK;
- break;
- case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(command, SMBHSTCMD(priv));
- xact = I801_BYTE;
- break;
- case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
- outb_p(command, SMBHSTCMD(priv));
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0(priv));
- xact = I801_BYTE_DATA;
- break;
- case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
- outb_p(command, SMBHSTCMD(priv));
- if (read_write == I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0(priv));
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
- }
- xact = I801_WORD_DATA;
- break;
- case I2C_SMBUS_BLOCK_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
- outb_p(command, SMBHSTCMD(priv));
- block = 1;
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- /*
- * NB: page 240 of ICH5 datasheet shows that the R/#W
- * bit should be cleared here, even when reading.
- * However if SPD Write Disable is set (Lynx Point and later),
- * the read will fail if we don't set the R/#W bit.
- */
- outb_p(((addr & 0x7f) << 1) |
- ((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
- (read_write & 0x01) : 0),
- SMBHSTADD(priv));
- if (read_write == I2C_SMBUS_READ) {
- /* NB: page 240 of ICH5 datasheet also shows
- * that DATA1 is the cmd field when reading */
- outb_p(command, SMBHSTDAT1(priv));
- } else
- outb_p(command, SMBHSTCMD(priv));
- block = 1;
- break;
- default:
- dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
- size);
- ret = -EOPNOTSUPP;
- goto out;
- }
-
if (hwpec) /* enable/disable hardware PEC */
- outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv));
+ iowrite8(ioread8(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv));
else
- outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC),
+ iowrite8(ioread8(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC),
SMBAUXCTL(priv));
- if (block)
- ret = i801_block_transaction(priv, data, read_write, size,
- hwpec);
+ if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_BLOCK_PROC_CALL)
+ ret = i801_smbus_block_transaction(priv, data, addr, command, read_write, size);
+ else if (size == I2C_SMBUS_I2C_BLOCK_DATA)
+ ret = i801_i2c_block_transaction(priv, data, addr, command, read_write, size);
else
- ret = i801_transaction(priv, xact);
+ ret = i801_simple_transaction(priv, data, addr, command, read_write, size);
- /* Some BIOSes don't like it when PEC is enabled at reboot or resume
- time, so we forcibly disable it after every transaction. Turn off
- E32B for the same reason. */
- if (hwpec || block)
- outb_p(inb_p(SMBAUXCTL(priv)) &
- ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
-
- if (block)
- goto out;
- if (ret)
- goto out;
- if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
- goto out;
-
- switch (xact & 0x7f) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0(priv));
- break;
- case I801_WORD_DATA:
- data->word = inb_p(SMBHSTDAT0(priv)) +
- (inb_p(SMBHSTDAT1(priv)) << 8);
- break;
- }
+ ret = i801_check_post(priv, ret);
+ /* Some BIOSes don't like it when PEC is enabled at reboot or resume
+ * time, so we forcibly disable it after every transaction.
+ */
+ if (hwpec)
+ iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv));
out:
- pm_runtime_mark_last_busy(&priv->pci_dev->dev);
+ /*
+ * Unlock the SMBus device for use by BIOS/ACPI,
+ * and clear status flags if not done already.
+ */
+ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
+
pm_runtime_put_autosuspend(&priv->pci_dev->dev);
- mutex_unlock(&priv->acpi_lock);
return ret;
}
@@ -954,8 +943,11 @@ static u32 i801_func(struct i2c_adapter *adapter)
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
+ ((priv->features & FEATURE_BLOCK_PROC) ?
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL : 0) |
((priv->features & FEATURE_I2C_BLOCK_READ) ?
I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) |
((priv->features & FEATURE_HOST_NOTIFY) ?
@@ -969,12 +961,16 @@ static void i801_enable_host_notify(struct i2c_adapter *adapter)
if (!(priv->features & FEATURE_HOST_NOTIFY))
return;
- if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd))
- outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd,
- SMBSLVCMD(priv));
+ /*
+ * Enable host notify interrupt and block the generation of interrupt
+ * from the SMB_ALERT signal because the driver does not support
+ * SMBus Alert.
+ */
+ iowrite8(SMBSLVCMD_HST_NTFY_INTREN | SMBSLVCMD_SMBALERT_DISABLE |
+ priv->original_slvcmd, SMBSLVCMD(priv));
/* clear Host Notify bit to allow a new notification */
- outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
+ iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
}
static void i801_disable_host_notify(struct i801_priv *priv)
@@ -982,7 +978,7 @@ static void i801_disable_host_notify(struct i801_priv *priv)
if (!(priv->features & FEATURE_HOST_NOTIFY))
return;
- outb_p(priv->original_slvcmd, SMBSLVCMD(priv));
+ iowrite8(priv->original_slvcmd, SMBSLVCMD(priv));
}
static const struct i2c_algorithm smbus_algorithm = {
@@ -990,61 +986,88 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = i801_func,
};
+#define FEATURES_ICH4 (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER | \
+ FEATURE_HOST_NOTIFY)
+#define FEATURES_ICH5 (FEATURES_ICH4 | FEATURE_BLOCK_PROC | \
+ FEATURE_I2C_BLOCK_READ | FEATURE_IRQ)
+
static const struct pci_device_id i801_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_4) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EP80579_1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROXTON_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
+ { PCI_DEVICE_DATA(INTEL, 82801AA_3, 0) },
+ { PCI_DEVICE_DATA(INTEL, 82801AB_3, 0) },
+ { PCI_DEVICE_DATA(INTEL, 82801BA_2, 0) },
+ { PCI_DEVICE_DATA(INTEL, 82801CA_3, FEATURE_HOST_NOTIFY) },
+ { PCI_DEVICE_DATA(INTEL, 82801DB_3, FEATURES_ICH4) },
+ { PCI_DEVICE_DATA(INTEL, 82801EB_3, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ESB_4, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH6_16, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH7_17, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ESB2_17, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH8_5, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH9_6, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, EP80579_1, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH10_4, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ICH10_5, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, 5_3400_SERIES_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, COUGARPOINT_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF0, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF1, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF2, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, DH89XXCC_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, PANTHERPOINT_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, LYNXPOINT_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, LYNXPOINT_LP_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, AVOTON_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS0, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS1, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS2, FEATURES_ICH5 | FEATURE_IDF) },
+ { PCI_DEVICE_DATA(INTEL, COLETOCREEK_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, GEMINILAKE_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, WILDCATPOINT_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, WILDCATPOINT_LP_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, BAYTRAIL_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, BRASWELL_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, SUNRISEPOINT_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, SUNRISEPOINT_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, CDF_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, DNV_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, EBG_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, BROXTON_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, LEWISBURG_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, LEWISBURG_SSKU_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, KABYLAKE_PCH_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, CANNONLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, CANNONLAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, ICELAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, ICELAKE_N_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, COMETLAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, COMETLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, COMETLAKE_V_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
+ { PCI_DEVICE_DATA(INTEL, ELKHART_LAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, TIGERLAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, TIGERLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, JASPER_LAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, RAPTOR_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, METEOR_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, METEOR_LAKE_SOC_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, METEOR_LAKE_PCH_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, BIRCH_STREAM_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, DIAMOND_RAPIDS_SMBUS, FEATURES_ICH5) },
+ { PCI_DEVICE_DATA(INTEL, ARROW_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, WILDCAT_LAKE_U_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, i801_ids);
#if defined CONFIG_X86 && defined CONFIG_DMI
-static unsigned char apanel_addr;
+static unsigned char apanel_addr __ro_after_init;
/* Scan the system ROM for the signature "FJKEYINF" */
static __init const void __iomem *bios_signature(const void __iomem *bios)
@@ -1102,8 +1125,8 @@ static void dmi_check_onboard_device(u8 type, const char *name,
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = dmi_devices[i].i2c_addr;
- strlcpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE);
- i2c_new_device(adap, &info);
+ strscpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE);
+ i2c_new_client_device(adap, &info);
break;
}
}
@@ -1115,7 +1138,7 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap)
{
int i, count;
- if (dm->type != 10)
+ if (dm->type != DMI_ENTRY_ONBOARD_DEVICE)
return;
count = (dm->length - sizeof(struct dmi_header)) / 2;
@@ -1139,36 +1162,41 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap)
}
}
-/* Register optional slaves */
-static void i801_probe_optional_slaves(struct i801_priv *priv)
+/* Register optional targets */
+static void i801_probe_optional_targets(struct i801_priv *priv)
{
- /* Only register slaves on main SMBus channel */
+ /* Only register targets on main SMBus channel */
if (priv->features & FEATURE_IDF)
return;
if (apanel_addr) {
- struct i2c_board_info info;
+ struct i2c_board_info info = {
+ .addr = apanel_addr,
+ .type = "fujitsu_apanel",
+ };
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = apanel_addr;
- strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
- i2c_new_device(&priv->adapter, &info);
+ i2c_new_client_device(&priv->adapter, &info);
}
if (dmi_name_in_vendors("FUJITSU"))
dmi_walk(dmi_check_onboard_devices, &priv->adapter);
+
+ /* Instantiate SPD EEPROMs unless the SMBus is multiplexed */
+#ifdef CONFIG_I2C_I801_MUX
+ if (!priv->mux_pdev)
+#endif
+ i2c_register_spd_write_enable(&priv->adapter);
}
#else
static void __init input_apanel_init(void) {}
-static void i801_probe_optional_slaves(struct i801_priv *priv) {}
+static void i801_probe_optional_targets(struct i801_priv *priv) {}
#endif /* CONFIG_X86 && CONFIG_DMI */
-#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
+#ifdef CONFIG_I2C_I801_MUX
static struct i801_mux_config i801_mux_config_asus_z8_d12 = {
.gpio_chip = "gpio_ich",
.values = { 0x02, 0x03 },
.n_values = 2,
- .classes = { I2C_CLASS_SPD, I2C_CLASS_SPD },
.gpios = { 52, 53 },
.n_gpios = 2,
};
@@ -1177,7 +1205,6 @@ static struct i801_mux_config i801_mux_config_asus_z8_d18 = {
.gpio_chip = "gpio_ich",
.values = { 0x02, 0x03, 0x01 },
.n_values = 3,
- .classes = { I2C_CLASS_SPD, I2C_CLASS_SPD, I2C_CLASS_SPD },
.gpios = { 52, 53 },
.n_gpios = 2,
};
@@ -1249,98 +1276,145 @@ static const struct dmi_system_id mux_dmi_table[] = {
{ }
};
+static int i801_notifier_call(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct i801_priv *priv = container_of(nb, struct i801_priv, mux_notifier_block);
+ struct device *dev = data;
+
+ if (action != BUS_NOTIFY_ADD_DEVICE ||
+ dev->type != &i2c_adapter_type ||
+ i2c_root_adapter(dev) != &priv->adapter)
+ return NOTIFY_DONE;
+
+ /* Call i2c_register_spd for muxed child segments */
+ i2c_register_spd_write_enable(to_i2c_adapter(dev));
+
+ return NOTIFY_OK;
+}
+
/* Setup multiplexing if needed */
-static int i801_add_mux(struct i801_priv *priv)
+static void i801_add_mux(struct i801_priv *priv)
{
struct device *dev = &priv->adapter.dev;
const struct i801_mux_config *mux_config;
struct i2c_mux_gpio_platform_data gpio_data;
- int err;
+ struct gpiod_lookup_table *lookup;
+ const struct dmi_system_id *id;
+ int i;
- if (!priv->mux_drvdata)
- return 0;
- mux_config = priv->mux_drvdata;
+ id = dmi_first_match(mux_dmi_table);
+ if (!id)
+ return;
+
+ mux_config = id->driver_data;
/* Prepare the platform data */
memset(&gpio_data, 0, sizeof(struct i2c_mux_gpio_platform_data));
gpio_data.parent = priv->adapter.nr;
gpio_data.values = mux_config->values;
gpio_data.n_values = mux_config->n_values;
- gpio_data.classes = mux_config->classes;
- gpio_data.gpio_chip = mux_config->gpio_chip;
- gpio_data.gpios = mux_config->gpios;
- gpio_data.n_gpios = mux_config->n_gpios;
gpio_data.idle = I2C_MUX_GPIO_NO_IDLE;
- /* Register the mux device */
+ /* Register GPIO descriptor lookup table */
+ lookup = devm_kzalloc(dev,
+ struct_size(lookup, table, mux_config->n_gpios + 1),
+ GFP_KERNEL);
+ if (!lookup)
+ return;
+ lookup->dev_id = "i2c-mux-gpio";
+ for (i = 0; i < mux_config->n_gpios; i++)
+ lookup->table[i] = GPIO_LOOKUP(mux_config->gpio_chip,
+ mux_config->gpios[i], "mux", 0);
+ gpiod_add_lookup_table(lookup);
+
+ priv->mux_notifier_block.notifier_call = i801_notifier_call;
+ if (bus_register_notifier(&i2c_bus_type, &priv->mux_notifier_block))
+ return;
+ /*
+ * Register the mux device, we use PLATFORM_DEVID_NONE here
+ * because since we are referring to the GPIO chip by name we are
+ * anyways in deep trouble if there is more than one of these
+ * devices, and there should likely only be one platform controller
+ * hub.
+ */
priv->mux_pdev = platform_device_register_data(dev, "i2c-mux-gpio",
- PLATFORM_DEVID_AUTO, &gpio_data,
+ PLATFORM_DEVID_NONE, &gpio_data,
sizeof(struct i2c_mux_gpio_platform_data));
if (IS_ERR(priv->mux_pdev)) {
- err = PTR_ERR(priv->mux_pdev);
- priv->mux_pdev = NULL;
+ gpiod_remove_lookup_table(lookup);
+ devm_kfree(dev, lookup);
dev_err(dev, "Failed to register i2c-mux-gpio device\n");
- return err;
+ } else {
+ priv->lookup = lookup;
}
-
- return 0;
}
static void i801_del_mux(struct i801_priv *priv)
{
- if (priv->mux_pdev)
- platform_device_unregister(priv->mux_pdev);
+ bus_unregister_notifier(&i2c_bus_type, &priv->mux_notifier_block);
+ platform_device_unregister(priv->mux_pdev);
+ gpiod_remove_lookup_table(priv->lookup);
}
+#else
+static inline void i801_add_mux(struct i801_priv *priv) { }
+static inline void i801_del_mux(struct i801_priv *priv) { }
+#endif
-static unsigned int i801_get_adapter_class(struct i801_priv *priv)
+static struct platform_device *
+i801_add_tco_spt(struct pci_dev *pci_dev, struct resource *tco_res)
{
- const struct dmi_system_id *id;
- const struct i801_mux_config *mux_config;
- unsigned int class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- int i;
+ static const struct itco_wdt_platform_data pldata = {
+ .name = "Intel PCH",
+ .version = 4,
+ };
+ struct resource *res;
+ int ret;
- id = dmi_first_match(mux_dmi_table);
- if (id) {
- /* Remove branch classes from trunk */
- mux_config = id->driver_data;
- for (i = 0; i < mux_config->n_values; i++)
- class &= ~mux_config->classes[i];
-
- /* Remember for later */
- priv->mux_drvdata = mux_config;
- }
+ /*
+ * We must access the NO_REBOOT bit over the Primary to Sideband
+ * (P2SB) bridge.
+ */
- return class;
-}
-#else
-static inline int i801_add_mux(struct i801_priv *priv) { return 0; }
-static inline void i801_del_mux(struct i801_priv *priv) { }
+ res = &tco_res[1];
+ ret = p2sb_bar(pci_dev->bus, 0, res);
+ if (ret)
+ return ERR_PTR(ret);
-static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
-{
- return I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
+ res->start += SBREG_SMBCTRL_DNV;
+ else
+ res->start += SBREG_SMBCTRL;
+
+ res->end = res->start + 3;
+
+ return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
+ tco_res, 2, &pldata, sizeof(pldata));
}
-#endif
-static const struct itco_wdt_platform_data tco_platform_data = {
- .name = "Intel PCH",
- .version = 4,
-};
+static struct platform_device *
+i801_add_tco_cnl(struct pci_dev *pci_dev, struct resource *tco_res)
+{
+ static const struct itco_wdt_platform_data pldata = {
+ .name = "Intel PCH",
+ .version = 6,
+ };
-static DEFINE_SPINLOCK(p2sb_spinlock);
+ return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
+ tco_res, 1, &pldata, sizeof(pldata));
+}
static void i801_add_tco(struct i801_priv *priv)
{
struct pci_dev *pci_dev = priv->pci_dev;
- struct resource tco_res[3], *res;
- struct platform_device *pdev;
- unsigned int devfn;
+ struct resource tco_res[2], *res;
u32 tco_base, tco_ctl;
- u32 base_addr, ctrl_val;
- u64 base64_addr;
- u8 hidden;
- if (!(priv->features & FEATURE_TCO))
+ /* If we have ACPI based watchdog use that instead */
+ if (acpi_has_watchdog())
+ return;
+
+ if (!(priv->features & (FEATURE_TCO_SPT | FEATURE_TCO_CNL)))
return;
pci_read_config_dword(pci_dev, TCOBASE, &tco_base);
@@ -1349,81 +1423,29 @@ static void i801_add_tco(struct i801_priv *priv)
return;
memset(tco_res, 0, sizeof(tco_res));
-
- res = &tco_res[ICH_RES_IO_TCO];
- res->start = tco_base & ~1;
- res->end = res->start + 32 - 1;
- res->flags = IORESOURCE_IO;
-
/*
- * Power Management registers.
+ * Always populate the main iTCO IO resource here. The second entry
+ * for NO_REBOOT MMIO is filled by the SPT specific function.
*/
- devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 2);
- pci_bus_read_config_dword(pci_dev->bus, devfn, ACPIBASE, &base_addr);
-
- res = &tco_res[ICH_RES_IO_SMI];
- res->start = (base_addr & ~1) + ACPIBASE_SMI_OFF;
- res->end = res->start + 3;
+ res = &tco_res[0];
+ res->start = tco_base & ~1;
+ res->end = res->start + 32 - 1;
res->flags = IORESOURCE_IO;
- /*
- * Enable the ACPI I/O space.
- */
- pci_bus_read_config_dword(pci_dev->bus, devfn, ACPICTRL, &ctrl_val);
- ctrl_val |= ACPICTRL_EN;
- pci_bus_write_config_dword(pci_dev->bus, devfn, ACPICTRL, ctrl_val);
-
- /*
- * We must access the NO_REBOOT bit over the Primary to Sideband
- * bridge (P2SB). The BIOS prevents the P2SB device from being
- * enumerated by the PCI subsystem, so we need to unhide/hide it
- * to lookup the P2SB BAR.
- */
- spin_lock(&p2sb_spinlock);
-
- devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
-
- /* Unhide the P2SB device, if it is hidden */
- pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden);
- if (hidden)
- pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);
-
- pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
- base64_addr = base_addr & 0xfffffff0;
-
- pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
- base64_addr |= (u64)base_addr << 32;
-
- /* Hide the P2SB device, if it was hidden before */
- if (hidden)
- pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden);
- spin_unlock(&p2sb_spinlock);
-
- res = &tco_res[ICH_RES_MEM_OFF];
- if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
- res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL_DNV;
+ if (priv->features & FEATURE_TCO_CNL)
+ priv->tco_pdev = i801_add_tco_cnl(pci_dev, tco_res);
else
- res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
-
- res->end = res->start + 3;
- res->flags = IORESOURCE_MEM;
-
- pdev = platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
- tco_res, 3, &tco_platform_data,
- sizeof(tco_platform_data));
- if (IS_ERR(pdev)) {
- dev_warn(&pci_dev->dev, "failed to create iTCO device\n");
- return;
- }
+ priv->tco_pdev = i801_add_tco_spt(pci_dev, tco_res);
- priv->tco_pdev = pdev;
+ if (IS_ERR(priv->tco_pdev))
+ pci_warn(pci_dev, "failed to create iTCO device\n");
}
#ifdef CONFIG_ACPI
static bool i801_acpi_is_smbus_ioport(const struct i801_priv *priv,
acpi_physical_address address)
{
- return address >= priv->smba &&
+ return address >= pci_resource_start(priv->pci_dev, SMBBAR) &&
address <= pci_resource_end(priv->pci_dev, SMBBAR);
}
@@ -1440,13 +1462,13 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
* further access from the driver itself. This device is now owned
* by the system firmware.
*/
- mutex_lock(&priv->acpi_lock);
+ i2c_lock_bus(&priv->adapter, I2C_LOCK_SEGMENT);
if (!priv->acpi_reserved && i801_acpi_is_smbus_ioport(priv, address)) {
priv->acpi_reserved = true;
- dev_warn(&pdev->dev, "BIOS is accessing SMBus registers\n");
- dev_warn(&pdev->dev, "Driver SMBus register access inhibited\n");
+ pci_warn(pdev, "BIOS is accessing SMBus registers\n");
+ pci_warn(pdev, "Driver SMBus register access inhibited\n");
/*
* BIOS is accessing the host controller so prevent it from
@@ -1460,53 +1482,53 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
else
status = acpi_os_write_port(address, (u32)*value, bits);
- mutex_unlock(&priv->acpi_lock);
+ i2c_unlock_bus(&priv->adapter, I2C_LOCK_SEGMENT);
return status;
}
static int i801_acpi_probe(struct i801_priv *priv)
{
- struct acpi_device *adev;
+ acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev);
acpi_status status;
- adev = ACPI_COMPANION(&priv->pci_dev->dev);
- if (adev) {
- status = acpi_install_address_space_handler(adev->handle,
- ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler,
- NULL, priv);
- if (ACPI_SUCCESS(status))
- return 0;
- }
+ status = acpi_install_address_space_handler(ah, ACPI_ADR_SPACE_SYSTEM_IO,
+ i801_acpi_io_handler, NULL, priv);
+ if (ACPI_SUCCESS(status))
+ return 0;
return acpi_check_resource_conflict(&priv->pci_dev->resource[SMBBAR]);
}
static void i801_acpi_remove(struct i801_priv *priv)
{
- struct acpi_device *adev;
-
- adev = ACPI_COMPANION(&priv->pci_dev->dev);
- if (!adev)
- return;
+ acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev);
- acpi_remove_address_space_handler(adev->handle,
- ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
-
- mutex_lock(&priv->acpi_lock);
- if (priv->acpi_reserved)
- pm_runtime_put(&priv->pci_dev->dev);
- mutex_unlock(&priv->acpi_lock);
+ acpi_remove_address_space_handler(ah, ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
}
#else
static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
static inline void i801_acpi_remove(struct i801_priv *priv) { }
#endif
+static void i801_setup_hstcfg(struct i801_priv *priv)
+{
+ unsigned char hstcfg = priv->original_hstcfg;
+
+ hstcfg &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
+ hstcfg |= SMBHSTCFG_HST_EN;
+ pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
+}
+
+static void i801_restore_regs(struct i801_priv *priv)
+{
+ iowrite8(priv->original_hstcnt, SMBHSTCNT(priv));
+ pci_write_config_byte(priv->pci_dev, SMBHSTCFG, priv->original_hstcfg);
+}
+
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- unsigned char temp;
- int err, i;
+ int err, i, bar = SMBBAR;
struct i801_priv *priv;
priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
@@ -1515,124 +1537,74 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
i2c_set_adapdata(&priv->adapter, priv);
priv->adapter.owner = THIS_MODULE;
- priv->adapter.class = i801_get_adapter_class(priv);
+ priv->adapter.class = I2C_CLASS_HWMON;
priv->adapter.algo = &smbus_algorithm;
priv->adapter.dev.parent = &dev->dev;
- ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&dev->dev));
+ acpi_use_parent_companion(&priv->adapter.dev);
priv->adapter.retries = 3;
- mutex_init(&priv->acpi_lock);
priv->pci_dev = dev;
- switch (dev->device) {
- case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
- case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
- case PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS:
- case PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS:
- case PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS:
- case PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS:
- case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
- case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
- case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
- case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
- priv->features |= FEATURE_I2C_BLOCK_READ;
- priv->features |= FEATURE_IRQ;
- priv->features |= FEATURE_SMBUS_PEC;
- priv->features |= FEATURE_BLOCK_BUFFER;
- /* If we have ACPI based watchdog use that instead */
- if (!acpi_has_watchdog())
- priv->features |= FEATURE_TCO;
- priv->features |= FEATURE_HOST_NOTIFY;
- break;
-
- case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
- case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
- case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
- case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0:
- case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1:
- case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2:
- priv->features |= FEATURE_IDF;
- /* fall through */
- default:
- priv->features |= FEATURE_I2C_BLOCK_READ;
- priv->features |= FEATURE_IRQ;
- /* fall through */
- case PCI_DEVICE_ID_INTEL_82801DB_3:
- priv->features |= FEATURE_SMBUS_PEC;
- priv->features |= FEATURE_BLOCK_BUFFER;
- /* fall through */
- case PCI_DEVICE_ID_INTEL_82801CA_3:
- priv->features |= FEATURE_HOST_NOTIFY;
- /* fall through */
- case PCI_DEVICE_ID_INTEL_82801BA_2:
- case PCI_DEVICE_ID_INTEL_82801AB_3:
- case PCI_DEVICE_ID_INTEL_82801AA_3:
- break;
- }
+ priv->features = id->driver_data;
/* Disable features on user request */
for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) {
if (priv->features & disable_features & (1 << i))
- dev_notice(&dev->dev, "%s disabled by user\n",
- i801_feature_names[i]);
+ pci_notice(dev, "%s disabled by user\n", i801_feature_names[i]);
}
priv->features &= ~disable_features;
- err = pcim_enable_device(dev);
+ /* The block process call uses block buffer mode */
+ if (!(priv->features & FEATURE_BLOCK_BUFFER))
+ priv->features &= ~FEATURE_BLOCK_PROC;
+
+ /*
+ * Do not call pcim_enable_device(), because the device has to remain
+ * enabled on driver detach. See i801_remove() for the reasoning.
+ */
+ err = pci_enable_device(dev);
if (err) {
- dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
- err);
+ pci_err(dev, "Failed to enable SMBus PCI device (%d)\n", err);
return err;
}
- pcim_pin_device(dev);
/* Determine the address of the SMBus area */
- priv->smba = pci_resource_start(dev, SMBBAR);
- if (!priv->smba) {
- dev_err(&dev->dev,
- "SMBus base address uninitialized, upgrade BIOS\n");
+ if (!pci_resource_start(dev, SMBBAR)) {
+ pci_err(dev, "SMBus base address uninitialized, upgrade BIOS\n");
return -ENODEV;
}
if (i801_acpi_probe(priv))
return -ENODEV;
- err = pcim_iomap_regions(dev, 1 << SMBBAR,
- dev_driver_string(&dev->dev));
- if (err) {
- dev_err(&dev->dev,
- "Failed to request SMBus region 0x%lx-0x%Lx\n",
- priv->smba,
- (unsigned long long)pci_resource_end(dev, SMBBAR));
+ if (pci_resource_flags(dev, SMBBAR_MMIO) & IORESOURCE_MEM)
+ bar = SMBBAR_MMIO;
+
+ priv->smba = pcim_iomap_region(dev, bar, DRV_NAME);
+ if (IS_ERR(priv->smba)) {
+ pci_err(dev, "Failed to request SMBus region %pr\n",
+ pci_resource_n(dev, bar));
i801_acpi_remove(priv);
- return err;
+ return PTR_ERR(priv->smba);
}
- pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &temp);
- priv->original_hstcfg = temp;
- temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
- if (!(temp & SMBHSTCFG_HST_EN)) {
- dev_info(&dev->dev, "Enabling SMBus device\n");
- temp |= SMBHSTCFG_HST_EN;
- }
- pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp);
+ pci_read_config_byte(dev, SMBHSTCFG, &priv->original_hstcfg);
+ i801_setup_hstcfg(priv);
+ if (!(priv->original_hstcfg & SMBHSTCFG_HST_EN))
+ pci_info(dev, "Enabling SMBus device\n");
- if (temp & SMBHSTCFG_SMB_SMI_EN) {
- dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
+ if (priv->original_hstcfg & SMBHSTCFG_SMB_SMI_EN) {
+ pci_dbg(dev, "SMBus using interrupt SMI#\n");
/* Disable SMBus interrupt feature if SMBus using SMI# */
priv->features &= ~FEATURE_IRQ;
}
- if (temp & SMBHSTCFG_SPD_WD)
- dev_info(&dev->dev, "SPD Write Disable is set\n");
+ if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
+ pci_info(dev, "SPD Write Disable is set\n");
/* Clear special mode bits */
if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
- outb_p(inb_p(SMBAUXCTL(priv)) &
+ iowrite8(ioread8(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
- /* Remember original Host Notify setting */
- if (priv->features & FEATURE_HOST_NOTIFY)
- priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
-
/* Default timeout in interrupt mode: 200 ms */
priv->adapter.timeout = HZ / 5;
@@ -1640,54 +1612,64 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
priv->features &= ~FEATURE_IRQ;
if (priv->features & FEATURE_IRQ) {
- u16 pcictl, pcists;
+ u16 pcists;
/* Complain if an interrupt is already pending */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (pcists & SMBPCISTS_INTS)
- dev_warn(&dev->dev, "An interrupt is pending!\n");
-
- /* Check if interrupts have been disabled */
- pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
- if (pcictl & SMBPCICTL_INTDIS) {
- dev_info(&dev->dev, "Interrupts are disabled\n");
- priv->features &= ~FEATURE_IRQ;
- }
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (pcists & PCI_STATUS_INTERRUPT)
+ pci_warn(dev, "An interrupt is pending!\n");
}
if (priv->features & FEATURE_IRQ) {
- init_waitqueue_head(&priv->waitq);
+ init_completion(&priv->done);
err = devm_request_irq(&dev->dev, dev->irq, i801_isr,
- IRQF_SHARED,
- dev_driver_string(&dev->dev), priv);
+ IRQF_SHARED, DRV_NAME, priv);
if (err) {
- dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
- dev->irq, err);
+ pci_err(dev, "Failed to allocate irq %d: %d\n", dev->irq, err);
priv->features &= ~FEATURE_IRQ;
}
}
- dev_info(&dev->dev, "SMBus using %s\n",
+ pci_info(dev, "SMBus using %s\n",
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
+ /* Host notification uses an interrupt */
+ if (!(priv->features & FEATURE_IRQ))
+ priv->features &= ~FEATURE_HOST_NOTIFY;
+
+ /* Remember original Interrupt and Host Notify settings */
+ priv->original_hstcnt = ioread8(SMBHSTCNT(priv)) & ~SMBHSTCNT_KILL;
+ if (priv->features & FEATURE_HOST_NOTIFY)
+ priv->original_slvcmd = ioread8(SMBSLVCMD(priv));
+
i801_add_tco(priv);
+ /*
+ * adapter.name is used by platform code to find the main I801 adapter
+ * to instantiante i2c_clients, do not change.
+ */
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
- "SMBus I801 adapter at %04lx", priv->smba);
+ "SMBus %s adapter at %s",
+ (priv->features & FEATURE_IDF) ? "I801 IDF" : "I801",
+ pci_name(dev));
+
err = i2c_add_adapter(&priv->adapter);
if (err) {
+ platform_device_unregister(priv->tco_pdev);
i801_acpi_remove(priv);
+ i801_restore_regs(priv);
return err;
}
i801_enable_host_notify(&priv->adapter);
- i801_probe_optional_slaves(priv);
/* We ignore errors - multiplexing is optional */
i801_add_mux(priv);
+ i801_probe_optional_targets(priv);
pci_set_drvdata(dev, priv);
+ dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
pm_runtime_set_autosuspend_delay(&dev->dev, 1000);
pm_runtime_use_autosuspend(&dev->dev);
pm_runtime_put_autosuspend(&dev->dev);
@@ -1700,17 +1682,19 @@ static void i801_remove(struct pci_dev *dev)
{
struct i801_priv *priv = pci_get_drvdata(dev);
- pm_runtime_forbid(&dev->dev);
- pm_runtime_get_noresume(&dev->dev);
-
i801_disable_host_notify(priv);
i801_del_mux(priv);
i2c_del_adapter(&priv->adapter);
i801_acpi_remove(priv);
- pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
platform_device_unregister(priv->tco_pdev);
+ /* if acpi_reserved is set then usage_count is incremented already */
+ if (!priv->acpi_reserved)
+ pm_runtime_get_noresume(&dev->dev);
+
+ i801_restore_regs(priv);
+
/*
* do not call pci_disable_device(dev) since it can cause hard hangs on
* some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
@@ -1721,60 +1705,56 @@ static void i801_shutdown(struct pci_dev *dev)
{
struct i801_priv *priv = pci_get_drvdata(dev);
- /* Restore config registers to avoid hard hang on some systems */
i801_disable_host_notify(priv);
- pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
+ /* Restore config registers to avoid hard hang on some systems */
+ i801_restore_regs(priv);
}
-#ifdef CONFIG_PM_SLEEP
static int i801_suspend(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct i801_priv *priv = pci_get_drvdata(pci_dev);
+ struct i801_priv *priv = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&priv->adapter);
+ i801_restore_regs(priv);
- pci_write_config_byte(pci_dev, SMBHSTCFG, priv->original_hstcfg);
return 0;
}
static int i801_resume(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct i801_priv *priv = pci_get_drvdata(pci_dev);
+ struct i801_priv *priv = dev_get_drvdata(dev);
+ i801_setup_hstcfg(priv);
i801_enable_host_notify(&priv->adapter);
+ i2c_mark_adapter_resumed(&priv->adapter);
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume);
static struct pci_driver i801_driver = {
- .name = "i801_smbus",
+ .name = DRV_NAME,
.id_table = i801_ids,
.probe = i801_probe,
.remove = i801_remove,
.shutdown = i801_shutdown,
.driver = {
- .pm = &i801_pm_ops,
+ .pm = pm_sleep_ptr(&i801_pm_ops),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
-static int __init i2c_i801_init(void)
+static int __init i2c_i801_init(struct pci_driver *drv)
{
if (dmi_name_in_vendors("FUJITSU"))
input_apanel_init();
- return pci_register_driver(&i801_driver);
-}
-
-static void __exit i2c_i801_exit(void)
-{
- pci_unregister_driver(&i801_driver);
+ return pci_register_driver(drv);
}
-MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, Jean Delvare <jdelvare@suse.de>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");
-module_init(i2c_i801_init);
-module_exit(i2c_i801_exit);
+module_driver(i801_driver, i2c_i801_init, pci_unregister_driver);
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index d78023d42a35..6bf45d752ff9 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/i2c/busses/i2c-ibm_iic.c
*
@@ -23,12 +24,6 @@
*
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>
* and even Frodo Looijaard <frodol@dds.nl>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
@@ -42,9 +37,10 @@
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/i2c.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include "i2c-ibm_iic.h"
@@ -140,11 +136,11 @@ static void iic_dev_init(struct ibm_iic_private* dev)
DBG("%d: init\n", dev->idx);
- /* Clear master address */
+ /* Clear remote target address */
out_8(&iic->lmadr, 0);
out_8(&iic->hmadr, 0);
- /* Clear slave address */
+ /* Clear local target address */
out_8(&iic->lsadr, 0);
out_8(&iic->hsadr, 0);
@@ -341,7 +337,7 @@ static irqreturn_t iic_handler(int irq, void *dev_id)
}
/*
- * Get master transfer result and clear errors if any.
+ * Get controller transfer result and clear errors if any.
* Returns the number of actually transferred bytes or error (<0)
*/
static int iic_xfer_result(struct ibm_iic_private* dev)
@@ -356,7 +352,7 @@ static int iic_xfer_result(struct ibm_iic_private* dev)
out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD |
EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA);
- /* Flush master data buffer */
+ /* Flush controller data buffer */
out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB);
/* Is bus free?
@@ -405,7 +401,7 @@ static void iic_abort_xfer(struct ibm_iic_private* dev)
}
/*
- * Wait for master transfer to complete.
+ * Wait for controller transfer to complete.
* It puts current process to sleep until we get interrupt or timeout expires.
* Returns the number of transferred bytes or error (<0)
*/
@@ -456,9 +452,6 @@ static int iic_wait_for_tc(struct ibm_iic_private* dev){
return ret;
}
-/*
- * Low level master transfer routine
- */
static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm,
int combined_xfer)
{
@@ -515,25 +508,21 @@ static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm,
return ret > 0 ? 0 : ret;
}
-/*
- * Set target slave address for master transfer
- */
+/* Set remote target address for transfer */
static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
- u16 addr = msg->addr;
DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx,
- addr, msg->flags & I2C_M_TEN ? 10 : 7);
+ msg->addr, msg->flags & I2C_M_TEN ? 10 : 7);
- if (msg->flags & I2C_M_TEN){
+ if (msg->flags & I2C_M_TEN) {
out_8(&iic->cntl, CNTL_AMD);
- out_8(&iic->lmadr, addr);
- out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06));
- }
- else {
+ out_8(&iic->lmadr, i2c_10bit_addr_lo_from_msg(msg));
+ out_8(&iic->hmadr, i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD);
+ } else {
out_8(&iic->cntl, 0);
- out_8(&iic->lmadr, addr << 1);
+ out_8(&iic->lmadr, i2c_8bit_addr_from_msg(msg) & ~I2C_M_RD);
}
}
@@ -550,7 +539,7 @@ static inline int iic_address_neq(const struct i2c_msg* p1,
}
/*
- * Generic master transfer entrypoint.
+ * Generic transfer entrypoint.
* Returns the number of processed messages or error (<0)
*/
static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
@@ -608,11 +597,11 @@ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
}
}
else {
- /* Flush master data buffer (just in case) */
+ /* Flush controller data buffer (just in case) */
out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB);
}
- /* Load slave address */
+ /* Load target address */
iic_address(dev, &msgs[0]);
/* Do real transfer */
@@ -628,8 +617,8 @@ static u32 iic_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm iic_algo = {
- .master_xfer = iic_xfer,
- .functionality = iic_func
+ .xfer = iic_xfer,
+ .functionality = iic_func
};
/*
@@ -699,10 +688,8 @@ static int iic_probe(struct platform_device *ofdev)
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&ofdev->dev, "failed to allocate device data\n");
+ if (!dev)
return -ENOMEM;
- }
platform_set_drvdata(ofdev, dev);
@@ -743,9 +730,9 @@ static int iic_probe(struct platform_device *ofdev)
adap = &dev->adap;
adap->dev.parent = &ofdev->dev;
adap->dev.of_node = of_node_get(np);
- strlcpy(adap->name, "IBM IIC", sizeof(adap->name));
+ strscpy(adap->name, "IBM IIC", sizeof(adap->name));
i2c_set_adapdata(adap, dev);
- adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->class = I2C_CLASS_HWMON;
adap->algo = &iic_algo;
adap->timeout = HZ;
@@ -774,7 +761,7 @@ error_cleanup:
/*
* Cleanup initialized IIC interface
*/
-static int iic_remove(struct platform_device *ofdev)
+static void iic_remove(struct platform_device *ofdev)
{
struct ibm_iic_private *dev = platform_get_drvdata(ofdev);
@@ -787,8 +774,6 @@ static int iic_remove(struct platform_device *ofdev)
iounmap(dev->vaddr);
kfree(dev);
-
- return 0;
}
static const struct of_device_id ibm_iic_match[] = {
@@ -803,7 +788,7 @@ static struct platform_driver ibm_iic_driver = {
.of_match_table = ibm_iic_match,
},
.probe = iic_probe,
- .remove = iic_remove,
+ .remove = iic_remove,
};
module_platform_driver(ibm_iic_driver);
diff --git a/drivers/i2c/busses/i2c-ibm_iic.h b/drivers/i2c/busses/i2c-ibm_iic.h
index fdaa48292cb6..f0288889c70f 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.h
+++ b/drivers/i2c/busses/i2c-ibm_iic.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* drivers/i2c/busses/i2c-ibm_iic.h
*
@@ -12,12 +13,6 @@
* Matt Porter <mporter@mvista.com>
*
* Copyright 2000-2003 MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef __I2C_IBM_IIC_H_
#define __I2C_IBM_IIC_H_
diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c
new file mode 100644
index 000000000000..febcb6f01d4d
--- /dev/null
+++ b/drivers/i2c/busses/i2c-icy.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * I2C driver for stand-alone PCF8584 style adapters on Zorro cards
+ *
+ * Original ICY documentation can be found on Aminet:
+ * https://aminet.net/package/docs/hard/icy
+ *
+ * There has been a modern community re-print of this design in 2019:
+ * https://www.a1k.org/forum/index.php?threads/70106/
+ *
+ * The card is basically a Philips PCF8584 connected straight to the
+ * beginning of the AutoConfig'd address space (register S1 on base+2),
+ * with /INT on /INT2 on the Zorro bus.
+ *
+ * Copyright (c) 2019 Max Staudt <max@enpas.org>
+ *
+ * This started as a fork of i2c-elektor.c and has evolved since.
+ * Thanks go to its authors for providing a base to grow on.
+ *
+ *
+ * IRQ support is currently not implemented.
+ *
+ * As it turns out, i2c-algo-pcf is really written with i2c-elektor's
+ * edge-triggered ISA interrupts in mind, while the Amiga's Zorro bus has
+ * level-triggered interrupts. This means that once an interrupt occurs, we
+ * have to tell the PCF8584 to shut up immediately, or it will keep the
+ * interrupt line busy and cause an IRQ storm.
+
+ * However, because of the PCF8584's host-side protocol, there is no good
+ * way to just quieten it without side effects. Rather, we have to perform
+ * the next read/write operation straight away, which will reset the /INT
+ * pin. This entails re-designing the core of i2c-algo-pcf in the future.
+ * For now, we never request an IRQ from the PCF8584, and poll it instead.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-pcf.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <linux/zorro.h>
+
+#include "../algos/i2c-algo-pcf.h"
+
+struct icy_i2c {
+ struct i2c_adapter adapter;
+
+ void __iomem *reg_s0;
+ void __iomem *reg_s1;
+ struct i2c_client *ltc2990_client;
+};
+
+/*
+ * Functions called by i2c-algo-pcf
+ */
+static void icy_pcf_setpcf(void *data, int ctl, int val)
+{
+ struct icy_i2c *i2c = (struct icy_i2c *)data;
+
+ u8 __iomem *address = ctl ? i2c->reg_s1 : i2c->reg_s0;
+
+ z_writeb(val, address);
+}
+
+static int icy_pcf_getpcf(void *data, int ctl)
+{
+ struct icy_i2c *i2c = (struct icy_i2c *)data;
+
+ u8 __iomem *address = ctl ? i2c->reg_s1 : i2c->reg_s0;
+
+ return z_readb(address);
+}
+
+static int icy_pcf_getown(void *data)
+{
+ return 0x55;
+}
+
+static int icy_pcf_getclock(void *data)
+{
+ return 0x1c;
+}
+
+static void icy_pcf_waitforpin(void *data)
+{
+ usleep_range(50, 150);
+}
+
+/*
+ * Main i2c-icy part
+ */
+static unsigned short const icy_ltc2990_addresses[] = {
+ 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END
+};
+
+/*
+ * Additional sensors exposed once this property is applied:
+ *
+ * in1 will be the voltage of the 5V rail, divided by 2.
+ * in2 will be the voltage of the 12V rail, divided by 4.
+ * temp3 will be measured using a PCB loop next the chip.
+ */
+static const u32 icy_ltc2990_meas_mode[] = {0, 3};
+
+static const struct property_entry icy_ltc2990_props[] = {
+ PROPERTY_ENTRY_U32_ARRAY("lltc,meas-mode", icy_ltc2990_meas_mode),
+ { }
+};
+
+static const struct software_node icy_ltc2990_node = {
+ .properties = icy_ltc2990_props,
+};
+
+static int icy_probe(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ struct icy_i2c *i2c;
+ struct i2c_algo_pcf_data *algo_data;
+ struct i2c_board_info ltc2990_info = {
+ .type = "ltc2990",
+ .swnode = &icy_ltc2990_node,
+ };
+
+ i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ algo_data = devm_kzalloc(&z->dev, sizeof(*algo_data), GFP_KERNEL);
+ if (!algo_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&z->dev, i2c);
+ i2c->adapter.dev.parent = &z->dev;
+ i2c->adapter.owner = THIS_MODULE;
+ /* i2c->adapter.algo assigned by i2c_pcf_add_bus() */
+ i2c->adapter.algo_data = algo_data;
+ strscpy(i2c->adapter.name, "ICY I2C Zorro adapter",
+ sizeof(i2c->adapter.name));
+
+ if (!devm_request_mem_region(&z->dev,
+ z->resource.start,
+ 4, i2c->adapter.name))
+ return -ENXIO;
+
+ /* Driver private data */
+ i2c->reg_s0 = ZTWO_VADDR(z->resource.start);
+ i2c->reg_s1 = ZTWO_VADDR(z->resource.start + 2);
+
+ algo_data->data = i2c;
+ algo_data->setpcf = icy_pcf_setpcf;
+ algo_data->getpcf = icy_pcf_getpcf;
+ algo_data->getown = icy_pcf_getown;
+ algo_data->getclock = icy_pcf_getclock;
+ algo_data->waitforpin = icy_pcf_waitforpin;
+
+ if (i2c_pcf_add_bus(&i2c->adapter)) {
+ dev_err(&z->dev, "i2c_pcf_add_bus() failed\n");
+ return -ENXIO;
+ }
+
+ dev_info(&z->dev, "ICY I2C controller at %pa, IRQ not implemented\n",
+ &z->resource.start);
+
+ /*
+ * The 2019 a1k.org PCBs have an LTC2990 at 0x4c, so start
+ * it automatically once ltc2990 is modprobed.
+ *
+ * in0 is the voltage of the internal 5V power supply.
+ * temp1 is the temperature inside the chip.
+ *
+ * See property_entry above for in1, in2, temp3.
+ */
+ i2c->ltc2990_client = i2c_new_scanned_device(&i2c->adapter,
+ &ltc2990_info,
+ icy_ltc2990_addresses,
+ NULL);
+ return 0;
+}
+
+static void icy_remove(struct zorro_dev *z)
+{
+ struct icy_i2c *i2c = dev_get_drvdata(&z->dev);
+
+ i2c_unregister_device(i2c->ltc2990_client);
+ i2c_del_adapter(&i2c->adapter);
+}
+
+static const struct zorro_device_id icy_zorro_tbl[] = {
+ { ZORRO_ID(VMC, 15, 0), },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(zorro, icy_zorro_tbl);
+
+static struct zorro_driver icy_driver = {
+ .name = "i2c-icy",
+ .id_table = icy_zorro_tbl,
+ .probe = icy_probe,
+ .remove = icy_remove,
+};
+
+module_driver(icy_driver,
+ zorro_register_driver,
+ zorro_unregister_driver);
+
+MODULE_AUTHOR("Max Staudt <max@enpas.org>");
+MODULE_DESCRIPTION("I2C bus via PCF8584 on ICY Zorro card");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c
index f038858b6c54..88192c25c44c 100644
--- a/drivers/i2c/busses/i2c-img-scb.c
+++ b/drivers/i2c/busses/i2c-img-scb.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C adapter for the IMG Serial Control Bus (SCB) IP block.
*
* Copyright (C) 2009, 2010, 2012, 2014 Imagination Technologies Ltd.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* There are three ways that this I2C controller can be driven:
*
* - Raw control of the SDA and SCK signals.
@@ -260,7 +257,7 @@
#define IMG_I2C_TIMEOUT (msecs_to_jiffies(1000))
/*
- * Worst incs are 1 (innacurate) and 16*256 (irregular).
+ * Worst incs are 1 (inaccurate) and 16*256 (irregular).
* So a sensible inc is the logarithmic mean: 64 (2^6), which is
* in the middle of the valid range (0-127).
*/
@@ -307,7 +304,7 @@ static struct img_i2c_timings timings[] = {
/* Standard mode */
{
.name = "standard",
- .max_bitrate = 100000,
+ .max_bitrate = I2C_MAX_STANDARD_MODE_FREQ,
.tckh = 4000,
.tckl = 4700,
.tsdh = 4700,
@@ -319,7 +316,7 @@ static struct img_i2c_timings timings[] = {
/* Fast mode */
{
.name = "fast",
- .max_bitrate = 400000,
+ .max_bitrate = I2C_MAX_FAST_MODE_FREQ,
.tckh = 600,
.tckl = 1300,
.tsdh = 600,
@@ -834,7 +831,7 @@ next_atomic_cmd:
*/
static void img_i2c_check_timer(struct timer_list *t)
{
- struct img_i2c *i2c = from_timer(i2c, t, check_timer);
+ struct img_i2c *i2c = timer_container_of(i2c, t, check_timer);
unsigned long flags;
unsigned int line_status;
@@ -916,7 +913,7 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c,
static irqreturn_t img_i2c_isr(int irq, void *dev_id)
{
- struct img_i2c *i2c = (struct img_i2c *)dev_id;
+ struct img_i2c *i2c = dev_id;
u32 int_status, line_status;
/* We handle transaction completion AFTER accessing registers */
unsigned int hret;
@@ -1060,7 +1057,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
atomic = true;
}
- ret = pm_runtime_get_sync(adap->dev.parent);
+ ret = pm_runtime_resume_and_get(adap->dev.parent);
if (ret < 0)
return ret;
@@ -1125,19 +1122,15 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
time_left = wait_for_completion_timeout(&i2c->msg_complete,
IMG_I2C_TIMEOUT);
- del_timer_sync(&i2c->check_timer);
+ timer_delete_sync(&i2c->check_timer);
- if (time_left == 0) {
- dev_err(adap->dev.parent, "i2c transfer timed out\n");
+ if (time_left == 0)
i2c->msg_status = -ETIMEDOUT;
- break;
- }
if (i2c->msg_status)
break;
}
- pm_runtime_mark_last_busy(adap->dev.parent);
pm_runtime_put_autosuspend(adap->dev.parent);
return i2c->msg_status ? i2c->msg_status : num;
@@ -1149,7 +1142,7 @@ static u32 img_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm img_i2c_algo = {
- .master_xfer = img_i2c_xfer,
+ .xfer = img_i2c_xfer,
.functionality = img_i2c_func,
};
@@ -1161,7 +1154,7 @@ static int img_i2c_init(struct img_i2c *i2c)
u32 rev;
int ret;
- ret = pm_runtime_get_sync(i2c->adap.dev.parent);
+ ret = pm_runtime_resume_and_get(i2c->adap.dev.parent);
if (ret < 0)
return ret;
@@ -1171,7 +1164,6 @@ static int img_i2c_init(struct img_i2c *i2c)
"Unknown hardware revision (%d.%d.%d.%d)\n",
(rev >> 24) & 0xff, (rev >> 16) & 0xff,
(rev >> 8) & 0xff, rev & 0xff);
- pm_runtime_mark_last_busy(i2c->adap.dev.parent);
pm_runtime_put_autosuspend(i2c->adap.dev.parent);
return -EINVAL;
}
@@ -1323,7 +1315,6 @@ static int img_i2c_init(struct img_i2c *i2c)
/* Perform a synchronous sequence to reset the bus */
ret = img_i2c_reset_bus(i2c);
- pm_runtime_mark_last_busy(i2c->adap.dev.parent);
pm_runtime_put_autosuspend(i2c->adap.dev.parent);
return ret;
@@ -1333,7 +1324,6 @@ static int img_i2c_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct img_i2c *i2c;
- struct resource *res;
int irq, ret;
u32 val;
@@ -1341,16 +1331,13 @@ static int img_i2c_probe(struct platform_device *pdev)
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "can't get irq number\n");
+ if (irq < 0)
return irq;
- }
i2c->sys_clk = devm_clk_get(&pdev->dev, "sys");
if (IS_ERR(i2c->sys_clk)) {
@@ -1420,7 +1407,7 @@ rpm_disable:
return ret;
}
-static int img_i2c_remove(struct platform_device *dev)
+static void img_i2c_remove(struct platform_device *dev)
{
struct img_i2c *i2c = platform_get_drvdata(dev);
@@ -1428,8 +1415,6 @@ static int img_i2c_remove(struct platform_device *dev)
pm_runtime_disable(&dev->dev);
if (!pm_runtime_status_suspended(&dev->dev))
img_i2c_runtime_suspend(&dev->dev);
-
- return 0;
}
static int img_i2c_runtime_suspend(struct device *dev)
@@ -1463,7 +1448,6 @@ static int img_i2c_runtime_resume(struct device *dev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int img_i2c_suspend(struct device *dev)
{
struct img_i2c *i2c = dev_get_drvdata(dev);
@@ -1491,13 +1475,10 @@ static int img_i2c_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops img_i2c_pm = {
- SET_RUNTIME_PM_OPS(img_i2c_runtime_suspend,
- img_i2c_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(img_i2c_suspend, img_i2c_resume)
+ RUNTIME_PM_OPS(img_i2c_runtime_suspend, img_i2c_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_i2c_suspend, img_i2c_resume)
};
static const struct of_device_id img_scb_i2c_match[] = {
@@ -1510,7 +1491,7 @@ static struct platform_driver img_scb_i2c_driver = {
.driver = {
.name = "img-i2c-scb",
.of_match_table = img_scb_i2c_match,
- .pm = &img_i2c_pm,
+ .pm = pm_ptr(&img_i2c_pm),
},
.probe = img_i2c_probe,
.remove = img_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index 06c4c767af32..2a0962a0b441 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -8,16 +8,18 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -30,6 +32,7 @@
#define LPI2C_MCR 0x10 /* i2c contrl register */
#define LPI2C_MSR 0x14 /* i2c status register */
#define LPI2C_MIER 0x18 /* i2c interrupt enable */
+#define LPI2C_MDER 0x1C /* i2c DMA enable */
#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
@@ -41,6 +44,20 @@
#define LPI2C_MTDR 0x60 /* i2c master TX data register */
#define LPI2C_MRDR 0x70 /* i2c master RX data register */
+#define LPI2C_SCR 0x110 /* i2c target control register */
+#define LPI2C_SSR 0x114 /* i2c target status register */
+#define LPI2C_SIER 0x118 /* i2c target interrupt enable */
+#define LPI2C_SDER 0x11C /* i2c target DMA enable */
+#define LPI2C_SCFGR0 0x120 /* i2c target configuration */
+#define LPI2C_SCFGR1 0x124 /* i2c target configuration */
+#define LPI2C_SCFGR2 0x128 /* i2c target configuration */
+#define LPI2C_SAMR 0x140 /* i2c target address match */
+#define LPI2C_SASR 0x150 /* i2c target address status */
+#define LPI2C_STAR 0x154 /* i2c target transmit ACK */
+#define LPI2C_STDR 0x160 /* i2c target transmit data */
+#define LPI2C_SRDR 0x170 /* i2c target receive data */
+#define LPI2C_SRDROR 0x178 /* i2c target receive data read only */
+
/* i2c command */
#define TRAN_DATA 0X00
#define RECV_DATA 0X01
@@ -71,17 +88,50 @@
#define MCFGR1_AUTOSTOP BIT(8)
#define MCFGR1_IGNACK BIT(9)
#define MRDR_RXEMPTY BIT(14)
+#define MDER_TDDE BIT(0)
+#define MDER_RDDE BIT(1)
+
+#define SCR_SEN BIT(0)
+#define SCR_RST BIT(1)
+#define SCR_FILTEN BIT(4)
+#define SCR_RTF BIT(8)
+#define SCR_RRF BIT(9)
+#define SSR_TDF BIT(0)
+#define SSR_RDF BIT(1)
+#define SSR_AVF BIT(2)
+#define SSR_TAF BIT(3)
+#define SSR_RSF BIT(8)
+#define SSR_SDF BIT(9)
+#define SSR_BEF BIT(10)
+#define SSR_FEF BIT(11)
+#define SSR_SBF BIT(24)
+#define SSR_BBF BIT(25)
+#define SSR_CLEAR_BITS (SSR_RSF | SSR_SDF | SSR_BEF | SSR_FEF)
+#define SIER_TDIE BIT(0)
+#define SIER_RDIE BIT(1)
+#define SIER_AVIE BIT(2)
+#define SIER_TAIE BIT(3)
+#define SIER_RSIE BIT(8)
+#define SIER_SDIE BIT(9)
+#define SIER_BEIE BIT(10)
+#define SIER_FEIE BIT(11)
+#define SIER_AM0F BIT(12)
+#define SCFGR1_RXSTALL BIT(1)
+#define SCFGR1_TXDSTALL BIT(2)
+#define SCFGR2_FILTSDA_SHIFT 24
+#define SCFGR2_FILTSCL_SHIFT 16
+#define SCFGR2_CLKHOLD(x) (x)
+#define SCFGR2_FILTSDA(x) ((x) << SCFGR2_FILTSDA_SHIFT)
+#define SCFGR2_FILTSCL(x) ((x) << SCFGR2_FILTSCL_SHIFT)
+#define SASR_READ_REQ 0x1
+#define SLAVE_INT_FLAG (SIER_TDIE | SIER_RDIE | SIER_AVIE | \
+ SIER_SDIE | SIER_BEIE)
#define I2C_CLK_RATIO 2
#define CHUNK_DATA 256
-#define LPI2C_DEFAULT_RATE 100000
-#define STARDARD_MAX_BITRATE 400000
-#define FAST_MAX_BITRATE 1000000
-#define FAST_PLUS_MAX_BITRATE 3400000
-#define HIGHSPEED_MAX_BITRATE 5000000
-
#define I2C_PM_TIMEOUT 10 /* ms */
+#define I2C_DMA_THRESHOLD 8 /* bytes */
enum lpi2c_imx_mode {
STANDARD, /* 100+Kbps */
@@ -98,13 +148,33 @@ enum lpi2c_imx_pincfg {
FOUR_PIN_PP,
};
+struct lpi2c_imx_dma {
+ bool using_pio_mode;
+ u8 rx_cmd_buf_len;
+ u8 *dma_buf;
+ u16 *rx_cmd_buf;
+ unsigned int dma_len;
+ unsigned int tx_burst_num;
+ unsigned int rx_burst_num;
+ unsigned long dma_msg_flag;
+ resource_size_t phy_addr;
+ dma_addr_t dma_tx_addr;
+ dma_addr_t dma_addr;
+ enum dma_data_direction dma_data_dir;
+ enum dma_transfer_direction dma_transfer_dir;
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+};
+
struct lpi2c_imx_struct {
struct i2c_adapter adapter;
- struct clk *clk;
+ int num_clks;
+ struct clk_bulk_data *clks;
void __iomem *base;
__u8 *rx_buf;
__u8 *tx_buf;
struct completion complete;
+ unsigned long rate_per;
unsigned int msglen;
unsigned int delivered;
unsigned int block_data;
@@ -112,53 +182,66 @@ struct lpi2c_imx_struct {
unsigned int txfifosize;
unsigned int rxfifosize;
enum lpi2c_imx_mode mode;
+ struct i2c_bus_recovery_info rinfo;
+ bool can_use_dma;
+ struct lpi2c_imx_dma *dma;
+ struct i2c_client *target;
};
+#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) \
+ (atomic ? readl_poll_timeout_atomic(lpi2c_imx->base + LPI2C_MSR, val, \
+ cond, 0, 500000) : \
+ readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val, cond, \
+ 0, 500000))
+
static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
unsigned int enable)
{
writel(enable, lpi2c_imx->base + LPI2C_MIER);
}
-static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
+static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
- unsigned long orig_jiffies = jiffies;
unsigned int temp;
+ int err;
- while (1) {
- temp = readl(lpi2c_imx->base + LPI2C_MSR);
-
- /* check for arbitration lost, clear if set */
- if (temp & MSR_ALF) {
- writel(temp, lpi2c_imx->base + LPI2C_MSR);
- return -EAGAIN;
- }
+ err = lpi2c_imx_read_msr_poll_timeout(atomic, temp,
+ temp & (MSR_ALF | MSR_BBF | MSR_MBF));
- if (temp & (MSR_BBF | MSR_MBF))
- break;
+ /* check for arbitration lost, clear if set */
+ if (temp & MSR_ALF) {
+ writel(temp, lpi2c_imx->base + LPI2C_MSR);
+ return -EAGAIN;
+ }
- if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
- dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
- return -ETIMEDOUT;
- }
- schedule();
+ /* check for bus not busy */
+ if (err) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
+ return -ETIMEDOUT;
}
return 0;
}
+static u32 lpi2c_imx_txfifo_cnt(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ return readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+}
+
static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
{
unsigned int bitrate = lpi2c_imx->bitrate;
enum lpi2c_imx_mode mode;
- if (bitrate < STARDARD_MAX_BITRATE)
+ if (bitrate < I2C_MAX_FAST_MODE_FREQ)
mode = STANDARD;
- else if (bitrate < FAST_MAX_BITRATE)
+ else if (bitrate < I2C_MAX_FAST_MODE_PLUS_FREQ)
mode = FAST;
- else if (bitrate < FAST_PLUS_MAX_BITRATE)
+ else if (bitrate < I2C_MAX_HIGH_SPEED_MODE_FREQ)
mode = FAST_PLUS;
- else if (bitrate < HIGHSPEED_MAX_BITRATE)
+ else if (bitrate < I2C_MAX_ULTRA_FAST_MODE_FREQ)
mode = HS;
else
mode = ULTRA_FAST;
@@ -167,7 +250,7 @@ static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
}
static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
- struct i2c_msg *msgs)
+ struct i2c_msg *msgs, bool atomic)
{
unsigned int temp;
@@ -179,41 +262,37 @@ static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8);
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
- return lpi2c_imx_bus_busy(lpi2c_imx);
+ return lpi2c_imx_bus_busy(lpi2c_imx, atomic);
}
-static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
+static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
- unsigned long orig_jiffies = jiffies;
unsigned int temp;
+ int err;
writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
- do {
- temp = readl(lpi2c_imx->base + LPI2C_MSR);
- if (temp & MSR_SDF)
- break;
-
- if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
- dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
- break;
- }
- schedule();
+ err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & MSR_SDF);
- } while (1);
+ if (err) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
+ }
}
/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
{
- u8 prescale, filt, sethold, clkhi, clklo, datavd;
- unsigned int clk_rate, clk_cycle;
+ u8 prescale, filt, sethold, datavd;
+ unsigned int clk_rate, clk_cycle, clkhi, clklo;
enum lpi2c_imx_pincfg pincfg;
unsigned int temp;
lpi2c_imx_set_mode(lpi2c_imx);
- clk_rate = clk_get_rate(lpi2c_imx->clk);
+ clk_rate = lpi2c_imx->rate_per;
+
if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
filt = 0;
else
@@ -222,7 +301,7 @@ static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
for (prescale = 0; prescale <= 7; prescale++) {
clk_cycle = clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
- 3 - (filt >> 1);
- clkhi = (clk_cycle + I2C_CLK_RATIO) / (I2C_CLK_RATIO + 1);
+ clkhi = DIV_ROUND_UP(clk_cycle, I2C_CLK_RATIO + 1);
clklo = clk_cycle - clkhi;
if (clklo < 64)
break;
@@ -265,7 +344,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
unsigned int temp;
int ret;
- ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent);
+ ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent);
if (ret < 0)
return ret;
@@ -284,7 +363,6 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
return 0;
rpm_put:
- pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent);
pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent);
return ret;
@@ -298,41 +376,39 @@ static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
temp &= ~MCR_MEN;
writel(temp, lpi2c_imx->base + LPI2C_MCR);
- pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent);
pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent);
return 0;
}
-static int lpi2c_imx_msg_complete(struct lpi2c_imx_struct *lpi2c_imx)
+static int lpi2c_imx_pio_msg_complete(struct lpi2c_imx_struct *lpi2c_imx)
{
- unsigned long timeout;
+ unsigned long time_left;
- timeout = wait_for_completion_timeout(&lpi2c_imx->complete, HZ);
+ time_left = wait_for_completion_timeout(&lpi2c_imx->complete, HZ);
- return timeout ? 0 : -ETIMEDOUT;
+ return time_left ? 0 : -ETIMEDOUT;
}
-static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
+static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
- unsigned long orig_jiffies = jiffies;
- u32 txcnt;
-
- do {
- txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+ unsigned int temp;
+ int err;
- if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
- dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
- return -EIO;
- }
+ err = lpi2c_imx_read_msr_poll_timeout(atomic, temp,
+ (temp & MSR_NDF) || !lpi2c_imx_txfifo_cnt(lpi2c_imx));
- if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
- dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
- return -ETIMEDOUT;
- }
- schedule();
+ if (temp & MSR_NDF) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
+ return -EIO;
+ }
- } while (txcnt);
+ if (err) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
+ return -ETIMEDOUT;
+ }
return 0;
}
@@ -356,7 +432,7 @@ static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR);
}
-static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
+static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
unsigned int data, txcnt;
@@ -371,13 +447,19 @@ static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
txcnt++;
}
- if (lpi2c_imx->delivered < lpi2c_imx->msglen)
- lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE);
- else
+ if (lpi2c_imx->delivered < lpi2c_imx->msglen) {
+ if (!atomic)
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE);
+ return false;
+ }
+
+ if (!atomic)
complete(&lpi2c_imx->complete);
+
+ return true;
}
-static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
+static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
unsigned int blocklen, remaining;
unsigned int temp, data;
@@ -402,8 +484,9 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
if (!remaining) {
- complete(&lpi2c_imx->complete);
- return;
+ if (!atomic)
+ complete(&lpi2c_imx->complete);
+ return true;
}
/* not finished, still waiting for rx data */
@@ -421,7 +504,10 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
}
- lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE);
+ if (!atomic)
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE);
+
+ return false;
}
static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
@@ -429,11 +515,29 @@ static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
{
lpi2c_imx->tx_buf = msgs->buf;
lpi2c_imx_set_tx_watermark(lpi2c_imx);
- lpi2c_imx_write_txfifo(lpi2c_imx);
+ lpi2c_imx_write_txfifo(lpi2c_imx, false);
+}
+
+static int lpi2c_imx_write_atomic(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ u32 temp;
+ int err;
+
+ lpi2c_imx->tx_buf = msgs->buf;
+
+ err = lpi2c_imx_read_msr_poll_timeout(true, temp,
+ (temp & MSR_NDF) ||
+ lpi2c_imx_write_txfifo(lpi2c_imx, true));
+
+ if (temp & MSR_NDF)
+ return -EIO;
+
+ return err;
}
-static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
- struct i2c_msg *msgs)
+static void lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
{
unsigned int temp;
@@ -444,12 +548,479 @@ static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
temp |= (RECV_DATA << 8);
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+}
+
+static bool lpi2c_imx_read_chunk_atomic(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 rxcnt;
+
+ rxcnt = (readl(lpi2c_imx->base + LPI2C_MFSR) >> 16) & 0xFF;
+ if (!rxcnt)
+ return false;
+
+ if (!lpi2c_imx_read_rxfifo(lpi2c_imx, true))
+ return false;
+
+ return true;
+}
+
+static int lpi2c_imx_read_atomic(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ u32 temp;
+ int tmo_us;
+
+ tmo_us = 1000000;
+ do {
+ if (lpi2c_imx_read_chunk_atomic(lpi2c_imx))
+ return 0;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+
+ if (temp & MSR_NDF)
+ return -EIO;
+
+ udelay(100);
+ tmo_us -= 100;
+ } while (tmo_us > 0);
+
+ return -ETIMEDOUT;
+}
+
+static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg)
+{
+ if (!lpi2c_imx->can_use_dma)
+ return false;
+
+ /*
+ * When the length of data is less than I2C_DMA_THRESHOLD,
+ * cpu mode is used directly to avoid low performance.
+ */
+ return !(msg->len < I2C_DMA_THRESHOLD);
+}
+
+static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ reinit_completion(&lpi2c_imx->complete);
+
+ if (msg->flags & I2C_M_RD) {
+ lpi2c_imx_read_init(lpi2c_imx, msg);
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
+ } else {
+ lpi2c_imx_write(lpi2c_imx, msg);
+ }
+
+ return lpi2c_imx_pio_msg_complete(lpi2c_imx);
+}
+
+static int lpi2c_imx_pio_xfer_atomic(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ if (msg->flags & I2C_M_RD) {
+ lpi2c_imx_read_init(lpi2c_imx, msg);
+ return lpi2c_imx_read_atomic(lpi2c_imx, msg);
+ }
+
+ return lpi2c_imx_write_atomic(lpi2c_imx, msg);
+}
+
+static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned long time = 0;
+
+ time = 8 * lpi2c_imx->dma->dma_len * 1000 / lpi2c_imx->bitrate;
+
+ /* Add extra second for scheduler related activities */
+ time += 1;
+
+ /* Double calculated time */
+ return secs_to_jiffies(time);
+}
+
+static int lpi2c_imx_alloc_rx_cmd_buf(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ u16 rx_remain = dma->dma_len;
+ int cmd_num;
+ u16 temp;
+
+ /*
+ * Calculate the number of rx command words via the DMA TX channel
+ * writing into command register based on the i2c msg len, and build
+ * the rx command words buffer.
+ */
+ cmd_num = DIV_ROUND_UP(rx_remain, CHUNK_DATA);
+ dma->rx_cmd_buf = kcalloc(cmd_num, sizeof(u16), GFP_KERNEL);
+ dma->rx_cmd_buf_len = cmd_num * sizeof(u16);
+
+ if (!dma->rx_cmd_buf) {
+ dev_err(&lpi2c_imx->adapter.dev, "Alloc RX cmd buffer failed\n");
+ return -ENOMEM;
+ }
+
+ for (int i = 0; i < cmd_num ; i++) {
+ temp = rx_remain > CHUNK_DATA ? CHUNK_DATA - 1 : rx_remain - 1;
+ temp |= (RECV_DATA << 8);
+ rx_remain -= CHUNK_DATA;
+ dma->rx_cmd_buf[i] = temp;
+ }
+
+ return 0;
+}
+
+static int lpi2c_imx_dma_msg_complete(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned long time_left, time;
- lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
+ time = lpi2c_imx_dma_timeout_calculate(lpi2c_imx);
+ time_left = wait_for_completion_timeout(&lpi2c_imx->complete, time);
+ if (time_left == 0) {
+ dev_err(&lpi2c_imx->adapter.dev, "I/O Error in DMA Data Transfer\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void lpi2c_dma_unmap(struct lpi2c_imx_dma *dma)
+{
+ struct dma_chan *chan = dma->dma_data_dir == DMA_FROM_DEVICE
+ ? dma->chan_rx : dma->chan_tx;
+
+ dma_unmap_single(chan->device->dev, dma->dma_addr,
+ dma->dma_len, dma->dma_data_dir);
+
+ dma->dma_data_dir = DMA_NONE;
+}
+
+static void lpi2c_cleanup_rx_cmd_dma(struct lpi2c_imx_dma *dma)
+{
+ dmaengine_terminate_sync(dma->chan_tx);
+ dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr,
+ dma->rx_cmd_buf_len, DMA_TO_DEVICE);
}
-static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
- struct i2c_msg *msgs, int num)
+static void lpi2c_cleanup_dma(struct lpi2c_imx_dma *dma)
+{
+ if (dma->dma_data_dir == DMA_FROM_DEVICE)
+ dmaengine_terminate_sync(dma->chan_rx);
+ else if (dma->dma_data_dir == DMA_TO_DEVICE)
+ dmaengine_terminate_sync(dma->chan_tx);
+
+ lpi2c_dma_unmap(dma);
+}
+
+static void lpi2c_dma_callback(void *data)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = (struct lpi2c_imx_struct *)data;
+
+ complete(&lpi2c_imx->complete);
+}
+
+static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct dma_async_tx_descriptor *rx_cmd_desc;
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ struct dma_chan *txchan = dma->chan_tx;
+ dma_cookie_t cookie;
+
+ dma->dma_tx_addr = dma_map_single(txchan->device->dev,
+ dma->rx_cmd_buf, dma->rx_cmd_buf_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(txchan->device->dev, dma->dma_tx_addr)) {
+ dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n");
+ return -EINVAL;
+ }
+
+ rx_cmd_desc = dmaengine_prep_slave_single(txchan, dma->dma_tx_addr,
+ dma->rx_cmd_buf_len, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rx_cmd_desc) {
+ dev_err(&lpi2c_imx->adapter.dev, "DMA prep slave sg failed, use pio\n");
+ goto desc_prepare_err_exit;
+ }
+
+ cookie = dmaengine_submit(rx_cmd_desc);
+ if (dma_submit_error(cookie)) {
+ dev_err(&lpi2c_imx->adapter.dev, "submitting DMA failed, use pio\n");
+ goto submit_err_exit;
+ }
+
+ dma_async_issue_pending(txchan);
+
+ return 0;
+
+desc_prepare_err_exit:
+ dma_unmap_single(txchan->device->dev, dma->dma_tx_addr,
+ dma->rx_cmd_buf_len, DMA_TO_DEVICE);
+ return -EINVAL;
+
+submit_err_exit:
+ dma_unmap_single(txchan->device->dev, dma->dma_tx_addr,
+ dma->rx_cmd_buf_len, DMA_TO_DEVICE);
+ dmaengine_desc_free(rx_cmd_desc);
+ return -EINVAL;
+}
+
+static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+
+ if (dma->dma_msg_flag & I2C_M_RD) {
+ chan = dma->chan_rx;
+ dma->dma_data_dir = DMA_FROM_DEVICE;
+ dma->dma_transfer_dir = DMA_DEV_TO_MEM;
+ } else {
+ chan = dma->chan_tx;
+ dma->dma_data_dir = DMA_TO_DEVICE;
+ dma->dma_transfer_dir = DMA_MEM_TO_DEV;
+ }
+
+ dma->dma_addr = dma_map_single(chan->device->dev,
+ dma->dma_buf, dma->dma_len, dma->dma_data_dir);
+ if (dma_mapping_error(chan->device->dev, dma->dma_addr)) {
+ dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n");
+ return -EINVAL;
+ }
+
+ desc = dmaengine_prep_slave_single(chan, dma->dma_addr,
+ dma->dma_len, dma->dma_transfer_dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(&lpi2c_imx->adapter.dev, "DMA prep slave sg failed, use pio\n");
+ goto desc_prepare_err_exit;
+ }
+
+ reinit_completion(&lpi2c_imx->complete);
+ desc->callback = lpi2c_dma_callback;
+ desc->callback_param = lpi2c_imx;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ dev_err(&lpi2c_imx->adapter.dev, "submitting DMA failed, use pio\n");
+ goto submit_err_exit;
+ }
+
+ /* Can't switch to PIO mode when DMA have started transfer */
+ dma->using_pio_mode = false;
+
+ dma_async_issue_pending(chan);
+
+ return 0;
+
+desc_prepare_err_exit:
+ lpi2c_dma_unmap(dma);
+ return -EINVAL;
+
+submit_err_exit:
+ lpi2c_dma_unmap(dma);
+ dmaengine_desc_free(desc);
+ return -EINVAL;
+}
+
+static int lpi2c_imx_find_max_burst_num(unsigned int fifosize, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = fifosize / 2; i > 0; i--)
+ if (!(len % i))
+ break;
+
+ return i;
+}
+
+/*
+ * For a highest DMA efficiency, tx/rx burst number should be calculated according
+ * to the FIFO depth.
+ */
+static void lpi2c_imx_dma_burst_num_calculate(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ unsigned int cmd_num;
+
+ if (dma->dma_msg_flag & I2C_M_RD) {
+ /*
+ * One RX cmd word can trigger DMA receive no more than 256 bytes.
+ * The number of RX cmd words should be calculated based on the data
+ * length.
+ */
+ cmd_num = DIV_ROUND_UP(dma->dma_len, CHUNK_DATA);
+ dma->tx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->txfifosize,
+ cmd_num);
+ dma->rx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->rxfifosize,
+ dma->dma_len);
+ } else {
+ dma->tx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->txfifosize,
+ dma->dma_len);
+ }
+}
+
+static int lpi2c_dma_config(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ struct dma_slave_config rx = {}, tx = {};
+ int ret;
+
+ lpi2c_imx_dma_burst_num_calculate(lpi2c_imx);
+
+ if (dma->dma_msg_flag & I2C_M_RD) {
+ tx.dst_addr = dma->phy_addr + LPI2C_MTDR;
+ tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ tx.dst_maxburst = dma->tx_burst_num;
+ tx.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma->chan_tx, &tx);
+ if (ret < 0)
+ return ret;
+
+ rx.src_addr = dma->phy_addr + LPI2C_MRDR;
+ rx.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ rx.src_maxburst = dma->rx_burst_num;
+ rx.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(dma->chan_rx, &rx);
+ if (ret < 0)
+ return ret;
+ } else {
+ tx.dst_addr = dma->phy_addr + LPI2C_MTDR;
+ tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ tx.dst_maxburst = dma->tx_burst_num;
+ tx.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma->chan_tx, &tx);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void lpi2c_dma_enable(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ /*
+ * TX interrupt will be triggered when the number of words in
+ * the transmit FIFO is equal or less than TX watermark.
+ * RX interrupt will be triggered when the number of words in
+ * the receive FIFO is greater than RX watermark.
+ * In order to trigger the DMA interrupt, TX watermark should be
+ * set equal to the DMA TX burst number but RX watermark should
+ * be set less than the DMA RX burst number.
+ */
+ if (dma->dma_msg_flag & I2C_M_RD) {
+ /* Set I2C TX/RX watermark */
+ writel(dma->tx_burst_num | (dma->rx_burst_num - 1) << 16,
+ lpi2c_imx->base + LPI2C_MFCR);
+ /* Enable I2C DMA TX/RX function */
+ writel(MDER_TDDE | MDER_RDDE, lpi2c_imx->base + LPI2C_MDER);
+ } else {
+ /* Set I2C TX watermark */
+ writel(dma->tx_burst_num, lpi2c_imx->base + LPI2C_MFCR);
+ /* Enable I2C DMA TX function */
+ writel(MDER_TDDE, lpi2c_imx->base + LPI2C_MDER);
+ }
+
+ /* Enable NACK detected */
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_NDIE);
+};
+
+/*
+ * When lpi2c is in TX DMA mode we can use one DMA TX channel to write
+ * data word into TXFIFO, but in RX DMA mode it is different.
+ *
+ * The LPI2C MTDR register is a command data and transmit data register.
+ * Bits 8-10 are the command data field and Bits 0-7 are the transmit
+ * data field. When the LPI2C master needs to read data, the number of
+ * bytes to read should be set in the command field and RECV_DATA should
+ * be set into the command data field to receive (DATA[7:0] + 1) bytes.
+ * The recv data command word is made of RECV_DATA in the command data
+ * field and the number of bytes to read in transmit data field. When the
+ * length of data to be read exceeds 256 bytes, recv data command word
+ * needs to be written to TXFIFO multiple times.
+ *
+ * So when in RX DMA mode, the TX channel also must to be configured to
+ * send RX command words and the RX command word must be set in advance
+ * before transmitting.
+ */
+static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
+ int ret;
+
+ /* When DMA mode fails before transferring, CPU mode can be used. */
+ dma->using_pio_mode = true;
+
+ dma->dma_len = msg->len;
+ dma->dma_msg_flag = msg->flags;
+ dma->dma_buf = i2c_get_dma_safe_msg_buf(msg, I2C_DMA_THRESHOLD);
+ if (!dma->dma_buf)
+ return -ENOMEM;
+
+ ret = lpi2c_dma_config(lpi2c_imx);
+ if (ret) {
+ dev_err(&lpi2c_imx->adapter.dev, "Failed to configure DMA (%d)\n", ret);
+ goto disable_dma;
+ }
+
+ lpi2c_dma_enable(lpi2c_imx);
+
+ ret = lpi2c_dma_submit(lpi2c_imx);
+ if (ret) {
+ dev_err(&lpi2c_imx->adapter.dev, "DMA submission failed (%d)\n", ret);
+ goto disable_dma;
+ }
+
+ if (dma->dma_msg_flag & I2C_M_RD) {
+ ret = lpi2c_imx_alloc_rx_cmd_buf(lpi2c_imx);
+ if (ret)
+ goto disable_cleanup_data_dma;
+
+ ret = lpi2c_dma_rx_cmd_submit(lpi2c_imx);
+ if (ret)
+ goto disable_cleanup_data_dma;
+ }
+
+ ret = lpi2c_imx_dma_msg_complete(lpi2c_imx);
+ if (ret)
+ goto disable_cleanup_all_dma;
+
+ /* When encountering NACK in transfer, clean up all DMA transfers */
+ if ((readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) && !ret) {
+ ret = -EIO;
+ goto disable_cleanup_all_dma;
+ }
+
+ if (dma->dma_msg_flag & I2C_M_RD)
+ dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr,
+ dma->rx_cmd_buf_len, DMA_TO_DEVICE);
+ lpi2c_dma_unmap(dma);
+
+ goto disable_dma;
+
+disable_cleanup_all_dma:
+ if (dma->dma_msg_flag & I2C_M_RD)
+ lpi2c_cleanup_rx_cmd_dma(dma);
+disable_cleanup_data_dma:
+ lpi2c_cleanup_dma(dma);
+disable_dma:
+ /* Disable I2C DMA function */
+ writel(0, lpi2c_imx->base + LPI2C_MDER);
+
+ if (dma->dma_msg_flag & I2C_M_RD)
+ kfree(dma->rx_cmd_buf);
+
+ if (ret)
+ i2c_put_dma_safe_msg_buf(dma->dma_buf, msg, false);
+ else
+ i2c_put_dma_safe_msg_buf(dma->dma_buf, msg, true);
+
+ return ret;
+}
+
+static int lpi2c_imx_xfer_common(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num, bool atomic)
{
struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
unsigned int temp;
@@ -460,7 +1031,7 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
return result;
for (i = 0; i < num; i++) {
- result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
+ result = lpi2c_imx_start(lpi2c_imx, &msgs[i], atomic);
if (result)
goto disable;
@@ -468,28 +1039,37 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
if (num == 1 && msgs[0].len == 0)
goto stop;
+ lpi2c_imx->rx_buf = NULL;
+ lpi2c_imx->tx_buf = NULL;
lpi2c_imx->delivered = 0;
lpi2c_imx->msglen = msgs[i].len;
- init_completion(&lpi2c_imx->complete);
- if (msgs[i].flags & I2C_M_RD)
- lpi2c_imx_read(lpi2c_imx, &msgs[i]);
- else
- lpi2c_imx_write(lpi2c_imx, &msgs[i]);
+ if (atomic) {
+ result = lpi2c_imx_pio_xfer_atomic(lpi2c_imx, &msgs[i]);
+ } else {
+ init_completion(&lpi2c_imx->complete);
+
+ if (is_use_dma(lpi2c_imx, &msgs[i])) {
+ result = lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]);
+ if (result && lpi2c_imx->dma->using_pio_mode)
+ result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
+ } else {
+ result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
+ }
+ }
- result = lpi2c_imx_msg_complete(lpi2c_imx);
if (result)
goto stop;
if (!(msgs[i].flags & I2C_M_RD)) {
- result = lpi2c_imx_txfifo_empty(lpi2c_imx);
+ result = lpi2c_imx_txfifo_empty(lpi2c_imx, atomic);
if (result)
goto stop;
}
}
stop:
- lpi2c_imx_stop(lpi2c_imx);
+ lpi2c_imx_stop(lpi2c_imx, atomic);
temp = readl(lpi2c_imx->base + LPI2C_MSR);
if ((temp & MSR_NDF) && !result)
@@ -505,26 +1085,269 @@ disable:
return (result < 0) ? result : num;
}
-static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
+static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
- struct lpi2c_imx_struct *lpi2c_imx = dev_id;
+ return lpi2c_imx_xfer_common(adapter, msgs, num, false);
+}
+
+static int lpi2c_imx_xfer_atomic(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+{
+ return lpi2c_imx_xfer_common(adapter, msgs, num, true);
+}
+
+static irqreturn_t lpi2c_imx_target_isr(struct lpi2c_imx_struct *lpi2c_imx,
+ u32 ssr, u32 sier_filter)
+{
+ u8 value;
+ u32 sasr;
+
+ /* Arbitration lost */
+ if (sier_filter & SSR_BEF) {
+ writel(0, lpi2c_imx->base + LPI2C_SIER);
+ return IRQ_HANDLED;
+ }
+
+ /* Address detected */
+ if (sier_filter & SSR_AVF) {
+ sasr = readl(lpi2c_imx->base + LPI2C_SASR);
+ if (SASR_READ_REQ & sasr) {
+ /* Read request */
+ i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_READ_REQUESTED, &value);
+ writel(value, lpi2c_imx->base + LPI2C_STDR);
+ goto ret;
+ } else {
+ /* Write request */
+ i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ }
+ }
+
+ if (sier_filter & SSR_SDF)
+ /* STOP */
+ i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_STOP, &value);
+
+ if (sier_filter & SSR_TDF) {
+ /* Target send data */
+ i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_READ_PROCESSED, &value);
+ writel(value, lpi2c_imx->base + LPI2C_STDR);
+ }
+
+ if (sier_filter & SSR_RDF) {
+ /* Target receive data */
+ value = readl(lpi2c_imx->base + LPI2C_SRDR);
+ i2c_slave_event(lpi2c_imx->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+
+ret:
+ /* Clear SSR */
+ writel(ssr & SSR_CLEAR_BITS, lpi2c_imx->base + LPI2C_SSR);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lpi2c_imx_master_isr(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int enabled;
unsigned int temp;
+ enabled = readl(lpi2c_imx->base + LPI2C_MIER);
+
lpi2c_imx_intctrl(lpi2c_imx, 0);
temp = readl(lpi2c_imx->base + LPI2C_MSR);
-
- if (temp & MSR_RDF)
- lpi2c_imx_read_rxfifo(lpi2c_imx);
-
- if (temp & MSR_TDF)
- lpi2c_imx_write_txfifo(lpi2c_imx);
+ temp &= enabled;
if (temp & MSR_NDF)
complete(&lpi2c_imx->complete);
+ else if (temp & MSR_RDF)
+ lpi2c_imx_read_rxfifo(lpi2c_imx, false);
+ else if (temp & MSR_TDF)
+ lpi2c_imx_write_txfifo(lpi2c_imx, false);
return IRQ_HANDLED;
}
+static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = dev_id;
+
+ if (lpi2c_imx->target) {
+ u32 scr = readl(lpi2c_imx->base + LPI2C_SCR);
+ u32 ssr = readl(lpi2c_imx->base + LPI2C_SSR);
+ u32 sier_filter = ssr & readl(lpi2c_imx->base + LPI2C_SIER);
+
+ /*
+ * The target is enabled and an interrupt has been triggered.
+ * Enter the target's irq handler.
+ */
+ if ((scr & SCR_SEN) && sier_filter)
+ return lpi2c_imx_target_isr(lpi2c_imx, ssr, sier_filter);
+ }
+
+ /*
+ * Otherwise the interrupt has been triggered by the master.
+ * Enter the master's irq handler.
+ */
+ return lpi2c_imx_master_isr(lpi2c_imx);
+}
+
+static void lpi2c_imx_target_init(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 temp;
+
+ /* reset target module */
+ writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR);
+ writel(0, lpi2c_imx->base + LPI2C_SCR);
+
+ /* Set target address */
+ writel((lpi2c_imx->target->addr << 1), lpi2c_imx->base + LPI2C_SAMR);
+
+ writel(SCFGR1_RXSTALL | SCFGR1_TXDSTALL, lpi2c_imx->base + LPI2C_SCFGR1);
+
+ /*
+ * set SCFGR2: FILTSDA, FILTSCL and CLKHOLD
+ *
+ * FILTSCL/FILTSDA can eliminate signal skew. It should generally be
+ * set to the same value and should be set >= 50ns.
+ *
+ * CLKHOLD is only used when clock stretching is enabled, but it will
+ * extend the clock stretching to ensure there is an additional delay
+ * between the target driving SDA and the target releasing the SCL pin.
+ *
+ * CLKHOLD setting is crucial for lpi2c target. When master read data
+ * from target, if there is a delay caused by cpu idle, excessive load,
+ * or other delays between two bytes in one message transmission, it
+ * will cause a short interval time between the driving SDA signal and
+ * releasing SCL signal. The lpi2c master will mistakenly think it is a stop
+ * signal resulting in an arbitration failure. This issue can be avoided
+ * by setting CLKHOLD.
+ *
+ * In order to ensure lpi2c function normally when the lpi2c speed is as
+ * low as 100kHz, CLKHOLD should be set to 3 and it is also compatible with
+ * higher clock frequency like 400kHz and 1MHz.
+ */
+ temp = SCFGR2_FILTSDA(2) | SCFGR2_FILTSCL(2) | SCFGR2_CLKHOLD(3);
+ writel(temp, lpi2c_imx->base + LPI2C_SCFGR2);
+
+ /*
+ * Enable module:
+ * SCR_FILTEN can enable digital filter and output delay counter for LPI2C
+ * target mode. So SCR_FILTEN need be asserted when enable SDA/SCL FILTER
+ * and CLKHOLD.
+ */
+ writel(SCR_SEN | SCR_FILTEN, lpi2c_imx->base + LPI2C_SCR);
+
+ /* Enable interrupt from i2c module */
+ writel(SLAVE_INT_FLAG, lpi2c_imx->base + LPI2C_SIER);
+}
+
+static int lpi2c_imx_register_target(struct i2c_client *client)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (lpi2c_imx->target)
+ return -EBUSY;
+
+ lpi2c_imx->target = client;
+
+ ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent);
+ if (ret < 0) {
+ dev_err(&lpi2c_imx->adapter.dev, "failed to resume i2c controller");
+ return ret;
+ }
+
+ lpi2c_imx_target_init(lpi2c_imx);
+
+ return 0;
+}
+
+static int lpi2c_imx_unregister_target(struct i2c_client *client)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!lpi2c_imx->target)
+ return -EINVAL;
+
+ /* Reset target address. */
+ writel(0, lpi2c_imx->base + LPI2C_SAMR);
+
+ writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR);
+ writel(0, lpi2c_imx->base + LPI2C_SCR);
+
+ lpi2c_imx->target = NULL;
+
+ ret = pm_runtime_put_sync(lpi2c_imx->adapter.dev.parent);
+ if (ret < 0)
+ dev_err(&lpi2c_imx->adapter.dev, "failed to suspend i2c controller");
+
+ return ret;
+}
+
+static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx,
+ struct platform_device *pdev)
+{
+ struct i2c_bus_recovery_info *bri = &lpi2c_imx->rinfo;
+
+ bri->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(bri->pinctrl))
+ return PTR_ERR(bri->pinctrl);
+
+ lpi2c_imx->adapter.bus_recovery_info = bri;
+
+ return 0;
+}
+
+static void dma_exit(struct device *dev, struct lpi2c_imx_dma *dma)
+{
+ if (dma->chan_rx)
+ dma_release_channel(dma->chan_rx);
+
+ if (dma->chan_tx)
+ dma_release_channel(dma->chan_tx);
+
+ devm_kfree(dev, dma);
+}
+
+static int lpi2c_dma_init(struct device *dev, dma_addr_t phy_addr)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+ struct lpi2c_imx_dma *dma;
+ int ret;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ dma->phy_addr = phy_addr;
+
+ /* Prepare for TX DMA: */
+ dma->chan_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(dma->chan_tx)) {
+ ret = PTR_ERR(dma->chan_tx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
+ dma->chan_tx = NULL;
+ goto dma_exit;
+ }
+
+ /* Prepare for RX DMA: */
+ dma->chan_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(dma->chan_rx)) {
+ ret = PTR_ERR(dma->chan_rx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
+ dma->chan_rx = NULL;
+ goto dma_exit;
+ }
+
+ lpi2c_imx->can_use_dma = true;
+ lpi2c_imx->dma = dma;
+ return 0;
+
+dma_exit:
+ dma_exit(dev, dma);
+ return ret;
+}
+
static u32 lpi2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
@@ -532,13 +1355,16 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm lpi2c_imx_algo = {
- .master_xfer = lpi2c_imx_xfer,
- .functionality = lpi2c_imx_func,
+ .xfer = lpi2c_imx_xfer,
+ .xfer_atomic = lpi2c_imx_xfer_atomic,
+ .functionality = lpi2c_imx_func,
+ .reg_target = lpi2c_imx_register_target,
+ .unreg_target = lpi2c_imx_unregister_target,
};
static const struct of_device_id lpi2c_imx_of_match[] = {
{ .compatible = "fsl,imx7ulp-lpi2c" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
@@ -546,6 +1372,7 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
{
struct lpi2c_imx_struct *lpi2c_imx;
struct resource *res;
+ dma_addr_t phy_addr;
unsigned int temp;
int irq, ret;
@@ -553,50 +1380,57 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
if (!lpi2c_imx)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- lpi2c_imx->base = devm_ioremap_resource(&pdev->dev, res);
+ lpi2c_imx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(lpi2c_imx->base))
return PTR_ERR(lpi2c_imx->base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "can't get irq number\n");
+ if (irq < 0)
return irq;
- }
lpi2c_imx->adapter.owner = THIS_MODULE;
lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
lpi2c_imx->adapter.dev.parent = &pdev->dev;
lpi2c_imx->adapter.dev.of_node = pdev->dev.of_node;
- strlcpy(lpi2c_imx->adapter.name, pdev->name,
+ strscpy(lpi2c_imx->adapter.name, pdev->name,
sizeof(lpi2c_imx->adapter.name));
+ phy_addr = (dma_addr_t)res->start;
- lpi2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(lpi2c_imx->clk)) {
- dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
- return PTR_ERR(lpi2c_imx->clk);
- }
+ ret = devm_clk_bulk_get_all(&pdev->dev, &lpi2c_imx->clks);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "can't get I2C peripheral clock\n");
+ lpi2c_imx->num_clks = ret;
ret = of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &lpi2c_imx->bitrate);
if (ret)
- lpi2c_imx->bitrate = LPI2C_DEFAULT_RATE;
+ lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
- ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,
+ ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
pdev->name, lpi2c_imx);
- if (ret) {
- dev_err(&pdev->dev, "can't claim irq %d\n", irq);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", irq);
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
platform_set_drvdata(pdev, lpi2c_imx);
- ret = clk_prepare_enable(lpi2c_imx->clk);
- if (ret) {
- dev_err(&pdev->dev, "clk enable failed %d\n", ret);
+ ret = clk_bulk_prepare_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (ret)
return ret;
- }
+
+ /*
+ * Lock the parent clock rate to avoid getting parent clock upon
+ * each transfer
+ */
+ ret = devm_clk_rate_exclusive_get(&pdev->dev, lpi2c_imx->clks[0].clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "can't lock I2C peripheral clock rate\n");
+
+ lpi2c_imx->rate_per = clk_get_rate(lpi2c_imx->clks[0].clk);
+ if (!lpi2c_imx->rate_per)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "can't get I2C peripheral clock rate\n");
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -608,11 +1442,24 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ /* Init optional bus recovery function */
+ ret = lpi2c_imx_init_recovery_info(lpi2c_imx, pdev);
+ /* Give it another chance if pinctrl used is not ready yet */
+ if (ret == -EPROBE_DEFER)
+ goto rpm_disable;
+
+ /* Init DMA */
+ ret = lpi2c_dma_init(&pdev->dev, phy_addr);
+ if (ret) {
+ if (ret == -EPROBE_DEFER)
+ goto rpm_disable;
+ dev_info(&pdev->dev, "use pio mode\n");
+ }
+
ret = i2c_add_adapter(&lpi2c_imx->adapter);
if (ret)
goto rpm_disable;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
@@ -620,14 +1467,14 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
return 0;
rpm_disable:
- pm_runtime_put(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return ret;
}
-static int lpi2c_imx_remove(struct platform_device *pdev)
+static void lpi2c_imx_remove(struct platform_device *pdev)
{
struct lpi2c_imx_struct *lpi2c_imx = platform_get_drvdata(pdev);
@@ -635,28 +1482,25 @@ static int lpi2c_imx_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
-
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int lpi2c_runtime_suspend(struct device *dev)
+static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
- clk_disable_unprepare(lpi2c_imx->clk);
+ clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
-static int lpi2c_runtime_resume(struct device *dev)
+static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
int ret;
pinctrl_pm_select_default_state(dev);
- ret = clk_prepare_enable(lpi2c_imx->clk);
+ ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
if (ret) {
dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
return ret;
@@ -665,16 +1509,70 @@ static int lpi2c_runtime_resume(struct device *dev)
return 0;
}
+static int __maybe_unused lpi2c_suspend_noirq(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int __maybe_unused lpi2c_resume_noirq(struct device *dev)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * If the I2C module powers down during system suspend,
+ * the register values will be lost. Therefore, reinitialize
+ * the target when the system resumes.
+ */
+ if (lpi2c_imx->target)
+ lpi2c_imx_target_init(lpi2c_imx);
+
+ return 0;
+}
+
+static int lpi2c_suspend(struct device *dev)
+{
+ /*
+ * Some I2C devices may need the I2C controller to remain active
+ * during resume_noirq() or suspend_noirq(). If the controller is
+ * autosuspended, there is no way to wake it up once runtime PM is
+ * disabled (in suspend_late()).
+ *
+ * During system resume, the I2C controller will be available only
+ * after runtime PM is re-enabled (in resume_early()). However, this
+ * may be too late for some devices.
+ *
+ * Wake up the controller in the suspend() callback while runtime PM
+ * is still enabled. The I2C controller will remain available until
+ * the suspend_noirq() callback (pm_runtime_force_suspend()) is
+ * called. During resume, the I2C controller can be restored by the
+ * resume_noirq() callback (pm_runtime_force_resume()).
+ *
+ * Finally, the resume() callback re-enables autosuspend, ensuring
+ * the I2C controller remains available until the system enters
+ * suspend_noirq() and from resume_noirq().
+ */
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int lpi2c_resume(struct device *dev)
+{
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
static const struct dev_pm_ops lpi2c_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpi2c_suspend_noirq,
+ lpi2c_resume_noirq)
+ SYSTEM_SLEEP_PM_OPS(lpi2c_suspend, lpi2c_resume)
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
lpi2c_runtime_resume, NULL)
};
-#define IMX_LPI2C_PM (&lpi2c_pm_ops)
-#else
-#define IMX_LPI2C_PM NULL
-#endif
static struct platform_driver lpi2c_imx_driver = {
.probe = lpi2c_imx_probe,
@@ -682,7 +1580,7 @@ static struct platform_driver lpi2c_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = lpi2c_imx_of_match,
- .pm = IMX_LPI2C_PM,
+ .pm = &lpi2c_pm_ops,
},
};
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index fa9ad53845d9..dcce882f3eba 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -17,10 +17,13 @@
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
*
* Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2020, 2024 NXP
*
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -33,10 +36,12 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/i2c-imx.h>
@@ -48,8 +53,7 @@
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "imx-i2c"
-/* Default value */
-#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
+#define I2C_IMX_CHECK_DELAY 30000 /* Time to check for bus idle, in NS */
/*
* Enable DMA if transfer byte size is bigger than this threshold.
@@ -62,7 +66,7 @@
/* IMX I2C registers:
* the I2C register offset is different between SoCs,
- * to provid support for all these chips, split the
+ * to provide support for all these chips, split the
* register offset into a fixed base address and a
* variable shift value, then the full register offset
* will be calculated by
@@ -74,8 +78,14 @@
#define IMX_I2C_I2SR 0x03 /* i2c status */
#define IMX_I2C_I2DR 0x04 /* i2c transfer data */
+/*
+ * All of the layerscape series SoCs support IBIC register.
+ */
+#define IMX_I2C_IBIC 0x05 /* i2c bus interrupt config */
+
#define IMX_I2C_REGSHIFT 2
#define VF610_I2C_REGSHIFT 0
+#define S32G_I2C_REGSHIFT 0
/* Bits of IMX I2C registers */
#define I2SR_RXAK 0x01
@@ -92,6 +102,7 @@
#define I2CR_MSTA 0x20
#define I2CR_IIEN 0x40
#define I2CR_IEN 0x80
+#define IBIC_BIIE 0x80 /* Bus idle interrupt enable */
/* register bits different operating codes definition:
* 1) I2SR: Interrupt flags clear operation differ between SoCs:
@@ -156,19 +167,50 @@ static struct imx_i2c_clk_pair vf610_i2c_clk_div[] = {
{ 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E },
};
+/* S32G2/S32G3 clock divider, register value pairs */
+static struct imx_i2c_clk_pair s32g2_i2c_clk_div[] = {
+ { 34, 0x00 }, { 36, 0x01 }, { 38, 0x02 }, { 40, 0x03 },
+ { 42, 0x04 }, { 44, 0x05 }, { 46, 0x06 }, { 48, 0x09 },
+ { 52, 0x0A }, { 54, 0x07 }, { 56, 0x0B }, { 60, 0x0C },
+ { 64, 0x0D }, { 68, 0x40 }, { 72, 0x0E }, { 76, 0x42 },
+ { 80, 0x12 }, { 84, 0x0F }, { 88, 0x13 }, { 96, 0x14 },
+ { 104, 0x15 }, { 108, 0x47 }, { 112, 0x19 }, { 120, 0x16 },
+ { 128, 0x1A }, { 136, 0x80 }, { 144, 0x17 }, { 152, 0x82 },
+ { 160, 0x1C }, { 168, 0x84 }, { 176, 0x1D }, { 192, 0x21 },
+ { 208, 0x1E }, { 216, 0x87 }, { 224, 0x22 }, { 240, 0x56 },
+ { 256, 0x1F }, { 288, 0x24 }, { 320, 0x25 }, { 336, 0x8F },
+ { 352, 0x93 }, { 356, 0x5D }, { 358, 0x98 }, { 384, 0x26 },
+ { 416, 0x56 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B },
+ { 576, 0x2C }, { 640, 0x2D }, { 704, 0x9D }, { 768, 0x2E },
+ { 832, 0x9D }, { 896, 0x32 }, { 960, 0x2F }, { 1024, 0x33 },
+ { 1152, 0x34 }, { 1280, 0x35 }, { 1536, 0x36 }, { 1792, 0x3A },
+ { 1920, 0x37 }, { 2048, 0x3B }, { 2304, 0x74 }, { 2560, 0x3D },
+ { 3072, 0x3E }, { 3584, 0x7A }, { 3840, 0x3F }, { 4096, 0x7B },
+ { 4608, 0x7C }, { 5120, 0x7D }, { 6144, 0x7E }, { 7168, 0xBA },
+ { 7680, 0x7F }, { 8192, 0xBB }, { 9216, 0xBC }, { 10240, 0xBD },
+ { 12288, 0xBE }, { 15360, 0xBF },
+};
+
enum imx_i2c_type {
IMX1_I2C,
IMX21_I2C,
+ S32G_I2C,
VF610_I2C,
};
struct imx_i2c_hwdata {
enum imx_i2c_type devtype;
- unsigned regshift;
+ unsigned int regshift;
struct imx_i2c_clk_pair *clk_div;
- unsigned ndivs;
- unsigned i2sr_clr_opcode;
- unsigned i2cr_ien_opcode;
+ unsigned int ndivs;
+ unsigned int i2sr_clr_opcode;
+ unsigned int i2cr_ien_opcode;
+ /*
+ * Errata ERR007805 or e7805:
+ * I2C: When the I2C clock speed is configured for 400 kHz,
+ * the SCL low period violates the I2C spec of 1.3 uS min.
+ */
+ bool has_err007805;
};
struct imx_i2c_dma {
@@ -182,6 +224,17 @@ struct imx_i2c_dma {
enum dma_data_direction dma_data_dir;
};
+enum imx_i2c_state {
+ IMX_I2C_STATE_DONE,
+ IMX_I2C_STATE_FAILED,
+ IMX_I2C_STATE_WRITE,
+ IMX_I2C_STATE_DMA,
+ IMX_I2C_STATE_READ,
+ IMX_I2C_STATE_READ_CONTINUE,
+ IMX_I2C_STATE_READ_BLOCK_DATA,
+ IMX_I2C_STATE_READ_BLOCK_DATA_LEN,
+};
+
struct imx_i2c_struct {
struct i2c_adapter adapter;
struct clk *clk;
@@ -197,11 +250,21 @@ struct imx_i2c_struct {
const struct imx_i2c_hwdata *hwdata;
struct i2c_bus_recovery_info rinfo;
- struct pinctrl *pinctrl;
- struct pinctrl_state *pinctrl_pins_default;
- struct pinctrl_state *pinctrl_pins_gpio;
-
struct imx_i2c_dma *dma;
+ struct i2c_client *slave;
+ enum i2c_slave_event last_slave_event;
+
+ struct i2c_msg *msg;
+ unsigned int msg_buf_idx;
+ int isr_result;
+ bool is_lastmsg;
+ enum imx_i2c_state state;
+
+ bool multi_master;
+
+ /* For checking slave events. */
+ spinlock_t slave_lock;
+ struct hrtimer slave_timer;
};
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
@@ -224,6 +287,16 @@ static const struct imx_i2c_hwdata imx21_i2c_hwdata = {
};
+static const struct imx_i2c_hwdata imx6_i2c_hwdata = {
+ .devtype = IMX21_I2C,
+ .regshift = IMX_I2C_REGSHIFT,
+ .clk_div = imx_i2c_clk_div,
+ .ndivs = ARRAY_SIZE(imx_i2c_clk_div),
+ .i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
+ .i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
+ .has_err007805 = true,
+};
+
static struct imx_i2c_hwdata vf610_i2c_hwdata = {
.devtype = VF610_I2C,
.regshift = VF610_I2C_REGSHIFT,
@@ -231,7 +304,15 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = {
.ndivs = ARRAY_SIZE(vf610_i2c_clk_div),
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
.i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
+};
+static const struct imx_i2c_hwdata s32g2_i2c_hwdata = {
+ .devtype = S32G_I2C,
+ .regshift = S32G_I2C_REGSHIFT,
+ .clk_div = s32g2_i2c_clk_div,
+ .ndivs = ARRAY_SIZE(s32g2_i2c_clk_div),
+ .i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
+ .i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
};
static const struct platform_device_id imx_i2c_devtype[] = {
@@ -250,16 +331,39 @@ MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);
static const struct of_device_id i2c_imx_dt_ids[] = {
{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
+ { .compatible = "fsl,imx6q-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx6sl-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx6sll-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx6sx-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx6ul-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx7d-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx7s-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx8mm-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx8mn-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx8mp-i2c", .data = &imx6_i2c_hwdata, },
+ { .compatible = "fsl,imx8mq-i2c", .data = &imx6_i2c_hwdata, },
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
+ { .compatible = "nxp,s32g2-i2c", .data = &s32g2_i2c_hwdata, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
+static const struct acpi_device_id i2c_imx_acpi_ids[] = {
+ {"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids);
+
static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx)
{
return i2c_imx->hwdata->devtype == IMX1_I2C;
}
+static inline int is_vf610_i2c(struct imx_i2c_struct *i2c_imx)
+{
+ return i2c_imx->hwdata->devtype == VF610_I2C;
+}
+
static inline void imx_i2c_write_reg(unsigned int val,
struct imx_i2c_struct *i2c_imx, unsigned int reg)
{
@@ -272,22 +376,44 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
}
+static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits)
+{
+ unsigned int temp;
+
+ /*
+ * i2sr_clr_opcode is the value to clear all interrupts. Here we want to
+ * clear only <bits>, so we write ~i2sr_clr_opcode with just <bits>
+ * toggled. This is required because i.MX needs W0C and Vybrid uses W1C.
+ */
+ temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
+}
+
+/* Set up i2c controller register and i2c status register to default value. */
+static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
+{
+ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ i2c_imx, IMX_I2C_I2CR);
+ i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL);
+}
+
/* Functions for DMA support */
-static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
- dma_addr_t phy_addr)
+static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_addr_t phy_addr)
{
struct imx_i2c_dma *dma;
struct dma_slave_config dma_sconfig;
- struct device *dev = &i2c_imx->adapter.dev;
+ struct device *dev = i2c_imx->adapter.dev.parent;
int ret;
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
- return;
+ return -ENOMEM;
- dma->chan_tx = dma_request_slave_channel(dev, "tx");
- if (!dma->chan_tx) {
- dev_dbg(dev, "can't request DMA tx channel\n");
+ dma->chan_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(dma->chan_tx)) {
+ ret = PTR_ERR(dma->chan_tx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
goto fail_al;
}
@@ -298,13 +424,15 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_sconfig.direction = DMA_MEM_TO_DEV;
ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
if (ret < 0) {
- dev_dbg(dev, "can't configure tx channel\n");
+ dev_err(dev, "can't configure tx channel (%d)\n", ret);
goto fail_tx;
}
- dma->chan_rx = dma_request_slave_channel(dev, "rx");
- if (!dma->chan_rx) {
- dev_dbg(dev, "can't request DMA rx channel\n");
+ dma->chan_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(dma->chan_rx)) {
+ ret = PTR_ERR(dma->chan_rx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
goto fail_tx;
}
@@ -315,7 +443,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_sconfig.direction = DMA_DEV_TO_MEM;
ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
if (ret < 0) {
- dev_dbg(dev, "can't configure rx channel\n");
+ dev_err(dev, "can't configure rx channel (%d)\n", ret);
goto fail_rx;
}
@@ -324,7 +452,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
- return;
+ return 0;
fail_rx:
dma_release_channel(dma->chan_rx);
@@ -332,7 +460,8 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
- dev_info(dev, "can't use DMA, using PIO instead.\n");
+
+ return ret;
}
static void i2c_imx_dma_callback(void *arg)
@@ -380,7 +509,7 @@ static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
return 0;
err_submit:
- dmaengine_terminate_all(dma->chan_using);
+ dmaengine_terminate_sync(dma->chan_using);
err_desc:
dma_unmap_single(chan_dev, dma->dma_buf,
dma->dma_len, dma->dma_data_dir);
@@ -404,24 +533,22 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
dma->chan_using = NULL;
}
-static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
+static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
{
+ bool multi_master = i2c_imx->multi_master;
unsigned long orig_jiffies = jiffies;
unsigned int temp;
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
while (1) {
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
/* check for arbitration lost */
- if (temp & I2SR_IAL) {
- temp &= ~I2SR_IAL;
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
+ if (multi_master && (temp & I2SR_IAL)) {
+ i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
return -EAGAIN;
}
- if (for_busy && (temp & I2SR_IBB)) {
+ if (for_busy && (!multi_master || (temp & I2SR_IBB))) {
i2c_imx->stopped = 0;
break;
}
@@ -434,20 +561,52 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
"<%s> I2C bus is busy\n", __func__);
return -ETIMEDOUT;
}
- schedule();
+ if (atomic)
+ udelay(100);
+ else
+ schedule();
}
return 0;
}
-static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx)
+static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
{
- wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
+ if (atomic) {
+ void __iomem *addr = i2c_imx->base + (IMX_I2C_I2SR << i2c_imx->hwdata->regshift);
+ unsigned int regval;
+
+ /*
+ * The formula for the poll timeout is documented in the RM
+ * Rev.5 on page 1878:
+ * T_min = 10/F_scl
+ * Set the value hard as it is done for the non-atomic use-case.
+ * Use 10 kHz for the calculation since this is the minimum
+ * allowed SMBus frequency. Also add an offset of 100us since it
+ * turned out that the I2SR_IIF bit isn't set correctly within
+ * the minimum timeout in polling mode.
+ */
+ readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100);
+ i2c_imx->i2csr = regval;
+ i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL);
+ } else {
+ wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
+ }
if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) {
dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
return -ETIMEDOUT;
}
+
+ /* In multi-master mode check for arbitration lost */
+ if (i2c_imx->multi_master && (i2c_imx->i2csr & I2SR_IAL)) {
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
+ i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
+
+ i2c_imx->i2csr = 0;
+ return -EAGAIN;
+ }
+
dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
i2c_imx->i2csr = 0;
return 0;
@@ -464,20 +623,31 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
return 0;
}
-static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
- unsigned int i2c_clk_rate)
+static int i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+ unsigned int i2c_clk_rate)
{
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
unsigned int div;
int i;
+ if (i2c_imx->hwdata->has_err007805 && i2c_imx->bitrate > 384000) {
+ dev_dbg(&i2c_imx->adapter.dev,
+ "SoC errata ERR007805 or e7805 applies, bus frequency limited from %d Hz to 384000 Hz.\n",
+ i2c_imx->bitrate);
+ i2c_imx->bitrate = 384000;
+ }
+
/* Divider value calculation */
if (i2c_imx->cur_clk == i2c_clk_rate)
- return;
+ return 0;
+
+ /* Keep the denominator of the following program always NOT equal to 0. */
+ if (!(i2c_clk_rate / 2))
+ return -EINVAL;
i2c_imx->cur_clk = i2c_clk_rate;
- div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate;
+ div = DIV_ROUND_UP(i2c_clk_rate, i2c_imx->bitrate);
if (div < i2c_clk_div[0].div)
i = 0;
else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
@@ -495,8 +665,8 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
* This delay is used in I2C bus disable function
* to fix chip hardware bug.
*/
- i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div
- + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2);
+ i2c_imx->disable_delay = DIV_ROUND_UP(500000U * i2c_clk_div[i].div,
+ i2c_clk_rate / 2);
#ifdef CONFIG_I2C_DEBUG_BUS
dev_dbg(&i2c_imx->adapter.dev, "I2C_CLK=%d, REQ DIV=%d\n",
@@ -504,59 +674,67 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
dev_dbg(&i2c_imx->adapter.dev, "IFDR[IC]=0x%x, REAL DIV=%d\n",
i2c_clk_div[i].val, i2c_clk_div[i].div);
#endif
+
+ return 0;
}
static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
unsigned long action, void *data)
{
struct clk_notifier_data *ndata = data;
- struct imx_i2c_struct *i2c_imx = container_of(&ndata->clk,
+ struct imx_i2c_struct *i2c_imx = container_of(nb,
struct imx_i2c_struct,
- clk);
+ clk_change_nb);
+ int ret = 0;
if (action & POST_RATE_CHANGE)
- i2c_imx_set_clk(i2c_imx, ndata->new_rate);
+ ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate);
- return NOTIFY_OK;
+ return notifier_from_errno(ret);
}
-static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
+static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic)
{
unsigned int temp = 0;
int result;
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
/* Wait controller to be stable */
- usleep_range(50, 150);
+ if (atomic)
+ udelay(50);
+ else
+ usleep_range(50, 150);
/* Start I2C transaction */
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
temp |= I2CR_MSTA;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- result = i2c_imx_bus_busy(i2c_imx, 1);
+ result = i2c_imx_bus_busy(i2c_imx, 1, atomic);
if (result)
return result;
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
+ if (atomic)
+ temp &= ~I2CR_IIEN; /* Disable interrupt */
+
temp &= ~I2CR_DMAEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
return result;
}
-static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
+static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
{
unsigned int temp = 0;
if (!i2c_imx->stopped) {
/* Stop I2C transaction */
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ i2c_imx->stopped = 1;
temp &= ~(I2CR_MSTA | I2CR_MTX);
if (i2c_imx->dma)
temp &= ~I2CR_DMAEN;
@@ -571,30 +749,404 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
}
if (!i2c_imx->stopped)
- i2c_imx_bus_busy(i2c_imx, 0);
+ i2c_imx_bus_busy(i2c_imx, 0, atomic);
/* Disable I2C controller */
- temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
}
-static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
+/*
+ * Enable bus idle interrupts
+ * Note: IBIC register will be cleared after disabled i2c module.
+ * All of layerscape series SoCs support IBIC register.
+ */
+static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
{
- struct imx_i2c_struct *i2c_imx = dev_id;
+ if (is_vf610_i2c(i2c_imx)) {
+ unsigned int temp;
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_IBIC);
+ temp |= IBIC_BIIE;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_IBIC);
+ }
+}
+
+static void i2c_imx_slave_event(struct imx_i2c_struct *i2c_imx,
+ enum i2c_slave_event event, u8 *val)
+{
+ i2c_slave_event(i2c_imx->slave, event, val);
+ i2c_imx->last_slave_event = event;
+}
+
+static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
+{
+ u8 val = 0;
+
+ while (i2c_imx->last_slave_event != I2C_SLAVE_STOP) {
+ switch (i2c_imx->last_slave_event) {
+ case I2C_SLAVE_READ_REQUESTED:
+ i2c_imx_slave_event(i2c_imx, I2C_SLAVE_READ_PROCESSED,
+ &val);
+ break;
+
+ case I2C_SLAVE_WRITE_REQUESTED:
+ case I2C_SLAVE_READ_PROCESSED:
+ case I2C_SLAVE_WRITE_RECEIVED:
+ i2c_imx_slave_event(i2c_imx, I2C_SLAVE_STOP, &val);
+ break;
+
+ case I2C_SLAVE_STOP:
+ break;
+ }
+ }
+}
+
+/* Returns true if the timer should be restarted, false if not. */
+static irqreturn_t i2c_imx_slave_handle(struct imx_i2c_struct *i2c_imx,
+ unsigned int status, unsigned int ctl)
+{
+ u8 value = 0;
+
+ if (status & I2SR_IAL) { /* Arbitration lost */
+ i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
+ if (!(status & I2SR_IAAS))
+ return IRQ_HANDLED;
+ }
+
+ if (!(status & I2SR_IBB)) {
+ /* No master on the bus, that could mean a stop condition. */
+ i2c_imx_slave_finish_op(i2c_imx);
+ return IRQ_HANDLED;
+ }
+
+ if (!(status & I2SR_ICF))
+ /* Data transfer still in progress, ignore this. */
+ goto out;
+
+ if (status & I2SR_IAAS) { /* Addressed as a slave */
+ i2c_imx_slave_finish_op(i2c_imx);
+ if (status & I2SR_SRW) { /* Master wants to read from us*/
+ dev_dbg(&i2c_imx->adapter.dev, "read requested");
+ i2c_imx_slave_event(i2c_imx,
+ I2C_SLAVE_READ_REQUESTED, &value);
+
+ /* Slave transmit */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ /* Send data */
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Master wants to write to us */
+ dev_dbg(&i2c_imx->adapter.dev, "write requested");
+ i2c_imx_slave_event(i2c_imx,
+ I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ /* Slave receive */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ /* Dummy read */
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+ } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
+ value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ i2c_imx_slave_event(i2c_imx,
+ I2C_SLAVE_WRITE_RECEIVED, &value);
+ } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_imx_slave_event(i2c_imx,
+ I2C_SLAVE_READ_PROCESSED, &value);
+
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Transmit mode received NAK, operation is done */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+
+ /* flag the last byte as processed */
+ i2c_imx_slave_event(i2c_imx,
+ I2C_SLAVE_READ_PROCESSED, &value);
+
+ i2c_imx_slave_finish_op(i2c_imx);
+ return IRQ_HANDLED;
+ }
+
+out:
+ /*
+ * No need to check the return value here. If it returns 0 or
+ * 1, then everything is fine. If it returns -1, then the
+ * timer is running in the handler. This will still work,
+ * though it may be redone (or already have been done) by the
+ * timer function.
+ */
+ hrtimer_try_to_cancel(&i2c_imx->slave_timer);
+ hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
+ hrtimer_restart(&i2c_imx->slave_timer);
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart i2c_imx_slave_timeout(struct hrtimer *t)
+{
+ struct imx_i2c_struct *i2c_imx = container_of(t, struct imx_i2c_struct,
+ slave_timer);
+ unsigned int ctl, status;
+
+ guard(spinlock_irqsave)(&i2c_imx->slave_lock);
+
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ i2c_imx_slave_handle(i2c_imx, status, ctl);
+
+ return HRTIMER_NORESTART;
+}
+
+static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
+{
+ int temp;
+
+ /* Set slave addr. */
+ imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ /* Enable module */
+ temp = i2c_imx->hwdata->i2cr_ien_opcode;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* Enable interrupt from i2c module */
+ temp |= I2CR_IIEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_imx_enable_bus_idle(i2c_imx);
+}
+
+static int i2c_imx_reg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (i2c_imx->slave)
+ return -EBUSY;
+
+ i2c_imx->slave = client;
+ i2c_imx->last_slave_event = I2C_SLAVE_STOP;
+
+ /* Resume */
+ ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
+ if (ret < 0) {
+ dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
+ return ret;
+ }
+
+ i2c_imx_slave_init(i2c_imx);
+
+ return 0;
+}
+
+static int i2c_imx_unreg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!i2c_imx->slave)
+ return -EINVAL;
+
+ /* Reset slave address. */
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ i2c_imx->slave = NULL;
+
+ /* Suspend */
+ ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
+ if (ret < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
+
+ return ret;
+}
+
+static inline int i2c_imx_isr_acked(struct imx_i2c_struct *i2c_imx)
+{
+ i2c_imx->isr_result = 0;
+
+ if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) {
+ i2c_imx->state = IMX_I2C_STATE_FAILED;
+ i2c_imx->isr_result = -ENXIO;
+ wake_up(&i2c_imx->queue);
+ }
+
+ return i2c_imx->isr_result;
+}
+
+static inline int i2c_imx_isr_write(struct imx_i2c_struct *i2c_imx)
+{
+ int result;
+
+ result = i2c_imx_isr_acked(i2c_imx);
+ if (result)
+ return result;
+
+ if (i2c_imx->msg->len == i2c_imx->msg_buf_idx)
+ return 0;
+
+ imx_i2c_write_reg(i2c_imx->msg->buf[i2c_imx->msg_buf_idx++], i2c_imx, IMX_I2C_I2DR);
+
+ return 1;
+}
+
+static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
+{
+ int result;
unsigned int temp;
- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
- if (temp & I2SR_IIF) {
- /* save status register */
- i2c_imx->i2csr = temp;
- temp &= ~I2SR_IIF;
- temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
+ result = i2c_imx_isr_acked(i2c_imx);
+ if (result)
+ return result;
+
+ /* setup bus to read data */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp &= ~I2CR_MTX;
+ if ((i2c_imx->msg->len - 1) || (i2c_imx->msg->flags & I2C_M_RECV_LEN))
+ temp &= ~I2CR_TXAK;
+
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
+
+ return 0;
+}
+
+static inline void i2c_imx_isr_read_continue(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int temp;
+
+ if ((i2c_imx->msg->len - 1) == i2c_imx->msg_buf_idx) {
+ if (i2c_imx->is_lastmsg) {
+ /*
+ * It must generate STOP before read I2DR to prevent
+ * controller from generating another clock cycle
+ */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ i2c_imx->stopped = 1;
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ } else {
+ /*
+ * For i2c master receiver repeat restart operation like:
+ * read -> repeat MSTA -> read/write
+ * The controller must set MTX before read the last byte in
+ * the first read operation, otherwise the first read cost
+ * one extra clock cycle.
+ */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_MTX;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ }
+ } else if (i2c_imx->msg_buf_idx == (i2c_imx->msg->len - 2)) {
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ }
+
+ i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+}
+
+static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx)
+{
+ u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+
+ if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) {
+ i2c_imx->isr_result = -EPROTO;
+ i2c_imx->state = IMX_I2C_STATE_FAILED;
wake_up(&i2c_imx->queue);
- return IRQ_HANDLED;
+ }
+ i2c_imx->msg->len += len;
+ i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len;
+}
+
+static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx, unsigned int status)
+{
+ /*
+ * This state machine handles I2C reception and transmission in non-DMA
+ * mode. We must process all the data in the ISR to reduce the delay
+ * between two consecutive messages. If the data is not processed in
+ * the ISR, SMBus devices may timeout, leading to a bus error.
+ */
+ switch (i2c_imx->state) {
+ case IMX_I2C_STATE_DMA:
+ i2c_imx->i2csr = status;
+ wake_up(&i2c_imx->queue);
+ break;
+
+ case IMX_I2C_STATE_READ:
+ if (i2c_imx_isr_read(i2c_imx))
+ break;
+ i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
+ break;
+
+ case IMX_I2C_STATE_READ_CONTINUE:
+ i2c_imx_isr_read_continue(i2c_imx);
+ if (i2c_imx->msg_buf_idx == i2c_imx->msg->len) {
+ i2c_imx->state = IMX_I2C_STATE_DONE;
+ wake_up(&i2c_imx->queue);
+ }
+ break;
+
+ case IMX_I2C_STATE_READ_BLOCK_DATA:
+ if (i2c_imx_isr_read(i2c_imx))
+ break;
+ i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA_LEN;
+ break;
+
+ case IMX_I2C_STATE_READ_BLOCK_DATA_LEN:
+ i2c_imx_isr_read_block_data_len(i2c_imx);
+ i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
+ break;
+
+ case IMX_I2C_STATE_WRITE:
+ if (i2c_imx_isr_write(i2c_imx))
+ break;
+ i2c_imx->state = IMX_I2C_STATE_DONE;
+ wake_up(&i2c_imx->queue);
+ break;
+
+ default:
+ i2c_imx->i2csr = status;
+ i2c_imx->state = IMX_I2C_STATE_FAILED;
+ i2c_imx->isr_result = -EINVAL;
+ wake_up(&i2c_imx->queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
+{
+ struct imx_i2c_struct *i2c_imx = dev_id;
+ unsigned int ctl, status;
+
+ scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+
+ if (!(status & I2SR_IIF))
+ return IRQ_NONE;
+
+ i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
+
+ if (i2c_imx->slave) {
+ if (!(ctl & I2CR_MSTA))
+ return i2c_imx_slave_handle(i2c_imx,
+ status, ctl);
+
+ i2c_imx_slave_finish_op(i2c_imx);
+ }
}
- return IRQ_NONE;
+ return i2c_imx_master_isr(i2c_imx, status);
}
static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
@@ -607,6 +1159,8 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
struct imx_i2c_dma *dma = i2c_imx->dma;
struct device *dev = &i2c_imx->adapter.dev;
+ i2c_imx->state = IMX_I2C_STATE_DMA;
+
dma->chan_using = dma->chan_tx;
dma->dma_transfer_dir = DMA_MEM_TO_DEV;
dma->dma_data_dir = DMA_TO_DEVICE;
@@ -628,7 +1182,7 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
&i2c_imx->dma->cmd_complete,
msecs_to_jiffies(DMA_TIMEOUT));
if (time_left == 0) {
- dmaengine_terminate_all(dma->chan_using);
+ dmaengine_terminate_sync(dma->chan_using);
return -ETIMEDOUT;
}
@@ -652,13 +1206,49 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
/* The last data byte must be transferred by the CPU. */
imx_i2c_write_reg(msgs->buf[msgs->len-1],
i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, false);
if (result)
return result;
return i2c_imx_acked(i2c_imx);
}
+static int i2c_imx_prepare_read(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs, bool use_dma)
+{
+ int result;
+ unsigned int temp = 0;
+
+ /* write slave address */
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
+ result = i2c_imx_trx_complete(i2c_imx, !use_dma);
+ if (result)
+ return result;
+ result = i2c_imx_acked(i2c_imx);
+ if (result)
+ return result;
+
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
+
+ /* setup bus to read data */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp &= ~I2CR_MTX;
+
+ /*
+ * Reset the I2CR_TXAK flag initially for SMBus block read since the
+ * length is unknown
+ */
+ if (msgs->len - 1)
+ temp &= ~I2CR_TXAK;
+ if (use_dma)
+ temp |= I2CR_DMAEN;
+
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
+
+ return 0;
+}
+
static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
struct i2c_msg *msgs, bool is_lastmsg)
{
@@ -669,6 +1259,13 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
struct imx_i2c_dma *dma = i2c_imx->dma;
struct device *dev = &i2c_imx->adapter.dev;
+ i2c_imx->state = IMX_I2C_STATE_DMA;
+
+ result = i2c_imx_prepare_read(i2c_imx, msgs, true);
+ if (result)
+ return result;
+
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
dma->chan_using = dma->chan_rx;
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
@@ -683,7 +1280,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
&i2c_imx->dma->cmd_complete,
msecs_to_jiffies(DMA_TIMEOUT));
if (time_left == 0) {
- dmaengine_terminate_all(dma->chan_using);
+ dmaengine_terminate_sync(dma->chan_using);
return -ETIMEDOUT;
}
@@ -711,7 +1308,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
/* read n byte data */
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, false);
if (result)
return result;
@@ -722,9 +1319,12 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
*/
dev_dbg(dev, "<%s> clear MSTA\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ i2c_imx->stopped = 1;
temp &= ~(I2CR_MSTA | I2CR_MTX);
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- i2c_imx_bus_busy(i2c_imx, 0);
+ if (!i2c_imx->stopped)
+ i2c_imx_bus_busy(i2c_imx, 0, false);
} else {
/*
* For i2c master receiver repeat restart operation like:
@@ -742,7 +1342,8 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
return 0;
}
-static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
+static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs)
{
int i, result;
@@ -751,7 +1352,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
/* write slave address */
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, true);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
@@ -765,7 +1366,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
"<%s> write byte: B%d=0x%X\n",
__func__, i, msgs->buf[i]);
imx_i2c_write_reg(msgs->buf[i], i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, true);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
@@ -775,53 +1376,54 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
return 0;
}
-static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg)
+static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
+{
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
+ __func__, i2c_8bit_addr_from_msg(msgs));
+
+ i2c_imx->state = IMX_I2C_STATE_WRITE;
+ i2c_imx->msg = msgs;
+ i2c_imx->msg_buf_idx = 0;
+
+ /*
+ * By writing the device address we start the state machine in the ISR.
+ * The ISR will report when it is done or when it fails.
+ */
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
+ wait_event_timeout(i2c_imx->queue,
+ i2c_imx->state == IMX_I2C_STATE_DONE ||
+ i2c_imx->state == IMX_I2C_STATE_FAILED,
+ (msgs->len + 1) * HZ / 10);
+ if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> write failed with %d\n",
+ __func__, i2c_imx->isr_result);
+ return i2c_imx->isr_result;
+ }
+ if (i2c_imx->state != IMX_I2C_STATE_DONE) {
+ dev_err(&i2c_imx->adapter.dev, "<%s> write timedout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs, bool is_lastmsg)
{
int i, result;
unsigned int temp;
int block_data = msgs->flags & I2C_M_RECV_LEN;
- int use_dma = i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data;
- dev_dbg(&i2c_imx->adapter.dev,
- "<%s> write slave address: addr=0x%x\n",
- __func__, i2c_8bit_addr_from_msg(msgs));
-
- /* write slave address */
- imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
- if (result)
- return result;
- result = i2c_imx_acked(i2c_imx);
+ result = i2c_imx_prepare_read(i2c_imx, msgs, false);
if (result)
return result;
- dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
-
- /* setup bus to read data */
- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
- temp &= ~I2CR_MTX;
-
- /*
- * Reset the I2CR_TXAK flag initially for SMBus block read since the
- * length is unknown
- */
- if ((msgs->len - 1) || block_data)
- temp &= ~I2CR_TXAK;
- if (use_dma)
- temp |= I2CR_DMAEN;
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
-
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
- if (use_dma)
- return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
-
/* read data */
for (i = 0; i < msgs->len; i++) {
u8 len = 0;
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, true);
if (result)
return result;
/*
@@ -847,9 +1449,12 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
dev_dbg(&i2c_imx->adapter.dev,
"<%s> clear MSTA\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ i2c_imx->stopped = 1;
temp &= ~(I2CR_MSTA | I2CR_MTX);
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- i2c_imx_bus_busy(i2c_imx, 0);
+ if (!i2c_imx->stopped)
+ i2c_imx_bus_busy(i2c_imx, 0, true);
} else {
/*
* For i2c master receiver repeat restart operation like:
@@ -880,26 +1485,67 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
return 0;
}
-static int i2c_imx_xfer(struct i2c_adapter *adapter,
- struct i2c_msg *msgs, int num)
+static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
+ bool is_lastmsg)
+{
+ int block_data = msgs->flags & I2C_M_RECV_LEN;
+
+ dev_dbg(&i2c_imx->adapter.dev,
+ "<%s> write slave address: addr=0x%x\n",
+ __func__, i2c_8bit_addr_from_msg(msgs));
+
+ i2c_imx->is_lastmsg = is_lastmsg;
+
+ if (block_data)
+ i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA;
+ else
+ i2c_imx->state = IMX_I2C_STATE_READ;
+ i2c_imx->msg = msgs;
+ i2c_imx->msg_buf_idx = 0;
+
+ /*
+ * By writing the device address we start the state machine in the ISR.
+ * The ISR will report when it is done or when it fails.
+ */
+ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
+ wait_event_timeout(i2c_imx->queue,
+ i2c_imx->state == IMX_I2C_STATE_DONE ||
+ i2c_imx->state == IMX_I2C_STATE_FAILED,
+ (msgs->len + 1) * HZ / 10);
+ if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> read failed with %d\n",
+ __func__, i2c_imx->isr_result);
+ return i2c_imx->isr_result;
+ }
+ if (i2c_imx->state != IMX_I2C_STATE_DONE) {
+ dev_err(&i2c_imx->adapter.dev, "<%s> read timedout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ if (!i2c_imx->stopped)
+ return i2c_imx_bus_busy(i2c_imx, 0, false);
+
+ return 0;
+}
+
+static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num, bool atomic)
{
unsigned int i, temp;
int result;
bool is_lastmsg = false;
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
-
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
- result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
- if (result < 0)
- goto out;
+ int use_dma = 0;
/* Start I2C transfer */
- result = i2c_imx_start(i2c_imx);
+ result = i2c_imx_start(i2c_imx, atomic);
if (result) {
- if (i2c_imx->adapter.bus_recovery_info) {
+ /*
+ * Bus recovery uses gpiod_get_value_cansleep() which is not
+ * allowed within atomic context.
+ */
+ if (!atomic && i2c_imx->adapter.bus_recovery_info) {
i2c_recover_bus(&i2c_imx->adapter);
- result = i2c_imx_start(i2c_imx);
+ result = i2c_imx_start(i2c_imx, atomic);
}
}
@@ -917,7 +1563,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
temp |= I2CR_RSTA;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- result = i2c_imx_bus_busy(i2c_imx, 1);
+ result = i2c_imx_bus_busy(i2c_imx, 1, atomic);
if (result)
goto fail0;
}
@@ -941,10 +1587,22 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
(temp & I2SR_RXAK ? 1 : 0));
#endif
- if (msgs[i].flags & I2C_M_RD)
- result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
- else {
- if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
+
+ use_dma = i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
+ msgs[i].flags & I2C_M_DMA_SAFE;
+ if (msgs[i].flags & I2C_M_RD) {
+ int block_data = msgs->flags & I2C_M_RECV_LEN;
+
+ if (atomic)
+ result = i2c_imx_atomic_read(i2c_imx, &msgs[i], is_lastmsg);
+ else if (use_dma && !block_data)
+ result = i2c_imx_dma_read(i2c_imx, &msgs[i], is_lastmsg);
+ else
+ result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
+ } else {
+ if (atomic)
+ result = i2c_imx_atomic_write(i2c_imx, &msgs[i]);
+ else if (use_dma)
result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
else
result = i2c_imx_write(i2c_imx, &msgs[i]);
@@ -955,34 +1613,50 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
fail0:
/* Stop I2C transfer */
- i2c_imx_stop(i2c_imx);
+ i2c_imx_stop(i2c_imx, atomic);
- pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
- pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
-
-out:
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
+ /* After data is transferred, switch to slave mode(as a receiver) */
+ if (i2c_imx->slave)
+ i2c_imx_slave_init(i2c_imx);
+
return (result < 0) ? result : num;
}
-static void i2c_imx_prepare_recovery(struct i2c_adapter *adap)
+static int i2c_imx_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
{
- struct imx_i2c_struct *i2c_imx;
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
+ int result;
+
+ result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
+ if (result < 0)
+ return result;
- i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
+ result = i2c_imx_xfer_common(adapter, msgs, num, false);
- pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_gpio);
+ pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
+
+ return result;
}
-static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap)
+static int i2c_imx_xfer_atomic(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
{
- struct imx_i2c_struct *i2c_imx;
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
+ int result;
- i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
+ result = clk_enable(i2c_imx->clk);
+ if (result)
+ return result;
+
+ result = i2c_imx_xfer_common(adapter, msgs, num, true);
- pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_default);
+ clk_disable(i2c_imx->clk);
+
+ return result;
}
/*
@@ -995,39 +1669,13 @@ static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap)
static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
struct platform_device *pdev)
{
- struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo;
-
- i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
- if (!i2c_imx->pinctrl || IS_ERR(i2c_imx->pinctrl)) {
- dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n");
- return PTR_ERR(i2c_imx->pinctrl);
- }
-
- i2c_imx->pinctrl_pins_default = pinctrl_lookup_state(i2c_imx->pinctrl,
- PINCTRL_STATE_DEFAULT);
- i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
- "gpio");
- rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN);
- rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
-
- if (PTR_ERR(rinfo->sda_gpiod) == -EPROBE_DEFER ||
- PTR_ERR(rinfo->scl_gpiod) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(rinfo->sda_gpiod) ||
- IS_ERR(rinfo->scl_gpiod) ||
- IS_ERR(i2c_imx->pinctrl_pins_default) ||
- IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
- dev_dbg(&pdev->dev, "recovery information incomplete\n");
- return 0;
- }
+ struct i2c_bus_recovery_info *bri = &i2c_imx->rinfo;
- dev_dbg(&pdev->dev, "using scl%s for recovery\n",
- rinfo->sda_gpiod ? ",sda" : "");
+ bri->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(bri->pinctrl))
+ return PTR_ERR(bri->pinctrl);
- rinfo->prepare_recovery = i2c_imx_prepare_recovery;
- rinfo->unprepare_recovery = i2c_imx_unprepare_recovery;
- rinfo->recover_bus = i2c_generic_scl_recovery;
- i2c_imx->adapter.bus_recovery_info = rinfo;
+ i2c_imx->adapter.bus_recovery_info = bri;
return 0;
}
@@ -1039,75 +1687,62 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm i2c_imx_algo = {
- .master_xfer = i2c_imx_xfer,
- .functionality = i2c_imx_func,
+ .xfer = i2c_imx_xfer,
+ .xfer_atomic = i2c_imx_xfer_atomic,
+ .functionality = i2c_imx_func,
+ .reg_slave = i2c_imx_reg_slave,
+ .unreg_slave = i2c_imx_unreg_slave,
};
static int i2c_imx_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
- &pdev->dev);
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
dma_addr_t phy_addr;
-
- dev_dbg(&pdev->dev, "<%s>\n", __func__);
+ const struct imx_i2c_hwdata *match;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "can't get irq number\n");
- return irq;
- }
+ if (irq < 0)
+ return dev_err_probe(&pdev->dev, irq, "can't get IRQ\n");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
- return PTR_ERR(base);
+ return dev_err_probe(&pdev->dev, PTR_ERR(base), "can't get IO memory\n");
phy_addr = (dma_addr_t)res->start;
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
if (!i2c_imx)
return -ENOMEM;
- if (of_id)
- i2c_imx->hwdata = of_id->data;
+ spin_lock_init(&i2c_imx->slave_lock);
+ hrtimer_setup(&i2c_imx->slave_timer, i2c_imx_slave_timeout, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS);
+
+ match = device_get_match_data(&pdev->dev);
+ if (match)
+ i2c_imx->hwdata = match;
else
i2c_imx->hwdata = (struct imx_i2c_hwdata *)
platform_get_device_id(pdev)->driver_data;
/* Setup i2c_imx driver structure */
- strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
+ strscpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
i2c_imx->base = base;
+ ACPI_COMPANION_SET(&i2c_imx->adapter.dev, ACPI_COMPANION(&pdev->dev));
/* Get I2C clock */
- i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2c_imx->clk)) {
- if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "can't get I2C clock\n");
- return PTR_ERR(i2c_imx->clk);
- }
-
- ret = clk_prepare_enable(i2c_imx->clk);
- if (ret) {
- dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
- return ret;
- }
-
- /* Request IRQ */
- ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
- pdev->name, i2c_imx);
- if (ret) {
- dev_err(&pdev->dev, "can't claim irq %d\n", irq);
- goto clk_disable;
- }
+ i2c_imx->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(i2c_imx->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c_imx->clk),
+ "can't get I2C clock\n");
/* Init queue */
init_waitqueue_head(&i2c_imx->queue);
@@ -1127,20 +1762,35 @@ static int i2c_imx_probe(struct platform_device *pdev)
if (ret < 0)
goto rpm_disable;
+ /* Request IRQ */
+ ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED | IRQF_NO_SUSPEND,
+ pdev->name, i2c_imx);
+ if (ret) {
+ dev_err(&pdev->dev, "can't claim irq %d\n", irq);
+ goto rpm_disable;
+ }
+
+ /*
+ * We use the single-master property for backward compatibility.
+ * By default multi master mode is enabled.
+ */
+ i2c_imx->multi_master = !of_property_read_bool(pdev->dev.of_node, "single-master");
+
/* Set up clock divider */
- i2c_imx->bitrate = IMX_I2C_BIT_RATE;
+ i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
ret = of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
i2c_imx->bitrate = pdata->bitrate;
i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
- i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
+ ret = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get I2C clock\n");
+ goto clk_notifier_unregister;
+ }
- /* Set up chip registers to defaults */
- imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
- i2c_imx, IMX_I2C_I2CR);
- imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+ i2c_imx_reset_regs(i2c_imx);
/* Init optional bus recovery function */
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
@@ -1148,12 +1798,29 @@ static int i2c_imx_probe(struct platform_device *pdev)
if (ret == -EPROBE_DEFER)
goto clk_notifier_unregister;
+ /*
+ * DMA mode should be optional for I2C, when encountering DMA errors,
+ * no need to exit I2C probe. Only print warning to show DMA error and
+ * use PIO mode directly to ensure I2C bus available as much as possible.
+ */
+ ret = i2c_imx_dma_request(i2c_imx, phy_addr);
+ if (ret) {
+ if (ret == -EPROBE_DEFER) {
+ dev_err_probe(&pdev->dev, ret, "can't get DMA channels\n");
+ goto clk_notifier_unregister;
+ } else if (ret == -ENODEV) {
+ dev_dbg(&pdev->dev, "Only use PIO mode\n");
+ } else {
+ dev_warn(&pdev->dev, "Failed to setup DMA (%pe), only use PIO mode\n",
+ ERR_PTR(ret));
+ }
+ }
+
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0)
goto clk_notifier_unregister;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
@@ -1162,32 +1829,27 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.name);
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
- /* Init DMA config if supported */
- i2c_imx_dma_request(i2c_imx, phy_addr);
-
return 0; /* Return OK */
clk_notifier_unregister:
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
+ free_irq(irq, i2c_imx);
rpm_disable:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
-
-clk_disable:
- clk_disable_unprepare(i2c_imx->clk);
return ret;
}
-static int i2c_imx_remove(struct platform_device *pdev)
+static void i2c_imx_remove(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
- int ret;
+ int irq, ret;
ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0)
- return ret;
+
+ hrtimer_cancel(&i2c_imx->slave_timer);
/* remove adapter */
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
@@ -1196,29 +1858,29 @@ static int i2c_imx_remove(struct platform_device *pdev)
if (i2c_imx->dma)
i2c_imx_dma_free(i2c_imx);
- /* setup chip registers to defaults */
- imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
- imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
- imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
- imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ if (ret >= 0) {
+ /* setup chip registers to defaults */
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ }
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
- clk_disable_unprepare(i2c_imx->clk);
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ free_irq(irq, i2c_imx);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
-#ifdef CONFIG_PM
static int i2c_imx_runtime_suspend(struct device *dev)
{
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
clk_disable(i2c_imx->clk);
-
- return 0;
+ return pinctrl_pm_select_sleep_state(dev);
}
static int i2c_imx_runtime_resume(struct device *dev)
@@ -1226,6 +1888,10 @@ static int i2c_imx_runtime_resume(struct device *dev)
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
int ret;
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
ret = clk_enable(i2c_imx->clk);
if (ret)
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
@@ -1233,22 +1899,53 @@ static int i2c_imx_runtime_resume(struct device *dev)
return ret;
}
+static int i2c_imx_suspend(struct device *dev)
+{
+ /*
+ * Some I2C devices may need the I2C controller to remain active
+ * during resume_noirq() or suspend_noirq(). If the controller is
+ * autosuspended, there is no way to wake it up once runtime PM is
+ * disabled (in suspend_late()).
+ *
+ * During system resume, the I2C controller will be available only
+ * after runtime PM is re-enabled (in resume_early()). However, this
+ * may be too late for some devices.
+ *
+ * Wake up the controller in the suspend() callback while runtime PM
+ * is still enabled. The I2C controller will remain available until
+ * the suspend_noirq() callback (pm_runtime_force_suspend()) is
+ * called. During resume, the I2C controller can be restored by the
+ * resume_noirq() callback (pm_runtime_force_resume()).
+ *
+ * Finally, the resume() callback re-enables autosuspend, ensuring
+ * the I2C controller remains available until the system enters
+ * suspend_noirq() and from resume_noirq().
+ */
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int i2c_imx_resume(struct device *dev)
+{
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
static const struct dev_pm_ops i2c_imx_pm_ops = {
- SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
- i2c_imx_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
+ RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
};
-#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
-#else
-#define I2C_IMX_PM_OPS NULL
-#endif /* CONFIG_PM */
static struct platform_driver i2c_imx_driver = {
.probe = i2c_imx_probe,
.remove = i2c_imx_remove,
.driver = {
.name = DRIVER_NAME,
- .pm = I2C_IMX_PM_OPS,
+ .pm = pm_ptr(&i2c_imx_pm_ops),
.of_match_table = i2c_imx_dt_ids,
+ .acpi_match_table = i2c_imx_acpi_ids,
},
.id_table = imx_i2c_devtype,
};
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 85cbe4b55578..ce5ca5b90b39 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* ------------------------------------------------------------------------- */
/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x */
/* ------------------------------------------------------------------------- */
@@ -21,12 +22,8 @@
* - Make it work with IXP46x chips
* - Cleanup function names, coding style, etc
*
- * - writing to slave address causes latchup on iop331.
+ * - writing to local target address causes latchup on iop331.
* fix: driver refuses to address self.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2.
*/
#include <linux/interrupt.h>
@@ -38,7 +35,7 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/io.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include "i2c-iop3xx.h"
@@ -71,17 +68,16 @@ iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
/*
* Every time unit enable is asserted, GPOD needs to be cleared
- * on IOP3XX to avoid data corruption on the bus.
+ * on IOP3XX to avoid data corruption on the bus. We use the
+ * gpiod_set_raw_value() to make sure the 0 hits the hardware
+ * GPOD register. These descriptors are only passed along to
+ * the device if this is necessary.
*/
-#if defined(CONFIG_ARCH_IOP32X) || defined(CONFIG_ARCH_IOP33X)
- if (iop3xx_adap->id == 0) {
- gpio_set_value(7, 0);
- gpio_set_value(6, 0);
- } else {
- gpio_set_value(5, 0);
- gpio_set_value(4, 0);
- }
-#endif
+ if (iop3xx_adap->gpio_scl)
+ gpiod_set_raw_value(iop3xx_adap->gpio_scl, 0);
+ if (iop3xx_adap->gpio_sda)
+ gpiod_set_raw_value(iop3xx_adap->gpio_sda, 0);
+
/* NB SR bits not same position as CR IE bits :-( */
iop3xx_adap->SR_enabled =
IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD |
@@ -129,10 +125,12 @@ iop3xx_i2c_error(u32 sr)
int rc = 0;
if ((sr & IOP3XX_ISR_BERRD)) {
- if ( !rc ) rc = -I2C_ERR_BERR;
+ if (!rc)
+ rc = -I2C_ERR_BERR;
}
if ((sr & IOP3XX_ISR_ALD)) {
- if ( !rc ) rc = -I2C_ERR_ALD;
+ if (!rc)
+ rc = -I2C_ERR_ALD;
}
return rc;
}
@@ -155,12 +153,12 @@ iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
* sleep until interrupted, then recover and analyse the SR
* saved by handler
*/
-typedef int (* compare_func)(unsigned test, unsigned mask);
+typedef int (*compare_func)(unsigned test, unsigned mask);
/* returns 1 on correct comparison */
static int
iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
- unsigned flags, unsigned* status,
+ unsigned flags, unsigned *status,
compare_func compare)
{
unsigned sr = 0;
@@ -171,7 +169,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
do {
interrupted = wait_event_interruptible_timeout (
iop3xx_adap->waitq,
- (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
+ (done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)),
1 * HZ
);
if ((rc = iop3xx_i2c_error(sr)) < 0) {
@@ -181,7 +179,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
*status = sr;
return -ETIMEDOUT;
}
- } while(!done);
+ } while (!done);
*status = sr;
@@ -208,7 +206,7 @@ iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_i2c_wait_event(
iop3xx_adap,
- IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
+ IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
status, any_bits_set);
}
@@ -230,13 +228,13 @@ iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
static int
iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
- struct i2c_msg* msg)
+ struct i2c_msg *msg)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
int status;
int rc;
- /* avoid writing to my slave address (hangs on 80331),
+ /* avoid writing to local target address (hangs on 80331),
* forbidden in Intel developer manual
*/
if (msg->addr == MYSAR) {
@@ -277,7 +275,7 @@ iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte,
}
static int
-iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte,
+iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char *byte,
int stop)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
@@ -309,7 +307,7 @@ iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
int rc = 0;
for (ii = 0; rc == 0 && ii != count; ++ii)
- rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
+ rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1);
return rc;
}
@@ -321,7 +319,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
int rc = 0;
for (ii = 0; rc == 0 && ii != count; ++ii)
- rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
+ rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1);
return rc;
}
@@ -334,7 +332,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
* condition.
*/
static int
-iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg)
+iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int rc;
@@ -351,12 +349,9 @@ iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg)
}
}
-/*
- * master_xfer() - main read/write entry
- */
static int
-iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
- int num)
+iop3xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+ int num)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int im = 0;
@@ -373,7 +368,7 @@ iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
iop3xx_i2c_transaction_cleanup(iop3xx_adap);
- if(ret)
+ if (ret)
return ret;
return im;
@@ -386,11 +381,11 @@ iop3xx_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm iop3xx_i2c_algo = {
- .master_xfer = iop3xx_i2c_master_xfer,
- .functionality = iop3xx_i2c_func,
+ .xfer = iop3xx_i2c_xfer,
+ .functionality = iop3xx_i2c_func,
};
-static int
+static void
iop3xx_i2c_remove(struct platform_device *pdev)
{
struct i2c_adapter *padapter = platform_get_drvdata(pdev);
@@ -410,8 +405,6 @@ iop3xx_i2c_remove(struct platform_device *pdev)
release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
kfree(adapter_data);
kfree(padapter);
-
- return 0;
}
static int
@@ -434,6 +427,21 @@ iop3xx_i2c_probe(struct platform_device *pdev)
goto free_adapter;
}
+ adapter_data->gpio_scl = devm_gpiod_get_optional(&pdev->dev,
+ "scl",
+ GPIOD_ASIS);
+ if (IS_ERR(adapter_data->gpio_scl)) {
+ ret = PTR_ERR(adapter_data->gpio_scl);
+ goto free_both;
+ }
+ adapter_data->gpio_sda = devm_gpiod_get_optional(&pdev->dev,
+ "sda",
+ GPIOD_ASIS);
+ if (IS_ERR(adapter_data->gpio_sda)) {
+ ret = PTR_ERR(adapter_data->gpio_sda);
+ goto free_both;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
@@ -456,21 +464,20 @@ iop3xx_i2c_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- ret = -ENXIO;
+ ret = irq;
goto unmap;
}
ret = request_irq(irq, iop3xx_i2c_irq_handler, 0,
pdev->name, adapter_data);
- if (ret) {
- ret = -EIO;
+ if (ret)
goto unmap;
- }
memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
new_adapter->owner = THIS_MODULE;
- new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ new_adapter->class = I2C_CLASS_HWMON;
new_adapter->dev.parent = &pdev->dev;
+ new_adapter->dev.of_node = pdev->dev.of_node;
new_adapter->nr = pdev->id;
/*
@@ -508,12 +515,19 @@ out:
return ret;
}
+static const struct of_device_id i2c_iop3xx_match[] = {
+ { .compatible = "intel,iop3xx-i2c", },
+ { .compatible = "intel,ixp4xx-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_iop3xx_match);
static struct platform_driver iop3xx_i2c_driver = {
.probe = iop3xx_i2c_probe,
.remove = iop3xx_i2c_remove,
.driver = {
.name = "IOP3xx-I2C",
+ .of_match_table = i2c_iop3xx_match,
},
};
diff --git a/drivers/i2c/busses/i2c-iop3xx.h b/drivers/i2c/busses/i2c-iop3xx.h
index 2d6929c2bd92..143548335504 100644
--- a/drivers/i2c/busses/i2c-iop3xx.h
+++ b/drivers/i2c/busses/i2c-iop3xx.h
@@ -1,17 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* ------------------------------------------------------------------------- */
/* i2c-iop3xx.h algorithm driver definitions private to i2c-iop3xx.c */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd
* <Peter dot Milne at D hyphen TACQ dot com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 2.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details. */
+ */
/* ------------------------------------------------------------------------- */
@@ -98,6 +92,8 @@ struct i2c_algo_iop3xx_data {
spinlock_t lock;
u32 SR_enabled, SR_received;
int id;
+ struct gpio_desc *gpio_scl;
+ struct gpio_desc *gpio_sda;
};
#endif /* I2C_IOP3XX_H */
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index 5c754bf659e2..a2ac992f9cb0 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -1,49 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- i2c-isch.c - Linux kernel driver for Intel SCH chipset SMBus
- - Based on i2c-piix4.c
- Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
- Philip Edelbrock <phil@netroedge.com>
- - Intel SCH support
- Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
+ * Linux kernel driver for Intel SCH chipset SMBus
+ * - Based on i2c-piix4.c
+ * Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
+ * Philip Edelbrock <phil@netroedge.com>
+ * - Intel SCH support
+ * Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com>
+ */
-/*
- Supports:
- Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L)
- Note: we assume there can only be one device, with one SMBus interface.
-*/
+/* Supports: Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L) */
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gfp_types.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
+#include <linux/sprintf.h>
#include <linux/stddef.h>
-#include <linux/ioport.h>
-#include <linux/i2c.h>
-#include <linux/io.h>
-#include <linux/acpi.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
/* SCH SMBus address offsets */
-#define SMBHSTCNT (0 + sch_smba)
-#define SMBHSTSTS (1 + sch_smba)
-#define SMBHSTCLK (2 + sch_smba)
-#define SMBHSTADD (4 + sch_smba) /* TSA */
-#define SMBHSTCMD (5 + sch_smba)
-#define SMBHSTDAT0 (6 + sch_smba)
-#define SMBHSTDAT1 (7 + sch_smba)
-#define SMBBLKDAT (0x20 + sch_smba)
-
-/* Other settings */
-#define MAX_RETRIES 5000
+#define SMBHSTCNT 0x00
+#define SMBHSTSTS 0x01
+#define SMBHSTCLK 0x02
+#define SMBHSTADD 0x04 /* TSA */
+#define SMBHSTCMD 0x05
+#define SMBHSTDAT0 0x06
+#define SMBHSTDAT1 0x07
+#define SMBBLKDAT 0x20
/* I2C constants */
#define SCH_QUICK 0x00
@@ -52,110 +42,134 @@
#define SCH_WORD_DATA 0x03
#define SCH_BLOCK_DATA 0x05
-static unsigned short sch_smba;
-static struct i2c_adapter sch_adapter;
+struct sch_i2c {
+ struct i2c_adapter adapter;
+ void __iomem *smba;
+};
+
static int backbone_speed = 33000; /* backbone speed in kHz */
-module_param(backbone_speed, int, S_IRUSR | S_IWUSR);
+module_param(backbone_speed, int, 0600);
MODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)");
-/*
- * Start the i2c transaction -- the i2c_access will prepare the transaction
- * and this function will execute it.
- * return 0 for success and others for failure.
+static inline u8 sch_io_rd8(struct sch_i2c *priv, unsigned int offset)
+{
+ return ioread8(priv->smba + offset);
+}
+
+static inline void sch_io_wr8(struct sch_i2c *priv, unsigned int offset, u8 value)
+{
+ iowrite8(value, priv->smba + offset);
+}
+
+static inline u16 sch_io_rd16(struct sch_i2c *priv, unsigned int offset)
+{
+ return ioread16(priv->smba + offset);
+}
+
+static inline void sch_io_wr16(struct sch_i2c *priv, unsigned int offset, u16 value)
+{
+ iowrite16(value, priv->smba + offset);
+}
+
+/**
+ * sch_transaction - Start the i2c transaction
+ * @adap: the i2c adapter pointer
+ *
+ * The sch_access() will prepare the transaction and
+ * this function will execute it.
+ *
+ * Return: 0 for success and others for failure.
*/
-static int sch_transaction(void)
+static int sch_transaction(struct i2c_adapter *adap)
{
+ struct sch_i2c *priv = container_of(adap, struct sch_i2c, adapter);
int temp;
- int result = 0;
- int retries = 0;
+ int rc;
- dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
- inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
- inb(SMBHSTDAT1));
+ dev_dbg(&adap->dev,
+ "Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+ sch_io_rd8(priv, SMBHSTCNT), sch_io_rd8(priv, SMBHSTCMD),
+ sch_io_rd8(priv, SMBHSTADD),
+ sch_io_rd8(priv, SMBHSTDAT0), sch_io_rd8(priv, SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
- temp = inb(SMBHSTSTS) & 0x0f;
+ temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
if (temp) {
/* Can not be busy since we checked it in sch_access */
- if (temp & 0x01) {
- dev_dbg(&sch_adapter.dev, "Completion (%02x). "
- "Clear...\n", temp);
- }
- if (temp & 0x06) {
- dev_dbg(&sch_adapter.dev, "SMBus error (%02x). "
- "Resetting...\n", temp);
- }
- outb(temp, SMBHSTSTS);
- temp = inb(SMBHSTSTS) & 0x0f;
+ if (temp & 0x01)
+ dev_dbg(&adap->dev, "Completion (%02x). Clear...\n", temp);
+ if (temp & 0x06)
+ dev_dbg(&adap->dev, "SMBus error (%02x). Resetting...\n", temp);
+ sch_io_wr8(priv, SMBHSTSTS, temp);
+ temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
if (temp) {
- dev_err(&sch_adapter.dev,
- "SMBus is not ready: (%02x)\n", temp);
+ dev_err(&adap->dev, "SMBus is not ready: (%02x)\n", temp);
return -EAGAIN;
}
}
- /* start the transaction by setting bit 4 */
- outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
-
- do {
- usleep_range(100, 200);
- temp = inb(SMBHSTSTS) & 0x0f;
- } while ((temp & 0x08) && (retries++ < MAX_RETRIES));
+ /* Start the transaction by setting bit 4 */
+ temp = sch_io_rd8(priv, SMBHSTCNT);
+ temp |= 0x10;
+ sch_io_wr8(priv, SMBHSTCNT, temp);
+ rc = read_poll_timeout(sch_io_rd8, temp, !(temp & 0x08), 200, 500000, true, priv, SMBHSTSTS);
/* If the SMBus is still busy, we give up */
- if (retries > MAX_RETRIES) {
- dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
- result = -ETIMEDOUT;
- }
- if (temp & 0x04) {
- result = -EIO;
- dev_dbg(&sch_adapter.dev, "Bus collision! SMBus may be "
- "locked until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
+ if (rc) {
+ dev_err(&adap->dev, "SMBus Timeout!\n");
+ } else if (temp & 0x04) {
+ rc = -EIO;
+ dev_dbg(&adap->dev, "Bus collision! SMBus may be locked until next hard reset. (sorry!)\n");
+ /* Clock stops and target is stuck in mid-transmission */
} else if (temp & 0x02) {
- result = -EIO;
- dev_err(&sch_adapter.dev, "Error: no response!\n");
+ rc = -EIO;
+ dev_err(&adap->dev, "Error: no response!\n");
} else if (temp & 0x01) {
- dev_dbg(&sch_adapter.dev, "Post complete!\n");
- outb(temp, SMBHSTSTS);
- temp = inb(SMBHSTSTS) & 0x07;
+ dev_dbg(&adap->dev, "Post complete!\n");
+ sch_io_wr8(priv, SMBHSTSTS, temp & 0x0f);
+ temp = sch_io_rd8(priv, SMBHSTSTS) & 0x07;
if (temp & 0x06) {
/* Completion clear failed */
- dev_dbg(&sch_adapter.dev, "Failed reset at end of "
- "transaction (%02x), Bus error!\n", temp);
+ dev_dbg(&adap->dev,
+ "Failed reset at end of transaction (%02x), Bus error!\n", temp);
}
} else {
- result = -ENXIO;
- dev_dbg(&sch_adapter.dev, "No such address.\n");
+ rc = -ENXIO;
+ dev_dbg(&adap->dev, "No such address.\n");
}
- dev_dbg(&sch_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
- inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
- inb(SMBHSTDAT1));
- return result;
+ dev_dbg(&adap->dev, "Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
+ sch_io_rd8(priv, SMBHSTCNT), sch_io_rd8(priv, SMBHSTCMD),
+ sch_io_rd8(priv, SMBHSTADD),
+ sch_io_rd8(priv, SMBHSTDAT0), sch_io_rd8(priv, SMBHSTDAT1));
+ return rc;
}
-/*
- * This is the main access entry for i2c-sch access
- * adap is i2c_adapter pointer, addr is the i2c device bus address, read_write
- * (0 for read and 1 for write), size is i2c transaction type and data is the
- * union of transaction for data to be transferred or data read from bus.
- * return 0 for success and others for failure.
+/**
+ * sch_access - the main access entry for i2c-sch access
+ * @adap: the i2c adapter pointer
+ * @addr: the i2c device bus address
+ * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
+ * @read_write: 0 for read and 1 for write
+ * @command: Byte interpreted by slave, for protocols which use such bytes
+ * @size: the i2c transaction type
+ * @data: the union of transaction for data to be transferred or data read from bus
+ *
+ * Return: 0 for success and others for failure.
*/
static s32 sch_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
+ struct sch_i2c *priv = container_of(adap, struct sch_i2c, adapter);
int i, len, temp, rc;
/* Make sure the SMBus host is not busy */
- temp = inb(SMBHSTSTS) & 0x0f;
+ temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
if (temp & 0x08) {
- dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp);
+ dev_dbg(&adap->dev, "SMBus busy (%02x)\n", temp);
return -EAGAIN;
}
- temp = inw(SMBHSTCLK);
+ temp = sch_io_rd16(priv, SMBHSTCLK);
if (!temp) {
/*
* We can't determine if we have 33 or 25 MHz clock for
@@ -163,50 +177,48 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
* 100 kHz. If we actually run at 25 MHz the bus will be
* run ~75 kHz instead which should do no harm.
*/
- dev_notice(&sch_adapter.dev,
- "Clock divider uninitialized. Setting defaults\n");
- outw(backbone_speed / (4 * 100), SMBHSTCLK);
+ dev_notice(&adap->dev, "Clock divider uninitialized. Setting defaults\n");
+ sch_io_wr16(priv, SMBHSTCLK, backbone_speed / (4 * 100));
}
- dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size,
- (read_write)?"READ":"WRITE");
+ dev_dbg(&adap->dev, "access size: %d %s\n", size, str_read_write(read_write));
switch (size) {
case I2C_SMBUS_QUICK:
- outb((addr << 1) | read_write, SMBHSTADD);
+ sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
size = SCH_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb((addr << 1) | read_write, SMBHSTADD);
+ sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
if (read_write == I2C_SMBUS_WRITE)
- outb(command, SMBHSTCMD);
+ sch_io_wr8(priv, SMBHSTCMD, command);
size = SCH_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb((addr << 1) | read_write, SMBHSTADD);
- outb(command, SMBHSTCMD);
+ sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
+ sch_io_wr8(priv, SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE)
- outb(data->byte, SMBHSTDAT0);
+ sch_io_wr8(priv, SMBHSTDAT0, data->byte);
size = SCH_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb((addr << 1) | read_write, SMBHSTADD);
- outb(command, SMBHSTCMD);
+ sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
+ sch_io_wr8(priv, SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE) {
- outb(data->word & 0xff, SMBHSTDAT0);
- outb((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ sch_io_wr8(priv, SMBHSTDAT0, data->word >> 0);
+ sch_io_wr8(priv, SMBHSTDAT1, data->word >> 8);
}
size = SCH_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
- outb((addr << 1) | read_write, SMBHSTADD);
- outb(command, SMBHSTCMD);
+ sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
+ sch_io_wr8(priv, SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
return -EINVAL;
- outb(len, SMBHSTDAT0);
+ sch_io_wr8(priv, SMBHSTDAT0, len);
for (i = 1; i <= len; i++)
- outb(data->block[i], SMBBLKDAT+i-1);
+ sch_io_wr8(priv, SMBBLKDAT + i - 1, data->block[i]);
}
size = SCH_BLOCK_DATA;
break;
@@ -214,10 +226,13 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}
- dev_dbg(&sch_adapter.dev, "write size %d to 0x%04x\n", size, SMBHSTCNT);
- outb((inb(SMBHSTCNT) & 0xb0) | (size & 0x7), SMBHSTCNT);
+ dev_dbg(&adap->dev, "write size %d to 0x%04x\n", size, SMBHSTCNT);
+
+ temp = sch_io_rd8(priv, SMBHSTCNT);
+ temp = (temp & 0xb0) | (size & 0x7);
+ sch_io_wr8(priv, SMBHSTCNT, temp);
- rc = sch_transaction();
+ rc = sch_transaction(adap);
if (rc) /* Error in transaction */
return rc;
@@ -227,17 +242,18 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
switch (size) {
case SCH_BYTE:
case SCH_BYTE_DATA:
- data->byte = inb(SMBHSTDAT0);
+ data->byte = sch_io_rd8(priv, SMBHSTDAT0);
break;
case SCH_WORD_DATA:
- data->word = inb(SMBHSTDAT0) + (inb(SMBHSTDAT1) << 8);
+ data->word = (sch_io_rd8(priv, SMBHSTDAT0) << 0) +
+ (sch_io_rd8(priv, SMBHSTDAT1) << 8);
break;
case SCH_BLOCK_DATA:
- data->block[0] = inb(SMBHSTDAT0);
+ data->block[0] = sch_io_rd8(priv, SMBHSTDAT0);
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
for (i = 1; i <= data->block[0]; i++)
- data->block[i] = inb(SMBBLKDAT+i-1);
+ data->block[i] = sch_io_rd8(priv, SMBBLKDAT + i - 1);
break;
}
return 0;
@@ -255,53 +271,34 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = sch_func,
};
-static struct i2c_adapter sch_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
- .algo = &smbus_algorithm,
-};
-
-static int smbus_sch_probe(struct platform_device *dev)
+static int smbus_sch_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct sch_i2c *priv;
struct resource *res;
- int retval;
- res = platform_get_resource(dev, IORESOURCE_IO, 0);
- if (!res)
- return -EBUSY;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- if (!devm_request_region(&dev->dev, res->start, resource_size(res),
- dev->name)) {
- dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
- sch_smba);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
return -EBUSY;
- }
- sch_smba = res->start;
+ priv->smba = devm_ioport_map(dev, res->start, resource_size(res));
+ if (!priv->smba)
+ return dev_err_probe(dev, -EBUSY, "SMBus region %pR already in use!\n", res);
- dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
+ /* Set up the sysfs linkage to our parent device */
+ priv->adapter.dev.parent = dev;
+ priv->adapter.owner = THIS_MODULE;
+ priv->adapter.class = I2C_CLASS_HWMON;
+ priv->adapter.algo = &smbus_algorithm;
- /* set up the sysfs linkage to our parent device */
- sch_adapter.dev.parent = &dev->dev;
+ snprintf(priv->adapter.name, sizeof(priv->adapter.name),
+ "SMBus SCH adapter at %04x", (unsigned short)res->start);
- snprintf(sch_adapter.name, sizeof(sch_adapter.name),
- "SMBus SCH adapter at %04x", sch_smba);
-
- retval = i2c_add_adapter(&sch_adapter);
- if (retval)
- sch_smba = 0;
-
- return retval;
-}
-
-static int smbus_sch_remove(struct platform_device *pdev)
-{
- if (sch_smba) {
- i2c_del_adapter(&sch_adapter);
- sch_smba = 0;
- }
-
- return 0;
+ return devm_i2c_add_adapter(dev, &priv->adapter);
}
static struct platform_driver smbus_sch_driver = {
@@ -309,7 +306,6 @@ static struct platform_driver smbus_sch_driver = {
.name = "isch_smbus",
},
.probe = smbus_sch_probe,
- .remove = smbus_sch_remove,
};
module_platform_driver(smbus_sch_driver);
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index 02d23edb2fb1..7aaefb21416a 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -53,7 +53,7 @@
* Features supported by this driver:
* Hardware PEC yes
* Block buffer yes
- * Block process call transaction no
+ * Block process call transaction yes
* Slave mode no
*/
@@ -77,10 +77,12 @@
#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a
#define PCI_DEVICE_ID_INTEL_CDF_SMT 0x18ac
#define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac
+#define PCI_DEVICE_ID_INTEL_EBG_SMT 0x1bff
#define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15
#define ISMT_DESC_ENTRIES 2 /* number of descriptor entries */
#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */
+#define ISMT_LOG_ENTRIES 3 /* number of interrupt cause log entries */
/* Hardware Descriptor Constants - Control Field */
#define ISMT_DESC_CWRL 0x01 /* Command/Write Length */
@@ -144,8 +146,8 @@
#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */
#define ISMT_SPGT_SPD_80K 0x00 /* 80 kHz */
#define ISMT_SPGT_SPD_100K (0x1 << 30) /* 100 kHz */
-#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */
-#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */
+#define ISMT_SPGT_SPD_400K (0x2U << 30) /* 400 kHz */
+#define ISMT_SPGT_SPD_1M (0x3U << 30) /* 1 MHz */
/* MSI Control Register (MSICTL) bit definitions */
@@ -174,16 +176,16 @@ struct ismt_priv {
u8 head; /* ring buffer head pointer */
struct completion cmp; /* interrupt completion */
u8 buffer[I2C_SMBUS_BLOCK_MAX + 16]; /* temp R/W data buffer */
+ dma_addr_t log_dma;
+ u32 *log;
};
-/**
- * ismt_ids - PCI device IDs supported by this driver
- */
static const struct pci_device_id ismt_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMT) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EBG_SMT) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
{ 0, }
};
@@ -197,6 +199,8 @@ MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
/**
* __ismt_desc_dump() - dump the contents of a specific descriptor
+ * @dev: the iSMT device
+ * @desc: the iSMT hardware descriptor
*/
static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc)
{
@@ -331,7 +335,8 @@ static int ismt_process_desc(const struct ismt_desc *desc,
if (desc->status & ISMT_DESC_SCS) {
if (read_write == I2C_SMBUS_WRITE &&
- size != I2C_SMBUS_PROC_CALL)
+ size != I2C_SMBUS_PROC_CALL &&
+ size != I2C_SMBUS_BLOCK_PROC_CALL)
return 0;
switch (size) {
@@ -344,6 +349,7 @@ static int ismt_process_desc(const struct ismt_desc *desc,
data->word = dma_buffer[0] | (dma_buffer[1] << 8);
break;
case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
if (desc->rxbytes != dma_buffer[0] + 1)
return -EMSGSIZE;
@@ -376,6 +382,15 @@ static int ismt_process_desc(const struct ismt_desc *desc,
}
/**
+ * ismt_kill_transaction() - kill current transaction
+ * @priv: iSMT private data
+ */
+static void ismt_kill_transaction(struct ismt_priv *priv)
+{
+ writel(ISMT_GCTRL_KILL, priv->smba + ISMT_GR_GCTRL);
+}
+
+/**
* ismt_access() - process an SMBus command
* @adap: the i2c host adapter
* @addr: address of the i2c/SMBus target
@@ -408,6 +423,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
memset(desc, 0, sizeof(struct ismt_desc));
desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
+ /* Always clear the log entries */
+ memset(priv->log, 0, ISMT_LOG_ENTRIES * sizeof(u32));
+
/* Initialize common control bits */
if (likely(pci_dev_msi_enabled(priv->pci_dev)))
desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
@@ -500,6 +518,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
if (read_write == I2C_SMBUS_WRITE) {
/* Block Write */
dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: WRITE\n");
+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EINVAL;
+
dma_size = data->block[0] + 1;
dma_direction = DMA_TO_DEVICE;
desc->wr_len_cmd = dma_size;
@@ -517,6 +538,21 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
}
break;
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ dev_dbg(dev, "I2C_SMBUS_BLOCK_PROC_CALL\n");
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EINVAL;
+
+ dma_size = I2C_SMBUS_BLOCK_MAX;
+ desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, 1);
+ desc->wr_len_cmd = data->block[0] + 1;
+ desc->rd_len = dma_size;
+ desc->control |= ISMT_DESC_BLK;
+ dma_direction = DMA_BIDIRECTIONAL;
+ dma_buffer[0] = command;
+ memcpy(&dma_buffer[1], &data->block[1], data->block[0]);
+ break;
+
case I2C_SMBUS_I2C_BLOCK_DATA:
/* Make sure the length is valid */
if (data->block[0] < 1)
@@ -596,7 +632,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
dma_unmap_single(dev, dma_addr, dma_size, dma_direction);
if (unlikely(!time_left)) {
- dev_err(dev, "completion wait timed out\n");
+ ismt_kill_transaction(priv);
ret = -ETIMEDOUT;
goto out;
}
@@ -623,16 +659,12 @@ static u32 ismt_func(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_PEC;
}
-/**
- * smbus_algorithm - the adapter algorithm and supported functionality
- * @smbus_xfer: the adapter algorithm
- * @functionality: functionality supported by the adapter
- */
static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ismt_access,
.functionality = ismt_func,
@@ -697,6 +729,8 @@ static void ismt_hw_init(struct ismt_priv *priv)
/* initialize the Master Descriptor Base Address (MDBA) */
writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA);
+ writeq(priv->log_dma, priv->smba + ISMT_GR_SMTICL);
+
/* initialize the Master Control Register (MCTRL) */
writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL);
@@ -781,11 +815,15 @@ static int ismt_dev_init(struct ismt_priv *priv)
if (!priv->hw)
return -ENOMEM;
- memset(priv->hw, 0, (ISMT_DESC_ENTRIES * sizeof(struct ismt_desc)));
-
priv->head = 0;
init_completion(&priv->cmp);
+ priv->log = dmam_alloc_coherent(&priv->pci_dev->dev,
+ ISMT_LOG_ENTRIES * sizeof(u32),
+ &priv->log_dma, GFP_KERNEL);
+ if (!priv->log)
+ return -ENOMEM;
+
return 0;
}
@@ -895,7 +933,7 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
- err = pci_request_region(pdev, SMBBAR, ismt_driver.name);
+ err = pcim_request_region(pdev, SMBBAR, ismt_driver.name);
if (err) {
dev_err(&pdev->dev,
"Failed to request SMBus region 0x%lx-0x%lx\n",
@@ -909,15 +947,10 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
- if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
- (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
- if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
- (pci_set_consistent_dma_mask(pdev,
- DMA_BIT_MASK(32)) != 0)) {
- dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n",
- pdev);
- return -ENODEV;
- }
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err) {
+ dev_err(&pdev->dev, "dma_set_mask fail\n");
+ return -ENODEV;
}
err = ismt_dev_init(priv);
diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c
index 30132c3957cd..664a5471d933 100644
--- a/drivers/i2c/busses/i2c-jz4780.c
+++ b/drivers/i2c/busses/i2c-jz4780.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ingenic JZ4780 I2C bus driver
*
* Copyright (C) 2006 - 2009 Ingenic Semiconductor Inc.
* Copyright (C) 2015 Imagination Technologies
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2019 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
*/
#include <linux/bitops.h>
@@ -26,6 +18,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -64,6 +57,7 @@
#define JZ4780_I2C_ACKGC 0x98
#define JZ4780_I2C_ENSTA 0x9C
#define JZ4780_I2C_SDAHD 0xD0
+#define X1000_I2C_SDAHD 0x7C
#define JZ4780_I2C_CTRL_STPHLD BIT(7)
#define JZ4780_I2C_CTRL_SLVDIS BIT(6)
@@ -82,24 +76,7 @@
#define JZ4780_I2C_STA_TFNF BIT(1)
#define JZ4780_I2C_STA_ACT BIT(0)
-static const char * const jz4780_i2c_abrt_src[] = {
- "ABRT_7B_ADDR_NOACK",
- "ABRT_10ADDR1_NOACK",
- "ABRT_10ADDR2_NOACK",
- "ABRT_XDATA_NOACK",
- "ABRT_GCALL_NOACK",
- "ABRT_GCALL_READ",
- "ABRT_HS_ACKD",
- "SBYTE_ACKDET",
- "ABRT_HS_NORSTRT",
- "SBYTE_NORSTRT",
- "ABRT_10B_RD_NORSTRT",
- "ABRT_MASTER_DIS",
- "ARB_LOST",
- "SLVFLUSH_TXFIFO",
- "SLV_ARBLOST",
- "SLVRD_INTX",
-};
+#define X1000_I2C_DC_STOP BIT(9)
#define JZ4780_I2C_INTST_IGC BIT(11)
#define JZ4780_I2C_INTST_ISTT BIT(10)
@@ -139,18 +116,33 @@ static const char * const jz4780_i2c_abrt_src[] = {
#define JZ4780_I2CFLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1))
#define JZ4780_I2C_FIFO_LEN 16
-#define TX_LEVEL 3
-#define RX_LEVEL (JZ4780_I2C_FIFO_LEN - TX_LEVEL - 1)
+
+#define X1000_I2C_FIFO_LEN 64
#define JZ4780_I2C_TIMEOUT 300
#define BUFSIZE 200
+enum ingenic_i2c_version {
+ ID_JZ4780,
+ ID_X1000,
+};
+
+/* ingenic_i2c_config: SoC specific config data. */
+struct ingenic_i2c_config {
+ enum ingenic_i2c_version version;
+
+ int fifosize;
+ int tx_level;
+ int rx_level;
+};
+
struct jz4780_i2c {
void __iomem *iomem;
int irq;
struct clk *clk;
struct i2c_adapter adap;
+ const struct ingenic_i2c_config *cdata;
/* lock to protect rbuf and wbuf between xfer_rd/wr and irq handler */
spinlock_t lock;
@@ -349,11 +341,18 @@ static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c)
if (hold_time >= 0) {
/*i2c hold time enable */
- hold_time |= JZ4780_I2C_SDAHD_HDENB;
- jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, hold_time);
+ if (i2c->cdata->version >= ID_X1000) {
+ jz4780_i2c_writew(i2c, X1000_I2C_SDAHD, hold_time);
+ } else {
+ hold_time |= JZ4780_I2C_SDAHD_HDENB;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, hold_time);
+ }
} else {
/* disable hold time */
- jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, 0);
+ if (i2c->cdata->version >= ID_X1000)
+ jz4780_i2c_writew(i2c, X1000_I2C_SDAHD, 0);
+ else
+ jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, 0);
}
return 0;
@@ -368,9 +367,11 @@ static int jz4780_i2c_cleanup(struct jz4780_i2c *i2c)
spin_lock_irqsave(&i2c->lock, flags);
/* can send stop now if need */
- tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
- tmp &= ~JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ if (i2c->cdata->version < ID_X1000) {
+ tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
+ tmp &= ~JZ4780_I2C_CTRL_STPHLD;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ }
/* disable all interrupts first */
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0);
@@ -408,11 +409,19 @@ static int jz4780_i2c_prepare(struct jz4780_i2c *i2c)
return jz4780_i2c_enable(i2c);
}
-static void jz4780_i2c_send_rcmd(struct jz4780_i2c *i2c, int cmd_count)
+static void jz4780_i2c_send_rcmd(struct jz4780_i2c *i2c,
+ int cmd_count,
+ int cmd_left)
{
int i;
- for (i = 0; i < cmd_count; i++)
+ for (i = 0; i < cmd_count - 1; i++)
+ jz4780_i2c_writew(i2c, JZ4780_I2C_DC, JZ4780_I2C_DC_READ);
+
+ if ((cmd_left == 0) && (i2c->cdata->version >= ID_X1000))
+ jz4780_i2c_writew(i2c, JZ4780_I2C_DC,
+ JZ4780_I2C_DC_READ | X1000_I2C_DC_STOP);
+ else
jz4780_i2c_writew(i2c, JZ4780_I2C_DC, JZ4780_I2C_DC_READ);
}
@@ -428,9 +437,8 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
unsigned short intst;
unsigned short intmsk;
struct jz4780_i2c *i2c = dev_id;
- unsigned long flags;
- spin_lock_irqsave(&i2c->lock, flags);
+ spin_lock(&i2c->lock);
intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM);
intst = jz4780_i2c_readw(i2c, JZ4780_I2C_INTST);
@@ -467,37 +475,44 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
rd_left = i2c->rd_total_len - i2c->rd_data_xfered;
- if (rd_left <= JZ4780_I2C_FIFO_LEN)
+ if (rd_left <= i2c->cdata->fifosize)
jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, rd_left - 1);
}
if (intst & JZ4780_I2C_INTST_TXEMP) {
if (i2c->is_write == 0) {
int cmd_left = i2c->rd_total_len - i2c->rd_cmd_xfered;
- int max_send = (JZ4780_I2C_FIFO_LEN - 1)
+ int max_send = (i2c->cdata->fifosize - 1)
- (i2c->rd_cmd_xfered
- i2c->rd_data_xfered);
int cmd_to_send = min(cmd_left, max_send);
if (i2c->rd_cmd_xfered != 0)
cmd_to_send = min(cmd_to_send,
- JZ4780_I2C_FIFO_LEN
- - TX_LEVEL - 1);
+ i2c->cdata->fifosize
+ - i2c->cdata->tx_level - 1);
if (cmd_to_send) {
- jz4780_i2c_send_rcmd(i2c, cmd_to_send);
i2c->rd_cmd_xfered += cmd_to_send;
+ cmd_left = i2c->rd_total_len -
+ i2c->rd_cmd_xfered;
+ jz4780_i2c_send_rcmd(i2c,
+ cmd_to_send, cmd_left);
+
}
- cmd_left = i2c->rd_total_len - i2c->rd_cmd_xfered;
if (cmd_left == 0) {
intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM);
intmsk &= ~JZ4780_I2C_INTM_MTXEMP;
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, intmsk);
- tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
- tmp &= ~JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ if (i2c->cdata->version < ID_X1000) {
+ tmp = jz4780_i2c_readw(i2c,
+ JZ4780_I2C_CTRL);
+ tmp &= ~JZ4780_I2C_CTRL_STPHLD;
+ jz4780_i2c_writew(i2c,
+ JZ4780_I2C_CTRL, tmp);
+ }
}
} else {
unsigned short data;
@@ -506,23 +521,26 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA);
while ((i2c_sta & JZ4780_I2C_STA_TFNF) &&
- (i2c->wt_len > 0)) {
+ (i2c->wt_len > 0)) {
i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA);
data = *i2c->wbuf;
data &= ~JZ4780_I2C_DC_READ;
- jz4780_i2c_writew(i2c, JZ4780_I2C_DC,
- data);
+ if ((i2c->wt_len == 1) && (!i2c->stop_hold) &&
+ (i2c->cdata->version >= ID_X1000))
+ data |= X1000_I2C_DC_STOP;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_DC, data);
i2c->wbuf++;
i2c->wt_len--;
}
if (i2c->wt_len == 0) {
- if (!i2c->stop_hold) {
+ if ((!i2c->stop_hold) && (i2c->cdata->version <
+ ID_X1000)) {
tmp = jz4780_i2c_readw(i2c,
- JZ4780_I2C_CTRL);
+ JZ4780_I2C_CTRL);
tmp &= ~JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL,
- tmp);
+ jz4780_i2c_writew(i2c,
+ JZ4780_I2C_CTRL, tmp);
}
jz4780_i2c_trans_done(i2c);
@@ -532,27 +550,14 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
}
done:
- spin_unlock_irqrestore(&i2c->lock, flags);
+ spin_unlock(&i2c->lock);
return IRQ_HANDLED;
}
static void jz4780_i2c_txabrt(struct jz4780_i2c *i2c, int src)
{
- int i;
-
- dev_err(&i2c->adap.dev, "txabrt: 0x%08x\n", src);
- dev_err(&i2c->adap.dev, "device addr=%x\n",
- jz4780_i2c_readw(i2c, JZ4780_I2C_TAR));
- dev_err(&i2c->adap.dev, "send cmd count:%d %d\n",
- i2c->cmd, i2c->cmd_buf[i2c->cmd]);
- dev_err(&i2c->adap.dev, "receive data count:%d %d\n",
- i2c->cmd, i2c->data_buf[i2c->cmd]);
-
- for (i = 0; i < 16; i++) {
- if (src & BIT(i))
- dev_dbg(&i2c->adap.dev, "I2C TXABRT[%d]=%s\n",
- i, jz4780_i2c_abrt_src[i]);
- }
+ dev_dbg(&i2c->adap.dev, "txabrt: 0x%08x, cmd: %d, send: %d, recv: %d\n",
+ src, i2c->cmd, i2c->cmd_buf[i2c->cmd], i2c->data_buf[i2c->cmd]);
}
static inline int jz4780_i2c_xfer_read(struct jz4780_i2c *i2c,
@@ -560,7 +565,7 @@ static inline int jz4780_i2c_xfer_read(struct jz4780_i2c *i2c,
int idx)
{
int ret = 0;
- long timeout;
+ unsigned long time_left;
int wait_time = JZ4780_I2C_TIMEOUT * (len + 5);
unsigned short tmp;
unsigned long flags;
@@ -576,27 +581,29 @@ static inline int jz4780_i2c_xfer_read(struct jz4780_i2c *i2c,
i2c->rd_data_xfered = 0;
i2c->rd_cmd_xfered = 0;
- if (len <= JZ4780_I2C_FIFO_LEN)
+ if (len <= i2c->cdata->fifosize)
jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, len - 1);
else
- jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, RX_LEVEL);
+ jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, i2c->cdata->rx_level);
- jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, TX_LEVEL);
+ jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, i2c->cdata->tx_level);
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM,
JZ4780_I2C_INTM_MRXFL | JZ4780_I2C_INTM_MTXEMP
| JZ4780_I2C_INTM_MTXABT | JZ4780_I2C_INTM_MRXOF);
- tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
- tmp |= JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ if (i2c->cdata->version < ID_X1000) {
+ tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
+ tmp |= JZ4780_I2C_CTRL_STPHLD;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ }
spin_unlock_irqrestore(&i2c->lock, flags);
- timeout = wait_for_completion_timeout(&i2c->trans_waitq,
- msecs_to_jiffies(wait_time));
+ time_left = wait_for_completion_timeout(&i2c->trans_waitq,
+ msecs_to_jiffies(wait_time));
- if (!timeout) {
+ if (!time_left) {
dev_err(&i2c->adap.dev, "irq read timeout\n");
dev_dbg(&i2c->adap.dev, "send cmd count:%d %d\n",
i2c->cmd, i2c->cmd_buf[i2c->cmd]);
@@ -620,7 +627,7 @@ static inline int jz4780_i2c_xfer_write(struct jz4780_i2c *i2c,
{
int ret = 0;
int wait_time = JZ4780_I2C_TIMEOUT * (len + 5);
- long timeout;
+ unsigned long time_left;
unsigned short tmp;
unsigned long flags;
@@ -635,25 +642,27 @@ static inline int jz4780_i2c_xfer_write(struct jz4780_i2c *i2c,
i2c->wbuf = buf;
i2c->wt_len = len;
- jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, TX_LEVEL);
+ jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, i2c->cdata->tx_level);
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, JZ4780_I2C_INTM_MTXEMP
| JZ4780_I2C_INTM_MTXABT);
- tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
- tmp |= JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ if (i2c->cdata->version < ID_X1000) {
+ tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
+ tmp |= JZ4780_I2C_CTRL_STPHLD;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ }
spin_unlock_irqrestore(&i2c->lock, flags);
- timeout = wait_for_completion_timeout(&i2c->trans_waitq,
- msecs_to_jiffies(wait_time));
- if (timeout && !i2c->stop_hold) {
+ time_left = wait_for_completion_timeout(&i2c->trans_waitq,
+ msecs_to_jiffies(wait_time));
+ if (time_left && !i2c->stop_hold) {
unsigned short i2c_sta;
int write_in_process;
- timeout = JZ4780_I2C_TIMEOUT * 100;
- for (; timeout > 0; timeout--) {
+ time_left = JZ4780_I2C_TIMEOUT * 100;
+ for (; time_left > 0; time_left--) {
i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA);
write_in_process = (i2c_sta & JZ4780_I2C_STA_MSTACT) ||
@@ -664,7 +673,7 @@ static inline int jz4780_i2c_xfer_write(struct jz4780_i2c *i2c,
}
}
- if (!timeout) {
+ if (!time_left) {
dev_err(&i2c->adap.dev, "write wait timeout\n");
ret = -EIO;
}
@@ -721,12 +730,30 @@ static u32 jz4780_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm jz4780_i2c_algorithm = {
- .master_xfer = jz4780_i2c_xfer,
- .functionality = jz4780_i2c_functionality,
+ .xfer = jz4780_i2c_xfer,
+ .functionality = jz4780_i2c_functionality,
+};
+
+static const struct ingenic_i2c_config jz4780_i2c_config = {
+ .version = ID_JZ4780,
+
+ .fifosize = JZ4780_I2C_FIFO_LEN,
+ .tx_level = JZ4780_I2C_FIFO_LEN / 2,
+ .rx_level = JZ4780_I2C_FIFO_LEN / 2 - 1,
+};
+
+static const struct ingenic_i2c_config x1000_i2c_config = {
+ .version = ID_X1000,
+
+ .fifosize = X1000_I2C_FIFO_LEN,
+ .tx_level = X1000_I2C_FIFO_LEN / 2,
+ .rx_level = X1000_I2C_FIFO_LEN / 2 - 1,
};
static const struct of_device_id jz4780_i2c_of_matches[] = {
- { .compatible = "ingenic,jz4780-i2c", },
+ { .compatible = "ingenic,jz4770-i2c", .data = &jz4780_i2c_config },
+ { .compatible = "ingenic,jz4780-i2c", .data = &jz4780_i2c_config },
+ { .compatible = "ingenic,x1000-i2c", .data = &x1000_i2c_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, jz4780_i2c_of_matches);
@@ -736,13 +763,18 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
int ret = 0;
unsigned int clk_freq = 0;
unsigned short tmp;
- struct resource *r;
struct jz4780_i2c *i2c;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct jz4780_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
+ i2c->cdata = device_get_match_data(&pdev->dev);
+ if (!i2c->cdata) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &jz4780_i2c_algorithm;
i2c->adap.algo_data = i2c;
@@ -754,68 +786,63 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
init_completion(&i2c->trans_waitq);
spin_lock_init(&i2c->lock);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->iomem = devm_ioremap_resource(&pdev->dev, r);
+ i2c->iomem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->iomem))
return PTR_ERR(i2c->iomem);
platform_set_drvdata(pdev, i2c);
- i2c->clk = devm_clk_get(&pdev->dev, NULL);
+ i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(i2c->clk))
return PTR_ERR(i2c->clk);
- ret = clk_prepare_enable(i2c->clk);
- if (ret)
- return ret;
-
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&clk_freq);
if (ret) {
dev_err(&pdev->dev, "clock-frequency not specified in DT\n");
- goto err;
+ return ret;
}
i2c->speed = clk_freq / 1000;
if (i2c->speed == 0) {
ret = -EINVAL;
dev_err(&pdev->dev, "clock-frequency minimum is 1000\n");
- goto err;
+ return ret;
}
jz4780_i2c_set_speed(i2c);
dev_info(&pdev->dev, "Bus frequency is %d KHz\n", i2c->speed);
- tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
- tmp &= ~JZ4780_I2C_CTRL_STPHLD;
- jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ if (i2c->cdata->version < ID_X1000) {
+ tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL);
+ tmp &= ~JZ4780_I2C_CTRL_STPHLD;
+ jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp);
+ }
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0);
- i2c->irq = platform_get_irq(pdev, 0);
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ i2c->irq = ret;
+
ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
dev_name(&pdev->dev), i2c);
if (ret)
- goto err;
+ return ret;
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
- goto err;
+ return ret;
return 0;
-
-err:
- clk_disable_unprepare(i2c->clk);
- return ret;
}
-static int jz4780_i2c_remove(struct platform_device *pdev)
+static void jz4780_i2c_remove(struct platform_device *pdev)
{
struct jz4780_i2c *i2c = platform_get_drvdata(pdev);
- clk_disable_unprepare(i2c->clk);
i2c_del_adapter(&i2c->adap);
- return 0;
}
static struct platform_driver jz4780_i2c_driver = {
@@ -823,7 +850,7 @@ static struct platform_driver jz4780_i2c_driver = {
.remove = jz4780_i2c_remove,
.driver = {
.name = "jz4780-i2c",
- .of_match_table = of_match_ptr(jz4780_i2c_of_matches),
+ .of_match_table = jz4780_i2c_of_matches,
},
};
diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c
new file mode 100644
index 000000000000..d42c03ef5db5
--- /dev/null
+++ b/drivers/i2c/busses/i2c-k1.c
@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 Troy Mitchell <troymitchell988@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/i2c.h>
+ #include <linux/iopoll.h>
+ #include <linux/module.h>
+ #include <linux/of_address.h>
+ #include <linux/platform_device.h>
+
+/* spacemit i2c registers */
+#define SPACEMIT_ICR 0x0 /* Control register */
+#define SPACEMIT_ISR 0x4 /* Status register */
+#define SPACEMIT_IDBR 0xc /* Data buffer register */
+#define SPACEMIT_IRCR 0x18 /* Reset cycle counter */
+#define SPACEMIT_IBMR 0x1c /* Bus monitor register */
+
+/* SPACEMIT_ICR register fields */
+#define SPACEMIT_CR_START BIT(0) /* start bit */
+#define SPACEMIT_CR_STOP BIT(1) /* stop bit */
+#define SPACEMIT_CR_ACKNAK BIT(2) /* send ACK(0) or NAK(1) */
+#define SPACEMIT_CR_TB BIT(3) /* transfer byte bit */
+/* Bits 4-7 are reserved */
+#define SPACEMIT_CR_MODE_FAST BIT(8) /* bus mode (master operation) */
+/* Bit 9 is reserved */
+#define SPACEMIT_CR_UR BIT(10) /* unit reset */
+#define SPACEMIT_CR_RSTREQ BIT(11) /* i2c bus reset request */
+/* Bit 12 is reserved */
+#define SPACEMIT_CR_SCLE BIT(13) /* master clock enable */
+#define SPACEMIT_CR_IUE BIT(14) /* unit enable */
+/* Bits 15-17 are reserved */
+#define SPACEMIT_CR_ALDIE BIT(18) /* enable arbitration interrupt */
+#define SPACEMIT_CR_DTEIE BIT(19) /* enable TX interrupts */
+#define SPACEMIT_CR_DRFIE BIT(20) /* enable RX interrupts */
+#define SPACEMIT_CR_GCD BIT(21) /* general call disable */
+#define SPACEMIT_CR_BEIE BIT(22) /* enable bus error ints */
+/* Bits 23-24 are reserved */
+#define SPACEMIT_CR_MSDIE BIT(25) /* master STOP detected int enable */
+#define SPACEMIT_CR_MSDE BIT(26) /* master STOP detected enable */
+#define SPACEMIT_CR_TXDONEIE BIT(27) /* transaction done int enable */
+#define SPACEMIT_CR_TXEIE BIT(28) /* transmit FIFO empty int enable */
+#define SPACEMIT_CR_RXHFIE BIT(29) /* receive FIFO half-full int enable */
+#define SPACEMIT_CR_RXFIE BIT(30) /* receive FIFO full int enable */
+#define SPACEMIT_CR_RXOVIE BIT(31) /* receive FIFO overrun int enable */
+
+#define SPACEMIT_I2C_INT_CTRL_MASK (SPACEMIT_CR_ALDIE | SPACEMIT_CR_DTEIE | \
+ SPACEMIT_CR_DRFIE | SPACEMIT_CR_BEIE | \
+ SPACEMIT_CR_TXDONEIE | SPACEMIT_CR_TXEIE | \
+ SPACEMIT_CR_RXHFIE | SPACEMIT_CR_RXFIE | \
+ SPACEMIT_CR_RXOVIE | SPACEMIT_CR_MSDIE)
+
+/* SPACEMIT_ISR register fields */
+/* Bits 0-13 are reserved */
+#define SPACEMIT_SR_ACKNAK BIT(14) /* ACK/NACK status */
+#define SPACEMIT_SR_UB BIT(15) /* unit busy */
+#define SPACEMIT_SR_IBB BIT(16) /* i2c bus busy */
+#define SPACEMIT_SR_EBB BIT(17) /* early bus busy */
+#define SPACEMIT_SR_ALD BIT(18) /* arbitration loss detected */
+#define SPACEMIT_SR_ITE BIT(19) /* TX buffer empty */
+#define SPACEMIT_SR_IRF BIT(20) /* RX buffer full */
+#define SPACEMIT_SR_GCAD BIT(21) /* general call address detected */
+#define SPACEMIT_SR_BED BIT(22) /* bus error no ACK/NAK */
+#define SPACEMIT_SR_SAD BIT(23) /* slave address detected */
+#define SPACEMIT_SR_SSD BIT(24) /* slave stop detected */
+/* Bit 25 is reserved */
+#define SPACEMIT_SR_MSD BIT(26) /* master stop detected */
+#define SPACEMIT_SR_TXDONE BIT(27) /* transaction done */
+#define SPACEMIT_SR_TXE BIT(28) /* TX FIFO empty */
+#define SPACEMIT_SR_RXHF BIT(29) /* RX FIFO half-full */
+#define SPACEMIT_SR_RXF BIT(30) /* RX FIFO full */
+#define SPACEMIT_SR_RXOV BIT(31) /* RX FIFO overrun */
+
+#define SPACEMIT_I2C_INT_STATUS_MASK (SPACEMIT_SR_RXOV | SPACEMIT_SR_RXF | SPACEMIT_SR_RXHF | \
+ SPACEMIT_SR_TXE | SPACEMIT_SR_TXDONE | SPACEMIT_SR_MSD | \
+ SPACEMIT_SR_SSD | SPACEMIT_SR_SAD | SPACEMIT_SR_BED | \
+ SPACEMIT_SR_GCAD | SPACEMIT_SR_IRF | SPACEMIT_SR_ITE | \
+ SPACEMIT_SR_ALD)
+
+#define SPACEMIT_RCR_SDA_GLITCH_NOFIX BIT(7) /* bypass the SDA glitch fix */
+/* the cycles of SCL during bus reset */
+#define SPACEMIT_RCR_FIELD_RST_CYC GENMASK(3, 0)
+
+/* SPACEMIT_IBMR register fields */
+#define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */
+#define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */
+
+/* i2c bus recover timeout: us */
+#define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000
+
+#define SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ 100000 /* Hz */
+#define SPACEMIT_I2C_MAX_FAST_MODE_FREQ 400000 /* Hz */
+
+#define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD)
+
+#define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9
+
+enum spacemit_i2c_state {
+ SPACEMIT_STATE_IDLE,
+ SPACEMIT_STATE_START,
+ SPACEMIT_STATE_READ,
+ SPACEMIT_STATE_WRITE,
+};
+
+/* i2c-spacemit driver's main struct */
+struct spacemit_i2c_dev {
+ struct device *dev;
+ struct i2c_adapter adapt;
+
+ /* hardware resources */
+ void __iomem *base;
+ int irq;
+ u32 clock_freq;
+
+ struct i2c_msg *msgs;
+ u32 msg_num;
+
+ /* index of the current message being processed */
+ u32 msg_idx;
+ u8 *msg_buf;
+ /* the number of unprocessed bytes remaining in the current message */
+ u32 unprocessed;
+
+ enum spacemit_i2c_state state;
+ bool read;
+ struct completion complete;
+ u32 status;
+};
+
+static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c)
+{
+ u32 val;
+
+ val = readl(i2c->base + SPACEMIT_ICR);
+ val |= SPACEMIT_CR_IUE;
+ writel(val, i2c->base + SPACEMIT_ICR);
+}
+
+static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c)
+{
+ u32 val;
+
+ val = readl(i2c->base + SPACEMIT_ICR);
+ val &= ~SPACEMIT_CR_IUE;
+ writel(val, i2c->base + SPACEMIT_ICR);
+}
+
+static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c)
+{
+ writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR);
+ udelay(5);
+ writel(0, i2c->base + SPACEMIT_ICR);
+}
+
+static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c)
+{
+ dev_dbg(i2c->dev, "i2c error status: 0x%08x\n", i2c->status);
+
+ /* Arbitration Loss Detected */
+ if (i2c->status & SPACEMIT_SR_ALD) {
+ spacemit_i2c_reset(i2c);
+ return -EAGAIN;
+ }
+
+ /* Bus Error No ACK/NAK */
+ if (i2c->status & SPACEMIT_SR_BED)
+ spacemit_i2c_reset(i2c);
+
+ return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO;
+}
+
+static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
+{
+ u32 status;
+ u8 clk_cnt;
+
+ /* if bus is locked, reset unit. 0: locked */
+ status = readl(i2c->base + SPACEMIT_IBMR);
+ if ((status & SPACEMIT_BMR_SDA) && (status & SPACEMIT_BMR_SCL))
+ return;
+
+ spacemit_i2c_reset(i2c);
+ usleep_range(10, 20);
+
+ for (clk_cnt = 0; clk_cnt < SPACEMIT_BUS_RESET_CLK_CNT_MAX; clk_cnt++) {
+ status = readl(i2c->base + SPACEMIT_IBMR);
+ if (status & SPACEMIT_BMR_SDA)
+ return;
+
+ /* There's nothing left to save here, we are about to exit */
+ writel(FIELD_PREP(SPACEMIT_RCR_FIELD_RST_CYC, 1),
+ i2c->base + SPACEMIT_IRCR);
+ writel(SPACEMIT_CR_RSTREQ, i2c->base + SPACEMIT_ICR);
+ usleep_range(20, 30);
+ }
+
+ /* check sda again here */
+ status = readl(i2c->base + SPACEMIT_IBMR);
+ if (!(status & SPACEMIT_BMR_SDA))
+ dev_warn_ratelimited(i2c->dev, "unit reset failed\n");
+}
+
+static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c)
+{
+ int ret;
+ u32 val;
+
+ val = readl(i2c->base + SPACEMIT_ISR);
+ if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)))
+ return 0;
+
+ ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR,
+ val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)),
+ 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT);
+ if (ret)
+ spacemit_i2c_reset(i2c);
+
+ return ret;
+}
+
+static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c)
+{
+ /* in case bus is not released after transfer completes */
+ if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) {
+ spacemit_i2c_conditionally_reset_bus(i2c);
+ usleep_range(90, 150);
+ }
+}
+
+static inline void
+spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask)
+{
+ writel(mask & SPACEMIT_I2C_INT_STATUS_MASK, i2c->base + SPACEMIT_ISR);
+}
+
+static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
+{
+ u32 val;
+
+ /*
+ * Unmask interrupt bits for all xfer mode:
+ * bus error, arbitration loss detected.
+ * For transaction complete signal, we use master stop
+ * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE.
+ */
+ val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE;
+
+ /*
+ * Unmask interrupt bits for interrupt xfer mode:
+ * When IDBR receives a byte, an interrupt is triggered.
+ *
+ * For the tx empty interrupt, it will be enabled in the
+ * i2c_start function.
+ * Otherwise, it will cause an erroneous empty interrupt before i2c_start.
+ */
+ val |= SPACEMIT_CR_DRFIE;
+
+ if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ)
+ val |= SPACEMIT_CR_MODE_FAST;
+
+ /* disable response to general call */
+ val |= SPACEMIT_CR_GCD;
+
+ /* enable SCL clock output */
+ val |= SPACEMIT_CR_SCLE;
+
+ /* enable master stop detected */
+ val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE;
+
+ writel(val, i2c->base + SPACEMIT_ICR);
+
+ /*
+ * The glitch fix in the K1 I2C controller introduces a delay
+ * on restart signals, so we disable the fix here.
+ */
+ val = readl(i2c->base + SPACEMIT_IRCR);
+ val |= SPACEMIT_RCR_SDA_GLITCH_NOFIX;
+ writel(val, i2c->base + SPACEMIT_IRCR);
+
+ spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK);
+}
+
+static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c)
+{
+ u32 target_addr_rw, val;
+ struct i2c_msg *cur_msg = i2c->msgs + i2c->msg_idx;
+
+ i2c->read = !!(cur_msg->flags & I2C_M_RD);
+
+ i2c->state = SPACEMIT_STATE_START;
+
+ target_addr_rw = (cur_msg->addr & 0x7f) << 1;
+ if (cur_msg->flags & I2C_M_RD)
+ target_addr_rw |= 1;
+
+ writel(target_addr_rw, i2c->base + SPACEMIT_IDBR);
+
+ /* send start pulse */
+ val = readl(i2c->base + SPACEMIT_ICR);
+ val &= ~SPACEMIT_CR_STOP;
+ val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE;
+ writel(val, i2c->base + SPACEMIT_ICR);
+}
+
+static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c)
+{
+ unsigned long time_left;
+ struct i2c_msg *msg;
+
+ for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) {
+ msg = &i2c->msgs[i2c->msg_idx];
+ i2c->msg_buf = msg->buf;
+ i2c->unprocessed = msg->len;
+ i2c->status = 0;
+
+ reinit_completion(&i2c->complete);
+
+ spacemit_i2c_start(i2c);
+
+ time_left = wait_for_completion_timeout(&i2c->complete,
+ i2c->adapt.timeout);
+ if (!time_left) {
+ dev_err(i2c->dev, "msg completion timeout\n");
+ spacemit_i2c_conditionally_reset_bus(i2c);
+ spacemit_i2c_reset(i2c);
+ return -ETIMEDOUT;
+ }
+
+ if (i2c->status & SPACEMIT_SR_ERR)
+ return spacemit_i2c_handle_err(i2c);
+ }
+
+ return 0;
+}
+
+static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c)
+{
+ if (i2c->msg_idx != i2c->msg_num - 1)
+ return false;
+
+ if (i2c->read)
+ return i2c->unprocessed == 1;
+
+ return !i2c->unprocessed;
+}
+
+static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c)
+{
+ /* if transfer completes, SPACEMIT_ISR will handle it */
+ if (i2c->status & SPACEMIT_SR_MSD)
+ return;
+
+ if (i2c->unprocessed) {
+ writel(*i2c->msg_buf++, i2c->base + SPACEMIT_IDBR);
+ i2c->unprocessed--;
+ return;
+ }
+
+ /* SPACEMIT_STATE_IDLE avoids trigger next byte */
+ i2c->state = SPACEMIT_STATE_IDLE;
+ complete(&i2c->complete);
+}
+
+static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c)
+{
+ if (i2c->unprocessed) {
+ *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR);
+ i2c->unprocessed--;
+ }
+
+ /* if transfer completes, SPACEMIT_ISR will handle it */
+ if (i2c->status & (SPACEMIT_SR_MSD | SPACEMIT_SR_ACKNAK))
+ return;
+
+ /* it has to append stop bit in icr that read last byte */
+ if (i2c->unprocessed)
+ return;
+
+ /* SPACEMIT_STATE_IDLE avoids trigger next byte */
+ i2c->state = SPACEMIT_STATE_IDLE;
+ complete(&i2c->complete);
+}
+
+static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c)
+{
+ i2c->state = i2c->read ? SPACEMIT_STATE_READ : SPACEMIT_STATE_WRITE;
+ if (i2c->state == SPACEMIT_STATE_WRITE)
+ spacemit_i2c_handle_write(i2c);
+}
+
+static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c)
+{
+ u32 val;
+
+ /*
+ * Send transaction complete signal:
+ * error happens, detect master stop
+ */
+ if (!(i2c->status & (SPACEMIT_SR_ERR | SPACEMIT_SR_MSD)))
+ return;
+
+ /*
+ * Here the transaction is already done, we don't need any
+ * other interrupt signals from now, in case any interrupt
+ * happens before spacemit_i2c_xfer to disable irq and i2c unit,
+ * we mask all the interrupt signals and clear the interrupt
+ * status.
+ */
+ val = readl(i2c->base + SPACEMIT_ICR);
+ val &= ~SPACEMIT_I2C_INT_CTRL_MASK;
+ writel(val, i2c->base + SPACEMIT_ICR);
+
+ spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK);
+
+ i2c->state = SPACEMIT_STATE_IDLE;
+ complete(&i2c->complete);
+}
+
+static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
+{
+ struct spacemit_i2c_dev *i2c = devid;
+ u32 status, val;
+
+ status = readl(i2c->base + SPACEMIT_ISR);
+ if (!status)
+ return IRQ_HANDLED;
+
+ i2c->status = status;
+
+ spacemit_i2c_clear_int_status(i2c, status);
+
+ if (i2c->status & SPACEMIT_SR_ERR)
+ goto err_out;
+
+ val = readl(i2c->base + SPACEMIT_ICR);
+ val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START);
+
+ switch (i2c->state) {
+ case SPACEMIT_STATE_START:
+ spacemit_i2c_handle_start(i2c);
+ break;
+ case SPACEMIT_STATE_READ:
+ spacemit_i2c_handle_read(i2c);
+ break;
+ case SPACEMIT_STATE_WRITE:
+ spacemit_i2c_handle_write(i2c);
+ break;
+ default:
+ break;
+ }
+
+ if (i2c->state != SPACEMIT_STATE_IDLE) {
+ val |= SPACEMIT_CR_TB | SPACEMIT_CR_ALDIE;
+
+ if (spacemit_i2c_is_last_msg(i2c)) {
+ /* trigger next byte with stop */
+ val |= SPACEMIT_CR_STOP;
+
+ if (i2c->read)
+ val |= SPACEMIT_CR_ACKNAK;
+ }
+ writel(val, i2c->base + SPACEMIT_ICR);
+ }
+
+err_out:
+ spacemit_i2c_err_check(i2c);
+ return IRQ_HANDLED;
+}
+
+static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c)
+{
+ unsigned long timeout;
+ int idx = 0, cnt = 0;
+
+ for (; idx < i2c->msg_num; idx++)
+ cnt += (i2c->msgs + idx)->len + 1;
+
+ /*
+ * Multiply by 9 because each byte in I2C transmission requires
+ * 9 clock cycles: 8 bits of data plus 1 ACK/NACK bit.
+ */
+ timeout = cnt * 9 * USEC_PER_SEC / i2c->clock_freq;
+
+ i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num;
+}
+
+static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num)
+{
+ struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt);
+ int ret;
+
+ i2c->msgs = msgs;
+ i2c->msg_num = num;
+
+ spacemit_i2c_calc_timeout(i2c);
+
+ spacemit_i2c_init(i2c);
+
+ spacemit_i2c_enable(i2c);
+
+ ret = spacemit_i2c_wait_bus_idle(i2c);
+ if (!ret) {
+ ret = spacemit_i2c_xfer_msg(i2c);
+ if (ret < 0)
+ dev_dbg(i2c->dev, "i2c transfer error: %d\n", ret);
+ } else {
+ spacemit_i2c_check_bus_release(i2c);
+ }
+
+ spacemit_i2c_disable(i2c);
+
+ if (ret == -ETIMEDOUT || ret == -EAGAIN)
+ dev_err(i2c->dev, "i2c transfer failed, ret %d err 0x%lx\n",
+ ret, i2c->status & SPACEMIT_SR_ERR);
+
+ return ret < 0 ? ret : num;
+}
+
+static u32 spacemit_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm spacemit_i2c_algo = {
+ .xfer = spacemit_i2c_xfer,
+ .functionality = spacemit_i2c_func,
+};
+
+static int spacemit_i2c_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = pdev->dev.of_node;
+ struct spacemit_i2c_dev *i2c;
+ int ret;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq);
+ if (ret && ret != -EINVAL)
+ dev_warn(dev, "failed to read clock-frequency property: %d\n", ret);
+
+ /* For now, this driver doesn't support high-speed. */
+ if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) {
+ dev_warn(dev, "unsupported clock frequency %u; using %u\n",
+ i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ);
+ i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ;
+ } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) {
+ dev_warn(dev, "unsupported clock frequency %u; using %u\n",
+ i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ);
+ i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ;
+ }
+
+ i2c->dev = &pdev->dev;
+
+ i2c->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i2c->base))
+ return dev_err_probe(dev, PTR_ERR(i2c->base), "failed to do ioremap");
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0)
+ return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
+
+ ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c->dev), i2c);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq");
+
+ clk = devm_clk_get_enabled(dev, "func");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
+
+ clk = devm_clk_get_enabled(dev, "bus");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock");
+
+ spacemit_i2c_reset(i2c);
+
+ i2c_set_adapdata(&i2c->adapt, i2c);
+ i2c->adapt.owner = THIS_MODULE;
+ i2c->adapt.algo = &spacemit_i2c_algo;
+ i2c->adapt.dev.parent = i2c->dev;
+ i2c->adapt.nr = pdev->id;
+
+ i2c->adapt.dev.of_node = of_node;
+
+ strscpy(i2c->adapt.name, "spacemit-i2c-adapter", sizeof(i2c->adapt.name));
+
+ init_completion(&i2c->complete);
+
+ platform_set_drvdata(pdev, i2c);
+
+ ret = i2c_add_numbered_adapter(&i2c->adapt);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to add i2c adapter");
+
+ return 0;
+}
+
+static void spacemit_i2c_remove(struct platform_device *pdev)
+{
+ struct spacemit_i2c_dev *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adapt);
+}
+
+static const struct of_device_id spacemit_i2c_of_match[] = {
+ { .compatible = "spacemit,k1-i2c", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spacemit_i2c_of_match);
+
+static struct platform_driver spacemit_i2c_driver = {
+ .probe = spacemit_i2c_probe,
+ .remove = spacemit_i2c_remove,
+ .driver = {
+ .name = "i2c-k1",
+ .of_match_table = spacemit_i2c_of_match,
+ },
+};
+module_platform_driver(spacemit_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C bus driver for SpacemiT K1 SoC");
diff --git a/drivers/i2c/busses/i2c-keba.c b/drivers/i2c/busses/i2c-keba.c
new file mode 100644
index 000000000000..9420c8b342b5
--- /dev/null
+++ b/drivers/i2c/busses/i2c-keba.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) KEBA Industrial Automation Gmbh 2024
+ *
+ * Driver for KEBA I2C controller FPGA IP core
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/misc/keba.h>
+
+#define KI2C "i2c-keba"
+
+#define KI2C_CAPABILITY_REG 0x02
+#define KI2C_CAPABILITY_CRYPTO 0x01
+#define KI2C_CAPABILITY_DC 0x02
+
+#define KI2C_CONTROL_REG 0x04
+#define KI2C_CONTROL_MEN 0x01
+#define KI2C_CONTROL_MSTA 0x02
+#define KI2C_CONTROL_RSTA 0x04
+#define KI2C_CONTROL_MTX 0x08
+#define KI2C_CONTROL_TXAK 0x10
+#define KI2C_CONTROL_DISABLE 0x00
+
+#define KI2C_CONTROL_DC_REG 0x05
+#define KI2C_CONTROL_DC_SDA 0x01
+#define KI2C_CONTROL_DC_SCL 0x02
+
+#define KI2C_STATUS_REG 0x08
+#define KI2C_STATUS_IN_USE 0x01
+#define KI2C_STATUS_ACK_CYC 0x02
+#define KI2C_STATUS_RXAK 0x04
+#define KI2C_STATUS_MCF 0x08
+
+#define KI2C_STATUS_DC_REG 0x09
+#define KI2C_STATUS_DC_SDA 0x01
+#define KI2C_STATUS_DC_SCL 0x02
+
+#define KI2C_DATA_REG 0x0c
+
+#define KI2C_INUSE_SLEEP_US (2 * USEC_PER_MSEC)
+#define KI2C_INUSE_TIMEOUT_US (10 * USEC_PER_SEC)
+
+#define KI2C_POLL_DELAY_US 5
+
+struct ki2c {
+ struct keba_i2c_auxdev *auxdev;
+ void __iomem *base;
+ struct i2c_adapter adapter;
+
+ struct i2c_client **client;
+ int client_size;
+};
+
+static int ki2c_inuse_lock(struct ki2c *ki2c)
+{
+ u8 sts;
+ int ret;
+
+ /*
+ * The I2C controller has an IN_USE bit for locking access to the
+ * controller. This enables the use of I2C controller by other none
+ * Linux processors.
+ *
+ * If the I2C controller is free, then the first read returns
+ * IN_USE == 0. After that the I2C controller is locked and further
+ * reads of IN_USE return 1.
+ *
+ * The I2C controller is unlocked by writing 1 into IN_USE.
+ *
+ * The IN_USE bit acts as a hardware semaphore for the I2C controller.
+ * Poll for semaphore, but sleep while polling to free the CPU.
+ */
+ ret = readb_poll_timeout(ki2c->base + KI2C_STATUS_REG,
+ sts, (sts & KI2C_STATUS_IN_USE) == 0,
+ KI2C_INUSE_SLEEP_US, KI2C_INUSE_TIMEOUT_US);
+ if (ret)
+ dev_err(&ki2c->auxdev->auxdev.dev, "%s err!\n", __func__);
+
+ return ret;
+}
+
+static void ki2c_inuse_unlock(struct ki2c *ki2c)
+{
+ /* unlock the controller by writing 1 into IN_USE */
+ iowrite8(KI2C_STATUS_IN_USE, ki2c->base + KI2C_STATUS_REG);
+}
+
+static int ki2c_wait_for_bit(void __iomem *addr, u8 mask, unsigned long timeout)
+{
+ u8 val;
+
+ return readb_poll_timeout(addr, val, (val & mask), KI2C_POLL_DELAY_US,
+ jiffies_to_usecs(timeout));
+}
+
+static int ki2c_wait_for_mcf(struct ki2c *ki2c)
+{
+ return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG, KI2C_STATUS_MCF,
+ ki2c->adapter.timeout);
+}
+
+static int ki2c_wait_for_data(struct ki2c *ki2c)
+{
+ int ret;
+
+ ret = ki2c_wait_for_mcf(ki2c);
+ if (ret < 0)
+ return ret;
+
+ return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG,
+ KI2C_STATUS_ACK_CYC,
+ ki2c->adapter.timeout);
+}
+
+static int ki2c_wait_for_data_ack(struct ki2c *ki2c)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = ki2c_wait_for_data(ki2c);
+ if (ret < 0)
+ return ret;
+
+ /* RXAK == 0 means ACK reveived */
+ reg = ioread8(ki2c->base + KI2C_STATUS_REG);
+ if (reg & KI2C_STATUS_RXAK)
+ return -EIO;
+
+ return 0;
+}
+
+static int ki2c_has_capability(struct ki2c *ki2c, unsigned int cap)
+{
+ unsigned int reg = ioread8(ki2c->base + KI2C_CAPABILITY_REG);
+
+ return (reg & cap) != 0;
+}
+
+static int ki2c_get_scl(struct ki2c *ki2c)
+{
+ unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG);
+
+ /* capability KI2C_CAPABILITY_DC required */
+ return (reg & KI2C_STATUS_DC_SCL) != 0;
+}
+
+static int ki2c_get_sda(struct ki2c *ki2c)
+{
+ unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG);
+
+ /* capability KI2C_CAPABILITY_DC required */
+ return (reg & KI2C_STATUS_DC_SDA) != 0;
+}
+
+static void ki2c_set_scl(struct ki2c *ki2c, int val)
+{
+ u8 control_dc;
+
+ /* capability KI2C_CAPABILITY_DC and KI2C_CONTROL_MEN = 0 reqired */
+ control_dc = ioread8(ki2c->base + KI2C_CONTROL_DC_REG);
+ if (val)
+ control_dc |= KI2C_CONTROL_DC_SCL;
+ else
+ control_dc &= ~KI2C_CONTROL_DC_SCL;
+ iowrite8(control_dc, ki2c->base + KI2C_CONTROL_DC_REG);
+}
+
+/*
+ * Resetting bus bitwise is done by checking SDA and applying clock cycles as
+ * long as SDA is low. 9 clock cycles are applied at most.
+ *
+ * Clock cycles are generated and udelay() determines the duration of clock
+ * cycles. Generated clock rate is 100 KHz and so duration of both clock levels
+ * is: delay in ns = (10^6 / 100) / 2
+ */
+#define KI2C_RECOVERY_CLK_CNT (9 * 2)
+#define KI2C_RECOVERY_UDELAY 5
+static int ki2c_reset_bus_bitwise(struct ki2c *ki2c)
+{
+ int val = 1;
+ int ret = 0;
+ int i;
+
+ /* disable I2C controller (MEN = 0) to get direct access to SCL/SDA */
+ iowrite8(0, ki2c->base + KI2C_CONTROL_REG);
+
+ /* generate clock cycles */
+ ki2c_set_scl(ki2c, val);
+ udelay(KI2C_RECOVERY_UDELAY);
+ for (i = 0; i < KI2C_RECOVERY_CLK_CNT; i++) {
+ if (val) {
+ /* SCL shouldn't be low here */
+ if (!ki2c_get_scl(ki2c)) {
+ dev_err(&ki2c->auxdev->auxdev.dev,
+ "SCL is stuck low!\n");
+ ret = -EBUSY;
+ break;
+ }
+
+ /* break if SDA is high */
+ if (ki2c_get_sda(ki2c))
+ break;
+ }
+
+ val = !val;
+ ki2c_set_scl(ki2c, val);
+ udelay(KI2C_RECOVERY_UDELAY);
+ }
+
+ if (!ki2c_get_sda(ki2c)) {
+ dev_err(&ki2c->auxdev->auxdev.dev, "SDA is still low!\n");
+ ret = -EBUSY;
+ }
+
+ /* reenable controller */
+ iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
+
+ return ret;
+}
+
+/*
+ * Resetting bus bytewise is done by writing start bit, 9 data bits and stop
+ * bit.
+ *
+ * This is not 100% safe. If target is an EEPROM and a write access was
+ * interrupted during the ACK cycle, this approach might not be able to recover
+ * the bus. The reason is, that after the 9 clock cycles the EEPROM will be in
+ * ACK cycle again and will hold SDA low like it did before the start of the
+ * routine. Furthermore the EEPROM might get written one additional byte with
+ * 0xff into it. Thus, use bitwise approach whenever possible, especially when
+ * EEPROMs are on the bus.
+ */
+static int ki2c_reset_bus_bytewise(struct ki2c *ki2c)
+{
+ int ret;
+
+ /* hold data line high for 9 clock cycles */
+ iowrite8(0xFF, ki2c->base + KI2C_DATA_REG);
+
+ /* create start condition */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK,
+ ki2c->base + KI2C_CONTROL_REG);
+ ret = ki2c_wait_for_mcf(ki2c);
+ if (ret < 0) {
+ dev_err(&ki2c->auxdev->auxdev.dev, "Start condition failed\n");
+
+ return ret;
+ }
+
+ /* create stop condition */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_TXAK,
+ ki2c->base + KI2C_CONTROL_REG);
+ ret = ki2c_wait_for_mcf(ki2c);
+ if (ret < 0)
+ dev_err(&ki2c->auxdev->auxdev.dev, "Stop condition failed\n");
+
+ return ret;
+}
+
+static int ki2c_reset_bus(struct ki2c *ki2c)
+{
+ int ret;
+
+ ret = ki2c_inuse_lock(ki2c);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * If the I2C controller is capable of direct control of SCL/SDA, then a
+ * bitwise reset is used. Otherwise fall back to bytewise reset.
+ */
+ if (ki2c_has_capability(ki2c, KI2C_CAPABILITY_DC))
+ ret = ki2c_reset_bus_bitwise(ki2c);
+ else
+ ret = ki2c_reset_bus_bytewise(ki2c);
+
+ ki2c_inuse_unlock(ki2c);
+
+ return ret;
+}
+
+static void ki2c_write_target_addr(struct ki2c *ki2c, struct i2c_msg *m)
+{
+ u8 addr;
+
+ addr = m->addr << 1;
+ /* Bit 0 signals RD/WR */
+ if (m->flags & I2C_M_RD)
+ addr |= 0x01;
+
+ iowrite8(addr, ki2c->base + KI2C_DATA_REG);
+}
+
+static int ki2c_start_addr(struct ki2c *ki2c, struct i2c_msg *m)
+{
+ int ret;
+
+ /*
+ * Store target address byte in the controller. This has to be done
+ * before sending START condition.
+ */
+ ki2c_write_target_addr(ki2c, m);
+
+ /* enable controller for TX */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX,
+ ki2c->base + KI2C_CONTROL_REG);
+
+ /* send START condition and target address byte */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA,
+ ki2c->base + KI2C_CONTROL_REG);
+
+ ret = ki2c_wait_for_data_ack(ki2c);
+ if (ret < 0)
+ /*
+ * For EEPROMs this is normal behavior during internal write
+ * operation.
+ */
+ dev_dbg(&ki2c->auxdev->auxdev.dev,
+ "%s wait for ACK err at 0x%02x!\n", __func__, m->addr);
+
+ return ret;
+}
+
+static int ki2c_repstart_addr(struct ki2c *ki2c, struct i2c_msg *m)
+{
+ int ret;
+
+ /* repeated start and write is not supported */
+ if ((m->flags & I2C_M_RD) == 0) {
+ dev_err(&ki2c->auxdev->auxdev.dev,
+ "Repeated start not supported for writes\n");
+ return -EINVAL;
+ }
+
+ /* send repeated start */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_RSTA,
+ ki2c->base + KI2C_CONTROL_REG);
+
+ ret = ki2c_wait_for_mcf(ki2c);
+ if (ret < 0) {
+ dev_err(&ki2c->auxdev->auxdev.dev,
+ "%s wait for MCF err at 0x%02x!\n", __func__, m->addr);
+ return ret;
+ }
+
+ /* write target-address byte */
+ ki2c_write_target_addr(ki2c, m);
+
+ ret = ki2c_wait_for_data_ack(ki2c);
+ if (ret < 0)
+ dev_err(&ki2c->auxdev->auxdev.dev,
+ "%s wait for ACK err at 0x%02x!\n", __func__, m->addr);
+
+ return ret;
+}
+
+static void ki2c_stop(struct ki2c *ki2c)
+{
+ iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
+ ki2c_wait_for_mcf(ki2c);
+}
+
+static int ki2c_write(struct ki2c *ki2c, const u8 *data, int len)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ /* write data byte */
+ iowrite8(data[i], ki2c->base + KI2C_DATA_REG);
+
+ ret = ki2c_wait_for_data_ack(ki2c);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ki2c_read(struct ki2c *ki2c, u8 *data, int len)
+{
+ u8 control;
+ int ret;
+ int i;
+
+ if (len == 0)
+ return 0; /* nothing to do */
+
+ control = KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA;
+
+ /* if just one byte => send tx-nack after transfer */
+ if (len == 1)
+ control |= KI2C_CONTROL_TXAK;
+
+ iowrite8(control, ki2c->base + KI2C_CONTROL_REG);
+
+ /* dummy read to start transfer on bus */
+ ioread8(ki2c->base + KI2C_DATA_REG);
+
+ for (i = 0; i < len; i++) {
+ ret = ki2c_wait_for_data(ki2c);
+ if (ret < 0)
+ return ret;
+
+ if (i == len - 2)
+ /* send tx-nack after transfer of last byte */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK,
+ ki2c->base + KI2C_CONTROL_REG);
+ else if (i == len - 1)
+ /*
+ * switch to TX on last byte, so that reading DATA
+ * register does not trigger another read transfer
+ */
+ iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_MTX,
+ ki2c->base + KI2C_CONTROL_REG);
+
+ /* read byte and start next transfer (if not last byte) */
+ data[i] = ioread8(ki2c->base + KI2C_DATA_REG);
+ }
+
+ return len;
+}
+
+static int ki2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct ki2c *ki2c = i2c_get_adapdata(adap);
+ int ret;
+ int i;
+
+ ret = ki2c_inuse_lock(ki2c);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *m = &msgs[i];
+
+ if (i == 0)
+ ret = ki2c_start_addr(ki2c, m);
+ else
+ ret = ki2c_repstart_addr(ki2c, m);
+ if (ret < 0)
+ break;
+
+ if (m->flags & I2C_M_RD)
+ ret = ki2c_read(ki2c, m->buf, m->len);
+ else
+ ret = ki2c_write(ki2c, m->buf, m->len);
+ if (ret < 0)
+ break;
+ }
+
+ ki2c_stop(ki2c);
+
+ ki2c_inuse_unlock(ki2c);
+
+ return ret < 0 ? ret : num;
+}
+
+static void ki2c_unregister_devices(struct ki2c *ki2c)
+{
+ int i;
+
+ for (i = 0; i < ki2c->client_size; i++)
+ i2c_unregister_device(ki2c->client[i]);
+}
+
+static int ki2c_register_devices(struct ki2c *ki2c)
+{
+ struct i2c_board_info *info = ki2c->auxdev->info;
+ int i;
+
+ /* register all known I2C devices */
+ for (i = 0; i < ki2c->client_size; i++) {
+ struct i2c_client *client;
+ unsigned short const addr_list[2] = { info[i].addr,
+ I2C_CLIENT_END };
+
+ client = i2c_new_scanned_device(&ki2c->adapter, &info[i],
+ addr_list, NULL);
+ if (!IS_ERR(client)) {
+ ki2c->client[i] = client;
+ } else if (PTR_ERR(client) != -ENODEV) {
+ ki2c->client_size = i;
+ ki2c_unregister_devices(ki2c);
+
+ return PTR_ERR(client);
+ }
+ }
+
+ return 0;
+}
+
+static u32 ki2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm ki2c_algo = {
+ .xfer = ki2c_xfer,
+ .functionality = ki2c_func,
+};
+
+static int ki2c_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &auxdev->dev;
+ struct i2c_adapter *adap;
+ struct ki2c *ki2c;
+ int ret;
+
+ ki2c = devm_kzalloc(dev, sizeof(*ki2c), GFP_KERNEL);
+ if (!ki2c)
+ return -ENOMEM;
+ ki2c->auxdev = container_of(auxdev, struct keba_i2c_auxdev, auxdev);
+ ki2c->client = devm_kcalloc(dev, ki2c->auxdev->info_size,
+ sizeof(*ki2c->client), GFP_KERNEL);
+ if (!ki2c->client)
+ return -ENOMEM;
+ ki2c->client_size = ki2c->auxdev->info_size;
+ auxiliary_set_drvdata(auxdev, ki2c);
+
+ ki2c->base = devm_ioremap_resource(dev, &ki2c->auxdev->io);
+ if (IS_ERR(ki2c->base))
+ return PTR_ERR(ki2c->base);
+
+ adap = &ki2c->adapter;
+ strscpy(adap->name, "KEBA I2C adapter", sizeof(adap->name));
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ adap->algo = &ki2c_algo;
+ adap->dev.parent = dev;
+
+ i2c_set_adapdata(adap, ki2c);
+
+ /* enable controller */
+ iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
+
+ /* reset bus before probing I2C devices */
+ ret = ki2c_reset_bus(ki2c);
+ if (ret)
+ goto out;
+
+ ret = devm_i2c_add_adapter(dev, adap);
+ if (ret) {
+ dev_err(dev, "Failed to add adapter (%d)!\n", ret);
+ goto out;
+ }
+
+ ret = ki2c_register_devices(ki2c);
+ if (ret) {
+ dev_err(dev, "Failed to register devices (%d)!\n", ret);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG);
+ return ret;
+}
+
+static void ki2c_remove(struct auxiliary_device *auxdev)
+{
+ struct ki2c *ki2c = auxiliary_get_drvdata(auxdev);
+
+ ki2c_unregister_devices(ki2c);
+
+ /* disable controller */
+ iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG);
+
+ auxiliary_set_drvdata(auxdev, NULL);
+}
+
+static const struct auxiliary_device_id ki2c_devtype_aux[] = {
+ { .name = "keba.i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ki2c_devtype_aux);
+
+static struct auxiliary_driver ki2c_driver_aux = {
+ .name = KI2C,
+ .id_table = ki2c_devtype_aux,
+ .probe = ki2c_probe,
+ .remove = ki2c_remove,
+};
+module_auxiliary_driver(ki2c_driver_aux);
+
+MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
+MODULE_DESCRIPTION("KEBA I2C bus controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c
index 1c874aaa0447..9b4c7cba62b6 100644
--- a/drivers/i2c/busses/i2c-kempld.c
+++ b/drivers/i2c/busses/i2c-kempld.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C bus driver for Kontron COM modules
*
@@ -5,15 +6,6 @@
* Author: Michael Brunner <michael.brunner@kontron.com>
*
* The driver is based on the i2c-ocores driver by Peter Korsgaard.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -123,9 +115,7 @@ static int kempld_i2c_process(struct kempld_i2c_data *i2c)
if (i2c->state == STATE_ADDR) {
/* 10 bit address? */
if (i2c->msg->flags & I2C_M_TEN) {
- addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6);
- /* Set read bit if necessary */
- addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0;
+ addr = i2c_10bit_addr_hi_from_msg(msg);
i2c->state = STATE_ADDR10;
} else {
addr = i2c_8bit_addr_from_msg(i2c->msg);
@@ -140,10 +130,12 @@ static int kempld_i2c_process(struct kempld_i2c_data *i2c)
/* Second part of 10 bit addressing */
if (i2c->state == STATE_ADDR10) {
- kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff);
+ addr = i2c_10bit_addr_lo_from_msg(msg);
+ i2c->state = STATE_START;
+
+ kempld_write8(pld, KEMPLD_I2C_DATA, addr);
kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE);
- i2c->state = STATE_START;
return 0;
}
@@ -284,14 +276,14 @@ static u32 kempld_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm kempld_i2c_algorithm = {
- .master_xfer = kempld_i2c_xfer,
- .functionality = kempld_i2c_func,
+ .xfer = kempld_i2c_xfer,
+ .functionality = kempld_i2c_func,
};
static const struct i2c_adapter kempld_i2c_adapter = {
.owner = THIS_MODULE,
.name = "i2c-kempld",
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED,
.algo = &kempld_i2c_algorithm,
};
@@ -310,6 +302,7 @@ static int kempld_i2c_probe(struct platform_device *pdev)
i2c->dev = &pdev->dev;
i2c->adap = kempld_i2c_adapter;
i2c->adap.dev.parent = i2c->dev;
+ ACPI_COMPANION_SET(&i2c->adap.dev, ACPI_COMPANION(&pdev->dev));
i2c_set_adapdata(&i2c->adap, i2c);
platform_set_drvdata(pdev, i2c);
@@ -335,7 +328,7 @@ static int kempld_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int kempld_i2c_remove(struct platform_device *pdev)
+static void kempld_i2c_remove(struct platform_device *pdev)
{
struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
struct kempld_device_data *pld = i2c->pld;
@@ -354,14 +347,11 @@ static int kempld_i2c_remove(struct platform_device *pdev)
kempld_release_mutex(pld);
i2c_del_adapter(&i2c->adap);
-
- return 0;
}
-#ifdef CONFIG_PM
-static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+static int kempld_i2c_suspend(struct device *dev)
{
- struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
+ struct kempld_i2c_data *i2c = dev_get_drvdata(dev);
struct kempld_device_data *pld = i2c->pld;
u8 ctrl;
@@ -374,9 +364,9 @@ static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int kempld_i2c_resume(struct platform_device *pdev)
+static int kempld_i2c_resume(struct device *dev)
{
- struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
+ struct kempld_i2c_data *i2c = dev_get_drvdata(dev);
struct kempld_device_data *pld = i2c->pld;
kempld_get_mutex(pld);
@@ -385,19 +375,17 @@ static int kempld_i2c_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define kempld_i2c_suspend NULL
-#define kempld_i2c_resume NULL
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(kempld_i2c_pm_ops,
+ kempld_i2c_suspend, kempld_i2c_resume);
static struct platform_driver kempld_i2c_driver = {
.driver = {
.name = "kempld-i2c",
+ .pm = pm_sleep_ptr(&kempld_i2c_pm_ops),
},
.probe = kempld_i2c_probe,
.remove = kempld_i2c_remove,
- .suspend = kempld_i2c_suspend,
- .resume = kempld_i2c_resume,
};
module_platform_driver(kempld_i2c_driver);
diff --git a/drivers/i2c/busses/i2c-ljca.c b/drivers/i2c/busses/i2c-ljca.c
new file mode 100644
index 000000000000..93274f0c2d72
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ljca.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel La Jolla Cove Adapter USB-I2C driver
+ *
+ * Copyright (c) 2023, Intel Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/dev_printk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/usb/ljca.h>
+
+/* I2C init flags */
+#define LJCA_I2C_INIT_FLAG_MODE BIT(0)
+#define LJCA_I2C_INIT_FLAG_MODE_POLLING FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 0)
+#define LJCA_I2C_INIT_FLAG_MODE_INTERRUPT FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 1)
+
+#define LJCA_I2C_INIT_FLAG_ADDR_16BIT BIT(0)
+
+#define LJCA_I2C_INIT_FLAG_FREQ GENMASK(2, 1)
+#define LJCA_I2C_INIT_FLAG_FREQ_100K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 0)
+#define LJCA_I2C_INIT_FLAG_FREQ_400K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 1)
+#define LJCA_I2C_INIT_FLAG_FREQ_1M FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 2)
+
+#define LJCA_I2C_BUF_SIZE 60u
+#define LJCA_I2C_MAX_XFER_SIZE (LJCA_I2C_BUF_SIZE - sizeof(struct ljca_i2c_rw_packet))
+
+/* I2C commands */
+enum ljca_i2c_cmd {
+ LJCA_I2C_INIT = 1,
+ LJCA_I2C_XFER,
+ LJCA_I2C_START,
+ LJCA_I2C_STOP,
+ LJCA_I2C_READ,
+ LJCA_I2C_WRITE,
+};
+
+enum ljca_xfer_type {
+ LJCA_I2C_WRITE_XFER_TYPE,
+ LJCA_I2C_READ_XFER_TYPE,
+};
+
+/* I2C raw commands: Init/Start/Read/Write/Stop */
+struct ljca_i2c_rw_packet {
+ u8 id;
+ __le16 len;
+ u8 data[] __counted_by(len);
+} __packed;
+
+struct ljca_i2c_dev {
+ struct ljca_client *ljca;
+ struct ljca_i2c_info *i2c_info;
+ struct i2c_adapter adap;
+
+ u8 obuf[LJCA_I2C_BUF_SIZE];
+ u8 ibuf[LJCA_I2C_BUF_SIZE];
+};
+
+static int ljca_i2c_init(struct ljca_i2c_dev *ljca_i2c, u8 id)
+{
+ struct ljca_i2c_rw_packet *w_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
+ int ret;
+
+ w_packet->id = id;
+ w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
+ w_packet->data[0] = LJCA_I2C_INIT_FLAG_FREQ_400K;
+
+ ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_INIT, (u8 *)w_packet,
+ struct_size(w_packet, data, 1), NULL, 0);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 target_addr,
+ enum ljca_xfer_type type)
+{
+ struct ljca_i2c_rw_packet *w_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
+ struct ljca_i2c_rw_packet *r_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
+ s16 rp_len;
+ int ret;
+
+ w_packet->id = ljca_i2c->i2c_info->id;
+ w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
+ w_packet->data[0] = (target_addr << 1) | type;
+
+ ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_START, (u8 *)w_packet,
+ struct_size(w_packet, data, 1), (u8 *)r_packet,
+ LJCA_I2C_BUF_SIZE);
+ if (ret < 0 || ret < sizeof(*r_packet))
+ return ret < 0 ? ret : -EIO;
+
+ rp_len = le16_to_cpu(r_packet->len);
+ if (rp_len < 0 || r_packet->id != w_packet->id) {
+ dev_dbg(&ljca_i2c->adap.dev,
+ "i2c start failed len: %d id: %d %d\n",
+ rp_len, r_packet->id, w_packet->id);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c)
+{
+ struct ljca_i2c_rw_packet *w_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
+ struct ljca_i2c_rw_packet *r_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
+ s16 rp_len;
+ int ret;
+
+ w_packet->id = ljca_i2c->i2c_info->id;
+ w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
+ w_packet->data[0] = 0;
+
+ ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_STOP, (u8 *)w_packet,
+ struct_size(w_packet, data, 1), (u8 *)r_packet,
+ LJCA_I2C_BUF_SIZE);
+ if (ret < 0 || ret < sizeof(*r_packet)) {
+ dev_dbg(&ljca_i2c->adap.dev,
+ "i2c stop failed ret: %d id: %d\n",
+ ret, w_packet->id);
+ return;
+ }
+
+ rp_len = le16_to_cpu(r_packet->len);
+ if (rp_len < 0 || r_packet->id != w_packet->id)
+ dev_dbg(&ljca_i2c->adap.dev,
+ "i2c stop failed len: %d id: %d %d\n",
+ rp_len, r_packet->id, w_packet->id);
+}
+
+static int ljca_i2c_pure_read(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len)
+{
+ struct ljca_i2c_rw_packet *w_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
+ struct ljca_i2c_rw_packet *r_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
+ s16 rp_len;
+ int ret;
+
+ w_packet->id = ljca_i2c->i2c_info->id;
+ w_packet->len = cpu_to_le16(len);
+ w_packet->data[0] = 0;
+
+ ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_READ, (u8 *)w_packet,
+ struct_size(w_packet, data, 1), (u8 *)r_packet,
+ LJCA_I2C_BUF_SIZE);
+ if (ret < 0 || ret < sizeof(*r_packet))
+ return ret < 0 ? ret : -EIO;
+
+ rp_len = le16_to_cpu(r_packet->len);
+ if (rp_len != len || r_packet->id != w_packet->id) {
+ dev_dbg(&ljca_i2c->adap.dev,
+ "i2c raw read failed len: %d id: %d %d\n",
+ rp_len, r_packet->id, w_packet->id);
+ return -EIO;
+ }
+
+ memcpy(data, r_packet->data, len);
+
+ return 0;
+}
+
+static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, u8 *data,
+ u8 len)
+{
+ int ret;
+
+ ret = ljca_i2c_start(ljca_i2c, target_addr, LJCA_I2C_READ_XFER_TYPE);
+ if (!ret)
+ ret = ljca_i2c_pure_read(ljca_i2c, data, len);
+
+ ljca_i2c_stop(ljca_i2c);
+
+ return ret;
+}
+
+static int ljca_i2c_pure_write(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len)
+{
+ struct ljca_i2c_rw_packet *w_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
+ struct ljca_i2c_rw_packet *r_packet =
+ (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
+ s16 rplen;
+ int ret;
+
+ w_packet->id = ljca_i2c->i2c_info->id;
+ w_packet->len = cpu_to_le16(len);
+ memcpy(w_packet->data, data, len);
+
+ ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_WRITE, (u8 *)w_packet,
+ struct_size(w_packet, data, len), (u8 *)r_packet,
+ LJCA_I2C_BUF_SIZE);
+ if (ret < 0 || ret < sizeof(*r_packet))
+ return ret < 0 ? ret : -EIO;
+
+ rplen = le16_to_cpu(r_packet->len);
+ if (rplen != len || r_packet->id != w_packet->id) {
+ dev_dbg(&ljca_i2c->adap.dev,
+ "i2c write failed len: %d id: %d/%d\n",
+ rplen, r_packet->id, w_packet->id);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 target_addr,
+ u8 *data, u8 len)
+{
+ int ret;
+
+ ret = ljca_i2c_start(ljca_i2c, target_addr, LJCA_I2C_WRITE_XFER_TYPE);
+ if (!ret)
+ ret = ljca_i2c_pure_write(ljca_i2c, data, len);
+
+ ljca_i2c_stop(ljca_i2c);
+
+ return ret;
+}
+
+static int ljca_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ int num)
+{
+ struct ljca_i2c_dev *ljca_i2c;
+ struct i2c_msg *cur_msg;
+ int i, ret;
+
+ ljca_i2c = i2c_get_adapdata(adapter);
+ if (!ljca_i2c)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ cur_msg = &msg[i];
+ if (cur_msg->flags & I2C_M_RD)
+ ret = ljca_i2c_read(ljca_i2c, cur_msg->addr,
+ cur_msg->buf, cur_msg->len);
+ else
+ ret = ljca_i2c_write(ljca_i2c, cur_msg->addr,
+ cur_msg->buf, cur_msg->len);
+
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
+static u32 ljca_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_adapter_quirks ljca_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+ .max_read_len = LJCA_I2C_MAX_XFER_SIZE,
+ .max_write_len = LJCA_I2C_MAX_XFER_SIZE,
+};
+
+static const struct i2c_algorithm ljca_i2c_algo = {
+ .xfer = ljca_i2c_xfer,
+ .functionality = ljca_i2c_func,
+};
+
+static int ljca_i2c_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *aux_dev_id)
+{
+ struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev);
+ struct ljca_i2c_dev *ljca_i2c;
+ int ret;
+
+ ljca_i2c = devm_kzalloc(&auxdev->dev, sizeof(*ljca_i2c), GFP_KERNEL);
+ if (!ljca_i2c)
+ return -ENOMEM;
+
+ ljca_i2c->ljca = ljca;
+ ljca_i2c->i2c_info = dev_get_platdata(&auxdev->dev);
+
+ ljca_i2c->adap.owner = THIS_MODULE;
+ ljca_i2c->adap.class = I2C_CLASS_HWMON;
+ ljca_i2c->adap.algo = &ljca_i2c_algo;
+ ljca_i2c->adap.quirks = &ljca_i2c_quirks;
+ ljca_i2c->adap.dev.parent = &auxdev->dev;
+
+ snprintf(ljca_i2c->adap.name, sizeof(ljca_i2c->adap.name), "%s-%s-%d",
+ dev_name(&auxdev->dev), dev_name(auxdev->dev.parent),
+ ljca_i2c->i2c_info->id);
+
+ device_set_node(&ljca_i2c->adap.dev, dev_fwnode(&auxdev->dev));
+
+ i2c_set_adapdata(&ljca_i2c->adap, ljca_i2c);
+ auxiliary_set_drvdata(auxdev, ljca_i2c);
+
+ ret = ljca_i2c_init(ljca_i2c, ljca_i2c->i2c_info->id);
+ if (ret)
+ return dev_err_probe(&auxdev->dev, -EIO,
+ "i2c init failed id: %d\n",
+ ljca_i2c->i2c_info->id);
+
+ ret = devm_i2c_add_adapter(&auxdev->dev, &ljca_i2c->adap);
+ if (ret)
+ return ret;
+
+ if (has_acpi_companion(&ljca_i2c->adap.dev))
+ acpi_dev_clear_dependencies(ACPI_COMPANION(&ljca_i2c->adap.dev));
+
+ return 0;
+}
+
+static void ljca_i2c_remove(struct auxiliary_device *auxdev)
+{
+ struct ljca_i2c_dev *ljca_i2c = auxiliary_get_drvdata(auxdev);
+
+ i2c_del_adapter(&ljca_i2c->adap);
+}
+
+static const struct auxiliary_device_id ljca_i2c_id_table[] = {
+ { "usb_ljca.ljca-i2c", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, ljca_i2c_id_table);
+
+static struct auxiliary_driver ljca_i2c_driver = {
+ .probe = ljca_i2c_probe,
+ .remove = ljca_i2c_remove,
+ .id_table = ljca_i2c_id_table,
+};
+module_auxiliary_driver(ljca_i2c_driver);
+
+MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
+MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
+MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LJCA");
diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c
index 59167c018ae7..ccd13c4fb83e 100644
--- a/drivers/i2c/busses/i2c-lpc2k.c
+++ b/drivers/i2c/busses/i2c-lpc2k.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 NXP Semiconductors
*
@@ -9,12 +10,6 @@
* Anton Protopopov, Emcraft Systems, antonp@emcraft.com
*
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
*/
#include <linux/clk.h>
@@ -25,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/time.h>
@@ -56,7 +50,7 @@
/*
* 26 possible I2C status codes, but codes applicable only
- * to master are listed here and used in this driver
+ * to controller mode are listed here and used in this driver
*/
enum {
M_BUS_ERROR = 0x00,
@@ -163,7 +157,7 @@ static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
break;
case MR_ADDR_R_ACK:
- /* Receive first byte from slave */
+ /* Receive first byte from target */
if (i2c->msg->len == 1) {
/* Last byte, return NACK */
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
@@ -202,7 +196,7 @@ static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
}
/*
- * One pre-last data input, send NACK to tell the slave that
+ * One pre-last data input, send NACK to tell the target that
* this is going to be the last data byte to be transferred.
*/
if (i2c->msg_idx >= i2c->msg->len - 2) {
@@ -344,14 +338,13 @@ static u32 i2c_lpc2k_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm i2c_lpc2k_algorithm = {
- .master_xfer = i2c_lpc2k_xfer,
- .functionality = i2c_lpc2k_functionality,
+ .xfer = i2c_lpc2k_xfer,
+ .functionality = i2c_lpc2k_functionality,
};
static int i2c_lpc2k_probe(struct platform_device *pdev)
{
struct lpc2k_i2c *i2c;
- struct resource *res;
u32 bus_clk_rate;
u32 scl_high;
u32 clkrate;
@@ -361,36 +354,27 @@ static int i2c_lpc2k_probe(struct platform_device *pdev)
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
i2c->irq = platform_get_irq(pdev, 0);
- if (i2c->irq < 0) {
- dev_err(&pdev->dev, "can't get interrupt resource\n");
+ if (i2c->irq < 0)
return i2c->irq;
- }
init_waitqueue_head(&i2c->wait);
- i2c->clk = devm_clk_get(&pdev->dev, NULL);
+ i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(i2c->clk)) {
- dev_err(&pdev->dev, "error getting clock\n");
+ dev_err(&pdev->dev, "failed to enable clock.\n");
return PTR_ERR(i2c->clk);
}
- ret = clk_prepare_enable(i2c->clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable clock.\n");
- return ret;
- }
-
ret = devm_request_irq(&pdev->dev, i2c->irq, i2c_lpc2k_handler, 0,
dev_name(&pdev->dev), i2c);
if (ret < 0) {
dev_err(&pdev->dev, "can't request interrupt.\n");
- goto fail_clk;
+ return ret;
}
disable_irq_nosync(i2c->irq);
@@ -401,20 +385,19 @@ static int i2c_lpc2k_probe(struct platform_device *pdev)
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&bus_clk_rate);
if (ret)
- bus_clk_rate = 100000; /* 100 kHz default clock rate */
+ bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
clkrate = clk_get_rate(i2c->clk);
if (clkrate == 0) {
dev_err(&pdev->dev, "can't get I2C base clock\n");
- ret = -EINVAL;
- goto fail_clk;
+ return -EINVAL;
}
/* Setup I2C dividers to generate clock with proper duty cycle */
clkrate = clkrate / bus_clk_rate;
- if (bus_clk_rate <= 100000)
+ if (bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ)
scl_high = (clkrate * I2C_STD_MODE_DUTY) / 100;
- else if (bus_clk_rate <= 400000)
+ else if (bus_clk_rate <= I2C_MAX_FAST_MODE_FREQ)
scl_high = (clkrate * I2C_FAST_MODE_DUTY) / 100;
else
scl_high = (clkrate * I2C_FAST_MODE_PLUS_DUTY) / 100;
@@ -426,35 +409,27 @@ static int i2c_lpc2k_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.owner = THIS_MODULE;
- strlcpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name));
i2c->adap.algo = &i2c_lpc2k_algorithm;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
- goto fail_clk;
+ return ret;
dev_info(&pdev->dev, "LPC2K I2C adapter\n");
return 0;
-
-fail_clk:
- clk_disable_unprepare(i2c->clk);
- return ret;
}
-static int i2c_lpc2k_remove(struct platform_device *dev)
+static void i2c_lpc2k_remove(struct platform_device *dev)
{
struct lpc2k_i2c *i2c = platform_get_drvdata(dev);
i2c_del_adapter(&i2c->adap);
- clk_disable_unprepare(i2c->clk);
-
- return 0;
}
-#ifdef CONFIG_PM
static int i2c_lpc2k_suspend(struct device *dev)
{
struct lpc2k_i2c *i2c = dev_get_drvdata(dev);
@@ -467,8 +442,13 @@ static int i2c_lpc2k_suspend(struct device *dev)
static int i2c_lpc2k_resume(struct device *dev)
{
struct lpc2k_i2c *i2c = dev_get_drvdata(dev);
+ int ret;
- clk_enable(i2c->clk);
+ ret = clk_enable(i2c->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock.\n");
+ return ret;
+ }
i2c_lpc2k_reset(i2c);
return 0;
@@ -479,11 +459,6 @@ static const struct dev_pm_ops i2c_lpc2k_dev_pm_ops = {
.resume_noirq = i2c_lpc2k_resume,
};
-#define I2C_LPC2K_DEV_PM_OPS (&i2c_lpc2k_dev_pm_ops)
-#else
-#define I2C_LPC2K_DEV_PM_OPS NULL
-#endif
-
static const struct of_device_id lpc2k_i2c_match[] = {
{ .compatible = "nxp,lpc1788-i2c" },
{},
@@ -492,10 +467,10 @@ MODULE_DEVICE_TABLE(of, lpc2k_i2c_match);
static struct platform_driver i2c_lpc2k_driver = {
.probe = i2c_lpc2k_probe,
- .remove = i2c_lpc2k_remove,
+ .remove = i2c_lpc2k_remove,
.driver = {
.name = "lpc2k-i2c",
- .pm = I2C_LPC2K_DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&i2c_lpc2k_dev_pm_ops),
.of_match_table = lpc2k_i2c_match,
},
};
diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c
new file mode 100644
index 000000000000..b475dd27b7af
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ls2x.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K/Loongson LS7A I2C controller mode driver
+ *
+ * Copyright (C) 2013 Loongson Technology Corporation Limited.
+ * Copyright (C) 2014-2017 Lemote, Inc.
+ * Copyright (C) 2018-2022 Loongson Technology Corporation Limited.
+ *
+ * Originally written by liushaozong
+ * Rewritten for mainline by Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/units.h>
+
+/* I2C Registers */
+#define I2C_LS2X_PRER_LO 0x0 /* Freq Division Low Byte Register */
+#define I2C_LS2X_PRER_HI 0x1 /* Freq Division High Byte Register */
+#define I2C_LS2X_CTR 0x2 /* Control Register */
+#define I2C_LS2X_TXR 0x3 /* Transport Data Register */
+#define I2C_LS2X_RXR 0x3 /* Receive Data Register */
+#define I2C_LS2X_CR 0x4 /* Command Control Register */
+#define I2C_LS2X_SR 0x4 /* State Register */
+
+/* Command Control Register Bit */
+#define LS2X_CR_START BIT(7) /* Start signal */
+#define LS2X_CR_STOP BIT(6) /* Stop signal */
+#define LS2X_CR_READ BIT(5) /* Read signal */
+#define LS2X_CR_WRITE BIT(4) /* Write signal */
+#define LS2X_CR_ACK BIT(3) /* Response signal */
+#define LS2X_CR_IACK BIT(0) /* Interrupt response signal */
+
+/* State Register Bit */
+#define LS2X_SR_NOACK BIT(7) /* Receive NACK */
+#define LS2X_SR_BUSY BIT(6) /* Bus busy state */
+#define LS2X_SR_AL BIT(5) /* Arbitration lost */
+#define LS2X_SR_TIP BIT(1) /* Transmission state */
+#define LS2X_SR_IF BIT(0) /* Interrupt flag */
+
+/* Control Register Bit */
+#define LS2X_CTR_EN BIT(7) /* 0: I2c frequency setting 1: Normal */
+#define LS2X_CTR_IEN BIT(6) /* Enable i2c interrupt */
+#define LS2X_CTR_MST BIT(5) /* 0: Target mode 1: Controller mode */
+#define CTR_FREQ_MASK GENMASK(7, 6)
+#define CTR_READY_MASK GENMASK(7, 5)
+
+/* The PCLK frequency from LPB */
+#define LS2X_I2C_PCLK_FREQ (50 * HZ_PER_MHZ)
+
+/* The default bus frequency, which is an empirical value */
+#define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ)
+
+struct ls2x_i2c_priv {
+ struct i2c_adapter adapter;
+ void __iomem *base;
+ struct i2c_timings i2c_t;
+ struct completion cmd_complete;
+};
+
+/*
+ * Interrupt service routine.
+ * This gets called whenever an I2C interrupt occurs.
+ */
+static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct ls2x_i2c_priv *priv = dev_id;
+
+ if (!(readb(priv->base + I2C_LS2X_SR) & LS2X_SR_IF))
+ return IRQ_NONE;
+
+ writeb(LS2X_CR_IACK, priv->base + I2C_LS2X_CR);
+ complete(&priv->cmd_complete);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The ls2x i2c controller supports standard mode and fast mode, so the
+ * maximum bus frequency is '400kHz'.
+ * The bus frequency is set to the empirical value of '33KHz' by default,
+ * but it can also be taken from ACPI or FDT for compatibility with more
+ * devices.
+ */
+static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
+{
+ u16 val;
+ struct i2c_timings *t = &priv->i2c_t;
+ struct device *dev = priv->adapter.dev.parent;
+ u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
+
+ i2c_parse_fw_timings(dev, t, false);
+
+ if (acpi_speed || t->bus_freq_hz)
+ t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
+ else
+ t->bus_freq_hz = LS2X_I2C_FREQ_STD;
+
+ /*
+ * According to the chip manual, we can only access the registers as bytes,
+ * otherwise the high bits will be truncated.
+ * So set the I2C frequency with a sequential writeb() instead of writew().
+ */
+ val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
+ writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_LO);
+ writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_HI);
+}
+
+static void ls2x_i2c_init(struct ls2x_i2c_priv *priv)
+{
+ /* Set i2c frequency setting mode and disable interrupts. */
+ writeb(readb(priv->base + I2C_LS2X_CTR) & ~CTR_FREQ_MASK,
+ priv->base + I2C_LS2X_CTR);
+
+ ls2x_i2c_adjust_bus_speed(priv);
+
+ /* Set i2c normal operating mode and enable interrupts. */
+ writeb(readb(priv->base + I2C_LS2X_CTR) | CTR_READY_MASK,
+ priv->base + I2C_LS2X_CTR);
+}
+
+static int ls2x_i2c_xfer_byte(struct ls2x_i2c_priv *priv, u8 txdata, u8 *rxdatap)
+{
+ u8 rxdata;
+ unsigned long time_left;
+
+ writeb(txdata, priv->base + I2C_LS2X_CR);
+
+ time_left = wait_for_completion_timeout(&priv->cmd_complete,
+ priv->adapter.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ rxdata = readb(priv->base + I2C_LS2X_SR);
+ if (rxdatap)
+ *rxdatap = rxdata;
+
+ return 0;
+}
+
+static int ls2x_i2c_send_byte(struct ls2x_i2c_priv *priv, u8 txdata)
+{
+ int ret;
+ u8 rxdata;
+
+ ret = ls2x_i2c_xfer_byte(priv, txdata, &rxdata);
+ if (ret)
+ return ret;
+
+ if (rxdata & LS2X_SR_AL)
+ return -EAGAIN;
+
+ if (rxdata & LS2X_SR_NOACK)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int ls2x_i2c_stop(struct ls2x_i2c_priv *priv)
+{
+ u8 value;
+
+ writeb(LS2X_CR_STOP, priv->base + I2C_LS2X_CR);
+ return readb_poll_timeout(priv->base + I2C_LS2X_SR, value,
+ !(value & LS2X_SR_BUSY), 100,
+ jiffies_to_usecs(priv->adapter.timeout));
+}
+
+static int ls2x_i2c_start(struct ls2x_i2c_priv *priv, struct i2c_msg *msgs)
+{
+ reinit_completion(&priv->cmd_complete);
+
+ writeb(i2c_8bit_addr_from_msg(msgs), priv->base + I2C_LS2X_TXR);
+ return ls2x_i2c_send_byte(priv, LS2X_CR_START | LS2X_CR_WRITE);
+}
+
+static int ls2x_i2c_rx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
+{
+ int ret;
+ u8 rxdata, *buf = msg->buf;
+ u16 len = msg->len;
+
+ /* Contains steps to send start condition and address. */
+ ret = ls2x_i2c_start(priv, msg);
+ if (ret)
+ return ret;
+
+ while (len--) {
+ ret = ls2x_i2c_xfer_byte(priv,
+ LS2X_CR_READ | (len ? 0 : LS2X_CR_ACK),
+ &rxdata);
+ if (ret)
+ return ret;
+
+ *buf++ = readb(priv->base + I2C_LS2X_RXR);
+ }
+
+ return 0;
+}
+
+static int ls2x_i2c_tx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
+{
+ int ret;
+ u8 *buf = msg->buf;
+ u16 len = msg->len;
+
+ /* Contains steps to send start condition and address. */
+ ret = ls2x_i2c_start(priv, msg);
+ if (ret)
+ return ret;
+
+ while (len--) {
+ writeb(*buf++, priv->base + I2C_LS2X_TXR);
+
+ ret = ls2x_i2c_send_byte(priv, LS2X_CR_WRITE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ls2x_i2c_xfer_one(struct ls2x_i2c_priv *priv,
+ struct i2c_msg *msg, bool stop)
+{
+ int ret;
+
+ if (msg->flags & I2C_M_RD)
+ ret = ls2x_i2c_rx(priv, msg);
+ else
+ ret = ls2x_i2c_tx(priv, msg);
+
+ if (ret < 0) {
+ /* Fatel error. Needs reinit. */
+ if (ret == -ETIMEDOUT)
+ ls2x_i2c_init(priv);
+
+ return ret;
+ }
+
+ if (stop) {
+ /* Failed to issue STOP. Needs reinit. */
+ ret = ls2x_i2c_stop(priv);
+ if (ret)
+ ls2x_i2c_init(priv);
+ }
+
+ return ret;
+}
+
+static int ls2x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ int ret;
+ struct i2c_msg *msg, *emsg = msgs + num;
+ struct ls2x_i2c_priv *priv = i2c_get_adapdata(adap);
+
+ for (msg = msgs; msg < emsg; msg++) {
+ ret = ls2x_i2c_xfer_one(priv, msg, msg == emsg - 1);
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
+static unsigned int ls2x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm ls2x_i2c_algo = {
+ .xfer = ls2x_i2c_xfer,
+ .functionality = ls2x_i2c_func,
+};
+
+static int ls2x_i2c_probe(struct platform_device *pdev)
+{
+ int ret, irq;
+ struct i2c_adapter *adap;
+ struct ls2x_i2c_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Map hardware registers */
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* Add the i2c adapter */
+ adap = &priv->adapter;
+ adap->retries = 5;
+ adap->nr = pdev->id;
+ adap->dev.parent = dev;
+ adap->owner = THIS_MODULE;
+ adap->algo = &ls2x_i2c_algo;
+ adap->timeout = msecs_to_jiffies(100);
+ device_set_node(&adap->dev, dev_fwnode(dev));
+ i2c_set_adapdata(adap, priv);
+ strscpy(adap->name, pdev->name, sizeof(adap->name));
+ init_completion(&priv->cmd_complete);
+ platform_set_drvdata(pdev, priv);
+
+ ls2x_i2c_init(priv);
+
+ ret = devm_request_irq(dev, irq, ls2x_i2c_isr, IRQF_SHARED, "ls2x-i2c",
+ priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
+
+ return devm_i2c_add_adapter(dev, adap);
+}
+
+static int ls2x_i2c_suspend(struct device *dev)
+{
+ struct ls2x_i2c_priv *priv = dev_get_drvdata(dev);
+
+ /* Disable interrupts */
+ writeb(readb(priv->base + I2C_LS2X_CTR) & ~LS2X_CTR_IEN,
+ priv->base + I2C_LS2X_CTR);
+
+ return 0;
+}
+
+static int ls2x_i2c_resume(struct device *dev)
+{
+ ls2x_i2c_init(dev_get_drvdata(dev));
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
+ ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
+
+static const struct of_device_id ls2x_i2c_id_table[] = {
+ { .compatible = "loongson,ls2k-i2c" },
+ { .compatible = "loongson,ls7a-i2c" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);
+
+static const struct acpi_device_id ls2x_i2c_acpi_match[] = {
+ { "LOON0004" }, /* Loongson LS7A */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match);
+
+static struct platform_driver ls2x_i2c_driver = {
+ .probe = ls2x_i2c_probe,
+ .driver = {
+ .name = "ls2x-i2c",
+ .pm = pm_sleep_ptr(&ls2x_i2c_pm_ops),
+ .of_match_table = ls2x_i2c_id_table,
+ .acpi_match_table = ls2x_i2c_acpi_match,
+ },
+};
+module_platform_driver(ls2x_i2c_driver);
+
+MODULE_DESCRIPTION("Loongson LS2X I2C Bus driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-mchp-pci1xxxx.c b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c
new file mode 100644
index 000000000000..bc0f1a0c8ee1
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip PCI1XXXX I2C adapter driver for PCIe Switch
+ * which has I2C controller in one of its downstream functions
+ *
+ * Copyright (C) 2021 - 2022 Microchip Technology Inc.
+ *
+ * Authors: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
+ * Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#define SMBUS_MAST_CORE_ADDR_BASE 0x00000
+#define SMBUS_MAST_SYS_REG_ADDR_BASE 0x01000
+
+/* SMB register space. */
+#define SMB_CORE_CTRL_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x00)
+
+#define SMB_CORE_CTRL_ESO BIT(6)
+#define SMB_CORE_CTRL_FW_ACK BIT(4)
+#define SMB_CORE_CTRL_ACK BIT(0)
+
+#define SMB_CORE_CMD_REG_OFF3 (SMBUS_MAST_CORE_ADDR_BASE + 0x0F)
+#define SMB_CORE_CMD_REG_OFF2 (SMBUS_MAST_CORE_ADDR_BASE + 0x0E)
+#define SMB_CORE_CMD_REG_OFF1 (SMBUS_MAST_CORE_ADDR_BASE + 0x0D)
+
+#define SMB_CORE_CMD_READM BIT(4)
+#define SMB_CORE_CMD_STOP BIT(2)
+#define SMB_CORE_CMD_START BIT(0)
+
+#define SMB_CORE_CMD_REG_OFF0 (SMBUS_MAST_CORE_ADDR_BASE + 0x0C)
+
+#define SMB_CORE_CMD_M_PROCEED BIT(1)
+#define SMB_CORE_CMD_M_RUN BIT(0)
+
+#define SMB_CORE_SR_HOLD_TIME_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x18)
+
+/*
+ * SR_HOLD_TIME_XK_TICKS field will indicate the number of ticks of the
+ * baud clock required to program 'Hold Time' at X KHz.
+ */
+#define SR_HOLD_TIME_100K_TICKS 150
+#define SR_HOLD_TIME_400K_TICKS 20
+#define SR_HOLD_TIME_1000K_TICKS 12
+
+#define SMB_CORE_COMPLETION_REG_OFF3 (SMBUS_MAST_CORE_ADDR_BASE + 0x23)
+
+#define COMPLETION_MDONE BIT(6)
+#define COMPLETION_IDLE BIT(5)
+#define COMPLETION_MNAKX BIT(0)
+
+#define SMB_CORE_IDLE_SCALING_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x24)
+
+/*
+ * FAIR_BUS_IDLE_MIN_XK_TICKS field will indicate the number of ticks of
+ * the baud clock required to program 'fair idle delay' at X KHz. Fair idle
+ * delay establishes the MCTP T(IDLE_DELAY) period.
+ */
+#define FAIR_BUS_IDLE_MIN_100K_TICKS 992
+#define FAIR_BUS_IDLE_MIN_400K_TICKS 500
+#define FAIR_BUS_IDLE_MIN_1000K_TICKS 500
+
+/*
+ * FAIR_IDLE_DELAY_XK_TICKS field will indicate the number of ticks of the
+ * baud clock required to satisfy the fairness protocol at X KHz.
+ */
+#define FAIR_IDLE_DELAY_100K_TICKS 963
+#define FAIR_IDLE_DELAY_400K_TICKS 156
+#define FAIR_IDLE_DELAY_1000K_TICKS 156
+
+#define SMB_IDLE_SCALING_100K \
+ ((FAIR_IDLE_DELAY_100K_TICKS << 16) | FAIR_BUS_IDLE_MIN_100K_TICKS)
+#define SMB_IDLE_SCALING_400K \
+ ((FAIR_IDLE_DELAY_400K_TICKS << 16) | FAIR_BUS_IDLE_MIN_400K_TICKS)
+#define SMB_IDLE_SCALING_1000K \
+ ((FAIR_IDLE_DELAY_1000K_TICKS << 16) | FAIR_BUS_IDLE_MIN_1000K_TICKS)
+
+#define SMB_CORE_CONFIG_REG3 (SMBUS_MAST_CORE_ADDR_BASE + 0x2B)
+
+#define SMB_CONFIG3_ENMI BIT(6)
+#define SMB_CONFIG3_ENIDI BIT(5)
+
+#define SMB_CORE_CONFIG_REG2 (SMBUS_MAST_CORE_ADDR_BASE + 0x2A)
+#define SMB_CORE_CONFIG_REG1 (SMBUS_MAST_CORE_ADDR_BASE + 0x29)
+
+#define SMB_CONFIG1_ASR BIT(7)
+#define SMB_CONFIG1_ENAB BIT(2)
+#define SMB_CONFIG1_RESET BIT(1)
+#define SMB_CONFIG1_FEN BIT(0)
+
+#define SMB_CORE_BUS_CLK_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x2C)
+
+/*
+ * BUS_CLK_XK_LOW_PERIOD_TICKS field defines the number of I2C Baud Clock
+ * periods that make up the low phase of the I2C/SMBus bus clock at X KHz.
+ */
+#define BUS_CLK_100K_LOW_PERIOD_TICKS 156
+#define BUS_CLK_400K_LOW_PERIOD_TICKS 41
+#define BUS_CLK_1000K_LOW_PERIOD_TICKS 15
+
+/*
+ * BUS_CLK_XK_HIGH_PERIOD_TICKS field defines the number of I2C Baud Clock
+ * periods that make up the high phase of the I2C/SMBus bus clock at X KHz.
+ */
+#define BUS_CLK_100K_HIGH_PERIOD_TICKS 154
+#define BUS_CLK_400K_HIGH_PERIOD_TICKS 35
+#define BUS_CLK_1000K_HIGH_PERIOD_TICKS 14
+
+#define BUS_CLK_100K \
+ ((BUS_CLK_100K_HIGH_PERIOD_TICKS << 8) | BUS_CLK_100K_LOW_PERIOD_TICKS)
+#define BUS_CLK_400K \
+ ((BUS_CLK_400K_HIGH_PERIOD_TICKS << 8) | BUS_CLK_400K_LOW_PERIOD_TICKS)
+#define BUS_CLK_1000K \
+ ((BUS_CLK_1000K_HIGH_PERIOD_TICKS << 8) | BUS_CLK_1000K_LOW_PERIOD_TICKS)
+
+#define SMB_CORE_CLK_SYNC_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x3C)
+
+/*
+ * CLK_SYNC_XK defines the number of clock cycles to sync up to the external
+ * clock before comparing the internal and external clocks for clock stretching
+ * at X KHz.
+ */
+#define CLK_SYNC_100K 4
+#define CLK_SYNC_400K 4
+#define CLK_SYNC_1000K 4
+
+#define SMB_CORE_DATA_TIMING_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x40)
+
+/*
+ *
+ * FIRST_START_HOLD_XK_TICKS will indicate the number of ticks of the baud
+ * clock required to program 'FIRST_START_HOLD' timer at X KHz. This timer
+ * determines the SCLK hold time following SDAT driven low during the first
+ * START bit in a transfer.
+ */
+#define FIRST_START_HOLD_100K_TICKS 23
+#define FIRST_START_HOLD_400K_TICKS 8
+#define FIRST_START_HOLD_1000K_TICKS 12
+
+/*
+ * STOP_SETUP_XK_TICKS will indicate the number of ticks of the baud clock
+ * required to program 'STOP_SETUP' timer at X KHz. This timer determines the
+ * SDAT setup time from the rising edge of SCLK for a STOP condition.
+ */
+#define STOP_SETUP_100K_TICKS 150
+#define STOP_SETUP_400K_TICKS 20
+#define STOP_SETUP_1000K_TICKS 12
+
+/*
+ * RESTART_SETUP_XK_TICKS will indicate the number of ticks of the baud clock
+ * required to program 'RESTART_SETUP' timer at X KHz. This timer determines the
+ * SDAT setup time from the rising edge of SCLK for a repeated START condition.
+ */
+#define RESTART_SETUP_100K_TICKS 156
+#define RESTART_SETUP_400K_TICKS 20
+#define RESTART_SETUP_1000K_TICKS 12
+
+/*
+ * DATA_HOLD_XK_TICKS will indicate the number of ticks of the baud clock
+ * required to program 'DATA_HOLD' timer at X KHz. This timer determines the
+ * SDAT hold time following SCLK driven low.
+ */
+#define DATA_HOLD_100K_TICKS 12
+#define DATA_HOLD_400K_TICKS 2
+#define DATA_HOLD_1000K_TICKS 2
+
+#define DATA_TIMING_100K \
+ ((FIRST_START_HOLD_100K_TICKS << 24) | (STOP_SETUP_100K_TICKS << 16) | \
+ (RESTART_SETUP_100K_TICKS << 8) | DATA_HOLD_100K_TICKS)
+#define DATA_TIMING_400K \
+ ((FIRST_START_HOLD_400K_TICKS << 24) | (STOP_SETUP_400K_TICKS << 16) | \
+ (RESTART_SETUP_400K_TICKS << 8) | DATA_HOLD_400K_TICKS)
+#define DATA_TIMING_1000K \
+ ((FIRST_START_HOLD_1000K_TICKS << 24) | (STOP_SETUP_1000K_TICKS << 16) | \
+ (RESTART_SETUP_1000K_TICKS << 8) | DATA_HOLD_1000K_TICKS)
+
+#define SMB_CORE_TO_SCALING_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x44)
+
+/*
+ * BUS_IDLE_MIN_XK_TICKS defines Bus Idle Minimum Time.
+ * Bus Idle Minimum time = BUS_IDLE_MIN[7:0] x Baud_Clock_Period x
+ * (BUS_IDLE_MIN_XK_TICKS[7] ? 4,1)
+ */
+#define BUS_IDLE_MIN_100K_TICKS 36UL
+#define BUS_IDLE_MIN_400K_TICKS 10UL
+#define BUS_IDLE_MIN_1000K_TICKS 4UL
+
+/*
+ * CTRL_CUM_TIME_OUT_XK_TICKS defines SMBus Controller Cumulative Time-Out.
+ * SMBus Controller Cumulative Time-Out duration =
+ * CTRL_CUM_TIME_OUT_XK_TICKS[7:0] x Baud_Clock_Period x 2048
+ */
+#define CTRL_CUM_TIME_OUT_100K_TICKS 76
+#define CTRL_CUM_TIME_OUT_400K_TICKS 76
+#define CTRL_CUM_TIME_OUT_1000K_TICKS 76
+
+/*
+ * TARGET_CUM_TIME_OUT_XK_TICKS defines SMBus Target Cumulative Time-Out duration.
+ * SMBus Target Cumulative Time-Out duration = TARGET_CUM_TIME_OUT_XK_TICKS[7:0] x
+ * Baud_Clock_Period x 4096
+ */
+#define TARGET_CUM_TIME_OUT_100K_TICKS 95
+#define TARGET_CUM_TIME_OUT_400K_TICKS 95
+#define TARGET_CUM_TIME_OUT_1000K_TICKS 95
+
+/*
+ * CLOCK_HIGH_TIME_OUT_XK defines Clock High time out period.
+ * Clock High time out period = CLOCK_HIGH_TIME_OUT_XK[7:0] x Baud_Clock_Period x 8
+ */
+#define CLOCK_HIGH_TIME_OUT_100K_TICKS 97
+#define CLOCK_HIGH_TIME_OUT_400K_TICKS 97
+#define CLOCK_HIGH_TIME_OUT_1000K_TICKS 97
+
+#define TO_SCALING_100K \
+ ((BUS_IDLE_MIN_100K_TICKS << 24) | (CTRL_CUM_TIME_OUT_100K_TICKS << 16) | \
+ (TARGET_CUM_TIME_OUT_100K_TICKS << 8) | CLOCK_HIGH_TIME_OUT_100K_TICKS)
+#define TO_SCALING_400K \
+ ((BUS_IDLE_MIN_400K_TICKS << 24) | (CTRL_CUM_TIME_OUT_400K_TICKS << 16) | \
+ (TARGET_CUM_TIME_OUT_400K_TICKS << 8) | CLOCK_HIGH_TIME_OUT_400K_TICKS)
+#define TO_SCALING_1000K \
+ ((BUS_IDLE_MIN_1000K_TICKS << 24) | (CTRL_CUM_TIME_OUT_1000K_TICKS << 16) | \
+ (TARGET_CUM_TIME_OUT_1000K_TICKS << 8) | CLOCK_HIGH_TIME_OUT_1000K_TICKS)
+
+#define I2C_SCL_PAD_CTRL_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x100)
+#define I2C_SDA_PAD_CTRL_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x101)
+
+#define I2C_FOD_EN BIT(4)
+#define I2C_PULL_UP_EN BIT(3)
+#define I2C_PULL_DOWN_EN BIT(2)
+#define I2C_INPUT_EN BIT(1)
+#define I2C_OUTPUT_EN BIT(0)
+
+#define SMBUS_CONTROL_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x200)
+
+#define CTL_RESET_COUNTERS BIT(3)
+#define CTL_TRANSFER_DIR BIT(2)
+#define CTL_HOST_FIFO_ENTRY BIT(1)
+#define CTL_RUN BIT(0)
+
+#define I2C_DIRN_WRITE 0
+#define I2C_DIRN_READ 1
+
+#define SMBUS_STATUS_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x204)
+
+#define STA_DMA_TERM BIT(7)
+#define STA_DMA_REQ BIT(6)
+#define STA_THRESHOLD BIT(2)
+#define STA_BUF_FULL BIT(1)
+#define STA_BUF_EMPTY BIT(0)
+
+#define SMBUS_INTR_STAT_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x208)
+
+#define INTR_STAT_DMA_TERM BIT(7)
+#define INTR_STAT_THRESHOLD BIT(2)
+#define INTR_STAT_BUF_FULL BIT(1)
+#define INTR_STAT_BUF_EMPTY BIT(0)
+
+#define SMBUS_INTR_MSK_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x20C)
+
+#define INTR_MSK_DMA_TERM BIT(7)
+#define INTR_MSK_THRESHOLD BIT(2)
+#define INTR_MSK_BUF_FULL BIT(1)
+#define INTR_MSK_BUF_EMPTY BIT(0)
+
+#define ALL_NW_LAYER_INTERRUPTS \
+ (INTR_MSK_DMA_TERM | INTR_MSK_THRESHOLD | INTR_MSK_BUF_FULL | \
+ INTR_MSK_BUF_EMPTY)
+
+#define SMBUS_MCU_COUNTER_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x214)
+
+#define SMBALERT_MST_PAD_CTRL_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x230)
+
+#define SMBALERT_MST_PU BIT(0)
+
+#define SMBUS_GEN_INT_STAT_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x23C)
+
+#define SMBUS_GEN_INT_MASK_REG_OFF (SMBUS_MAST_CORE_ADDR_BASE + 0x240)
+
+#define SMBALERT_INTR_MASK BIT(10)
+#define I2C_BUF_MSTR_INTR_MASK BIT(9)
+#define I2C_INTR_MASK BIT(8)
+#define SMBALERT_WAKE_INTR_MASK BIT(2)
+#define I2C_BUF_MSTR_WAKE_INTR_MASK BIT(1)
+#define I2C_WAKE_INTR_MASK BIT(0)
+
+#define ALL_HIGH_LAYER_INTR \
+ (SMBALERT_INTR_MASK | I2C_BUF_MSTR_INTR_MASK | I2C_INTR_MASK | \
+ SMBALERT_WAKE_INTR_MASK | I2C_BUF_MSTR_WAKE_INTR_MASK | \
+ I2C_WAKE_INTR_MASK)
+
+#define SMBUS_RESET_REG (SMBUS_MAST_CORE_ADDR_BASE + 0x248)
+
+#define PERI_SMBUS_D3_RESET_DIS BIT(16)
+
+#define SMBUS_MST_BUF (SMBUS_MAST_CORE_ADDR_BASE + 0x280)
+
+#define SMBUS_BUF_MAX_SIZE 0x80
+
+#define I2C_FLAGS_DIRECT_MODE BIT(7)
+#define I2C_FLAGS_POLLING_MODE BIT(6)
+#define I2C_FLAGS_STOP BIT(5)
+#define I2C_FLAGS_SMB_BLK_READ BIT(4)
+
+#define PCI1XXXX_I2C_TIMEOUT_MS 1000
+
+/* General Purpose Register. */
+#define SMB_GPR_REG (SMBUS_MAST_CORE_ADDR_BASE + 0x1000 + 0x0c00 + \
+ 0x00)
+
+/* Lock Register. */
+#define SMB_GPR_LOCK_REG (SMBUS_MAST_CORE_ADDR_BASE + 0x1000 + 0x0000 + \
+ 0x00A0)
+
+#define SMBUS_PERI_LOCK BIT(3)
+
+struct pci1xxxx_i2c {
+ struct completion i2c_xfer_done;
+ bool i2c_xfer_in_progress;
+ struct i2c_adapter adap;
+ void __iomem *i2c_base;
+ u32 freq;
+ u32 flags;
+};
+
+static int set_sys_lock(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMB_GPR_LOCK_REG;
+ u8 data;
+
+ writel(SMBUS_PERI_LOCK, p);
+ data = readl(p);
+ if (data != SMBUS_PERI_LOCK)
+ return -EPERM;
+
+ return 0;
+}
+
+static int release_sys_lock(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMB_GPR_LOCK_REG;
+ u8 data;
+
+ data = readl(p);
+ if (data != SMBUS_PERI_LOCK)
+ return 0;
+
+ writel(0, p);
+ data = readl(p);
+ if (data & SMBUS_PERI_LOCK)
+ return -EPERM;
+
+ return 0;
+}
+
+static void pci1xxxx_ack_high_level_intr(struct pci1xxxx_i2c *i2c, u16 intr_msk)
+{
+ writew(intr_msk, i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF);
+}
+
+static void pci1xxxx_i2c_configure_smbalert_pin(struct pci1xxxx_i2c *i2c,
+ bool enable)
+{
+ void __iomem *p = i2c->i2c_base + SMBALERT_MST_PAD_CTRL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+
+ if (enable)
+ regval |= SMBALERT_MST_PU;
+ else
+ regval &= ~SMBALERT_MST_PU;
+
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_send_start_stop(struct pci1xxxx_i2c *i2c, bool start)
+{
+ void __iomem *p = i2c->i2c_base + SMB_CORE_CMD_REG_OFF1;
+ u8 regval;
+
+ regval = readb(p);
+
+ if (start)
+ regval |= SMB_CORE_CMD_START;
+ else
+ regval |= SMB_CORE_CMD_STOP;
+
+ writeb(regval, p);
+}
+
+/*
+ * When accessing the core control reg, we should not do a read modified write
+ * as they are write '1' to clear bits. Instead we need to write with the
+ * specific bits that needs to be set.
+ */
+static void pci1xxxx_i2c_set_clear_FW_ACK(struct pci1xxxx_i2c *i2c, bool set)
+{
+ u8 regval;
+
+ if (set)
+ regval = SMB_CORE_CTRL_FW_ACK | SMB_CORE_CTRL_ESO | SMB_CORE_CTRL_ACK;
+ else
+ regval = SMB_CORE_CTRL_ESO | SMB_CORE_CTRL_ACK;
+
+ writeb(regval, i2c->i2c_base + SMB_CORE_CTRL_REG_OFF);
+}
+
+static void pci1xxxx_i2c_buffer_write(struct pci1xxxx_i2c *i2c, u8 slaveaddr,
+ u8 transferlen, unsigned char *buf)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_MST_BUF;
+
+ if (slaveaddr)
+ writeb(slaveaddr, p++);
+
+ if (buf)
+ memcpy_toio(p, buf, transferlen);
+}
+
+/*
+ * When accessing the core control reg, we should not do a read modified write
+ * as there are write '1' to clear bits. Instead we need to write with the
+ * specific bits that needs to be set.
+ */
+static void pci1xxxx_i2c_enable_ESO(struct pci1xxxx_i2c *i2c)
+{
+ writeb(SMB_CORE_CTRL_ESO, i2c->i2c_base + SMB_CORE_CTRL_REG_OFF);
+}
+
+static void pci1xxxx_i2c_reset_counters(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_CONTROL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+ regval |= CTL_RESET_COUNTERS;
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_set_transfer_dir(struct pci1xxxx_i2c *i2c, u8 direction)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_CONTROL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+ if (direction == I2C_DIRN_WRITE)
+ regval &= ~CTL_TRANSFER_DIR;
+ else
+ regval |= CTL_TRANSFER_DIR;
+
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_set_mcu_count(struct pci1xxxx_i2c *i2c, u8 count)
+{
+ writeb(count, i2c->i2c_base + SMBUS_MCU_COUNTER_REG_OFF);
+}
+
+static void pci1xxxx_i2c_set_read_count(struct pci1xxxx_i2c *i2c, u8 readcount)
+{
+ writeb(readcount, i2c->i2c_base + SMB_CORE_CMD_REG_OFF3);
+}
+
+static void pci1xxxx_i2c_set_write_count(struct pci1xxxx_i2c *i2c, u8 writecount)
+{
+ writeb(writecount, i2c->i2c_base + SMB_CORE_CMD_REG_OFF2);
+}
+
+static void pci1xxxx_i2c_set_DMA_run(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_CONTROL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+ regval |= CTL_RUN;
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_set_mrun_proceed(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMB_CORE_CMD_REG_OFF0;
+ u8 regval;
+
+ regval = readb(p);
+ regval |= SMB_CORE_CMD_M_RUN;
+ regval |= SMB_CORE_CMD_M_PROCEED;
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_start_DMA(struct pci1xxxx_i2c *i2c)
+{
+ pci1xxxx_i2c_set_DMA_run(i2c);
+ pci1xxxx_i2c_set_mrun_proceed(i2c);
+}
+
+static void pci1xxxx_i2c_config_asr(struct pci1xxxx_i2c *i2c, bool enable)
+{
+ void __iomem *p = i2c->i2c_base + SMB_CORE_CONFIG_REG1;
+ u8 regval;
+
+ regval = readb(p);
+ if (enable)
+ regval |= SMB_CONFIG1_ASR;
+ else
+ regval &= ~SMB_CONFIG1_ASR;
+ writeb(regval, p);
+}
+
+static irqreturn_t pci1xxxx_i2c_isr(int irq, void *dev)
+{
+ struct pci1xxxx_i2c *i2c = dev;
+ void __iomem *p1 = i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF;
+ void __iomem *p2 = i2c->i2c_base + SMBUS_INTR_STAT_REG_OFF;
+ irqreturn_t intr_handled = IRQ_NONE;
+ u16 reg1;
+ u8 reg3;
+
+ /*
+ * Read the SMBus interrupt status register to see if the
+ * DMA_TERM interrupt has caused this callback.
+ */
+ reg1 = readw(p1);
+
+ if (reg1 & I2C_BUF_MSTR_INTR_MASK) {
+ reg3 = readb(p2);
+ if (reg3 & INTR_STAT_DMA_TERM) {
+ complete(&i2c->i2c_xfer_done);
+ intr_handled = IRQ_HANDLED;
+ writeb(INTR_STAT_DMA_TERM, p2);
+ }
+ pci1xxxx_ack_high_level_intr(i2c, I2C_BUF_MSTR_INTR_MASK);
+ }
+
+ if (reg1 & SMBALERT_INTR_MASK) {
+ intr_handled = IRQ_HANDLED;
+ pci1xxxx_ack_high_level_intr(i2c, SMBALERT_INTR_MASK);
+ }
+
+ return intr_handled;
+}
+
+static void pci1xxxx_i2c_set_count(struct pci1xxxx_i2c *i2c, u8 mcucount,
+ u8 writecount, u8 readcount)
+{
+ pci1xxxx_i2c_set_mcu_count(i2c, mcucount);
+ pci1xxxx_i2c_set_write_count(i2c, writecount);
+ pci1xxxx_i2c_set_read_count(i2c, readcount);
+}
+
+static void pci1xxxx_i2c_set_readm(struct pci1xxxx_i2c *i2c, bool enable)
+{
+ void __iomem *p = i2c->i2c_base + SMB_CORE_CMD_REG_OFF1;
+ u8 regval;
+
+ regval = readb(p);
+ if (enable)
+ regval |= SMB_CORE_CMD_READM;
+ else
+ regval &= ~SMB_CORE_CMD_READM;
+
+ writeb(regval, p);
+}
+
+static void pci1xxxx_ack_nw_layer_intr(struct pci1xxxx_i2c *i2c, u8 ack_intr_msk)
+{
+ writeb(ack_intr_msk, i2c->i2c_base + SMBUS_INTR_STAT_REG_OFF);
+}
+
+static void pci1xxxx_config_nw_layer_intr(struct pci1xxxx_i2c *i2c,
+ u8 intr_msk, bool enable)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_INTR_MSK_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+ if (enable)
+ regval &= ~intr_msk;
+ else
+ regval |= intr_msk;
+
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_config_padctrl(struct pci1xxxx_i2c *i2c, bool enable)
+{
+ void __iomem *p1 = i2c->i2c_base + I2C_SCL_PAD_CTRL_REG_OFF;
+ void __iomem *p2 = i2c->i2c_base + I2C_SDA_PAD_CTRL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p1);
+ if (enable)
+ regval |= I2C_INPUT_EN | I2C_OUTPUT_EN;
+ else
+ regval &= ~(I2C_INPUT_EN | I2C_OUTPUT_EN);
+
+ writeb(regval, p1);
+
+ regval = readb(p2);
+ if (enable)
+ regval |= I2C_INPUT_EN | I2C_OUTPUT_EN;
+ else
+ regval &= ~(I2C_INPUT_EN | I2C_OUTPUT_EN);
+
+ writeb(regval, p2);
+}
+
+static void pci1xxxx_i2c_set_mode(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_CONTROL_REG_OFF;
+ u8 regval;
+
+ regval = readb(p);
+ if (i2c->flags & I2C_FLAGS_DIRECT_MODE)
+ regval &= ~CTL_HOST_FIFO_ENTRY;
+ else
+ regval |= CTL_HOST_FIFO_ENTRY;
+
+ writeb(regval, p);
+}
+
+static void pci1xxxx_i2c_config_high_level_intr(struct pci1xxxx_i2c *i2c,
+ u16 intr_msk, bool enable)
+{
+ void __iomem *p = i2c->i2c_base + SMBUS_GEN_INT_MASK_REG_OFF;
+ u16 regval;
+
+ regval = readw(p);
+ if (enable)
+ regval &= ~intr_msk;
+ else
+ regval |= intr_msk;
+ writew(regval, p);
+}
+
+static void pci1xxxx_i2c_configure_core_reg(struct pci1xxxx_i2c *i2c, bool enable)
+{
+ void __iomem *p1 = i2c->i2c_base + SMB_CORE_CONFIG_REG1;
+ void __iomem *p3 = i2c->i2c_base + SMB_CORE_CONFIG_REG3;
+ u8 reg1;
+ u8 reg3;
+
+ reg1 = readb(p1);
+ reg3 = readb(p3);
+ if (enable) {
+ reg1 |= SMB_CONFIG1_ENAB | SMB_CONFIG1_FEN;
+ reg3 |= SMB_CONFIG3_ENMI | SMB_CONFIG3_ENIDI;
+ } else {
+ reg1 &= ~(SMB_CONFIG1_ENAB | SMB_CONFIG1_FEN);
+ reg3 &= ~(SMB_CONFIG3_ENMI | SMB_CONFIG3_ENIDI);
+ }
+
+ writeb(reg1, p1);
+ writeb(reg3, p3);
+}
+
+static void pci1xxxx_i2c_set_freq(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *bp = i2c->i2c_base;
+ void __iomem *p_idle_scaling = bp + SMB_CORE_IDLE_SCALING_REG_OFF;
+ void __iomem *p_data_timing = bp + SMB_CORE_DATA_TIMING_REG_OFF;
+ void __iomem *p_hold_time = bp + SMB_CORE_SR_HOLD_TIME_REG_OFF;
+ void __iomem *p_to_scaling = bp + SMB_CORE_TO_SCALING_REG_OFF;
+ void __iomem *p_clk_sync = bp + SMB_CORE_CLK_SYNC_REG_OFF;
+ void __iomem *p_clk_reg = bp + SMB_CORE_BUS_CLK_REG_OFF;
+
+ switch (i2c->freq) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ writeb(SR_HOLD_TIME_100K_TICKS, p_hold_time);
+ writel(SMB_IDLE_SCALING_100K, p_idle_scaling);
+ writew(BUS_CLK_100K, p_clk_reg);
+ writel(CLK_SYNC_100K, p_clk_sync);
+ writel(DATA_TIMING_100K, p_data_timing);
+ writel(TO_SCALING_100K, p_to_scaling);
+ break;
+
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ writeb(SR_HOLD_TIME_1000K_TICKS, p_hold_time);
+ writel(SMB_IDLE_SCALING_1000K, p_idle_scaling);
+ writew(BUS_CLK_1000K, p_clk_reg);
+ writel(CLK_SYNC_1000K, p_clk_sync);
+ writel(DATA_TIMING_1000K, p_data_timing);
+ writel(TO_SCALING_1000K, p_to_scaling);
+ break;
+
+ case I2C_MAX_FAST_MODE_FREQ:
+ default:
+ writeb(SR_HOLD_TIME_400K_TICKS, p_hold_time);
+ writel(SMB_IDLE_SCALING_400K, p_idle_scaling);
+ writew(BUS_CLK_400K, p_clk_reg);
+ writel(CLK_SYNC_400K, p_clk_sync);
+ writel(DATA_TIMING_400K, p_data_timing);
+ writel(TO_SCALING_400K, p_to_scaling);
+ break;
+ }
+}
+
+static void pci1xxxx_i2c_init(struct pci1xxxx_i2c *i2c)
+{
+ void __iomem *p2 = i2c->i2c_base + SMBUS_STATUS_REG_OFF;
+ void __iomem *p1 = i2c->i2c_base + SMB_GPR_REG;
+ u8 regval;
+ int ret;
+
+ ret = set_sys_lock(i2c);
+ if (ret == -EPERM) {
+ /*
+ * Configure I2C Fast Mode as default frequency if unable
+ * to acquire sys lock.
+ */
+ regval = 0;
+ } else {
+ regval = readl(p1);
+ release_sys_lock(i2c);
+ }
+
+ switch (regval) {
+ case 0:
+ i2c->freq = I2C_MAX_FAST_MODE_FREQ;
+ pci1xxxx_i2c_set_freq(i2c);
+ break;
+ case 1:
+ i2c->freq = I2C_MAX_STANDARD_MODE_FREQ;
+ pci1xxxx_i2c_set_freq(i2c);
+ break;
+ case 2:
+ i2c->freq = I2C_MAX_FAST_MODE_PLUS_FREQ;
+ pci1xxxx_i2c_set_freq(i2c);
+ break;
+ case 3:
+ default:
+ break;
+ }
+
+ pci1xxxx_i2c_config_padctrl(i2c, true);
+ i2c->flags |= I2C_FLAGS_DIRECT_MODE;
+ pci1xxxx_i2c_set_mode(i2c);
+
+ /*
+ * Added as a precaution since BUF_EMPTY in status register
+ * also trigered an Interrupt.
+ */
+ writeb(STA_BUF_EMPTY, p2);
+
+ /* Configure core I2c control registers. */
+ pci1xxxx_i2c_configure_core_reg(i2c, true);
+
+ /*
+ * Enable pull-up for the SMB alert pin which is just used for
+ * wakeup right now.
+ */
+ pci1xxxx_i2c_configure_smbalert_pin(i2c, true);
+}
+
+static void pci1xxxx_i2c_clear_flags(struct pci1xxxx_i2c *i2c)
+{
+ u8 regval;
+
+ /* Reset the internal buffer counters. */
+ pci1xxxx_i2c_reset_counters(i2c);
+
+ /* Clear low level interrupts. */
+ regval = COMPLETION_MNAKX | COMPLETION_IDLE | COMPLETION_MDONE;
+ writeb(regval, i2c->i2c_base + SMB_CORE_COMPLETION_REG_OFF3);
+ reinit_completion(&i2c->i2c_xfer_done);
+ pci1xxxx_ack_nw_layer_intr(i2c, ALL_NW_LAYER_INTERRUPTS);
+ pci1xxxx_ack_high_level_intr(i2c, ALL_HIGH_LAYER_INTR);
+}
+
+static int pci1xxxx_i2c_read(struct pci1xxxx_i2c *i2c, u8 slaveaddr,
+ unsigned char *buf, u16 total_len)
+{
+ void __iomem *p2 = i2c->i2c_base + SMB_CORE_COMPLETION_REG_OFF3;
+ void __iomem *p1 = i2c->i2c_base + SMB_CORE_CMD_REG_OFF1;
+ void __iomem *p3 = i2c->i2c_base + SMBUS_MST_BUF;
+ unsigned long time_left;
+ u16 remainingbytes;
+ u8 transferlen;
+ int retval = 0;
+ u8 read_count;
+ u32 regval;
+ u16 count;
+
+ /* Enable I2C host controller by setting the ESO bit in the CONTROL REG. */
+ pci1xxxx_i2c_enable_ESO(i2c);
+ pci1xxxx_i2c_clear_flags(i2c);
+ pci1xxxx_config_nw_layer_intr(i2c, INTR_MSK_DMA_TERM, true);
+ pci1xxxx_i2c_config_high_level_intr(i2c, I2C_BUF_MSTR_INTR_MASK, true);
+
+ /*
+ * The I2C transfer could be more than 128 bytes. Our Core is
+ * capable of only sending 128 at a time.
+ * As far as the I2C read is concerned, initailly send the
+ * read slave address along with the number of bytes to read in
+ * ReadCount. After sending the slave address the interrupt
+ * is generated. On seeing the ACK for the slave address, reverse the
+ * buffer direction and run the DMA to initiate Read from slave.
+ */
+ for (count = 0; count < total_len; count += transferlen) {
+
+ /*
+ * Before start of any transaction clear the existing
+ * START/STOP conditions.
+ */
+ writeb(0, p1);
+ remainingbytes = total_len - count;
+ transferlen = min_t(u16, remainingbytes, SMBUS_BUF_MAX_SIZE);
+
+ /*
+ * Send STOP bit for the last chunk in the transaction.
+ * For I2C read transaction of more than BUF_SIZE, NACK should
+ * only be sent for the last read.
+ * Hence a bit FW_ACK is set for all the read chunks except for
+ * the last chunk. For the last chunk NACK should be sent and
+ * FW_ACK is cleared Send STOP only when I2C_FLAGS_STOP bit is
+ * set in the flags and only for the last transaction.
+ */
+ if ((count + transferlen >= total_len) &&
+ (i2c->flags & I2C_FLAGS_STOP)) {
+ pci1xxxx_i2c_set_clear_FW_ACK(i2c, false);
+ pci1xxxx_i2c_send_start_stop(i2c, 0);
+ } else {
+ pci1xxxx_i2c_set_clear_FW_ACK(i2c, true);
+ }
+
+ /* Send START bit for the first transaction. */
+ if (count == 0) {
+ pci1xxxx_i2c_set_transfer_dir(i2c, I2C_DIRN_WRITE);
+ pci1xxxx_i2c_send_start_stop(i2c, 1);
+
+ /* Write I2c buffer with just the slave addr. */
+ pci1xxxx_i2c_buffer_write(i2c, slaveaddr, 0, NULL);
+
+ /* Set the count. Readcount is the transfer bytes. */
+ pci1xxxx_i2c_set_count(i2c, 1, 1, transferlen);
+
+ /*
+ * Set the Auto_start_read bit so that the HW itself
+ * will take care of the read phase.
+ */
+ pci1xxxx_i2c_config_asr(i2c, true);
+ if (i2c->flags & I2C_FLAGS_SMB_BLK_READ)
+ pci1xxxx_i2c_set_readm(i2c, true);
+ } else {
+ pci1xxxx_i2c_set_count(i2c, 0, 0, transferlen);
+ pci1xxxx_i2c_config_asr(i2c, false);
+ pci1xxxx_i2c_clear_flags(i2c);
+ pci1xxxx_i2c_set_transfer_dir(i2c, I2C_DIRN_READ);
+ }
+
+ /* Start the DMA. */
+ pci1xxxx_i2c_start_DMA(i2c);
+
+ /* Wait for the DMA_TERM interrupt. */
+ time_left = wait_for_completion_timeout(&i2c->i2c_xfer_done,
+ msecs_to_jiffies(PCI1XXXX_I2C_TIMEOUT_MS));
+ if (time_left == 0) {
+ /* Reset the I2C core to release the bus lock. */
+ pci1xxxx_i2c_init(i2c);
+ retval = -ETIMEDOUT;
+ goto cleanup;
+ }
+
+ /* Read the completion reg to know the reason for DMA_TERM. */
+ regval = readb(p2);
+
+ /* Slave did not respond. */
+ if (regval & COMPLETION_MNAKX) {
+ writeb(COMPLETION_MNAKX, p2);
+ retval = -ETIMEDOUT;
+ goto cleanup;
+ }
+
+ if (i2c->flags & I2C_FLAGS_SMB_BLK_READ) {
+ buf[0] = readb(p3);
+ read_count = buf[0];
+ memcpy_fromio(&buf[1], p3 + 1, read_count);
+ } else {
+ memcpy_fromio(&buf[count], p3, transferlen);
+ }
+ }
+
+cleanup:
+ /* Disable all the interrupts. */
+ pci1xxxx_config_nw_layer_intr(i2c, INTR_MSK_DMA_TERM, false);
+ pci1xxxx_i2c_config_high_level_intr(i2c, I2C_BUF_MSTR_INTR_MASK, false);
+ pci1xxxx_i2c_config_asr(i2c, false);
+ return retval;
+}
+
+static int pci1xxxx_i2c_write(struct pci1xxxx_i2c *i2c, u8 slaveaddr,
+ unsigned char *buf, u16 total_len)
+{
+ void __iomem *p2 = i2c->i2c_base + SMB_CORE_COMPLETION_REG_OFF3;
+ void __iomem *p1 = i2c->i2c_base + SMB_CORE_CMD_REG_OFF1;
+ unsigned long time_left;
+ u16 remainingbytes;
+ u8 actualwritelen;
+ u8 transferlen;
+ int retval = 0;
+ u32 regval;
+ u16 count;
+
+ /* Enable I2C host controller by setting the ESO bit in the CONTROL REG. */
+ pci1xxxx_i2c_enable_ESO(i2c);
+
+ /* Set the Buffer direction. */
+ pci1xxxx_i2c_set_transfer_dir(i2c, I2C_DIRN_WRITE);
+ pci1xxxx_config_nw_layer_intr(i2c, INTR_MSK_DMA_TERM, true);
+ pci1xxxx_i2c_config_high_level_intr(i2c, I2C_BUF_MSTR_INTR_MASK, true);
+
+ /*
+ * The i2c transfer could be more than 128 bytes. Our Core is
+ * capable of only sending 128 at a time.
+ */
+ for (count = 0; count < total_len; count += transferlen) {
+ /*
+ * Before start of any transaction clear the existing
+ * START/STOP conditions.
+ */
+ writeb(0, p1);
+ pci1xxxx_i2c_clear_flags(i2c);
+ remainingbytes = total_len - count;
+
+ /* If it is the starting of the transaction send START. */
+ if (count == 0) {
+ pci1xxxx_i2c_send_start_stop(i2c, 1);
+
+ /* -1 for the slave address. */
+ transferlen = min_t(u16, SMBUS_BUF_MAX_SIZE - 1,
+ remainingbytes);
+ pci1xxxx_i2c_buffer_write(i2c, slaveaddr,
+ transferlen, &buf[count]);
+ /*
+ * The actual number of bytes written on the I2C bus
+ * is including the slave address.
+ */
+ actualwritelen = transferlen + 1;
+ } else {
+ transferlen = min_t(u16, SMBUS_BUF_MAX_SIZE, remainingbytes);
+ pci1xxxx_i2c_buffer_write(i2c, 0, transferlen, &buf[count]);
+ actualwritelen = transferlen;
+ }
+
+ pci1xxxx_i2c_set_count(i2c, actualwritelen, actualwritelen, 0);
+
+ /*
+ * Send STOP only when I2C_FLAGS_STOP bit is set in the flags and
+ * only for the last transaction.
+ */
+ if (remainingbytes <= transferlen &&
+ (i2c->flags & I2C_FLAGS_STOP))
+ pci1xxxx_i2c_send_start_stop(i2c, 0);
+
+ pci1xxxx_i2c_start_DMA(i2c);
+
+ /*
+ * Wait for the DMA_TERM interrupt.
+ */
+ time_left = wait_for_completion_timeout(&i2c->i2c_xfer_done,
+ msecs_to_jiffies(PCI1XXXX_I2C_TIMEOUT_MS));
+ if (time_left == 0) {
+ /* Reset the I2C core to release the bus lock. */
+ pci1xxxx_i2c_init(i2c);
+ retval = -ETIMEDOUT;
+ goto cleanup;
+ }
+
+ regval = readb(p2);
+ if (regval & COMPLETION_MNAKX) {
+ writeb(COMPLETION_MNAKX, p2);
+ retval = -ETIMEDOUT;
+ goto cleanup;
+ }
+ }
+cleanup:
+ /* Disable all the interrupts. */
+ pci1xxxx_config_nw_layer_intr(i2c, INTR_MSK_DMA_TERM, false);
+ pci1xxxx_i2c_config_high_level_intr(i2c, I2C_BUF_MSTR_INTR_MASK, false);
+
+ return retval;
+}
+
+static int pci1xxxx_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct pci1xxxx_i2c *i2c = i2c_get_adapdata(adap);
+ u8 slaveaddr;
+ int retval;
+ u32 i;
+
+ i2c->i2c_xfer_in_progress = true;
+ for (i = 0; i < num; i++) {
+ slaveaddr = i2c_8bit_addr_from_msg(&msgs[i]);
+
+ /*
+ * Send the STOP bit if the transfer is the final one or
+ * if the I2C_M_STOP flag is set.
+ */
+ if ((i == num - 1) || (msgs[i].flags & I2C_M_STOP))
+ i2c->flags |= I2C_FLAGS_STOP;
+ else
+ i2c->flags &= ~I2C_FLAGS_STOP;
+
+ if (msgs[i].flags & I2C_M_RECV_LEN)
+ i2c->flags |= I2C_FLAGS_SMB_BLK_READ;
+ else
+ i2c->flags &= ~I2C_FLAGS_SMB_BLK_READ;
+
+ if (msgs[i].flags & I2C_M_RD)
+ retval = pci1xxxx_i2c_read(i2c, slaveaddr,
+ msgs[i].buf, msgs[i].len);
+ else
+ retval = pci1xxxx_i2c_write(i2c, slaveaddr,
+ msgs[i].buf, msgs[i].len);
+
+ if (retval < 0)
+ break;
+ }
+ i2c->i2c_xfer_in_progress = false;
+
+ if (retval < 0)
+ return retval;
+
+ return num;
+}
+
+/*
+ * List of supported functions by the driver.
+ */
+static u32 pci1xxxx_i2c_get_funcs(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm pci1xxxx_i2c_algo = {
+ .xfer = pci1xxxx_i2c_xfer,
+ .functionality = pci1xxxx_i2c_get_funcs,
+};
+
+static const struct i2c_adapter_quirks pci1xxxx_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
+static const struct i2c_adapter pci1xxxx_i2c_ops = {
+ .owner = THIS_MODULE,
+ .name = "PCI1xxxx I2C Adapter",
+ .algo = &pci1xxxx_i2c_algo,
+ .quirks = &pci1xxxx_i2c_quirks,
+};
+
+static int pci1xxxx_i2c_suspend(struct device *dev)
+{
+ struct pci1xxxx_i2c *i2c = dev_get_drvdata(dev);
+ void __iomem *p = i2c->i2c_base + SMBUS_RESET_REG;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u32 regval;
+
+ i2c_mark_adapter_suspended(&i2c->adap);
+
+ /*
+ * If the system is put into 'suspend' state when the I2C transfer is in
+ * progress, wait until the transfer completes.
+ */
+ while (i2c->i2c_xfer_in_progress)
+ msleep(20);
+
+ pci1xxxx_i2c_config_high_level_intr(i2c, SMBALERT_WAKE_INTR_MASK, true);
+
+ /*
+ * Enable the PERST_DIS bit to mask the PERST from resetting the core
+ * registers.
+ */
+ regval = readl(p);
+ regval |= PERI_SMBUS_D3_RESET_DIS;
+ writel(regval, p);
+
+ /* Enable PCI wake in the PMCSR register. */
+ device_set_wakeup_enable(dev, true);
+ pci_wake_from_d3(pdev, true);
+
+ return 0;
+}
+
+static int pci1xxxx_i2c_resume(struct device *dev)
+{
+ struct pci1xxxx_i2c *i2c = dev_get_drvdata(dev);
+ void __iomem *p1 = i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF;
+ void __iomem *p2 = i2c->i2c_base + SMBUS_RESET_REG;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u32 regval;
+
+ regval = readw(p1);
+ writew(regval, p1);
+ pci1xxxx_i2c_config_high_level_intr(i2c, SMBALERT_WAKE_INTR_MASK, false);
+ regval = readl(p2);
+ regval &= ~PERI_SMBUS_D3_RESET_DIS;
+ writel(regval, p2);
+ i2c_mark_adapter_resumed(&i2c->adap);
+ pci_wake_from_d3(pdev, false);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_i2c_pm_ops, pci1xxxx_i2c_suspend,
+ pci1xxxx_i2c_resume);
+
+static void pci1xxxx_i2c_shutdown(void *data)
+{
+ struct pci1xxxx_i2c *i2c = data;
+
+ pci1xxxx_i2c_config_padctrl(i2c, false);
+ pci1xxxx_i2c_configure_core_reg(i2c, false);
+}
+
+static int pci1xxxx_i2c_probe_pci(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ struct pci1xxxx_i2c *i2c;
+ int ret;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, i2c);
+ i2c->i2c_xfer_in_progress = false;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ /*
+ * We are getting the base address of the SMB core. SMB core uses
+ * BAR0 and size is 32K.
+ */
+ ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
+ if (ret < 0)
+ return ret;
+
+ i2c->i2c_base = pcim_iomap_table(pdev)[0];
+ init_completion(&i2c->i2c_xfer_done);
+ pci1xxxx_i2c_init(i2c);
+
+ ret = devm_add_action(dev, pci1xxxx_i2c_shutdown, i2c);
+ if (ret)
+ return ret;
+
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), pci1xxxx_i2c_isr,
+ 0, pci_name(pdev), i2c);
+ if (ret)
+ return ret;
+
+ i2c->adap = pci1xxxx_i2c_ops;
+ i2c->adap.dev.parent = dev;
+
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name),
+ "MCHP PCI1xxxx i2c adapter at %s", pci_name(pdev));
+
+ i2c_set_adapdata(&i2c->adap, i2c);
+
+ ret = devm_i2c_add_adapter(dev, &i2c->adap);
+ if (ret)
+ return dev_err_probe(dev, ret, "i2c add adapter failed\n");
+
+ return 0;
+}
+
+static const struct pci_device_id pci1xxxx_i2c_pci_id_table[] = {
+ { PCI_VDEVICE(EFAR, 0xA003) },
+ { PCI_VDEVICE(EFAR, 0xA013) },
+ { PCI_VDEVICE(EFAR, 0xA023) },
+ { PCI_VDEVICE(EFAR, 0xA033) },
+ { PCI_VDEVICE(EFAR, 0xA043) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, pci1xxxx_i2c_pci_id_table);
+
+static struct pci_driver pci1xxxx_i2c_pci_driver = {
+ .name = "i2c-mchp-pci1xxxx",
+ .id_table = pci1xxxx_i2c_pci_id_table,
+ .probe = pci1xxxx_i2c_probe_pci,
+ .driver = {
+ .pm = pm_sleep_ptr(&pci1xxxx_i2c_pm_ops),
+ },
+};
+module_pci_driver(pci1xxxx_i2c_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tharun Kumar P<tharunkumar.pasumarthi@microchip.com>");
+MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
+MODULE_DESCRIPTION("Microchip Technology Inc. pci1xxxx I2C bus driver");
diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c
index 90f5d0407d73..0d9032953e48 100644
--- a/drivers/i2c/busses/i2c-meson.c
+++ b/drivers/i2c/busses/i2c-meson.c
@@ -1,22 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C bus driver for Amlogic Meson SoCs
*
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/types.h>
@@ -31,16 +29,24 @@
#define REG_TOK_RDATA1 0x1c
/* 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)
+#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, REG_CTRL_CLKDIV_SHIFT)
+#define REG_CTRL_CLKDIVEXT_SHIFT 28
+#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, REG_CTRL_CLKDIVEXT_SHIFT)
+
+#define REG_SLV_ADDR_MASK GENMASK(7, 0)
+#define REG_SLV_SDA_FILTER_MASK GENMASK(10, 8)
+#define REG_SLV_SCL_FILTER_MASK GENMASK(13, 11)
+#define REG_SLV_SCL_LOW_SHIFT 16
+#define REG_SLV_SCL_LOW_MASK GENMASK(27, REG_SLV_SCL_LOW_SHIFT)
+#define REG_SLV_SCL_LOW_EN BIT(28)
#define I2C_TIMEOUT_MS 500
+#define FILTER_DELAY 15
enum {
TOKEN_END = 0,
@@ -58,10 +64,6 @@ enum {
STATE_WRITE,
};
-struct meson_i2c_data {
- unsigned char div_factor;
-};
-
/**
* struct meson_i2c - Meson I2C device private data
*
@@ -79,7 +81,7 @@ struct meson_i2c_data {
* @done: Completion used to wait for transfer termination
* @tokens: Sequence of tokens to be written to the device
* @num_tokens: Number of tokens
- * @data: Pointer to the controlller's platform data
+ * @data: Pointer to the controller's platform data
*/
struct meson_i2c {
struct i2c_adapter adap;
@@ -102,6 +104,10 @@ struct meson_i2c {
const struct meson_i2c_data *data;
};
+struct meson_i2c_data {
+ void (*set_clk_div)(struct meson_i2c *i2c, unsigned int freq);
+};
+
static void meson_i2c_set_mask(struct meson_i2c *i2c, int reg, u32 mask,
u32 val)
{
@@ -130,24 +136,77 @@ static void meson_i2c_add_token(struct meson_i2c *i2c, int token)
i2c->num_tokens++;
}
-static void meson_i2c_set_clk_div(struct meson_i2c *i2c, unsigned int freq)
+static void meson_gxbb_axg_i2c_set_clk_div(struct meson_i2c *i2c, unsigned int freq)
+{
+ unsigned long clk_rate = clk_get_rate(i2c->clk);
+ unsigned int div_h, div_l;
+
+ /* According to I2C-BUS Spec 2.1, in FAST-MODE, the minimum LOW period is 1.3uS, and
+ * minimum HIGH is least 0.6us.
+ * For 400000 freq, the period is 2.5us. To keep within the specs, give 40% of period to
+ * HIGH and 60% to LOW. This means HIGH at 1.0us and LOW 1.5us.
+ * The same applies for Fast-mode plus, where LOW is 0.5us and HIGH is 0.26us.
+ * Duty = H/(H + L) = 2/5
+ */
+ if (freq <= I2C_MAX_STANDARD_MODE_FREQ) {
+ div_h = DIV_ROUND_UP(clk_rate, freq);
+ div_l = DIV_ROUND_UP(div_h, 4);
+ div_h = DIV_ROUND_UP(div_h, 2) - FILTER_DELAY;
+ } else {
+ div_h = DIV_ROUND_UP(clk_rate * 2, freq * 5) - FILTER_DELAY;
+ div_l = DIV_ROUND_UP(clk_rate * 3, freq * 5 * 2);
+ }
+
+ /* clock divider has 12 bits */
+ if (div_h > GENMASK(11, 0)) {
+ dev_err(i2c->dev, "requested bus frequency too low\n");
+ div_h = GENMASK(11, 0);
+ }
+ if (div_l > GENMASK(11, 0)) {
+ dev_err(i2c->dev, "requested bus frequency too low\n");
+ div_l = GENMASK(11, 0);
+ }
+
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK,
+ FIELD_PREP(REG_CTRL_CLKDIV_MASK, div_h & GENMASK(9, 0)));
+
+ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK,
+ FIELD_PREP(REG_CTRL_CLKDIVEXT_MASK, div_h >> 10));
+
+ /* set SCL low delay */
+ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SCL_LOW_MASK,
+ FIELD_PREP(REG_SLV_SCL_LOW_MASK, div_l));
+
+ /* Enable HIGH/LOW mode */
+ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SCL_LOW_EN, REG_SLV_SCL_LOW_EN);
+
+ dev_dbg(i2c->dev, "%s: clk %lu, freq %u, divh %u, divl %u\n", __func__,
+ clk_rate, freq, div_h, div_l);
+}
+
+static void meson6_i2c_set_clk_div(struct meson_i2c *i2c, unsigned int freq)
{
unsigned long clk_rate = clk_get_rate(i2c->clk);
unsigned int div;
- div = DIV_ROUND_UP(clk_rate, freq * i2c->data->div_factor);
+ div = DIV_ROUND_UP(clk_rate, freq);
+ div -= FILTER_DELAY;
+ div = DIV_ROUND_UP(div, 4);
/* clock divider has 12 bits */
- if (div >= (1 << 12)) {
+ if (div > GENMASK(11, 0)) {
dev_err(i2c->dev, "requested bus frequency too low\n");
- div = (1 << 12) - 1;
+ div = GENMASK(11, 0);
}
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK,
- (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT);
+ FIELD_PREP(REG_CTRL_CLKDIV_MASK, div & GENMASK(9, 0)));
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK,
- (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT);
+ FIELD_PREP(REG_CTRL_CLKDIVEXT_MASK, div >> 10));
+
+ /* Disable HIGH/LOW mode */
+ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SCL_LOW_EN, 0);
dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__,
clk_rate, freq, div);
@@ -216,6 +275,30 @@ static void meson_i2c_prepare_xfer(struct meson_i2c *i2c)
writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1);
}
+static void meson_i2c_transfer_complete(struct meson_i2c *i2c, u32 ctrl)
+{
+ if (ctrl & REG_CTRL_ERROR) {
+ /*
+ * The bit is set when the IGNORE_NAK bit is cleared
+ * and the device didn't respond. In this case, the
+ * I2C controller automatically generates a STOP
+ * condition.
+ */
+ dev_dbg(i2c->dev, "error bit set\n");
+ i2c->error = -ENXIO;
+ i2c->state = STATE_IDLE;
+ } else {
+ if (i2c->state == STATE_READ && i2c->count)
+ meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos,
+ i2c->count);
+
+ i2c->pos += i2c->count;
+
+ if (i2c->pos >= i2c->msg->len)
+ i2c->state = STATE_IDLE;
+ }
+}
+
static irqreturn_t meson_i2c_irq(int irqno, void *dev_id)
{
struct meson_i2c *i2c = dev_id;
@@ -235,27 +318,9 @@ static irqreturn_t meson_i2c_irq(int irqno, void *dev_id)
return IRQ_NONE;
}
- if (ctrl & REG_CTRL_ERROR) {
- /*
- * The bit is set when the IGNORE_NAK bit is cleared
- * and the device didn't respond. In this case, the
- * I2C controller automatically generates a STOP
- * condition.
- */
- dev_dbg(i2c->dev, "error bit set\n");
- i2c->error = -ENXIO;
- i2c->state = STATE_IDLE;
- complete(&i2c->done);
- goto out;
- }
-
- if (i2c->state == STATE_READ && i2c->count)
- meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos, i2c->count);
+ meson_i2c_transfer_complete(i2c, ctrl);
- i2c->pos += i2c->count;
-
- if (i2c->pos >= i2c->msg->len) {
- i2c->state = STATE_IDLE;
+ if (i2c->state == STATE_IDLE) {
complete(&i2c->done);
goto out;
}
@@ -276,16 +341,20 @@ static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg)
token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ :
TOKEN_SLAVE_ADDR_WRITE;
- writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR);
+
+ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_ADDR_MASK,
+ FIELD_PREP(REG_SLV_ADDR_MASK, msg->addr << 1));
+
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)
+ int last, bool atomic)
{
unsigned long time_left, flags;
int ret = 0;
+ u32 ctrl;
i2c->msg = msg;
i2c->last = last;
@@ -303,13 +372,24 @@ static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
meson_i2c_prepare_xfer(i2c);
- reinit_completion(&i2c->done);
+
+ if (!atomic)
+ reinit_completion(&i2c->done);
/* Start the transfer */
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START);
- time_left = msecs_to_jiffies(I2C_TIMEOUT_MS);
- time_left = wait_for_completion_timeout(&i2c->done, time_left);
+ if (atomic) {
+ ret = readl_poll_timeout_atomic(i2c->regs + REG_CTRL, ctrl,
+ !(ctrl & REG_CTRL_STATUS),
+ 10, I2C_TIMEOUT_MS * 1000);
+ } else {
+ time_left = msecs_to_jiffies(I2C_TIMEOUT_MS);
+ time_left = wait_for_completion_timeout(&i2c->done, time_left);
+
+ if (!time_left)
+ ret = -ETIMEDOUT;
+ }
/*
* Protect access to i2c struct and registers from interrupt
@@ -318,13 +398,14 @@ static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
*/
spin_lock_irqsave(&i2c->lock, flags);
+ if (atomic && !ret)
+ meson_i2c_transfer_complete(i2c, ctrl);
+
/* Abort any active operation */
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
- if (!time_left) {
+ if (ret)
i2c->state = STATE_IDLE;
- ret = -ETIMEDOUT;
- }
if (i2c->error)
ret = i2c->error;
@@ -334,40 +415,48 @@ static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
return ret;
}
-static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
+static int meson_i2c_xfer_messages(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num, bool atomic)
{
struct meson_i2c *i2c = adap->algo_data;
int i, ret = 0;
- clk_enable(i2c->clk);
-
for (i = 0; i < num; i++) {
- ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1);
+ ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1, atomic);
if (ret)
break;
}
- clk_disable(i2c->clk);
-
return ret ?: i;
}
+static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ return meson_i2c_xfer_messages(adap, msgs, num, false);
+}
+
+static int meson_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return meson_i2c_xfer_messages(adap, msgs, num, true);
+}
+
static u32 meson_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm meson_i2c_algorithm = {
- .master_xfer = meson_i2c_xfer,
- .functionality = meson_i2c_func,
+ .xfer = meson_i2c_xfer,
+ .xfer_atomic = meson_i2c_xfer_atomic,
+ .functionality = meson_i2c_func,
};
static int meson_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct meson_i2c *i2c;
- struct resource *mem;
struct i2c_timings timings;
int irq, ret = 0;
@@ -392,16 +481,13 @@ static int meson_i2c_probe(struct platform_device *pdev)
return PTR_ERR(i2c->clk);
}
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+ i2c->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "can't find IRQ\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, meson_i2c_irq, 0, NULL, i2c);
if (ret < 0) {
@@ -409,13 +495,13 @@ static int meson_i2c_probe(struct platform_device *pdev)
return ret;
}
- ret = clk_prepare(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
if (ret < 0) {
dev_err(&pdev->dev, "can't prepare clock\n");
return ret;
}
- strlcpy(i2c->adap.name, "Meson I2C adapter",
+ strscpy(i2c->adap.name, "Meson I2C adapter",
sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &meson_i2c_algorithm;
@@ -429,37 +515,43 @@ static int meson_i2c_probe(struct platform_device *pdev)
*/
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
+ /* Disable filtering */
+ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR,
+ REG_SLV_SDA_FILTER_MASK | REG_SLV_SCL_FILTER_MASK, 0);
+
+ if (!i2c->data->set_clk_div) {
+ clk_disable_unprepare(i2c->clk);
+ return -EINVAL;
+ }
+ i2c->data->set_clk_div(i2c, timings.bus_freq_hz);
+
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
- clk_unprepare(i2c->clk);
+ clk_disable_unprepare(i2c->clk);
return ret;
}
- meson_i2c_set_clk_div(i2c, timings.bus_freq_hz);
-
return 0;
}
-static int meson_i2c_remove(struct platform_device *pdev)
+static void meson_i2c_remove(struct platform_device *pdev)
{
struct meson_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
- clk_unprepare(i2c->clk);
-
- return 0;
+ clk_disable_unprepare(i2c->clk);
}
static const struct meson_i2c_data i2c_meson6_data = {
- .div_factor = 4,
+ .set_clk_div = meson6_i2c_set_clk_div,
};
static const struct meson_i2c_data i2c_gxbb_data = {
- .div_factor = 4,
+ .set_clk_div = meson_gxbb_axg_i2c_set_clk_div,
};
static const struct meson_i2c_data i2c_axg_data = {
- .div_factor = 3,
+ .set_clk_div = meson_gxbb_axg_i2c_set_clk_div,
};
static const struct of_device_id meson_i2c_match[] = {
@@ -473,7 +565,7 @@ MODULE_DEVICE_TABLE(of, meson_i2c_match);
static struct platform_driver meson_i2c_driver = {
.probe = meson_i2c_probe,
- .remove = meson_i2c_remove,
+ .remove = meson_i2c_remove,
.driver = {
.name = "meson-i2c",
.of_match_table = meson_i2c_match,
diff --git a/drivers/i2c/busses/i2c-microchip-corei2c.c b/drivers/i2c/busses/i2c-microchip-corei2c.c
new file mode 100644
index 000000000000..c8599733633e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-microchip-corei2c.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip CoreI2C I2C controller driver
+ *
+ * Copyright (c) 2018-2022 Microchip Corporation. All rights reserved.
+ *
+ * Author: Daire McNamara <daire.mcnamara@microchip.com>
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CORE_I2C_CTRL (0x00)
+#define CTRL_CR0 BIT(0)
+#define CTRL_CR1 BIT(1)
+#define CTRL_AA BIT(2)
+#define CTRL_SI BIT(3)
+#define CTRL_STO BIT(4)
+#define CTRL_STA BIT(5)
+#define CTRL_ENS1 BIT(6)
+#define CTRL_CR2 BIT(7)
+
+#define STATUS_BUS_ERROR (0x00)
+#define STATUS_M_START_SENT (0x08)
+#define STATUS_M_REPEATED_START_SENT (0x10)
+#define STATUS_M_SLAW_ACK (0x18)
+#define STATUS_M_SLAW_NACK (0x20)
+#define STATUS_M_TX_DATA_ACK (0x28)
+#define STATUS_M_TX_DATA_NACK (0x30)
+#define STATUS_M_ARB_LOST (0x38)
+#define STATUS_M_SLAR_ACK (0x40)
+#define STATUS_M_SLAR_NACK (0x48)
+#define STATUS_M_RX_DATA_ACKED (0x50)
+#define STATUS_M_RX_DATA_NACKED (0x58)
+#define STATUS_S_SLAW_ACKED (0x60)
+#define STATUS_S_ARB_LOST_SLAW_ACKED (0x68)
+#define STATUS_S_GENERAL_CALL_ACKED (0x70)
+#define STATUS_S_ARB_LOST_GENERAL_CALL_ACKED (0x78)
+#define STATUS_S_RX_DATA_ACKED (0x80)
+#define STATUS_S_RX_DATA_NACKED (0x88)
+#define STATUS_S_GENERAL_CALL_RX_DATA_ACKED (0x90)
+#define STATUS_S_GENERAL_CALL_RX_DATA_NACKED (0x98)
+#define STATUS_S_RX_STOP (0xA0)
+#define STATUS_S_SLAR_ACKED (0xA8)
+#define STATUS_S_ARB_LOST_SLAR_ACKED (0xB0)
+#define STATUS_S_TX_DATA_ACK (0xB8)
+#define STATUS_S_TX_DATA_NACK (0xC0)
+#define STATUS_LAST_DATA_ACK (0xC8)
+#define STATUS_M_SMB_MASTER_RESET (0xD0)
+#define STATUS_S_SCL_LOW_TIMEOUT (0xD8) /* 25 ms */
+#define STATUS_NO_STATE_INFO (0xF8)
+
+#define CORE_I2C_STATUS (0x04)
+#define CORE_I2C_DATA (0x08)
+#define WRITE_BIT (0x0)
+#define READ_BIT (0x1)
+#define SLAVE_ADDR_SHIFT (1)
+#define CORE_I2C_SLAVE0_ADDR (0x0c)
+#define GENERAL_CALL_BIT (0x0)
+#define CORE_I2C_SMBUS (0x10)
+#define SMBALERT_INT_ENB (0x0)
+#define SMBSUS_INT_ENB (0x1)
+#define SMBUS_ENB (0x2)
+#define SMBALERT_NI_STATUS (0x3)
+#define SMBALERT_NO_CTRL (0x4)
+#define SMBSUS_NI_STATUS (0x5)
+#define SMBSUS_NO_CTRL (0x6)
+#define SMBUS_RESET (0x7)
+#define CORE_I2C_FREQ (0x14)
+#define CORE_I2C_GLITCHREG (0x18)
+#define CORE_I2C_SLAVE1_ADDR (0x1c)
+#define CORE_I2C_SMBUS_MSG_WR (0x0)
+#define CORE_I2C_SMBUS_MSG_RD (0x1)
+
+#define PCLK_DIV_960 (CTRL_CR2)
+#define PCLK_DIV_256 (0)
+#define PCLK_DIV_224 (CTRL_CR0)
+#define PCLK_DIV_192 (CTRL_CR1)
+#define PCLK_DIV_160 (CTRL_CR0 | CTRL_CR1)
+#define PCLK_DIV_120 (CTRL_CR0 | CTRL_CR2)
+#define PCLK_DIV_60 (CTRL_CR1 | CTRL_CR2)
+#define BCLK_DIV_8 (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
+#define CLK_MASK (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
+
+/**
+ * struct mchp_corei2c_dev - Microchip CoreI2C device private data
+ *
+ * @base: pointer to register struct
+ * @dev: device reference
+ * @i2c_clk: clock reference for i2c input clock
+ * @msg_queue: pointer to the messages requiring sending
+ * @buf: pointer to msg buffer for easier use
+ * @msg_complete: xfer completion object
+ * @adapter: core i2c abstraction
+ * @msg_err: error code for completed message
+ * @bus_clk_rate: current i2c bus clock rate
+ * @isr_status: cached copy of local ISR status
+ * @total_num: total number of messages to be sent/received
+ * @current_num: index of the current message being sent/received
+ * @msg_len: number of bytes transferred in msg
+ * @addr: address of the current slave
+ * @restart_needed: whether or not a repeated start is required after current message
+ */
+struct mchp_corei2c_dev {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *i2c_clk;
+ struct i2c_msg *msg_queue;
+ u8 *buf;
+ struct completion msg_complete;
+ struct i2c_adapter adapter;
+ int msg_err;
+ int total_num;
+ int current_num;
+ u32 bus_clk_rate;
+ u32 isr_status;
+ u16 msg_len;
+ u8 addr;
+ bool restart_needed;
+};
+
+static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev)
+{
+ u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
+
+ ctrl &= ~CTRL_ENS1;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+}
+
+static void mchp_corei2c_core_enable(struct mchp_corei2c_dev *idev)
+{
+ u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
+
+ ctrl |= CTRL_ENS1;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+}
+
+static void mchp_corei2c_reset(struct mchp_corei2c_dev *idev)
+{
+ mchp_corei2c_core_disable(idev);
+ mchp_corei2c_core_enable(idev);
+}
+
+static inline void mchp_corei2c_stop(struct mchp_corei2c_dev *idev)
+{
+ u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
+
+ ctrl |= CTRL_STO;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+}
+
+static inline int mchp_corei2c_set_divisor(u32 rate,
+ struct mchp_corei2c_dev *idev)
+{
+ u8 clkval, ctrl;
+
+ if (rate >= 960)
+ clkval = PCLK_DIV_960;
+ else if (rate >= 256)
+ clkval = PCLK_DIV_256;
+ else if (rate >= 224)
+ clkval = PCLK_DIV_224;
+ else if (rate >= 192)
+ clkval = PCLK_DIV_192;
+ else if (rate >= 160)
+ clkval = PCLK_DIV_160;
+ else if (rate >= 120)
+ clkval = PCLK_DIV_120;
+ else if (rate >= 60)
+ clkval = PCLK_DIV_60;
+ else if (rate >= 8)
+ clkval = BCLK_DIV_8;
+ else
+ return -EINVAL;
+
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl &= ~CLK_MASK;
+ ctrl |= clkval;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ if ((ctrl & CLK_MASK) != clkval)
+ return -EIO;
+
+ return 0;
+}
+
+static int mchp_corei2c_init(struct mchp_corei2c_dev *idev)
+{
+ u32 clk_rate = clk_get_rate(idev->i2c_clk);
+ u32 divisor = clk_rate / idev->bus_clk_rate;
+ int ret;
+
+ ret = mchp_corei2c_set_divisor(divisor, idev);
+ if (ret)
+ return ret;
+
+ mchp_corei2c_reset(idev);
+
+ return 0;
+}
+
+static void mchp_corei2c_empty_rx(struct mchp_corei2c_dev *idev)
+{
+ u8 ctrl;
+
+ if (idev->msg_len > 0) {
+ *idev->buf++ = readb(idev->base + CORE_I2C_DATA);
+ idev->msg_len--;
+ }
+
+ if (idev->msg_len <= 1) {
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl &= ~CTRL_AA;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+ }
+}
+
+static int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev)
+{
+ if (idev->msg_len > 0)
+ writeb(*idev->buf++, idev->base + CORE_I2C_DATA);
+ idev->msg_len--;
+
+ return 0;
+}
+
+static void mchp_corei2c_next_msg(struct mchp_corei2c_dev *idev)
+{
+ struct i2c_msg *this_msg;
+ u8 ctrl;
+
+ if (idev->current_num >= idev->total_num) {
+ complete(&idev->msg_complete);
+ return;
+ }
+
+ /*
+ * If there's been an error, the isr needs to return control
+ * to the "main" part of the driver, so as not to keep sending
+ * messages once it completes and clears the SI bit.
+ */
+ if (idev->msg_err) {
+ complete(&idev->msg_complete);
+ return;
+ }
+
+ this_msg = idev->msg_queue++;
+
+ if (idev->current_num < (idev->total_num - 1)) {
+ struct i2c_msg *next_msg = idev->msg_queue;
+
+ idev->restart_needed = next_msg->flags & I2C_M_RD;
+ } else {
+ idev->restart_needed = false;
+ }
+
+ idev->addr = i2c_8bit_addr_from_msg(this_msg);
+ idev->msg_len = this_msg->len;
+ idev->buf = this_msg->buf;
+
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl |= CTRL_STA;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+
+ idev->current_num++;
+}
+
+static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
+{
+ u32 status = idev->isr_status;
+ u8 ctrl;
+ bool last_byte = false, finished = false;
+
+ if (!idev->buf)
+ return IRQ_NONE;
+
+ switch (status) {
+ case STATUS_M_START_SENT:
+ case STATUS_M_REPEATED_START_SENT:
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl &= ~CTRL_STA;
+ writeb(idev->addr, idev->base + CORE_I2C_DATA);
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+ break;
+ case STATUS_M_ARB_LOST:
+ idev->msg_err = -EAGAIN;
+ finished = true;
+ break;
+ case STATUS_M_SLAW_ACK:
+ case STATUS_M_TX_DATA_ACK:
+ if (idev->msg_len > 0) {
+ mchp_corei2c_fill_tx(idev);
+ } else {
+ if (idev->restart_needed)
+ finished = true;
+ else
+ last_byte = true;
+ }
+ break;
+ case STATUS_M_TX_DATA_NACK:
+ case STATUS_M_SLAR_NACK:
+ case STATUS_M_SLAW_NACK:
+ idev->msg_err = -ENXIO;
+ last_byte = true;
+ break;
+ case STATUS_M_SLAR_ACK:
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ if (idev->msg_len == 1u) {
+ ctrl &= ~CTRL_AA;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+ } else {
+ ctrl |= CTRL_AA;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+ }
+ if (idev->msg_len < 1u)
+ last_byte = true;
+ break;
+ case STATUS_M_RX_DATA_ACKED:
+ mchp_corei2c_empty_rx(idev);
+ break;
+ case STATUS_M_RX_DATA_NACKED:
+ mchp_corei2c_empty_rx(idev);
+ if (idev->msg_len == 0)
+ last_byte = true;
+ break;
+ default:
+ break;
+ }
+
+ /* On the last byte to be transmitted, send STOP */
+ if (last_byte)
+ mchp_corei2c_stop(idev);
+
+ if (last_byte || finished)
+ mchp_corei2c_next_msg(idev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mchp_corei2c_isr(int irq, void *_dev)
+{
+ struct mchp_corei2c_dev *idev = _dev;
+ irqreturn_t ret = IRQ_NONE;
+ u8 ctrl;
+
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ if (ctrl & CTRL_SI) {
+ idev->isr_status = readb(idev->base + CORE_I2C_STATUS);
+ ret = mchp_corei2c_handle_isr(idev);
+ }
+
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl &= ~CTRL_SI;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+
+ return ret;
+}
+
+static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
+ struct i2c_msg *this_msg = msgs;
+ unsigned long time_left;
+ u8 ctrl;
+
+ mchp_corei2c_core_enable(idev);
+
+ /*
+ * The isr controls the flow of a transfer, this info needs to be saved
+ * to a location that it can access the queue information from.
+ */
+ idev->restart_needed = false;
+ idev->msg_queue = msgs;
+ idev->total_num = num;
+ idev->current_num = 0;
+
+ /*
+ * But the first entry to the isr is triggered by the start in this
+ * function, so the first message needs to be "dequeued".
+ */
+ idev->addr = i2c_8bit_addr_from_msg(this_msg);
+ idev->msg_len = this_msg->len;
+ idev->buf = this_msg->buf;
+ idev->msg_err = 0;
+
+ if (idev->total_num > 1) {
+ struct i2c_msg *next_msg = msgs + 1;
+
+ idev->restart_needed = next_msg->flags & I2C_M_RD;
+ }
+
+ idev->current_num++;
+ idev->msg_queue++;
+
+ reinit_completion(&idev->msg_complete);
+
+ /*
+ * Send the first start to pass control to the isr
+ */
+ ctrl = readb(idev->base + CORE_I2C_CTRL);
+ ctrl |= CTRL_STA;
+ writeb(ctrl, idev->base + CORE_I2C_CTRL);
+
+ time_left = wait_for_completion_timeout(&idev->msg_complete,
+ idev->adapter.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ if (idev->msg_err)
+ return idev->msg_err;
+
+ return num;
+}
+
+static u32 mchp_corei2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int mchp_corei2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
+ char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_msg msgs[2];
+ struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
+ u8 tx_buf[I2C_SMBUS_BLOCK_MAX + 2];
+ u8 rx_buf[I2C_SMBUS_BLOCK_MAX + 1];
+ int num_msgs = 1;
+ int ret;
+
+ msgs[CORE_I2C_SMBUS_MSG_WR].addr = addr;
+ msgs[CORE_I2C_SMBUS_MSG_WR].flags = 0;
+
+ if (read_write == I2C_SMBUS_READ && size <= I2C_SMBUS_BYTE)
+ msgs[CORE_I2C_SMBUS_MSG_WR].flags = I2C_M_RD;
+
+ if (read_write == I2C_SMBUS_WRITE && size <= I2C_SMBUS_WORD_DATA)
+ msgs[CORE_I2C_SMBUS_MSG_WR].len = size;
+
+ if (read_write == I2C_SMBUS_WRITE && size > I2C_SMBUS_BYTE) {
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf = tx_buf;
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[0] = command;
+ }
+
+ if (read_write == I2C_SMBUS_READ && size >= I2C_SMBUS_BYTE_DATA) {
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf = tx_buf;
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[0] = command;
+ msgs[CORE_I2C_SMBUS_MSG_RD].addr = addr;
+ msgs[CORE_I2C_SMBUS_MSG_RD].flags = I2C_M_RD;
+ num_msgs = 2;
+ }
+
+ if (read_write == I2C_SMBUS_READ && size > I2C_SMBUS_QUICK)
+ msgs[CORE_I2C_SMBUS_MSG_WR].len = 1;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf = NULL;
+ return 0;
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE)
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf = &command;
+ else
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf = &data->byte;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_WRITE) {
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[1] = data->byte;
+ } else {
+ msgs[CORE_I2C_SMBUS_MSG_RD].len = size - 1;
+ msgs[CORE_I2C_SMBUS_MSG_RD].buf = &data->byte;
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_WRITE) {
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[1] = data->word & 0xFF;
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[2] = (data->word >> 8) & 0xFF;
+ } else {
+ msgs[CORE_I2C_SMBUS_MSG_RD].len = size - 1;
+ msgs[CORE_I2C_SMBUS_MSG_RD].buf = rx_buf;
+ }
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_WRITE) {
+ int data_len;
+
+ data_len = data->block[0];
+ msgs[CORE_I2C_SMBUS_MSG_WR].len = data_len + 2;
+ for (int i = 0; i <= data_len; i++)
+ msgs[CORE_I2C_SMBUS_MSG_WR].buf[i + 1] = data->block[i];
+ } else {
+ msgs[CORE_I2C_SMBUS_MSG_RD].len = I2C_SMBUS_BLOCK_MAX + 1;
+ msgs[CORE_I2C_SMBUS_MSG_RD].buf = rx_buf;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = mchp_corei2c_xfer(&idev->adapter, msgs, num_msgs);
+ if (ret < 0)
+ return ret;
+
+ if (read_write == I2C_SMBUS_WRITE || size <= I2C_SMBUS_BYTE_DATA)
+ return 0;
+
+ switch (size) {
+ case I2C_SMBUS_WORD_DATA:
+ data->word = (rx_buf[0] | (rx_buf[1] << 8));
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (rx_buf[0] > I2C_SMBUS_BLOCK_MAX)
+ rx_buf[0] = I2C_SMBUS_BLOCK_MAX;
+ /* As per protocol first member of block is size of the block. */
+ for (int i = 0; i <= rx_buf[0]; i++)
+ data->block[i] = rx_buf[i];
+ break;
+ }
+
+ return 0;
+}
+
+static const struct i2c_algorithm mchp_corei2c_algo = {
+ .xfer = mchp_corei2c_xfer,
+ .functionality = mchp_corei2c_func,
+ .smbus_xfer = mchp_corei2c_smbus_xfer,
+};
+
+static int mchp_corei2c_probe(struct platform_device *pdev)
+{
+ struct mchp_corei2c_dev *idev;
+ struct resource *res;
+ int irq, ret;
+
+ idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
+ if (!idev)
+ return -ENOMEM;
+
+ idev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(idev->base))
+ return PTR_ERR(idev->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ idev->i2c_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(idev->i2c_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(idev->i2c_clk),
+ "missing clock\n");
+
+ idev->dev = &pdev->dev;
+ init_completion(&idev->msg_complete);
+
+ ret = device_property_read_u32(idev->dev, "clock-frequency",
+ &idev->bus_clk_rate);
+ if (ret || !idev->bus_clk_rate) {
+ dev_info(&pdev->dev, "default to 100kHz\n");
+ idev->bus_clk_rate = 100000;
+ }
+
+ if (idev->bus_clk_rate > 400000)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "clock-frequency too high: %d\n",
+ idev->bus_clk_rate);
+
+ /*
+ * This driver supports both the hard peripherals & soft FPGA cores.
+ * The hard peripherals do not have shared IRQs, but we don't have
+ * control over what way the interrupts are wired for the soft cores.
+ */
+ ret = devm_request_irq(&pdev->dev, irq, mchp_corei2c_isr, IRQF_SHARED,
+ pdev->name, idev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to claim irq %d\n", irq);
+
+ ret = clk_prepare_enable(idev->i2c_clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to enable clock\n");
+
+ ret = mchp_corei2c_init(idev);
+ if (ret) {
+ clk_disable_unprepare(idev->i2c_clk);
+ return dev_err_probe(&pdev->dev, ret, "failed to program clock divider\n");
+ }
+
+ i2c_set_adapdata(&idev->adapter, idev);
+ snprintf(idev->adapter.name, sizeof(idev->adapter.name),
+ "Microchip I2C hw bus at %08lx", (unsigned long)res->start);
+ idev->adapter.owner = THIS_MODULE;
+ idev->adapter.algo = &mchp_corei2c_algo;
+ idev->adapter.dev.parent = &pdev->dev;
+ idev->adapter.dev.of_node = pdev->dev.of_node;
+ idev->adapter.timeout = HZ;
+
+ platform_set_drvdata(pdev, idev);
+
+ ret = i2c_add_adapter(&idev->adapter);
+ if (ret) {
+ clk_disable_unprepare(idev->i2c_clk);
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "registered CoreI2C bus driver\n");
+
+ return 0;
+}
+
+static void mchp_corei2c_remove(struct platform_device *pdev)
+{
+ struct mchp_corei2c_dev *idev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(idev->i2c_clk);
+ i2c_del_adapter(&idev->adapter);
+}
+
+static const struct of_device_id mchp_corei2c_of_match[] = {
+ { .compatible = "microchip,mpfs-i2c" },
+ { .compatible = "microchip,corei2c-rtl-v7" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mchp_corei2c_of_match);
+
+static struct platform_driver mchp_corei2c_driver = {
+ .probe = mchp_corei2c_probe,
+ .remove = mchp_corei2c_remove,
+ .driver = {
+ .name = "microchip-corei2c",
+ .of_match_table = mchp_corei2c_of_match,
+ },
+};
+
+module_platform_driver(mchp_corei2c_driver);
+
+MODULE_DESCRIPTION("Microchip CoreI2C bus driver");
+MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c
new file mode 100644
index 000000000000..8345f7e6385d
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mlxbf.c
@@ -0,0 +1,2454 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mellanox BlueField I2C bus driver
+ *
+ * Copyright (C) 2020 Mellanox Technologies, Ltd.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+
+/* Defines what functionality is present. */
+#define MLXBF_I2C_FUNC_SMBUS_BLOCK \
+ (I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
+
+#define MLXBF_I2C_FUNC_SMBUS_DEFAULT \
+ (I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK | \
+ I2C_FUNC_SMBUS_PROC_CALL)
+
+#define MLXBF_I2C_FUNC_ALL \
+ (MLXBF_I2C_FUNC_SMBUS_DEFAULT | MLXBF_I2C_FUNC_SMBUS_BLOCK | \
+ I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SLAVE)
+
+/* Shared resources info in BlueField platforms. */
+
+#define MLXBF_I2C_COALESCE_TYU_ADDR 0x02801300
+#define MLXBF_I2C_COALESCE_TYU_SIZE 0x010
+
+#define MLXBF_I2C_GPIO_TYU_ADDR 0x02802000
+#define MLXBF_I2C_GPIO_TYU_SIZE 0x100
+
+#define MLXBF_I2C_COREPLL_TYU_ADDR 0x02800358
+#define MLXBF_I2C_COREPLL_TYU_SIZE 0x008
+
+#define MLXBF_I2C_COREPLL_YU_ADDR 0x02800c30
+#define MLXBF_I2C_COREPLL_YU_SIZE 0x00c
+
+#define MLXBF_I2C_COREPLL_RSH_YU_ADDR 0x13409824
+#define MLXBF_I2C_COREPLL_RSH_YU_SIZE 0x00c
+
+#define MLXBF_I2C_SHARED_RES_MAX 3
+
+/*
+ * Note that the following SMBus, CAUSE, GPIO and PLL register addresses
+ * refer to their respective offsets relative to the corresponding
+ * memory-mapped region whose addresses are specified in either the DT or
+ * the ACPI tables or above.
+ */
+
+/*
+ * SMBus Master core clock frequency. Timing configurations are
+ * strongly dependent on the core clock frequency of the SMBus
+ * Master. Default value is set to 400MHz.
+ */
+#define MLXBF_I2C_TYU_PLL_OUT_FREQ (400 * 1000 * 1000)
+/* Reference clock for Bluefield - 156 MHz. */
+#define MLXBF_I2C_PLL_IN_FREQ 156250000ULL
+
+/* Constant used to determine the PLL frequency. */
+#define MLNXBF_I2C_COREPLL_CONST 16384ULL
+
+#define MLXBF_I2C_FREQUENCY_1GHZ 1000000000ULL
+
+/* PLL registers. */
+#define MLXBF_I2C_CORE_PLL_REG1 0x4
+#define MLXBF_I2C_CORE_PLL_REG2 0x8
+
+/* OR cause register. */
+#define MLXBF_I2C_CAUSE_OR_EVTEN0 0x14
+#define MLXBF_I2C_CAUSE_OR_CLEAR 0x18
+
+/* Arbiter Cause Register. */
+#define MLXBF_I2C_CAUSE_ARBITER 0x1c
+
+/*
+ * Cause Status flags. Note that those bits might be considered
+ * as interrupt enabled bits.
+ */
+
+/* Transaction ended with STOP. */
+#define MLXBF_I2C_CAUSE_TRANSACTION_ENDED BIT(0)
+/* Master arbitration lost. */
+#define MLXBF_I2C_CAUSE_M_ARBITRATION_LOST BIT(1)
+/* Unexpected start detected. */
+#define MLXBF_I2C_CAUSE_UNEXPECTED_START BIT(2)
+/* Unexpected stop detected. */
+#define MLXBF_I2C_CAUSE_UNEXPECTED_STOP BIT(3)
+/* Wait for transfer continuation. */
+#define MLXBF_I2C_CAUSE_WAIT_FOR_FW_DATA BIT(4)
+/* Failed to generate STOP. */
+#define MLXBF_I2C_CAUSE_PUT_STOP_FAILED BIT(5)
+/* Failed to generate START. */
+#define MLXBF_I2C_CAUSE_PUT_START_FAILED BIT(6)
+/* Clock toggle completed. */
+#define MLXBF_I2C_CAUSE_CLK_TOGGLE_DONE BIT(7)
+/* Transfer timeout occurred. */
+#define MLXBF_I2C_CAUSE_M_FW_TIMEOUT BIT(8)
+/* Master busy bit reset. */
+#define MLXBF_I2C_CAUSE_M_GW_BUSY_FALL BIT(9)
+
+#define MLXBF_I2C_CAUSE_MASTER_ARBITER_BITS_MASK GENMASK(9, 0)
+
+#define MLXBF_I2C_CAUSE_MASTER_STATUS_ERROR \
+ (MLXBF_I2C_CAUSE_M_ARBITRATION_LOST | \
+ MLXBF_I2C_CAUSE_UNEXPECTED_START | \
+ MLXBF_I2C_CAUSE_UNEXPECTED_STOP | \
+ MLXBF_I2C_CAUSE_PUT_STOP_FAILED | \
+ MLXBF_I2C_CAUSE_PUT_START_FAILED | \
+ MLXBF_I2C_CAUSE_CLK_TOGGLE_DONE | \
+ MLXBF_I2C_CAUSE_M_FW_TIMEOUT)
+
+/*
+ * Slave cause status flags. Note that those bits might be considered
+ * as interrupt enabled bits.
+ */
+
+/* Write transaction received successfully. */
+#define MLXBF_I2C_CAUSE_WRITE_SUCCESS BIT(0)
+/* Read transaction received, waiting for response. */
+#define MLXBF_I2C_CAUSE_READ_WAIT_FW_RESPONSE BIT(13)
+/* Slave busy bit reset. */
+#define MLXBF_I2C_CAUSE_S_GW_BUSY_FALL BIT(18)
+
+/* Cause coalesce registers. */
+#define MLXBF_I2C_CAUSE_COALESCE_0 0x00
+
+#define MLXBF_I2C_CAUSE_TYU_SLAVE_BIT 3
+#define MLXBF_I2C_CAUSE_YU_SLAVE_BIT 1
+
+/* Functional enable register. */
+#define MLXBF_I2C_GPIO_0_FUNC_EN_0 0x28
+/* Force OE enable register. */
+#define MLXBF_I2C_GPIO_0_FORCE_OE_EN 0x30
+/*
+ * Note that Smbus GWs are on GPIOs 30:25. Two pins are used to control
+ * SDA/SCL lines:
+ *
+ * SMBUS GW0 -> bits[26:25]
+ * SMBUS GW1 -> bits[28:27]
+ * SMBUS GW2 -> bits[30:29]
+ */
+#define MLXBF_I2C_GPIO_SMBUS_GW_PINS(num) (25 + ((num) << 1))
+
+/* Note that gw_id can be 0,1 or 2. */
+#define MLXBF_I2C_GPIO_SMBUS_GW_MASK(num) \
+ (0xffffffff & (~(0x3 << MLXBF_I2C_GPIO_SMBUS_GW_PINS(num))))
+
+#define MLXBF_I2C_GPIO_SMBUS_GW_RESET_PINS(num, val) \
+ ((val) & MLXBF_I2C_GPIO_SMBUS_GW_MASK(num))
+
+#define MLXBF_I2C_GPIO_SMBUS_GW_ASSERT_PINS(num, val) \
+ ((val) | (0x3 << MLXBF_I2C_GPIO_SMBUS_GW_PINS(num)))
+
+/*
+ * Defines SMBus operating frequency and core clock frequency.
+ * According to ADB files, default values are compliant to 100KHz SMBus
+ * @ 400MHz core clock. The driver should be able to calculate core
+ * frequency based on PLL parameters.
+ */
+#define MLXBF_I2C_COREPLL_FREQ MLXBF_I2C_TYU_PLL_OUT_FREQ
+
+/* Core PLL TYU configuration. */
+#define MLXBF_I2C_COREPLL_CORE_F_TYU_MASK GENMASK(15, 3)
+#define MLXBF_I2C_COREPLL_CORE_OD_TYU_MASK GENMASK(19, 16)
+#define MLXBF_I2C_COREPLL_CORE_R_TYU_MASK GENMASK(25, 20)
+
+/* Core PLL YU configuration. */
+#define MLXBF_I2C_COREPLL_CORE_F_YU_MASK GENMASK(25, 0)
+#define MLXBF_I2C_COREPLL_CORE_OD_YU_MASK GENMASK(3, 0)
+#define MLXBF_I2C_COREPLL_CORE_R_YU_MASK GENMASK(31, 26)
+
+/* SMBus timing parameters. */
+#define MLXBF_I2C_SMBUS_TIMER_SCL_LOW_SCL_HIGH 0x00
+#define MLXBF_I2C_SMBUS_TIMER_FALL_RISE_SPIKE 0x04
+#define MLXBF_I2C_SMBUS_TIMER_THOLD 0x08
+#define MLXBF_I2C_SMBUS_TIMER_TSETUP_START_STOP 0x0c
+#define MLXBF_I2C_SMBUS_TIMER_TSETUP_DATA 0x10
+#define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF 0x14
+#define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT 0x18
+
+#define MLXBF_I2C_SHIFT_0 0
+#define MLXBF_I2C_SHIFT_8 8
+#define MLXBF_I2C_SHIFT_16 16
+#define MLXBF_I2C_SHIFT_24 24
+
+#define MLXBF_I2C_MASK_8 GENMASK(7, 0)
+#define MLXBF_I2C_MASK_16 GENMASK(15, 0)
+#define MLXBF_I2C_MASK_32 GENMASK(31, 0)
+
+#define MLXBF_I2C_MST_ADDR_OFFSET 0x200
+
+/* SMBus Master GW. */
+#define MLXBF_I2C_SMBUS_MASTER_GW 0x0
+/* Number of bytes received and sent. */
+#define MLXBF_I2C_YU_SMBUS_RS_BYTES 0x100
+#define MLXBF_I2C_RSH_YU_SMBUS_RS_BYTES 0x10c
+/* Packet error check (PEC) value. */
+#define MLXBF_I2C_SMBUS_MASTER_PEC 0x104
+/* Status bits (ACK/NACK/FW Timeout). */
+#define MLXBF_I2C_SMBUS_MASTER_STATUS 0x108
+/* SMbus Master Finite State Machine. */
+#define MLXBF_I2C_YU_SMBUS_MASTER_FSM 0x110
+#define MLXBF_I2C_RSH_YU_SMBUS_MASTER_FSM 0x100
+
+/* SMBus master GW control bits offset in MLXBF_I2C_SMBUS_MASTER_GW[31:3]. */
+#define MLXBF_I2C_MASTER_LOCK_BIT BIT(31) /* Lock bit. */
+#define MLXBF_I2C_MASTER_BUSY_BIT BIT(30) /* Busy bit. */
+#define MLXBF_I2C_MASTER_START_BIT BIT(29) /* Control start. */
+#define MLXBF_I2C_MASTER_CTL_WRITE_BIT BIT(28) /* Control write phase. */
+#define MLXBF_I2C_MASTER_CTL_READ_BIT BIT(19) /* Control read phase. */
+#define MLXBF_I2C_MASTER_STOP_BIT BIT(3) /* Control stop. */
+
+#define MLXBF_I2C_MASTER_ENABLE \
+ (MLXBF_I2C_MASTER_LOCK_BIT | MLXBF_I2C_MASTER_BUSY_BIT | \
+ MLXBF_I2C_MASTER_START_BIT)
+
+#define MLXBF_I2C_MASTER_ENABLE_WRITE \
+ (MLXBF_I2C_MASTER_ENABLE | MLXBF_I2C_MASTER_CTL_WRITE_BIT)
+
+#define MLXBF_I2C_MASTER_ENABLE_READ \
+ (MLXBF_I2C_MASTER_ENABLE | MLXBF_I2C_MASTER_CTL_READ_BIT)
+
+#define MLXBF_I2C_MASTER_WRITE_SHIFT 21 /* Control write bytes */
+#define MLXBF_I2C_MASTER_SEND_PEC_SHIFT 20 /* Send PEC byte when set to 1 */
+#define MLXBF_I2C_MASTER_PARSE_EXP_SHIFT 11 /* Control parse expected bytes */
+#define MLXBF_I2C_MASTER_SLV_ADDR_SHIFT 12 /* Slave address */
+#define MLXBF_I2C_MASTER_READ_SHIFT 4 /* Control read bytes */
+
+/* SMBus master GW Data descriptor. */
+#define MLXBF_I2C_MASTER_DATA_DESC_ADDR 0x80
+#define MLXBF_I2C_MASTER_DATA_DESC_SIZE 0x80 /* Size in bytes. */
+
+/* Maximum bytes to read/write per SMBus transaction. */
+#define MLXBF_I2C_MASTER_DATA_R_LENGTH MLXBF_I2C_MASTER_DATA_DESC_SIZE
+#define MLXBF_I2C_MASTER_DATA_W_LENGTH (MLXBF_I2C_MASTER_DATA_DESC_SIZE - 1)
+
+/* All bytes were transmitted. */
+#define MLXBF_I2C_SMBUS_STATUS_BYTE_CNT_DONE BIT(0)
+/* NACK received. */
+#define MLXBF_I2C_SMBUS_STATUS_NACK_RCV BIT(1)
+/* Slave's byte count >128 bytes. */
+#define MLXBF_I2C_SMBUS_STATUS_READ_ERR BIT(2)
+/* Timeout occurred. */
+#define MLXBF_I2C_SMBUS_STATUS_FW_TIMEOUT BIT(3)
+
+#define MLXBF_I2C_SMBUS_MASTER_STATUS_MASK GENMASK(3, 0)
+
+#define MLXBF_I2C_SMBUS_MASTER_STATUS_ERROR \
+ (MLXBF_I2C_SMBUS_STATUS_NACK_RCV | \
+ MLXBF_I2C_SMBUS_STATUS_READ_ERR | \
+ MLXBF_I2C_SMBUS_STATUS_FW_TIMEOUT)
+
+#define MLXBF_I2C_SMBUS_MASTER_FSM_STOP_MASK BIT(31)
+#define MLXBF_I2C_SMBUS_MASTER_FSM_PS_STATE_MASK BIT(15)
+
+#define MLXBF_I2C_SLV_ADDR_OFFSET 0x400
+
+/* SMBus slave GW. */
+#define MLXBF_I2C_SMBUS_SLAVE_GW 0x0
+/* Number of bytes received and sent from/to master. */
+#define MLXBF_I2C_SMBUS_SLAVE_RS_MASTER_BYTES 0x100
+/* Packet error check (PEC) value. */
+#define MLXBF_I2C_SMBUS_SLAVE_PEC 0x104
+/* SMBus slave Finite State Machine (FSM). */
+#define MLXBF_I2C_SMBUS_SLAVE_FSM 0x110
+/*
+ * Should be set when all raised causes handled, and cleared by HW on
+ * every new cause.
+ */
+#define MLXBF_I2C_SMBUS_SLAVE_READY 0x12c
+
+/* SMBus slave GW control bits offset in MLXBF_I2C_SMBUS_SLAVE_GW[31:19]. */
+#define MLXBF_I2C_SLAVE_BUSY_BIT BIT(30) /* Busy bit. */
+#define MLXBF_I2C_SLAVE_WRITE_BIT BIT(29) /* Control write enable. */
+
+#define MLXBF_I2C_SLAVE_ENABLE \
+ (MLXBF_I2C_SLAVE_BUSY_BIT | MLXBF_I2C_SLAVE_WRITE_BIT)
+
+#define MLXBF_I2C_SLAVE_WRITE_BYTES_SHIFT 22 /* Number of bytes to write. */
+#define MLXBF_I2C_SLAVE_SEND_PEC_SHIFT 21 /* Send PEC byte shift. */
+
+/* SMBus slave GW Data descriptor. */
+#define MLXBF_I2C_SLAVE_DATA_DESC_ADDR 0x80
+#define MLXBF_I2C_SLAVE_DATA_DESC_SIZE 0x80 /* Size in bytes. */
+
+/* SMbus slave configuration registers. */
+#define MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG 0x114
+#define MLXBF_I2C_SMBUS_SLAVE_ADDR_CNT 16
+#define MLXBF_I2C_SMBUS_SLAVE_ADDR_EN_BIT BIT(7)
+#define MLXBF_I2C_SMBUS_SLAVE_ADDR_MASK GENMASK(6, 0)
+
+/*
+ * Timeout is given in microsends. Note also that timeout handling is not
+ * exact.
+ */
+#define MLXBF_I2C_SMBUS_TIMEOUT (300 * 1000) /* 300ms */
+#define MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT (300 * 1000) /* 300ms */
+
+/* Polling frequency in microseconds. */
+#define MLXBF_I2C_POLL_FREQ_IN_USEC 200
+
+#define MLXBF_I2C_SMBUS_OP_CNT_1 1
+#define MLXBF_I2C_SMBUS_OP_CNT_2 2
+#define MLXBF_I2C_SMBUS_OP_CNT_3 3
+#define MLXBF_I2C_SMBUS_MAX_OP_CNT MLXBF_I2C_SMBUS_OP_CNT_3
+
+/* Helper macro to define an I2C resource parameters. */
+#define MLXBF_I2C_RES_PARAMS(addr, size, str) \
+ { \
+ .start = (addr), \
+ .end = (addr) + (size) - 1, \
+ .name = (str) \
+ }
+
+enum {
+ MLXBF_I2C_TIMING_100KHZ = 100000,
+ MLXBF_I2C_TIMING_400KHZ = 400000,
+ MLXBF_I2C_TIMING_1000KHZ = 1000000,
+};
+
+enum {
+ MLXBF_I2C_F_READ = BIT(0),
+ MLXBF_I2C_F_WRITE = BIT(1),
+ MLXBF_I2C_F_NORESTART = BIT(3),
+ MLXBF_I2C_F_SMBUS_OPERATION = BIT(4),
+ MLXBF_I2C_F_SMBUS_BLOCK = BIT(5),
+ MLXBF_I2C_F_SMBUS_PEC = BIT(6),
+ MLXBF_I2C_F_SMBUS_PROCESS_CALL = BIT(7),
+ MLXBF_I2C_F_WRITE_WITHOUT_STOP = BIT(8),
+};
+
+/* Mellanox BlueField chip type. */
+enum mlxbf_i2c_chip_type {
+ MLXBF_I2C_CHIP_TYPE_1, /* Mellanox BlueField-1 chip. */
+ MLXBF_I2C_CHIP_TYPE_2, /* Mellanox BlueField-2 chip. */
+ MLXBF_I2C_CHIP_TYPE_3 /* Mellanox BlueField-3 chip. */
+};
+
+/* List of chip resources that are being accessed by the driver. */
+enum {
+ MLXBF_I2C_SMBUS_RES,
+ MLXBF_I2C_MST_CAUSE_RES,
+ MLXBF_I2C_SLV_CAUSE_RES,
+ MLXBF_I2C_COALESCE_RES,
+ MLXBF_I2C_SMBUS_TIMER_RES,
+ MLXBF_I2C_SMBUS_MST_RES,
+ MLXBF_I2C_SMBUS_SLV_RES,
+ MLXBF_I2C_COREPLL_RES,
+ MLXBF_I2C_GPIO_RES,
+ MLXBF_I2C_END_RES
+};
+
+/* Encapsulates timing parameters. */
+struct mlxbf_i2c_timings {
+ u16 scl_high; /* Clock high period. */
+ u16 scl_low; /* Clock low period. */
+ u8 sda_rise; /* Data rise time. */
+ u8 sda_fall; /* Data fall time. */
+ u8 scl_rise; /* Clock rise time. */
+ u8 scl_fall; /* Clock fall time. */
+ u16 hold_start; /* Hold time after (REPEATED) START. */
+ u16 hold_data; /* Data hold time. */
+ u16 setup_start; /* REPEATED START condition setup time. */
+ u16 setup_stop; /* STOP condition setup time. */
+ u16 setup_data; /* Data setup time. */
+ u16 pad; /* Padding. */
+ u16 buf; /* Bus free time between STOP and START. */
+ u16 thigh_max; /* Thigh max. */
+ u32 timeout; /* Detect clock low timeout. */
+};
+
+struct mlxbf_i2c_smbus_operation {
+ u32 flags;
+ u32 length; /* Buffer length in bytes. */
+ u8 *buffer;
+};
+
+struct mlxbf_i2c_smbus_request {
+ u8 slave;
+ u8 operation_cnt;
+ struct mlxbf_i2c_smbus_operation operation[MLXBF_I2C_SMBUS_MAX_OP_CNT];
+};
+
+struct mlxbf_i2c_resource {
+ void __iomem *io;
+ struct resource *params;
+ struct mutex *lock; /* Mutex to protect mlxbf_i2c_resource. */
+ u8 type;
+};
+
+struct mlxbf_i2c_chip_info {
+ enum mlxbf_i2c_chip_type type;
+ /* Chip shared resources that are being used by the I2C controller. */
+ struct mlxbf_i2c_resource *shared_res[MLXBF_I2C_SHARED_RES_MAX];
+
+ /* Callback to calculate the core PLL frequency. */
+ u64 (*calculate_freq)(struct mlxbf_i2c_resource *corepll_res);
+
+ /* Registers' address offset */
+ u32 smbus_master_rs_bytes_off;
+ u32 smbus_master_fsm_off;
+};
+
+struct mlxbf_i2c_priv {
+ const struct mlxbf_i2c_chip_info *chip;
+ struct i2c_adapter adap;
+ struct mlxbf_i2c_resource *smbus;
+ struct mlxbf_i2c_resource *timer;
+ struct mlxbf_i2c_resource *mst;
+ struct mlxbf_i2c_resource *slv;
+ struct mlxbf_i2c_resource *mst_cause;
+ struct mlxbf_i2c_resource *slv_cause;
+ struct mlxbf_i2c_resource *coalesce;
+ u64 frequency; /* Core frequency in Hz. */
+ int bus; /* Physical bus identifier. */
+ int irq;
+ struct i2c_client *slave[MLXBF_I2C_SMBUS_SLAVE_ADDR_CNT];
+ u32 resource_version;
+};
+
+/* Core PLL frequency. */
+static u64 mlxbf_i2c_corepll_frequency;
+
+static struct resource mlxbf_i2c_coalesce_tyu_params =
+ MLXBF_I2C_RES_PARAMS(MLXBF_I2C_COALESCE_TYU_ADDR,
+ MLXBF_I2C_COALESCE_TYU_SIZE,
+ "COALESCE_MEM");
+static struct resource mlxbf_i2c_corepll_tyu_params =
+ MLXBF_I2C_RES_PARAMS(MLXBF_I2C_COREPLL_TYU_ADDR,
+ MLXBF_I2C_COREPLL_TYU_SIZE,
+ "COREPLL_MEM");
+static struct resource mlxbf_i2c_corepll_yu_params =
+ MLXBF_I2C_RES_PARAMS(MLXBF_I2C_COREPLL_YU_ADDR,
+ MLXBF_I2C_COREPLL_YU_SIZE,
+ "COREPLL_MEM");
+static struct resource mlxbf_i2c_corepll_rsh_yu_params =
+ MLXBF_I2C_RES_PARAMS(MLXBF_I2C_COREPLL_RSH_YU_ADDR,
+ MLXBF_I2C_COREPLL_RSH_YU_SIZE,
+ "COREPLL_MEM");
+static struct resource mlxbf_i2c_gpio_tyu_params =
+ MLXBF_I2C_RES_PARAMS(MLXBF_I2C_GPIO_TYU_ADDR,
+ MLXBF_I2C_GPIO_TYU_SIZE,
+ "GPIO_MEM");
+
+static struct mutex mlxbf_i2c_coalesce_lock;
+static struct mutex mlxbf_i2c_corepll_lock;
+static struct mutex mlxbf_i2c_gpio_lock;
+
+static struct mlxbf_i2c_resource mlxbf_i2c_coalesce_res[] = {
+ [MLXBF_I2C_CHIP_TYPE_1] = {
+ .params = &mlxbf_i2c_coalesce_tyu_params,
+ .lock = &mlxbf_i2c_coalesce_lock,
+ .type = MLXBF_I2C_COALESCE_RES
+ },
+ {}
+};
+
+static struct mlxbf_i2c_resource mlxbf_i2c_corepll_res[] = {
+ [MLXBF_I2C_CHIP_TYPE_1] = {
+ .params = &mlxbf_i2c_corepll_tyu_params,
+ .lock = &mlxbf_i2c_corepll_lock,
+ .type = MLXBF_I2C_COREPLL_RES
+ },
+ [MLXBF_I2C_CHIP_TYPE_2] = {
+ .params = &mlxbf_i2c_corepll_yu_params,
+ .lock = &mlxbf_i2c_corepll_lock,
+ .type = MLXBF_I2C_COREPLL_RES,
+ },
+ [MLXBF_I2C_CHIP_TYPE_3] = {
+ .params = &mlxbf_i2c_corepll_rsh_yu_params,
+ .lock = &mlxbf_i2c_corepll_lock,
+ .type = MLXBF_I2C_COREPLL_RES,
+ }
+};
+
+static struct mlxbf_i2c_resource mlxbf_i2c_gpio_res[] = {
+ [MLXBF_I2C_CHIP_TYPE_1] = {
+ .params = &mlxbf_i2c_gpio_tyu_params,
+ .lock = &mlxbf_i2c_gpio_lock,
+ .type = MLXBF_I2C_GPIO_RES
+ },
+ {}
+};
+
+static u8 mlxbf_i2c_bus_count;
+
+static struct mutex mlxbf_i2c_bus_lock;
+
+static bool mlxbf_i2c_smbus_transaction_success(u32 master_status,
+ u32 cause_status)
+{
+ /*
+ * When transaction ended with STOP, all bytes were transmitted,
+ * and no NACK received, then the transaction ended successfully.
+ * On the other hand, when the GW is configured with the stop bit
+ * de-asserted then the SMBus expects the following GW configuration
+ * for transfer continuation.
+ */
+ if ((cause_status & MLXBF_I2C_CAUSE_WAIT_FOR_FW_DATA) ||
+ ((cause_status & MLXBF_I2C_CAUSE_TRANSACTION_ENDED) &&
+ (master_status & MLXBF_I2C_SMBUS_STATUS_BYTE_CNT_DONE) &&
+ !(master_status & MLXBF_I2C_SMBUS_STATUS_NACK_RCV)))
+ return true;
+
+ return false;
+}
+
+/*
+ * Poll SMBus master status and return transaction status,
+ * i.e. whether succeeded or failed. I2C and SMBus fault codes
+ * are returned as negative numbers from most calls, with zero
+ * or some positive number indicating a non-fault return.
+ */
+static int mlxbf_i2c_smbus_check_status(struct mlxbf_i2c_priv *priv)
+{
+ u32 master_status_bits;
+ u32 cause_status_bits;
+ u32 bits;
+
+ /*
+ * GW busy bit is raised by the driver and cleared by the HW
+ * when the transaction is completed. The busy bit is a good
+ * indicator of transaction status. So poll the busy bit, and
+ * then read the cause and master status bits to determine if
+ * errors occurred during the transaction.
+ */
+ readl_poll_timeout_atomic(priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW,
+ bits, !(bits & MLXBF_I2C_MASTER_BUSY_BIT),
+ MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT);
+
+ /* Read cause status bits. */
+ cause_status_bits = readl(priv->mst_cause->io +
+ MLXBF_I2C_CAUSE_ARBITER);
+ cause_status_bits &= MLXBF_I2C_CAUSE_MASTER_ARBITER_BITS_MASK;
+
+ /*
+ * Parse both Cause and Master GW bits, then return transaction status.
+ */
+
+ master_status_bits = readl(priv->mst->io +
+ MLXBF_I2C_SMBUS_MASTER_STATUS);
+ master_status_bits &= MLXBF_I2C_SMBUS_MASTER_STATUS_MASK;
+
+ if (mlxbf_i2c_smbus_transaction_success(master_status_bits,
+ cause_status_bits))
+ return 0;
+
+ /*
+ * In case of timeout on GW busy, the ISR will clear busy bit but
+ * transaction ended bits cause will not be set so the transaction
+ * fails. Then, we must check Master GW status bits.
+ */
+ if ((master_status_bits & MLXBF_I2C_SMBUS_MASTER_STATUS_ERROR) &&
+ (cause_status_bits & (MLXBF_I2C_CAUSE_TRANSACTION_ENDED |
+ MLXBF_I2C_CAUSE_M_GW_BUSY_FALL)))
+ return -EIO;
+
+ if (cause_status_bits & MLXBF_I2C_CAUSE_MASTER_STATUS_ERROR)
+ return -EAGAIN;
+
+ return -ETIMEDOUT;
+}
+
+static void mlxbf_i2c_smbus_write_data(struct mlxbf_i2c_priv *priv,
+ const u8 *data, u8 length, u32 addr,
+ bool is_master)
+{
+ u8 offset, aligned_length;
+ u32 data32;
+
+ aligned_length = round_up(length, 4);
+
+ /*
+ * Copy data bytes from 4-byte aligned source buffer.
+ * Data copied to the Master GW Data Descriptor MUST be shifted
+ * left so the data starts at the MSB of the descriptor registers
+ * as required by the underlying hardware. Enable byte swapping
+ * when writing data bytes to the 32 * 32-bit HW Data registers
+ * a.k.a Master GW Data Descriptor.
+ */
+ for (offset = 0; offset < aligned_length; offset += sizeof(u32)) {
+ data32 = *((u32 *)(data + offset));
+ if (is_master)
+ iowrite32be(data32, priv->mst->io + addr + offset);
+ else
+ iowrite32be(data32, priv->slv->io + addr + offset);
+ }
+}
+
+static void mlxbf_i2c_smbus_read_data(struct mlxbf_i2c_priv *priv,
+ u8 *data, u8 length, u32 addr,
+ bool is_master)
+{
+ u32 data32, mask;
+ u8 byte, offset;
+
+ mask = sizeof(u32) - 1;
+
+ /*
+ * Data bytes in the Master GW Data Descriptor are shifted left
+ * so the data starts at the MSB of the descriptor registers as
+ * set by the underlying hardware. Enable byte swapping while
+ * reading data bytes from the 32 * 32-bit HW Data registers
+ * a.k.a Master GW Data Descriptor.
+ */
+
+ for (offset = 0; offset < (length & ~mask); offset += sizeof(u32)) {
+ if (is_master)
+ data32 = ioread32be(priv->mst->io + addr + offset);
+ else
+ data32 = ioread32be(priv->slv->io + addr + offset);
+ *((u32 *)(data + offset)) = data32;
+ }
+
+ if (!(length & mask))
+ return;
+
+ if (is_master)
+ data32 = ioread32be(priv->mst->io + addr + offset);
+ else
+ data32 = ioread32be(priv->slv->io + addr + offset);
+
+ for (byte = 0; byte < (length & mask); byte++) {
+ data[offset + byte] = data32 & GENMASK(7, 0);
+ data32 = ror32(data32, MLXBF_I2C_SHIFT_8);
+ }
+}
+
+static int mlxbf_i2c_smbus_enable(struct mlxbf_i2c_priv *priv, u8 slave,
+ u8 len, u8 block_en, u8 pec_en, bool read,
+ bool stop)
+{
+ u32 command = 0;
+
+ /* Set Master GW control word. */
+ if (stop)
+ command |= MLXBF_I2C_MASTER_STOP_BIT;
+ if (read) {
+ command |= MLXBF_I2C_MASTER_ENABLE_READ;
+ command |= rol32(len, MLXBF_I2C_MASTER_READ_SHIFT);
+ } else {
+ command |= MLXBF_I2C_MASTER_ENABLE_WRITE;
+ command |= rol32(len, MLXBF_I2C_MASTER_WRITE_SHIFT);
+ }
+ command |= rol32(slave, MLXBF_I2C_MASTER_SLV_ADDR_SHIFT);
+ command |= rol32(block_en, MLXBF_I2C_MASTER_PARSE_EXP_SHIFT);
+ command |= rol32(pec_en, MLXBF_I2C_MASTER_SEND_PEC_SHIFT);
+
+ /* Clear status bits. */
+ writel(0x0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_STATUS);
+ /* Set the cause data. */
+ writel(~0x0, priv->mst_cause->io + MLXBF_I2C_CAUSE_OR_CLEAR);
+ /* Zero PEC byte. */
+ writel(0x0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_PEC);
+ /* Zero byte count. */
+ writel(0x0, priv->mst->io + priv->chip->smbus_master_rs_bytes_off);
+
+ /* GW activation. */
+ writel(command, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW);
+
+ /*
+ * Poll master status and check status bits. An ACK is sent when
+ * completing writing data to the bus (Master 'byte_count_done' bit
+ * is set to 1).
+ */
+ return mlxbf_i2c_smbus_check_status(priv);
+}
+
+static int
+mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
+ struct mlxbf_i2c_smbus_request *request)
+{
+ u8 data_desc[MLXBF_I2C_MASTER_DATA_DESC_SIZE] = { 0 };
+ u8 op_idx, data_idx, data_len, write_len, read_len;
+ struct mlxbf_i2c_smbus_operation *operation;
+ u8 read_en, write_en, block_en, pec_en;
+ bool stop_after_write = true;
+ u8 slave, addr;
+ u8 *read_buf;
+ u32 flags;
+ u32 bits;
+ int ret;
+
+ if (request->operation_cnt > MLXBF_I2C_SMBUS_MAX_OP_CNT)
+ return -EINVAL;
+
+ read_buf = NULL;
+ data_idx = 0;
+ read_en = 0;
+ write_en = 0;
+ write_len = 0;
+ read_len = 0;
+ block_en = 0;
+ pec_en = 0;
+ slave = request->slave & GENMASK(6, 0);
+ addr = slave << 1;
+
+ /*
+ * Try to acquire the smbus gw lock before any reads of the GW register since
+ * a read sets the lock.
+ */
+ ret = readl_poll_timeout_atomic(priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW,
+ bits, !(bits & MLXBF_I2C_MASTER_LOCK_BIT),
+ MLXBF_I2C_POLL_FREQ_IN_USEC,
+ MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT);
+ if (WARN_ON(ret))
+ return -EBUSY;
+
+ /*
+ * SW must make sure that the SMBus Master GW is idle before starting
+ * a transaction. Accordingly, this call polls the Master FSM stop bit;
+ * it returns -ETIMEDOUT when the bit is asserted, 0 if not.
+ */
+ ret = readl_poll_timeout_atomic(priv->mst->io + priv->chip->smbus_master_fsm_off,
+ bits, !(bits & MLXBF_I2C_SMBUS_MASTER_FSM_STOP_MASK),
+ MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT);
+ if (WARN_ON(ret)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* Set first byte. */
+ data_desc[data_idx++] = addr;
+
+ for (op_idx = 0; op_idx < request->operation_cnt; op_idx++) {
+ operation = &request->operation[op_idx];
+ flags = operation->flags;
+
+ /*
+ * Note that read and write operations might be handled by a
+ * single command. If the MLXBF_I2C_F_SMBUS_OPERATION is set
+ * then write command byte and set the optional SMBus specific
+ * bits such as block_en and pec_en. These bits MUST be
+ * submitted by the first operation only.
+ */
+ if (op_idx == 0 && flags & MLXBF_I2C_F_SMBUS_OPERATION) {
+ block_en = flags & MLXBF_I2C_F_SMBUS_BLOCK;
+ pec_en = flags & MLXBF_I2C_F_SMBUS_PEC;
+ }
+
+ if (flags & MLXBF_I2C_F_WRITE) {
+ write_en = 1;
+ write_len += operation->length;
+ if (data_idx + operation->length >
+ MLXBF_I2C_MASTER_DATA_DESC_SIZE) {
+ ret = -ENOBUFS;
+ goto out_unlock;
+ }
+ memcpy(data_desc + data_idx,
+ operation->buffer, operation->length);
+ data_idx += operation->length;
+
+ /*
+ * The stop condition can be skipped when writing on the bus
+ * to implement a repeated start condition on the next read
+ * as required for several SMBus and I2C operations.
+ */
+ if (flags & MLXBF_I2C_F_WRITE_WITHOUT_STOP)
+ stop_after_write = false;
+ }
+
+ /*
+ * We assume that read operations are performed only once per
+ * SMBus transaction. *TBD* protect this statement so it won't
+ * be executed twice? or return an error if we try to read more
+ * than once?
+ */
+ if (flags & MLXBF_I2C_F_READ) {
+ read_en = 1;
+ /* Subtract 1 as required by HW. */
+ read_len = operation->length - 1;
+ read_buf = operation->buffer;
+ }
+ }
+
+ /* Set Master GW data descriptor. */
+ data_len = write_len + 1; /* Add one byte of the slave address. */
+ /*
+ * Note that data_len cannot be 0. Indeed, the slave address byte
+ * must be written to the data registers.
+ */
+ mlxbf_i2c_smbus_write_data(priv, (const u8 *)data_desc, data_len,
+ MLXBF_I2C_MASTER_DATA_DESC_ADDR, true);
+
+ if (write_en) {
+ ret = mlxbf_i2c_smbus_enable(priv, slave, write_len, block_en,
+ pec_en, 0, stop_after_write);
+ if (ret)
+ goto out_unlock;
+ }
+
+ if (read_en) {
+ /* Write slave address to Master GW data descriptor. */
+ mlxbf_i2c_smbus_write_data(priv, (const u8 *)&addr, 1,
+ MLXBF_I2C_MASTER_DATA_DESC_ADDR, true);
+ ret = mlxbf_i2c_smbus_enable(priv, slave, read_len, block_en,
+ pec_en, 1, true);
+ if (!ret) {
+ /* Get Master GW data descriptor. */
+ mlxbf_i2c_smbus_read_data(priv, data_desc, read_len + 1,
+ MLXBF_I2C_MASTER_DATA_DESC_ADDR, true);
+
+ /* Get data from Master GW data descriptor. */
+ memcpy(read_buf, data_desc, read_len + 1);
+ }
+
+ /*
+ * After a read operation the SMBus FSM ps (present state)
+ * needs to be 'manually' reset. This should be removed in
+ * next tag integration.
+ */
+ writel(MLXBF_I2C_SMBUS_MASTER_FSM_PS_STATE_MASK,
+ priv->mst->io + priv->chip->smbus_master_fsm_off);
+ }
+
+out_unlock:
+ /* Clear the gw to clear the lock */
+ writel(0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW);
+
+ return ret;
+}
+
+/* I2C SMBus protocols. */
+
+static void
+mlxbf_i2c_smbus_quick_command(struct mlxbf_i2c_smbus_request *request,
+ u8 read)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_1;
+
+ request->operation[0].length = 0;
+ request->operation[0].flags = MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= read ? MLXBF_I2C_F_READ : 0;
+}
+
+static void mlxbf_i2c_smbus_byte_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *data, bool read, bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_1;
+
+ request->operation[0].length = 1;
+ request->operation[0].length += pec_check;
+
+ request->operation[0].flags = MLXBF_I2C_F_SMBUS_OPERATION;
+ request->operation[0].flags |= read ?
+ MLXBF_I2C_F_READ : MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+
+ request->operation[0].buffer = data;
+}
+
+static void
+mlxbf_i2c_smbus_data_byte_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, bool read, bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_2;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ request->operation[1].length = 1;
+ request->operation[1].length += pec_check;
+ request->operation[1].flags = read ?
+ MLXBF_I2C_F_READ : MLXBF_I2C_F_WRITE;
+ request->operation[1].buffer = data;
+}
+
+static void
+mlxbf_i2c_smbus_data_word_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, bool read, bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_2;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ request->operation[1].length = 2;
+ request->operation[1].length += pec_check;
+ request->operation[1].flags = read ?
+ MLXBF_I2C_F_READ : MLXBF_I2C_F_WRITE;
+ request->operation[1].buffer = data;
+}
+
+static void
+mlxbf_i2c_smbus_i2c_block_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, u8 *data_len, bool read,
+ bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_2;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ if (read)
+ request->operation[0].flags |= MLXBF_I2C_F_WRITE_WITHOUT_STOP;
+
+ /*
+ * As specified in the standard, the max number of bytes to read/write
+ * per block operation is 32 bytes. In Golan code, the controller can
+ * read up to 128 bytes and write up to 127 bytes.
+ */
+ request->operation[1].length =
+ (*data_len + pec_check > I2C_SMBUS_BLOCK_MAX) ?
+ I2C_SMBUS_BLOCK_MAX : *data_len + pec_check;
+ request->operation[1].flags = read ?
+ MLXBF_I2C_F_READ : MLXBF_I2C_F_WRITE;
+ /*
+ * Skip the first data byte, which corresponds to the number of bytes
+ * to read/write.
+ */
+ request->operation[1].buffer = data + 1;
+
+ *data_len = request->operation[1].length;
+
+ /* Set the number of byte to read. This will be used by userspace. */
+ if (read)
+ data[0] = *data_len;
+}
+
+static void mlxbf_i2c_smbus_block_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, u8 *data_len,
+ bool read, bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_2;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= MLXBF_I2C_F_SMBUS_BLOCK;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ request->operation[1].length =
+ (*data_len + pec_check > I2C_SMBUS_BLOCK_MAX) ?
+ I2C_SMBUS_BLOCK_MAX : *data_len + pec_check;
+ request->operation[1].flags = read ?
+ MLXBF_I2C_F_READ : MLXBF_I2C_F_WRITE;
+ request->operation[1].buffer = data + 1;
+
+ *data_len = request->operation[1].length;
+
+ /* Set the number of bytes to read. This will be used by userspace. */
+ if (read)
+ data[0] = *data_len;
+}
+
+static void
+mlxbf_i2c_smbus_process_call_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, bool pec_check)
+{
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_3;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= MLXBF_I2C_F_SMBUS_BLOCK;
+ request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ request->operation[1].length = 2;
+ request->operation[1].flags = MLXBF_I2C_F_WRITE;
+ request->operation[1].buffer = data;
+
+ request->operation[2].length = 3;
+ request->operation[2].flags = MLXBF_I2C_F_READ;
+ request->operation[2].buffer = data;
+}
+
+static void
+mlxbf_i2c_smbus_blk_process_call_func(struct mlxbf_i2c_smbus_request *request,
+ u8 *command, u8 *data, u8 *data_len,
+ bool pec_check)
+{
+ u32 length;
+
+ request->operation_cnt = MLXBF_I2C_SMBUS_OP_CNT_3;
+
+ request->operation[0].length = 1;
+ request->operation[0].flags =
+ MLXBF_I2C_F_SMBUS_OPERATION | MLXBF_I2C_F_WRITE;
+ request->operation[0].flags |= MLXBF_I2C_F_SMBUS_BLOCK;
+ request->operation[0].flags |= (pec_check) ? MLXBF_I2C_F_SMBUS_PEC : 0;
+ request->operation[0].buffer = command;
+
+ length = (*data_len + pec_check > I2C_SMBUS_BLOCK_MAX) ?
+ I2C_SMBUS_BLOCK_MAX : *data_len + pec_check;
+
+ request->operation[1].length = length - pec_check;
+ request->operation[1].flags = MLXBF_I2C_F_WRITE;
+ request->operation[1].buffer = data;
+
+ request->operation[2].length = length;
+ request->operation[2].flags = MLXBF_I2C_F_READ;
+ request->operation[2].buffer = data;
+
+ *data_len = length; /* including PEC byte. */
+}
+
+/* Initialization functions. */
+
+static bool mlxbf_i2c_has_chip_type(struct mlxbf_i2c_priv *priv, u8 type)
+{
+ return priv->chip->type == type;
+}
+
+static struct mlxbf_i2c_resource *
+mlxbf_i2c_get_shared_resource(struct mlxbf_i2c_priv *priv, u8 type)
+{
+ const struct mlxbf_i2c_chip_info *chip = priv->chip;
+ struct mlxbf_i2c_resource *res;
+ u8 res_idx = 0;
+
+ for (res_idx = 0; res_idx < MLXBF_I2C_SHARED_RES_MAX; res_idx++) {
+ res = chip->shared_res[res_idx];
+ if (res && res->type == type)
+ return res;
+ }
+
+ return NULL;
+}
+
+static int mlxbf_i2c_init_resource(struct platform_device *pdev,
+ struct mlxbf_i2c_resource **res,
+ u8 type)
+{
+ struct mlxbf_i2c_resource *tmp_res;
+ struct device *dev = &pdev->dev;
+
+ if (!res || *res || type >= MLXBF_I2C_END_RES)
+ return -EINVAL;
+
+ tmp_res = devm_kzalloc(dev, sizeof(struct mlxbf_i2c_resource),
+ GFP_KERNEL);
+ if (!tmp_res)
+ return -ENOMEM;
+
+ tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params);
+ if (IS_ERR(tmp_res->io)) {
+ devm_kfree(dev, tmp_res);
+ return PTR_ERR(tmp_res->io);
+ }
+
+ tmp_res->type = type;
+
+ *res = tmp_res;
+
+ return 0;
+}
+
+static u32 mlxbf_i2c_get_ticks(struct mlxbf_i2c_priv *priv, u64 nanoseconds,
+ bool minimum)
+{
+ u64 frequency;
+ u32 ticks;
+
+ /*
+ * Compute ticks as follow:
+ *
+ * Ticks
+ * Time = --------- x 10^9 => Ticks = Time x Frequency x 10^-9
+ * Frequency
+ */
+ frequency = priv->frequency;
+ ticks = div_u64(nanoseconds * frequency, MLXBF_I2C_FREQUENCY_1GHZ);
+ /*
+ * The number of ticks is rounded down and if minimum is equal to 1
+ * then add one tick.
+ */
+ if (minimum)
+ ticks++;
+
+ return ticks;
+}
+
+static u32 mlxbf_i2c_set_timer(struct mlxbf_i2c_priv *priv, u64 nsec, bool opt,
+ u32 mask, u8 shift)
+{
+ u32 val = (mlxbf_i2c_get_ticks(priv, nsec, opt) & mask) << shift;
+
+ return val;
+}
+
+static void mlxbf_i2c_set_timings(struct mlxbf_i2c_priv *priv,
+ const struct mlxbf_i2c_timings *timings)
+{
+ u32 timer;
+
+ timer = mlxbf_i2c_set_timer(priv, timings->scl_high,
+ false, MLXBF_I2C_MASK_16,
+ MLXBF_I2C_SHIFT_0);
+ timer |= mlxbf_i2c_set_timer(priv, timings->scl_low,
+ false, MLXBF_I2C_MASK_16,
+ MLXBF_I2C_SHIFT_16);
+ writel(timer, priv->timer->io +
+ MLXBF_I2C_SMBUS_TIMER_SCL_LOW_SCL_HIGH);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->sda_rise, false,
+ MLXBF_I2C_MASK_8, MLXBF_I2C_SHIFT_0);
+ timer |= mlxbf_i2c_set_timer(priv, timings->sda_fall, false,
+ MLXBF_I2C_MASK_8, MLXBF_I2C_SHIFT_8);
+ timer |= mlxbf_i2c_set_timer(priv, timings->scl_rise, false,
+ MLXBF_I2C_MASK_8, MLXBF_I2C_SHIFT_16);
+ timer |= mlxbf_i2c_set_timer(priv, timings->scl_fall, false,
+ MLXBF_I2C_MASK_8, MLXBF_I2C_SHIFT_24);
+ writel(timer, priv->timer->io +
+ MLXBF_I2C_SMBUS_TIMER_FALL_RISE_SPIKE);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->hold_start, true,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_0);
+ timer |= mlxbf_i2c_set_timer(priv, timings->hold_data, true,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_16);
+ writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_TIMER_THOLD);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->setup_start, true,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_0);
+ timer |= mlxbf_i2c_set_timer(priv, timings->setup_stop, true,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_16);
+ writel(timer, priv->timer->io +
+ MLXBF_I2C_SMBUS_TIMER_TSETUP_START_STOP);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->setup_data, true,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_0);
+ writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_TIMER_TSETUP_DATA);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->buf, false,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_0);
+ timer |= mlxbf_i2c_set_timer(priv, timings->thigh_max, false,
+ MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_16);
+ writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_THIGH_MAX_TBUF);
+
+ timer = mlxbf_i2c_set_timer(priv, timings->timeout, false,
+ MLXBF_I2C_MASK_32, MLXBF_I2C_SHIFT_0);
+ writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT);
+}
+
+enum mlxbf_i2c_timings_config {
+ MLXBF_I2C_TIMING_CONFIG_100KHZ,
+ MLXBF_I2C_TIMING_CONFIG_400KHZ,
+ MLXBF_I2C_TIMING_CONFIG_1000KHZ,
+};
+
+/* Timing values are in nanoseconds */
+static const struct mlxbf_i2c_timings mlxbf_i2c_timings[] = {
+ [MLXBF_I2C_TIMING_CONFIG_100KHZ] = {
+ .scl_high = 4810,
+ .scl_low = 5000,
+ .hold_start = 4000,
+ .setup_start = 4800,
+ .setup_stop = 4000,
+ .setup_data = 250,
+ .sda_rise = 50,
+ .sda_fall = 50,
+ .scl_rise = 50,
+ .scl_fall = 50,
+ .hold_data = 300,
+ .buf = 20000,
+ .thigh_max = 50000,
+ .timeout = 35000000
+ },
+ [MLXBF_I2C_TIMING_CONFIG_400KHZ] = {
+ .scl_high = 1011,
+ .scl_low = 1300,
+ .hold_start = 600,
+ .setup_start = 700,
+ .setup_stop = 600,
+ .setup_data = 100,
+ .sda_rise = 50,
+ .sda_fall = 50,
+ .scl_rise = 50,
+ .scl_fall = 50,
+ .hold_data = 300,
+ .buf = 20000,
+ .thigh_max = 50000,
+ .timeout = 35000000
+ },
+ [MLXBF_I2C_TIMING_CONFIG_1000KHZ] = {
+ .scl_high = 383,
+ .scl_low = 460,
+ .hold_start = 600,
+ .setup_start = 260,
+ .setup_stop = 260,
+ .setup_data = 50,
+ .sda_rise = 50,
+ .sda_fall = 50,
+ .scl_rise = 50,
+ .scl_fall = 50,
+ .hold_data = 300,
+ .buf = 500,
+ .thigh_max = 50000,
+ .timeout = 35000000
+ }
+};
+
+static int mlxbf_i2c_init_timings(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ enum mlxbf_i2c_timings_config config_idx;
+ struct device *dev = &pdev->dev;
+ u32 config_khz;
+
+ int ret;
+
+ ret = device_property_read_u32(dev, "clock-frequency", &config_khz);
+ if (ret < 0)
+ config_khz = I2C_MAX_STANDARD_MODE_FREQ;
+
+ switch (config_khz) {
+ default:
+ /* Default settings is 100 KHz. */
+ pr_warn("Illegal value %d: defaulting to 100 KHz\n",
+ config_khz);
+ fallthrough;
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ;
+ break;
+
+ case I2C_MAX_FAST_MODE_FREQ:
+ config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ;
+ break;
+
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ;
+ break;
+ }
+
+ mlxbf_i2c_set_timings(priv, &mlxbf_i2c_timings[config_idx]);
+
+ return 0;
+}
+
+static int mlxbf_i2c_get_gpio(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *gpio_res;
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+ resource_size_t size;
+
+ gpio_res = mlxbf_i2c_get_shared_resource(priv, MLXBF_I2C_GPIO_RES);
+ if (!gpio_res)
+ return -EPERM;
+
+ /*
+ * The GPIO region in TYU space is shared among I2C busses.
+ * This function MUST be serialized to avoid racing when
+ * claiming the memory region and/or setting up the GPIO.
+ */
+ lockdep_assert_held(gpio_res->lock);
+
+ /* Check whether the memory map exist. */
+ if (gpio_res->io)
+ return 0;
+
+ params = gpio_res->params;
+ size = resource_size(params);
+
+ if (!devm_request_mem_region(dev, params->start, size, params->name))
+ return -EFAULT;
+
+ gpio_res->io = devm_ioremap(dev, params->start, size);
+ if (!gpio_res->io) {
+ devm_release_mem_region(dev, params->start, size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int mlxbf_i2c_release_gpio(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *gpio_res;
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+
+ gpio_res = mlxbf_i2c_get_shared_resource(priv, MLXBF_I2C_GPIO_RES);
+ if (!gpio_res)
+ return 0;
+
+ mutex_lock(gpio_res->lock);
+
+ if (gpio_res->io) {
+ /* Release the GPIO resource. */
+ params = gpio_res->params;
+ devm_iounmap(dev, gpio_res->io);
+ devm_release_mem_region(dev, params->start,
+ resource_size(params));
+ }
+
+ mutex_unlock(gpio_res->lock);
+
+ return 0;
+}
+
+static int mlxbf_i2c_get_corepll(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *corepll_res;
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+ resource_size_t size;
+
+ corepll_res = mlxbf_i2c_get_shared_resource(priv,
+ MLXBF_I2C_COREPLL_RES);
+ if (!corepll_res)
+ return -EPERM;
+
+ /*
+ * The COREPLL region in TYU space is shared among I2C busses.
+ * This function MUST be serialized to avoid racing when
+ * claiming the memory region.
+ */
+ lockdep_assert_held(corepll_res->lock);
+
+ /* Check whether the memory map exist. */
+ if (corepll_res->io)
+ return 0;
+
+ params = corepll_res->params;
+ size = resource_size(params);
+
+ if (!devm_request_mem_region(dev, params->start, size, params->name))
+ return -EFAULT;
+
+ corepll_res->io = devm_ioremap(dev, params->start, size);
+ if (!corepll_res->io) {
+ devm_release_mem_region(dev, params->start, size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int mlxbf_i2c_release_corepll(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *corepll_res;
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+
+ corepll_res = mlxbf_i2c_get_shared_resource(priv,
+ MLXBF_I2C_COREPLL_RES);
+
+ mutex_lock(corepll_res->lock);
+
+ if (corepll_res->io) {
+ /* Release the CorePLL resource. */
+ params = corepll_res->params;
+ devm_iounmap(dev, corepll_res->io);
+ devm_release_mem_region(dev, params->start,
+ resource_size(params));
+ }
+
+ mutex_unlock(corepll_res->lock);
+
+ return 0;
+}
+
+static int mlxbf_i2c_init_master(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *gpio_res;
+ struct device *dev = &pdev->dev;
+ u32 config_reg;
+ int ret;
+
+ /* This configuration is only needed for BlueField 1. */
+ if (!mlxbf_i2c_has_chip_type(priv, MLXBF_I2C_CHIP_TYPE_1))
+ return 0;
+
+ gpio_res = mlxbf_i2c_get_shared_resource(priv, MLXBF_I2C_GPIO_RES);
+ if (!gpio_res)
+ return -EPERM;
+
+ /*
+ * The GPIO region in TYU space is shared among I2C busses.
+ * This function MUST be serialized to avoid racing when
+ * claiming the memory region and/or setting up the GPIO.
+ */
+
+ mutex_lock(gpio_res->lock);
+
+ ret = mlxbf_i2c_get_gpio(pdev, priv);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get gpio resource");
+ mutex_unlock(gpio_res->lock);
+ return ret;
+ }
+
+ /*
+ * TYU - Configuration for GPIO pins. Those pins must be asserted in
+ * MLXBF_I2C_GPIO_0_FUNC_EN_0, i.e. GPIO 0 is controlled by HW, and must
+ * be reset in MLXBF_I2C_GPIO_0_FORCE_OE_EN, i.e. GPIO_OE will be driven
+ * instead of HW_OE.
+ * For now, we do not reset the GPIO state when the driver is removed.
+ * First, it is not necessary to disable the bus since we are using
+ * the same busses. Then, some busses might be shared among Linux and
+ * platform firmware; disabling the bus might compromise the system
+ * functionality.
+ */
+ config_reg = readl(gpio_res->io + MLXBF_I2C_GPIO_0_FUNC_EN_0);
+ config_reg = MLXBF_I2C_GPIO_SMBUS_GW_ASSERT_PINS(priv->bus,
+ config_reg);
+ writel(config_reg, gpio_res->io + MLXBF_I2C_GPIO_0_FUNC_EN_0);
+
+ config_reg = readl(gpio_res->io + MLXBF_I2C_GPIO_0_FORCE_OE_EN);
+ config_reg = MLXBF_I2C_GPIO_SMBUS_GW_RESET_PINS(priv->bus,
+ config_reg);
+ writel(config_reg, gpio_res->io + MLXBF_I2C_GPIO_0_FORCE_OE_EN);
+
+ mutex_unlock(gpio_res->lock);
+
+ return 0;
+}
+
+static u64 mlxbf_i2c_calculate_freq_from_tyu(struct mlxbf_i2c_resource *corepll_res)
+{
+ u64 core_frequency;
+ u8 core_od, core_r;
+ u32 corepll_val;
+ u16 core_f;
+
+ corepll_val = readl(corepll_res->io + MLXBF_I2C_CORE_PLL_REG1);
+
+ /* Get Core PLL configuration bits. */
+ core_f = FIELD_GET(MLXBF_I2C_COREPLL_CORE_F_TYU_MASK, corepll_val);
+ core_od = FIELD_GET(MLXBF_I2C_COREPLL_CORE_OD_TYU_MASK, corepll_val);
+ core_r = FIELD_GET(MLXBF_I2C_COREPLL_CORE_R_TYU_MASK, corepll_val);
+
+ /*
+ * Compute PLL output frequency as follow:
+ *
+ * CORE_F + 1
+ * PLL_OUT_FREQ = PLL_IN_FREQ * ----------------------------
+ * (CORE_R + 1) * (CORE_OD + 1)
+ *
+ * Where PLL_OUT_FREQ and PLL_IN_FREQ refer to CoreFrequency
+ * and PadFrequency, respectively.
+ */
+ core_frequency = MLXBF_I2C_PLL_IN_FREQ * (++core_f);
+
+ return div_u64(core_frequency, (++core_r) * (++core_od));
+}
+
+static u64 mlxbf_i2c_calculate_freq_from_yu(struct mlxbf_i2c_resource *corepll_res)
+{
+ u32 corepll_reg1_val, corepll_reg2_val;
+ u64 corepll_frequency;
+ u8 core_od, core_r;
+ u32 core_f;
+
+ corepll_reg1_val = readl(corepll_res->io + MLXBF_I2C_CORE_PLL_REG1);
+ corepll_reg2_val = readl(corepll_res->io + MLXBF_I2C_CORE_PLL_REG2);
+
+ /* Get Core PLL configuration bits */
+ core_f = FIELD_GET(MLXBF_I2C_COREPLL_CORE_F_YU_MASK, corepll_reg1_val);
+ core_r = FIELD_GET(MLXBF_I2C_COREPLL_CORE_R_YU_MASK, corepll_reg1_val);
+ core_od = FIELD_GET(MLXBF_I2C_COREPLL_CORE_OD_YU_MASK, corepll_reg2_val);
+
+ /*
+ * Compute PLL output frequency as follow:
+ *
+ * CORE_F / 16384
+ * PLL_OUT_FREQ = PLL_IN_FREQ * ----------------------------
+ * (CORE_R + 1) * (CORE_OD + 1)
+ *
+ * Where PLL_OUT_FREQ and PLL_IN_FREQ refer to CoreFrequency
+ * and PadFrequency, respectively.
+ */
+ corepll_frequency = (MLXBF_I2C_PLL_IN_FREQ * core_f) / MLNXBF_I2C_COREPLL_CONST;
+
+ return div_u64(corepll_frequency, (++core_r) * (++core_od));
+}
+
+static int mlxbf_i2c_calculate_corepll_freq(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ const struct mlxbf_i2c_chip_info *chip = priv->chip;
+ struct mlxbf_i2c_resource *corepll_res;
+ struct device *dev = &pdev->dev;
+ u64 *freq = &priv->frequency;
+ int ret;
+
+ corepll_res = mlxbf_i2c_get_shared_resource(priv,
+ MLXBF_I2C_COREPLL_RES);
+ if (!corepll_res)
+ return -EPERM;
+
+ /*
+ * First, check whether the TYU core Clock frequency is set.
+ * The TYU core frequency is the same for all I2C busses; when
+ * the first device gets probed the frequency is determined and
+ * stored into a globally visible variable. So, first of all,
+ * check whether the frequency is already set. Here, we assume
+ * that the frequency is expected to be greater than 0.
+ */
+ mutex_lock(corepll_res->lock);
+ if (!mlxbf_i2c_corepll_frequency) {
+ if (!chip->calculate_freq) {
+ mutex_unlock(corepll_res->lock);
+ return -EPERM;
+ }
+
+ ret = mlxbf_i2c_get_corepll(pdev, priv);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get corePLL resource");
+ mutex_unlock(corepll_res->lock);
+ return ret;
+ }
+
+ mlxbf_i2c_corepll_frequency = chip->calculate_freq(corepll_res);
+ }
+ mutex_unlock(corepll_res->lock);
+
+ *freq = mlxbf_i2c_corepll_frequency;
+
+ return 0;
+}
+
+static int mlxbf_i2c_slave_enable(struct mlxbf_i2c_priv *priv,
+ struct i2c_client *slave)
+{
+ u8 reg, reg_cnt, byte, addr_tmp;
+ u32 slave_reg, slave_reg_tmp;
+
+ if (!priv)
+ return -EPERM;
+
+ reg_cnt = MLXBF_I2C_SMBUS_SLAVE_ADDR_CNT >> 2;
+
+ /*
+ * Read the slave registers. There are 4 * 32-bit slave registers.
+ * Each slave register can hold up to 4 * 8-bit slave configuration:
+ * 1) A 7-bit address
+ * 2) And a status bit (1 if enabled, 0 if not).
+ * Look for the next available slave register slot.
+ */
+ for (reg = 0; reg < reg_cnt; reg++) {
+ slave_reg = readl(priv->slv->io +
+ MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG + reg * 0x4);
+ /*
+ * Each register holds 4 slave addresses. So, we have to keep
+ * the byte order consistent with the value read in order to
+ * update the register correctly, if needed.
+ */
+ slave_reg_tmp = slave_reg;
+ for (byte = 0; byte < 4; byte++) {
+ addr_tmp = slave_reg_tmp & GENMASK(7, 0);
+
+ /*
+ * If an enable bit is not set in the
+ * MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG register, then the
+ * slave address slot associated with that bit is
+ * free. So set the enable bit and write the
+ * slave address bits.
+ */
+ if (!(addr_tmp & MLXBF_I2C_SMBUS_SLAVE_ADDR_EN_BIT)) {
+ slave_reg &= ~(MLXBF_I2C_SMBUS_SLAVE_ADDR_MASK << (byte * 8));
+ slave_reg |= (slave->addr << (byte * 8));
+ slave_reg |= MLXBF_I2C_SMBUS_SLAVE_ADDR_EN_BIT << (byte * 8);
+ writel(slave_reg, priv->slv->io +
+ MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG +
+ (reg * 0x4));
+
+ /*
+ * Set the slave at the corresponding index.
+ */
+ priv->slave[(reg * 4) + byte] = slave;
+
+ return 0;
+ }
+
+ /* Parse next byte. */
+ slave_reg_tmp >>= 8;
+ }
+ }
+
+ return -EBUSY;
+}
+
+static int mlxbf_i2c_slave_disable(struct mlxbf_i2c_priv *priv, u8 addr)
+{
+ u8 addr_tmp, reg, reg_cnt, byte;
+ u32 slave_reg, slave_reg_tmp;
+
+ reg_cnt = MLXBF_I2C_SMBUS_SLAVE_ADDR_CNT >> 2;
+
+ /*
+ * Read the slave registers. There are 4 * 32-bit slave registers.
+ * Each slave register can hold up to 4 * 8-bit slave configuration:
+ * 1) A 7-bit address
+ * 2) And a status bit (1 if enabled, 0 if not).
+ * Check if addr is present in the registers.
+ */
+ for (reg = 0; reg < reg_cnt; reg++) {
+ slave_reg = readl(priv->slv->io +
+ MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG + reg * 0x4);
+
+ /* Check whether the address slots are empty. */
+ if (!slave_reg)
+ continue;
+
+ /*
+ * Check if addr matches any of the 4 slave addresses
+ * in the register.
+ */
+ slave_reg_tmp = slave_reg;
+ for (byte = 0; byte < 4; byte++) {
+ addr_tmp = slave_reg_tmp & MLXBF_I2C_SMBUS_SLAVE_ADDR_MASK;
+ /*
+ * Parse slave address bytes and check whether the
+ * slave address already exists.
+ */
+ if (addr_tmp == addr) {
+ /* Clear the slave address slot. */
+ slave_reg &= ~(GENMASK(7, 0) << (byte * 8));
+ writel(slave_reg, priv->slv->io +
+ MLXBF_I2C_SMBUS_SLAVE_ADDR_CFG +
+ (reg * 0x4));
+ /* Free slave at the corresponding index */
+ priv->slave[(reg * 4) + byte] = NULL;
+
+ return 0;
+ }
+
+ /* Parse next byte. */
+ slave_reg_tmp >>= 8;
+ }
+ }
+
+ return -ENXIO;
+}
+
+static int mlxbf_i2c_init_coalesce(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *coalesce_res;
+ struct resource *params;
+ resource_size_t size;
+ int ret = 0;
+
+ /*
+ * Unlike BlueField-1 platform, the coalesce registers is a dedicated
+ * resource in the next generations of BlueField.
+ */
+ if (mlxbf_i2c_has_chip_type(priv, MLXBF_I2C_CHIP_TYPE_1)) {
+ coalesce_res = mlxbf_i2c_get_shared_resource(priv,
+ MLXBF_I2C_COALESCE_RES);
+ if (!coalesce_res)
+ return -EPERM;
+
+ /*
+ * The Cause Coalesce group in TYU space is shared among
+ * I2C busses. This function MUST be serialized to avoid
+ * racing when claiming the memory region.
+ */
+ lockdep_assert_held(mlxbf_i2c_gpio_res->lock);
+
+ /* Check whether the memory map exist. */
+ if (coalesce_res->io) {
+ priv->coalesce = coalesce_res;
+ return 0;
+ }
+
+ params = coalesce_res->params;
+ size = resource_size(params);
+
+ if (!request_mem_region(params->start, size, params->name))
+ return -EFAULT;
+
+ coalesce_res->io = ioremap(params->start, size);
+ if (!coalesce_res->io) {
+ release_mem_region(params->start, size);
+ return -ENOMEM;
+ }
+
+ priv->coalesce = coalesce_res;
+
+ } else {
+ ret = mlxbf_i2c_init_resource(pdev, &priv->coalesce,
+ MLXBF_I2C_COALESCE_RES);
+ }
+
+ return ret;
+}
+
+static int mlxbf_i2c_release_coalesce(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct mlxbf_i2c_resource *coalesce_res;
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+ resource_size_t size;
+
+ coalesce_res = priv->coalesce;
+
+ if (coalesce_res->io) {
+ params = coalesce_res->params;
+ size = resource_size(params);
+ if (mlxbf_i2c_has_chip_type(priv, MLXBF_I2C_CHIP_TYPE_1)) {
+ mutex_lock(coalesce_res->lock);
+ iounmap(coalesce_res->io);
+ release_mem_region(params->start, size);
+ mutex_unlock(coalesce_res->lock);
+ } else {
+ devm_release_mem_region(dev, params->start, size);
+ }
+ }
+
+ return 0;
+}
+
+static int mlxbf_i2c_init_slave(struct platform_device *pdev,
+ struct mlxbf_i2c_priv *priv)
+{
+ struct device *dev = &pdev->dev;
+ u32 int_reg;
+ int ret;
+
+ /* Reset FSM. */
+ writel(0, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_FSM);
+
+ /*
+ * Enable slave cause interrupt bits. Drive
+ * MLXBF_I2C_CAUSE_READ_WAIT_FW_RESPONSE and
+ * MLXBF_I2C_CAUSE_WRITE_SUCCESS, these are enabled when an external
+ * masters issue a Read and Write, respectively. But, clear all
+ * interrupts first.
+ */
+ writel(~0, priv->slv_cause->io + MLXBF_I2C_CAUSE_OR_CLEAR);
+ int_reg = MLXBF_I2C_CAUSE_READ_WAIT_FW_RESPONSE;
+ int_reg |= MLXBF_I2C_CAUSE_WRITE_SUCCESS;
+ writel(int_reg, priv->slv_cause->io + MLXBF_I2C_CAUSE_OR_EVTEN0);
+
+ /* Finally, set the 'ready' bit to start handling transactions. */
+ writel(0x1, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_READY);
+
+ /* Initialize the cause coalesce resource. */
+ ret = mlxbf_i2c_init_coalesce(pdev, priv);
+ if (ret < 0) {
+ dev_err(dev, "failed to initialize cause coalesce\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool mlxbf_i2c_has_coalesce(struct mlxbf_i2c_priv *priv, bool *read,
+ bool *write)
+{
+ const struct mlxbf_i2c_chip_info *chip = priv->chip;
+ u32 coalesce0_reg, cause_reg;
+ u8 slave_shift, is_set;
+
+ *write = false;
+ *read = false;
+
+ slave_shift = chip->type != MLXBF_I2C_CHIP_TYPE_1 ?
+ MLXBF_I2C_CAUSE_YU_SLAVE_BIT :
+ priv->bus + MLXBF_I2C_CAUSE_TYU_SLAVE_BIT;
+
+ coalesce0_reg = readl(priv->coalesce->io + MLXBF_I2C_CAUSE_COALESCE_0);
+ is_set = coalesce0_reg & (1 << slave_shift);
+
+ if (!is_set)
+ return false;
+
+ /* Check the source of the interrupt, i.e. whether a Read or Write. */
+ cause_reg = readl(priv->slv_cause->io + MLXBF_I2C_CAUSE_ARBITER);
+ if (cause_reg & MLXBF_I2C_CAUSE_READ_WAIT_FW_RESPONSE)
+ *read = true;
+ else if (cause_reg & MLXBF_I2C_CAUSE_WRITE_SUCCESS)
+ *write = true;
+
+ /* Clear cause bits. */
+ writel(~0x0, priv->slv_cause->io + MLXBF_I2C_CAUSE_OR_CLEAR);
+
+ return true;
+}
+
+static struct i2c_client *mlxbf_i2c_get_slave_from_addr(
+ struct mlxbf_i2c_priv *priv, u8 addr)
+{
+ int i;
+
+ for (i = 0; i < MLXBF_I2C_SMBUS_SLAVE_ADDR_CNT; i++) {
+ if (!priv->slave[i])
+ continue;
+
+ if (priv->slave[i]->addr == addr)
+ return priv->slave[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Send byte to 'external' smbus master. This function is executed when
+ * an external smbus master wants to read data from the BlueField.
+ */
+static int mlxbf_i2c_irq_send(struct mlxbf_i2c_priv *priv, u8 recv_bytes)
+{
+ u8 data_desc[MLXBF_I2C_SLAVE_DATA_DESC_SIZE] = { 0 };
+ u8 write_size, pec_en, addr, value, byte_cnt;
+ struct i2c_client *slave;
+ u32 control32, data32;
+ int ret = 0;
+
+ /*
+ * Read the first byte received from the external master to
+ * determine the slave address. This byte is located in the
+ * first data descriptor register of the slave GW.
+ */
+ data32 = ioread32be(priv->slv->io +
+ MLXBF_I2C_SLAVE_DATA_DESC_ADDR);
+ addr = (data32 & GENMASK(7, 0)) >> 1;
+
+ /*
+ * Check if the slave address received in the data descriptor register
+ * matches any of the slave addresses registered. If there is a match,
+ * set the slave.
+ */
+ slave = mlxbf_i2c_get_slave_from_addr(priv, addr);
+ if (!slave) {
+ ret = -ENXIO;
+ goto clear_csr;
+ }
+
+ /*
+ * An I2C read can consist of a WRITE bit transaction followed by
+ * a READ bit transaction. Indeed, slave devices often expect
+ * the slave address to be followed by the internal address.
+ * So, write the internal address byte first, and then, send the
+ * requested data to the master.
+ */
+ if (recv_bytes > 1) {
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ value = (data32 >> 8) & GENMASK(7, 0);
+ ret = i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED,
+ &value);
+ i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+ if (ret < 0)
+ goto clear_csr;
+ }
+
+ /*
+ * Send data to the master. Currently, the driver supports
+ * READ_BYTE, READ_WORD and BLOCK READ protocols. The
+ * hardware can send up to 128 bytes per transfer which is
+ * the total size of the data registers.
+ */
+ i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+
+ for (byte_cnt = 0; byte_cnt < MLXBF_I2C_SLAVE_DATA_DESC_SIZE; byte_cnt++) {
+ data_desc[byte_cnt] = value;
+ i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+
+ /* Send a stop condition to the backend. */
+ i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+ /* Set the number of bytes to write to master. */
+ write_size = (byte_cnt - 1) & 0x7f;
+
+ /* Write data to Slave GW data descriptor. */
+ mlxbf_i2c_smbus_write_data(priv, data_desc, byte_cnt,
+ MLXBF_I2C_SLAVE_DATA_DESC_ADDR, false);
+
+ pec_en = 0; /* Disable PEC since it is not supported. */
+
+ /* Prepare control word. */
+ control32 = MLXBF_I2C_SLAVE_ENABLE;
+ control32 |= rol32(write_size, MLXBF_I2C_SLAVE_WRITE_BYTES_SHIFT);
+ control32 |= rol32(pec_en, MLXBF_I2C_SLAVE_SEND_PEC_SHIFT);
+
+ writel(control32, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_GW);
+
+ /*
+ * Wait until the transfer is completed; the driver will wait
+ * until the GW is idle, a cause will rise on fall of GW busy.
+ */
+ readl_poll_timeout_atomic(priv->slv_cause->io + MLXBF_I2C_CAUSE_ARBITER,
+ data32, data32 & MLXBF_I2C_CAUSE_S_GW_BUSY_FALL,
+ MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT);
+
+clear_csr:
+ /* Release the Slave GW. */
+ writel(0x0, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_RS_MASTER_BYTES);
+ writel(0x0, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_PEC);
+ writel(0x1, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_READY);
+
+ return ret;
+}
+
+/*
+ * Receive bytes from 'external' smbus master. This function is executed when
+ * an external smbus master wants to write data to the BlueField.
+ */
+static int mlxbf_i2c_irq_recv(struct mlxbf_i2c_priv *priv, u8 recv_bytes)
+{
+ u8 data_desc[MLXBF_I2C_SLAVE_DATA_DESC_SIZE] = { 0 };
+ struct i2c_client *slave;
+ u8 value, byte, addr;
+ int ret = 0;
+
+ /* Read data from Slave GW data descriptor. */
+ mlxbf_i2c_smbus_read_data(priv, data_desc, recv_bytes,
+ MLXBF_I2C_SLAVE_DATA_DESC_ADDR, false);
+ addr = data_desc[0] >> 1;
+
+ /*
+ * Check if the slave address received in the data descriptor register
+ * matches any of the slave addresses registered.
+ */
+ slave = mlxbf_i2c_get_slave_from_addr(priv, addr);
+ if (!slave) {
+ ret = -EINVAL;
+ goto clear_csr;
+ }
+
+ /*
+ * Notify the slave backend that an smbus master wants to write data
+ * to the BlueField.
+ */
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ /* Send the received data to the slave backend. */
+ for (byte = 1; byte < recv_bytes; byte++) {
+ value = data_desc[byte];
+ ret = i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED,
+ &value);
+ if (ret < 0)
+ break;
+ }
+
+ /*
+ * Send a stop event to the slave backend, to signal
+ * the end of the write transactions.
+ */
+ i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+clear_csr:
+ /* Release the Slave GW. */
+ writel(0x0, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_RS_MASTER_BYTES);
+ writel(0x0, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_PEC);
+ writel(0x1, priv->slv->io + MLXBF_I2C_SMBUS_SLAVE_READY);
+
+ return ret;
+}
+
+static irqreturn_t mlxbf_i2c_irq(int irq, void *ptr)
+{
+ struct mlxbf_i2c_priv *priv = ptr;
+ bool read, write, irq_is_set;
+ u32 rw_bytes_reg;
+ u8 recv_bytes;
+
+ /*
+ * Read TYU interrupt register and determine the source of the
+ * interrupt. Based on the source of the interrupt one of the
+ * following actions are performed:
+ * - Receive data and send response to master.
+ * - Send data and release slave GW.
+ *
+ * Handle read/write transaction only. CRmaster and Iarp requests
+ * are ignored for now.
+ */
+ irq_is_set = mlxbf_i2c_has_coalesce(priv, &read, &write);
+ if (!irq_is_set || (!read && !write)) {
+ /* Nothing to do here, interrupt was not from this device. */
+ return IRQ_NONE;
+ }
+
+ /*
+ * The MLXBF_I2C_SMBUS_SLAVE_RS_MASTER_BYTES includes the number of
+ * bytes from/to master. These are defined by 8-bits each. If the lower
+ * 8 bits are set, then the master expect to read N bytes from the
+ * slave, if the higher 8 bits are sent then the slave expect N bytes
+ * from the master.
+ */
+ rw_bytes_reg = readl(priv->slv->io +
+ MLXBF_I2C_SMBUS_SLAVE_RS_MASTER_BYTES);
+ recv_bytes = (rw_bytes_reg >> 8) & GENMASK(7, 0);
+
+ /*
+ * For now, the slave supports 128 bytes transfer. Discard remaining
+ * data bytes if the master wrote more than
+ * MLXBF_I2C_SLAVE_DATA_DESC_SIZE, i.e, the actual size of the slave
+ * data descriptor.
+ *
+ * Note that we will never expect to transfer more than 128 bytes; as
+ * specified in the SMBus standard, block transactions cannot exceed
+ * 32 bytes.
+ */
+ recv_bytes = recv_bytes > MLXBF_I2C_SLAVE_DATA_DESC_SIZE ?
+ MLXBF_I2C_SLAVE_DATA_DESC_SIZE : recv_bytes;
+
+ if (read)
+ mlxbf_i2c_irq_send(priv, recv_bytes);
+ else
+ mlxbf_i2c_irq_recv(priv, recv_bytes);
+
+ return IRQ_HANDLED;
+}
+
+/* Return negative errno on error. */
+static s32 mlxbf_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ struct mlxbf_i2c_smbus_request request = { 0 };
+ struct mlxbf_i2c_priv *priv;
+ bool read, pec;
+ u8 byte_cnt;
+
+ request.slave = addr;
+
+ read = (read_write == I2C_SMBUS_READ);
+ pec = flags & I2C_FUNC_SMBUS_PEC;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ mlxbf_i2c_smbus_quick_command(&request, read);
+ dev_dbg(&adap->dev, "smbus quick, slave 0x%02x\n", addr);
+ break;
+
+ case I2C_SMBUS_BYTE:
+ mlxbf_i2c_smbus_byte_func(&request,
+ read ? &data->byte : &command, read,
+ pec);
+ dev_dbg(&adap->dev, "smbus %s byte, slave 0x%02x.\n",
+ str_read_write(read), addr);
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ mlxbf_i2c_smbus_data_byte_func(&request, &command, &data->byte,
+ read, pec);
+ dev_dbg(&adap->dev, "smbus %s byte data at 0x%02x, slave 0x%02x.\n",
+ str_read_write(read), command, addr);
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ mlxbf_i2c_smbus_data_word_func(&request, &command,
+ (u8 *)&data->word, read, pec);
+ dev_dbg(&adap->dev, "smbus %s word data at 0x%02x, slave 0x%02x.\n",
+ str_read_write(read), command, addr);
+ break;
+
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ byte_cnt = data->block[0];
+ mlxbf_i2c_smbus_i2c_block_func(&request, &command, data->block,
+ &byte_cnt, read, pec);
+ dev_dbg(&adap->dev, "i2c %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
+ str_read_write(read), byte_cnt, command, addr);
+ break;
+
+ case I2C_SMBUS_BLOCK_DATA:
+ byte_cnt = read ? I2C_SMBUS_BLOCK_MAX : data->block[0];
+ mlxbf_i2c_smbus_block_func(&request, &command, data->block,
+ &byte_cnt, read, pec);
+ dev_dbg(&adap->dev, "smbus %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
+ str_read_write(read), byte_cnt, command, addr);
+ break;
+
+ case I2C_FUNC_SMBUS_PROC_CALL:
+ mlxbf_i2c_smbus_process_call_func(&request, &command,
+ (u8 *)&data->word, pec);
+ dev_dbg(&adap->dev, "process call, wr/rd at 0x%02x, slave 0x%02x.\n",
+ command, addr);
+ break;
+
+ case I2C_FUNC_SMBUS_BLOCK_PROC_CALL:
+ byte_cnt = data->block[0];
+ mlxbf_i2c_smbus_blk_process_call_func(&request, &command,
+ data->block, &byte_cnt,
+ pec);
+ dev_dbg(&adap->dev, "block process call, wr/rd %d bytes, slave 0x%02x.\n",
+ byte_cnt, addr);
+ break;
+
+ default:
+ dev_dbg(&adap->dev, "Unsupported I2C/SMBus command %d\n",
+ size);
+ return -EOPNOTSUPP;
+ }
+
+ priv = i2c_get_adapdata(adap);
+
+ return mlxbf_i2c_smbus_start_transaction(priv, &request);
+}
+
+static int mlxbf_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct mlxbf_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+ struct device *dev = &slave->dev;
+ int ret;
+
+ /*
+ * Do not support ten bit chip address and do not use Packet Error
+ * Checking (PEC).
+ */
+ if (slave->flags & (I2C_CLIENT_TEN | I2C_CLIENT_PEC)) {
+ dev_err(dev, "SMBus PEC and 10 bit address not supported\n");
+ return -EAFNOSUPPORT;
+ }
+
+ ret = mlxbf_i2c_slave_enable(priv, slave);
+ if (ret)
+ dev_err(dev, "Surpassed max number of registered slaves allowed\n");
+
+ return 0;
+}
+
+static int mlxbf_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct mlxbf_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+ struct device *dev = &slave->dev;
+ int ret;
+
+ /*
+ * Unregister slave by:
+ * 1) Disabling the slave address in hardware
+ * 2) Freeing priv->slave at the corresponding index
+ */
+ ret = mlxbf_i2c_slave_disable(priv, slave->addr);
+ if (ret)
+ dev_err(dev, "Unable to find slave 0x%x\n", slave->addr);
+
+ return ret;
+}
+
+static u32 mlxbf_i2c_functionality(struct i2c_adapter *adap)
+{
+ return MLXBF_I2C_FUNC_ALL;
+}
+
+static struct mlxbf_i2c_chip_info mlxbf_i2c_chip[] = {
+ [MLXBF_I2C_CHIP_TYPE_1] = {
+ .type = MLXBF_I2C_CHIP_TYPE_1,
+ .shared_res = {
+ [0] = &mlxbf_i2c_coalesce_res[MLXBF_I2C_CHIP_TYPE_1],
+ [1] = &mlxbf_i2c_corepll_res[MLXBF_I2C_CHIP_TYPE_1],
+ [2] = &mlxbf_i2c_gpio_res[MLXBF_I2C_CHIP_TYPE_1]
+ },
+ .calculate_freq = mlxbf_i2c_calculate_freq_from_tyu,
+ .smbus_master_rs_bytes_off = MLXBF_I2C_YU_SMBUS_RS_BYTES,
+ .smbus_master_fsm_off = MLXBF_I2C_YU_SMBUS_MASTER_FSM
+ },
+ [MLXBF_I2C_CHIP_TYPE_2] = {
+ .type = MLXBF_I2C_CHIP_TYPE_2,
+ .shared_res = {
+ [0] = &mlxbf_i2c_corepll_res[MLXBF_I2C_CHIP_TYPE_2]
+ },
+ .calculate_freq = mlxbf_i2c_calculate_freq_from_yu,
+ .smbus_master_rs_bytes_off = MLXBF_I2C_YU_SMBUS_RS_BYTES,
+ .smbus_master_fsm_off = MLXBF_I2C_YU_SMBUS_MASTER_FSM
+ },
+ [MLXBF_I2C_CHIP_TYPE_3] = {
+ .type = MLXBF_I2C_CHIP_TYPE_3,
+ .shared_res = {
+ [0] = &mlxbf_i2c_corepll_res[MLXBF_I2C_CHIP_TYPE_3]
+ },
+ .calculate_freq = mlxbf_i2c_calculate_freq_from_yu,
+ .smbus_master_rs_bytes_off = MLXBF_I2C_RSH_YU_SMBUS_RS_BYTES,
+ .smbus_master_fsm_off = MLXBF_I2C_RSH_YU_SMBUS_MASTER_FSM
+ }
+};
+
+static const struct i2c_algorithm mlxbf_i2c_algo = {
+ .smbus_xfer = mlxbf_i2c_smbus_xfer,
+ .functionality = mlxbf_i2c_functionality,
+ .reg_slave = mlxbf_i2c_reg_slave,
+ .unreg_slave = mlxbf_i2c_unreg_slave,
+};
+
+static struct i2c_adapter_quirks mlxbf_i2c_quirks = {
+ .max_read_len = MLXBF_I2C_MASTER_DATA_R_LENGTH,
+ .max_write_len = MLXBF_I2C_MASTER_DATA_W_LENGTH,
+};
+
+static const struct acpi_device_id mlxbf_i2c_acpi_ids[] = {
+ { "MLNXBF03", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_1] },
+ { "MLNXBF23", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_2] },
+ { "MLNXBF31", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_3] },
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_i2c_acpi_ids);
+
+static int mlxbf_i2c_acpi_probe(struct device *dev, struct mlxbf_i2c_priv *priv)
+{
+ const struct acpi_device_id *aid;
+ u64 bus_id;
+ int ret;
+
+ if (acpi_disabled)
+ return -ENOENT;
+
+ aid = acpi_match_device(mlxbf_i2c_acpi_ids, dev);
+ if (!aid)
+ return -ENODEV;
+
+ priv->chip = (struct mlxbf_i2c_chip_info *)aid->driver_data;
+
+ ret = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &bus_id);
+ if (ret) {
+ dev_err(dev, "Cannot retrieve UID\n");
+ return ret;
+ }
+
+ priv->bus = bus_id;
+
+ return 0;
+}
+
+static int mlxbf_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mlxbf_i2c_priv *priv;
+ struct i2c_adapter *adap;
+ u32 resource_version;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct mlxbf_i2c_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = mlxbf_i2c_acpi_probe(dev, priv);
+ if (ret < 0)
+ return ret;
+
+ /* This property allows the driver to stay backward compatible with older
+ * ACPI tables.
+ * Starting BlueField-3 SoC, the "smbus" resource was broken down into 3
+ * separate resources "timer", "master" and "slave".
+ */
+ if (device_property_read_u32(dev, "resource_version", &resource_version))
+ resource_version = 0;
+
+ priv->resource_version = resource_version;
+
+ if (priv->chip->type < MLXBF_I2C_CHIP_TYPE_3 && resource_version == 0) {
+ priv->timer = devm_kzalloc(dev, sizeof(struct mlxbf_i2c_resource), GFP_KERNEL);
+ if (!priv->timer)
+ return -ENOMEM;
+
+ priv->mst = devm_kzalloc(dev, sizeof(struct mlxbf_i2c_resource), GFP_KERNEL);
+ if (!priv->mst)
+ return -ENOMEM;
+
+ priv->slv = devm_kzalloc(dev, sizeof(struct mlxbf_i2c_resource), GFP_KERNEL);
+ if (!priv->slv)
+ return -ENOMEM;
+
+ ret = mlxbf_i2c_init_resource(pdev, &priv->smbus,
+ MLXBF_I2C_SMBUS_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch smbus resource info");
+
+ priv->timer->io = priv->smbus->io;
+ priv->mst->io = priv->smbus->io + MLXBF_I2C_MST_ADDR_OFFSET;
+ priv->slv->io = priv->smbus->io + MLXBF_I2C_SLV_ADDR_OFFSET;
+ } else {
+ ret = mlxbf_i2c_init_resource(pdev, &priv->timer,
+ MLXBF_I2C_SMBUS_TIMER_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch timer resource info");
+
+ ret = mlxbf_i2c_init_resource(pdev, &priv->mst,
+ MLXBF_I2C_SMBUS_MST_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch master resource info");
+
+ ret = mlxbf_i2c_init_resource(pdev, &priv->slv,
+ MLXBF_I2C_SMBUS_SLV_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch slave resource info");
+ }
+
+ ret = mlxbf_i2c_init_resource(pdev, &priv->mst_cause,
+ MLXBF_I2C_MST_CAUSE_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch cause master resource info");
+
+ ret = mlxbf_i2c_init_resource(pdev, &priv->slv_cause,
+ MLXBF_I2C_SLV_CAUSE_RES);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot fetch cause slave resource info");
+
+ adap = &priv->adap;
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ adap->algo = &mlxbf_i2c_algo;
+ adap->quirks = &mlxbf_i2c_quirks;
+ adap->dev.parent = dev;
+ adap->dev.of_node = dev->of_node;
+ adap->nr = priv->bus;
+
+ snprintf(adap->name, sizeof(adap->name), "i2c%d", adap->nr);
+ i2c_set_adapdata(adap, priv);
+
+ /* Read Core PLL frequency. */
+ ret = mlxbf_i2c_calculate_corepll_freq(pdev, priv);
+ if (ret < 0) {
+ dev_err(dev, "cannot get core clock frequency\n");
+ /* Set to default value. */
+ priv->frequency = MLXBF_I2C_COREPLL_FREQ;
+ }
+
+ /*
+ * Initialize master.
+ * Note that a physical bus might be shared among Linux and firmware
+ * (e.g., ATF). Thus, the bus should be initialized and ready and
+ * bus initialization would be unnecessary. This requires additional
+ * knowledge about physical busses. But, since an extra initialization
+ * does not really hurt, then keep the code as is.
+ */
+ ret = mlxbf_i2c_init_master(pdev, priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to initialize smbus master %d",
+ priv->bus);
+
+ mlxbf_i2c_init_timings(pdev, priv);
+
+ mlxbf_i2c_init_slave(pdev, priv);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(dev, irq, mlxbf_i2c_irq,
+ IRQF_SHARED | IRQF_PROBE_SHARED,
+ dev_name(dev), priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot get irq %d\n", irq);
+
+ priv->irq = irq;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&mlxbf_i2c_bus_lock);
+ mlxbf_i2c_bus_count++;
+ mutex_unlock(&mlxbf_i2c_bus_lock);
+
+ return 0;
+}
+
+static void mlxbf_i2c_remove(struct platform_device *pdev)
+{
+ struct mlxbf_i2c_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct resource *params;
+
+ if (priv->chip->type < MLXBF_I2C_CHIP_TYPE_3 && priv->resource_version == 0) {
+ params = priv->smbus->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+ } else {
+ params = priv->timer->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+
+ params = priv->mst->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+
+ params = priv->slv->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+ }
+
+ params = priv->mst_cause->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+
+ params = priv->slv_cause->params;
+ devm_release_mem_region(dev, params->start, resource_size(params));
+
+ /*
+ * Release shared resources. This should be done when releasing
+ * the I2C controller.
+ */
+ mutex_lock(&mlxbf_i2c_bus_lock);
+ if (--mlxbf_i2c_bus_count == 0) {
+ mlxbf_i2c_release_coalesce(pdev, priv);
+ mlxbf_i2c_release_corepll(pdev, priv);
+ mlxbf_i2c_release_gpio(pdev, priv);
+ }
+ mutex_unlock(&mlxbf_i2c_bus_lock);
+
+ devm_free_irq(dev, priv->irq, priv);
+
+ i2c_del_adapter(&priv->adap);
+}
+
+static struct platform_driver mlxbf_i2c_driver = {
+ .probe = mlxbf_i2c_probe,
+ .remove = mlxbf_i2c_remove,
+ .driver = {
+ .name = "i2c-mlxbf",
+ .acpi_match_table = ACPI_PTR(mlxbf_i2c_acpi_ids),
+ },
+};
+
+static int __init mlxbf_i2c_init(void)
+{
+ mutex_init(&mlxbf_i2c_coalesce_lock);
+ mutex_init(&mlxbf_i2c_corepll_lock);
+ mutex_init(&mlxbf_i2c_gpio_lock);
+
+ mutex_init(&mlxbf_i2c_bus_lock);
+
+ return platform_driver_register(&mlxbf_i2c_driver);
+}
+module_init(mlxbf_i2c_init);
+
+static void __exit mlxbf_i2c_exit(void)
+{
+ platform_driver_unregister(&mlxbf_i2c_driver);
+
+ mutex_destroy(&mlxbf_i2c_bus_lock);
+
+ mutex_destroy(&mlxbf_i2c_gpio_lock);
+ mutex_destroy(&mlxbf_i2c_corepll_lock);
+ mutex_destroy(&mlxbf_i2c_coalesce_lock);
+}
+module_exit(mlxbf_i2c_exit);
+
+MODULE_DESCRIPTION("Mellanox BlueField I2C bus driver");
+MODULE_AUTHOR("Khalil Blaiech <kblaiech@nvidia.com>");
+MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c
index 745ed43a22d6..07d3cadbf510 100644
--- a/drivers/i2c/busses/i2c-mlxcpld.c
+++ b/drivers/i2c/busses/i2c-mlxcpld.c
@@ -1,34 +1,8 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ * Mellanox i2c driver
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * Copyright (C) 2016-2020 Mellanox Technologies
*/
#include <linux/delay.h>
@@ -37,7 +11,9 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/platform_data/mlxreg.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
/* General defines */
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
@@ -46,12 +22,13 @@
#define MLXCPLD_I2C_BUS_NUM 1
#define MLXCPLD_I2C_DATA_REG_SZ 36
#define MLXCPLD_I2C_DATA_SZ_BIT BIT(5)
+#define MLXCPLD_I2C_DATA_EXT2_SZ_BIT BIT(6)
#define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5)
#define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7)
#define MLXCPLD_I2C_MAX_ADDR_LEN 4
#define MLXCPLD_I2C_RETR_NUM 2
#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
-#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
+#define MLXCPLD_I2C_POLL_TIME 200 /* usec */
/* LPC I2C registers */
#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0
@@ -64,7 +41,7 @@
#define MLXCPLD_LPCI2C_STATUS_REG 0x9
#define MLXCPLD_LPCI2C_DATA_REG 0xa
-/* LPC I2C masks and parametres */
+/* LPC I2C masks and parameters */
#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1
#define MLXCPLD_LPCI2C_TRANS_END 0x1
#define MLXCPLD_LPCI2C_STATUS_NACK 0x10
@@ -72,6 +49,16 @@
#define MLXCPLD_LPCI2C_ACK_IND 1
#define MLXCPLD_LPCI2C_NACK_IND 2
+#define MLXCPLD_I2C_FREQ_1000KHZ_SET 0x04
+#define MLXCPLD_I2C_FREQ_400KHZ_SET 0x0e
+#define MLXCPLD_I2C_FREQ_100KHZ_SET 0x42
+
+enum mlxcpld_i2c_frequency {
+ MLXCPLD_I2C_FREQ_1000KHZ = 1,
+ MLXCPLD_I2C_FREQ_400KHZ = 2,
+ MLXCPLD_I2C_FREQ_100KHZ = 3,
+};
+
struct mlxcpld_i2c_curr_xfer {
u8 cmd;
u8 addr_width;
@@ -87,6 +74,7 @@ struct mlxcpld_i2c_priv {
struct mlxcpld_i2c_curr_xfer xfer;
struct device *dev;
bool smbus_block;
+ int polling_time;
};
static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
@@ -209,8 +197,8 @@ static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status)
if (val & MLXCPLD_LPCI2C_TRANS_END) {
if (val & MLXCPLD_LPCI2C_STATUS_NACK)
/*
- * The slave is unable to accept the data. No such
- * slave, command not understood, or unable to accept
+ * The target is unable to accept the data. No such
+ * target, command not understood, or unable to accept
* any more data.
*/
*status = MLXCPLD_LPCI2C_NACK_IND;
@@ -281,8 +269,8 @@ static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
do {
if (!mlxcpld_i2c_check_busy(priv))
break;
- usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
- timeout += MLXCPLD_I2C_POLL_TIME;
+ usleep_range(priv->polling_time / 2, priv->polling_time);
+ timeout += priv->polling_time;
} while (timeout <= MLXCPLD_I2C_XFER_TO);
if (timeout > MLXCPLD_I2C_XFER_TO)
@@ -292,7 +280,7 @@ static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
}
/*
- * Wait for master transfer to complete.
+ * Wait for transfer to complete.
* It puts current process to sleep until we get interrupt or timeout expires.
* Returns the number of transferred or read bytes or error (<0).
*/
@@ -302,10 +290,10 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
u8 datalen, val;
do {
- usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
+ usleep_range(priv->polling_time / 2, priv->polling_time);
if (!mlxcpld_i2c_check_status(priv, &status))
break;
- timeout += MLXCPLD_I2C_POLL_TIME;
+ timeout += priv->polling_time;
} while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO);
switch (status) {
@@ -327,7 +315,7 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
/*
* Actual read data len will be always the same as
* requested len. 0xff (line pull-up) will be returned
- * if slave has no data to return. Thus don't read
+ * if target has no data to return. Thus don't read
* MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. Only in case of
* SMBus block read transaction data len can be different,
* check this case.
@@ -337,9 +325,9 @@ static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
if (priv->smbus_block && (val & MLXCPLD_I2C_SMBUS_BLK_BIT)) {
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
&datalen, 1);
- if (unlikely(datalen > (I2C_SMBUS_BLOCK_MAX + 1))) {
+ if (unlikely(datalen > I2C_SMBUS_BLOCK_MAX)) {
dev_err(priv->dev, "Incorrect smbus block read message len\n");
- return -E2BIG;
+ return -EPROTO;
}
} else {
datalen = priv->xfer.data_len;
@@ -387,7 +375,7 @@ static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv)
}
/*
- * Set target slave address with command for master transfer.
+ * Set target address with command for transfer.
* It should be latest executed function before CPLD transaction.
*/
cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd;
@@ -461,8 +449,8 @@ static u32 mlxcpld_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm mlxcpld_i2c_algo = {
- .master_xfer = mlxcpld_i2c_xfer,
- .functionality = mlxcpld_i2c_func
+ .xfer = mlxcpld_i2c_xfer,
+ .functionality = mlxcpld_i2c_func
};
static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = {
@@ -479,18 +467,64 @@ static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = {
.max_comb_1st_msg_len = 4,
};
+static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext2 = {
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .max_read_len = (MLXCPLD_I2C_DATA_REG_SZ - 4) * 4,
+ .max_write_len = (MLXCPLD_I2C_DATA_REG_SZ - 4) * 4 + MLXCPLD_I2C_MAX_ADDR_LEN,
+ .max_comb_1st_msg_len = 4,
+};
+
static struct i2c_adapter mlxcpld_i2c_adapter = {
.owner = THIS_MODULE,
.name = "i2c-mlxcpld",
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &mlxcpld_i2c_algo,
.quirks = &mlxcpld_i2c_quirks,
.retries = MLXCPLD_I2C_RETR_NUM,
.nr = MLXCPLD_I2C_BUS_NUM,
};
+static int
+mlxcpld_i2c_set_frequency(struct mlxcpld_i2c_priv *priv,
+ struct mlxreg_core_hotplug_platform_data *pdata)
+{
+ struct mlxreg_core_item *item = pdata->items;
+ struct mlxreg_core_data *data;
+ u32 regval;
+ u8 freq;
+ int err;
+
+ if (!item)
+ return 0;
+
+ /* Read frequency setting. */
+ data = item->data;
+ err = regmap_read(pdata->regmap, data->reg, &regval);
+ if (err)
+ return err;
+
+ /* Set frequency only if it is not 100KHz, which is default. */
+ switch ((regval & data->mask) >> data->bit) {
+ case MLXCPLD_I2C_FREQ_1000KHZ:
+ freq = MLXCPLD_I2C_FREQ_1000KHZ_SET;
+ priv->polling_time /= 4;
+ break;
+ case MLXCPLD_I2C_FREQ_400KHZ:
+ freq = MLXCPLD_I2C_FREQ_400KHZ_SET;
+ priv->polling_time /= 4;
+ break;
+ default:
+ return 0;
+ }
+
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_HALF_CYC_REG, &freq, 1);
+
+ return 0;
+}
+
static int mlxcpld_i2c_probe(struct platform_device *pdev)
{
+ struct mlxreg_core_hotplug_platform_data *pdata;
struct mlxcpld_i2c_priv *priv;
int err;
u8 val;
@@ -503,6 +537,16 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
priv->dev = &pdev->dev;
+ priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
+ priv->polling_time = MLXCPLD_I2C_POLL_TIME;
+
+ /* Set I2C bus frequency if platform data provides this info. */
+ pdata = dev_get_platdata(&pdev->dev);
+ if (pdata) {
+ err = mlxcpld_i2c_set_frequency(priv, pdata);
+ if (err)
+ goto mlxcpld_i2_probe_failed;
+ }
/* Register with i2c layer */
mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
@@ -511,6 +555,8 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
/* Check support for extended transaction length */
if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_SZ_BIT)
mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext;
+ else if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_EXT2_SZ_BIT)
+ mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext2;
/* Check support for smbus block transaction */
if (val & MLXCPLD_I2C_SMBUS_BLK_BIT)
priv->smbus_block = true;
@@ -518,24 +564,29 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
mlxcpld_i2c_adapter.nr = pdev->id;
priv->adap = mlxcpld_i2c_adapter;
priv->adap.dev.parent = &pdev->dev;
- priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
i2c_set_adapdata(&priv->adap, priv);
err = i2c_add_numbered_adapter(&priv->adap);
if (err)
- mutex_destroy(&priv->lock);
+ goto mlxcpld_i2_probe_failed;
+ /* Notify caller when adapter is added. */
+ if (pdata && pdata->completion_notify)
+ pdata->completion_notify(pdata->handle, mlxcpld_i2c_adapter.nr);
+
+ return 0;
+
+mlxcpld_i2_probe_failed:
+ mutex_destroy(&priv->lock);
return err;
}
-static int mlxcpld_i2c_remove(struct platform_device *pdev)
+static void mlxcpld_i2c_remove(struct platform_device *pdev)
{
struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
mutex_destroy(&priv->lock);
-
- return 0;
}
static struct platform_driver mlxcpld_i2c_driver = {
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index d94f05c8b8b7..28c5c5c1fb7a 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -1,28 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * (C) Copyright 2003-2004
- * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
-
* This is a combined i2c adapter and algorithm driver for the
* MPC107/Tsi107 PowerPC northbridge and processors that include
* the same I2C unit (8240, 8245, 85xx).
*
- * Release 0.8
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
+ * Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk
+ * Copyright (C) 2021 Allied Telesis Labs
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/fsl_devices.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -32,8 +30,6 @@
#include <asm/mpc85xx.h>
#include <sysdev/fsl_soc.h>
-#define DRV_NAME "mpc-i2c"
-
#define MPC_I2C_CLOCK_LEGACY 0
#define MPC_I2C_CLOCK_PRESERVE (~0U)
@@ -49,6 +45,7 @@
#define CCR_MTX 0x10
#define CCR_TXAK 0x08
#define CCR_RSTA 0x04
+#define CCR_RSVD 0x02
#define CSR_MCF 0x80
#define CSR_MAAS 0x40
@@ -58,18 +55,49 @@
#define CSR_MIF 0x02
#define CSR_RXAK 0x01
+enum mpc_i2c_action {
+ MPC_I2C_ACTION_START = 1,
+ MPC_I2C_ACTION_RESTART,
+ MPC_I2C_ACTION_READ_BEGIN,
+ MPC_I2C_ACTION_READ_BYTE,
+ MPC_I2C_ACTION_WRITE,
+ MPC_I2C_ACTION_STOP,
+
+ __MPC_I2C_ACTION_CNT
+};
+
+static const char * const action_str[] = {
+ "invalid",
+ "start",
+ "restart",
+ "read begin",
+ "read",
+ "write",
+ "stop",
+};
+
+static_assert(ARRAY_SIZE(action_str) == __MPC_I2C_ACTION_CNT);
+
struct mpc_i2c {
struct device *dev;
void __iomem *base;
u32 interrupt;
- wait_queue_head_t queue;
+ wait_queue_head_t waitq;
+ spinlock_t lock;
struct i2c_adapter adap;
int irq;
u32 real_clk;
-#ifdef CONFIG_PM_SLEEP
u8 fdr, dfsrr;
-#endif
- struct clk *clk_per;
+ u32 cntl_bits;
+ enum mpc_i2c_action action;
+ struct i2c_msg *msgs;
+ int num_msgs;
+ int curr_msg;
+ u32 byte_posn;
+ u32 block;
+ int rc;
+ int expect_rxack;
+ bool has_errata_A004447;
};
struct mpc_i2c_divider {
@@ -86,94 +114,102 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x)
writeb(x, i2c->base + MPC_I2C_CR);
}
-static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
-{
- struct mpc_i2c *i2c = dev_id;
- if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
- /* Read again to allow register to stabilise */
- i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
- writeb(0, i2c->base + MPC_I2C_SR);
- wake_up(&i2c->queue);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
+/* Sometimes 9th clock pulse isn't generated, and target doesn't release
* the bus, because it wants to send ACK.
* Following sequence of enabling/disabling and sending start/stop generates
- * the 9 pulses, so it's all OK.
+ * the 9 pulses, each with a START then ending with STOP, so it's all OK.
*/
static void mpc_i2c_fixup(struct mpc_i2c *i2c)
{
int k;
- u32 delay_val = 1000000 / i2c->real_clk + 1;
-
- if (delay_val < 2)
- delay_val = 2;
+ unsigned long flags;
for (k = 9; k; k--) {
writeccr(i2c, 0);
- writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
+ writeb(0, i2c->base + MPC_I2C_SR); /* clear any status bits */
+ writeccr(i2c, CCR_MEN | CCR_MSTA); /* START */
+ readb(i2c->base + MPC_I2C_DR); /* init xfer */
+ udelay(15); /* let it hit the bus */
+ local_irq_save(flags); /* should not be delayed further */
+ writeccr(i2c, CCR_MEN | CCR_MSTA | CCR_RSTA); /* delay SDA */
readb(i2c->base + MPC_I2C_DR);
- writeccr(i2c, CCR_MEN);
- udelay(delay_val << 1);
+ if (k != 1)
+ udelay(5);
+ local_irq_restore(flags);
}
+ writeccr(i2c, CCR_MEN); /* Initiate STOP */
+ readb(i2c->base + MPC_I2C_DR);
+ udelay(15); /* Let STOP propagate */
+ writeccr(i2c, 0);
}
-static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
+static int i2c_mpc_wait_sr(struct mpc_i2c *i2c, int mask)
{
- unsigned long orig_jiffies = jiffies;
- u32 cmd_err;
- int result = 0;
-
- if (!i2c->irq) {
- while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
- schedule();
- if (time_after(jiffies, orig_jiffies + timeout)) {
- dev_dbg(i2c->dev, "timeout\n");
- writeccr(i2c, 0);
- result = -ETIMEDOUT;
- break;
- }
- }
- cmd_err = readb(i2c->base + MPC_I2C_SR);
- writeb(0, i2c->base + MPC_I2C_SR);
- } else {
- /* Interrupt mode */
- result = wait_event_timeout(i2c->queue,
- (i2c->interrupt & CSR_MIF), timeout);
-
- if (unlikely(!(i2c->interrupt & CSR_MIF))) {
- dev_dbg(i2c->dev, "wait timeout\n");
- writeccr(i2c, 0);
- result = -ETIMEDOUT;
- }
+ void __iomem *addr = i2c->base + MPC_I2C_SR;
+ u8 val;
- cmd_err = i2c->interrupt;
- i2c->interrupt = 0;
- }
+ return readb_poll_timeout(addr, val, val & mask, 0, 100);
+}
- if (result < 0)
- return result;
+/*
+ * Workaround for Erratum A004447. From the P2040CE Rev Q
+ *
+ * 1. Set up the frequency divider and sampling rate.
+ * 2. I2CCR - a0h
+ * 3. Poll for I2CSR[MBB] to get set.
+ * 4. If I2CSR[MAL] is set (an indication that SDA is stuck low), then go to
+ * step 5. If MAL is not set, then go to step 13.
+ * 5. I2CCR - 00h
+ * 6. I2CCR - 22h
+ * 7. I2CCR - a2h
+ * 8. Poll for I2CSR[MBB] to get set.
+ * 9. Issue read to I2CDR.
+ * 10. Poll for I2CSR[MIF] to be set.
+ * 11. I2CCR - 82h
+ * 12. Workaround complete. Skip the next steps.
+ * 13. Issue read to I2CDR.
+ * 14. Poll for I2CSR[MIF] to be set.
+ * 15. I2CCR - 80h
+ */
+static void mpc_i2c_fixup_A004447(struct mpc_i2c *i2c)
+{
+ int ret;
+ u32 val;
- if (!(cmd_err & CSR_MCF)) {
- dev_dbg(i2c->dev, "unfinished\n");
- return -EIO;
+ writeccr(i2c, CCR_MEN | CCR_MSTA);
+ ret = i2c_mpc_wait_sr(i2c, CSR_MBB);
+ if (ret) {
+ dev_err(i2c->dev, "timeout waiting for CSR_MBB\n");
+ return;
}
- if (cmd_err & CSR_MAL) {
- dev_dbg(i2c->dev, "MAL\n");
- return -EAGAIN;
- }
+ val = readb(i2c->base + MPC_I2C_SR);
- if (writing && (cmd_err & CSR_RXAK)) {
- dev_dbg(i2c->dev, "No RXAK\n");
- /* generate stop */
+ if (val & CSR_MAL) {
+ writeccr(i2c, 0x00);
+ writeccr(i2c, CCR_MSTA | CCR_RSVD);
+ writeccr(i2c, CCR_MEN | CCR_MSTA | CCR_RSVD);
+ ret = i2c_mpc_wait_sr(i2c, CSR_MBB);
+ if (ret) {
+ dev_err(i2c->dev, "timeout waiting for CSR_MBB\n");
+ return;
+ }
+ val = readb(i2c->base + MPC_I2C_DR);
+ ret = i2c_mpc_wait_sr(i2c, CSR_MIF);
+ if (ret) {
+ dev_err(i2c->dev, "timeout waiting for CSR_MIF\n");
+ return;
+ }
+ writeccr(i2c, CCR_MEN | CCR_RSVD);
+ } else {
+ val = readb(i2c->base + MPC_I2C_DR);
+ ret = i2c_mpc_wait_sr(i2c, CSR_MIF);
+ if (ret) {
+ dev_err(i2c->dev, "timeout waiting for CSR_MIF\n");
+ return;
+ }
writeccr(i2c, CCR_MEN);
- return -ENXIO;
}
- return 0;
}
#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
@@ -201,6 +237,7 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
static int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
u32 *real_clk)
{
+ struct fwnode_handle *fwnode = of_fwnode_handle(node);
const struct mpc_i2c_divider *div = NULL;
unsigned int pvr = mfspr(SPRN_PVR);
u32 divider;
@@ -208,12 +245,12 @@ static int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
if (clock == MPC_I2C_CLOCK_LEGACY) {
/* see below - default fdr = 0x3f -> div = 2048 */
- *real_clk = mpc5xxx_get_bus_frequency(node) / 2048;
+ *real_clk = mpc5xxx_fwnode_get_bus_frequency(fwnode) / 2048;
return -EINVAL;
}
/* Determine divider value */
- divider = mpc5xxx_get_bus_frequency(node) / clock;
+ divider = mpc5xxx_fwnode_get_bus_frequency(fwnode) / clock;
/*
* We want to choose an FDR/DFSR that generates an I2C bus speed that
@@ -228,7 +265,7 @@ static int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
break;
}
- *real_clk = mpc5xxx_get_bus_frequency(node) / div->divider;
+ *real_clk = mpc5xxx_fwnode_get_bus_frequency(fwnode) / div->divider;
return (int)div->fdr;
}
@@ -266,24 +303,22 @@ static void mpc_i2c_setup_512x(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock)
{
- struct device_node *node_ctrl;
void __iomem *ctrl;
- const u32 *pval;
u32 idx;
/* Enable I2C interrupts for mpc5121 */
- node_ctrl = of_find_compatible_node(NULL, NULL,
- "fsl,mpc5121-i2c-ctrl");
+ struct device_node *node_ctrl __free(device_node) =
+ of_find_compatible_node(NULL, NULL, "fsl,mpc5121-i2c-ctrl");
if (node_ctrl) {
ctrl = of_iomap(node_ctrl, 0);
if (ctrl) {
+ u64 addr;
/* Interrupt enable bits for i2c-0/1/2: bit 24/26/28 */
- pval = of_get_property(node, "reg", NULL);
- idx = (*pval & 0xff) / 0x20;
+ of_property_read_reg(node, 0, &addr, NULL);
+ idx = (addr & 0xff) / 0x20;
setbits32(ctrl, 1 << (24 + idx * 2));
iounmap(ctrl);
}
- of_node_put(node_ctrl);
}
/* The clock setup for the 52xx works also fine for the 512x */
@@ -320,11 +355,11 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
static u32 mpc_i2c_get_sec_cfg_8xxx(void)
{
- struct device_node *node;
u32 __iomem *reg;
u32 val = 0;
- node = of_find_node_by_name(NULL, "global-utilities");
+ struct device_node *node __free(device_node) =
+ of_find_node_by_name(NULL, "global-utilities");
if (node) {
const u32 *prop = of_get_property(node, "reg", NULL);
if (prop) {
@@ -345,7 +380,6 @@ static u32 mpc_i2c_get_sec_cfg_8xxx(void)
iounmap(reg);
}
}
- of_node_put(node);
return val;
}
@@ -415,7 +449,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
}
*real_clk = fsl_get_sys_freq() / prescaler / div->divider;
- return div ? (int)div->fdr : -EINVAL;
+ return (int)div->fdr;
}
static void mpc_i2c_setup_8xxx(struct device_node *node,
@@ -450,168 +484,214 @@ static void mpc_i2c_setup_8xxx(struct device_node *node,
}
#endif /* CONFIG_FSL_SOC */
-static void mpc_i2c_start(struct mpc_i2c *i2c)
+static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc)
{
- /* Clear arbitration */
- writeb(0, i2c->base + MPC_I2C_SR);
- /* Start with MEN */
- writeccr(i2c, CCR_MEN);
+ i2c->rc = rc;
+ i2c->block = 0;
+ i2c->cntl_bits = CCR_MEN;
+ writeccr(i2c, i2c->cntl_bits);
+ wake_up(&i2c->waitq);
}
-static void mpc_i2c_stop(struct mpc_i2c *i2c)
+static void mpc_i2c_do_action(struct mpc_i2c *i2c)
{
- writeccr(i2c, CCR_MEN);
-}
+ struct i2c_msg *msg = NULL;
+ int dir = 0;
+ int recv_len = 0;
+ u8 byte;
-static int mpc_write(struct mpc_i2c *i2c, int target,
- const u8 *data, int length, int restart)
-{
- int i, result;
- unsigned timeout = i2c->adap.timeout;
- u32 flags = restart ? CCR_RSTA : 0;
+ dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]);
- /* Start as master */
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
- /* Write target byte */
- writeb((target << 1), i2c->base + MPC_I2C_DR);
+ i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK);
- result = i2c_wait(i2c, timeout, 1);
- if (result < 0)
- return result;
+ if (i2c->action != MPC_I2C_ACTION_STOP) {
+ msg = &i2c->msgs[i2c->curr_msg];
+ if (msg->flags & I2C_M_RD)
+ dir = 1;
+ if (msg->flags & I2C_M_RECV_LEN)
+ recv_len = 1;
+ }
- for (i = 0; i < length; i++) {
- /* Write data byte */
- writeb(data[i], i2c->base + MPC_I2C_DR);
+ switch (i2c->action) {
+ case MPC_I2C_ACTION_RESTART:
+ i2c->cntl_bits |= CCR_RSTA;
+ fallthrough;
+
+ case MPC_I2C_ACTION_START:
+ i2c->cntl_bits |= CCR_MSTA | CCR_MTX;
+ writeccr(i2c, i2c->cntl_bits);
+ writeb((msg->addr << 1) | dir, i2c->base + MPC_I2C_DR);
+ i2c->expect_rxack = 1;
+ i2c->action = dir ? MPC_I2C_ACTION_READ_BEGIN : MPC_I2C_ACTION_WRITE;
+ break;
+
+ case MPC_I2C_ACTION_READ_BEGIN:
+ if (msg->len) {
+ if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
+ i2c->cntl_bits |= CCR_TXAK;
+
+ writeccr(i2c, i2c->cntl_bits);
+ /* Dummy read */
+ readb(i2c->base + MPC_I2C_DR);
+ }
+ i2c->action = MPC_I2C_ACTION_READ_BYTE;
+ break;
+
+ case MPC_I2C_ACTION_READ_BYTE:
+ if (i2c->byte_posn || !recv_len) {
+ /* Generate Tx ACK on next to last byte */
+ if (i2c->byte_posn == msg->len - 2)
+ i2c->cntl_bits |= CCR_TXAK;
+ /* Do not generate stop on last byte */
+ if (i2c->byte_posn == msg->len - 1)
+ i2c->cntl_bits |= CCR_MTX;
- result = i2c_wait(i2c, timeout, 1);
- if (result < 0)
- return result;
- }
+ writeccr(i2c, i2c->cntl_bits);
+ }
- return 0;
-}
+ byte = readb(i2c->base + MPC_I2C_DR);
-static int mpc_read(struct mpc_i2c *i2c, int target,
- u8 *data, int length, int restart, bool recv_len)
-{
- unsigned timeout = i2c->adap.timeout;
- int i, result;
- u32 flags = restart ? CCR_RSTA : 0;
+ if (i2c->byte_posn == 0 && recv_len) {
+ if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) {
+ mpc_i2c_finish(i2c, -EPROTO);
+ return;
+ }
+ msg->len += byte;
+ /*
+ * For block reads, generate Tx ACK here if data length
+ * is 1 byte (total length is 2 bytes).
+ */
+ if (msg->len == 2) {
+ i2c->cntl_bits |= CCR_TXAK;
+ writeccr(i2c, i2c->cntl_bits);
+ }
+ }
- /* Switch to read - restart */
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
- /* Write target address byte - this time with the read flag set */
- writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
+ dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte);
+ msg->buf[i2c->byte_posn++] = byte;
+ break;
- result = i2c_wait(i2c, timeout, 1);
- if (result < 0)
- return result;
+ case MPC_I2C_ACTION_WRITE:
+ dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action],
+ msg->buf[i2c->byte_posn]);
+ writeb(msg->buf[i2c->byte_posn++], i2c->base + MPC_I2C_DR);
+ i2c->expect_rxack = 1;
+ break;
- if (length) {
- if (length == 1 && !recv_len)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
- else
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
- /* Dummy read */
- readb(i2c->base + MPC_I2C_DR);
- }
+ case MPC_I2C_ACTION_STOP:
+ mpc_i2c_finish(i2c, 0);
+ break;
- for (i = 0; i < length; i++) {
- u8 byte;
-
- result = i2c_wait(i2c, timeout, 0);
- if (result < 0)
- return result;
-
- /*
- * For block reads, we have to know the total length (1st byte)
- * before we can determine if we are done.
- */
- if (i || !recv_len) {
- /* Generate txack on next to last byte */
- if (i == length - 2)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
- | CCR_TXAK);
- /* Do not generate stop on last byte */
- if (i == length - 1)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
- | CCR_MTX);
- }
+ default:
+ WARN(1, "Unexpected action %d\n", i2c->action);
+ break;
+ }
- byte = readb(i2c->base + MPC_I2C_DR);
+ if (msg && msg->len == i2c->byte_posn) {
+ i2c->curr_msg++;
+ i2c->byte_posn = 0;
- /*
- * Adjust length if first received byte is length.
- * The length is 1 length byte plus actually data length
- */
- if (i == 0 && recv_len) {
- if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
- return -EPROTO;
- length += byte;
+ if (i2c->curr_msg == i2c->num_msgs) {
+ i2c->action = MPC_I2C_ACTION_STOP;
/*
- * For block reads, generate txack here if data length
- * is 1 byte (total length is 2 bytes).
+ * We don't get another interrupt on read so
+ * finish the transfer now
*/
- if (length == 2)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
- | CCR_TXAK);
+ if (dir)
+ mpc_i2c_finish(i2c, 0);
+ } else {
+ i2c->action = MPC_I2C_ACTION_RESTART;
}
- data[i] = byte;
}
-
- return length;
}
-static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status)
{
- struct i2c_msg *pmsg;
- int i;
- int ret = 0;
- unsigned long orig_jiffies = jiffies;
- struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+ spin_lock(&i2c->lock);
- mpc_i2c_start(i2c);
+ if (!(status & CSR_MCF)) {
+ dev_dbg(i2c->dev, "unfinished\n");
+ mpc_i2c_finish(i2c, -EIO);
+ goto out;
+ }
- /* Allow bus up to 1s to become not busy */
- while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
- if (signal_pending(current)) {
- dev_dbg(i2c->dev, "Interrupted\n");
- writeccr(i2c, 0);
- return -EINTR;
- }
- if (time_after(jiffies, orig_jiffies + HZ)) {
- u8 status = readb(i2c->base + MPC_I2C_SR);
+ if (status & CSR_MAL) {
+ dev_dbg(i2c->dev, "arbitration lost\n");
+ mpc_i2c_finish(i2c, -EAGAIN);
+ goto out;
+ }
- dev_dbg(i2c->dev, "timeout\n");
- if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
- writeb(status & ~CSR_MAL,
- i2c->base + MPC_I2C_SR);
- mpc_i2c_fixup(i2c);
- }
- return -EIO;
- }
- schedule();
+ if (i2c->expect_rxack && (status & CSR_RXAK)) {
+ dev_dbg(i2c->dev, "no Rx ACK\n");
+ mpc_i2c_finish(i2c, -ENXIO);
+ goto out;
}
+ i2c->expect_rxack = 0;
- for (i = 0; ret >= 0 && i < num; i++) {
- pmsg = &msgs[i];
- dev_dbg(i2c->dev,
- "Doing %s %d bytes to 0x%02x - %d of %d messages\n",
- pmsg->flags & I2C_M_RD ? "read" : "write",
- pmsg->len, pmsg->addr, i + 1, num);
- if (pmsg->flags & I2C_M_RD) {
- bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
-
- ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
- recv_len);
- if (recv_len && ret > 0)
- pmsg->len = ret;
- } else {
- ret =
- mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
- }
+ mpc_i2c_do_action(i2c);
+
+out:
+ spin_unlock(&i2c->lock);
+}
+
+static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
+{
+ struct mpc_i2c *i2c = dev_id;
+ u8 status;
+
+ status = readb(i2c->base + MPC_I2C_SR);
+ if (status & CSR_MIF) {
+ /* Wait up to 100us for transfer to properly complete */
+ readb_poll_timeout_atomic(i2c->base + MPC_I2C_SR, status, status & CSR_MCF, 0, 100);
+ writeb(0, i2c->base + MPC_I2C_SR);
+ mpc_i2c_do_intr(i2c, status);
+ return IRQ_HANDLED;
}
- mpc_i2c_stop(i2c); /* Initiate STOP */
+ return IRQ_NONE;
+}
+
+static int mpc_i2c_wait_for_completion(struct mpc_i2c *i2c)
+{
+ long time_left;
+
+ time_left = wait_event_timeout(i2c->waitq, !i2c->block, i2c->adap.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+ if (time_left < 0)
+ return time_left;
+
+ return 0;
+}
+
+static int mpc_i2c_execute_msg(struct mpc_i2c *i2c)
+{
+ unsigned long orig_jiffies;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+
+ i2c->curr_msg = 0;
+ i2c->rc = 0;
+ i2c->byte_posn = 0;
+ i2c->block = 1;
+ i2c->action = MPC_I2C_ACTION_START;
+
+ i2c->cntl_bits = CCR_MEN | CCR_MIEN;
+ writeb(0, i2c->base + MPC_I2C_SR);
+ writeccr(i2c, i2c->cntl_bits);
+
+ mpc_i2c_do_action(i2c);
+
+ spin_unlock_irqrestore(&i2c->lock, flags);
+
+ ret = mpc_i2c_wait_for_completion(i2c);
+ if (ret)
+ i2c->rc = ret;
+
+ if (i2c->rc == -EIO || i2c->rc == -EAGAIN || i2c->rc == -ETIMEDOUT)
+ i2c_recover_bus(&i2c->adap);
+
orig_jiffies = jiffies;
/* Wait until STOP is seen, allow up to 1 s */
while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
@@ -622,13 +702,41 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
writeb(status & ~CSR_MAL,
i2c->base + MPC_I2C_SR);
- mpc_i2c_fixup(i2c);
+ i2c_recover_bus(&i2c->adap);
}
return -EIO;
}
cond_resched();
}
- return (ret < 0) ? ret : num;
+
+ return i2c->rc;
+}
+
+static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ int rc, ret = num;
+ struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+ int i;
+
+ dev_dbg(i2c->dev, "num = %d\n", num);
+ for (i = 0; i < num; i++)
+ dev_dbg(i2c->dev, " addr = %02x, flags = %02x, len = %d, %*ph\n",
+ msgs[i].addr, msgs[i].flags, msgs[i].len,
+ msgs[i].flags & I2C_M_RD ? 0 : msgs[i].len,
+ msgs[i].buf);
+
+ WARN_ON(i2c->msgs != NULL);
+ i2c->msgs = msgs;
+ i2c->num_msgs = num;
+
+ rc = mpc_i2c_execute_msg(i2c);
+ if (rc < 0)
+ ret = rc;
+
+ i2c->num_msgs = 0;
+ i2c->msgs = NULL;
+
+ return ret;
}
static u32 mpc_functionality(struct i2c_adapter *adap)
@@ -637,148 +745,142 @@ static u32 mpc_functionality(struct i2c_adapter *adap)
| I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
}
+static int fsl_i2c_bus_recovery(struct i2c_adapter *adap)
+{
+ struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+
+ if (i2c->has_errata_A004447)
+ mpc_i2c_fixup_A004447(i2c);
+ else
+ mpc_i2c_fixup(i2c);
+
+ return 0;
+}
+
static const struct i2c_algorithm mpc_algo = {
- .master_xfer = mpc_xfer,
+ .xfer = mpc_xfer,
.functionality = mpc_functionality,
};
static struct i2c_adapter mpc_ops = {
.owner = THIS_MODULE,
.algo = &mpc_algo,
- .timeout = HZ,
};
-static const struct of_device_id mpc_i2c_of_match[];
+static struct i2c_bus_recovery_info fsl_i2c_recovery_info = {
+ .recover_bus = fsl_i2c_bus_recovery,
+};
+
static int fsl_i2c_probe(struct platform_device *op)
{
- const struct of_device_id *match;
+ const struct mpc_i2c_data *data;
struct mpc_i2c *i2c;
- const u32 *prop;
- u32 clock = MPC_I2C_CLOCK_LEGACY;
- int result = 0;
- int plen;
- struct resource res;
struct clk *clk;
- int err;
-
- match = of_match_device(mpc_i2c_of_match, &op->dev);
- if (!match)
- return -EINVAL;
+ int result;
+ u32 clock;
- i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->dev = &op->dev; /* for debug and error output */
- init_waitqueue_head(&i2c->queue);
+ init_waitqueue_head(&i2c->waitq);
+ spin_lock_init(&i2c->lock);
- i2c->base = of_iomap(op->dev.of_node, 0);
- if (!i2c->base) {
- dev_err(i2c->dev, "failed to map controller\n");
- result = -ENOMEM;
- goto fail_map;
- }
+ i2c->base = devm_platform_ioremap_resource(op, 0);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
- i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0);
- if (i2c->irq) { /* no i2c->irq implies polling */
- result = request_irq(i2c->irq, mpc_i2c_isr,
- IRQF_SHARED, "i2c-mpc", i2c);
- if (result < 0) {
- dev_err(i2c->dev, "failed to attach interrupt\n");
- goto fail_request;
- }
+ i2c->irq = platform_get_irq(op, 0);
+ if (i2c->irq < 0)
+ return i2c->irq;
+
+ result = devm_request_irq(&op->dev, i2c->irq, mpc_i2c_isr,
+ IRQF_SHARED, "i2c-mpc", i2c);
+ if (result < 0) {
+ dev_err(i2c->dev, "failed to attach interrupt\n");
+ return result;
}
/*
* enable clock for the I2C peripheral (non fatal),
* keep a reference upon successful allocation
*/
- clk = devm_clk_get(&op->dev, NULL);
- if (!IS_ERR(clk)) {
- err = clk_prepare_enable(clk);
- if (err) {
- dev_err(&op->dev, "failed to enable clock\n");
- goto fail_request;
- } else {
- i2c->clk_per = clk;
- }
+ clk = devm_clk_get_optional_enabled(&op->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&op->dev, "failed to enable clock\n");
+ return PTR_ERR(clk);
}
if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) {
clock = MPC_I2C_CLOCK_PRESERVE;
} else {
- prop = of_get_property(op->dev.of_node, "clock-frequency",
- &plen);
- if (prop && plen == sizeof(u32))
- clock = *prop;
+ result = of_property_read_u32(op->dev.of_node,
+ "clock-frequency", &clock);
+ if (result)
+ clock = MPC_I2C_CLOCK_LEGACY;
}
- if (match->data) {
- const struct mpc_i2c_data *data = match->data;
+ data = device_get_match_data(&op->dev);
+ if (data) {
data->setup(op->dev.of_node, i2c, clock);
} else {
/* Backwards compatibility */
- if (of_get_property(op->dev.of_node, "dfsrr", NULL))
+ if (of_property_read_bool(op->dev.of_node, "dfsrr"))
mpc_i2c_setup_8xxx(op->dev.of_node, i2c, clock);
}
- prop = of_get_property(op->dev.of_node, "fsl,timeout", &plen);
- if (prop && plen == sizeof(u32)) {
- mpc_ops.timeout = *prop * HZ / 1000000;
+ /* Sadly, we have to support two deprecated bindings here */
+ result = of_property_read_u32(op->dev.of_node,
+ "i2c-transfer-timeout-us",
+ &mpc_ops.timeout);
+ if (result == -EINVAL)
+ result = of_property_read_u32(op->dev.of_node,
+ "i2c-scl-clk-low-timeout-us",
+ &mpc_ops.timeout);
+ if (result == -EINVAL)
+ result = of_property_read_u32(op->dev.of_node,
+ "fsl,timeout", &mpc_ops.timeout);
+
+ if (!result) {
+ mpc_ops.timeout *= HZ / 1000000;
if (mpc_ops.timeout < 5)
mpc_ops.timeout = 5;
+ } else {
+ mpc_ops.timeout = HZ;
}
+
dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
- platform_set_drvdata(op, i2c);
+ if (of_property_read_bool(op->dev.of_node, "fsl,i2c-erratum-a004447"))
+ i2c->has_errata_A004447 = true;
i2c->adap = mpc_ops;
- of_address_to_resource(op->dev.of_node, 0, &res);
scnprintf(i2c->adap.name, sizeof(i2c->adap.name),
- "MPC adapter at 0x%llx", (unsigned long long)res.start);
- i2c_set_adapdata(&i2c->adap, i2c);
+ "MPC adapter (%s)", of_node_full_name(op->dev.of_node));
i2c->adap.dev.parent = &op->dev;
+ i2c->adap.nr = op->id;
i2c->adap.dev.of_node = of_node_get(op->dev.of_node);
+ i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info;
+ platform_set_drvdata(op, i2c);
+ i2c_set_adapdata(&i2c->adap, i2c);
- result = i2c_add_adapter(&i2c->adap);
- if (result < 0)
- goto fail_add;
-
- return result;
-
- fail_add:
- if (i2c->clk_per)
- clk_disable_unprepare(i2c->clk_per);
- free_irq(i2c->irq, i2c);
- fail_request:
- irq_dispose_mapping(i2c->irq);
- iounmap(i2c->base);
- fail_map:
- kfree(i2c);
- return result;
+ result = i2c_add_numbered_adapter(&i2c->adap);
+ if (result)
+ return result;
+
+ return 0;
};
-static int fsl_i2c_remove(struct platform_device *op)
+static void fsl_i2c_remove(struct platform_device *op)
{
struct mpc_i2c *i2c = platform_get_drvdata(op);
i2c_del_adapter(&i2c->adap);
-
- if (i2c->clk_per)
- clk_disable_unprepare(i2c->clk_per);
-
- if (i2c->irq)
- free_irq(i2c->irq, i2c);
-
- irq_dispose_mapping(i2c->irq);
- iounmap(i2c->base);
- kfree(i2c);
- return 0;
};
-#ifdef CONFIG_PM_SLEEP
-static int mpc_i2c_suspend(struct device *dev)
+static int __maybe_unused mpc_i2c_suspend(struct device *dev)
{
struct mpc_i2c *i2c = dev_get_drvdata(dev);
@@ -788,7 +890,7 @@ static int mpc_i2c_suspend(struct device *dev)
return 0;
}
-static int mpc_i2c_resume(struct device *dev)
+static int __maybe_unused mpc_i2c_resume(struct device *dev)
{
struct mpc_i2c *i2c = dev_get_drvdata(dev);
@@ -797,12 +899,7 @@ static int mpc_i2c_resume(struct device *dev)
return 0;
}
-
static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume);
-#define MPC_I2C_PM_OPS (&mpc_i2c_pm_ops)
-#else
-#define MPC_I2C_PM_OPS NULL
-#endif
static const struct mpc_i2c_data mpc_i2c_data_512x = {
.setup = mpc_i2c_setup_512x,
@@ -843,9 +940,9 @@ static struct platform_driver mpc_i2c_driver = {
.probe = fsl_i2c_probe,
.remove = fsl_i2c_remove,
.driver = {
- .name = DRV_NAME,
+ .name = "mpc-i2c",
.of_match_table = mpc_i2c_of_match,
- .pm = MPC_I2C_PM_OPS,
+ .pm = &mpc_i2c_pm_ops,
},
};
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index a74ef76705e0..aefdbee1f03c 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Xudong Chen <xudong.chen@mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/clk.h>
@@ -23,18 +15,18 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#define I2C_RS_TRANSFER (1 << 4)
+#define I2C_ARB_LOST (1 << 3)
#define I2C_HS_NACKERR (1 << 2)
#define I2C_ACKERR (1 << 1)
#define I2C_TRANSAC_COMP (1 << 0)
@@ -45,6 +37,7 @@
#define I2C_IO_CONFIG_OPEN_DRAIN 0x0003
#define I2C_IO_CONFIG_PUSH_PULL 0x0000
#define I2C_SOFT_RST 0x0001
+#define I2C_HANDSHAKE_RST 0x0020
#define I2C_FIFO_ADDR_CLR 0x0001
#define I2C_DELAY_LEN 0x0002
#define I2C_ST_START_CON 0x8001
@@ -53,22 +46,31 @@
#define I2C_TIME_DEFAULT_VALUE 0x0003
#define I2C_WRRD_TRANAC_VALUE 0x0002
#define I2C_RD_TRANAC_VALUE 0x0001
+#define I2C_SCL_MIS_COMP_VALUE 0x0000
+#define I2C_CHN_CLR_FLAG 0x0000
+#define I2C_RELIABILITY 0x0010
+#define I2C_DMAACK_ENABLE 0x0008
#define I2C_DMA_CON_TX 0x0000
#define I2C_DMA_CON_RX 0x0001
+#define I2C_DMA_ASYNC_MODE 0x0004
+#define I2C_DMA_SKIP_CONFIG 0x0010
+#define I2C_DMA_DIR_CHANGE 0x0200
#define I2C_DMA_START_EN 0x0001
#define I2C_DMA_INT_FLAG_NONE 0x0000
#define I2C_DMA_CLR_FLAG 0x0000
+#define I2C_DMA_WARM_RST 0x0001
#define I2C_DMA_HARD_RST 0x0002
-#define I2C_DMA_4G_MODE 0x0001
+#define I2C_DMA_HANDSHAKE_RST 0x0004
-#define I2C_DEFAULT_CLK_DIV 5
-#define I2C_DEFAULT_SPEED 100000 /* hz */
-#define MAX_FS_MODE_SPEED 400000
-#define MAX_HS_MODE_SPEED 3400000
#define MAX_SAMPLE_CNT_DIV 8
#define MAX_STEP_CNT_DIV 64
+#define MAX_CLOCK_DIV_8BITS 256
+#define MAX_CLOCK_DIV_5BITS 32
#define MAX_HS_STEP_CNT_DIV 8
+#define I2C_STANDARD_MODE_BUFFER (1000 / 3)
+#define I2C_FAST_MODE_BUFFER (300 / 3)
+#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3)
#define I2C_CONTROL_RS (0x1 << 1)
#define I2C_CONTROL_DMA_EN (0x1 << 2)
@@ -76,10 +78,33 @@
#define I2C_CONTROL_DIR_CHANGE (0x1 << 4)
#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5)
#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6)
+#define I2C_CONTROL_DMAACK_EN (0x1 << 8)
+#define I2C_CONTROL_ASYNC_MODE (0x1 << 9)
#define I2C_CONTROL_WRAPPER (0x1 << 0)
#define I2C_DRV_NAME "i2c-mt65xx"
+/**
+ * enum i2c_mt65xx_clks - Clocks enumeration for MT65XX I2C
+ *
+ * @I2C_MT65XX_CLK_MAIN: main clock for i2c bus
+ * @I2C_MT65XX_CLK_DMA: DMA clock for i2c via DMA
+ * @I2C_MT65XX_CLK_PMIC: PMIC clock for i2c from PMIC
+ * @I2C_MT65XX_CLK_ARB: Arbitrator clock for i2c
+ * @I2C_MT65XX_CLK_MAX: Number of supported clocks
+ */
+enum i2c_mt65xx_clks {
+ I2C_MT65XX_CLK_MAIN = 0,
+ I2C_MT65XX_CLK_DMA,
+ I2C_MT65XX_CLK_PMIC,
+ I2C_MT65XX_CLK_ARB,
+ I2C_MT65XX_CLK_MAX
+};
+
+static const char * const i2c_mt65xx_clk_ids[I2C_MT65XX_CLK_MAX] = {
+ "main", "dma", "pmic", "arb"
+};
+
enum DMA_REGS_OFFSET {
OFFSET_INT_FLAG = 0x0,
OFFSET_INT_EN = 0x04,
@@ -106,53 +131,168 @@ enum mtk_trans_op {
};
enum I2C_REGS_OFFSET {
- OFFSET_DATA_PORT = 0x0,
- OFFSET_SLAVE_ADDR = 0x04,
- OFFSET_INTR_MASK = 0x08,
- OFFSET_INTR_STAT = 0x0c,
- OFFSET_CONTROL = 0x10,
- OFFSET_TRANSFER_LEN = 0x14,
- OFFSET_TRANSAC_LEN = 0x18,
- OFFSET_DELAY_LEN = 0x1c,
- OFFSET_TIMING = 0x20,
- OFFSET_START = 0x24,
- OFFSET_EXT_CONF = 0x28,
- OFFSET_FIFO_STAT = 0x30,
- OFFSET_FIFO_THRESH = 0x34,
- OFFSET_FIFO_ADDR_CLR = 0x38,
- OFFSET_IO_CONFIG = 0x40,
- OFFSET_RSV_DEBUG = 0x44,
- OFFSET_HS = 0x48,
- OFFSET_SOFTRESET = 0x50,
- OFFSET_DCM_EN = 0x54,
- OFFSET_PATH_DIR = 0x60,
- OFFSET_DEBUGSTAT = 0x64,
- OFFSET_DEBUGCTRL = 0x68,
- OFFSET_TRANSFER_LEN_AUX = 0x6c,
- OFFSET_CLOCK_DIV = 0x70,
+ OFFSET_DATA_PORT,
+ OFFSET_SLAVE_ADDR,
+ OFFSET_INTR_MASK,
+ OFFSET_INTR_STAT,
+ OFFSET_CONTROL,
+ OFFSET_TRANSFER_LEN,
+ OFFSET_TRANSAC_LEN,
+ OFFSET_DELAY_LEN,
+ OFFSET_TIMING,
+ OFFSET_START,
+ OFFSET_EXT_CONF,
+ OFFSET_FIFO_STAT,
+ OFFSET_FIFO_THRESH,
+ OFFSET_FIFO_ADDR_CLR,
+ OFFSET_IO_CONFIG,
+ OFFSET_RSV_DEBUG,
+ OFFSET_HS,
+ OFFSET_SOFTRESET,
+ OFFSET_DCM_EN,
+ OFFSET_MULTI_DMA,
+ OFFSET_PATH_DIR,
+ OFFSET_DEBUGSTAT,
+ OFFSET_DEBUGCTRL,
+ OFFSET_TRANSFER_LEN_AUX,
+ OFFSET_CLOCK_DIV,
+ OFFSET_LTIMING,
+ OFFSET_SCL_HIGH_LOW_RATIO,
+ OFFSET_HS_SCL_HIGH_LOW_RATIO,
+ OFFSET_SCL_MIS_COMP_POINT,
+ OFFSET_STA_STO_AC_TIMING,
+ OFFSET_HS_STA_STO_AC_TIMING,
+ OFFSET_SDA_TIMING,
+};
+
+static const u16 mt_i2c_regs_v1[] = {
+ [OFFSET_DATA_PORT] = 0x0,
+ [OFFSET_SLAVE_ADDR] = 0x4,
+ [OFFSET_INTR_MASK] = 0x8,
+ [OFFSET_INTR_STAT] = 0xc,
+ [OFFSET_CONTROL] = 0x10,
+ [OFFSET_TRANSFER_LEN] = 0x14,
+ [OFFSET_TRANSAC_LEN] = 0x18,
+ [OFFSET_DELAY_LEN] = 0x1c,
+ [OFFSET_TIMING] = 0x20,
+ [OFFSET_START] = 0x24,
+ [OFFSET_EXT_CONF] = 0x28,
+ [OFFSET_FIFO_STAT] = 0x30,
+ [OFFSET_FIFO_THRESH] = 0x34,
+ [OFFSET_FIFO_ADDR_CLR] = 0x38,
+ [OFFSET_IO_CONFIG] = 0x40,
+ [OFFSET_RSV_DEBUG] = 0x44,
+ [OFFSET_HS] = 0x48,
+ [OFFSET_SOFTRESET] = 0x50,
+ [OFFSET_DCM_EN] = 0x54,
+ [OFFSET_PATH_DIR] = 0x60,
+ [OFFSET_DEBUGSTAT] = 0x64,
+ [OFFSET_DEBUGCTRL] = 0x68,
+ [OFFSET_TRANSFER_LEN_AUX] = 0x6c,
+ [OFFSET_CLOCK_DIV] = 0x70,
+ [OFFSET_SCL_HIGH_LOW_RATIO] = 0x74,
+ [OFFSET_HS_SCL_HIGH_LOW_RATIO] = 0x78,
+ [OFFSET_SCL_MIS_COMP_POINT] = 0x7C,
+ [OFFSET_STA_STO_AC_TIMING] = 0x80,
+ [OFFSET_HS_STA_STO_AC_TIMING] = 0x84,
+ [OFFSET_SDA_TIMING] = 0x88,
+};
+
+static const u16 mt_i2c_regs_v2[] = {
+ [OFFSET_DATA_PORT] = 0x0,
+ [OFFSET_SLAVE_ADDR] = 0x4,
+ [OFFSET_INTR_MASK] = 0x8,
+ [OFFSET_INTR_STAT] = 0xc,
+ [OFFSET_CONTROL] = 0x10,
+ [OFFSET_TRANSFER_LEN] = 0x14,
+ [OFFSET_TRANSAC_LEN] = 0x18,
+ [OFFSET_DELAY_LEN] = 0x1c,
+ [OFFSET_TIMING] = 0x20,
+ [OFFSET_START] = 0x24,
+ [OFFSET_EXT_CONF] = 0x28,
+ [OFFSET_LTIMING] = 0x2c,
+ [OFFSET_HS] = 0x30,
+ [OFFSET_IO_CONFIG] = 0x34,
+ [OFFSET_FIFO_ADDR_CLR] = 0x38,
+ [OFFSET_SDA_TIMING] = 0x3c,
+ [OFFSET_TRANSFER_LEN_AUX] = 0x44,
+ [OFFSET_CLOCK_DIV] = 0x48,
+ [OFFSET_SOFTRESET] = 0x50,
+ [OFFSET_MULTI_DMA] = 0x8c,
+ [OFFSET_SCL_MIS_COMP_POINT] = 0x90,
+ [OFFSET_DEBUGSTAT] = 0xe4,
+ [OFFSET_DEBUGCTRL] = 0xe8,
+ [OFFSET_FIFO_STAT] = 0xf4,
+ [OFFSET_FIFO_THRESH] = 0xf8,
+ [OFFSET_DCM_EN] = 0xf88,
+};
+
+static const u16 mt_i2c_regs_v3[] = {
+ [OFFSET_DATA_PORT] = 0x0,
+ [OFFSET_INTR_MASK] = 0x8,
+ [OFFSET_INTR_STAT] = 0xc,
+ [OFFSET_CONTROL] = 0x10,
+ [OFFSET_TRANSFER_LEN] = 0x14,
+ [OFFSET_TRANSAC_LEN] = 0x18,
+ [OFFSET_DELAY_LEN] = 0x1c,
+ [OFFSET_TIMING] = 0x20,
+ [OFFSET_START] = 0x24,
+ [OFFSET_EXT_CONF] = 0x28,
+ [OFFSET_LTIMING] = 0x2c,
+ [OFFSET_HS] = 0x30,
+ [OFFSET_IO_CONFIG] = 0x34,
+ [OFFSET_FIFO_ADDR_CLR] = 0x38,
+ [OFFSET_SDA_TIMING] = 0x3c,
+ [OFFSET_TRANSFER_LEN_AUX] = 0x44,
+ [OFFSET_CLOCK_DIV] = 0x48,
+ [OFFSET_SOFTRESET] = 0x50,
+ [OFFSET_MULTI_DMA] = 0x8c,
+ [OFFSET_SCL_MIS_COMP_POINT] = 0x90,
+ [OFFSET_SLAVE_ADDR] = 0x94,
+ [OFFSET_DEBUGSTAT] = 0xe4,
+ [OFFSET_DEBUGCTRL] = 0xe8,
+ [OFFSET_FIFO_STAT] = 0xf4,
+ [OFFSET_FIFO_THRESH] = 0xf8,
+ [OFFSET_DCM_EN] = 0xf88,
};
struct mtk_i2c_compatible {
const struct i2c_adapter_quirks *quirks;
+ const u16 *regs;
unsigned char pmic_i2c: 1;
unsigned char dcm: 1;
unsigned char auto_restart: 1;
unsigned char aux_len_reg: 1;
- unsigned char support_33bits: 1;
unsigned char timing_adjust: 1;
+ unsigned char dma_sync: 1;
+ unsigned char ltiming_adjust: 1;
+ unsigned char apdma_sync: 1;
+ unsigned char max_dma_support;
+};
+
+struct mtk_i2c_ac_timing {
+ u16 htiming;
+ u16 ltiming;
+ u16 hs;
+ u16 ext;
+ u16 inter_clk_div;
+ u16 scl_hl_ratio;
+ u16 hs_scl_hl_ratio;
+ u16 sta_stop;
+ u16 hs_sta_stop;
+ u16 sda_timing;
};
struct mtk_i2c {
struct i2c_adapter adap; /* i2c host adapter */
struct device *dev;
struct completion msg_complete;
+ struct i2c_timings timing_info;
/* set in i2c probe */
void __iomem *base; /* i2c base addr */
void __iomem *pdmabase; /* dma base address*/
- struct clk *clk_main; /* main clock for i2c bus */
- struct clk *clk_dma; /* DMA clock for i2c via DMA */
- struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
+ struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */
bool have_pmic; /* can use i2c pins from PMIC */
bool use_push_pull; /* IO config push-pull mode */
@@ -162,11 +302,48 @@ struct mtk_i2c {
enum mtk_trans_op op;
u16 timing_reg;
u16 high_speed_reg;
+ u16 ltiming_reg;
unsigned char auto_restart;
bool ignore_restart_irq;
+ struct mtk_i2c_ac_timing ac_timing;
const struct mtk_i2c_compatible *dev_comp;
};
+/**
+ * struct i2c_spec_values:
+ * @min_low_ns: min LOW period of the SCL clock
+ * @min_su_sta_ns: min set-up time for a repeated START condition
+ * @max_hd_dat_ns: max data hold time
+ * @min_su_dat_ns: min data set-up time
+ */
+struct i2c_spec_values {
+ unsigned int min_low_ns;
+ unsigned int min_su_sta_ns;
+ unsigned int max_hd_dat_ns;
+ unsigned int min_su_dat_ns;
+};
+
+static const struct i2c_spec_values standard_mode_spec = {
+ .min_low_ns = 4700 + I2C_STANDARD_MODE_BUFFER,
+ .min_su_sta_ns = 4700 + I2C_STANDARD_MODE_BUFFER,
+ .max_hd_dat_ns = 3450 - I2C_STANDARD_MODE_BUFFER,
+ .min_su_dat_ns = 250 + I2C_STANDARD_MODE_BUFFER,
+};
+
+static const struct i2c_spec_values fast_mode_spec = {
+ .min_low_ns = 1300 + I2C_FAST_MODE_BUFFER,
+ .min_su_sta_ns = 600 + I2C_FAST_MODE_BUFFER,
+ .max_hd_dat_ns = 900 - I2C_FAST_MODE_BUFFER,
+ .min_su_dat_ns = 100 + I2C_FAST_MODE_BUFFER,
+};
+
+static const struct i2c_spec_values fast_mode_plus_spec = {
+ .min_low_ns = 500 + I2C_FAST_MODE_PLUS_BUFFER,
+ .min_su_sta_ns = 260 + I2C_FAST_MODE_PLUS_BUFFER,
+ .max_hd_dat_ns = 400 - I2C_FAST_MODE_PLUS_BUFFER,
+ .min_su_dat_ns = 50 + I2C_FAST_MODE_PLUS_BUFFER,
+};
+
static const struct i2c_adapter_quirks mt6577_i2c_quirks = {
.flags = I2C_AQ_COMB_WRITE_THEN_READ,
.max_num_msgs = 1,
@@ -180,52 +357,168 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = {
.max_num_msgs = 255,
};
+static const struct i2c_adapter_quirks mt8183_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static const struct mtk_i2c_compatible mt2712_compat = {
+ .regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 1,
.aux_len_reg = 1,
- .support_33bits = 1,
.timing_adjust = 1,
+ .dma_sync = 0,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 33,
};
static const struct mtk_i2c_compatible mt6577_compat = {
.quirks = &mt6577_i2c_quirks,
+ .regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 0,
.aux_len_reg = 0,
- .support_33bits = 0,
.timing_adjust = 0,
+ .dma_sync = 0,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 32,
};
static const struct mtk_i2c_compatible mt6589_compat = {
.quirks = &mt6577_i2c_quirks,
+ .regs = mt_i2c_regs_v1,
.pmic_i2c = 1,
.dcm = 0,
.auto_restart = 0,
.aux_len_reg = 0,
- .support_33bits = 0,
.timing_adjust = 0,
+ .dma_sync = 0,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 32,
};
static const struct mtk_i2c_compatible mt7622_compat = {
.quirks = &mt7622_i2c_quirks,
+ .regs = mt_i2c_regs_v1,
+ .pmic_i2c = 0,
+ .dcm = 1,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 0,
+ .dma_sync = 0,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 32,
+};
+
+static const struct mtk_i2c_compatible mt8168_compat = {
+ .regs = mt_i2c_regs_v1,
+ .pmic_i2c = 0,
+ .dcm = 1,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 1,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 33,
+};
+
+static const struct mtk_i2c_compatible mt7981_compat = {
+ .regs = mt_i2c_regs_v3,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 1,
+ .ltiming_adjust = 1,
+ .max_dma_support = 33
+};
+
+static const struct mtk_i2c_compatible mt7986_compat = {
+ .quirks = &mt7622_i2c_quirks,
+ .regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 1,
.aux_len_reg = 1,
- .support_33bits = 0,
.timing_adjust = 0,
+ .dma_sync = 1,
+ .ltiming_adjust = 0,
+ .max_dma_support = 32,
};
static const struct mtk_i2c_compatible mt8173_compat = {
+ .regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 1,
.aux_len_reg = 1,
- .support_33bits = 1,
.timing_adjust = 0,
+ .dma_sync = 0,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 33,
+};
+
+static const struct mtk_i2c_compatible mt8183_compat = {
+ .quirks = &mt8183_i2c_quirks,
+ .regs = mt_i2c_regs_v2,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 1,
+ .ltiming_adjust = 1,
+ .apdma_sync = 0,
+ .max_dma_support = 33,
+};
+
+static const struct mtk_i2c_compatible mt8186_compat = {
+ .regs = mt_i2c_regs_v2,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 0,
+ .ltiming_adjust = 1,
+ .apdma_sync = 0,
+ .max_dma_support = 36,
+};
+
+static const struct mtk_i2c_compatible mt8188_compat = {
+ .regs = mt_i2c_regs_v3,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 0,
+ .ltiming_adjust = 1,
+ .apdma_sync = 1,
+ .max_dma_support = 36,
+};
+
+static const struct mtk_i2c_compatible mt8192_compat = {
+ .quirks = &mt8183_i2c_quirks,
+ .regs = mt_i2c_regs_v2,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 1,
+ .ltiming_adjust = 1,
+ .apdma_sync = 1,
+ .max_dma_support = 36,
};
static const struct of_device_id mtk_i2c_of_match[] = {
@@ -233,80 +526,262 @@ static const struct of_device_id mtk_i2c_of_match[] = {
{ .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat },
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
+ { .compatible = "mediatek,mt7981-i2c", .data = &mt7981_compat },
+ { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat },
+ { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat },
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
+ { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat },
+ { .compatible = "mediatek,mt8188-i2c", .data = &mt8188_compat },
+ { .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat },
{}
};
MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
-static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
+static u16 mtk_i2c_readw(struct mtk_i2c *i2c, enum I2C_REGS_OFFSET reg)
{
- int ret;
-
- ret = clk_prepare_enable(i2c->clk_dma);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(i2c->clk_main);
- if (ret)
- goto err_main;
-
- if (i2c->have_pmic) {
- ret = clk_prepare_enable(i2c->clk_pmic);
- if (ret)
- goto err_pmic;
- }
- return 0;
-
-err_pmic:
- clk_disable_unprepare(i2c->clk_main);
-err_main:
- clk_disable_unprepare(i2c->clk_dma);
-
- return ret;
+ return readw(i2c->base + i2c->dev_comp->regs[reg]);
}
-static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
+static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
+ enum I2C_REGS_OFFSET reg)
{
- if (i2c->have_pmic)
- clk_disable_unprepare(i2c->clk_pmic);
-
- clk_disable_unprepare(i2c->clk_main);
- clk_disable_unprepare(i2c->clk_dma);
+ writew(val, i2c->base + i2c->dev_comp->regs[reg]);
}
static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
{
u16 control_reg;
-
- writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET);
+ u16 intr_stat_reg;
+ u16 ext_conf_val;
+
+ mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_START);
+ intr_stat_reg = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
+ mtk_i2c_writew(i2c, intr_stat_reg, OFFSET_INTR_STAT);
+
+ if (i2c->dev_comp->apdma_sync) {
+ writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST);
+ udelay(10);
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+ udelay(10);
+ writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_HARD_RST,
+ i2c->pdmabase + OFFSET_RST);
+ mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST | I2C_SOFT_RST,
+ OFFSET_SOFTRESET);
+ udelay(10);
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+ mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET);
+ } else {
+ writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
+ udelay(50);
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+ mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
+ }
/* Set ioconfig */
if (i2c->use_push_pull)
- writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG);
+ mtk_i2c_writew(i2c, I2C_IO_CONFIG_PUSH_PULL, OFFSET_IO_CONFIG);
else
- writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG);
+ mtk_i2c_writew(i2c, I2C_IO_CONFIG_OPEN_DRAIN, OFFSET_IO_CONFIG);
if (i2c->dev_comp->dcm)
- writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN);
+ mtk_i2c_writew(i2c, I2C_DCM_DISABLE, OFFSET_DCM_EN);
- if (i2c->dev_comp->timing_adjust)
- writew(I2C_DEFAULT_CLK_DIV - 1, i2c->base + OFFSET_CLOCK_DIV);
+ mtk_i2c_writew(i2c, i2c->timing_reg, OFFSET_TIMING);
+ mtk_i2c_writew(i2c, i2c->high_speed_reg, OFFSET_HS);
+ if (i2c->dev_comp->ltiming_adjust)
+ mtk_i2c_writew(i2c, i2c->ltiming_reg, OFFSET_LTIMING);
- writew(i2c->timing_reg, i2c->base + OFFSET_TIMING);
- writew(i2c->high_speed_reg, i2c->base + OFFSET_HS);
+ if (i2c->speed_hz <= I2C_MAX_STANDARD_MODE_FREQ)
+ ext_conf_val = I2C_ST_START_CON;
+ else
+ ext_conf_val = I2C_FS_START_CON;
+
+ if (i2c->dev_comp->timing_adjust) {
+ ext_conf_val = i2c->ac_timing.ext;
+ mtk_i2c_writew(i2c, i2c->ac_timing.inter_clk_div,
+ OFFSET_CLOCK_DIV);
+ mtk_i2c_writew(i2c, I2C_SCL_MIS_COMP_VALUE,
+ OFFSET_SCL_MIS_COMP_POINT);
+ mtk_i2c_writew(i2c, i2c->ac_timing.sda_timing,
+ OFFSET_SDA_TIMING);
+
+ if (i2c->dev_comp->ltiming_adjust) {
+ mtk_i2c_writew(i2c, i2c->ac_timing.htiming,
+ OFFSET_TIMING);
+ mtk_i2c_writew(i2c, i2c->ac_timing.hs, OFFSET_HS);
+ mtk_i2c_writew(i2c, i2c->ac_timing.ltiming,
+ OFFSET_LTIMING);
+ } else {
+ mtk_i2c_writew(i2c, i2c->ac_timing.scl_hl_ratio,
+ OFFSET_SCL_HIGH_LOW_RATIO);
+ mtk_i2c_writew(i2c, i2c->ac_timing.hs_scl_hl_ratio,
+ OFFSET_HS_SCL_HIGH_LOW_RATIO);
+ mtk_i2c_writew(i2c, i2c->ac_timing.sta_stop,
+ OFFSET_STA_STO_AC_TIMING);
+ mtk_i2c_writew(i2c, i2c->ac_timing.hs_sta_stop,
+ OFFSET_HS_STA_STO_AC_TIMING);
+ }
+ }
+ mtk_i2c_writew(i2c, ext_conf_val, OFFSET_EXT_CONF);
/* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */
if (i2c->have_pmic)
- writew(I2C_CONTROL_WRAPPER, i2c->base + OFFSET_PATH_DIR);
+ mtk_i2c_writew(i2c, I2C_CONTROL_WRAPPER, OFFSET_PATH_DIR);
control_reg = I2C_CONTROL_ACKERR_DET_EN |
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
- writew(control_reg, i2c->base + OFFSET_CONTROL);
- writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN);
+ if (i2c->dev_comp->dma_sync)
+ control_reg |= I2C_CONTROL_DMAACK_EN | I2C_CONTROL_ASYNC_MODE;
+
+ mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
+ mtk_i2c_writew(i2c, I2C_DELAY_LEN, OFFSET_DELAY_LEN);
+}
+
+static const struct i2c_spec_values *mtk_i2c_get_spec(unsigned int speed)
+{
+ if (speed <= I2C_MAX_STANDARD_MODE_FREQ)
+ return &standard_mode_spec;
+ else if (speed <= I2C_MAX_FAST_MODE_FREQ)
+ return &fast_mode_spec;
+ else
+ return &fast_mode_plus_spec;
+}
+
+static int mtk_i2c_max_step_cnt(unsigned int target_speed)
+{
+ if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ)
+ return MAX_HS_STEP_CNT_DIV;
+ else
+ return MAX_STEP_CNT_DIV;
+}
+
+static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c,
+ unsigned int sample_cnt)
+{
+ int clk_div_restri = 0;
+
+ if (i2c->dev_comp->ltiming_adjust == 0)
+ return 0;
+
+ if (sample_cnt == 1) {
+ if (i2c->ac_timing.inter_clk_div == 0)
+ clk_div_restri = 0;
+ else
+ clk_div_restri = 1;
+ } else {
+ if (i2c->ac_timing.inter_clk_div == 0)
+ clk_div_restri = -1;
+ else if (i2c->ac_timing.inter_clk_div == 1)
+ clk_div_restri = 0;
+ else
+ clk_div_restri = 1;
+ }
+
+ return clk_div_restri;
+}
+
+/*
+ * Check and Calculate i2c ac-timing
+ *
+ * Hardware design:
+ * sample_ns = (1000000000 * (sample_cnt + 1)) / clk_src
+ * xxx_cnt_div = spec->min_xxx_ns / sample_ns
+ *
+ * Sample_ns is rounded down for xxx_cnt_div would be greater
+ * than the smallest spec.
+ * The sda_timing is chosen as the middle value between
+ * the largest and smallest.
+ */
+static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
+ unsigned int clk_src,
+ unsigned int check_speed,
+ unsigned int step_cnt,
+ unsigned int sample_cnt)
+{
+ const struct i2c_spec_values *spec;
+ unsigned int su_sta_cnt, low_cnt, high_cnt, max_step_cnt;
+ unsigned int sda_max, sda_min, clk_ns, max_sta_cnt = 0x3f;
+ unsigned int sample_ns = div_u64(1000000000ULL * (sample_cnt + 1),
+ clk_src);
+
+ if (!i2c->dev_comp->timing_adjust)
+ return 0;
+
+ if (i2c->dev_comp->ltiming_adjust)
+ max_sta_cnt = 0x100;
+
+ spec = mtk_i2c_get_spec(check_speed);
- writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
- udelay(50);
- writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+ if (i2c->dev_comp->ltiming_adjust)
+ clk_ns = 1000000000 / clk_src;
+ else
+ clk_ns = sample_ns / 2;
+
+ su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns +
+ i2c->timing_info.scl_int_delay_ns, clk_ns);
+ if (su_sta_cnt > max_sta_cnt)
+ return -1;
+
+ low_cnt = DIV_ROUND_UP(spec->min_low_ns, sample_ns);
+ max_step_cnt = mtk_i2c_max_step_cnt(check_speed);
+ if ((2 * step_cnt) > low_cnt && low_cnt < max_step_cnt) {
+ if (low_cnt > step_cnt) {
+ high_cnt = 2 * step_cnt - low_cnt;
+ } else {
+ high_cnt = step_cnt;
+ low_cnt = step_cnt;
+ }
+ } else {
+ return -2;
+ }
+
+ sda_max = spec->max_hd_dat_ns / sample_ns;
+ if (sda_max > low_cnt)
+ sda_max = 0;
+
+ sda_min = DIV_ROUND_UP(spec->min_su_dat_ns, sample_ns);
+ if (sda_min < low_cnt)
+ sda_min = 0;
+
+ if (sda_min > sda_max)
+ return -3;
+
+ if (check_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
+ if (i2c->dev_comp->ltiming_adjust) {
+ i2c->ac_timing.hs = I2C_TIME_DEFAULT_VALUE |
+ (sample_cnt << 12) | (high_cnt << 8);
+ i2c->ac_timing.ltiming &= ~GENMASK(15, 9);
+ i2c->ac_timing.ltiming |= (sample_cnt << 12) |
+ (low_cnt << 9);
+ i2c->ac_timing.ext &= ~GENMASK(7, 1);
+ i2c->ac_timing.ext |= (su_sta_cnt << 1) | (1 << 0);
+ } else {
+ i2c->ac_timing.hs_scl_hl_ratio = (1 << 12) |
+ (high_cnt << 6) | low_cnt;
+ i2c->ac_timing.hs_sta_stop = (su_sta_cnt << 8) |
+ su_sta_cnt;
+ }
+ i2c->ac_timing.sda_timing &= ~GENMASK(11, 6);
+ i2c->ac_timing.sda_timing |= (1 << 12) |
+ ((sda_max + sda_min) / 2) << 6;
+ } else {
+ if (i2c->dev_comp->ltiming_adjust) {
+ i2c->ac_timing.htiming = (sample_cnt << 8) | (high_cnt);
+ i2c->ac_timing.ltiming = (sample_cnt << 6) | (low_cnt);
+ i2c->ac_timing.ext = (su_sta_cnt << 8) | (1 << 0);
+ } else {
+ i2c->ac_timing.scl_hl_ratio = (1 << 12) |
+ (high_cnt << 6) | low_cnt;
+ i2c->ac_timing.sta_stop = (su_sta_cnt << 8) |
+ su_sta_cnt;
+ }
+
+ i2c->ac_timing.sda_timing = (1 << 12) |
+ (sda_max + sda_min) / 2;
+ }
+
+ return 0;
}
/*
@@ -333,15 +808,13 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
unsigned int opt_div;
unsigned int best_mul;
unsigned int cnt_mul;
+ int ret = -EINVAL;
+ int clk_div_restri = 0;
- if (target_speed > MAX_HS_MODE_SPEED)
- target_speed = MAX_HS_MODE_SPEED;
-
- if (target_speed > MAX_FS_MODE_SPEED)
- max_step_cnt = MAX_HS_STEP_CNT_DIV;
- else
- max_step_cnt = MAX_STEP_CNT_DIV;
+ if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ)
+ target_speed = I2C_MAX_HIGH_SPEED_MODE_FREQ;
+ max_step_cnt = mtk_i2c_max_step_cnt(target_speed);
base_step_cnt = max_step_cnt;
/* Find the best combination */
opt_div = DIV_ROUND_UP(clk_src >> 1, target_speed);
@@ -354,24 +827,34 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
* optimizing for sample_cnt * step_cnt being minimal
*/
for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
- step_cnt = DIV_ROUND_UP(opt_div, sample_cnt);
+ clk_div_restri = mtk_i2c_get_clk_div_restri(i2c, sample_cnt);
+ step_cnt = DIV_ROUND_UP(opt_div + clk_div_restri, sample_cnt);
cnt_mul = step_cnt * sample_cnt;
if (step_cnt > max_step_cnt)
continue;
if (cnt_mul < best_mul) {
+ ret = mtk_i2c_check_ac_timing(i2c, clk_src,
+ target_speed, step_cnt - 1, sample_cnt - 1);
+ if (ret)
+ continue;
+
best_mul = cnt_mul;
base_sample_cnt = sample_cnt;
base_step_cnt = step_cnt;
- if (best_mul == opt_div)
+ if (best_mul == (opt_div + clk_div_restri))
break;
}
}
+ if (ret)
+ return -EINVAL;
+
sample_cnt = base_sample_cnt;
step_cnt = base_step_cnt;
- if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) {
+ if ((clk_src / (2 * (sample_cnt * step_cnt - clk_div_restri))) >
+ target_speed) {
/* In this case, hardware can't support such
* low i2c_bus_freq
*/
@@ -385,52 +868,127 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
return 0;
}
-static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
+static void mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
{
unsigned int clk_src;
unsigned int step_cnt;
unsigned int sample_cnt;
+ unsigned int l_step_cnt;
+ unsigned int l_sample_cnt;
unsigned int target_speed;
+ unsigned int clk_div;
+ unsigned int max_clk_div;
int ret;
- clk_src = parent_clk / i2c->clk_src_div;
target_speed = i2c->speed_hz;
+ parent_clk /= i2c->clk_src_div;
- if (target_speed > MAX_FS_MODE_SPEED) {
- /* Set master code speed register */
- ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
- &step_cnt, &sample_cnt);
- if (ret < 0)
- return ret;
+ if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust)
+ max_clk_div = MAX_CLOCK_DIV_5BITS;
+ else if (i2c->dev_comp->timing_adjust)
+ max_clk_div = MAX_CLOCK_DIV_8BITS;
+ else
+ max_clk_div = 1;
+
+ for (clk_div = 1; clk_div <= max_clk_div; clk_div++) {
+ clk_src = parent_clk / clk_div;
+ i2c->ac_timing.inter_clk_div = clk_div - 1;
+
+ if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
+ /* Set master code speed register */
+ ret = mtk_i2c_calculate_speed(i2c, clk_src,
+ I2C_MAX_FAST_MODE_FREQ,
+ &l_step_cnt,
+ &l_sample_cnt);
+ if (ret < 0)
+ continue;
+
+ i2c->timing_reg = (l_sample_cnt << 8) | l_step_cnt;
+
+ /* Set the high speed mode register */
+ ret = mtk_i2c_calculate_speed(i2c, clk_src,
+ target_speed, &step_cnt,
+ &sample_cnt);
+ if (ret < 0)
+ continue;
+
+ i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
+ (sample_cnt << 12) | (step_cnt << 8);
+
+ if (i2c->dev_comp->ltiming_adjust)
+ i2c->ltiming_reg =
+ (l_sample_cnt << 6) | l_step_cnt |
+ (sample_cnt << 12) | (step_cnt << 9);
+ } else {
+ ret = mtk_i2c_calculate_speed(i2c, clk_src,
+ target_speed, &l_step_cnt,
+ &l_sample_cnt);
+ if (ret < 0)
+ continue;
- i2c->timing_reg = (sample_cnt << 8) | step_cnt;
+ i2c->timing_reg = (l_sample_cnt << 8) | l_step_cnt;
- /* Set the high speed mode register */
- ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
- &step_cnt, &sample_cnt);
- if (ret < 0)
- return ret;
+ /* Disable the high speed transaction */
+ i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
- i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
- (sample_cnt << 12) | (step_cnt << 8);
- } else {
- ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
- &step_cnt, &sample_cnt);
- if (ret < 0)
- return ret;
-
- i2c->timing_reg = (sample_cnt << 8) | step_cnt;
+ if (i2c->dev_comp->ltiming_adjust)
+ i2c->ltiming_reg =
+ (l_sample_cnt << 6) | l_step_cnt;
+ }
- /* Disable the high speed transaction */
- i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
+ break;
}
-
- return 0;
}
-static inline u32 mtk_i2c_set_4g_mode(dma_addr_t addr)
+static void i2c_dump_register(struct mtk_i2c *i2c)
{
- return (addr & BIT_ULL(32)) ? I2C_DMA_4G_MODE : I2C_DMA_CLR_FLAG;
+ dev_dbg(i2c->dev, "SLAVE_ADDR: 0x%x, INTR_MASK: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_SLAVE_ADDR),
+ mtk_i2c_readw(i2c, OFFSET_INTR_MASK));
+ dev_dbg(i2c->dev, "INTR_STAT: 0x%x, CONTROL: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_INTR_STAT),
+ mtk_i2c_readw(i2c, OFFSET_CONTROL));
+ dev_dbg(i2c->dev, "TRANSFER_LEN: 0x%x, TRANSAC_LEN: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN),
+ mtk_i2c_readw(i2c, OFFSET_TRANSAC_LEN));
+ dev_dbg(i2c->dev, "DELAY_LEN: 0x%x, HTIMING: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_DELAY_LEN),
+ mtk_i2c_readw(i2c, OFFSET_TIMING));
+ dev_dbg(i2c->dev, "START: 0x%x, EXT_CONF: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_START),
+ mtk_i2c_readw(i2c, OFFSET_EXT_CONF));
+ dev_dbg(i2c->dev, "HS: 0x%x, IO_CONFIG: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_HS),
+ mtk_i2c_readw(i2c, OFFSET_IO_CONFIG));
+ dev_dbg(i2c->dev, "DCM_EN: 0x%x, TRANSFER_LEN_AUX: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_DCM_EN),
+ mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN_AUX));
+ dev_dbg(i2c->dev, "CLOCK_DIV: 0x%x, FIFO_STAT: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_CLOCK_DIV),
+ mtk_i2c_readw(i2c, OFFSET_FIFO_STAT));
+ dev_dbg(i2c->dev, "DEBUGCTRL : 0x%x, DEBUGSTAT: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_DEBUGCTRL),
+ mtk_i2c_readw(i2c, OFFSET_DEBUGSTAT));
+ if (i2c->dev_comp->regs == mt_i2c_regs_v2) {
+ dev_dbg(i2c->dev, "LTIMING: 0x%x, MULTI_DMA: 0x%x\n",
+ mtk_i2c_readw(i2c, OFFSET_LTIMING),
+ mtk_i2c_readw(i2c, OFFSET_MULTI_DMA));
+ }
+ dev_dbg(i2c->dev, "\nDMA_INT_FLAG: 0x%x, DMA_INT_EN: 0x%x\n",
+ readl(i2c->pdmabase + OFFSET_INT_FLAG),
+ readl(i2c->pdmabase + OFFSET_INT_EN));
+ dev_dbg(i2c->dev, "DMA_EN: 0x%x, DMA_CON: 0x%x\n",
+ readl(i2c->pdmabase + OFFSET_EN),
+ readl(i2c->pdmabase + OFFSET_CON));
+ dev_dbg(i2c->dev, "DMA_TX_MEM_ADDR: 0x%x, DMA_RX_MEM_ADDR: 0x%x\n",
+ readl(i2c->pdmabase + OFFSET_TX_MEM_ADDR),
+ readl(i2c->pdmabase + OFFSET_RX_MEM_ADDR));
+ dev_dbg(i2c->dev, "DMA_TX_LEN: 0x%x, DMA_RX_LEN: 0x%x\n",
+ readl(i2c->pdmabase + OFFSET_TX_LEN),
+ readl(i2c->pdmabase + OFFSET_RX_LEN));
+ dev_dbg(i2c->dev, "DMA_TX_4G_MODE: 0x%x, DMA_RX_4G_MODE: 0x%x",
+ readl(i2c->pdmabase + OFFSET_TX_4G_MODE),
+ readl(i2c->pdmabase + OFFSET_RX_4G_MODE));
}
static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
@@ -440,7 +998,9 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
u16 start_reg;
u16 control_reg;
u16 restart_flag = 0;
+ u16 dma_sync = 0;
u32 reg_4g_mode;
+ u32 reg_dma_reset;
u8 *dma_rd_buf = NULL;
u8 *dma_wr_buf = NULL;
dma_addr_t rpaddr = 0;
@@ -454,56 +1014,79 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
reinit_completion(&i2c->msg_complete);
- control_reg = readw(i2c->base + OFFSET_CONTROL) &
+ if (i2c->dev_comp->apdma_sync &&
+ i2c->op != I2C_MASTER_WRRD && num > 1) {
+ mtk_i2c_writew(i2c, 0x00, OFFSET_DEBUGCTRL);
+ writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_WARM_RST,
+ i2c->pdmabase + OFFSET_RST);
+
+ ret = readw_poll_timeout(i2c->pdmabase + OFFSET_RST,
+ reg_dma_reset,
+ !(reg_dma_reset & I2C_DMA_WARM_RST),
+ 0, 100);
+ if (ret) {
+ dev_err(i2c->dev, "DMA warm reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+ mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST, OFFSET_SOFTRESET);
+ mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET);
+ mtk_i2c_writew(i2c, I2C_RELIABILITY | I2C_DMAACK_ENABLE,
+ OFFSET_DEBUGCTRL);
+ }
+
+ control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
- if ((i2c->speed_hz > 400000) || (left_num >= 1))
+ if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1))
control_reg |= I2C_CONTROL_RS;
if (i2c->op == I2C_MASTER_WRRD)
control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
- writew(control_reg, i2c->base + OFFSET_CONTROL);
-
- /* set start condition */
- if (i2c->speed_hz <= 100000)
- writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
- else
- writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
+ mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
addr_reg = i2c_8bit_addr_from_msg(msgs);
- writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
+ mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR);
/* Clear interrupt status */
- writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
- I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT);
- writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR);
+ mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_STAT);
+
+ mtk_i2c_writew(i2c, I2C_FIFO_ADDR_CLR, OFFSET_FIFO_ADDR_CLR);
/* Enable interrupt */
- writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
- I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK);
+ mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_MASK);
/* Set transfer and transaction len */
if (i2c->op == I2C_MASTER_WRRD) {
if (i2c->dev_comp->aux_len_reg) {
- writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
- writew((msgs + 1)->len, i2c->base +
- OFFSET_TRANSFER_LEN_AUX);
+ mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
+ mtk_i2c_writew(i2c, (msgs + 1)->len,
+ OFFSET_TRANSFER_LEN_AUX);
} else {
- writew(msgs->len | ((msgs + 1)->len) << 8,
- i2c->base + OFFSET_TRANSFER_LEN);
+ mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8,
+ OFFSET_TRANSFER_LEN);
}
- writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
+ mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
} else {
- writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
- writew(num, i2c->base + OFFSET_TRANSAC_LEN);
+ mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
+ mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
+ }
+
+ if (i2c->dev_comp->apdma_sync) {
+ dma_sync = I2C_DMA_SKIP_CONFIG | I2C_DMA_ASYNC_MODE;
+ if (i2c->op == I2C_MASTER_WRRD)
+ dma_sync |= I2C_DMA_DIR_CHANGE;
}
/* Prepare buffer data to start transfer */
if (i2c->op == I2C_MASTER_RD) {
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
- writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON);
+ writel(I2C_DMA_CON_RX | dma_sync, i2c->pdmabase + OFFSET_CON);
- dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_rd_buf)
return -ENOMEM;
@@ -515,8 +1098,8 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ENOMEM;
}
- if (i2c->dev_comp->support_33bits) {
- reg_4g_mode = mtk_i2c_set_4g_mode(rpaddr);
+ if (i2c->dev_comp->max_dma_support > 32) {
+ reg_4g_mode = upper_32_bits(rpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE);
}
@@ -524,9 +1107,9 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(msgs->len, i2c->pdmabase + OFFSET_RX_LEN);
} else if (i2c->op == I2C_MASTER_WR) {
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
- writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
+ writel(I2C_DMA_CON_TX | dma_sync, i2c->pdmabase + OFFSET_CON);
- dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_wr_buf)
return -ENOMEM;
@@ -538,8 +1121,8 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ENOMEM;
}
- if (i2c->dev_comp->support_33bits) {
- reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
+ if (i2c->dev_comp->max_dma_support > 32) {
+ reg_4g_mode = upper_32_bits(wpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
}
@@ -547,9 +1130,9 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
} else {
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
- writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
+ writel(I2C_DMA_CLR_FLAG | dma_sync, i2c->pdmabase + OFFSET_CON);
- dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_wr_buf)
return -ENOMEM;
@@ -561,7 +1144,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ENOMEM;
}
- dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 0);
+ dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 1);
if (!dma_rd_buf) {
dma_unmap_single(i2c->dev, wpaddr,
msgs->len, DMA_TO_DEVICE);
@@ -584,11 +1167,11 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ENOMEM;
}
- if (i2c->dev_comp->support_33bits) {
- reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
+ if (i2c->dev_comp->max_dma_support > 32) {
+ reg_4g_mode = upper_32_bits(wpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
- reg_4g_mode = mtk_i2c_set_4g_mode(rpaddr);
+ reg_4g_mode = upper_32_bits(rpaddr);
writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE);
}
@@ -607,14 +1190,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (left_num >= 1)
start_reg |= I2C_RS_MUL_CNFG;
}
- writew(start_reg, i2c->base + OFFSET_START);
+ mtk_i2c_writew(i2c, start_reg, OFFSET_START);
ret = wait_for_completion_timeout(&i2c->msg_complete,
i2c->adap.timeout);
/* Clear interrupt mask */
- writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
- I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
+ mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_ARB_LOST | I2C_TRANSAC_COMP), OFFSET_INTR_MASK);
if (i2c->op == I2C_MASTER_WR) {
dma_unmap_single(i2c->dev, wpaddr,
@@ -638,12 +1221,11 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (ret == 0) {
dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr);
+ i2c_dump_register(i2c);
mtk_i2c_init_hw(i2c);
return -ETIMEDOUT;
}
- completion_done(&i2c->msg_complete);
-
if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
mtk_i2c_init_hw(i2c);
@@ -658,9 +1240,10 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
{
int ret;
int left_num = num;
+ bool write_then_read_en = false;
struct mtk_i2c *i2c = i2c_get_adapdata(adap);
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret)
return ret;
@@ -671,10 +1254,12 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
msgs[0].addr == msgs[1].addr) {
i2c->auto_restart = 0;
+ write_then_read_en = true;
}
}
- if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
+ if (i2c->auto_restart && num >= 2 &&
+ i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ)
/* ignore the first restart irq after the master code,
* otherwise the first transfer will be discarded.
*/
@@ -694,12 +1279,10 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
else
i2c->op = I2C_MASTER_WR;
- if (!i2c->auto_restart) {
- if (num > 1) {
- /* combined two messages into one transaction */
- i2c->op = I2C_MASTER_WRRD;
- left_num--;
- }
+ if (write_then_read_en) {
+ /* combined two messages into one transaction */
+ i2c->op = I2C_MASTER_WRRD;
+ left_num--;
}
/* always use DMA mode. */
@@ -707,27 +1290,27 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
if (ret < 0)
goto err_exit;
- msgs++;
+ if (i2c->op == I2C_MASTER_WRRD)
+ msgs += 2;
+ else
+ msgs++;
}
/* the return value is number of executed messages */
ret = num;
err_exit:
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
return ret;
}
static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
{
struct mtk_i2c *i2c = dev_id;
- u16 restart_flag = 0;
+ u16 restart_flag = i2c->auto_restart ? I2C_RS_TRANSFER : 0;
u16 intr_stat;
- if (i2c->auto_restart)
- restart_flag = I2C_RS_TRANSFER;
-
- intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
- writew(intr_stat, i2c->base + OFFSET_INTR_STAT);
+ intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
+ mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT);
/*
* when occurs ack error, i2c controller generate two interrupts
@@ -739,8 +1322,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
i2c->ignore_restart_irq = false;
i2c->irq_stat = 0;
- writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
- i2c->base + OFFSET_START);
+ mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
+ I2C_TRANSAC_START, OFFSET_START);
} else {
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
complete(&i2c->msg_complete);
@@ -751,11 +1334,15 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
static u32 mtk_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ if (i2c_check_quirks(adap, I2C_AQ_NO_ZERO_LEN))
+ return I2C_FUNC_I2C |
+ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ else
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mtk_i2c_algorithm = {
- .master_xfer = mtk_i2c_transfer,
+ .xfer = mtk_i2c_transfer,
.functionality = mtk_i2c_functionality,
};
@@ -765,7 +1352,7 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c)
ret = of_property_read_u32(np, "clock-frequency", &i2c->speed_hz);
if (ret < 0)
- i2c->speed_hz = I2C_DEFAULT_SPEED;
+ i2c->speed_hz = I2C_MAX_STANDARD_MODE_FREQ;
ret = of_property_read_u32(np, "clock-div", &i2c->clk_src_div);
if (ret < 0)
@@ -778,6 +1365,8 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c)
i2c->use_push_pull =
of_property_read_bool(np, "mediatek,use-push-pull");
+ i2c_parse_fw_timings(i2c->dev, &i2c->timing_info, true);
+
return 0;
}
@@ -785,26 +1374,22 @@ static int mtk_i2c_probe(struct platform_device *pdev)
{
int ret = 0;
struct mtk_i2c *i2c;
- struct clk *clk;
- struct resource *res;
- int irq;
+ int i, irq, speed_clk;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
+ i2c->pdmabase = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
if (IS_ERR(i2c->pdmabase))
return PTR_ERR(i2c->pdmabase);
irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
+ if (irq < 0)
return irq;
init_completion(&i2c->msg_complete);
@@ -818,97 +1403,128 @@ static int mtk_i2c_probe(struct platform_device *pdev)
i2c->adap.quirks = i2c->dev_comp->quirks;
i2c->adap.timeout = 2 * HZ;
i2c->adap.retries = 1;
+ i2c->adap.bus_regulator = devm_regulator_get_optional(&pdev->dev, "vbus");
+ if (IS_ERR(i2c->adap.bus_regulator)) {
+ if (PTR_ERR(i2c->adap.bus_regulator) == -ENODEV)
+ i2c->adap.bus_regulator = NULL;
+ else
+ return PTR_ERR(i2c->adap.bus_regulator);
+ }
ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
if (ret)
return -EINVAL;
- if (i2c->dev_comp->timing_adjust)
- i2c->clk_src_div *= I2C_DEFAULT_CLK_DIV;
-
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
return -EINVAL;
- i2c->clk_main = devm_clk_get(&pdev->dev, "main");
- if (IS_ERR(i2c->clk_main)) {
+ /* Fill in clk-bulk IDs */
+ for (i = 0; i < I2C_MT65XX_CLK_MAX; i++)
+ i2c->clocks[i].id = i2c_mt65xx_clk_ids[i];
+
+ /* Get clocks one by one, some may be optional */
+ i2c->clocks[I2C_MT65XX_CLK_MAIN].clk = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk)) {
dev_err(&pdev->dev, "cannot get main clock\n");
- return PTR_ERR(i2c->clk_main);
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk);
}
- i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
- if (IS_ERR(i2c->clk_dma)) {
+ i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) {
dev_err(&pdev->dev, "cannot get dma clock\n");
- return PTR_ERR(i2c->clk_dma);
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk);
+ }
+
+ i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk))
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk);
+
+ i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get_optional(&pdev->dev, "pmic");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) {
+ dev_err(&pdev->dev, "cannot get pmic clock\n");
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk);
}
- clk = i2c->clk_main;
if (i2c->have_pmic) {
- i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
- if (IS_ERR(i2c->clk_pmic)) {
+ if (!i2c->clocks[I2C_MT65XX_CLK_PMIC].clk) {
dev_err(&pdev->dev, "cannot get pmic clock\n");
- return PTR_ERR(i2c->clk_pmic);
+ return -ENODEV;
}
- clk = i2c->clk_pmic;
+ speed_clk = I2C_MT65XX_CLK_PMIC;
+ } else {
+ speed_clk = I2C_MT65XX_CLK_MAIN;
}
- strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
- ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
- if (ret) {
- dev_err(&pdev->dev, "Failed to set the speed.\n");
- return -EINVAL;
- }
+ mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk));
- if (i2c->dev_comp->support_33bits) {
- ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
+ if (i2c->dev_comp->max_dma_support > 32) {
+ ret = dma_set_mask(&pdev->dev,
+ DMA_BIT_MASK(i2c->dev_comp->max_dma_support));
if (ret) {
dev_err(&pdev->dev, "dma_set_mask return error.\n");
return ret;
}
}
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(&pdev->dev, "clock enable failed!\n");
return ret;
}
mtk_i2c_init_hw(i2c);
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
- IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
+ dev_name(&pdev->dev), i2c);
if (ret < 0) {
dev_err(&pdev->dev,
"Request I2C IRQ %d fail\n", irq);
- return ret;
+ goto err_bulk_unprepare;
}
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_adapter(&i2c->adap);
if (ret)
- return ret;
+ goto err_bulk_unprepare;
platform_set_drvdata(pdev, i2c);
return 0;
+
+err_bulk_unprepare:
+ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
+
+ return ret;
}
-static int mtk_i2c_remove(struct platform_device *pdev)
+static void mtk_i2c_remove(struct platform_device *pdev)
{
struct mtk_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
+ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
+}
+
+static int mtk_i2c_suspend_noirq(struct device *dev)
+{
+ struct mtk_i2c *i2c = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&i2c->adap);
+ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
+
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int mtk_i2c_resume(struct device *dev)
+static int mtk_i2c_resume_noirq(struct device *dev)
{
int ret;
struct mtk_i2c *i2c = dev_get_drvdata(dev);
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(dev, "clock enable failed!\n");
return ret;
@@ -916,14 +1532,16 @@ static int mtk_i2c_resume(struct device *dev)
mtk_i2c_init_hw(i2c);
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
+
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
}
-#endif
static const struct dev_pm_ops mtk_i2c_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(NULL, mtk_i2c_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_i2c_suspend_noirq,
+ mtk_i2c_resume_noirq)
};
static struct platform_driver mtk_i2c_driver = {
@@ -931,8 +1549,8 @@ static struct platform_driver mtk_i2c_driver = {
.remove = mtk_i2c_remove,
.driver = {
.name = I2C_DRV_NAME,
- .pm = &mtk_i2c_pm,
- .of_match_table = of_match_ptr(mtk_i2c_of_match),
+ .pm = pm_sleep_ptr(&mtk_i2c_pm),
+ .of_match_table = mtk_i2c_of_match,
},
};
diff --git a/drivers/i2c/busses/i2c-mt7621.c b/drivers/i2c/busses/i2c-mt7621.c
new file mode 100644
index 000000000000..0a288c998419
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mt7621.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/i2c/busses/i2c-mt7621.c
+ *
+ * Copyright (C) 2013 Steven Liu <steven_liu@mediatek.com>
+ * Copyright (C) 2016 Michael Lee <igvtee@gmail.com>
+ * Copyright (C) 2018 Jan Breuer <jan.breuer@jaybee.cz>
+ *
+ * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus.
+ * (C) 2014 Sittisak <sittisaks@hotmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define REG_SM0CFG2_REG 0x28
+#define REG_SM0CTL0_REG 0x40
+#define REG_SM0CTL1_REG 0x44
+#define REG_SM0D0_REG 0x50
+#define REG_SM0D1_REG 0x54
+#define REG_PINTEN_REG 0x5c
+#define REG_PINTST_REG 0x60
+#define REG_PINTCL_REG 0x64
+
+/* REG_SM0CFG2_REG */
+#define SM0CFG2_IS_AUTOMODE BIT(0)
+
+/* REG_SM0CTL0_REG */
+#define SM0CTL0_ODRAIN BIT(31)
+#define SM0CTL0_CLK_DIV_MASK (0x7ff << 16)
+#define SM0CTL0_CLK_DIV_MAX 0x7ff
+#define SM0CTL0_CS_STATUS BIT(4)
+#define SM0CTL0_SCL_STATE BIT(3)
+#define SM0CTL0_SDA_STATE BIT(2)
+#define SM0CTL0_EN BIT(1)
+#define SM0CTL0_SCL_STRETCH BIT(0)
+
+/* REG_SM0CTL1_REG */
+#define SM0CTL1_ACK_MASK (0xff << 16)
+#define SM0CTL1_PGLEN_MASK (0x7 << 8)
+#define SM0CTL1_PGLEN(x) ((((x) - 1) << 8) & SM0CTL1_PGLEN_MASK)
+#define SM0CTL1_READ (5 << 4)
+#define SM0CTL1_READ_LAST (4 << 4)
+#define SM0CTL1_STOP (3 << 4)
+#define SM0CTL1_WRITE (2 << 4)
+#define SM0CTL1_START (1 << 4)
+#define SM0CTL1_MODE_MASK (0x7 << 4)
+#define SM0CTL1_TRI BIT(0)
+
+/* timeout waiting for I2C devices to respond */
+#define TIMEOUT_MS 1000
+
+struct mtk_i2c {
+ void __iomem *base;
+ struct device *dev;
+ struct i2c_adapter adap;
+ u32 bus_freq;
+ u32 clk_div;
+ u32 flags;
+ struct clk *clk;
+};
+
+static int mtk_i2c_wait_idle(struct mtk_i2c *i2c)
+{
+ int ret;
+ u32 val;
+
+ ret = readl_relaxed_poll_timeout(i2c->base + REG_SM0CTL1_REG,
+ val, !(val & SM0CTL1_TRI),
+ 10, TIMEOUT_MS * 1000);
+ if (ret)
+ dev_dbg(i2c->dev, "idle err(%d)\n", ret);
+
+ return ret;
+}
+
+static void mtk_i2c_reset(struct mtk_i2c *i2c)
+{
+ int ret;
+
+ ret = device_reset(i2c->adap.dev.parent);
+ if (ret)
+ dev_err(i2c->dev, "I2C reset failed!\n");
+
+ /*
+ * Don't set SM0CTL0_ODRAIN as its bit meaning is inverted. To
+ * configure open-drain mode, this bit needs to be cleared.
+ */
+ iowrite32(((i2c->clk_div << 16) & SM0CTL0_CLK_DIV_MASK) | SM0CTL0_EN |
+ SM0CTL0_SCL_STRETCH, i2c->base + REG_SM0CTL0_REG);
+ iowrite32(0, i2c->base + REG_SM0CFG2_REG);
+}
+
+static void mtk_i2c_dump_reg(struct mtk_i2c *i2c)
+{
+ dev_dbg(i2c->dev,
+ "SM0CFG2 %08x, SM0CTL0 %08x, SM0CTL1 %08x, SM0D0 %08x, SM0D1 %08x\n",
+ ioread32(i2c->base + REG_SM0CFG2_REG),
+ ioread32(i2c->base + REG_SM0CTL0_REG),
+ ioread32(i2c->base + REG_SM0CTL1_REG),
+ ioread32(i2c->base + REG_SM0D0_REG),
+ ioread32(i2c->base + REG_SM0D1_REG));
+}
+
+static int mtk_i2c_check_ack(struct mtk_i2c *i2c, u32 expected)
+{
+ u32 ack = readl_relaxed(i2c->base + REG_SM0CTL1_REG);
+ u32 ack_expected = (expected << 16) & SM0CTL1_ACK_MASK;
+
+ return ((ack & ack_expected) == ack_expected) ? 0 : -ENXIO;
+}
+
+static int mtk_i2c_start(struct mtk_i2c *i2c)
+{
+ iowrite32(SM0CTL1_START | SM0CTL1_TRI, i2c->base + REG_SM0CTL1_REG);
+ return mtk_i2c_wait_idle(i2c);
+}
+
+static int mtk_i2c_stop(struct mtk_i2c *i2c)
+{
+ iowrite32(SM0CTL1_STOP | SM0CTL1_TRI, i2c->base + REG_SM0CTL1_REG);
+ return mtk_i2c_wait_idle(i2c);
+}
+
+static int mtk_i2c_cmd(struct mtk_i2c *i2c, u32 cmd, int page_len)
+{
+ iowrite32(cmd | SM0CTL1_TRI | SM0CTL1_PGLEN(page_len),
+ i2c->base + REG_SM0CTL1_REG);
+ return mtk_i2c_wait_idle(i2c);
+}
+
+static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct mtk_i2c *i2c;
+ struct i2c_msg *pmsg;
+ u16 addr;
+ int i, j, ret, len, page_len;
+ u32 cmd;
+ u32 data[2];
+
+ i2c = i2c_get_adapdata(adap);
+
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+
+ /* wait hardware idle */
+ ret = mtk_i2c_wait_idle(i2c);
+ if (ret)
+ goto err_timeout;
+
+ /* start sequence */
+ ret = mtk_i2c_start(i2c);
+ if (ret)
+ goto err_timeout;
+
+ /* write address */
+ if (pmsg->flags & I2C_M_TEN) {
+ /* 10 bits address */
+ addr = i2c_10bit_addr_hi_from_msg(pmsg);
+ addr |= i2c_10bit_addr_lo_from_msg(pmsg) << 8;
+ len = 2;
+ } else {
+ /* 7 bits address */
+ addr = i2c_8bit_addr_from_msg(pmsg);
+ len = 1;
+ }
+ iowrite32(addr, i2c->base + REG_SM0D0_REG);
+ ret = mtk_i2c_cmd(i2c, SM0CTL1_WRITE, len);
+ if (ret)
+ goto err_timeout;
+
+ /* check address ACK */
+ if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
+ ret = mtk_i2c_check_ack(i2c, BIT(0));
+ if (ret)
+ goto err_ack;
+ }
+
+ /* transfer data */
+ for (len = pmsg->len, j = 0; len > 0; len -= 8, j += 8) {
+ page_len = (len >= 8) ? 8 : len;
+
+ if (pmsg->flags & I2C_M_RD) {
+ cmd = (len > 8) ?
+ SM0CTL1_READ : SM0CTL1_READ_LAST;
+ } else {
+ memcpy(data, &pmsg->buf[j], page_len);
+ iowrite32(data[0], i2c->base + REG_SM0D0_REG);
+ iowrite32(data[1], i2c->base + REG_SM0D1_REG);
+ cmd = SM0CTL1_WRITE;
+ }
+
+ ret = mtk_i2c_cmd(i2c, cmd, page_len);
+ if (ret)
+ goto err_timeout;
+
+ if (pmsg->flags & I2C_M_RD) {
+ data[0] = ioread32(i2c->base + REG_SM0D0_REG);
+ data[1] = ioread32(i2c->base + REG_SM0D1_REG);
+ memcpy(&pmsg->buf[j], data, page_len);
+ } else {
+ if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
+ ret = mtk_i2c_check_ack(i2c,
+ (1 << page_len)
+ - 1);
+ if (ret)
+ goto err_ack;
+ }
+ }
+ }
+ }
+
+ ret = mtk_i2c_stop(i2c);
+ if (ret)
+ goto err_timeout;
+
+ /* the return value is number of executed messages */
+ return i;
+
+err_ack:
+ ret = mtk_i2c_stop(i2c);
+ if (ret)
+ goto err_timeout;
+ return -ENXIO;
+
+err_timeout:
+ mtk_i2c_dump_reg(i2c);
+ mtk_i2c_reset(i2c);
+ return ret;
+}
+
+static u32 mtk_i2c_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+static const struct i2c_algorithm mtk_i2c_algo = {
+ .xfer = mtk_i2c_xfer,
+ .functionality = mtk_i2c_func,
+};
+
+static const struct of_device_id i2c_mtk_dt_ids[] = {
+ { .compatible = "mediatek,mt7621-i2c" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, i2c_mtk_dt_ids);
+
+static void mtk_i2c_init(struct mtk_i2c *i2c)
+{
+ i2c->clk_div = clk_get_rate(i2c->clk) / i2c->bus_freq - 1;
+ if (i2c->clk_div < 99)
+ i2c->clk_div = 99;
+ if (i2c->clk_div > SM0CTL0_CLK_DIV_MAX)
+ i2c->clk_div = SM0CTL0_CLK_DIV_MAX;
+
+ mtk_i2c_reset(i2c);
+}
+
+static int mtk_i2c_probe(struct platform_device *pdev)
+{
+ struct mtk_i2c *i2c;
+ struct i2c_adapter *adap;
+ int ret;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct mtk_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk)) {
+ dev_err(&pdev->dev, "Failed to enable clock\n");
+ return PTR_ERR(i2c->clk);
+ }
+
+ i2c->dev = &pdev->dev;
+
+ if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &i2c->bus_freq))
+ i2c->bus_freq = I2C_MAX_STANDARD_MODE_FREQ;
+
+ if (i2c->bus_freq == 0) {
+ dev_warn(i2c->dev, "clock-frequency 0 not supported\n");
+ return -EINVAL;
+ }
+
+ adap = &i2c->adap;
+ adap->owner = THIS_MODULE;
+ adap->algo = &mtk_i2c_algo;
+ adap->retries = 3;
+ adap->dev.parent = &pdev->dev;
+ i2c_set_adapdata(adap, i2c);
+ adap->dev.of_node = pdev->dev.of_node;
+ strscpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
+
+ platform_set_drvdata(pdev, i2c);
+
+ mtk_i2c_init(i2c);
+
+ ret = i2c_add_adapter(adap);
+ if (ret < 0)
+ return ret;
+
+ dev_info(&pdev->dev, "clock %u kHz\n", i2c->bus_freq / 1000);
+
+ return 0;
+}
+
+static void mtk_i2c_remove(struct platform_device *pdev)
+{
+ struct mtk_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+}
+
+static struct platform_driver mtk_i2c_driver = {
+ .probe = mtk_i2c_probe,
+ .remove = mtk_i2c_remove,
+ .driver = {
+ .name = "i2c-mt7621",
+ .of_match_table = i2c_mtk_dt_ids,
+ },
+};
+
+module_platform_driver(mtk_i2c_driver);
+
+MODULE_AUTHOR("Steven Liu");
+MODULE_DESCRIPTION("MT7621 I2C host driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:MT7621-I2C");
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index a5a95ea5b81a..1acba628e16c 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -17,16 +17,16 @@
#include <linux/interrupt.h>
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
-#define MV64XXX_I2C_ADDR_ADDR(val) ((val & 0x7f) << 1)
#define MV64XXX_I2C_BAUD_DIV_N(val) (val & 0x7)
#define MV64XXX_I2C_BAUD_DIV_M(val) ((val & 0xf) << 3)
@@ -88,8 +88,8 @@ enum {
MV64XXX_I2C_STATE_WAITING_FOR_RESTART,
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
- MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
- MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+ MV64XXX_I2C_STATE_WAITING_FOR_TARGET_ACK,
+ MV64XXX_I2C_STATE_WAITING_FOR_TARGET_DATA,
};
/* Driver actions */
@@ -147,6 +147,8 @@ struct mv64xxx_i2c_data {
bool irq_clear_inverted;
/* Clk div is 2 to the power n, not 2 to the power n + 1 */
bool clk_n_base_0;
+ struct i2c_bus_recovery_info rinfo;
+ bool atomic;
};
static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
@@ -173,19 +175,17 @@ static void
mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
struct i2c_msg *msg)
{
- u32 dir = 0;
-
drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
- MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+ MV64XXX_I2C_REG_CONTROL_TWSIEN;
- if (msg->flags & I2C_M_RD)
- dir = 1;
+ if (!drv_data->atomic)
+ drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_INTEN;
if (msg->flags & I2C_M_TEN) {
- drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
- drv_data->addr2 = (u32)msg->addr & 0xff;
+ drv_data->addr1 = i2c_10bit_addr_hi_from_msg(msg);
+ drv_data->addr2 = i2c_10bit_addr_lo_from_msg(msg);
} else {
- drv_data->addr1 = MV64XXX_I2C_ADDR_ADDR((u32)msg->addr) | dir;
+ drv_data->addr1 = i2c_8bit_addr_from_msg(msg);
drv_data->addr2 = 0;
}
}
@@ -218,6 +218,10 @@ mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
writel(0, drv_data->reg_base + drv_data->reg_offsets.ext_addr);
writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + drv_data->reg_offsets.control);
+
+ if (drv_data->errata_delay)
+ udelay(5);
+
drv_data->state = MV64XXX_I2C_STATE_IDLE;
}
@@ -251,7 +255,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
break;
}
- /* FALLTHRU */
+ fallthrough;
case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
if ((drv_data->bytes_left == 0)
@@ -269,7 +273,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
drv_data->state =
- MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+ MV64XXX_I2C_STATE_WAITING_FOR_TARGET_ACK;
drv_data->bytes_left--;
}
break;
@@ -282,14 +286,14 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
break;
}
- /* FALLTHRU */
+ fallthrough;
case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
if (drv_data->bytes_left == 0) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
break;
}
- /* FALLTHRU */
+ fallthrough;
case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
@@ -297,7 +301,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
drv_data->bytes_left--;
}
- drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+ drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_TARGET_DATA;
if ((drv_data->bytes_left == 1) || drv_data->aborting)
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
@@ -325,7 +329,8 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
drv_data->msg->flags);
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
mv64xxx_i2c_hw_init(drv_data);
- drv_data->rc = -EIO;
+ i2c_recover_bus(&drv_data->adapter);
+ drv_data->rc = -EAGAIN;
}
}
@@ -401,7 +406,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
drv_data->msg->buf[drv_data->byte_posn++] =
readl(drv_data->reg_base + drv_data->reg_offsets.data);
- drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+ if (!drv_data->atomic)
+ drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + drv_data->reg_offsets.control);
drv_data->block = 0;
@@ -417,10 +423,10 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
"mv64xxx_i2c_do_action: Invalid action: %d\n",
drv_data->action);
drv_data->rc = -EIO;
-
- /* FALLTHRU */
+ fallthrough;
case MV64XXX_I2C_ACTION_SEND_STOP:
- drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+ if (!drv_data->atomic)
+ drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + drv_data->reg_offsets.control);
drv_data->block = 0;
@@ -497,17 +503,27 @@ static irqreturn_t
mv64xxx_i2c_intr(int irq, void *dev_id)
{
struct mv64xxx_i2c_data *drv_data = dev_id;
- unsigned long flags;
u32 status;
irqreturn_t rc = IRQ_NONE;
- spin_lock_irqsave(&drv_data->lock, flags);
+ spin_lock(&drv_data->lock);
if (drv_data->offload_enabled)
rc = mv64xxx_i2c_intr_offload(drv_data);
while (readl(drv_data->reg_base + drv_data->reg_offsets.control) &
MV64XXX_I2C_REG_CONTROL_IFLG) {
+ /*
+ * It seems that sometime the controller updates the status
+ * register only after it asserts IFLG in control register.
+ * This may result in weird bugs when in atomic mode. A delay
+ * of 100 ns before reading the status register solves this
+ * issue. This bug does not seem to appear when using
+ * interrupts.
+ */
+ if (drv_data->atomic)
+ ndelay(100);
+
status = readl(drv_data->reg_base + drv_data->reg_offsets.status);
mv64xxx_i2c_fsm(drv_data, status);
mv64xxx_i2c_do_action(drv_data);
@@ -518,7 +534,7 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
rc = IRQ_HANDLED;
}
- spin_unlock_irqrestore(&drv_data->lock, flags);
+ spin_unlock(&drv_data->lock);
return rc;
}
@@ -563,11 +579,23 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
"time_left: %d\n", drv_data->block,
(int)time_left);
mv64xxx_i2c_hw_init(drv_data);
+ i2c_recover_bus(&drv_data->adapter);
}
} else
spin_unlock_irqrestore(&drv_data->lock, flags);
}
+static void mv64xxx_i2c_wait_polling(struct mv64xxx_i2c_data *drv_data)
+{
+ ktime_t timeout = ktime_add_ms(ktime_get(), drv_data->adapter.timeout);
+
+ while (READ_ONCE(drv_data->block) &&
+ ktime_compare(ktime_get(), timeout) < 0) {
+ udelay(5);
+ mv64xxx_i2c_intr(0, drv_data);
+ }
+}
+
static int
mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
int is_last)
@@ -583,7 +611,11 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
mv64xxx_i2c_send_start(drv_data);
spin_unlock_irqrestore(&drv_data->lock, flags);
- mv64xxx_i2c_wait_for_completion(drv_data);
+ if (!drv_data->atomic)
+ mv64xxx_i2c_wait_for_completion(drv_data);
+ else
+ mv64xxx_i2c_wait_polling(drv_data);
+
return drv_data->rc;
}
@@ -710,16 +742,20 @@ mv64xxx_i2c_functionality(struct i2c_adapter *adap)
}
static int
-mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
int rc, ret = num;
+ rc = pm_runtime_resume_and_get(&adap->dev);
+ if (rc)
+ return rc;
+
BUG_ON(drv_data->msgs != NULL);
drv_data->msgs = msgs;
drv_data->num_msgs = num;
- if (mv64xxx_i2c_can_offload(drv_data))
+ if (mv64xxx_i2c_can_offload(drv_data) && !drv_data->atomic)
rc = mv64xxx_i2c_offload_xfer(drv_data);
else
rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1);
@@ -730,11 +766,32 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
drv_data->num_msgs = 0;
drv_data->msgs = NULL;
+ pm_runtime_put_autosuspend(&adap->dev);
+
return ret;
}
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+
+ drv_data->atomic = 0;
+ return mv64xxx_i2c_xfer_core(adap, msgs, num);
+}
+
+static int mv64xxx_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+
+ drv_data->atomic = 1;
+ return mv64xxx_i2c_xfer_core(adap, msgs, num);
+}
+
static const struct i2c_algorithm mv64xxx_i2c_algo = {
- .master_xfer = mv64xxx_i2c_xfer,
+ .xfer = mv64xxx_i2c_xfer,
+ .xfer_atomic = mv64xxx_i2c_xfer_atomic,
.functionality = mv64xxx_i2c_functionality,
};
@@ -794,7 +851,7 @@ static int
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
struct device *dev)
{
- const struct of_device_id *device;
+ const struct mv64xxx_i2c_regs *data;
struct device_node *np = dev->of_node;
u32 bus_freq, tclk;
int rc = 0;
@@ -803,14 +860,14 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
* need to know tclk in order to calculate bus clock
* factors.
*/
- if (IS_ERR(drv_data->clk)) {
+ if (!drv_data->clk) {
rc = -ENODEV;
goto out;
}
tclk = clk_get_rate(drv_data->clk);
if (of_property_read_u32(np, "clock-frequency", &bus_freq))
- bus_freq = 100000; /* 100kHz by default */
+ bus_freq = I2C_MAX_STANDARD_MODE_FREQ; /* 100kHz by default */
if (of_device_is_compatible(np, "allwinner,sun4i-a10-i2c") ||
of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
@@ -826,18 +883,17 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
rc = PTR_ERR(drv_data->rstc);
goto out;
}
- reset_control_deassert(drv_data->rstc);
/* Its not yet defined how timeouts will be specified in device tree.
* So hard code the value to 1 second.
*/
drv_data->adapter.timeout = HZ;
- device = of_match_device(mv64xxx_i2c_of_match_table, dev);
- if (!device)
+ data = device_get_match_data(dev);
+ if (!data)
return -ENODEV;
- memcpy(&drv_data->reg_offsets, device->data, sizeof(drv_data->reg_offsets));
+ memcpy(&drv_data->reg_offsets, data, sizeof(drv_data->reg_offsets));
/*
* For controllers embedded in new SoCs activate the
@@ -846,14 +902,14 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
if (of_device_is_compatible(np, "marvell,mv78230-i2c")) {
drv_data->offload_enabled = true;
/* The delay is only needed in standard mode (100kHz) */
- if (bus_freq <= 100000)
+ if (bus_freq <= I2C_MAX_STANDARD_MODE_FREQ)
drv_data->errata_delay = true;
}
if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) {
drv_data->offload_enabled = false;
/* The delay is only needed in standard mode (100kHz) */
- if (bus_freq <= 100000)
+ if (bus_freq <= I2C_MAX_STANDARD_MODE_FREQ)
drv_data->errata_delay = true;
}
@@ -872,12 +928,56 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
}
#endif /* CONFIG_OF */
+static int mv64xxx_i2c_init_recovery_info(struct mv64xxx_i2c_data *drv_data,
+ struct device *dev)
+{
+ struct i2c_bus_recovery_info *rinfo = &drv_data->rinfo;
+
+ rinfo->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(rinfo->pinctrl)) {
+ if (PTR_ERR(rinfo->pinctrl) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_info(dev, "can't get pinctrl, bus recovery not supported\n");
+ return PTR_ERR(rinfo->pinctrl);
+ } else if (!rinfo->pinctrl) {
+ return -ENODEV;
+ }
+
+ drv_data->adapter.bus_recovery_info = rinfo;
+ return 0;
+}
+
+static int
+mv64xxx_i2c_runtime_suspend(struct device *dev)
+{
+ struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
+
+ reset_control_assert(drv_data->rstc);
+ clk_disable_unprepare(drv_data->reg_clk);
+ clk_disable_unprepare(drv_data->clk);
+
+ return 0;
+}
+
+static int
+mv64xxx_i2c_runtime_resume(struct device *dev)
+{
+ struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
+
+ clk_prepare_enable(drv_data->clk);
+ clk_prepare_enable(drv_data->reg_clk);
+ reset_control_reset(drv_data->rstc);
+
+ mv64xxx_i2c_hw_init(drv_data);
+
+ return 0;
+}
+
static int
mv64xxx_i2c_probe(struct platform_device *pd)
{
struct mv64xxx_i2c_data *drv_data;
struct mv64xxx_i2c_pdata *pdata = dev_get_platdata(&pd->dev);
- struct resource *r;
int rc;
if ((!pdata && !pd->dev.of_node))
@@ -888,12 +988,11 @@ mv64xxx_i2c_probe(struct platform_device *pd)
if (!drv_data)
return -ENOMEM;
- r = platform_get_resource(pd, IORESOURCE_MEM, 0);
- drv_data->reg_base = devm_ioremap_resource(&pd->dev, r);
+ drv_data->reg_base = devm_platform_ioremap_resource(pd, 0);
if (IS_ERR(drv_data->reg_base))
return PTR_ERR(drv_data->reg_base);
- strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+ strscpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
sizeof(drv_data->adapter.name));
init_waitqueue_head(&drv_data->waitq);
@@ -901,19 +1000,22 @@ mv64xxx_i2c_probe(struct platform_device *pd)
/* Not all platforms have clocks */
drv_data->clk = devm_clk_get(&pd->dev, NULL);
- if (IS_ERR(drv_data->clk) && PTR_ERR(drv_data->clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (!IS_ERR(drv_data->clk))
- clk_prepare_enable(drv_data->clk);
+ if (IS_ERR(drv_data->clk)) {
+ if (PTR_ERR(drv_data->clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ drv_data->clk = NULL;
+ }
drv_data->reg_clk = devm_clk_get(&pd->dev, "reg");
- if (IS_ERR(drv_data->reg_clk) &&
- PTR_ERR(drv_data->reg_clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (!IS_ERR(drv_data->reg_clk))
- clk_prepare_enable(drv_data->reg_clk);
+ if (IS_ERR(drv_data->reg_clk)) {
+ if (PTR_ERR(drv_data->reg_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ drv_data->reg_clk = NULL;
+ }
drv_data->irq = platform_get_irq(pd, 0);
+ if (drv_data->irq < 0)
+ return drv_data->irq;
if (pdata) {
drv_data->freq_m = pdata->freq_m;
@@ -924,13 +1026,13 @@ mv64xxx_i2c_probe(struct platform_device *pd)
} else if (pd->dev.of_node) {
rc = mv64xxx_of_config(drv_data, &pd->dev);
if (rc)
- goto exit_clk;
- }
- if (drv_data->irq < 0) {
- rc = drv_data->irq;
- goto exit_reset;
+ return rc;
}
+ rc = mv64xxx_i2c_init_recovery_info(drv_data, &pd->dev);
+ if (rc == -EPROBE_DEFER)
+ return rc;
+
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
@@ -940,7 +1042,14 @@ mv64xxx_i2c_probe(struct platform_device *pd)
platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
- mv64xxx_i2c_hw_init(drv_data);
+ pm_runtime_set_autosuspend_delay(&pd->dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(&pd->dev);
+ pm_runtime_enable(&pd->dev);
+ if (!pm_runtime_enabled(&pd->dev)) {
+ rc = mv64xxx_i2c_runtime_resume(&pd->dev);
+ if (rc)
+ goto exit_disable_pm;
+ }
rc = request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
MV64XXX_I2C_CTLR_NAME, drv_data);
@@ -948,7 +1057,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
dev_err(&drv_data->adapter.dev,
"mv64xxx: Can't register intr handler irq%d: %d\n",
drv_data->irq, rc);
- goto exit_reset;
+ goto exit_disable_pm;
} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
dev_err(&drv_data->adapter.dev,
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
@@ -959,54 +1068,39 @@ mv64xxx_i2c_probe(struct platform_device *pd)
exit_free_irq:
free_irq(drv_data->irq, drv_data);
-exit_reset:
- reset_control_assert(drv_data->rstc);
-exit_clk:
- clk_disable_unprepare(drv_data->reg_clk);
- clk_disable_unprepare(drv_data->clk);
+exit_disable_pm:
+ pm_runtime_disable(&pd->dev);
+ if (!pm_runtime_status_suspended(&pd->dev))
+ mv64xxx_i2c_runtime_suspend(&pd->dev);
return rc;
}
-static int
-mv64xxx_i2c_remove(struct platform_device *dev)
+static void
+mv64xxx_i2c_remove(struct platform_device *pd)
{
- struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(dev);
+ struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pd);
i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data);
- reset_control_assert(drv_data->rstc);
- clk_disable_unprepare(drv_data->reg_clk);
- clk_disable_unprepare(drv_data->clk);
-
- return 0;
+ pm_runtime_disable(&pd->dev);
+ if (!pm_runtime_status_suspended(&pd->dev))
+ mv64xxx_i2c_runtime_suspend(&pd->dev);
}
-#ifdef CONFIG_PM
-static int mv64xxx_i2c_resume(struct device *dev)
-{
- struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
-
- mv64xxx_i2c_hw_init(drv_data);
-
- return 0;
-}
-
-static const struct dev_pm_ops mv64xxx_i2c_pm = {
- .resume = mv64xxx_i2c_resume,
+static const struct dev_pm_ops mv64xxx_i2c_pm_ops = {
+ SET_RUNTIME_PM_OPS(mv64xxx_i2c_runtime_suspend,
+ mv64xxx_i2c_runtime_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
-#define mv64xxx_i2c_pm_ops (&mv64xxx_i2c_pm)
-#else
-#define mv64xxx_i2c_pm_ops NULL
-#endif
-
static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
- .remove = mv64xxx_i2c_remove,
+ .remove = mv64xxx_i2c_remove,
.driver = {
.name = MV64XXX_I2C_CTLR_NAME,
- .pm = mv64xxx_i2c_pm_ops,
+ .pm = &mv64xxx_i2c_pm_ops,
.of_match_table = mv64xxx_i2c_of_match_table,
},
};
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 7d79317a1046..08c9091a1e35 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -22,9 +22,9 @@
#include <linux/io.h>
#include <linux/stmp_device.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/dma/mxs-dma.h>
#define DRIVER_NAME "mxs-i2c"
@@ -170,7 +170,7 @@ static void mxs_i2c_dma_irq_callback(void *param)
}
static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msg, uint32_t flags)
+ struct i2c_msg *msg, u8 *buf, uint32_t flags)
{
struct dma_async_tx_descriptor *desc;
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
@@ -200,7 +200,8 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
dma_map_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[0], 1,
DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ DMA_PREP_INTERRUPT |
+ MXS_DMA_CTRL_WAIT4END);
if (!desc) {
dev_err(i2c->dev,
"Failed to get DMA data write descriptor.\n");
@@ -224,11 +225,12 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
}
/* Queue the DMA data transfer. */
- sg_init_one(&i2c->sg_io[1], msg->buf, msg->len);
+ sg_init_one(&i2c->sg_io[1], buf, msg->len);
dma_map_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE);
desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[1], 1,
DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ DMA_PREP_INTERRUPT |
+ MXS_DMA_CTRL_WAIT4END);
if (!desc) {
dev_err(i2c->dev,
"Failed to get DMA data write descriptor.\n");
@@ -256,11 +258,12 @@ static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap,
/* Queue the DMA data transfer. */
sg_init_table(i2c->sg_io, 2);
sg_set_buf(&i2c->sg_io[0], &i2c->addr_data, 1);
- sg_set_buf(&i2c->sg_io[1], msg->buf, msg->len);
+ sg_set_buf(&i2c->sg_io[1], buf, msg->len);
dma_map_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
desc = dmaengine_prep_slave_sg(i2c->dmach, i2c->sg_io, 2,
DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ DMA_PREP_INTERRUPT |
+ MXS_DMA_CTRL_WAIT4END);
if (!desc) {
dev_err(i2c->dev,
"Failed to get DMA data write descriptor.\n");
@@ -286,14 +289,14 @@ read_init_dma_fail:
select_init_dma_fail:
dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
select_init_pio_fail:
- dmaengine_terminate_all(i2c->dmach);
+ dmaengine_terminate_sync(i2c->dmach);
return -EINVAL;
/* Write failpath. */
write_init_dma_fail:
dma_unmap_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
write_init_pio_fail:
- dmaengine_terminate_all(i2c->dmach);
+ dmaengine_terminate_sync(i2c->dmach);
return -EINVAL;
}
@@ -559,6 +562,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
int ret;
int flags;
+ u8 *dma_buf;
int use_pio = 0;
unsigned long time_left;
@@ -584,13 +588,20 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
if (ret && (ret != -ENXIO))
mxs_i2c_reset(i2c);
} else {
+ dma_buf = i2c_get_dma_safe_msg_buf(msg, 1);
+ if (!dma_buf)
+ return -ENOMEM;
+
reinit_completion(&i2c->cmd_complete);
- ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
- if (ret)
+ ret = mxs_i2c_dma_setup_xfer(adap, msg, dma_buf, flags);
+ if (ret) {
+ i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
return ret;
+ }
time_left = wait_for_completion_timeout(&i2c->cmd_complete,
msecs_to_jiffies(1000));
+ i2c_put_dma_safe_msg_buf(dma_buf, msg, true);
if (!time_left)
goto timeout;
@@ -676,7 +687,7 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
}
static const struct i2c_algorithm mxs_i2c_algo = {
- .master_xfer = mxs_i2c_xfer,
+ .xfer = mxs_i2c_xfer,
.functionality = mxs_i2c_func,
};
@@ -731,7 +742,7 @@ static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed)
* This is compensated for by subtracting the respective constants
* from the values written to the timing registers.
*/
- if (speed > 100000) {
+ if (speed > I2C_MAX_STANDARD_MODE_FREQ) {
/* fast mode */
low_count = DIV_ROUND_CLOSEST(divider * 13, (13 + 6));
high_count = DIV_ROUND_CLOSEST(divider * 6, (13 + 6));
@@ -769,7 +780,7 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
ret = of_property_read_u32(node, "clock-frequency", &speed);
if (ret) {
dev_warn(dev, "No I2C speed selected, using 100kHz\n");
- speed = 100000;
+ speed = I2C_MAX_STANDARD_MODE_FREQ;
}
mxs_i2c_derive_timing(i2c, speed);
@@ -777,45 +788,27 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
return 0;
}
-static const struct platform_device_id mxs_i2c_devtype[] = {
- {
- .name = "imx23-i2c",
- .driver_data = MXS_I2C_V1,
- }, {
- .name = "imx28-i2c",
- .driver_data = MXS_I2C_V2,
- }, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, mxs_i2c_devtype);
-
static const struct of_device_id mxs_i2c_dt_ids[] = {
- { .compatible = "fsl,imx23-i2c", .data = &mxs_i2c_devtype[0], },
- { .compatible = "fsl,imx28-i2c", .data = &mxs_i2c_devtype[1], },
+ { .compatible = "fsl,imx23-i2c", .data = (void *)MXS_I2C_V1, },
+ { .compatible = "fsl,imx28-i2c", .data = (void *)MXS_I2C_V2, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_i2c_dt_ids);
static int mxs_i2c_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id =
- of_match_device(mxs_i2c_dt_ids, &pdev->dev);
struct device *dev = &pdev->dev;
struct mxs_i2c_dev *i2c;
struct i2c_adapter *adap;
- struct resource *res;
int err, irq;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
- if (of_id) {
- const struct platform_device_id *device_id = of_id->data;
- i2c->dev_type = device_id->driver_data;
- }
+ i2c->dev_type = (uintptr_t)of_device_get_match_data(&pdev->dev);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, res);
+ i2c->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
@@ -838,10 +831,10 @@ static int mxs_i2c_probe(struct platform_device *pdev)
}
/* Setup the DMA */
- i2c->dmach = dma_request_slave_channel(dev, "rx-tx");
- if (!i2c->dmach) {
- dev_err(dev, "Failed to request dma\n");
- return -ENODEV;
+ i2c->dmach = dma_request_chan(dev, "rx-tx");
+ if (IS_ERR(i2c->dmach)) {
+ return dev_err_probe(dev, PTR_ERR(i2c->dmach),
+ "Failed to request dma\n");
}
platform_set_drvdata(pdev, i2c);
@@ -852,7 +845,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
return err;
adap = &i2c->adapter;
- strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
+ strscpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
adap->owner = THIS_MODULE;
adap->algo = &mxs_i2c_algo;
adap->quirks = &mxs_i2c_quirks;
@@ -870,7 +863,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int mxs_i2c_remove(struct platform_device *pdev)
+static void mxs_i2c_remove(struct platform_device *pdev)
{
struct mxs_i2c_dev *i2c = platform_get_drvdata(pdev);
@@ -880,8 +873,6 @@ static int mxs_i2c_remove(struct platform_device *pdev)
dma_release_channel(i2c->dmach);
writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET);
-
- return 0;
}
static struct platform_driver mxs_i2c_driver = {
diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
new file mode 100644
index 000000000000..1413ab6f9462
--- /dev/null
+++ b/drivers/i2c/busses/i2c-nct6694.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NCT6694 I2C adapter driver based on USB interface.
+ *
+ * Copyright (C) 2025 Nuvoton Technology Corp.
+ */
+
+#include <linux/i2c.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+/*
+ * USB command module type for NCT6694 I2C controller.
+ * This defines the module type used for communication with the NCT6694
+ * I2C controller over the USB interface.
+ */
+#define NCT6694_I2C_MOD 0x03
+
+/* Command 00h - I2C Deliver */
+#define NCT6694_I2C_DELIVER 0x00
+#define NCT6694_I2C_DELIVER_SEL 0x00
+
+#define NCT6694_I2C_MAX_XFER_SIZE 64
+#define NCT6694_I2C_MAX_DEVS 6
+
+static unsigned char br_reg[NCT6694_I2C_MAX_DEVS] = {[0 ... (NCT6694_I2C_MAX_DEVS - 1)] = 0xFF};
+
+module_param_array(br_reg, byte, NULL, 0644);
+MODULE_PARM_DESC(br_reg,
+ "I2C Baudrate register per adapter: (0=25K, 1=50K, 2=100K, 3=200K, 4=400K, 5=800K, 6=1M), default=2");
+
+enum nct6694_i2c_baudrate {
+ NCT6694_I2C_BR_25K = 0,
+ NCT6694_I2C_BR_50K,
+ NCT6694_I2C_BR_100K,
+ NCT6694_I2C_BR_200K,
+ NCT6694_I2C_BR_400K,
+ NCT6694_I2C_BR_800K,
+ NCT6694_I2C_BR_1M
+};
+
+struct __packed nct6694_i2c_deliver {
+ u8 port;
+ u8 br;
+ u8 addr;
+ u8 w_cnt;
+ u8 r_cnt;
+ u8 rsv[11];
+ u8 write_data[NCT6694_I2C_MAX_XFER_SIZE];
+ u8 read_data[NCT6694_I2C_MAX_XFER_SIZE];
+};
+
+struct nct6694_i2c_data {
+ struct device *dev;
+ struct nct6694 *nct6694;
+ struct i2c_adapter adapter;
+ struct nct6694_i2c_deliver deliver;
+ unsigned char port;
+ unsigned char br;
+};
+
+static int nct6694_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct nct6694_i2c_data *data = adap->algo_data;
+ struct nct6694_i2c_deliver *deliver = &data->deliver;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_I2C_MOD,
+ .cmd = NCT6694_I2C_DELIVER,
+ .sel = NCT6694_I2C_DELIVER_SEL,
+ .len = cpu_to_le16(sizeof(*deliver))
+ };
+ int ret, i;
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *msg_temp = &msgs[i];
+
+ memset(deliver, 0, sizeof(*deliver));
+
+ deliver->port = data->port;
+ deliver->br = data->br;
+ deliver->addr = i2c_8bit_addr_from_msg(msg_temp);
+ if (msg_temp->flags & I2C_M_RD) {
+ deliver->r_cnt = msg_temp->len;
+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);
+ if (ret < 0)
+ return ret;
+
+ memcpy(msg_temp->buf, deliver->read_data, msg_temp->len);
+ } else {
+ deliver->w_cnt = msg_temp->len;
+ memcpy(deliver->write_data, msg_temp->buf, msg_temp->len);
+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return num;
+}
+
+static u32 nct6694_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_adapter_quirks nct6694_i2c_quirks = {
+ .max_read_len = NCT6694_I2C_MAX_XFER_SIZE,
+ .max_write_len = NCT6694_I2C_MAX_XFER_SIZE,
+};
+
+static const struct i2c_algorithm nct6694_i2c_algo = {
+ .xfer = nct6694_i2c_xfer,
+ .functionality = nct6694_i2c_func,
+};
+
+static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data)
+{
+ if (data->port >= NCT6694_I2C_MAX_DEVS) {
+ dev_err(data->dev, "Invalid I2C port index %d\n", data->port);
+ return -EINVAL;
+ }
+
+ if (br_reg[data->port] > NCT6694_I2C_BR_1M) {
+ dev_warn(data->dev, "Invalid baudrate %d for I2C%d, using 100K\n",
+ br_reg[data->port], data->port);
+ br_reg[data->port] = NCT6694_I2C_BR_100K;
+ }
+
+ data->br = br_reg[data->port];
+
+ return 0;
+}
+
+static void nct6694_i2c_ida_free(void *d)
+{
+ struct nct6694_i2c_data *data = d;
+ struct nct6694 *nct6694 = data->nct6694;
+
+ ida_free(&nct6694->i2c_ida, data->port);
+}
+
+static int nct6694_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
+ struct nct6694_i2c_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+ data->nct6694 = nct6694;
+
+ ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+ data->port = ret;
+
+ ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data);
+ if (ret)
+ return ret;
+
+ ret = nct6694_i2c_set_baudrate(data);
+ if (ret)
+ return ret;
+
+ sprintf(data->adapter.name, "NCT6694 I2C Adapter %d", data->port);
+ data->adapter.owner = THIS_MODULE;
+ data->adapter.algo = &nct6694_i2c_algo;
+ data->adapter.quirks = &nct6694_i2c_quirks;
+ data->adapter.dev.parent = dev;
+ data->adapter.algo_data = data;
+
+ platform_set_drvdata(pdev, data);
+
+ return devm_i2c_add_adapter(dev, &data->adapter);
+}
+
+static struct platform_driver nct6694_i2c_driver = {
+ .driver = {
+ .name = "nct6694-i2c",
+ },
+ .probe = nct6694_i2c_probe,
+};
+
+module_platform_driver(nct6694_i2c_driver);
+
+MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nct6694-i2c");
diff --git a/drivers/i2c/busses/i2c-nforce2-s4985.c b/drivers/i2c/busses/i2c-nforce2-s4985.c
deleted file mode 100644
index 58a0fbf0e074..000000000000
--- a/drivers/i2c/busses/i2c-nforce2-s4985.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard
- *
- * Copyright (C) 2008 Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-/*
- * We select the channels by sending commands to the Philips
- * PCA9556 chip at I2C address 0x18. The main adapter is used for
- * the non-multiplexed part of the bus, and 4 virtual adapters
- * are defined for the multiplexed addresses: 0x50-0x53 (memory
- * module EEPROM) located on channels 1-4. We define one virtual
- * adapter per CPU, which corresponds to one multiplexed channel:
- * CPU0: virtual adapter 1, channel 1
- * CPU1: virtual adapter 2, channel 2
- * CPU2: virtual adapter 3, channel 3
- * CPU3: virtual adapter 4, channel 4
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-
-extern struct i2c_adapter *nforce2_smbus;
-
-static struct i2c_adapter *s4985_adapter;
-static struct i2c_algorithm *s4985_algo;
-
-/* Wrapper access functions for multiplexed SMBus */
-static DEFINE_MUTEX(nforce2_lock);
-
-static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data)
-{
- int error;
-
- /* We exclude the multiplexed addresses */
- if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
- || addr == 0x18)
- return -ENXIO;
-
- mutex_lock(&nforce2_lock);
- error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
- command, size, data);
- mutex_unlock(&nforce2_lock);
-
- return error;
-}
-
-/* We remember the last used channels combination so as to only switch
- channels when it is really needed. This greatly reduces the SMBus
- overhead, but also assumes that nobody will be writing to the PCA9556
- in our back. */
-static u8 last_channels;
-
-static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data,
- u8 channels)
-{
- int error;
-
- /* We exclude the non-multiplexed addresses */
- if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
- return -ENXIO;
-
- mutex_lock(&nforce2_lock);
- if (last_channels != channels) {
- union i2c_smbus_data mplxdata;
- mplxdata.byte = channels;
-
- error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,
- I2C_SMBUS_WRITE, 0x01,
- I2C_SMBUS_BYTE_DATA,
- &mplxdata);
- if (error)
- goto UNLOCK;
- last_channels = channels;
- }
- error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
- command, size, data);
-
-UNLOCK:
- mutex_unlock(&nforce2_lock);
- return error;
-}
-
-static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data)
-{
- /* CPU0: channel 1 enabled */
- return nforce2_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x02);
-}
-
-static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data)
-{
- /* CPU1: channel 2 enabled */
- return nforce2_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x04);
-}
-
-static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data)
-{
- /* CPU2: channel 3 enabled */
- return nforce2_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x08);
-}
-
-static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data *data)
-{
- /* CPU3: channel 4 enabled */
- return nforce2_access_channel(adap, addr, flags, read_write, command,
- size, data, 0x10);
-}
-
-static int __init nforce2_s4985_init(void)
-{
- int i, error;
- union i2c_smbus_data ioconfig;
-
- if (!nforce2_smbus)
- return -ENODEV;
-
- /* Configure the PCA9556 multiplexer */
- ioconfig.byte = 0x00; /* All I/O to output mode */
- error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
- I2C_SMBUS_BYTE_DATA, &ioconfig);
- if (error) {
- dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");
- error = -EIO;
- goto ERROR0;
- }
-
- /* Unregister physical bus */
- i2c_del_adapter(nforce2_smbus);
-
- printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");
- /* Define the 5 virtual adapters and algorithms structures */
- s4985_adapter = kcalloc(5, sizeof(struct i2c_adapter), GFP_KERNEL);
- if (!s4985_adapter) {
- error = -ENOMEM;
- goto ERROR1;
- }
- s4985_algo = kcalloc(5, sizeof(struct i2c_algorithm), GFP_KERNEL);
- if (!s4985_algo) {
- error = -ENOMEM;
- goto ERROR2;
- }
-
- /* Fill in the new structures */
- s4985_algo[0] = *(nforce2_smbus->algo);
- s4985_algo[0].smbus_xfer = nforce2_access_virt0;
- s4985_adapter[0] = *nforce2_smbus;
- s4985_adapter[0].algo = s4985_algo;
- s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;
- for (i = 1; i < 5; i++) {
- s4985_algo[i] = *(nforce2_smbus->algo);
- s4985_adapter[i] = *nforce2_smbus;
- snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),
- "SMBus nForce2 adapter (CPU%d)", i - 1);
- s4985_adapter[i].algo = s4985_algo + i;
- s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;
- }
- s4985_algo[1].smbus_xfer = nforce2_access_virt1;
- s4985_algo[2].smbus_xfer = nforce2_access_virt2;
- s4985_algo[3].smbus_xfer = nforce2_access_virt3;
- s4985_algo[4].smbus_xfer = nforce2_access_virt4;
-
- /* Register virtual adapters */
- for (i = 0; i < 5; i++) {
- error = i2c_add_adapter(s4985_adapter + i);
- if (error) {
- printk(KERN_ERR "i2c-nforce2-s4985: "
- "Virtual adapter %d registration "
- "failed, module not inserted\n", i);
- for (i--; i >= 0; i--)
- i2c_del_adapter(s4985_adapter + i);
- goto ERROR3;
- }
- }
-
- return 0;
-
-ERROR3:
- kfree(s4985_algo);
- s4985_algo = NULL;
-ERROR2:
- kfree(s4985_adapter);
- s4985_adapter = NULL;
-ERROR1:
- /* Restore physical bus */
- i2c_add_adapter(nforce2_smbus);
-ERROR0:
- return error;
-}
-
-static void __exit nforce2_s4985_exit(void)
-{
- if (s4985_adapter) {
- int i;
-
- for (i = 0; i < 5; i++)
- i2c_del_adapter(s4985_adapter+i);
- kfree(s4985_adapter);
- s4985_adapter = NULL;
- }
- kfree(s4985_algo);
- s4985_algo = NULL;
-
- /* Restore physical bus */
- if (i2c_add_adapter(nforce2_smbus))
- printk(KERN_ERR "i2c-nforce2-s4985: "
- "Physical bus restoration failed\n");
-}
-
-MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
-MODULE_DESCRIPTION("S4985 SMBus multiplexing");
-MODULE_LICENSE("GPL");
-
-module_init(nforce2_s4985_init);
-module_exit(nforce2_s4985_exit);
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index f6a1272c5854..d58a308582e4 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
SMBus driver for nVidia nForce2 MCP
@@ -8,15 +9,6 @@
SMBus 2.0 driver for AMD-8111 IO-Hub
Copyright (c) 2002 Vojtech Pavlik
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -125,20 +117,6 @@ static const struct dmi_system_id nforce2_dmi_blacklist2[] = {
static struct pci_driver nforce2_driver;
-/* For multiplexing support, we need a global reference to the 1st
- SMBus channel */
-#if IS_ENABLED(CONFIG_I2C_NFORCE2_S4985)
-struct i2c_adapter *nforce2_smbus;
-EXPORT_SYMBOL_GPL(nforce2_smbus);
-
-static void nforce2_set_reference(struct i2c_adapter *adap)
-{
- nforce2_smbus = adap;
-}
-#else
-static inline void nforce2_set_reference(struct i2c_adapter *adap) { }
-#endif
-
static void nforce2_abort(struct i2c_adapter *adap)
{
struct nforce2_smbus *smbus = adap->algo_data;
@@ -335,8 +313,8 @@ static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg,
/* Older incarnations of the device used non-standard BARs */
u16 iobase;
- if (pci_read_config_word(dev, alt_reg, &iobase)
- != PCIBIOS_SUCCESSFUL) {
+ error = pci_read_config_word(dev, alt_reg, &iobase);
+ if (error != PCIBIOS_SUCCESSFUL) {
dev_err(&dev->dev, "Error reading PCI config for %s\n",
name);
return -EIO;
@@ -357,7 +335,7 @@ static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg,
return -EBUSY;
}
smbus->adapter.owner = THIS_MODULE;
- smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ smbus->adapter.class = I2C_CLASS_HWMON;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.dev.parent = &dev->dev;
@@ -419,7 +397,6 @@ static int nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
return -ENODEV;
}
- nforce2_set_reference(&smbuses[0].adapter);
return 0;
}
@@ -428,7 +405,6 @@ static void nforce2_remove(struct pci_dev *dev)
{
struct nforce2_smbus *smbuses = pci_get_drvdata(dev);
- nforce2_set_reference(NULL);
if (smbuses[0].base) {
i2c_del_adapter(&smbuses[0].adapter);
release_region(smbuses[0].base, smbuses[0].size);
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 0ed5a41804dc..19b648fc094d 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2009 ST-Ericsson SA
* Copyright (C) 2009 STMicroelectronics
@@ -5,25 +6,31 @@
* I2C master mode controller driver, used in Nomadik 8815
* and Ux500 platforms.
*
+ * The Mobileye EyeQ5 and EyeQ6H platforms are also supported; they use
+ * the same Ux500/DB8500 IP block with two quirks:
+ * - The memory bus only supports 32-bit accesses.
+ * - (only EyeQ5) A register must be configured for the I2C speed mode;
+ * it is located in a shared register region called OLB.
+ *
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
* Author: Sachin Verma <sachin.verma@st.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
*/
-#include <linux/init.h>
-#include <linux/module.h>
#include <linux/amba/bus.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/err.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
#define DRIVER_NAME "nmk-i2c"
@@ -45,61 +52,63 @@
#define I2C_ICR (0x038)
/* Control registers */
-#define I2C_CR_PE (0x1 << 0) /* Peripheral Enable */
-#define I2C_CR_OM (0x3 << 1) /* Operating mode */
-#define I2C_CR_SAM (0x1 << 3) /* Slave addressing mode */
-#define I2C_CR_SM (0x3 << 4) /* Speed mode */
-#define I2C_CR_SGCM (0x1 << 6) /* Slave general call mode */
-#define I2C_CR_FTX (0x1 << 7) /* Flush Transmit */
-#define I2C_CR_FRX (0x1 << 8) /* Flush Receive */
-#define I2C_CR_DMA_TX_EN (0x1 << 9) /* DMA Tx enable */
-#define I2C_CR_DMA_RX_EN (0x1 << 10) /* DMA Rx Enable */
-#define I2C_CR_DMA_SLE (0x1 << 11) /* DMA sync. logic enable */
-#define I2C_CR_LM (0x1 << 12) /* Loopback mode */
-#define I2C_CR_FON (0x3 << 13) /* Filtering on */
-#define I2C_CR_FS (0x3 << 15) /* Force stop enable */
+#define I2C_CR_PE BIT(0) /* Peripheral Enable */
+#define I2C_CR_OM GENMASK(2, 1) /* Operating mode */
+#define I2C_CR_SAM BIT(3) /* Slave addressing mode */
+#define I2C_CR_SM GENMASK(5, 4) /* Speed mode */
+#define I2C_CR_SGCM BIT(6) /* Slave general call mode */
+#define I2C_CR_FTX BIT(7) /* Flush Transmit */
+#define I2C_CR_FRX BIT(8) /* Flush Receive */
+#define I2C_CR_DMA_TX_EN BIT(9) /* DMA Tx enable */
+#define I2C_CR_DMA_RX_EN BIT(10) /* DMA Rx Enable */
+#define I2C_CR_DMA_SLE BIT(11) /* DMA sync. logic enable */
+#define I2C_CR_LM BIT(12) /* Loopback mode */
+#define I2C_CR_FON GENMASK(14, 13) /* Filtering on */
+#define I2C_CR_FS GENMASK(16, 15) /* Force stop enable */
+
+/* Slave control register (SCR) */
+#define I2C_SCR_SLSU GENMASK(31, 16) /* Slave data setup time */
/* Master controller (MCR) register */
-#define I2C_MCR_OP (0x1 << 0) /* Operation */
-#define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */
-#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */
-#define I2C_MCR_SB (0x1 << 11) /* Extended address */
-#define I2C_MCR_AM (0x3 << 12) /* Address type */
-#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */
-#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */
+#define I2C_MCR_OP BIT(0) /* Operation */
+#define I2C_MCR_A7 GENMASK(7, 1) /* 7-bit address */
+#define I2C_MCR_EA10 GENMASK(10, 8) /* 10-bit Extended address */
+#define I2C_MCR_SB BIT(11) /* Extended address */
+#define I2C_MCR_AM GENMASK(13, 12) /* Address type */
+#define I2C_MCR_STOP BIT(14) /* Stop condition */
+#define I2C_MCR_LENGTH GENMASK(25, 15) /* Transaction length */
/* Status register (SR) */
-#define I2C_SR_OP (0x3 << 0) /* Operation */
-#define I2C_SR_STATUS (0x3 << 2) /* controller status */
-#define I2C_SR_CAUSE (0x7 << 4) /* Abort cause */
-#define I2C_SR_TYPE (0x3 << 7) /* Receive type */
-#define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */
+#define I2C_SR_OP GENMASK(1, 0) /* Operation */
+#define I2C_SR_STATUS GENMASK(3, 2) /* controller status */
+#define I2C_SR_CAUSE GENMASK(6, 4) /* Abort cause */
+#define I2C_SR_TYPE GENMASK(8, 7) /* Receive type */
+#define I2C_SR_LENGTH GENMASK(19, 9) /* Transfer length */
+
+/* Baud-rate counter register (BRCR) */
+#define I2C_BRCR_BRCNT1 GENMASK(31, 16) /* Baud-rate counter 1 */
+#define I2C_BRCR_BRCNT2 GENMASK(15, 0) /* Baud-rate counter 2 */
/* Interrupt mask set/clear (IMSCR) bits */
-#define I2C_IT_TXFE (0x1 << 0)
-#define I2C_IT_TXFNE (0x1 << 1)
-#define I2C_IT_TXFF (0x1 << 2)
-#define I2C_IT_TXFOVR (0x1 << 3)
-#define I2C_IT_RXFE (0x1 << 4)
-#define I2C_IT_RXFNF (0x1 << 5)
-#define I2C_IT_RXFF (0x1 << 6)
-#define I2C_IT_RFSR (0x1 << 16)
-#define I2C_IT_RFSE (0x1 << 17)
-#define I2C_IT_WTSR (0x1 << 18)
-#define I2C_IT_MTD (0x1 << 19)
-#define I2C_IT_STD (0x1 << 20)
-#define I2C_IT_MAL (0x1 << 24)
-#define I2C_IT_BERR (0x1 << 25)
-#define I2C_IT_MTDWS (0x1 << 28)
-
-#define GEN_MASK(val, mask, sb) (((val) << (sb)) & (mask))
+#define I2C_IT_TXFE BIT(0)
+#define I2C_IT_TXFNE BIT(1)
+#define I2C_IT_TXFF BIT(2)
+#define I2C_IT_TXFOVR BIT(3)
+#define I2C_IT_RXFE BIT(4)
+#define I2C_IT_RXFNF BIT(5)
+#define I2C_IT_RXFF BIT(6)
+#define I2C_IT_RFSR BIT(16)
+#define I2C_IT_RFSE BIT(17)
+#define I2C_IT_WTSR BIT(18)
+#define I2C_IT_MTD BIT(19)
+#define I2C_IT_STD BIT(20)
+#define I2C_IT_MAL BIT(24)
+#define I2C_IT_BERR BIT(25)
+#define I2C_IT_MTDWS BIT(28)
/* some bits in ICR are reserved */
#define I2C_CLEAR_ALL_INTS 0x131f007f
-/* first three msb bits are reserved */
-#define IRQ_MASK(mask) (mask & 0x1fffffff)
-
/* maximum threshold value */
#define MAX_I2C_FIFO_THRESHOLD 15
@@ -110,6 +119,15 @@ enum i2c_freq_mode {
I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */
};
+/* Mobileye EyeQ5 offset into a shared register region (called OLB) */
+#define NMK_I2C_EYEQ5_OLB_IOCR2 0x0B8
+
+enum i2c_eyeq5_speed {
+ I2C_EYEQ5_SPEED_FAST,
+ I2C_EYEQ5_SPEED_FAST_PLUS,
+ I2C_EYEQ5_SPEED_HIGH_SPEED,
+};
+
/**
* struct i2c_vendor_data - per-vendor variations
* @has_mtdws: variant has the MTDWS bit
@@ -134,6 +152,12 @@ enum i2c_operation {
I2C_READ = 0x01
};
+enum i2c_operating_mode {
+ I2C_OM_SLAVE,
+ I2C_OM_MASTER,
+ I2C_OM_MASTER_OR_SLAVE,
+};
+
/**
* struct i2c_nmk_client - client specific data
* @slave_adr: 7-bit slave address
@@ -162,11 +186,13 @@ struct i2c_nmk_client {
* @clk_freq: clock frequency for the operation mode
* @tft: Tx FIFO Threshold in bytes
* @rft: Rx FIFO Threshold in bytes
- * @timeout Slave response timeout (ms)
+ * @timeout_usecs: Slave response timeout
* @sm: speed mode
* @stop: stop condition.
- * @xfer_complete: acknowledge completion for a I2C message.
+ * @xfer_wq: xfer done wait queue.
+ * @xfer_done: xfer done boolean.
* @result: controller propogated result.
+ * @has_32b_bus: controller is on a bus that only supports 32-bit accesses.
*/
struct nmk_i2c_dev {
struct i2c_vendor_data *vendor;
@@ -179,11 +205,13 @@ struct nmk_i2c_dev {
u32 clk_freq;
unsigned char tft;
unsigned char rft;
- int timeout;
+ u32 timeout_usecs;
enum i2c_freq_mode sm;
int stop;
- struct completion xfer_complete;
+ struct wait_queue_head xfer_wq;
+ bool xfer_done;
int result;
+ bool has_32b_bus;
};
/* controller's abort causes */
@@ -207,18 +235,36 @@ static inline void i2c_clr_bit(void __iomem *reg, u32 mask)
writel(readl(reg) & ~mask, reg);
}
+static inline u8 nmk_i2c_readb(const struct nmk_i2c_dev *priv,
+ unsigned long reg)
+{
+ if (priv->has_32b_bus)
+ return readl(priv->virtbase + reg);
+ else
+ return readb(priv->virtbase + reg);
+}
+
+static inline void nmk_i2c_writeb(const struct nmk_i2c_dev *priv, u32 val,
+ unsigned long reg)
+{
+ if (priv->has_32b_bus)
+ writel(val, priv->virtbase + reg);
+ else
+ writeb(val, priv->virtbase + reg);
+}
+
/**
* flush_i2c_fifo() - This function flushes the I2C FIFO
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
*
* This function flushes the I2C Tx and Rx FIFOs. It returns
* 0 on successful flushing of FIFO
*/
-static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
+static int flush_i2c_fifo(struct nmk_i2c_dev *priv)
{
#define LOOP_ATTEMPTS 10
+ ktime_t timeout;
int i;
- unsigned long timeout;
/*
* flush the transmit and receive FIFO. The flushing
@@ -227,19 +273,19 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
* bits, until then no one must access Tx, Rx FIFO and
* should poll on these bits waiting for the completion.
*/
- writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR);
+ writel((I2C_CR_FTX | I2C_CR_FRX), priv->virtbase + I2C_CR);
for (i = 0; i < LOOP_ATTEMPTS; i++) {
- timeout = jiffies + dev->adap.timeout;
+ timeout = ktime_add_us(ktime_get(), priv->timeout_usecs);
- while (!time_after(jiffies, timeout)) {
- if ((readl(dev->virtbase + I2C_CR) &
+ while (ktime_after(timeout, ktime_get())) {
+ if ((readl(priv->virtbase + I2C_CR) &
(I2C_CR_FTX | I2C_CR_FRX)) == 0)
- return 0;
+ return 0;
}
}
- dev_err(&dev->adev->dev,
+ dev_err(&priv->adev->dev,
"flushing operation timed out giving up after %d attempts",
LOOP_ATTEMPTS);
@@ -248,120 +294,121 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
/**
* disable_all_interrupts() - Disable all interrupts of this I2c Bus
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
*/
-static void disable_all_interrupts(struct nmk_i2c_dev *dev)
+static void disable_all_interrupts(struct nmk_i2c_dev *priv)
{
- u32 mask = IRQ_MASK(0);
- writel(mask, dev->virtbase + I2C_IMSCR);
+ writel(0, priv->virtbase + I2C_IMSCR);
}
/**
* clear_all_interrupts() - Clear all interrupts of I2C Controller
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
*/
-static void clear_all_interrupts(struct nmk_i2c_dev *dev)
+static void clear_all_interrupts(struct nmk_i2c_dev *priv)
{
- u32 mask;
- mask = IRQ_MASK(I2C_CLEAR_ALL_INTS);
- writel(mask, dev->virtbase + I2C_ICR);
+ writel(I2C_CLEAR_ALL_INTS, priv->virtbase + I2C_ICR);
}
/**
* init_hw() - initialize the I2C hardware
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
*/
-static int init_hw(struct nmk_i2c_dev *dev)
+static int init_hw(struct nmk_i2c_dev *priv)
{
int stat;
- stat = flush_i2c_fifo(dev);
+ stat = flush_i2c_fifo(priv);
if (stat)
goto exit;
/* disable the controller */
- i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
+ i2c_clr_bit(priv->virtbase + I2C_CR, I2C_CR_PE);
- disable_all_interrupts(dev);
+ disable_all_interrupts(priv);
- clear_all_interrupts(dev);
+ clear_all_interrupts(priv);
- dev->cli.operation = I2C_NO_OPERATION;
+ priv->cli.operation = I2C_NO_OPERATION;
exit:
return stat;
}
/* enable peripheral, master mode operation */
-#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE)
+#define DEFAULT_I2C_REG_CR (FIELD_PREP(I2C_CR_OM, I2C_OM_MASTER) | I2C_CR_PE)
+
+/* grab top three bits from extended I2C addresses */
+#define ADR_3MSB_BITS GENMASK(9, 7)
/**
* load_i2c_mcr_reg() - load the MCR register
- * @dev: private data of controller
+ * @priv: private data of controller
* @flags: message flags
*/
-static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev, u16 flags)
+static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *priv, u16 flags)
{
u32 mcr = 0;
unsigned short slave_adr_3msb_bits;
- mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1);
+ mcr |= FIELD_PREP(I2C_MCR_A7, priv->cli.slave_adr);
if (unlikely(flags & I2C_M_TEN)) {
/* 10-bit address transaction */
- mcr |= GEN_MASK(2, I2C_MCR_AM, 12);
+ mcr |= FIELD_PREP(I2C_MCR_AM, 2);
/*
* Get the top 3 bits.
* EA10 represents extended address in MCR. This includes
* the extension (MSB bits) of the 7 bit address loaded
* in A7
*/
- slave_adr_3msb_bits = (dev->cli.slave_adr >> 7) & 0x7;
+ slave_adr_3msb_bits = FIELD_GET(ADR_3MSB_BITS,
+ priv->cli.slave_adr);
- mcr |= GEN_MASK(slave_adr_3msb_bits, I2C_MCR_EA10, 8);
+ mcr |= FIELD_PREP(I2C_MCR_EA10, slave_adr_3msb_bits);
} else {
/* 7-bit address transaction */
- mcr |= GEN_MASK(1, I2C_MCR_AM, 12);
+ mcr |= FIELD_PREP(I2C_MCR_AM, 1);
}
/* start byte procedure not applied */
- mcr |= GEN_MASK(0, I2C_MCR_SB, 11);
+ mcr |= FIELD_PREP(I2C_MCR_SB, 0);
/* check the operation, master read/write? */
- if (dev->cli.operation == I2C_WRITE)
- mcr |= GEN_MASK(I2C_WRITE, I2C_MCR_OP, 0);
+ if (priv->cli.operation == I2C_WRITE)
+ mcr |= FIELD_PREP(I2C_MCR_OP, I2C_WRITE);
else
- mcr |= GEN_MASK(I2C_READ, I2C_MCR_OP, 0);
+ mcr |= FIELD_PREP(I2C_MCR_OP, I2C_READ);
/* stop or repeated start? */
- if (dev->stop)
- mcr |= GEN_MASK(1, I2C_MCR_STOP, 14);
+ if (priv->stop)
+ mcr |= FIELD_PREP(I2C_MCR_STOP, 1);
else
- mcr &= ~(GEN_MASK(1, I2C_MCR_STOP, 14));
+ mcr &= ~FIELD_PREP(I2C_MCR_STOP, 1);
- mcr |= GEN_MASK(dev->cli.count, I2C_MCR_LENGTH, 15);
+ mcr |= FIELD_PREP(I2C_MCR_LENGTH, priv->cli.count);
return mcr;
}
/**
* setup_i2c_controller() - setup the controller
- * @dev: private data of controller
+ * @priv: private data of controller
*/
-static void setup_i2c_controller(struct nmk_i2c_dev *dev)
+static void setup_i2c_controller(struct nmk_i2c_dev *priv)
{
- u32 brcr1, brcr2;
+ u32 brcr;
u32 i2c_clk, div;
u32 ns;
u16 slsu;
- writel(0x0, dev->virtbase + I2C_CR);
- writel(0x0, dev->virtbase + I2C_HSMCR);
- writel(0x0, dev->virtbase + I2C_TFTR);
- writel(0x0, dev->virtbase + I2C_RFTR);
- writel(0x0, dev->virtbase + I2C_DMAR);
+ writel(0x0, priv->virtbase + I2C_CR);
+ writel(0x0, priv->virtbase + I2C_HSMCR);
+ writel(0x0, priv->virtbase + I2C_TFTR);
+ writel(0x0, priv->virtbase + I2C_RFTR);
+ writel(0x0, priv->virtbase + I2C_DMAR);
- i2c_clk = clk_get_rate(dev->clk);
+ i2c_clk = clk_get_rate(priv->clk);
/*
* set the slsu:
@@ -376,7 +423,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* slsu = cycles / (1000000000 / f) + 1
*/
ns = DIV_ROUND_UP_ULL(1000000000ULL, i2c_clk);
- switch (dev->sm) {
+ switch (priv->sm) {
case I2C_FREQ_MODE_FAST:
case I2C_FREQ_MODE_FAST_PLUS:
slsu = DIV_ROUND_UP(100, ns); /* Fast */
@@ -391,154 +438,161 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
}
slsu += 1;
- dev_dbg(&dev->adev->dev, "calculated SLSU = %04x\n", slsu);
- writel(slsu << 16, dev->virtbase + I2C_SCR);
+ dev_dbg(&priv->adev->dev, "calculated SLSU = %04x\n", slsu);
+ writel(FIELD_PREP(I2C_SCR_SLSU, slsu), priv->virtbase + I2C_SCR);
/*
* The spec says, in case of std. mode the divider is
* 2 whereas it is 3 for fast and fastplus mode of
- * operation. TODO - high speed support.
+ * operation.
*/
- div = (dev->clk_freq > 100000) ? 3 : 2;
+ div = (priv->clk_freq > I2C_MAX_STANDARD_MODE_FREQ) ? 3 : 2;
/*
* generate the mask for baud rate counters. The controller
* has two baud rate counters. One is used for High speed
* operation, and the other is for std, fast mode, fast mode
- * plus operation. Currently we do not supprt high speed mode
- * so set brcr1 to 0.
+ * plus operation.
+ *
+ * BRCR is a clock divider amount. Pick highest value that
+ * leads to rate strictly below target. Eg when asking for
+ * 400kHz you want a bus rate <=400kHz (and not >=400kHz).
*/
- brcr1 = 0 << 16;
- brcr2 = (i2c_clk/(dev->clk_freq * div)) & 0xffff;
+ brcr = DIV_ROUND_UP(i2c_clk, priv->clk_freq * div);
+
+ if (priv->sm == I2C_FREQ_MODE_HIGH_SPEED)
+ brcr = FIELD_PREP(I2C_BRCR_BRCNT1, brcr);
+ else
+ brcr = FIELD_PREP(I2C_BRCR_BRCNT2, brcr);
/* set the baud rate counter register */
- writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
+ writel(brcr, priv->virtbase + I2C_BRCR);
- /*
- * set the speed mode. Currently we support
- * only standard and fast mode of operation
- * TODO - support for fast mode plus (up to 1Mb/s)
- * and high speed (up to 3.4 Mb/s)
- */
- if (dev->sm > I2C_FREQ_MODE_FAST) {
- dev_err(&dev->adev->dev,
- "do not support this mode defaulting to std. mode\n");
- brcr2 = i2c_clk/(100000 * 2) & 0xffff;
- writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
- writel(I2C_FREQ_MODE_STANDARD << 4,
- dev->virtbase + I2C_CR);
- }
- writel(dev->sm << 4, dev->virtbase + I2C_CR);
+ /* set the speed mode */
+ writel(FIELD_PREP(I2C_CR_SM, priv->sm), priv->virtbase + I2C_CR);
/* set the Tx and Rx FIFO threshold */
- writel(dev->tft, dev->virtbase + I2C_TFTR);
- writel(dev->rft, dev->virtbase + I2C_RFTR);
+ writel(priv->tft, priv->virtbase + I2C_TFTR);
+ writel(priv->rft, priv->virtbase + I2C_RFTR);
+}
+
+static bool nmk_i2c_wait_xfer_done(struct nmk_i2c_dev *priv)
+{
+ if (priv->timeout_usecs < jiffies_to_usecs(1)) {
+ unsigned long timeout_usecs = priv->timeout_usecs;
+ ktime_t timeout = ktime_set(0, timeout_usecs * NSEC_PER_USEC);
+
+ wait_event_hrtimeout(priv->xfer_wq, priv->xfer_done, timeout);
+ } else {
+ unsigned long timeout = usecs_to_jiffies(priv->timeout_usecs);
+
+ wait_event_timeout(priv->xfer_wq, priv->xfer_done, timeout);
+ }
+
+ return priv->xfer_done;
}
/**
* read_i2c() - Read from I2C client device
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
* @flags: message flags
*
* This function reads from i2c client device when controller is in
* master mode. There is a completion timeout. If there is no transfer
* before timeout error is returned.
*/
-static int read_i2c(struct nmk_i2c_dev *dev, u16 flags)
+static int read_i2c(struct nmk_i2c_dev *priv, u16 flags)
{
- int status = 0;
u32 mcr, irq_mask;
- unsigned long timeout;
+ int status = 0;
+ bool xfer_done;
- mcr = load_i2c_mcr_reg(dev, flags);
- writel(mcr, dev->virtbase + I2C_MCR);
+ mcr = load_i2c_mcr_reg(priv, flags);
+ writel(mcr, priv->virtbase + I2C_MCR);
/* load the current CR value */
- writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR,
- dev->virtbase + I2C_CR);
+ writel(readl(priv->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR,
+ priv->virtbase + I2C_CR);
/* enable the controller */
- i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
+ i2c_set_bit(priv->virtbase + I2C_CR, I2C_CR_PE);
- init_completion(&dev->xfer_complete);
+ init_waitqueue_head(&priv->xfer_wq);
+ priv->xfer_done = false;
/* enable interrupts by setting the mask */
irq_mask = (I2C_IT_RXFNF | I2C_IT_RXFF |
I2C_IT_MAL | I2C_IT_BERR);
- if (dev->stop || !dev->vendor->has_mtdws)
+ if (priv->stop || !priv->vendor->has_mtdws)
irq_mask |= I2C_IT_MTD;
else
irq_mask |= I2C_IT_MTDWS;
- irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask);
+ irq_mask &= I2C_CLEAR_ALL_INTS;
- writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
- dev->virtbase + I2C_IMSCR);
+ writel(readl(priv->virtbase + I2C_IMSCR) | irq_mask,
+ priv->virtbase + I2C_IMSCR);
- timeout = wait_for_completion_timeout(
- &dev->xfer_complete, dev->adap.timeout);
+ xfer_done = nmk_i2c_wait_xfer_done(priv);
- if (timeout == 0) {
- /* Controller timed out */
- dev_err(&dev->adev->dev, "read from slave 0x%x timed out\n",
- dev->cli.slave_adr);
+ if (!xfer_done)
status = -ETIMEDOUT;
- }
+
return status;
}
-static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
+static void fill_tx_fifo(struct nmk_i2c_dev *priv, int no_bytes)
{
int count;
for (count = (no_bytes - 2);
(count > 0) &&
- (dev->cli.count != 0);
+ (priv->cli.count != 0);
count--) {
/* write to the Tx FIFO */
- writeb(*dev->cli.buffer,
- dev->virtbase + I2C_TFR);
- dev->cli.buffer++;
- dev->cli.count--;
- dev->cli.xfer_bytes++;
+ nmk_i2c_writeb(priv, *priv->cli.buffer, I2C_TFR);
+ priv->cli.buffer++;
+ priv->cli.count--;
+ priv->cli.xfer_bytes++;
}
}
/**
* write_i2c() - Write data to I2C client.
- * @dev: private data of I2C Driver
+ * @priv: private data of I2C Driver
* @flags: message flags
*
* This function writes data to I2C client
*/
-static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
+static int write_i2c(struct nmk_i2c_dev *priv, u16 flags)
{
- u32 status = 0;
u32 mcr, irq_mask;
- unsigned long timeout;
+ u32 status = 0;
+ bool xfer_done;
- mcr = load_i2c_mcr_reg(dev, flags);
+ mcr = load_i2c_mcr_reg(priv, flags);
- writel(mcr, dev->virtbase + I2C_MCR);
+ writel(mcr, priv->virtbase + I2C_MCR);
/* load the current CR value */
- writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR,
- dev->virtbase + I2C_CR);
+ writel(readl(priv->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR,
+ priv->virtbase + I2C_CR);
/* enable the controller */
- i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
+ i2c_set_bit(priv->virtbase + I2C_CR, I2C_CR_PE);
- init_completion(&dev->xfer_complete);
+ init_waitqueue_head(&priv->xfer_wq);
+ priv->xfer_done = false;
/* enable interrupts by settings the masks */
irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR);
/* Fill the TX FIFO with transmit data */
- fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD);
+ fill_tx_fifo(priv, MAX_I2C_FIFO_THRESHOLD);
- if (dev->cli.count != 0)
+ if (priv->cli.count != 0)
irq_mask |= I2C_IT_TXFNE;
/*
@@ -546,23 +600,22 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
* set the MTDWS bit (Master Transaction Done Without Stop)
* to start repeated start operation
*/
- if (dev->stop || !dev->vendor->has_mtdws)
+ if (priv->stop || !priv->vendor->has_mtdws)
irq_mask |= I2C_IT_MTD;
else
irq_mask |= I2C_IT_MTDWS;
- irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask);
+ irq_mask &= I2C_CLEAR_ALL_INTS;
- writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
- dev->virtbase + I2C_IMSCR);
+ writel(readl(priv->virtbase + I2C_IMSCR) | irq_mask,
+ priv->virtbase + I2C_IMSCR);
- timeout = wait_for_completion_timeout(
- &dev->xfer_complete, dev->adap.timeout);
+ xfer_done = nmk_i2c_wait_xfer_done(priv);
- if (timeout == 0) {
+ if (!xfer_done) {
/* Controller timed out */
- dev_err(&dev->adev->dev, "write to slave 0x%x timed out\n",
- dev->cli.slave_adr);
+ dev_err(&priv->adev->dev, "write to slave 0x%x timed out\n",
+ priv->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -571,44 +624,39 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
/**
* nmk_i2c_xfer_one() - transmit a single I2C message
- * @dev: device with a message encoded into it
+ * @priv: device with a message encoded into it
* @flags: message flags
*/
-static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
+static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 flags)
{
int status;
if (flags & I2C_M_RD) {
/* read operation */
- dev->cli.operation = I2C_READ;
- status = read_i2c(dev, flags);
+ priv->cli.operation = I2C_READ;
+ status = read_i2c(priv, flags);
} else {
/* write operation */
- dev->cli.operation = I2C_WRITE;
- status = write_i2c(dev, flags);
+ priv->cli.operation = I2C_WRITE;
+ status = write_i2c(priv, flags);
}
- if (status || (dev->result)) {
+ if (status || priv->result) {
u32 i2c_sr;
u32 cause;
- i2c_sr = readl(dev->virtbase + I2C_SR);
- /*
- * Check if the controller I2C operation status
- * is set to ABORT(11b).
- */
- if (((i2c_sr >> 2) & 0x3) == 0x3) {
- /* get the abort cause */
- cause = (i2c_sr >> 4) & 0x7;
- dev_err(&dev->adev->dev, "%s\n",
+ i2c_sr = readl(priv->virtbase + I2C_SR);
+ if (FIELD_GET(I2C_SR_STATUS, i2c_sr) == I2C_ABORT) {
+ cause = FIELD_GET(I2C_SR_CAUSE, i2c_sr);
+ dev_err(&priv->adev->dev, "%s\n",
cause >= ARRAY_SIZE(abort_causes) ?
"unknown reason" :
abort_causes[cause]);
}
- (void) init_hw(dev);
+ init_hw(priv);
- status = status ? status : dev->result;
+ status = status ? status : priv->result;
}
return status;
@@ -666,24 +714,24 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
{
int status = 0;
int i;
- struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
+ struct nmk_i2c_dev *priv = i2c_get_adapdata(i2c_adap);
int j;
- pm_runtime_get_sync(&dev->adev->dev);
+ pm_runtime_get_sync(&priv->adev->dev);
/* Attempt three times to send the message queue */
for (j = 0; j < 3; j++) {
/* setup the i2c controller */
- setup_i2c_controller(dev);
+ setup_i2c_controller(priv);
for (i = 0; i < num_msgs; i++) {
- dev->cli.slave_adr = msgs[i].addr;
- dev->cli.buffer = msgs[i].buf;
- dev->cli.count = msgs[i].len;
- dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
- dev->result = 0;
+ priv->cli.slave_adr = msgs[i].addr;
+ priv->cli.buffer = msgs[i].buf;
+ priv->cli.count = msgs[i].len;
+ priv->stop = (i < (num_msgs - 1)) ? 0 : 1;
+ priv->result = 0;
- status = nmk_i2c_xfer_one(dev, msgs[i].flags);
+ status = nmk_i2c_xfer_one(priv, msgs[i].flags);
if (status != 0)
break;
}
@@ -691,7 +739,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
break;
}
- pm_runtime_put_sync(&dev->adev->dev);
+ pm_runtime_put_sync(&priv->adev->dev);
/* return the no. messages processed */
if (status)
@@ -702,14 +750,14 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
/**
* disable_interrupts() - disable the interrupts
- * @dev: private data of controller
+ * @priv: private data of controller
* @irq: interrupt number
*/
-static int disable_interrupts(struct nmk_i2c_dev *dev, u32 irq)
+static int disable_interrupts(struct nmk_i2c_dev *priv, u32 irq)
{
- irq = IRQ_MASK(irq);
- writel(readl(dev->virtbase + I2C_IMSCR) & ~(I2C_CLEAR_ALL_INTS & irq),
- dev->virtbase + I2C_IMSCR);
+ irq &= I2C_CLEAR_ALL_INTS;
+ writel(readl(priv->virtbase + I2C_IMSCR) & ~irq,
+ priv->virtbase + I2C_IMSCR);
return 0;
}
@@ -726,38 +774,39 @@ static int disable_interrupts(struct nmk_i2c_dev *dev, u32 irq)
*/
static irqreturn_t i2c_irq_handler(int irq, void *arg)
{
- struct nmk_i2c_dev *dev = arg;
+ struct nmk_i2c_dev *priv = arg;
+ struct device *dev = &priv->adev->dev;
u32 tft, rft;
u32 count;
u32 misr, src;
/* load Tx FIFO and Rx FIFO threshold values */
- tft = readl(dev->virtbase + I2C_TFTR);
- rft = readl(dev->virtbase + I2C_RFTR);
+ tft = readl(priv->virtbase + I2C_TFTR);
+ rft = readl(priv->virtbase + I2C_RFTR);
/* read interrupt status register */
- misr = readl(dev->virtbase + I2C_MISR);
+ misr = readl(priv->virtbase + I2C_MISR);
src = __ffs(misr);
- switch ((1 << src)) {
+ switch (BIT(src)) {
/* Transmit FIFO nearly empty interrupt */
case I2C_IT_TXFNE:
{
- if (dev->cli.operation == I2C_READ) {
+ if (priv->cli.operation == I2C_READ) {
/*
* in read operation why do we care for writing?
* so disable the Transmit FIFO interrupt
*/
- disable_interrupts(dev, I2C_IT_TXFNE);
+ disable_interrupts(priv, I2C_IT_TXFNE);
} else {
- fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
+ fill_tx_fifo(priv, (MAX_I2C_FIFO_THRESHOLD - tft));
/*
* if done, close the transfer by disabling the
* corresponding TXFNE interrupt
*/
- if (dev->cli.count == 0)
- disable_interrupts(dev, I2C_IT_TXFNE);
+ if (priv->cli.count == 0)
+ disable_interrupts(priv, I2C_IT_TXFNE);
}
}
break;
@@ -771,60 +820,63 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
case I2C_IT_RXFNF:
for (count = rft; count > 0; count--) {
/* Read the Rx FIFO */
- *dev->cli.buffer = readb(dev->virtbase + I2C_RFR);
- dev->cli.buffer++;
+ *priv->cli.buffer = nmk_i2c_readb(priv, I2C_RFR);
+ priv->cli.buffer++;
}
- dev->cli.count -= rft;
- dev->cli.xfer_bytes += rft;
+ priv->cli.count -= rft;
+ priv->cli.xfer_bytes += rft;
break;
/* Rx FIFO full */
case I2C_IT_RXFF:
for (count = MAX_I2C_FIFO_THRESHOLD; count > 0; count--) {
- *dev->cli.buffer = readb(dev->virtbase + I2C_RFR);
- dev->cli.buffer++;
+ *priv->cli.buffer = nmk_i2c_readb(priv, I2C_RFR);
+ priv->cli.buffer++;
}
- dev->cli.count -= MAX_I2C_FIFO_THRESHOLD;
- dev->cli.xfer_bytes += MAX_I2C_FIFO_THRESHOLD;
+ priv->cli.count -= MAX_I2C_FIFO_THRESHOLD;
+ priv->cli.xfer_bytes += MAX_I2C_FIFO_THRESHOLD;
break;
/* Master Transaction Done with/without stop */
case I2C_IT_MTD:
case I2C_IT_MTDWS:
- if (dev->cli.operation == I2C_READ) {
- while (!(readl(dev->virtbase + I2C_RISR)
+ if (priv->cli.operation == I2C_READ) {
+ while (!(readl(priv->virtbase + I2C_RISR)
& I2C_IT_RXFE)) {
- if (dev->cli.count == 0)
+ if (priv->cli.count == 0)
break;
- *dev->cli.buffer =
- readb(dev->virtbase + I2C_RFR);
- dev->cli.buffer++;
- dev->cli.count--;
- dev->cli.xfer_bytes++;
+ *priv->cli.buffer =
+ nmk_i2c_readb(priv, I2C_RFR);
+ priv->cli.buffer++;
+ priv->cli.count--;
+ priv->cli.xfer_bytes++;
}
}
- disable_all_interrupts(dev);
- clear_all_interrupts(dev);
+ disable_all_interrupts(priv);
+ clear_all_interrupts(priv);
- if (dev->cli.count) {
- dev->result = -EIO;
- dev_err(&dev->adev->dev,
- "%lu bytes still remain to be xfered\n",
- dev->cli.count);
- (void) init_hw(dev);
+ if (priv->cli.count) {
+ priv->result = -EIO;
+ dev_err(dev, "%lu bytes still remain to be xfered\n",
+ priv->cli.count);
+ init_hw(priv);
}
- complete(&dev->xfer_complete);
+ priv->xfer_done = true;
+ wake_up(&priv->xfer_wq);
+
break;
/* Master Arbitration lost interrupt */
case I2C_IT_MAL:
- dev->result = -EIO;
- (void) init_hw(dev);
+ priv->result = -EIO;
+ init_hw(priv);
+
+ i2c_set_bit(priv->virtbase + I2C_ICR, I2C_IT_MAL);
+ priv->xfer_done = true;
+ wake_up(&priv->xfer_wq);
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL);
- complete(&dev->xfer_complete);
break;
@@ -834,15 +886,20 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* during the transaction.
*/
case I2C_IT_BERR:
- dev->result = -EIO;
- /* get the status */
- if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT)
- (void) init_hw(dev);
+ {
+ u32 sr;
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_BERR);
- complete(&dev->xfer_complete);
+ sr = readl(priv->virtbase + I2C_SR);
+ priv->result = -EIO;
+ if (FIELD_GET(I2C_SR_STATUS, sr) == I2C_ABORT)
+ init_hw(priv);
- break;
+ i2c_set_bit(priv->virtbase + I2C_ICR, I2C_IT_BERR);
+ priv->xfer_done = true;
+ wake_up(&priv->xfer_wq);
+
+ }
+ break;
/*
* Tx FIFO overrun interrupt.
@@ -850,11 +907,13 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* the Tx FIFO is full.
*/
case I2C_IT_TXFOVR:
- dev->result = -EIO;
- (void) init_hw(dev);
+ priv->result = -EIO;
+ init_hw(priv);
+
+ dev_err(dev, "Tx Fifo Over run\n");
+ priv->xfer_done = true;
+ wake_up(&priv->xfer_wq);
- dev_err(&dev->adev->dev, "Tx Fifo Over run\n");
- complete(&dev->xfer_complete);
break;
@@ -866,17 +925,16 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
case I2C_IT_RFSE:
case I2C_IT_WTSR:
case I2C_IT_STD:
- dev_err(&dev->adev->dev, "unhandled Interrupt\n");
+ dev_err(dev, "unhandled Interrupt\n");
break;
default:
- dev_err(&dev->adev->dev, "spurious Interrupt..\n");
+ dev_err(dev, "spurious Interrupt..\n");
break;
}
return IRQ_HANDLED;
}
-#ifdef CONFIG_PM_SLEEP
static int nmk_i2c_suspend_late(struct device *dev)
{
int ret;
@@ -893,15 +951,13 @@ static int nmk_i2c_resume_early(struct device *dev)
{
return pm_runtime_force_resume(dev);
}
-#endif
-#ifdef CONFIG_PM
static int nmk_i2c_runtime_suspend(struct device *dev)
{
struct amba_device *adev = to_amba_device(dev);
- struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
+ struct nmk_i2c_dev *priv = amba_get_drvdata(adev);
- clk_disable_unprepare(nmk_i2c->clk);
+ clk_disable_unprepare(priv->clk);
pinctrl_pm_select_idle_state(dev);
return 0;
}
@@ -909,10 +965,10 @@ static int nmk_i2c_runtime_suspend(struct device *dev)
static int nmk_i2c_runtime_resume(struct device *dev)
{
struct amba_device *adev = to_amba_device(dev);
- struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
+ struct nmk_i2c_dev *priv = amba_get_drvdata(adev);
int ret;
- ret = clk_prepare_enable(nmk_i2c->clk);
+ ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "can't prepare_enable clock\n");
return ret;
@@ -920,21 +976,18 @@ static int nmk_i2c_runtime_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
- ret = init_hw(nmk_i2c);
+ ret = init_hw(priv);
if (ret) {
- clk_disable_unprepare(nmk_i2c->clk);
+ clk_disable_unprepare(priv->clk);
pinctrl_pm_select_idle_state(dev);
}
return ret;
}
-#endif
static const struct dev_pm_ops nmk_i2c_pm = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early)
- SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,
- nmk_i2c_runtime_resume,
- NULL)
+ LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early)
+ RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, nmk_i2c_runtime_resume, NULL)
};
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
@@ -943,137 +996,191 @@ static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm nmk_i2c_algo = {
- .master_xfer = nmk_i2c_xfer,
- .functionality = nmk_i2c_functionality
+ .xfer = nmk_i2c_xfer,
+ .functionality = nmk_i2c_functionality
};
static void nmk_i2c_of_probe(struct device_node *np,
- struct nmk_i2c_dev *nmk)
+ struct nmk_i2c_dev *priv)
{
+ u32 timeout_usecs;
+
/* Default to 100 kHz if no frequency is given in the node */
- if (of_property_read_u32(np, "clock-frequency", &nmk->clk_freq))
- nmk->clk_freq = 100000;
+ if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
+ priv->clk_freq = I2C_MAX_STANDARD_MODE_FREQ;
+
+ if (priv->clk_freq <= I2C_MAX_STANDARD_MODE_FREQ)
+ priv->sm = I2C_FREQ_MODE_STANDARD;
+ else if (priv->clk_freq <= I2C_MAX_FAST_MODE_FREQ)
+ priv->sm = I2C_FREQ_MODE_FAST;
+ else if (priv->clk_freq <= I2C_MAX_FAST_MODE_PLUS_FREQ)
+ priv->sm = I2C_FREQ_MODE_FAST_PLUS;
+ else
+ priv->sm = I2C_FREQ_MODE_HIGH_SPEED;
+ priv->tft = 1; /* Tx FIFO threshold */
+ priv->rft = 8; /* Rx FIFO threshold */
- /* This driver only supports 'standard' and 'fast' modes of operation. */
- if (nmk->clk_freq <= 100000)
- nmk->sm = I2C_FREQ_MODE_STANDARD;
+ /* Slave response timeout */
+ if (!of_property_read_u32(np, "i2c-transfer-timeout-us", &timeout_usecs))
+ priv->timeout_usecs = timeout_usecs;
else
- nmk->sm = I2C_FREQ_MODE_FAST;
- nmk->tft = 1; /* Tx FIFO threshold */
- nmk->rft = 8; /* Rx FIFO threshold */
- nmk->timeout = 200; /* Slave response timeout(ms) */
+ priv->timeout_usecs = 200 * USEC_PER_MSEC;
}
+static const unsigned int nmk_i2c_eyeq5_masks[] = {
+ GENMASK(5, 4),
+ GENMASK(7, 6),
+ GENMASK(9, 8),
+ GENMASK(11, 10),
+ GENMASK(13, 12),
+};
+
+static int nmk_i2c_eyeq5_probe(struct nmk_i2c_dev *priv)
+{
+ struct device *dev = &priv->adev->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int mask, speed_mode;
+ struct regmap *olb;
+ unsigned int id;
+
+ olb = syscon_regmap_lookup_by_phandle_args(np, "mobileye,olb", 1, &id);
+ if (IS_ERR(olb))
+ return PTR_ERR(olb);
+ if (id >= ARRAY_SIZE(nmk_i2c_eyeq5_masks))
+ return -ENOENT;
+
+ if (priv->clk_freq <= 400000)
+ speed_mode = I2C_EYEQ5_SPEED_FAST;
+ else if (priv->clk_freq <= 1000000)
+ speed_mode = I2C_EYEQ5_SPEED_FAST_PLUS;
+ else
+ speed_mode = I2C_EYEQ5_SPEED_HIGH_SPEED;
+
+ mask = nmk_i2c_eyeq5_masks[id];
+ regmap_update_bits(olb, NMK_I2C_EYEQ5_OLB_IOCR2,
+ mask, speed_mode << __fls(mask));
+
+ return 0;
+}
+
+#define NMK_I2C_EYEQ_FLAG_32B_BUS BIT(0)
+#define NMK_I2C_EYEQ_FLAG_IS_EYEQ5 BIT(1)
+
+static const struct of_device_id nmk_i2c_eyeq_match_table[] = {
+ {
+ .compatible = "mobileye,eyeq5-i2c",
+ .data = (void *)(NMK_I2C_EYEQ_FLAG_32B_BUS | NMK_I2C_EYEQ_FLAG_IS_EYEQ5),
+ },
+ {
+ .compatible = "mobileye,eyeq6h-i2c",
+ .data = (void *)NMK_I2C_EYEQ_FLAG_32B_BUS,
+ },
+ { /* sentinel */ }
+};
+
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
{
- int ret = 0;
- struct device_node *np = adev->dev.of_node;
- struct nmk_i2c_dev *dev;
- struct i2c_adapter *adap;
struct i2c_vendor_data *vendor = id->data;
u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1;
+ struct device_node *np = adev->dev.of_node;
+ const struct of_device_id *match;
+ struct device *dev = &adev->dev;
+ unsigned long match_flags = 0;
+ struct nmk_i2c_dev *priv;
+ struct i2c_adapter *adap;
+ int ret = 0;
- dev = devm_kzalloc(&adev->dev, sizeof(struct nmk_i2c_dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&adev->dev, "cannot allocate memory\n");
- ret = -ENOMEM;
- goto err_no_mem;
- }
- dev->vendor = vendor;
- dev->adev = adev;
- nmk_i2c_of_probe(np, dev);
-
- if (dev->tft > max_fifo_threshold) {
- dev_warn(&adev->dev, "requested TX FIFO threshold %u, adjusted down to %u\n",
- dev->tft, max_fifo_threshold);
- dev->tft = max_fifo_threshold;
+ /*
+ * We do not want to attach a .of_match_table to our amba driver.
+ * Do not convert to device_get_match_data().
+ */
+ match = of_match_device(nmk_i2c_eyeq_match_table, dev);
+ if (match)
+ match_flags = (unsigned long)match->data;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->vendor = vendor;
+ priv->adev = adev;
+ priv->has_32b_bus = match_flags & NMK_I2C_EYEQ_FLAG_32B_BUS;
+ nmk_i2c_of_probe(np, priv);
+
+ if (match_flags & NMK_I2C_EYEQ_FLAG_IS_EYEQ5) {
+ ret = nmk_i2c_eyeq5_probe(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed OLB lookup\n");
}
- if (dev->rft > max_fifo_threshold) {
- dev_warn(&adev->dev, "requested RX FIFO threshold %u, adjusted down to %u\n",
- dev->rft, max_fifo_threshold);
- dev->rft = max_fifo_threshold;
+ if (priv->tft > max_fifo_threshold) {
+ dev_warn(dev, "requested TX FIFO threshold %u, adjusted down to %u\n",
+ priv->tft, max_fifo_threshold);
+ priv->tft = max_fifo_threshold;
}
- amba_set_drvdata(adev, dev);
-
- dev->virtbase = devm_ioremap(&adev->dev, adev->res.start,
- resource_size(&adev->res));
- if (!dev->virtbase) {
- ret = -ENOMEM;
- goto err_no_mem;
+ if (priv->rft > max_fifo_threshold) {
+ dev_warn(dev, "requested RX FIFO threshold %u, adjusted down to %u\n",
+ priv->rft, max_fifo_threshold);
+ priv->rft = max_fifo_threshold;
}
- dev->irq = adev->irq[0];
- ret = devm_request_irq(&adev->dev, dev->irq, i2c_irq_handler, 0,
- DRIVER_NAME, dev);
- if (ret) {
- dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq);
- goto err_no_mem;
- }
+ amba_set_drvdata(adev, priv);
- dev->clk = devm_clk_get(&adev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- dev_err(&adev->dev, "could not get i2c clock\n");
- ret = PTR_ERR(dev->clk);
- goto err_no_mem;
- }
+ priv->virtbase = devm_ioremap(dev, adev->res.start,
+ resource_size(&adev->res));
+ if (!priv->virtbase)
+ return -ENOMEM;
- ret = clk_prepare_enable(dev->clk);
- if (ret) {
- dev_err(&adev->dev, "can't prepare_enable clock\n");
- goto err_no_mem;
- }
+ priv->irq = adev->irq[0];
+ ret = devm_request_irq(dev, priv->irq, i2c_irq_handler, 0,
+ DRIVER_NAME, priv);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "cannot claim the irq %d\n", priv->irq);
+
+ priv->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "could enable i2c clock\n");
- init_hw(dev);
+ init_hw(priv);
- adap = &dev->adap;
+ adap = &priv->adap;
adap->dev.of_node = np;
- adap->dev.parent = &adev->dev;
+ adap->dev.parent = dev;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
adap->algo = &nmk_i2c_algo;
- adap->timeout = msecs_to_jiffies(dev->timeout);
+ adap->timeout = usecs_to_jiffies(priv->timeout_usecs);
snprintf(adap->name, sizeof(adap->name),
"Nomadik I2C at %pR", &adev->res);
- i2c_set_adapdata(adap, dev);
+ i2c_set_adapdata(adap, priv);
- dev_info(&adev->dev,
+ dev_info(dev,
"initialize %s on virtual base %p\n",
- adap->name, dev->virtbase);
+ adap->name, priv->virtbase);
ret = i2c_add_adapter(adap);
if (ret)
- goto err_no_adap;
+ return ret;
- pm_runtime_put(&adev->dev);
+ pm_runtime_put(dev);
return 0;
-
- err_no_adap:
- clk_disable_unprepare(dev->clk);
- err_no_mem:
-
- return ret;
}
-static int nmk_i2c_remove(struct amba_device *adev)
+static void nmk_i2c_remove(struct amba_device *adev)
{
- struct resource *res = &adev->res;
- struct nmk_i2c_dev *dev = amba_get_drvdata(adev);
+ struct nmk_i2c_dev *priv = amba_get_drvdata(adev);
- i2c_del_adapter(&dev->adap);
- flush_i2c_fifo(dev);
- disable_all_interrupts(dev);
- clear_all_interrupts(dev);
+ i2c_del_adapter(&priv->adap);
+ flush_i2c_fifo(priv);
+ disable_all_interrupts(priv);
+ clear_all_interrupts(priv);
/* disable the controller */
- i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
- clk_disable_unprepare(dev->clk);
- if (res)
- release_mem_region(res->start, resource_size(res));
-
- return 0;
+ i2c_clr_bit(priv->virtbase + I2C_CR, I2C_CR_PE);
}
static struct i2c_vendor_data vendor_stn8815 = {
@@ -1104,9 +1211,8 @@ MODULE_DEVICE_TABLE(amba, nmk_i2c_ids);
static struct amba_driver nmk_i2c_driver = {
.drv = {
- .owner = THIS_MODULE,
.name = DRIVER_NAME,
- .pm = &nmk_i2c_pm,
+ .pm = pm_ptr(&nmk_i2c_pm),
},
.id_table = nmk_i2c_ids,
.probe = nmk_i2c_probe,
@@ -1126,6 +1232,7 @@ static void __exit nmk_i2c_exit(void)
subsys_initcall(nmk_i2c_init);
module_exit(nmk_i2c_exit);
-MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR");
+MODULE_AUTHOR("Sachin Verma");
+MODULE_AUTHOR("Srinidhi KASAGAR");
MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
new file mode 100644
index 000000000000..8b7e15240fb0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,2621 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx I2C Controller driver
+ *
+ * Copyright (C) 2020 Nuvoton Technologies tali.perry@nuvoton.com
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+enum i2c_mode {
+ I2C_MASTER,
+ I2C_SLAVE,
+};
+
+/*
+ * External I2C Interface driver xfer indication values, which indicate status
+ * of the bus.
+ */
+enum i2c_state_ind {
+ I2C_NO_STATUS_IND = 0,
+ I2C_SLAVE_RCV_IND,
+ I2C_SLAVE_XMIT_IND,
+ I2C_SLAVE_XMIT_MISSING_DATA_IND,
+ I2C_SLAVE_RESTART_IND,
+ I2C_SLAVE_DONE_IND,
+ I2C_MASTER_DONE_IND,
+ I2C_NACK_IND,
+ I2C_BUS_ERR_IND,
+ I2C_WAKE_UP_IND,
+ I2C_BLOCK_BYTES_ERR_IND,
+ I2C_SLAVE_RCV_MISSING_DATA_IND,
+};
+
+/*
+ * Operation type values (used to define the operation currently running)
+ * module is interrupt driven, on each interrupt the current operation is
+ * checked to see if the module is currently reading or writing.
+ */
+enum i2c_oper {
+ I2C_NO_OPER = 0,
+ I2C_WRITE_OPER,
+ I2C_READ_OPER,
+};
+
+/* I2C Bank (module had 2 banks of registers) */
+enum i2c_bank {
+ I2C_BANK_0 = 0,
+ I2C_BANK_1,
+};
+
+/* Internal I2C states values (for the I2C module state machine). */
+enum i2c_state {
+ I2C_DISABLE = 0,
+ I2C_IDLE,
+ I2C_MASTER_START,
+ I2C_SLAVE_MATCH,
+ I2C_OPER_STARTED,
+ I2C_STOP_PENDING,
+};
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+/* Module supports setting multiple own slave addresses */
+enum i2c_addr {
+ I2C_SLAVE_ADDR1 = 0,
+ I2C_SLAVE_ADDR2,
+ I2C_SLAVE_ADDR3,
+ I2C_SLAVE_ADDR4,
+ I2C_SLAVE_ADDR5,
+ I2C_SLAVE_ADDR6,
+ I2C_SLAVE_ADDR7,
+ I2C_SLAVE_ADDR8,
+ I2C_SLAVE_ADDR9,
+ I2C_SLAVE_ADDR10,
+ I2C_GC_ADDR,
+ I2C_ARP_ADDR,
+};
+#endif
+
+/* init register and default value required to enable module */
+#define NPCM_I2CSEGCTL 0xE4
+
+/* Common regs */
+#define NPCM_I2CSDA 0x00
+#define NPCM_I2CST 0x02
+#define NPCM_I2CCST 0x04
+#define NPCM_I2CCTL1 0x06
+#define NPCM_I2CADDR1 0x08
+#define NPCM_I2CCTL2 0x0A
+#define NPCM_I2CADDR2 0x0C
+#define NPCM_I2CCTL3 0x0E
+#define NPCM_I2CCST2 0x18
+#define NPCM_I2CCST3 0x19
+#define I2C_VER 0x1F
+
+/* BANK 0 regs */
+#define NPCM_I2CADDR3 0x10
+#define NPCM_I2CADDR7 0x11
+#define NPCM_I2CADDR4 0x12
+#define NPCM_I2CADDR8 0x13
+#define NPCM_I2CADDR5 0x14
+#define NPCM_I2CADDR9 0x15
+#define NPCM_I2CADDR6 0x16
+#define NPCM_I2CADDR10 0x17
+#define NPCM_I2CCTL4 0x1A
+#define NPCM_I2CCTL5 0x1B
+#define NPCM_I2CSCLLT 0x1C /* SCL Low Time */
+#define NPCM_I2CFIF_CTL 0x1D /* FIFO Control */
+#define NPCM_I2CSCLHT 0x1E /* SCL High Time */
+
+/* BANK 1 regs */
+#define NPCM_I2CFIF_CTS 0x10 /* Both FIFOs Control and Status */
+#define NPCM_I2CTXF_CTL 0x12 /* Tx-FIFO Control */
+#define NPCM_I2CT_OUT 0x14 /* Bus T.O. */
+#define NPCM_I2CPEC 0x16 /* PEC Data */
+#define NPCM_I2CTXF_STS 0x1A /* Tx-FIFO Status */
+#define NPCM_I2CRXF_STS 0x1C /* Rx-FIFO Status */
+#define NPCM_I2CRXF_CTL 0x1E /* Rx-FIFO Control */
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+/*
+ * npcm_i2caddr array:
+ * The module supports having multiple own slave addresses.
+ * Since the addr regs are sprinkled all over the address space,
+ * use this array to get the address or each register.
+ */
+#define I2C_NUM_OWN_ADDR 10
+#define I2C_NUM_OWN_ADDR_SUPPORTED 2
+
+static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
+ NPCM_I2CADDR1, NPCM_I2CADDR2, NPCM_I2CADDR3, NPCM_I2CADDR4,
+ NPCM_I2CADDR5, NPCM_I2CADDR6, NPCM_I2CADDR7, NPCM_I2CADDR8,
+ NPCM_I2CADDR9, NPCM_I2CADDR10,
+};
+#endif
+
+/* NPCM_I2CST reg fields */
+#define NPCM_I2CST_XMIT BIT(0) /* Transmit mode */
+#define NPCM_I2CST_MASTER BIT(1) /* Master mode */
+#define NPCM_I2CST_NMATCH BIT(2) /* New match */
+#define NPCM_I2CST_STASTR BIT(3) /* Stall after start */
+#define NPCM_I2CST_NEGACK BIT(4) /* Negative ACK */
+#define NPCM_I2CST_BER BIT(5) /* Bus error */
+#define NPCM_I2CST_SDAST BIT(6) /* SDA status */
+#define NPCM_I2CST_SLVSTP BIT(7) /* Slave stop */
+
+/* NPCM_I2CCST reg fields */
+#define NPCM_I2CCST_BUSY BIT(0) /* Busy */
+#define NPCM_I2CCST_BB BIT(1) /* Bus busy */
+#define NPCM_I2CCST_MATCH BIT(2) /* Address match */
+#define NPCM_I2CCST_GCMATCH BIT(3) /* Global call match */
+#define NPCM_I2CCST_TSDA BIT(4) /* Test SDA line */
+#define NPCM_I2CCST_TGSCL BIT(5) /* Toggle SCL line */
+#define NPCM_I2CCST_MATCHAF BIT(6) /* Match address field */
+#define NPCM_I2CCST_ARPMATCH BIT(7) /* ARP address match */
+
+/* NPCM_I2CCTL1 reg fields */
+#define NPCM_I2CCTL1_START BIT(0) /* Generate start condition */
+#define NPCM_I2CCTL1_STOP BIT(1) /* Generate stop condition */
+#define NPCM_I2CCTL1_INTEN BIT(2) /* Interrupt enable */
+#define NPCM_I2CCTL1_EOBINTE BIT(3)
+#define NPCM_I2CCTL1_ACK BIT(4)
+#define NPCM_I2CCTL1_GCMEN BIT(5) /* Global call match enable */
+#define NPCM_I2CCTL1_NMINTE BIT(6) /* New match interrupt enable */
+#define NPCM_I2CCTL1_STASTRE BIT(7) /* Stall after start enable */
+
+/* RW1S fields (inside a RW reg): */
+#define NPCM_I2CCTL1_RWS \
+ (NPCM_I2CCTL1_START | NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_ACK)
+
+/* npcm_i2caddr reg fields */
+#define NPCM_I2CADDR_A GENMASK(6, 0) /* Address */
+#define NPCM_I2CADDR_SAEN BIT(7) /* Slave address enable */
+
+/* NPCM_I2CCTL2 reg fields */
+#define I2CCTL2_ENABLE BIT(0) /* Module enable */
+#define I2CCTL2_SCLFRQ6_0 GENMASK(7, 1) /* Bits 0:6 of frequency divisor */
+
+/* NPCM_I2CCTL3 reg fields */
+#define I2CCTL3_SCLFRQ8_7 GENMASK(1, 0) /* Bits 7:8 of frequency divisor */
+#define I2CCTL3_ARPMEN BIT(2) /* ARP match enable */
+#define I2CCTL3_IDL_START BIT(3)
+#define I2CCTL3_400K_MODE BIT(4)
+#define I2CCTL3_BNK_SEL BIT(5)
+#define I2CCTL3_SDA_LVL BIT(6)
+#define I2CCTL3_SCL_LVL BIT(7)
+
+/* NPCM_I2CCST2 reg fields */
+#define NPCM_I2CCST2_MATCHA1F BIT(0)
+#define NPCM_I2CCST2_MATCHA2F BIT(1)
+#define NPCM_I2CCST2_MATCHA3F BIT(2)
+#define NPCM_I2CCST2_MATCHA4F BIT(3)
+#define NPCM_I2CCST2_MATCHA5F BIT(4)
+#define NPCM_I2CCST2_MATCHA6F BIT(5)
+#define NPCM_I2CCST2_MATCHA7F BIT(5)
+#define NPCM_I2CCST2_INTSTS BIT(7)
+
+/* NPCM_I2CCST3 reg fields */
+#define NPCM_I2CCST3_MATCHA8F BIT(0)
+#define NPCM_I2CCST3_MATCHA9F BIT(1)
+#define NPCM_I2CCST3_MATCHA10F BIT(2)
+#define NPCM_I2CCST3_EO_BUSY BIT(7)
+
+/* NPCM_I2CCTL4 reg fields */
+#define I2CCTL4_HLDT GENMASK(5, 0)
+#define I2CCTL4_LVL_WE BIT(7)
+
+/* NPCM_I2CCTL5 reg fields */
+#define I2CCTL5_DBNCT GENMASK(3, 0)
+
+/* NPCM_I2CFIF_CTS reg fields */
+#define NPCM_I2CFIF_CTS_RXF_TXE BIT(1)
+#define NPCM_I2CFIF_CTS_RFTE_IE BIT(3)
+#define NPCM_I2CFIF_CTS_CLR_FIFO BIT(6)
+#define NPCM_I2CFIF_CTS_SLVRSTR BIT(7)
+
+/* NPCM_I2CTXF_CTL reg field */
+#define NPCM_I2CTXF_CTL_THR_TXIE BIT(6)
+
+/* NPCM_I2CT_OUT reg fields */
+#define NPCM_I2CT_OUT_TO_CKDIV GENMASK(5, 0)
+#define NPCM_I2CT_OUT_T_OUTIE BIT(6)
+#define NPCM_I2CT_OUT_T_OUTST BIT(7)
+
+/* NPCM_I2CTXF_STS reg fields */
+#define NPCM_I2CTXF_STS_TX_THST BIT(6)
+
+/* NPCM_I2CRXF_STS reg fields */
+#define NPCM_I2CRXF_STS_RX_THST BIT(6)
+
+/* NPCM_I2CFIF_CTL reg fields */
+#define NPCM_I2CFIF_CTL_FIFO_EN BIT(4)
+
+/* NPCM_I2CRXF_CTL reg fields */
+#define NPCM_I2CRXF_CTL_THR_RXIE BIT(6)
+
+#define MAX_I2C_HW_FIFO_SIZE 32
+
+/* I2C_VER reg fields */
+#define I2C_VER_VERSION GENMASK(6, 0)
+#define I2C_VER_FIFO_EN BIT(7)
+
+/* stall/stuck timeout in us */
+#define DEFAULT_STALL_COUNT 25
+
+/* SCLFRQ field position */
+#define SCLFRQ_0_TO_6 GENMASK(6, 0)
+#define SCLFRQ_7_TO_8 GENMASK(8, 7)
+
+/* supported clk settings. values in Hz. */
+#define I2C_FREQ_MIN_HZ 10000
+#define I2C_FREQ_MAX_HZ I2C_MAX_FAST_MODE_PLUS_FREQ
+
+struct smb_timing_t {
+ u32 core_clk;
+ u8 hldt;
+ u8 dbcnt;
+ u16 sclfrq;
+ u8 scllt;
+ u8 sclht;
+ bool fast_mode;
+};
+
+static struct smb_timing_t smb_timing_100khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x4,
+ .sclfrq = 0xFB, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x9D, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x7E, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x79, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x65, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 30000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x4C, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 29000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x49, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 26000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x42, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x3F, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x3D, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x33, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 16180000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x29, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 15000000, .hldt = 0x23, .dbcnt = 0x1,
+ .sclfrq = 0x26, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 13000000, .hldt = 0x1D, .dbcnt = 0x1,
+ .sclfrq = 0x21, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 12000000, .hldt = 0x1B, .dbcnt = 0x1,
+ .sclfrq = 0x1F, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 10000000, .hldt = 0x18, .dbcnt = 0x1,
+ .sclfrq = 0x1A, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 9000000, .hldt = 0x16, .dbcnt = 0x1,
+ .sclfrq = 0x17, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 8090000, .hldt = 0x14, .dbcnt = 0x1,
+ .sclfrq = 0x15, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 7500000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x13, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 6500000, .hldt = 0xE, .dbcnt = 0x1,
+ .sclfrq = 0x11, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 4000000, .hldt = 0x9, .dbcnt = 0x1,
+ .sclfrq = 0xB, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+};
+
+static struct smb_timing_t smb_timing_400khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x3,
+ .sclfrq = 0x0, .scllt = 0x47, .sclht = 0x35,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0x2C, .sclht = 0x22,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0x21, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x1B,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x1E, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x19,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x1B, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x1E, .sclht = 0x14,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 33000000, .hldt = 0x15, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x19, .sclht = 0x11,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 30000000, .hldt = 0x15, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x19, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 29000000, .hldt = 0x11, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x15, .sclht = 0x10,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 26000000, .hldt = 0x10, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xE,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0xF, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0xD, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x12, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0xB, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xF, .sclht = 0xA,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 16180000, .hldt = 0xA, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 15000000, .hldt = 0x9, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x8,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 13000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 12000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x6,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 10000000, .hldt = 0x6, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5,
+ .fast_mode = true,
+ },
+};
+
+static struct smb_timing_t smb_timing_1000khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x15, .dbcnt = 0x4,
+ .sclfrq = 0x0, .scllt = 0x1C, .sclht = 0x15,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0xF, .dbcnt = 0x3,
+ .sclfrq = 0x0, .scllt = 0x11, .sclht = 0xE,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0xA, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xE, .sclht = 0xB,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x9, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xD, .sclht = 0xB,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 41000000, .hldt = 0x9, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x8, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 33000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0x4, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x7, .sclht = 0x6,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0x4, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x6, .sclht = 0x4,
+ .fast_mode = true,
+ },
+};
+
+struct npcm_i2c_data {
+ u8 fifo_size;
+ u32 segctl_init_val;
+ u8 txf_sts_tx_bytes;
+ u8 rxf_sts_rx_bytes;
+ u8 rxf_ctl_last_pec;
+};
+
+static const struct npcm_i2c_data npxm7xx_i2c_data = {
+ .fifo_size = 16,
+ .segctl_init_val = 0x0333F000,
+ .txf_sts_tx_bytes = GENMASK(4, 0),
+ .rxf_sts_rx_bytes = GENMASK(4, 0),
+ .rxf_ctl_last_pec = BIT(5),
+};
+
+static const struct npcm_i2c_data npxm8xx_i2c_data = {
+ .fifo_size = 32,
+ .segctl_init_val = 0x9333F000,
+ .txf_sts_tx_bytes = GENMASK(5, 0),
+ .rxf_sts_rx_bytes = GENMASK(5, 0),
+ .rxf_ctl_last_pec = BIT(7),
+};
+
+/* Status of one I2C module */
+struct npcm_i2c {
+ struct i2c_adapter adap;
+ struct device *dev;
+ unsigned char __iomem *reg;
+ const struct npcm_i2c_data *data;
+ spinlock_t lock; /* IRQ synchronization */
+ struct completion cmd_complete;
+ int cmd_err;
+ struct i2c_msg *msgs;
+ int msgs_num;
+ int num;
+ u32 apb_clk;
+ struct i2c_bus_recovery_info rinfo;
+ enum i2c_state state;
+ enum i2c_oper operation;
+ enum i2c_mode master_or_slave;
+ enum i2c_state_ind stop_ind;
+ u8 dest_addr;
+ u8 *rd_buf;
+ u16 rd_size;
+ u16 rd_ind;
+ u8 *wr_buf;
+ u16 wr_size;
+ u16 wr_ind;
+ bool fifo_use;
+ u16 PEC_mask; /* PEC bit mask per slave address */
+ bool PEC_use;
+ bool read_block_use;
+ unsigned long int_time_stamp;
+ unsigned long bus_freq; /* in Hz */
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ u8 own_slave_addr;
+ struct i2c_client *slave;
+ int slv_rd_size;
+ int slv_rd_ind;
+ int slv_wr_size;
+ int slv_wr_ind;
+ u8 slv_rd_buf[MAX_I2C_HW_FIFO_SIZE];
+ u8 slv_wr_buf[MAX_I2C_HW_FIFO_SIZE];
+#endif
+ u64 ber_cnt;
+ u64 rec_succ_cnt;
+ u64 rec_fail_cnt;
+ u64 nack_cnt;
+ u64 timeout_cnt;
+ u64 tx_complete_cnt;
+ bool ber_state; /* Indicate the bus error state */
+};
+
+static inline void npcm_i2c_select_bank(struct npcm_i2c *bus,
+ enum i2c_bank bank)
+{
+ u8 i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3);
+
+ if (bank == I2C_BANK_0)
+ i2cctl3 = i2cctl3 & ~I2CCTL3_BNK_SEL;
+ else
+ i2cctl3 = i2cctl3 | I2CCTL3_BNK_SEL;
+ iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3);
+}
+
+static void npcm_i2c_init_params(struct npcm_i2c *bus)
+{
+ bus->stop_ind = I2C_NO_STATUS_IND;
+ bus->rd_size = 0;
+ bus->wr_size = 0;
+ bus->rd_ind = 0;
+ bus->wr_ind = 0;
+ bus->read_block_use = false;
+ bus->int_time_stamp = 0;
+ bus->PEC_use = false;
+ bus->PEC_mask = 0;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave)
+ bus->master_or_slave = I2C_SLAVE;
+#endif
+}
+
+static inline void npcm_i2c_wr_byte(struct npcm_i2c *bus, u8 data)
+{
+ iowrite8(data, bus->reg + NPCM_I2CSDA);
+}
+
+static inline u8 npcm_i2c_rd_byte(struct npcm_i2c *bus)
+{
+ return ioread8(bus->reg + NPCM_I2CSDA);
+}
+
+static int npcm_i2c_get_SCL(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ return !!(I2CCTL3_SCL_LVL & ioread8(bus->reg + NPCM_I2CCTL3));
+}
+
+static int npcm_i2c_get_SDA(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ return !!(I2CCTL3_SDA_LVL & ioread8(bus->reg + NPCM_I2CCTL3));
+}
+
+static inline u16 npcm_i2c_get_index(struct npcm_i2c *bus)
+{
+ if (bus->operation == I2C_READ_OPER)
+ return bus->rd_ind;
+ if (bus->operation == I2C_WRITE_OPER)
+ return bus->wr_ind;
+ return 0;
+}
+
+/* quick protocol (just address) */
+static inline bool npcm_i2c_is_quick(struct npcm_i2c *bus)
+{
+ return bus->wr_size == 0 && bus->rd_size == 0;
+}
+
+static void npcm_i2c_disable(struct npcm_i2c *bus)
+{
+ u8 i2cctl2;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ int i;
+
+ /* Slave addresses removal */
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++)
+ iowrite8(0, bus->reg + npcm_i2caddr[i]);
+
+#endif
+ /* Disable module */
+ i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
+ i2cctl2 = i2cctl2 & ~I2CCTL2_ENABLE;
+ iowrite8(i2cctl2, bus->reg + NPCM_I2CCTL2);
+
+ bus->state = I2C_DISABLE;
+}
+
+static void npcm_i2c_enable(struct npcm_i2c *bus)
+{
+ u8 i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
+
+ i2cctl2 = i2cctl2 | I2CCTL2_ENABLE;
+ iowrite8(i2cctl2, bus->reg + NPCM_I2CCTL2);
+ bus->state = I2C_IDLE;
+}
+
+/* enable\disable end of busy (EOB) interrupts */
+static inline void npcm_i2c_eob_int(struct npcm_i2c *bus, bool enable)
+{
+ u8 val;
+
+ /* Clear EO_BUSY pending bit: */
+ val = ioread8(bus->reg + NPCM_I2CCST3);
+ val = val | NPCM_I2CCST3_EO_BUSY;
+ iowrite8(val, bus->reg + NPCM_I2CCST3);
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ val |= NPCM_I2CCTL1_EOBINTE;
+ else
+ val &= ~NPCM_I2CCTL1_EOBINTE;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline bool npcm_i2c_tx_fifo_empty(struct npcm_i2c *bus)
+{
+ u8 tx_fifo_sts;
+
+ tx_fifo_sts = ioread8(bus->reg + NPCM_I2CTXF_STS);
+ /* check if TX FIFO is not empty */
+ if ((tx_fifo_sts & bus->data->txf_sts_tx_bytes) == 0)
+ return false;
+
+ /* check if TX FIFO status bit is set: */
+ return !!FIELD_GET(NPCM_I2CTXF_STS_TX_THST, tx_fifo_sts);
+}
+
+static inline bool npcm_i2c_rx_fifo_full(struct npcm_i2c *bus)
+{
+ u8 rx_fifo_sts;
+
+ rx_fifo_sts = ioread8(bus->reg + NPCM_I2CRXF_STS);
+ /* check if RX FIFO is not empty: */
+ if ((rx_fifo_sts & bus->data->rxf_sts_rx_bytes) == 0)
+ return false;
+
+ /* check if rx fifo full status is set: */
+ return !!FIELD_GET(NPCM_I2CRXF_STS_RX_THST, rx_fifo_sts);
+}
+
+static inline void npcm_i2c_clear_fifo_int(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ val = (val & NPCM_I2CFIF_CTS_SLVRSTR) | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTS);
+}
+
+static inline void npcm_i2c_clear_tx_fifo(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CTXF_STS);
+ val = val | NPCM_I2CTXF_STS_TX_THST;
+ iowrite8(val, bus->reg + NPCM_I2CTXF_STS);
+}
+
+static inline void npcm_i2c_clear_rx_fifo(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CRXF_STS);
+ val = val | NPCM_I2CRXF_STS_RX_THST;
+ iowrite8(val, bus->reg + NPCM_I2CRXF_STS);
+}
+
+static void npcm_i2c_int_enable(struct npcm_i2c *bus, bool enable)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ val |= NPCM_I2CCTL1_INTEN;
+ else
+ val &= ~NPCM_I2CCTL1_INTEN;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_master_start(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_ACK);
+ val |= NPCM_I2CCTL1_START;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_master_stop(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ /*
+ * override HW issue: I2C may fail to supply stop condition in Master
+ * Write operation.
+ * Need to delay at least 5 us from the last int, before issueing a stop
+ */
+ udelay(10); /* function called from interrupt, can't sleep */
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_START | NPCM_I2CCTL1_ACK);
+ val |= NPCM_I2CCTL1_STOP;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+
+ if (!bus->fifo_use)
+ return;
+
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_clear_rx_fifo(bus);
+ else
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_fifo_int(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+}
+
+static inline void npcm_i2c_stall_after_start(struct npcm_i2c *bus, bool stall)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (stall)
+ val |= NPCM_I2CCTL1_STASTRE;
+ else
+ val &= ~NPCM_I2CCTL1_STASTRE;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_nack(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_START);
+ val |= NPCM_I2CCTL1_ACK;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_clear_master_status(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ /* Clear NEGACK, STASTR and BER bits */
+ val = NPCM_I2CST_BER | NPCM_I2CST_NEGACK | NPCM_I2CST_STASTR;
+ iowrite8(val, bus->reg + NPCM_I2CST);
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void npcm_i2c_slave_int_enable(struct npcm_i2c *bus, bool enable)
+{
+ u8 i2cctl1;
+
+ /* enable interrupt on slave match: */
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+ i2cctl1 &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ i2cctl1 |= NPCM_I2CCTL1_NMINTE;
+ else
+ i2cctl1 &= ~NPCM_I2CCTL1_NMINTE;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+}
+
+static int npcm_i2c_slave_enable(struct npcm_i2c *bus, enum i2c_addr addr_type,
+ u8 addr, bool enable)
+{
+ u8 i2cctl1;
+ u8 i2cctl3;
+ u8 sa_reg;
+
+ sa_reg = (addr & 0x7F) | FIELD_PREP(NPCM_I2CADDR_SAEN, enable);
+ if (addr_type == I2C_GC_ADDR) {
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+ if (enable)
+ i2cctl1 |= NPCM_I2CCTL1_GCMEN;
+ else
+ i2cctl1 &= ~NPCM_I2CCTL1_GCMEN;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+ return 0;
+ } else if (addr_type == I2C_ARP_ADDR) {
+ i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3);
+ if (enable)
+ i2cctl3 |= I2CCTL3_ARPMEN;
+ else
+ i2cctl3 &= ~I2CCTL3_ARPMEN;
+ iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3);
+ return 0;
+ }
+ if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10)
+ dev_err(bus->dev, "try to enable more than 2 SA not supported\n");
+
+ if (addr_type >= I2C_ARP_ADDR)
+ return -EFAULT;
+
+ /* Set and enable the address */
+ iowrite8(sa_reg, bus->reg + npcm_i2caddr[addr_type]);
+ npcm_i2c_slave_int_enable(bus, enable);
+
+ return 0;
+}
+#endif
+
+static void npcm_i2c_reset(struct npcm_i2c *bus)
+{
+ /*
+ * Save I2CCTL1 relevant bits. It is being cleared when the module
+ * is disabled.
+ */
+ u8 i2cctl1;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ u8 addr;
+#endif
+
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+
+ npcm_i2c_disable(bus);
+ npcm_i2c_enable(bus);
+
+ /* Restore NPCM_I2CCTL1 Status */
+ i2cctl1 &= ~NPCM_I2CCTL1_RWS;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+
+ /* Clear BB (BUS BUSY) bit */
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ iowrite8(0xFF, bus->reg + NPCM_I2CST);
+
+ /* Clear and disable EOB */
+ npcm_i2c_eob_int(bus, false);
+
+ /* Clear all fifo bits: */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave) {
+ addr = bus->slave->addr;
+ npcm_i2c_slave_enable(bus, I2C_SLAVE_ADDR1, addr, true);
+ }
+#endif
+
+ /* Clear status bits for spurious interrupts */
+ npcm_i2c_clear_master_status(bus);
+
+ bus->state = I2C_IDLE;
+}
+
+static inline bool npcm_i2c_is_master(struct npcm_i2c *bus)
+{
+ return !!FIELD_GET(NPCM_I2CST_MASTER, ioread8(bus->reg + NPCM_I2CST));
+}
+
+static void npcm_i2c_callback(struct npcm_i2c *bus,
+ enum i2c_state_ind op_status, u16 info)
+{
+ struct i2c_msg *msgs;
+ int msgs_num;
+ bool do_complete = false;
+
+ msgs = bus->msgs;
+ msgs_num = bus->msgs_num;
+ /*
+ * check that transaction was not timed-out, and msgs still
+ * holds a valid value.
+ */
+ if (!msgs)
+ return;
+
+ if (completion_done(&bus->cmd_complete))
+ return;
+
+ switch (op_status) {
+ case I2C_MASTER_DONE_IND:
+ bus->cmd_err = bus->msgs_num;
+ if (bus->tx_complete_cnt < ULLONG_MAX)
+ bus->tx_complete_cnt++;
+ fallthrough;
+ case I2C_BLOCK_BYTES_ERR_IND:
+ /* Master tx finished and all transmit bytes were sent */
+ if (bus->msgs) {
+ if (msgs[0].flags & I2C_M_RD)
+ msgs[0].len = info;
+ else if (msgs_num == 2 &&
+ msgs[1].flags & I2C_M_RD)
+ msgs[1].len = info;
+ }
+ do_complete = true;
+ break;
+ case I2C_NACK_IND:
+ /* MASTER transmit got a NACK before tx all bytes */
+ bus->cmd_err = -ENXIO;
+ do_complete = true;
+ break;
+ case I2C_BUS_ERR_IND:
+ /* Bus error */
+ bus->cmd_err = -EAGAIN;
+ do_complete = true;
+ break;
+ case I2C_WAKE_UP_IND:
+ /* I2C wake up */
+ break;
+ default:
+ break;
+ }
+
+ bus->operation = I2C_NO_OPER;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave)
+ bus->master_or_slave = I2C_SLAVE;
+#endif
+ if (do_complete)
+ complete(&bus->cmd_complete);
+}
+
+static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus)
+{
+ if (bus->operation == I2C_WRITE_OPER)
+ return (bus->data->txf_sts_tx_bytes &
+ ioread8(bus->reg + NPCM_I2CTXF_STS));
+ if (bus->operation == I2C_READ_OPER)
+ return (bus->data->rxf_sts_rx_bytes &
+ ioread8(bus->reg + NPCM_I2CRXF_STS));
+ return 0;
+}
+
+static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus, u16 max_bytes)
+{
+ u8 size_free_fifo;
+
+ /*
+ * Fill the FIFO, while the FIFO is not full and there are more bytes
+ * to write
+ */
+ size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus);
+ while (max_bytes-- && size_free_fifo) {
+ if (bus->wr_ind < bus->wr_size)
+ npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
+ else
+ npcm_i2c_wr_byte(bus, 0xFF);
+ size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus);
+ }
+}
+
+/*
+ * npcm_i2c_set_fifo:
+ * configure the FIFO before using it. If nread is -1 RX FIFO will not be
+ * configured. same for nwrite
+ */
+static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
+{
+ u8 rxf_ctl = 0;
+
+ if (!bus->fifo_use)
+ return;
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+
+ /* configure RX FIFO */
+ if (nread > 0) {
+ rxf_ctl = min_t(int, nread, bus->data->fifo_size);
+
+ /* set LAST bit. if LAST is set next FIFO packet is nacked */
+ if (nread <= bus->data->fifo_size)
+ rxf_ctl |= bus->data->rxf_ctl_last_pec;
+
+ /*
+ * if we are about to read the first byte in blk rd mode,
+ * don't NACK it. If slave returns zero size HW can't NACK
+ * it immediately, it will read extra byte and then NACK.
+ */
+ if (bus->rd_ind == 0 && bus->read_block_use) {
+ /* set fifo to read one byte, no last: */
+ rxf_ctl = 1;
+ }
+
+ /* set fifo size: */
+ iowrite8(rxf_ctl, bus->reg + NPCM_I2CRXF_CTL);
+ }
+
+ /* configure TX FIFO */
+ if (nwrite > 0) {
+ if (nwrite > bus->data->fifo_size)
+ /* data to send is more then FIFO size. */
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CTXF_CTL);
+ else
+ iowrite8(nwrite, bus->reg + NPCM_I2CTXF_CTL);
+
+ npcm_i2c_clear_tx_fifo(bus);
+ }
+}
+
+static void npcm_i2c_read_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
+{
+ u8 data;
+
+ while (bytes_in_fifo--) {
+ data = npcm_i2c_rd_byte(bus);
+ if (bus->rd_ind < bus->rd_size)
+ bus->rd_buf[bus->rd_ind++] = data;
+ }
+}
+
+static void npcm_i2c_master_abort(struct npcm_i2c *bus)
+{
+ /* Only current master is allowed to issue a stop condition */
+ if (!npcm_i2c_is_master(bus))
+ return;
+
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_clear_master_status(bus);
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static u8 npcm_i2c_get_slave_addr(struct npcm_i2c *bus, enum i2c_addr addr_type)
+{
+ if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10)
+ dev_err(bus->dev, "get slave: try to use more than 2 SA not supported\n");
+
+ return ioread8(bus->reg + npcm_i2caddr[addr_type]);
+}
+
+static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add)
+{
+ int i;
+
+ /* Set the enable bit */
+ slave_add |= 0x80;
+
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++) {
+ if (ioread8(bus->reg + npcm_i2caddr[i]) == slave_add)
+ iowrite8(0, bus->reg + npcm_i2caddr[i]);
+ }
+
+ return 0;
+}
+
+static void npcm_i2c_write_fifo_slave(struct npcm_i2c *bus, u16 max_bytes)
+{
+ /*
+ * Fill the FIFO, while the FIFO is not full and there are more bytes
+ * to write
+ */
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ while (max_bytes-- && bus->data->fifo_size != npcm_i2c_fifo_usage(bus)) {
+ if (bus->slv_wr_size <= 0)
+ break;
+ bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1);
+ npcm_i2c_wr_byte(bus, bus->slv_wr_buf[bus->slv_wr_ind]);
+ bus->slv_wr_ind++;
+ bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1);
+ bus->slv_wr_size--;
+ }
+}
+
+static void npcm_i2c_read_fifo_slave(struct npcm_i2c *bus, u8 bytes_in_fifo)
+{
+ u8 data;
+
+ if (!bus->slave)
+ return;
+
+ while (bytes_in_fifo--) {
+ data = npcm_i2c_rd_byte(bus);
+
+ bus->slv_rd_ind = bus->slv_rd_ind & (bus->data->fifo_size - 1);
+ bus->slv_rd_buf[bus->slv_rd_ind] = data;
+ bus->slv_rd_ind++;
+
+ /* 1st byte is length in block protocol: */
+ if (bus->slv_rd_ind == 1 && bus->read_block_use)
+ bus->slv_rd_size = data + bus->PEC_use + 1;
+ }
+}
+
+static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus)
+{
+ int i;
+ u8 value;
+ int ind;
+ int ret = bus->slv_wr_ind;
+
+ /* fill a cyclic buffer */
+ for (i = 0; i < bus->data->fifo_size; i++) {
+ if (bus->slv_wr_size >= bus->data->fifo_size)
+ break;
+ if (bus->state == I2C_SLAVE_MATCH) {
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value);
+ bus->state = I2C_OPER_STARTED;
+ } else {
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+ ind = (bus->slv_wr_ind + bus->slv_wr_size) & (bus->data->fifo_size - 1);
+ bus->slv_wr_buf[ind] = value;
+ bus->slv_wr_size++;
+ }
+ return bus->data->fifo_size - ret;
+}
+
+static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus)
+{
+ int i;
+
+ for (i = 0; i < bus->slv_rd_ind; i++)
+ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED,
+ &bus->slv_rd_buf[i]);
+ /*
+ * once we send bytes up, need to reset the counter of the wr buf
+ * got data from master (new offset in device), ignore wr fifo:
+ */
+ if (bus->slv_rd_ind) {
+ bus->slv_wr_size = 0;
+ bus->slv_wr_ind = 0;
+ }
+
+ bus->slv_rd_ind = 0;
+ bus->slv_rd_size = bus->adap.quirks->max_read_len;
+
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+}
+
+static void npcm_i2c_slave_receive(struct npcm_i2c *bus, u16 nread,
+ u8 *read_data)
+{
+ bus->state = I2C_OPER_STARTED;
+ bus->operation = I2C_READ_OPER;
+ bus->slv_rd_size = nread;
+ bus->slv_rd_ind = 0;
+
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+}
+
+static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
+ u8 *write_data)
+{
+ if (nwrite == 0)
+ return;
+
+ bus->operation = I2C_WRITE_OPER;
+
+ /* get the next buffer */
+ npcm_i2c_slave_get_wr_buf(bus);
+ npcm_i2c_write_fifo_slave(bus, nwrite);
+}
+
+/*
+ * npcm_i2c_slave_wr_buf_sync:
+ * currently slave IF only supports single byte operations.
+ * in order to utilize the npcm HW FIFO, the driver will ask for 16 bytes
+ * at a time, pack them in buffer, and then transmit them all together
+ * to the FIFO and onward to the bus.
+ * NACK on read will be once reached to bus->adap->quirks->max_read_len.
+ * sending a NACK wherever the backend requests for it is not supported.
+ * the next two functions allow reading to local buffer before writing it all
+ * to the HW FIFO.
+ */
+static void npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus)
+{
+ int left_in_fifo;
+
+ left_in_fifo = bus->data->txf_sts_tx_bytes &
+ ioread8(bus->reg + NPCM_I2CTXF_STS);
+
+ /* fifo already full: */
+ if (left_in_fifo >= bus->data->fifo_size ||
+ bus->slv_wr_size >= bus->data->fifo_size)
+ return;
+
+ /* update the wr fifo index back to the untransmitted bytes: */
+ bus->slv_wr_ind = bus->slv_wr_ind - left_in_fifo;
+ bus->slv_wr_size = bus->slv_wr_size + left_in_fifo;
+
+ if (bus->slv_wr_ind < 0)
+ bus->slv_wr_ind += bus->data->fifo_size;
+}
+
+static void npcm_i2c_slave_rd_wr(struct npcm_i2c *bus)
+{
+ if (NPCM_I2CST_XMIT & ioread8(bus->reg + NPCM_I2CST)) {
+ /*
+ * Slave got an address match with direction bit 1 so it should
+ * transmit data. Write till the master will NACK
+ */
+ bus->operation = I2C_WRITE_OPER;
+ npcm_i2c_slave_xmit(bus, bus->adap.quirks->max_write_len,
+ bus->slv_wr_buf);
+ } else {
+ /*
+ * Slave got an address match with direction bit 0 so it should
+ * receive data.
+ * this module does not support saying no to bytes.
+ * it will always ACK.
+ */
+ bus->operation = I2C_READ_OPER;
+ npcm_i2c_read_fifo_slave(bus, npcm_i2c_fifo_usage(bus));
+ bus->stop_ind = I2C_SLAVE_RCV_IND;
+ npcm_i2c_slave_send_rd_buf(bus);
+ npcm_i2c_slave_receive(bus, bus->adap.quirks->max_read_len,
+ bus->slv_rd_buf);
+ }
+}
+
+static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
+{
+ u8 val;
+ irqreturn_t ret = IRQ_NONE;
+ u8 i2cst = ioread8(bus->reg + NPCM_I2CST);
+
+ /* Slave: A NACK has occurred */
+ if (NPCM_I2CST_NEGACK & i2cst) {
+ bus->stop_ind = I2C_NACK_IND;
+ npcm_i2c_slave_wr_buf_sync(bus);
+ if (bus->fifo_use)
+ /* clear the FIFO */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO,
+ bus->reg + NPCM_I2CFIF_CTS);
+
+ /* In slave write, NACK is OK, otherwise it is a problem */
+ bus->stop_ind = I2C_NO_STATUS_IND;
+ bus->operation = I2C_NO_OPER;
+ bus->own_slave_addr = 0xFF;
+
+ /*
+ * Slave has to wait for STOP to decide this is the end
+ * of the transaction. tx is not yet considered as done
+ */
+ iowrite8(NPCM_I2CST_NEGACK, bus->reg + NPCM_I2CST);
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Slave mode: a Bus Error (BER) has been identified */
+ if (NPCM_I2CST_BER & i2cst) {
+ /*
+ * Check whether bus arbitration or Start or Stop during data
+ * xfer bus arbitration problem should not result in recovery
+ */
+ bus->stop_ind = I2C_BUS_ERR_IND;
+
+ /* wait for bus busy before clear fifo */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+
+ bus->state = I2C_IDLE;
+
+ /*
+ * in BER case we might get 2 interrupts: one for slave one for
+ * master ( for a channel which is master\slave switching)
+ */
+ if (completion_done(&bus->cmd_complete) == false) {
+ bus->cmd_err = -EIO;
+ complete(&bus->cmd_complete);
+ }
+ bus->own_slave_addr = 0xFF;
+ iowrite8(NPCM_I2CST_BER, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ }
+
+ /* A Slave Stop Condition has been identified */
+ if (NPCM_I2CST_SLVSTP & i2cst) {
+ u8 bytes_in_fifo = npcm_i2c_fifo_usage(bus);
+
+ bus->stop_ind = I2C_SLAVE_DONE_IND;
+
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_read_fifo_slave(bus, bytes_in_fifo);
+
+ /* if the buffer is empty nothing will be sent */
+ npcm_i2c_slave_send_rd_buf(bus);
+
+ /* Slave done transmitting or receiving */
+ bus->stop_ind = I2C_NO_STATUS_IND;
+
+ /*
+ * Note, just because we got here, it doesn't mean we through
+ * away the wr buffer.
+ * we keep it until the next received offset.
+ */
+ bus->operation = I2C_NO_OPER;
+ bus->own_slave_addr = 0xFF;
+ i2c_slave_event(bus->slave, I2C_SLAVE_STOP, 0);
+ iowrite8(NPCM_I2CST_SLVSTP, bus->reg + NPCM_I2CST);
+ if (bus->fifo_use) {
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO,
+ bus->reg + NPCM_I2CFIF_CTS);
+ }
+ bus->state = I2C_IDLE;
+ ret = IRQ_HANDLED;
+ }
+
+ /* restart condition occurred and Rx-FIFO was not empty */
+ if (bus->fifo_use && FIELD_GET(NPCM_I2CFIF_CTS_SLVRSTR,
+ ioread8(bus->reg + NPCM_I2CFIF_CTS))) {
+ bus->stop_ind = I2C_SLAVE_RESTART_IND;
+ bus->master_or_slave = I2C_SLAVE;
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_read_fifo_slave(bus, npcm_i2c_fifo_usage(bus));
+ bus->operation = I2C_WRITE_OPER;
+ iowrite8(0, bus->reg + NPCM_I2CRXF_CTL);
+ val = NPCM_I2CFIF_CTS_CLR_FIFO | NPCM_I2CFIF_CTS_SLVRSTR |
+ NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTS);
+ npcm_i2c_slave_rd_wr(bus);
+ ret = IRQ_HANDLED;
+ }
+
+ /* A Slave Address Match has been identified */
+ if (NPCM_I2CST_NMATCH & i2cst) {
+ u8 info = 0;
+
+ /* Address match automatically implies slave mode */
+ bus->master_or_slave = I2C_SLAVE;
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL);
+ if (NPCM_I2CST_XMIT & i2cst) {
+ bus->operation = I2C_WRITE_OPER;
+ } else {
+ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_REQUESTED,
+ &info);
+ bus->operation = I2C_READ_OPER;
+ }
+ if (bus->own_slave_addr == 0xFF) {
+ /* Check which type of address match */
+ val = ioread8(bus->reg + NPCM_I2CCST);
+ if (NPCM_I2CCST_MATCH & val) {
+ u16 addr;
+ enum i2c_addr eaddr;
+ u8 i2ccst2;
+ u8 i2ccst3;
+
+ i2ccst3 = ioread8(bus->reg + NPCM_I2CCST3);
+ i2ccst2 = ioread8(bus->reg + NPCM_I2CCST2);
+
+ /*
+ * the i2c module can response to 10 own SA.
+ * check which one was addressed by the master.
+ * respond to the first one.
+ */
+ addr = ((i2ccst3 & 0x07) << 7) |
+ (i2ccst2 & 0x7F);
+ info = ffs(addr);
+ eaddr = (enum i2c_addr)info;
+ addr = npcm_i2c_get_slave_addr(bus, eaddr);
+ addr &= 0x7F;
+ bus->own_slave_addr = addr;
+ if (bus->PEC_mask & BIT(info))
+ bus->PEC_use = true;
+ else
+ bus->PEC_use = false;
+ } else {
+ if (NPCM_I2CCST_GCMATCH & val)
+ bus->own_slave_addr = 0;
+ if (NPCM_I2CCST_ARPMATCH & val)
+ bus->own_slave_addr = 0x61;
+ }
+ } else {
+ /*
+ * Slave match can happen in two options:
+ * 1. Start, SA, read (slave read without further ado)
+ * 2. Start, SA, read, data, restart, SA, read, ...
+ * (slave read in fragmented mode)
+ * 3. Start, SA, write, data, restart, SA, read, ..
+ * (regular write-read mode)
+ */
+ if ((bus->state == I2C_OPER_STARTED &&
+ bus->operation == I2C_READ_OPER &&
+ bus->stop_ind == I2C_SLAVE_XMIT_IND) ||
+ bus->stop_ind == I2C_SLAVE_RCV_IND) {
+ /* slave tx after slave rx w/o STOP */
+ bus->stop_ind = I2C_SLAVE_RESTART_IND;
+ }
+ }
+
+ if (NPCM_I2CST_XMIT & i2cst)
+ bus->stop_ind = I2C_SLAVE_XMIT_IND;
+ else
+ bus->stop_ind = I2C_SLAVE_RCV_IND;
+ bus->state = I2C_SLAVE_MATCH;
+ npcm_i2c_slave_rd_wr(bus);
+ iowrite8(NPCM_I2CST_NMATCH, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ }
+
+ /* Slave SDA status is set - tx or rx */
+ if ((NPCM_I2CST_SDAST & i2cst) ||
+ (bus->fifo_use &&
+ (npcm_i2c_tx_fifo_empty(bus) || npcm_i2c_rx_fifo_full(bus)))) {
+ npcm_i2c_slave_rd_wr(bus);
+ iowrite8(NPCM_I2CST_SDAST, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ } /* SDAST */
+
+ /*
+ * If irq is not one of the above, make sure EOB is disabled and all
+ * status bits are cleared.
+ */
+ if (ret == IRQ_NONE) {
+ npcm_i2c_eob_int(bus, false);
+ npcm_i2c_clear_master_status(bus);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_i2c_reg_slave(struct i2c_client *client)
+{
+ unsigned long lock_flags;
+ struct npcm_i2c *bus = i2c_get_adapdata(client->adapter);
+
+ bus->slave = client;
+
+ if (client->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+
+ npcm_i2c_init_params(bus);
+ bus->slv_rd_size = 0;
+ bus->slv_wr_size = 0;
+ bus->slv_rd_ind = 0;
+ bus->slv_wr_ind = 0;
+ if (client->flags & I2C_CLIENT_PEC)
+ bus->PEC_use = true;
+
+ dev_info(bus->dev, "i2c%d register slave SA=0x%x, PEC=%d\n", bus->num,
+ client->addr, bus->PEC_use);
+
+ npcm_i2c_slave_enable(bus, I2C_SLAVE_ADDR1, client->addr, true);
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_slave_int_enable(bus, true);
+
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return 0;
+}
+
+static int npcm_i2c_unreg_slave(struct i2c_client *client)
+{
+ struct npcm_i2c *bus = client->adapter->algo_data;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+ if (!bus->slave) {
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return -EINVAL;
+ }
+ npcm_i2c_slave_int_enable(bus, false);
+ npcm_i2c_remove_slave_addr(bus, client->addr);
+ bus->slave = NULL;
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return 0;
+}
+#endif /* CONFIG_I2C_SLAVE */
+
+static void npcm_i2c_master_fifo_read(struct npcm_i2c *bus)
+{
+ int rcount;
+ int fifo_bytes;
+ enum i2c_state_ind ind = I2C_MASTER_DONE_IND;
+
+ fifo_bytes = npcm_i2c_fifo_usage(bus);
+ rcount = bus->rd_size - bus->rd_ind;
+
+ /*
+ * In order not to change the RX_TRH during transaction (we found that
+ * this might be problematic if it takes too much time to read the FIFO)
+ * we read the data in the following way. If the number of bytes to
+ * read == FIFO Size + C (where C < FIFO Size)then first read C bytes
+ * and in the next int we read rest of the data.
+ */
+ if (rcount < (2 * bus->data->fifo_size) && rcount > bus->data->fifo_size)
+ fifo_bytes = rcount - bus->data->fifo_size;
+
+ if (rcount <= fifo_bytes) {
+ /* last bytes are about to be read - end of tx */
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = ind;
+ npcm_i2c_eob_int(bus, true);
+ /* Stop should be set before reading last byte. */
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_read_fifo(bus, fifo_bytes);
+ } else {
+ npcm_i2c_read_fifo(bus, fifo_bytes);
+ rcount = bus->rd_size - bus->rd_ind;
+ npcm_i2c_set_fifo(bus, rcount, -1);
+ }
+}
+
+static void npcm_i2c_irq_master_handler_write(struct npcm_i2c *bus)
+{
+ u16 wcount;
+
+ if (bus->fifo_use)
+ npcm_i2c_clear_tx_fifo(bus); /* clear the TX fifo status bit */
+
+ /* Master write operation - last byte handling */
+ if (bus->wr_ind == bus->wr_size) {
+ if (bus->fifo_use && npcm_i2c_fifo_usage(bus) > 0)
+ /*
+ * No more bytes to send (to add to the FIFO),
+ * however the FIFO is not empty yet. It is
+ * still in the middle of tx. Currently there's nothing
+ * to do except for waiting to the end of the tx
+ * We will get an int when the FIFO will get empty.
+ */
+ return;
+
+ if (bus->rd_size == 0) {
+ /* all bytes have been written, in wr only operation */
+ npcm_i2c_eob_int(bus, true);
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_MASTER_DONE_IND;
+ npcm_i2c_master_stop(bus);
+ /* Clear SDA Status bit (by writing dummy byte) */
+ npcm_i2c_wr_byte(bus, 0xFF);
+
+ } else {
+ /* last write-byte written on previous int - restart */
+ npcm_i2c_set_fifo(bus, bus->rd_size, -1);
+ /* Generate repeated start upon next write to SDA */
+ npcm_i2c_master_start(bus);
+
+ /*
+ * Receiving one byte only - stall after successful
+ * completion of send address byte. If we NACK here, and
+ * slave doesn't ACK the address, we might
+ * unintentionally NACK the next multi-byte read.
+ */
+ if (bus->rd_size == 1)
+ npcm_i2c_stall_after_start(bus, true);
+
+ /* Next int will occur on read */
+ bus->operation = I2C_READ_OPER;
+ /* send the slave address in read direction */
+ npcm_i2c_wr_byte(bus, bus->dest_addr | 0x1);
+ }
+ } else {
+ /* write next byte not last byte and not slave address */
+ if (!bus->fifo_use || bus->wr_size == 1) {
+ npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
+ } else {
+ wcount = bus->wr_size - bus->wr_ind;
+ npcm_i2c_set_fifo(bus, -1, wcount);
+ if (wcount)
+ npcm_i2c_write_to_fifo_master(bus, wcount);
+ }
+ }
+}
+
+static void npcm_i2c_irq_master_handler_read(struct npcm_i2c *bus)
+{
+ u16 block_extra_bytes_size;
+ u8 data;
+
+ /* added bytes to the packet: */
+ block_extra_bytes_size = bus->read_block_use + bus->PEC_use;
+
+ /*
+ * Perform master read, distinguishing between last byte and the rest of
+ * the bytes. The last byte should be read when the clock is stopped
+ */
+ if (bus->rd_ind == 0) { /* first byte handling: */
+ if (bus->read_block_use) {
+ /* first byte in block protocol is the size: */
+ data = npcm_i2c_rd_byte(bus);
+ data = clamp_val(data, 1, I2C_SMBUS_BLOCK_MAX);
+ bus->rd_size = data + block_extra_bytes_size;
+ bus->rd_buf[bus->rd_ind++] = data;
+
+ /* clear RX FIFO interrupt status: */
+ if (bus->fifo_use) {
+ data = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ data = data | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(data, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ npcm_i2c_set_fifo(bus, bus->rd_size - 1, -1);
+ npcm_i2c_stall_after_start(bus, false);
+ } else {
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_master_fifo_read(bus);
+ }
+ } else {
+ if (bus->rd_size == block_extra_bytes_size &&
+ bus->read_block_use) {
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_BLOCK_BYTES_ERR_IND;
+ bus->cmd_err = -EIO;
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_read_fifo(bus, npcm_i2c_fifo_usage(bus));
+ } else {
+ npcm_i2c_master_fifo_read(bus);
+ }
+ }
+}
+
+static void npcm_i2c_irq_handle_nmatch(struct npcm_i2c *bus)
+{
+ iowrite8(NPCM_I2CST_NMATCH, bus->reg + NPCM_I2CST);
+ npcm_i2c_nack(bus);
+ bus->stop_ind = I2C_BUS_ERR_IND;
+ npcm_i2c_callback(bus, bus->stop_ind, npcm_i2c_get_index(bus));
+}
+
+/* A NACK has occurred */
+static void npcm_i2c_irq_handle_nack(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ if (bus->nack_cnt < ULLONG_MAX)
+ bus->nack_cnt++;
+
+ if (bus->fifo_use) {
+ /*
+ * if there are still untransmitted bytes in TX FIFO
+ * reduce them from wr_ind
+ */
+ if (bus->operation == I2C_WRITE_OPER)
+ bus->wr_ind -= npcm_i2c_fifo_usage(bus);
+
+ /* clear the FIFO */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ /* In master write operation, got unexpected NACK */
+ bus->stop_ind = I2C_NACK_IND;
+ /* Only current master is allowed to issue Stop Condition */
+ if (npcm_i2c_is_master(bus)) {
+ /* stopping in the middle */
+ npcm_i2c_eob_int(bus, false);
+ npcm_i2c_master_stop(bus);
+
+ /* Clear SDA Status bit (by reading dummy byte) */
+ npcm_i2c_rd_byte(bus);
+
+ /*
+ * The bus is released from stall only after the SW clears
+ * NEGACK bit. Then a Stop condition is sent.
+ */
+ npcm_i2c_clear_master_status(bus);
+ readx_poll_timeout_atomic(ioread8, bus->reg + NPCM_I2CCST, val,
+ !(val & NPCM_I2CCST_BUSY), 10, 200);
+ /* Verify no status bits are still set after bus is released */
+ npcm_i2c_clear_master_status(bus);
+ }
+ bus->state = I2C_IDLE;
+
+ /*
+ * In Master mode, NACK should be cleared only after STOP.
+ * In such case, the bus is released from stall only after the
+ * software clears NACK bit. Then a Stop condition is sent.
+ */
+ npcm_i2c_callback(bus, bus->stop_ind, bus->wr_ind);
+}
+
+ /* Master mode: a Bus Error has been identified */
+static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
+{
+ if (bus->ber_cnt < ULLONG_MAX)
+ bus->ber_cnt++;
+ bus->stop_ind = I2C_BUS_ERR_IND;
+ if (npcm_i2c_is_master(bus)) {
+ npcm_i2c_master_abort(bus);
+ } else {
+ bus->ber_state = true;
+ npcm_i2c_clear_master_status(bus);
+
+ /* Clear BB (BUS BUSY) bit */
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+
+ bus->cmd_err = -EAGAIN;
+ npcm_i2c_callback(bus, bus->stop_ind, npcm_i2c_get_index(bus));
+ }
+ bus->state = I2C_IDLE;
+}
+
+ /* EOB: a master End Of Busy (meaning STOP completed) */
+static void npcm_i2c_irq_handle_eob(struct npcm_i2c *bus)
+{
+ npcm_i2c_eob_int(bus, false);
+ bus->state = I2C_IDLE;
+ npcm_i2c_callback(bus, bus->stop_ind, bus->rd_ind);
+}
+
+/* Address sent and requested stall occurred (Master mode) */
+static void npcm_i2c_irq_handle_stall_after_start(struct npcm_i2c *bus)
+{
+ if (npcm_i2c_is_quick(bus)) {
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_MASTER_DONE_IND;
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ } else if ((bus->rd_size == 1) && !bus->read_block_use) {
+ /*
+ * Receiving one byte only - set NACK after ensuring
+ * slave ACKed the address byte.
+ */
+ npcm_i2c_nack(bus);
+ }
+
+ /* Reset stall-after-address-byte */
+ npcm_i2c_stall_after_start(bus, false);
+
+ /* Clear stall only after setting STOP */
+ iowrite8(NPCM_I2CST_STASTR, bus->reg + NPCM_I2CST);
+}
+
+/* SDA status is set - TX or RX, master */
+static void npcm_i2c_irq_handle_sda(struct npcm_i2c *bus, u8 i2cst)
+{
+ u8 fif_cts;
+
+ if (!npcm_i2c_is_master(bus))
+ return;
+
+ if (bus->state == I2C_IDLE) {
+ bus->stop_ind = I2C_WAKE_UP_IND;
+
+ if (npcm_i2c_is_quick(bus) || bus->read_block_use)
+ /*
+ * Need to stall after successful
+ * completion of sending address byte
+ */
+ npcm_i2c_stall_after_start(bus, true);
+ else
+ npcm_i2c_stall_after_start(bus, false);
+
+ /*
+ * Receiving one byte only - stall after successful completion
+ * of sending address byte If we NACK here, and slave doesn't
+ * ACK the address, we might unintentionally NACK the next
+ * multi-byte read
+ */
+ if (bus->wr_size == 0 && bus->rd_size == 1)
+ npcm_i2c_stall_after_start(bus, true);
+
+ /* Initiate I2C master tx */
+
+ /* select bank 1 for FIFO regs */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ fif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ fif_cts = fif_cts & ~NPCM_I2CFIF_CTS_SLVRSTR;
+
+ /* clear FIFO and relevant status bits. */
+ fif_cts = fif_cts | NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+
+ /* re-enable */
+ fif_cts = fif_cts | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+
+ /*
+ * Configure the FIFO threshold:
+ * according to the needed # of bytes to read.
+ * Note: due to HW limitation can't config the rx fifo before it
+ * got and ACK on the restart. LAST bit will not be reset unless
+ * RX completed. It will stay set on the next tx.
+ */
+ if (bus->wr_size)
+ npcm_i2c_set_fifo(bus, -1, bus->wr_size);
+ else
+ npcm_i2c_set_fifo(bus, bus->rd_size, -1);
+
+ bus->state = I2C_OPER_STARTED;
+
+ if (npcm_i2c_is_quick(bus) || bus->wr_size)
+ npcm_i2c_wr_byte(bus, bus->dest_addr);
+ else
+ npcm_i2c_wr_byte(bus, bus->dest_addr | BIT(0));
+ /* SDA interrupt, after start\restart */
+ } else {
+ if (bus->operation == I2C_WRITE_OPER)
+ npcm_i2c_irq_master_handler_write(bus);
+ else if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_irq_master_handler_read(bus);
+ }
+}
+
+static int npcm_i2c_int_master_handler(struct npcm_i2c *bus)
+{
+ u8 i2cst;
+ int ret = -EIO;
+
+ i2cst = ioread8(bus->reg + NPCM_I2CST);
+
+ if (FIELD_GET(NPCM_I2CST_NMATCH, i2cst)) {
+ npcm_i2c_irq_handle_nmatch(bus);
+ return 0;
+ }
+ /* A NACK has occurred */
+ if (FIELD_GET(NPCM_I2CST_NEGACK, i2cst)) {
+ npcm_i2c_irq_handle_nack(bus);
+ return 0;
+ }
+
+ /* Master mode: a Bus Error has been identified */
+ if (FIELD_GET(NPCM_I2CST_BER, i2cst)) {
+ npcm_i2c_irq_handle_ber(bus);
+ return 0;
+ }
+
+ /* EOB: a master End Of Busy (meaning STOP completed) */
+ if ((FIELD_GET(NPCM_I2CCTL1_EOBINTE,
+ ioread8(bus->reg + NPCM_I2CCTL1)) == 1) &&
+ (FIELD_GET(NPCM_I2CCST3_EO_BUSY,
+ ioread8(bus->reg + NPCM_I2CCST3)))) {
+ npcm_i2c_irq_handle_eob(bus);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* reenable slave if it was enabled */
+ if (bus->slave)
+ iowrite8(bus->slave->addr | NPCM_I2CADDR_SAEN,
+ bus->reg + NPCM_I2CADDR1);
+#endif
+ return 0;
+ }
+
+ /* Address sent and requested stall occurred (Master mode) */
+ if (FIELD_GET(NPCM_I2CST_STASTR, i2cst)) {
+ npcm_i2c_irq_handle_stall_after_start(bus);
+ ret = 0;
+ }
+
+ /* SDA status is set - TX or RX, master */
+ if (FIELD_GET(NPCM_I2CST_SDAST, i2cst) ||
+ (bus->fifo_use &&
+ (npcm_i2c_tx_fifo_empty(bus) || npcm_i2c_rx_fifo_full(bus)))) {
+ npcm_i2c_irq_handle_sda(bus, i2cst);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* recovery using TGCLK functionality of the module */
+static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
+{
+ u8 val;
+ u8 fif_cts;
+ bool done = false;
+ int status = -ENOTRECOVERABLE;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+ /* Allow 3 bytes (27 toggles) to be read from the slave: */
+ int iter = 27;
+
+ if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1)) {
+ dev_dbg(bus->dev, "bus%d-0x%x recovery skipped, bus not stuck",
+ bus->num, bus->dest_addr);
+ npcm_i2c_reset(bus);
+ bus->ber_state = false;
+ return 0;
+ }
+
+ npcm_i2c_int_enable(bus, false);
+ npcm_i2c_disable(bus);
+ npcm_i2c_enable(bus);
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CRXF_CTL);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ npcm_i2c_stall_after_start(bus, false);
+
+ /* select bank 1 for FIFO regs */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ /* clear FIFO and relevant status bits. */
+ fif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ fif_cts &= ~NPCM_I2CFIF_CTS_SLVRSTR;
+ fif_cts |= NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+ npcm_i2c_set_fifo(bus, -1, 0);
+
+ /* Repeat the following sequence until SDA is released */
+ do {
+ /* Issue a single SCL toggle */
+ iowrite8(NPCM_I2CCST_TGSCL, bus->reg + NPCM_I2CCST);
+ usleep_range(20, 30);
+ /* If SDA line is inactive (high), stop */
+ if (npcm_i2c_get_SDA(_adap)) {
+ done = true;
+ status = 0;
+ }
+ } while (!done && iter--);
+
+ /* If SDA line is released: send start-addr-stop, to re-sync. */
+ if (npcm_i2c_get_SDA(_adap)) {
+ /* Send an address byte in write direction: */
+ npcm_i2c_wr_byte(bus, bus->dest_addr);
+ npcm_i2c_master_start(bus);
+ /* Wait until START condition is sent */
+ status = readx_poll_timeout(npcm_i2c_get_SCL, _adap, val, !val,
+ 20, 200);
+ /* If START condition was sent */
+ if (npcm_i2c_is_master(bus) > 0) {
+ usleep_range(20, 30);
+ npcm_i2c_master_stop(bus);
+ usleep_range(200, 500);
+ }
+ }
+ npcm_i2c_reset(bus);
+ npcm_i2c_int_enable(bus, true);
+
+ if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1))
+ status = 0;
+ else
+ status = -ENOTRECOVERABLE;
+ if (status) {
+ if (bus->rec_fail_cnt < ULLONG_MAX)
+ bus->rec_fail_cnt++;
+ } else {
+ if (bus->rec_succ_cnt < ULLONG_MAX)
+ bus->rec_succ_cnt++;
+ }
+ bus->ber_state = false;
+ return status;
+}
+
+/* recovery using bit banging functionality of the module */
+static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+ struct i2c_bus_recovery_info *rinfo = &bus->rinfo;
+
+ rinfo->recover_bus = npcm_i2c_recovery_tgclk;
+
+ /*
+ * npcm i2c HW allows direct reading of SCL and SDA.
+ * However, it does not support setting SCL and SDA directly.
+ * The recovery function can toggle SCL when SDA is low (but not set)
+ * Getter functions used internally, and can be used externally.
+ */
+ rinfo->get_scl = npcm_i2c_get_SCL;
+ rinfo->get_sda = npcm_i2c_get_SDA;
+ _adap->bus_recovery_info = rinfo;
+}
+
+/* SCLFRQ min/max field values */
+#define SCLFRQ_MIN 10
+#define SCLFRQ_MAX 511
+#define clk_coef(freq, mul) DIV_ROUND_UP((freq) * (mul), 1000000)
+
+/*
+ * npcm_i2c_init_clk: init HW timing parameters.
+ * NPCM7XX i2c module timing parameters are dependent on module core clk (APB)
+ * and bus frequency.
+ * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric.
+ * 400kHz bus requires asymmetric HT and LT. A different equation is recommended
+ * by the HW designer, given core clock range (equations in comments below).
+ *
+ */
+static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz)
+{
+ struct smb_timing_t *smb_timing;
+ u8 scl_table_cnt = 0, table_size = 0;
+ u8 fast_mode = 0;
+
+ bus->bus_freq = bus_freq_hz;
+
+ switch (bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ smb_timing = smb_timing_100khz;
+ table_size = ARRAY_SIZE(smb_timing_100khz);
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ smb_timing = smb_timing_400khz;
+ table_size = ARRAY_SIZE(smb_timing_400khz);
+ fast_mode = I2CCTL3_400K_MODE;
+ break;
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ smb_timing = smb_timing_1000khz;
+ table_size = ARRAY_SIZE(smb_timing_1000khz);
+ fast_mode = I2CCTL3_400K_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (scl_table_cnt = 0; scl_table_cnt < table_size; scl_table_cnt++)
+ if (bus->apb_clk >= smb_timing[scl_table_cnt].core_clk)
+ break;
+
+ if (scl_table_cnt == table_size)
+ return -EINVAL;
+
+ /* write sclfrq value. bits [6:0] are in I2CCTL2 reg */
+ iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, smb_timing[scl_table_cnt].sclfrq & 0x7F),
+ bus->reg + NPCM_I2CCTL2);
+
+ /* bits [8:7] are in I2CCTL3 reg */
+ iowrite8(FIELD_PREP(I2CCTL3_SCLFRQ8_7, (smb_timing[scl_table_cnt].sclfrq >> 7) & 0x3) |
+ fast_mode,
+ bus->reg + NPCM_I2CCTL3);
+
+ /* Select Bank 0 to access NPCM_I2CCTL4/NPCM_I2CCTL5 */
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+
+ if (bus_freq_hz >= I2C_MAX_FAST_MODE_FREQ) {
+ /*
+ * Set SCL Low/High Time:
+ * k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2
+ * k2 = 2 * SCLLT7-0 -> High Time = k2 / 2
+ */
+ iowrite8(smb_timing[scl_table_cnt].scllt, bus->reg + NPCM_I2CSCLLT);
+ iowrite8(smb_timing[scl_table_cnt].sclht, bus->reg + NPCM_I2CSCLHT);
+
+ iowrite8(smb_timing[scl_table_cnt].dbcnt, bus->reg + NPCM_I2CCTL5);
+ }
+
+ iowrite8(smb_timing[scl_table_cnt].hldt, bus->reg + NPCM_I2CCTL4);
+
+ /* Return to Bank 1, and stay there by default: */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ return 0;
+}
+
+static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode,
+ u32 bus_freq_hz)
+{
+ u8 val;
+ int ret;
+
+ /* Check whether module already enabled or frequency is out of bounds */
+ if ((bus->state != I2C_DISABLE && bus->state != I2C_IDLE) ||
+ bus_freq_hz < I2C_FREQ_MIN_HZ || bus_freq_hz > I2C_FREQ_MAX_HZ)
+ return -EINVAL;
+
+ npcm_i2c_int_enable(bus, false);
+ npcm_i2c_disable(bus);
+
+ /* Configure FIFO mode : */
+ if (FIELD_GET(I2C_VER_FIFO_EN, ioread8(bus->reg + I2C_VER))) {
+ bus->fifo_use = true;
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+ val = ioread8(bus->reg + NPCM_I2CFIF_CTL);
+ val |= NPCM_I2CFIF_CTL_FIFO_EN;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTL);
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ } else {
+ bus->fifo_use = false;
+ }
+
+ /* Configure I2C module clock frequency */
+ ret = npcm_i2c_init_clk(bus, bus_freq_hz);
+ if (ret) {
+ dev_err(bus->dev, "npcm_i2c_init_clk failed\n");
+ return ret;
+ }
+
+ /* Enable module (before configuring CTL1) */
+ npcm_i2c_enable(bus);
+ bus->state = I2C_IDLE;
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val = (val | NPCM_I2CCTL1_NMINTE) & ~NPCM_I2CCTL1_RWS;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+
+ npcm_i2c_reset(bus);
+
+ /* Check HW is OK: SDA and SCL should be high at this point. */
+ if ((npcm_i2c_get_SDA(&bus->adap) == 0) || (npcm_i2c_get_SCL(&bus->adap) == 0)) {
+ dev_warn(bus->dev, " I2C%d SDA=%d SCL=%d, attempting to recover\n", bus->num,
+ npcm_i2c_get_SDA(&bus->adap), npcm_i2c_get_SCL(&bus->adap));
+ if (npcm_i2c_recovery_tgclk(&bus->adap)) {
+ dev_err(bus->dev, "I2C%d init fail: SDA=%d SCL=%d\n",
+ bus->num, npcm_i2c_get_SDA(&bus->adap),
+ npcm_i2c_get_SCL(&bus->adap));
+ return -ENXIO;
+ }
+ }
+
+ npcm_i2c_int_enable(bus, true);
+ return 0;
+}
+
+static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev)
+{
+ u32 clk_freq_hz;
+ int ret;
+
+ /* Initialize the internal data structures */
+ bus->state = I2C_DISABLE;
+ bus->master_or_slave = I2C_SLAVE;
+ bus->int_time_stamp = 0;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ bus->slave = NULL;
+#endif
+
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency",
+ &clk_freq_hz);
+ if (ret) {
+ dev_info(&pdev->dev, "Could not read clock-frequency property");
+ clk_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ }
+
+ ret = npcm_i2c_init_module(bus, I2C_MASTER, clk_freq_hz);
+ if (ret) {
+ dev_err(&pdev->dev, "npcm_i2c_init_module failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
+{
+ struct npcm_i2c *bus = dev_id;
+
+ if (npcm_i2c_is_master(bus))
+ bus->master_or_slave = I2C_MASTER;
+
+ if (bus->master_or_slave == I2C_MASTER) {
+ bus->int_time_stamp = jiffies;
+ if (!npcm_i2c_int_master_handler(bus))
+ return IRQ_HANDLED;
+ }
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave) {
+ bus->master_or_slave = I2C_SLAVE;
+ if (npcm_i2c_int_slave_handler(bus))
+ return IRQ_HANDLED;
+ }
+#endif
+ /* Clear status bits for spurious interrupts */
+ npcm_i2c_clear_master_status(bus);
+
+ return IRQ_HANDLED;
+}
+
+static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
+ u16 nwrite, u16 nread,
+ u8 *write_data, u8 *read_data,
+ bool use_PEC, bool use_read_block)
+{
+ if (bus->state != I2C_IDLE) {
+ bus->cmd_err = -EBUSY;
+ return false;
+ }
+ bus->wr_buf = write_data;
+ bus->wr_size = nwrite;
+ bus->wr_ind = 0;
+ bus->rd_buf = read_data;
+ bus->rd_size = nread;
+ bus->rd_ind = 0;
+ bus->PEC_use = 0;
+
+ /* for tx PEC is appended to buffer from i2c IF. PEC flag is ignored */
+ if (nread)
+ bus->PEC_use = use_PEC;
+
+ bus->read_block_use = use_read_block;
+ if (nread && !nwrite)
+ bus->operation = I2C_READ_OPER;
+ else
+ bus->operation = I2C_WRITE_OPER;
+ if (bus->fifo_use) {
+ u8 i2cfif_cts;
+
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ /* clear FIFO and relevant status bits. */
+ i2cfif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ i2cfif_cts &= ~NPCM_I2CFIF_CTS_SLVRSTR;
+ i2cfif_cts |= NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(i2cfif_cts, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ bus->state = I2C_IDLE;
+ npcm_i2c_stall_after_start(bus, true);
+ npcm_i2c_master_start(bus);
+ return true;
+}
+
+static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct npcm_i2c *bus = container_of(adap, struct npcm_i2c, adap);
+ struct i2c_msg *msg0, *msg1;
+ unsigned long time_left, flags;
+ u16 nwrite, nread;
+ u8 *write_data, *read_data;
+ unsigned long timeout;
+ bool read_block = false;
+ bool read_PEC = false;
+ u8 bus_busy;
+ unsigned long timeout_usec;
+
+ if (bus->state == I2C_DISABLE) {
+ dev_err(bus->dev, "I2C%d module is disabled", bus->num);
+ return -EINVAL;
+ }
+
+ msg0 = &msgs[0];
+ if (msg0->flags & I2C_M_RD) { /* read */
+ nwrite = 0;
+ write_data = NULL;
+ read_data = msg0->buf;
+ if (msg0->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ read_block = true;
+ if (msg0->flags & I2C_CLIENT_PEC)
+ read_PEC = true;
+ } else {
+ nread = msg0->len;
+ }
+ } else { /* write */
+ nwrite = msg0->len;
+ write_data = msg0->buf;
+ nread = 0;
+ read_data = NULL;
+ if (num == 2) {
+ msg1 = &msgs[1];
+ read_data = msg1->buf;
+ if (msg1->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ read_block = true;
+ if (msg1->flags & I2C_CLIENT_PEC)
+ read_PEC = true;
+ } else {
+ nread = msg1->len;
+ read_block = false;
+ }
+ }
+ }
+
+ if (nwrite >= 32 * 1024 || nread >= 32 * 1024) {
+ dev_err(bus->dev, "i2c%d buffer too big\n", bus->num);
+ return -EINVAL;
+ }
+
+ time_left = jiffies + bus->adap.timeout / bus->adap.retries + 1;
+ do {
+ /*
+ * we must clear slave address immediately when the bus is not
+ * busy, so we spinlock it, but we don't keep the lock for the
+ * entire while since it is too long.
+ */
+ spin_lock_irqsave(&bus->lock, flags);
+ bus_busy = ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BB;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (!bus_busy && bus->slave)
+ iowrite8((bus->slave->addr & 0x7F),
+ bus->reg + NPCM_I2CADDR1);
+#endif
+ spin_unlock_irqrestore(&bus->lock, flags);
+
+ } while (time_is_after_jiffies(time_left) && bus_busy);
+
+ /*
+ * Store the address early in a global position to ensure it is
+ * accessible for a potential call to i2c_recover_bus().
+ *
+ * Since the transfer might be a read operation, remove the I2C_M_RD flag
+ * from the bus->dest_addr for the i2c_recover_bus() call later.
+ *
+ * The i2c_recover_bus() uses the address in a write direction to recover
+ * the i2c bus if some error condition occurs.
+ *
+ * Remove the I2C_M_RD flag from the address since npcm_i2c_master_start_xmit()
+ * handles the read/write operation internally.
+ */
+ bus->dest_addr = i2c_8bit_addr_from_msg(msg0) & ~I2C_M_RD;
+
+ /*
+ * Check the BER (bus error) state, when ber_state is true, it means that the module
+ * detects the bus error which is caused by some factor like that the electricity
+ * noise occurs on the bus. Under this condition, the module is reset and the bus
+ * gets recovered.
+ *
+ * While ber_state is false, the module reset and bus recovery also get done as the
+ * bus is busy.
+ */
+ if (bus_busy || bus->ber_state) {
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ npcm_i2c_reset(bus);
+ i2c_recover_bus(adap);
+ return -EAGAIN;
+ }
+
+ npcm_i2c_init_params(bus);
+ bus->msgs = msgs;
+ bus->msgs_num = num;
+ bus->cmd_err = 0;
+ bus->read_block_use = read_block;
+
+ reinit_completion(&bus->cmd_complete);
+
+ npcm_i2c_int_enable(bus, true);
+
+ if (npcm_i2c_master_start_xmit(bus, nwrite, nread,
+ write_data, read_data, read_PEC,
+ read_block)) {
+ /*
+ * Adaptive TimeOut: estimated time in usec + 100% margin:
+ * 2: double the timeout for clock stretching case
+ * 9: bits per transaction (including the ack/nack)
+ */
+ timeout_usec = (2 * 9 * USEC_PER_SEC / bus->bus_freq) * (2 + nread + nwrite);
+ timeout = max_t(unsigned long, bus->adap.timeout / bus->adap.retries,
+ usecs_to_jiffies(timeout_usec));
+ time_left = wait_for_completion_timeout(&bus->cmd_complete,
+ timeout);
+
+ if (time_left == 0) {
+ if (bus->timeout_cnt < ULLONG_MAX)
+ bus->timeout_cnt++;
+ if (bus->master_or_slave == I2C_MASTER) {
+ i2c_recover_bus(adap);
+ bus->cmd_err = -EIO;
+ bus->state = I2C_IDLE;
+ }
+ }
+ }
+
+ /* if there was BER, check if need to recover the bus: */
+ if (bus->cmd_err == -EAGAIN)
+ bus->cmd_err = i2c_recover_bus(adap);
+
+ /*
+ * After any type of error, check if LAST bit is still set,
+ * due to a HW issue.
+ * It cannot be cleared without resetting the module.
+ */
+ else if (bus->cmd_err &&
+ (bus->data->rxf_ctl_last_pec & ioread8(bus->reg + NPCM_I2CRXF_CTL)))
+ npcm_i2c_reset(bus);
+
+ /* After any xfer, successful or not, stall and EOB must be disabled */
+ npcm_i2c_stall_after_start(bus, false);
+ npcm_i2c_eob_int(bus, false);
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* reenable slave if it was enabled */
+ if (bus->slave)
+ iowrite8((bus->slave->addr & 0x7F) | NPCM_I2CADDR_SAEN,
+ bus->reg + NPCM_I2CADDR1);
+#else
+ npcm_i2c_int_enable(bus, false);
+#endif
+ return bus->cmd_err;
+}
+
+static u32 npcm_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_PEC |
+ I2C_FUNC_SLAVE;
+}
+
+static const struct i2c_adapter_quirks npcm_i2c_quirks = {
+ .max_read_len = 32768,
+ .max_write_len = 32768,
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
+static const struct i2c_algorithm npcm_i2c_algo = {
+ .xfer = npcm_i2c_master_xfer,
+ .functionality = npcm_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = npcm_i2c_reg_slave,
+ .unreg_slave = npcm_i2c_unreg_slave,
+#endif
+};
+
+static void npcm_i2c_init_debugfs(struct platform_device *pdev,
+ struct npcm_i2c *bus)
+{
+ debugfs_create_u64("ber_cnt", 0444, bus->adap.debugfs, &bus->ber_cnt);
+ debugfs_create_u64("nack_cnt", 0444, bus->adap.debugfs, &bus->nack_cnt);
+ debugfs_create_u64("rec_succ_cnt", 0444, bus->adap.debugfs, &bus->rec_succ_cnt);
+ debugfs_create_u64("rec_fail_cnt", 0444, bus->adap.debugfs, &bus->rec_fail_cnt);
+ debugfs_create_u64("timeout_cnt", 0444, bus->adap.debugfs, &bus->timeout_cnt);
+ debugfs_create_u64("tx_complete_cnt", 0444, bus->adap.debugfs, &bus->tx_complete_cnt);
+}
+
+static int npcm_i2c_probe_bus(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ static struct regmap *gcr_regmap;
+ struct device *dev = &pdev->dev;
+ struct i2c_adapter *adap;
+ struct npcm_i2c *bus;
+ struct clk *i2c_clk;
+ int irq;
+ int ret;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->dev = &pdev->dev;
+
+ bus->data = of_device_get_match_data(dev);
+ if (!bus->data) {
+ dev_err(dev, "OF data missing\n");
+ return -EINVAL;
+ }
+
+ bus->num = of_alias_get_id(pdev->dev.of_node, "i2c");
+ /* core clk must be acquired to calculate module timing settings */
+ i2c_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_clk))
+ return PTR_ERR(i2c_clk);
+ bus->apb_clk = clk_get_rate(i2c_clk);
+
+ gcr_regmap = syscon_regmap_lookup_by_phandle(np, "nuvoton,sys-mgr");
+ if (IS_ERR(gcr_regmap))
+ gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+
+ if (IS_ERR(gcr_regmap))
+ return PTR_ERR(gcr_regmap);
+ regmap_write(gcr_regmap, NPCM_I2CSEGCTL, bus->data->segctl_init_val);
+
+ bus->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bus->reg))
+ return PTR_ERR(bus->reg);
+
+ spin_lock_init(&bus->lock);
+ init_completion(&bus->cmd_complete);
+
+ adap = &bus->adap;
+ adap->owner = THIS_MODULE;
+ adap->retries = 3;
+ /*
+ * The users want to connect a lot of masters on the same bus.
+ * This timeout is used to determine the time it takes to take bus ownership.
+ * The transactions are very long, so waiting 35ms is not enough.
+ */
+ adap->timeout = 2 * HZ;
+ adap->algo = &npcm_i2c_algo;
+ adap->quirks = &npcm_i2c_quirks;
+ adap->algo_data = bus;
+ adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
+ adap->nr = pdev->id;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /*
+ * Disable the interrupt to avoid the interrupt handler being triggered
+ * incorrectly by the asynchronous interrupt status since the machine
+ * might do a warm reset during the last smbus/i2c transfer session.
+ */
+ npcm_i2c_int_enable(bus, false);
+
+ ret = devm_request_irq(bus->dev, irq, npcm_i2c_bus_irq, 0,
+ dev_name(bus->dev), bus);
+ if (ret)
+ return ret;
+
+ ret = __npcm_i2c_init(bus, pdev);
+ if (ret)
+ return ret;
+
+ npcm_i2c_recovery_init(adap);
+
+ i2c_set_adapdata(adap, bus);
+
+ snprintf(bus->adap.name, sizeof(bus->adap.name), "npcm_i2c_%d",
+ bus->num);
+ ret = i2c_add_numbered_adapter(&bus->adap);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, bus);
+ npcm_i2c_init_debugfs(pdev, bus);
+ return 0;
+}
+
+static void npcm_i2c_remove_bus(struct platform_device *pdev)
+{
+ unsigned long lock_flags;
+ struct npcm_i2c *bus = platform_get_drvdata(pdev);
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+ npcm_i2c_disable(bus);
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ i2c_del_adapter(&bus->adap);
+}
+
+static const struct of_device_id npcm_i2c_bus_of_table[] = {
+ { .compatible = "nuvoton,npcm750-i2c", .data = &npxm7xx_i2c_data },
+ { .compatible = "nuvoton,npcm845-i2c", .data = &npxm8xx_i2c_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
+
+static struct platform_driver npcm_i2c_bus_driver = {
+ .probe = npcm_i2c_probe_bus,
+ .remove = npcm_i2c_remove_bus,
+ .driver = {
+ .name = "nuvoton-i2c",
+ .of_match_table = npcm_i2c_bus_of_table,
+ }
+};
+
+module_platform_driver(npcm_i2c_bus_driver);
+
+MODULE_AUTHOR("Avi Fishman <avi.fishman@gmail.com>");
+MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>");
+MODULE_AUTHOR("Tyrone Ting <kfting@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton I2C Bus Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c
index 4e67d5ed480e..14c059b03945 100644
--- a/drivers/i2c/busses/i2c-nvidia-gpu.c
+++ b/drivers/i2c/busses/i2c-nvidia-gpu.c
@@ -8,13 +8,17 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
+
+#include "i2c-ccgx-ucsi.h"
/* I2C definitions */
#define I2C_MST_CNTL 0x00
@@ -51,6 +55,7 @@ struct gpu_i2c_dev {
void __iomem *regs;
struct i2c_adapter adapter;
struct i2c_board_info *gpu_ccgx_ucsi;
+ struct i2c_client *ccgx_client;
};
static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd)
@@ -74,20 +79,15 @@ static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd)
static int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd)
{
- unsigned long target = jiffies + msecs_to_jiffies(1000);
u32 val;
+ int ret;
- do {
- val = readl(i2cd->regs + I2C_MST_CNTL);
- if (!(val & I2C_MST_CNTL_CYCLE_TRIGGER))
- break;
- if ((val & I2C_MST_CNTL_STATUS) !=
- I2C_MST_CNTL_STATUS_BUS_BUSY)
- break;
- usleep_range(500, 600);
- } while (time_is_after_jiffies(target));
-
- if (time_is_before_jiffies(target)) {
+ ret = readl_poll_timeout(i2cd->regs + I2C_MST_CNTL, val,
+ !(val & I2C_MST_CNTL_CYCLE_TRIGGER) ||
+ (val & I2C_MST_CNTL_STATUS) != I2C_MST_CNTL_STATUS_BUS_BUSY,
+ 500, 1000 * USEC_PER_MSEC);
+
+ if (ret) {
dev_err(i2cd->dev, "i2c timeout error %x\n", val);
return -ETIMEDOUT;
}
@@ -128,8 +128,7 @@ static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len)
put_unaligned_be16(val, data);
break;
case 3:
- put_unaligned_be16(val >> 8, data);
- data[2] = val;
+ put_unaligned_be24(val, data);
break;
case 4:
put_unaligned_be32(val, data);
@@ -164,17 +163,18 @@ static int gpu_i2c_write(struct gpu_i2c_dev *i2cd, u8 data)
return gpu_i2c_check_status(i2cd);
}
-static int gpu_i2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
+static int gpu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap);
int status, status2;
+ bool send_stop = true;
int i, j;
/*
* The controller supports maximum 4 byte read due to known
* limitation of sending STOP after every read.
*/
+ pm_runtime_get_sync(i2cd->dev);
for (i = 0; i < num; i++) {
if (msgs[i].flags & I2C_M_RD) {
/* program client address before starting read */
@@ -182,37 +182,41 @@ static int gpu_i2c_master_xfer(struct i2c_adapter *adap,
/* gpu_i2c_read has implicit start */
status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len);
if (status < 0)
- goto stop;
+ goto exit;
} else {
u8 addr = i2c_8bit_addr_from_msg(msgs + i);
status = gpu_i2c_start(i2cd);
if (status < 0) {
if (i == 0)
- return status;
- goto stop;
+ send_stop = false;
+ goto exit;
}
status = gpu_i2c_write(i2cd, addr);
if (status < 0)
- goto stop;
+ goto exit;
for (j = 0; j < msgs[i].len; j++) {
status = gpu_i2c_write(i2cd, msgs[i].buf[j]);
if (status < 0)
- goto stop;
+ goto exit;
}
}
}
+ send_stop = false;
status = gpu_i2c_stop(i2cd);
if (status < 0)
- return status;
-
- return i;
-stop:
- status2 = gpu_i2c_stop(i2cd);
- if (status2 < 0)
- dev_err(i2cd->dev, "i2c stop failed %d\n", status2);
+ goto exit;
+
+ status = i;
+exit:
+ if (send_stop) {
+ status2 = gpu_i2c_stop(i2cd);
+ if (status2 < 0)
+ dev_err(i2cd->dev, "i2c stop failed %d\n", status2);
+ }
+ pm_runtime_put_autosuspend(i2cd->dev);
return status;
}
@@ -228,8 +232,8 @@ static u32 gpu_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm gpu_i2c_algorithm = {
- .master_xfer = gpu_i2c_master_xfer,
- .functionality = gpu_i2c_functionality,
+ .xfer = gpu_i2c_xfer,
+ .functionality = gpu_i2c_functionality,
};
/*
@@ -253,78 +257,69 @@ static const struct pci_device_id gpu_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(pci, gpu_i2c_ids);
-static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
-{
- struct i2c_client *ccgx_client;
-
- i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
- sizeof(*i2cd->gpu_ccgx_ucsi),
- GFP_KERNEL);
- if (!i2cd->gpu_ccgx_ucsi)
- return -ENOMEM;
-
- strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi",
- sizeof(i2cd->gpu_ccgx_ucsi->type));
- i2cd->gpu_ccgx_ucsi->addr = 0x8;
- i2cd->gpu_ccgx_ucsi->irq = irq;
- ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
- if (!ccgx_client)
- return -ENODEV;
+static const struct property_entry ccgx_props[] = {
+ /* Use FW built for NVIDIA GPU only */
+ PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"),
+ /* USB-C doesn't power the system */
+ PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE),
+ { }
+};
- return 0;
-}
+static const struct software_node ccgx_node = {
+ .properties = ccgx_props,
+};
static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
+ struct device *dev = &pdev->dev;
struct gpu_i2c_dev *i2cd;
int status;
- i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL);
+ i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL);
if (!i2cd)
return -ENOMEM;
- i2cd->dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, i2cd);
+ i2cd->dev = dev;
+ dev_set_drvdata(dev, i2cd);
status = pcim_enable_device(pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status);
- return status;
- }
+ if (status < 0)
+ return dev_err_probe(dev, status, "pcim_enable_device failed\n");
pci_set_master(pdev);
i2cd->regs = pcim_iomap(pdev, 0, 0);
- if (!i2cd->regs) {
- dev_err(&pdev->dev, "pcim_iomap failed\n");
- return -ENOMEM;
- }
+ if (!i2cd->regs)
+ return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n");
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
- if (status < 0) {
- dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status);
- return status;
- }
+ if (status < 0)
+ return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n");
gpu_enable_i2c_bus(i2cd);
i2c_set_adapdata(&i2cd->adapter, i2cd);
i2cd->adapter.owner = THIS_MODULE;
- strlcpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter",
+ strscpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter",
sizeof(i2cd->adapter.name));
i2cd->adapter.algo = &gpu_i2c_algorithm;
i2cd->adapter.quirks = &gpu_i2c_quirks;
- i2cd->adapter.dev.parent = &pdev->dev;
+ i2cd->adapter.dev.parent = dev;
status = i2c_add_adapter(&i2cd->adapter);
if (status < 0)
goto free_irq_vectors;
- status = gpu_populate_client(i2cd, pdev->irq);
- if (status < 0) {
- dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status);
+ i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node);
+ if (IS_ERR(i2cd->ccgx_client)) {
+ status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n");
goto del_adapter;
}
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put_autosuspend(dev);
+ pm_runtime_allow(dev);
+
return 0;
del_adapter:
@@ -336,21 +331,32 @@ free_irq_vectors:
static void gpu_i2c_remove(struct pci_dev *pdev)
{
- struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev);
+ struct gpu_i2c_dev *i2cd = pci_get_drvdata(pdev);
+ pm_runtime_get_noresume(i2cd->dev);
i2c_del_adapter(&i2cd->adapter);
pci_free_irq_vectors(pdev);
}
+#define gpu_i2c_suspend NULL
+
static __maybe_unused int gpu_i2c_resume(struct device *dev)
{
struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev);
gpu_enable_i2c_bus(i2cd);
+ /*
+ * Runtime resume ccgx client so that it can see for any
+ * connector change event. Old ccg firmware has known
+ * issue of not triggering interrupt when a device is
+ * connected to runtime resume the controller.
+ */
+ pm_request_resume(&i2cd->ccgx_client->dev);
return 0;
}
-static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, NULL, gpu_i2c_resume, NULL);
+static UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume,
+ NULL);
static struct pci_driver gpu_i2c_driver = {
.name = "nvidia-gpu",
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 87f9caacba85..0f67e57cdeff 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* i2c-ocores.c: I2C bus driver for OpenCores I2C controller
* (https://opencores.org/project/i2c/overview)
@@ -6,13 +7,10 @@
*
* Support for the GRLIB port of the controller by
* Andreas Larsson <andreas@gaisler.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -25,17 +23,25 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/log2.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+/*
+ * 'process_lock' exists because ocores_process() and ocores_process_timeout()
+ * can't run in parallel.
+ */
struct ocores_i2c {
void __iomem *base;
u32 reg_shift;
u32 reg_io_width;
+ unsigned long flags;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *msg;
int pos;
int nmsgs;
int state; /* see STATE_ */
+ spinlock_t process_lock;
struct clk *clk;
int ip_clock_khz;
int bus_clock_khz;
@@ -77,6 +83,8 @@ struct ocores_i2c {
#define TYPE_OCORES 0
#define TYPE_GRLIB 1
+#define OCORES_FLAG_BROKEN_IRQ BIT(1) /* Broken IRQ for FU540-C000 SoC */
+
static void oc_setreg_8(struct ocores_i2c *i2c, int reg, u8 value)
{
iowrite8(value, i2c->base + (reg << i2c->reg_shift));
@@ -137,23 +145,29 @@ static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
return i2c->getreg(i2c, reg);
}
-static void ocores_process(struct ocores_i2c *i2c)
+static void ocores_process(struct ocores_i2c *i2c, u8 stat)
{
struct i2c_msg *msg = i2c->msg;
- u8 stat = oc_getreg(i2c, OCI2C_STATUS);
+ unsigned long flags;
+
+ /*
+ * If we spin here is because we are in timeout, so we are going
+ * to be in STATE_ERROR. See ocores_process_timeout()
+ */
+ spin_lock_irqsave(&i2c->process_lock, flags);
if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {
/* stop has been sent */
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
wake_up(&i2c->wait);
- return;
+ goto out;
}
/* error? */
if (stat & OCI2C_STAT_ARBLOST) {
i2c->state = STATE_ERROR;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
@@ -163,10 +177,11 @@ static void ocores_process(struct ocores_i2c *i2c)
if (stat & OCI2C_STAT_NACK) {
i2c->state = STATE_ERROR;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
- } else
+ } else {
msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);
+ }
/* end of msg? */
if (i2c->pos == msg->len) {
@@ -183,15 +198,15 @@ static void ocores_process(struct ocores_i2c *i2c)
i2c->state = STATE_START;
oc_setreg(i2c, OCI2C_DATA, addr);
- oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
- return;
- } else
- i2c->state = (msg->flags & I2C_M_RD)
- ? STATE_READ : STATE_WRITE;
+ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
+ goto out;
+ }
+ i2c->state = (msg->flags & I2C_M_RD)
+ ? STATE_READ : STATE_WRITE;
} else {
i2c->state = STATE_DONE;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
}
@@ -202,20 +217,158 @@ static void ocores_process(struct ocores_i2c *i2c)
oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);
}
+
+out:
+ spin_unlock_irqrestore(&i2c->process_lock, flags);
}
static irqreturn_t ocores_isr(int irq, void *dev_id)
{
struct ocores_i2c *i2c = dev_id;
+ u8 stat = oc_getreg(i2c, OCI2C_STATUS);
- ocores_process(i2c);
+ if (i2c->flags & OCORES_FLAG_BROKEN_IRQ) {
+ if ((stat & OCI2C_STAT_IF) && !(stat & OCI2C_STAT_BUSY))
+ return IRQ_NONE;
+ } else if (!(stat & OCI2C_STAT_IF)) {
+ return IRQ_NONE;
+ }
+ ocores_process(i2c, stat);
return IRQ_HANDLED;
}
-static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+/**
+ * ocores_process_timeout() - Process timeout event
+ * @i2c: ocores I2C device instance
+ */
+static void ocores_process_timeout(struct ocores_i2c *i2c)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c->process_lock, flags);
+ i2c->state = STATE_ERROR;
+ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
+ spin_unlock_irqrestore(&i2c->process_lock, flags);
+}
+
+/**
+ * ocores_wait() - Wait until something change in a given register
+ * @i2c: ocores I2C device instance
+ * @reg: register to query
+ * @mask: bitmask to apply on register value
+ * @val: expected result
+ * @timeout: timeout in jiffies
+ *
+ * Timeout is necessary to avoid to stay here forever when the chip
+ * does not answer correctly.
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+static int ocores_wait(struct ocores_i2c *i2c,
+ int reg, u8 mask, u8 val,
+ const unsigned long timeout)
+{
+ unsigned long j;
+
+ j = jiffies + timeout;
+ while (1) {
+ u8 status = oc_getreg(i2c, reg);
+
+ if ((status & mask) == val)
+ break;
+
+ if (time_after(jiffies, j))
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/**
+ * ocores_poll_wait() - Wait until is possible to process some data
+ * @i2c: ocores I2C device instance
+ *
+ * Used when the device is in polling mode (interrupts disabled).
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+static int ocores_poll_wait(struct ocores_i2c *i2c)
{
- struct ocores_i2c *i2c = i2c_get_adapdata(adap);
+ u8 mask;
+ int err;
+
+ if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) {
+ /* transfer is over */
+ mask = OCI2C_STAT_BUSY;
+ } else {
+ /* on going transfer */
+ mask = OCI2C_STAT_TIP;
+ /*
+ * We wait for the data to be transferred (8bit),
+ * then we start polling on the ACK/NACK bit
+ */
+ udelay((8 * 1000) / i2c->bus_clock_khz);
+ }
+
+ /*
+ * once we are here we expect to get the expected result immediately
+ * so if after 1ms we timeout then something is broken.
+ */
+ err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1));
+ if (err)
+ dev_warn(i2c->adap.dev.parent,
+ "%s: STATUS timeout, bit 0x%x did not clear in 1ms\n",
+ __func__, mask);
+ return err;
+}
+
+/**
+ * ocores_process_polling() - It handles an IRQ-less transfer
+ * @i2c: ocores I2C device instance
+ *
+ * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same
+ * (only that IRQ are not produced). This means that we can re-use entirely
+ * ocores_isr(), we just add our polling code around it.
+ *
+ * It can run in atomic context
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+static int ocores_process_polling(struct ocores_i2c *i2c)
+{
+ irqreturn_t ret;
+ int err = 0;
+
+ while (1) {
+ err = ocores_poll_wait(i2c);
+ if (err)
+ break; /* timeout */
+
+ ret = ocores_isr(-1, i2c);
+ if (ret == IRQ_NONE)
+ break; /* all messages have been transferred */
+ else {
+ if (i2c->flags & OCORES_FLAG_BROKEN_IRQ)
+ if (i2c->state == STATE_DONE)
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int ocores_xfer_core(struct ocores_i2c *i2c,
+ struct i2c_msg *msgs, int num,
+ bool polling)
+{
+ int ret = 0;
+ u8 ctrl;
+
+ ctrl = oc_getreg(i2c, OCI2C_CONTROL);
+ if (polling)
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN);
+ else
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN);
i2c->msg = msgs;
i2c->pos = 0;
@@ -225,11 +378,32 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg));
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
- if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
- (i2c->state == STATE_DONE), HZ))
- return (i2c->state == STATE_DONE) ? num : -EIO;
- else
- return -ETIMEDOUT;
+ if (polling) {
+ ret = ocores_process_polling(i2c);
+ } else {
+ if (wait_event_timeout(i2c->wait,
+ (i2c->state == STATE_ERROR) ||
+ (i2c->state == STATE_DONE), HZ) == 0)
+ ret = -ETIMEDOUT;
+ }
+ if (ret) {
+ ocores_process_timeout(i2c);
+ return ret;
+ }
+
+ return (i2c->state == STATE_DONE) ? num : -EIO;
+}
+
+static int ocores_xfer_polling(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true);
+}
+
+static int ocores_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false);
}
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
@@ -239,7 +413,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* make sure the device is disabled */
- oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1;
prescale = clamp(prescale, 0, 0xffff);
@@ -256,8 +431,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);
/* Init the device */
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN);
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
- oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
return 0;
}
@@ -268,8 +443,9 @@ static u32 ocores_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static const struct i2c_algorithm ocores_algorithm = {
- .master_xfer = ocores_xfer,
+static struct i2c_algorithm ocores_algorithm = {
+ .xfer = ocores_xfer,
+ .xfer_atomic = ocores_xfer_polling,
.functionality = ocores_func,
};
@@ -289,18 +465,27 @@ static const struct of_device_id ocores_i2c_match[] = {
.compatible = "aeroflexgaisler,i2cmst",
.data = (void *)TYPE_GRLIB,
},
+ {
+ .compatible = "sifive,fu540-c000-i2c",
+ },
+ {
+ .compatible = "sifive,i2c0",
+ },
{},
};
MODULE_DEVICE_TABLE(of, ocores_i2c_match);
#ifdef CONFIG_OF
-/* Read and write functions for the GRLIB port of the controller. Registers are
+/*
+ * Read and write functions for the GRLIB port of the controller. Registers are
* 32-bit big endian and the PRELOW and PREHIGH registers are merged into one
- * register. The subsequent registers has their offset decreased accordingly. */
+ * register. The subsequent registers have their offsets decreased accordingly.
+ */
static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg)
{
u32 rd;
int rreg = reg;
+
if (reg != OCI2C_PRELOW)
rreg--;
rd = ioread32be(i2c->base + (rreg << i2c->reg_shift));
@@ -314,6 +499,7 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value)
{
u32 curr, wr;
int rreg = reg;
+
if (reg != OCI2C_PRELOW)
rreg--;
if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) {
@@ -355,28 +541,20 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
&clock_frequency);
i2c->bus_clock_khz = 100;
- i2c->clk = devm_clk_get(&pdev->dev, NULL);
-
- if (!IS_ERR(i2c->clk)) {
- int ret = clk_prepare_enable(i2c->clk);
-
- if (ret) {
- dev_err(&pdev->dev,
- "clk_prepare_enable failed: %d\n", ret);
- return ret;
- }
- i2c->ip_clock_khz = clk_get_rate(i2c->clk) / 1000;
- if (clock_frequency_present)
- i2c->bus_clock_khz = clock_frequency / 1000;
- }
+ i2c->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
+ "devm_clk_get_optional_enabled failed\n");
+ i2c->ip_clock_khz = clk_get_rate(i2c->clk) / 1000;
+ if (clock_frequency_present)
+ i2c->bus_clock_khz = clock_frequency / 1000;
if (i2c->ip_clock_khz == 0) {
if (of_property_read_u32(np, "opencores,ip-clock-frequency",
&val)) {
if (!clock_frequency_present) {
dev_err(&pdev->dev,
"Missing required parameter 'opencores,ip-clock-frequency'\n");
- clk_disable_unprepare(i2c->clk);
return -ENODEV;
}
i2c->ip_clock_khz = clock_frequency / 1000;
@@ -402,7 +580,7 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
return 0;
}
#else
-#define ocores_i2c_of_probe(pdev,i2c) -ENODEV
+#define ocores_i2c_of_probe(pdev, i2c) -ENODEV
#endif
static int ocores_i2c_probe(struct platform_device *pdev)
@@ -414,25 +592,45 @@ static int ocores_i2c_probe(struct platform_device *pdev)
int ret;
int i;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
+ spin_lock_init(&i2c->process_lock);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(i2c->base))
- return PTR_ERR(i2c->base);
+ if (res) {
+ i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+ } else {
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EINVAL;
+ if (!devm_request_region(&pdev->dev, res->start,
+ resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "Can't get I/O resource.\n");
+ return -EBUSY;
+ }
+ i2c->base = devm_ioport_map(&pdev->dev, res->start,
+ resource_size(res));
+ if (!i2c->base) {
+ dev_err(&pdev->dev, "Can't map I/O resource.\n");
+ return -EBUSY;
+ }
+ i2c->reg_io_width = 1;
+ }
pdata = dev_get_platdata(&pdev->dev);
if (pdata) {
i2c->reg_shift = pdata->reg_shift;
i2c->reg_io_width = pdata->reg_io_width;
i2c->ip_clock_khz = pdata->clock_khz;
- i2c->bus_clock_khz = 100;
+ if (pdata->bus_khz)
+ i2c->bus_clock_khz = pdata->bus_khz;
+ else
+ i2c->bus_clock_khz = 100;
} else {
ret = ocores_i2c_of_probe(pdev, i2c);
if (ret)
@@ -465,23 +663,45 @@ static int ocores_i2c_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "Unsupported I/O width (%d)\n",
i2c->reg_io_width);
- ret = -EINVAL;
- goto err_clk;
+ return -EINVAL;
}
}
- ret = ocores_init(&pdev->dev, i2c);
- if (ret)
- goto err_clk;
-
init_waitqueue_head(&i2c->wait);
- ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
- pdev->name, i2c);
- if (ret) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- goto err_clk;
+
+ irq = platform_get_irq_optional(pdev, 0);
+ /*
+ * Since the SoC does have an interrupt, its DT has an interrupt
+ * property - But this should be bypassed as the IRQ logic in this
+ * SoC is broken.
+ */
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "sifive,fu540-c000-i2c")) {
+ i2c->flags |= OCORES_FLAG_BROKEN_IRQ;
+ irq = -ENXIO;
+ }
+
+ if (irq == -ENXIO) {
+ ocores_algorithm.xfer = ocores_xfer_polling;
+ } else {
+ if (irq < 0)
+ return irq;
}
+ if (ocores_algorithm.xfer != ocores_xfer_polling) {
+ ret = devm_request_any_context_irq(&pdev->dev, irq,
+ ocores_isr, 0,
+ pdev->name, i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot claim IRQ\n");
+ return ret;
+ }
+ }
+
+ ret = ocores_init(&pdev->dev, i2c);
+ if (ret)
+ return ret;
+
/* hook up driver to tree */
platform_set_drvdata(pdev, i2c);
i2c->adap = ocores_adapter;
@@ -492,85 +712,68 @@ static int ocores_i2c_probe(struct platform_device *pdev)
/* add i2c adapter to i2c tree */
ret = i2c_add_adapter(&i2c->adap);
if (ret)
- goto err_clk;
+ return ret;
/* add in known devices to the bus */
if (pdata) {
for (i = 0; i < pdata->num_devices; i++)
- i2c_new_device(&i2c->adap, pdata->devices + i);
+ i2c_new_client_device(&i2c->adap, pdata->devices + i);
}
return 0;
-
-err_clk:
- clk_disable_unprepare(i2c->clk);
- return ret;
}
-static int ocores_i2c_remove(struct platform_device *pdev)
+static void ocores_i2c_remove(struct platform_device *pdev)
{
struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+ u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* disable i2c logic */
- oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)
- & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
-
- if (!IS_ERR(i2c->clk))
- clk_disable_unprepare(i2c->clk);
-
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int ocores_i2c_suspend(struct device *dev)
{
struct ocores_i2c *i2c = dev_get_drvdata(dev);
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* make sure the device is disabled */
- oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
- if (!IS_ERR(i2c->clk))
- clk_disable_unprepare(i2c->clk);
+ clk_disable_unprepare(i2c->clk);
return 0;
}
static int ocores_i2c_resume(struct device *dev)
{
struct ocores_i2c *i2c = dev_get_drvdata(dev);
+ unsigned long rate;
+ int ret;
- if (!IS_ERR(i2c->clk)) {
- unsigned long rate;
- int ret = clk_prepare_enable(i2c->clk);
-
- if (ret) {
- dev_err(dev,
- "clk_prepare_enable failed: %d\n", ret);
- return ret;
- }
- rate = clk_get_rate(i2c->clk) / 1000;
- if (rate)
- i2c->ip_clock_khz = rate;
- }
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "clk_prepare_enable failed\n");
+ rate = clk_get_rate(i2c->clk) / 1000;
+ if (rate)
+ i2c->ip_clock_khz = rate;
return ocores_init(dev, i2c);
}
-static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume);
-#define OCORES_I2C_PM (&ocores_i2c_pm)
-#else
-#define OCORES_I2C_PM NULL
-#endif
+static DEFINE_NOIRQ_DEV_PM_OPS(ocores_i2c_pm,
+ ocores_i2c_suspend, ocores_i2c_resume);
static struct platform_driver ocores_i2c_driver = {
.probe = ocores_i2c_probe,
- .remove = ocores_i2c_remove,
+ .remove = ocores_i2c_remove,
.driver = {
.name = "ocores-i2c",
.of_match_table = ocores_i2c_match,
- .pm = OCORES_I2C_PM,
+ .pm = pm_sleep_ptr(&ocores_i2c_pm),
},
};
diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index d9607905dc2f..93a49e4637ec 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -17,9 +17,14 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include "i2c-octeon-core.h"
+#define INITIAL_DELTA_HZ 1000000
+#define TWSI_MASTER_CLK_REG_DEF_VAL 0x18
+#define TWSI_MASTER_CLK_REG_OTX2_VAL 0x3
+
/* interrupt service routine */
irqreturn_t octeon_i2c_isr(int irq, void *dev_id)
{
@@ -40,7 +45,7 @@ static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c)
* octeon_i2c_wait - wait for the IFLG to be set
* @i2c: The struct octeon_i2c
*
- * Returns 0 on success, otherwise a negative errno.
+ * Returns: 0 on success, otherwise a negative errno.
*/
static int octeon_i2c_wait(struct octeon_i2c *i2c)
{
@@ -80,7 +85,7 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c)
{
- return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
+ return (__raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)) & SW_TWSI_V) == 0;
}
static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c)
@@ -130,11 +135,37 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB);
}
+static void octeon_i2c_block_enable(struct octeon_i2c *i2c)
+{
+ u64 mode;
+
+ if (i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c))
+ return;
+
+ i2c->block_enabled = true;
+ mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ mode |= TWSX_MODE_BLOCK_MODE;
+ octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
+}
+
+static void octeon_i2c_block_disable(struct octeon_i2c *i2c)
+{
+ u64 mode;
+
+ if (!i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c))
+ return;
+
+ i2c->block_enabled = false;
+ mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ mode &= ~TWSX_MODE_BLOCK_MODE;
+ octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
+}
+
/**
* octeon_i2c_hlc_wait - wait for an HLC operation to complete
* @i2c: The struct octeon_i2c
*
- * Returns 0 on success, otherwise -ETIMEDOUT.
+ * Returns: 0 on success, otherwise -ETIMEDOUT.
*/
static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
{
@@ -177,13 +208,14 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read)
{
u8 stat;
+ u64 mode;
/*
* This is ugly... in HLC mode the status is not in the status register
- * but in the lower 8 bits of SW_TWSI.
+ * but in the lower 8 bits of OCTEON_REG_SW_TWSI.
*/
if (i2c->hlc_enabled)
- stat = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ stat = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
else
stat = octeon_i2c_stat_read(i2c);
@@ -215,14 +247,14 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read)
case STAT_LOST_ARB_B0:
return -EAGAIN;
- /* Being addressed as slave, should back off & listen */
+ /* Being addressed as local target, should back off & listen */
case STAT_SLAVE_60:
case STAT_SLAVE_70:
case STAT_GENDATA_ACK:
case STAT_GENDATA_NAK:
return -EOPNOTSUPP;
- /* Core busy as slave */
+ /* Core busy as local target */
case STAT_SLAVE_80:
case STAT_SLAVE_88:
case STAT_SLAVE_A0:
@@ -239,6 +271,13 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read)
case STAT_RXADDR_NAK:
case STAT_AD2W_NAK:
return -ENXIO;
+
+ case STAT_WDOG_TOUT:
+ mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ /* Set BUS_MON_RST to reset bus monitor */
+ mode |= BUS_MON_RST_MASK;
+ octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ return -EIO;
default:
dev_err(i2c->dev, "unhandled state: %d\n", stat);
return -EIO;
@@ -260,7 +299,7 @@ static int octeon_i2c_recovery(struct octeon_i2c *i2c)
* octeon_i2c_start - send START to the bus
* @i2c: The struct octeon_i2c
*
- * Returns 0 on success, otherwise a negative errno.
+ * Returns: 0 on success, otherwise a negative errno.
*/
static int octeon_i2c_start(struct octeon_i2c *i2c)
{
@@ -268,6 +307,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c)
u8 stat;
octeon_i2c_hlc_disable(i2c);
+ octeon_i2c_block_disable(i2c);
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA);
ret = octeon_i2c_wait(i2c);
@@ -301,7 +341,7 @@ static void octeon_i2c_stop(struct octeon_i2c *i2c)
*
* The address is sent over the bus, then the data is read.
*
- * Returns 0 on success, otherwise a negative errno.
+ * Returns: 0 on success, otherwise a negative errno.
*/
static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
u8 *data, u16 *rlength, bool recv_len)
@@ -347,7 +387,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
if (result)
return result;
if (recv_len && i == 0) {
- if (data[i] > I2C_SMBUS_BLOCK_MAX + 1)
+ if (data[i] > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
length += data[i];
}
@@ -369,7 +409,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
*
* The address is sent over the bus, then the data.
*
- * Returns 0 on success, otherwise a negative errno.
+ * Returns: 0 on success, otherwise a negative errno.
*/
static int octeon_i2c_write(struct octeon_i2c *i2c, int target,
const u8 *data, int length)
@@ -408,23 +448,18 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
octeon_i2c_hlc_enable(i2c);
octeon_i2c_hlc_int_clear(i2c);
- cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR;
+ cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7;
/* SIZE */
cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;
/* A */
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
- if (msgs[0].flags & I2C_M_TEN)
- cmd |= SW_TWSI_OP_10;
- else
- cmd |= SW_TWSI_OP_7;
-
- octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c));
+ octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
ret = octeon_i2c_hlc_wait(i2c);
if (ret)
goto err;
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if ((cmd & SW_TWSI_R) == 0)
return octeon_i2c_check_status(i2c, false);
@@ -432,7 +467,7 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff;
if (msgs[0].len > 4) {
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)
msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff;
}
@@ -450,17 +485,12 @@ static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
octeon_i2c_hlc_enable(i2c);
octeon_i2c_hlc_int_clear(i2c);
- cmd = SW_TWSI_V | SW_TWSI_SOVR;
+ cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7;
/* SIZE */
cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;
/* A */
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
- if (msgs[0].flags & I2C_M_TEN)
- cmd |= SW_TWSI_OP_10;
- else
- cmd |= SW_TWSI_OP_7;
-
for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--)
cmd |= (u64)msgs[0].buf[j] << (8 * i);
@@ -469,15 +499,15 @@ static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)
ext |= (u64)msgs[0].buf[j] << (8 * i);
- octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c));
+ octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
}
- octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c));
+ octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
ret = octeon_i2c_hlc_wait(i2c);
if (ret)
goto err;
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if ((cmd & SW_TWSI_R) == 0)
return octeon_i2c_check_status(i2c, false);
@@ -485,6 +515,45 @@ err:
return ret;
}
+/* Process hlc transaction */
+static int octeon_i2c_hlc_cmd_send(struct octeon_i2c *i2c, u64 cmd)
+{
+ octeon_i2c_hlc_int_clear(i2c);
+ octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
+
+ return octeon_i2c_hlc_wait(i2c);
+}
+
+/* Generic consideration for extended internal addresses in i2c hlc r/w ops */
+static bool octeon_i2c_hlc_ext(struct octeon_i2c *i2c, struct i2c_msg msg, u64 *cmd_in, u64 *ext)
+{
+ bool set_ext = false;
+ u64 cmd = 0;
+
+ if (msg.len == 2) {
+ cmd |= SW_TWSI_EIA;
+ *ext = (u64)msg.buf[0] << SW_TWSI_IA_SHIFT;
+ cmd |= (u64)msg.buf[1] << SW_TWSI_IA_SHIFT;
+ set_ext = true;
+ } else {
+ cmd |= (u64)msg.buf[0] << SW_TWSI_IA_SHIFT;
+ }
+
+ *cmd_in |= cmd;
+ return set_ext;
+}
+
+/* Construct and send i2c transaction core cmd for read ops */
+static int octeon_i2c_hlc_read_cmd(struct octeon_i2c *i2c, struct i2c_msg msg, u64 cmd)
+{
+ u64 ext = 0;
+
+ if (octeon_i2c_hlc_ext(i2c, msg, &cmd, &ext))
+ octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
+
+ return octeon_i2c_hlc_cmd_send(i2c, cmd);
+}
+
/* high-level-controller composite write+read, msg0=addr, msg1=data */
static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{
@@ -493,36 +562,18 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs
octeon_i2c_hlc_enable(i2c);
- cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR;
+ cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
/* SIZE */
cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT;
/* A */
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
- if (msgs[0].flags & I2C_M_TEN)
- cmd |= SW_TWSI_OP_10_IA;
- else
- cmd |= SW_TWSI_OP_7_IA;
-
- if (msgs[0].len == 2) {
- u64 ext = 0;
-
- cmd |= SW_TWSI_EIA;
- ext = (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT;
- cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT;
- octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c));
- } else {
- cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT;
- }
-
- octeon_i2c_hlc_int_clear(i2c);
- octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c));
-
- ret = octeon_i2c_hlc_wait(i2c);
+ /* Send core command */
+ ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd);
if (ret)
goto err;
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if ((cmd & SW_TWSI_R) == 0)
return octeon_i2c_check_status(i2c, false);
@@ -530,7 +581,7 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs
msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff;
if (msgs[1].len > 4) {
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--)
msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff;
}
@@ -548,25 +599,14 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg
octeon_i2c_hlc_enable(i2c);
- cmd = SW_TWSI_V | SW_TWSI_SOVR;
+ cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
/* SIZE */
cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT;
/* A */
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
- if (msgs[0].flags & I2C_M_TEN)
- cmd |= SW_TWSI_OP_10_IA;
- else
- cmd |= SW_TWSI_OP_7_IA;
-
- if (msgs[0].len == 2) {
- cmd |= SW_TWSI_EIA;
- ext |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT;
- set_ext = true;
- cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT;
- } else {
- cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT;
- }
+ /* Set parameters for extended message (if required) */
+ set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext);
for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--)
cmd |= (u64)msgs[1].buf[j] << (8 * i);
@@ -577,16 +617,13 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg
set_ext = true;
}
if (set_ext)
- octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c));
-
- octeon_i2c_hlc_int_clear(i2c);
- octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c));
+ octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
- ret = octeon_i2c_hlc_wait(i2c);
+ ret = octeon_i2c_hlc_cmd_send(i2c, cmd);
if (ret)
goto err;
- cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if ((cmd & SW_TWSI_R) == 0)
return octeon_i2c_check_status(i2c, false);
@@ -595,37 +632,166 @@ err:
}
/**
- * octeon_i2c_xfer - The driver's master_xfer function
+ * octeon_i2c_hlc_block_comp_read - high-level-controller composite block read
+ * @i2c: The struct octeon_i2c
+ * @msgs: msg[0] contains address, place read data into msg[1]
+ *
+ * i2c core command is constructed and written into the SW_TWSI register.
+ * The execution of the command will result in requested data being
+ * placed into a FIFO buffer, ready to be read.
+ * Used in the case where the i2c xfer is for greater than 8 bytes of read data.
+ *
+ * Returns: 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_hlc_block_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
+{
+ int ret;
+ u16 len, i;
+ u64 cmd;
+
+ octeon_i2c_hlc_enable(i2c);
+ octeon_i2c_block_enable(i2c);
+
+ /* Write (size - 1) into block control register */
+ len = msgs[1].len - 1;
+ octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
+
+ /* Prepare core command */
+ cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
+ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
+
+ /* Send core command */
+ ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd);
+ if (ret)
+ goto err;
+
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
+ if ((cmd & SW_TWSI_R) == 0) {
+ octeon_i2c_block_disable(i2c);
+ return octeon_i2c_check_status(i2c, false);
+ }
+
+ /* read data in FIFO */
+ octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
+ i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
+ for (i = 0; i <= len; i += 8) {
+ /* Byte-swap FIFO data and copy into msg buffer */
+ __be64 rd = cpu_to_be64(__raw_readq(i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c)));
+
+ memcpy(&msgs[1].buf[i], &rd, min(8, msgs[1].len - i));
+ }
+
+err:
+ octeon_i2c_block_disable(i2c);
+ return ret;
+}
+
+/**
+ * octeon_i2c_hlc_block_comp_write - high-level-controller composite block write
+ * @i2c: The struct octeon_i2c
+ * @msgs: msg[0] contains address, msg[1] contains data to be written
+ *
+ * i2c core command is constructed and write data is written into the FIFO buffer.
+ * The execution of the command will result in HW write, using the data in FIFO.
+ * Used in the case where the i2c xfer is for greater than 8 bytes of write data.
+ *
+ * Returns: 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_hlc_block_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
+{
+ bool set_ext;
+ int ret;
+ u16 len, i;
+ u64 cmd, ext = 0;
+
+ octeon_i2c_hlc_enable(i2c);
+ octeon_i2c_block_enable(i2c);
+
+ /* Write (size - 1) into block control register */
+ len = msgs[1].len - 1;
+ octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
+
+ /* Prepare core command */
+ cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
+ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
+
+ /* Set parameters for extended message (if required) */
+ set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext);
+
+ /* Write msg into FIFO buffer */
+ octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
+ i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
+ for (i = 0; i <= len; i += 8) {
+ __be64 buf = 0;
+
+ /* Copy 8 bytes or remaining bytes from message buffer */
+ memcpy(&buf, &msgs[1].buf[i], min(8, msgs[1].len - i));
+
+ /* Byte-swap message data and write into FIFO */
+ buf = cpu_to_be64(buf);
+ octeon_i2c_writeq_flush((u64)buf, i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c));
+ }
+ if (set_ext)
+ octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
+
+ /* Send command to core (send data in FIFO) */
+ ret = octeon_i2c_hlc_cmd_send(i2c, cmd);
+ if (ret)
+ goto err;
+
+ cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
+ if ((cmd & SW_TWSI_R) == 0) {
+ octeon_i2c_block_disable(i2c);
+ return octeon_i2c_check_status(i2c, false);
+ }
+
+err:
+ octeon_i2c_block_disable(i2c);
+ return ret;
+}
+
+/**
+ * octeon_i2c_xfer - The driver's xfer function
* @adap: Pointer to the i2c_adapter structure
* @msgs: Pointer to the messages to be processed
* @num: Length of the MSGS array
*
- * Returns the number of messages processed, or a negative errno on failure.
+ * Returns: the number of messages processed, or a negative errno on failure.
*/
int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct octeon_i2c *i2c = i2c_get_adapdata(adap);
int i, ret = 0;
- if (num == 1) {
- if (msgs[0].len > 0 && msgs[0].len <= 8) {
- if (msgs[0].flags & I2C_M_RD)
- ret = octeon_i2c_hlc_read(i2c, msgs);
- else
- ret = octeon_i2c_hlc_write(i2c, msgs);
- goto out;
- }
- } else if (num == 2) {
- if ((msgs[0].flags & I2C_M_RD) == 0 &&
- (msgs[1].flags & I2C_M_RECV_LEN) == 0 &&
- msgs[0].len > 0 && msgs[0].len <= 2 &&
- msgs[1].len > 0 && msgs[1].len <= 8 &&
- msgs[0].addr == msgs[1].addr) {
- if (msgs[1].flags & I2C_M_RD)
- ret = octeon_i2c_hlc_comp_read(i2c, msgs);
- else
- ret = octeon_i2c_hlc_comp_write(i2c, msgs);
- goto out;
+ if (IS_LS_FREQ(i2c->twsi_freq)) {
+ if (num == 1) {
+ if (msgs[0].len > 0 && msgs[0].len <= 8) {
+ if (msgs[0].flags & I2C_M_RD)
+ ret = octeon_i2c_hlc_read(i2c, msgs);
+ else
+ ret = octeon_i2c_hlc_write(i2c, msgs);
+ goto out;
+ }
+ } else if (num == 2) {
+ if ((msgs[0].flags & I2C_M_RD) == 0 &&
+ (msgs[1].flags & I2C_M_RECV_LEN) == 0 &&
+ msgs[0].len > 0 && msgs[0].len <= 2 &&
+ msgs[1].len > 0 &&
+ msgs[0].addr == msgs[1].addr) {
+ if (msgs[1].len <= 8) {
+ if (msgs[1].flags & I2C_M_RD)
+ ret = octeon_i2c_hlc_comp_read(i2c, msgs);
+ else
+ ret = octeon_i2c_hlc_comp_write(i2c, msgs);
+ goto out;
+ } else if (msgs[1].len <= 1024 && OCTEON_REG_BLOCK_CTL(i2c)) {
+ if (msgs[1].flags & I2C_M_RD)
+ ret = octeon_i2c_hlc_block_comp_read(i2c, msgs);
+ else
+ ret = octeon_i2c_hlc_block_comp_write(i2c, msgs);
+ goto out;
+ }
+ }
}
}
@@ -658,31 +824,64 @@ out:
void octeon_i2c_set_clock(struct octeon_i2c *i2c)
{
int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff;
- int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000;
+ bool is_plat_otx2;
+ /*
+ * Find divisors to produce target frequency, start with large delta
+ * to cover wider range of divisors, note thp = TCLK half period and
+ * ds is OSCL output frequency divisor.
+ */
+ unsigned int thp, mdiv_min, mdiv = 2, ndiv = 0, ds = 10;
+ unsigned int delta_hz = INITIAL_DELTA_HZ;
+
+ is_plat_otx2 = octeon_i2c_is_otx2(to_pci_dev(i2c->dev));
+
+ if (is_plat_otx2) {
+ thp = TWSI_MASTER_CLK_REG_OTX2_VAL;
+ mdiv_min = 0;
+ if (!IS_LS_FREQ(i2c->twsi_freq))
+ ds = 15;
+ } else {
+ thp = TWSI_MASTER_CLK_REG_DEF_VAL;
+ mdiv_min = 2;
+ }
for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) {
/*
* An mdiv value of less than 2 seems to not work well
* with ds1337 RTCs, so we constrain it to larger values.
*/
- for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) {
+ for (mdiv_idx = 15; mdiv_idx >= mdiv_min && delta_hz != 0; mdiv_idx--) {
/*
* For given ndiv and mdiv values check the
* two closest thp values.
*/
- tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10;
+ tclk = i2c->twsi_freq * (mdiv_idx + 1) * ds;
tclk *= (1 << ndiv_idx);
- thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
+ if (is_plat_otx2)
+ thp_base = (i2c->sys_freq / tclk) - 2;
+ else
+ thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
for (inc = 0; inc <= 1; inc++) {
thp_idx = thp_base + inc;
if (thp_idx < 5 || thp_idx > 0xff)
continue;
- foscl = i2c->sys_freq / (2 * (thp_idx + 1));
+ if (is_plat_otx2)
+ foscl = i2c->sys_freq / (thp_idx + 2);
+ else
+ foscl = i2c->sys_freq /
+ (2 * (thp_idx + 1));
foscl = foscl / (1 << ndiv_idx);
- foscl = foscl / (mdiv_idx + 1) / 10;
+ foscl = foscl / (mdiv_idx + 1) / ds;
+ if (foscl > i2c->twsi_freq)
+ continue;
diff = abs(foscl - i2c->twsi_freq);
+ /*
+ * Diff holds difference between calculated frequency
+ * value vs desired frequency.
+ * Delta_hz is updated with last minimum diff.
+ */
if (diff < delta_hz) {
delta_hz = diff;
thp = thp_idx;
@@ -694,6 +893,17 @@ void octeon_i2c_set_clock(struct octeon_i2c *i2c)
}
octeon_i2c_reg_write(i2c, SW_TWSI_OP_TWSI_CLK, thp);
octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv);
+ if (is_plat_otx2) {
+ u64 mode;
+
+ mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ /* Set REFCLK_SRC and HS_MODE in TWSX_MODE register */
+ if (!IS_LS_FREQ(i2c->twsi_freq))
+ mode |= TWSX_MODE_HS_MASK;
+ else
+ mode &= ~TWSX_MODE_HS_MASK;
+ octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
+ }
}
int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c)
diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h
index 9bb9f64fdda0..32a44f2d6274 100644
--- a/drivers/i2c/busses/i2c-octeon-core.h
+++ b/drivers/i2c/busses/i2c-octeon-core.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/atomic.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -7,6 +8,7 @@
#include <linux/i2c-smbus.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/pci.h>
/* Controller command patterns */
#define SW_TWSI_V BIT_ULL(63) /* Valid bit */
@@ -37,8 +39,8 @@
/* Controller command and status bits */
#define TWSI_CTL_CE 0x80 /* High level controller enable */
#define TWSI_CTL_ENAB 0x40 /* Bus enable */
-#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */
-#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */
+#define TWSI_CTL_STA 0x20 /* Controller-mode start, HW clears when done */
+#define TWSI_CTL_STP 0x10 /* Controller-mode stop, HW clears when done */
#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */
#define TWSI_CTL_AAK 0x04 /* Assert ACK */
@@ -71,6 +73,7 @@
#define STAT_SLAVE_ACK 0xC8
#define STAT_AD2W_ACK 0xD0
#define STAT_AD2W_NAK 0xD8
+#define STAT_WDOG_TOUT 0xF0
#define STAT_IDLE 0xF8
/* TWSI_INT values */
@@ -92,11 +95,31 @@ struct octeon_i2c_reg_offset {
unsigned int sw_twsi;
unsigned int twsi_int;
unsigned int sw_twsi_ext;
+ unsigned int mode;
+ unsigned int block_ctl;
+ unsigned int block_sts;
+ unsigned int block_fifo;
};
-#define SW_TWSI(x) (x->roff.sw_twsi)
-#define TWSI_INT(x) (x->roff.twsi_int)
-#define SW_TWSI_EXT(x) (x->roff.sw_twsi_ext)
+#define OCTEON_REG_SW_TWSI(x) ((x)->roff.sw_twsi)
+#define OCTEON_REG_TWSI_INT(x) ((x)->roff.twsi_int)
+#define OCTEON_REG_SW_TWSI_EXT(x) ((x)->roff.sw_twsi_ext)
+#define OCTEON_REG_MODE(x) ((x)->roff.mode)
+#define OCTEON_REG_BLOCK_CTL(x) ((x)->roff.block_ctl)
+#define OCTEON_REG_BLOCK_STS(x) ((x)->roff.block_sts)
+#define OCTEON_REG_BLOCK_FIFO(x) ((x)->roff.block_fifo)
+
+/* TWSX_MODE register */
+#define TWSX_MODE_REFCLK_SRC BIT(4)
+#define TWSX_MODE_BLOCK_MODE BIT(2)
+#define TWSX_MODE_HS_MODE BIT(0)
+#define TWSX_MODE_HS_MASK (TWSX_MODE_REFCLK_SRC | TWSX_MODE_HS_MODE)
+
+/* TWSX_BLOCK_STS register */
+#define TWSX_BLOCK_STS_RESET_PTR BIT(0)
+
+/* Set BUS_MON_RST to reset bus monitor */
+#define BUS_MON_RST_MASK BIT(3)
struct octeon_i2c {
wait_queue_head_t queue;
@@ -110,6 +133,7 @@ struct octeon_i2c {
void __iomem *twsi_base;
struct device *dev;
bool hlc_enabled;
+ bool block_enabled;
bool broken_irq_mode;
bool broken_irq_check;
void (*int_enable)(struct octeon_i2c *);
@@ -134,16 +158,16 @@ static inline void octeon_i2c_writeq_flush(u64 val, void __iomem *addr)
* @eop_reg: Register selector
* @data: Value to be written
*
- * The I2C core registers are accessed indirectly via the SW_TWSI CSR.
+ * The I2C core registers are accessed indirectly via the OCTEON_REG_SW_TWSI CSR.
*/
static inline void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data)
{
int tries = 1000;
u64 tmp;
- __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI(i2c));
+ __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
do {
- tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ tmp = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if (--tries < 0)
return;
} while ((tmp & SW_TWSI_V) != 0);
@@ -169,9 +193,9 @@ static inline int octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg,
int tries = 1000;
u64 tmp;
- __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI(i2c));
+ __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
do {
- tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ tmp = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
if (--tries < 0) {
/* signal that the returned data is invalid */
if (error)
@@ -191,24 +215,40 @@ static inline int octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg,
octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT, NULL)
/**
- * octeon_i2c_read_int - read the TWSI_INT register
+ * octeon_i2c_read_int - read the OCTEON_REG_TWSI_INT register
* @i2c: The struct octeon_i2c
*
* Returns the value of the register.
*/
static inline u64 octeon_i2c_read_int(struct octeon_i2c *i2c)
{
- return __raw_readq(i2c->twsi_base + TWSI_INT(i2c));
+ return __raw_readq(i2c->twsi_base + OCTEON_REG_TWSI_INT(i2c));
}
/**
- * octeon_i2c_write_int - write the TWSI_INT register
+ * octeon_i2c_write_int - write the OCTEON_REG_TWSI_INT register
* @i2c: The struct octeon_i2c
* @data: Value to be written
*/
static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
{
- octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT(i2c));
+ octeon_i2c_writeq_flush(data, i2c->twsi_base + OCTEON_REG_TWSI_INT(i2c));
+}
+
+#define IS_LS_FREQ(twsi_freq) ((twsi_freq) <= 400000)
+#define PCI_SUBSYS_DEVID_9XXX 0xB
+#define PCI_SUBSYS_MASK GENMASK(15, 12)
+/**
+ * octeon_i2c_is_otx2 - check for chip ID
+ * @pdev: PCI dev structure
+ *
+ * Returns true if the device is an OcteonTX2, false otherwise.
+ */
+static inline bool octeon_i2c_is_otx2(struct pci_dev *pdev)
+{
+ u32 chip_id = FIELD_GET(PCI_SUBSYS_MASK, pdev->subsystem_device);
+
+ return (chip_id == PCI_SUBSYS_DEVID_9XXX);
}
/* Prototypes */
diff --git a/drivers/i2c/busses/i2c-octeon-platdrv.c b/drivers/i2c/busses/i2c-octeon-platdrv.c
index 64bda83e65ac..edfca7b20f29 100644
--- a/drivers/i2c/busses/i2c-octeon-platdrv.c
+++ b/drivers/i2c/busses/i2c-octeon-platdrv.c
@@ -122,7 +122,7 @@ static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm octeon_i2c_algo = {
- .master_xfer = octeon_i2c_xfer,
+ .xfer = octeon_i2c_xfer,
.functionality = octeon_i2c_functionality,
};
@@ -136,7 +136,6 @@ static int octeon_i2c_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
int irq, result = 0, hlc_irq = 0;
- struct resource *res_mem;
struct octeon_i2c *i2c;
bool cn78xx_style;
@@ -167,8 +166,7 @@ static int octeon_i2c_probe(struct platform_device *pdev)
i2c->roff.twsi_int = 0x10;
i2c->roff.sw_twsi_ext = 0x18;
- res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->twsi_base = devm_ioremap_resource(&pdev->dev, res_mem);
+ i2c->twsi_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->twsi_base)) {
result = PTR_ERR(i2c->twsi_base);
goto out;
@@ -255,12 +253,11 @@ out:
return result;
};
-static int octeon_i2c_remove(struct platform_device *pdev)
+static void octeon_i2c_remove(struct platform_device *pdev)
{
struct octeon_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
- return 0;
};
static const struct of_device_id octeon_i2c_match[] = {
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index b1086bfb0465..d9f590f0c384 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TI OMAP I2C master mode driver
*
@@ -12,16 +13,6 @@
* Juha Yrjölä <juha.yrjola@solidboot.com>
* Syed Khasim <x0khasim@ti.com>
* Nishant Menon <nm@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -33,12 +24,13 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/mux/consumer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/platform_data/i2c-omap.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/property.h>
/* I2C controller revisions */
#define OMAP_I2C_OMAP1_REV_2 0x20
@@ -220,6 +212,7 @@ struct omap_i2c_dev {
u16 syscstate;
u16 westate;
u16 errata;
+ struct mux_state *mux_state;
};
static const u8 reg_map_ip_v1[] = {
@@ -269,6 +262,8 @@ static const u8 reg_map_ip_v2[] = {
[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
};
+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap);
+
static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
int reg, u16 val)
{
@@ -648,15 +643,28 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx)
(1000 * omap->speed / 8);
}
+static void omap_i2c_wait(struct omap_i2c_dev *omap)
+{
+ u16 stat;
+ u16 mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
+ int count = 0;
+
+ do {
+ stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
+ count++;
+ } while (!(stat & mask) && count < 5);
+}
+
/*
* Low level master read/write transaction.
*/
static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
- struct i2c_msg *msg, int stop)
+ struct i2c_msg *msg, int stop, bool polling)
{
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
- unsigned long timeout;
+ unsigned long time_left;
u16 w;
+ int ret;
dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop);
@@ -680,7 +688,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);
- reinit_completion(&omap->cmd_complete);
+ if (!polling)
+ reinit_completion(&omap->cmd_complete);
omap->cmd_err = 0;
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@@ -732,10 +741,19 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* REVISIT: We should abort the transfer on signals, but the bus goes
* into arbitration and we're currently unable to recover from it.
*/
- timeout = wait_for_completion_timeout(&omap->cmd_complete,
- OMAP_I2C_TIMEOUT);
- if (timeout == 0) {
- dev_err(omap->dev, "controller timed out\n");
+ if (!polling) {
+ time_left = wait_for_completion_timeout(&omap->cmd_complete,
+ OMAP_I2C_TIMEOUT);
+ } else {
+ do {
+ omap_i2c_wait(omap);
+ ret = omap_i2c_xfer_data(omap);
+ } while (ret == -EAGAIN);
+
+ time_left = !ret;
+ }
+
+ if (time_left == 0) {
omap_i2c_reset(omap);
__omap_i2c_init(omap);
return -ETIMEDOUT;
@@ -772,7 +790,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* to do the work during IRQ processing.
*/
static int
-omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+omap_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg msgs[], int num,
+ bool polling)
{
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
int i;
@@ -794,7 +813,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap->set_mpu_wkup_lat(omap->dev, omap->latency);
for (i = 0; i < num; i++) {
- r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)),
+ polling);
if (r != 0)
break;
}
@@ -808,11 +828,22 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap->set_mpu_wkup_lat(omap->dev, -1);
out:
- pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return r;
}
+static int
+omap_i2c_xfer_irq(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ return omap_i2c_xfer_common(adap, msgs, num, false);
+}
+
+static int
+omap_i2c_xfer_polling(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ return omap_i2c_xfer_common(adap, msgs, num, true);
+}
+
static u32
omap_i2c_func(struct i2c_adapter *adap)
{
@@ -1018,27 +1049,8 @@ static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes,
return 0;
}
-static irqreturn_t
-omap_i2c_isr(int irq, void *dev_id)
+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
{
- struct omap_i2c_dev *omap = dev_id;
- irqreturn_t ret = IRQ_HANDLED;
- u16 mask;
- u16 stat;
-
- stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
- mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
-
- if (stat & mask)
- ret = IRQ_WAKE_THREAD;
-
- return ret;
-}
-
-static irqreturn_t
-omap_i2c_isr_thread(int this_irq, void *dev_id)
-{
- struct omap_i2c_dev *omap = dev_id;
u16 bits;
u16 stat;
int err = 0, count = 0;
@@ -1056,7 +1068,8 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
if (!stat) {
/* my work here is done */
- goto out;
+ err = -EAGAIN;
+ break;
}
dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
@@ -1066,8 +1079,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
}
if (stat & OMAP_I2C_STAT_NACK) {
- err |= OMAP_I2C_STAT_NACK;
+ omap->cmd_err |= OMAP_I2C_STAT_NACK;
omap_i2c_ack_stat(omap, OMAP_I2C_STAT_NACK);
+
+ if (!(stat & ~OMAP_I2C_STAT_NACK)) {
+ err = -EAGAIN;
+ break;
+ }
}
if (stat & OMAP_I2C_STAT_AL) {
@@ -1165,15 +1183,26 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
}
} while (stat);
- omap_i2c_complete_cmd(omap, err);
+ return err;
+}
+
+static irqreturn_t
+omap_i2c_isr_thread(int this_irq, void *dev_id)
+{
+ int ret;
+ struct omap_i2c_dev *omap = dev_id;
+
+ ret = omap_i2c_xfer_data(omap);
+ if (ret != -EAGAIN)
+ omap_i2c_complete_cmd(omap, ret);
-out:
return IRQ_HANDLED;
}
static const struct i2c_algorithm omap_i2c_algo = {
- .master_xfer = omap_i2c_xfer,
- .functionality = omap_i2c_func,
+ .xfer = omap_i2c_xfer_irq,
+ .xfer_atomic = omap_i2c_xfer_polling,
+ .functionality = omap_i2c_func,
};
static const struct i2c_adapter_quirks omap_i2c_quirks = {
@@ -1221,7 +1250,7 @@ static const struct of_device_id omap_i2c_of_match[] = {
.compatible = "ti,omap2420-i2c",
.data = &omap2420_pdata,
},
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
#endif
@@ -1314,36 +1343,30 @@ omap_i2c_probe(struct platform_device *pdev)
{
struct omap_i2c_dev *omap;
struct i2c_adapter *adap;
- struct resource *mem;
const struct omap_i2c_bus_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
- const struct of_device_id *match;
int irq;
int r;
u32 rev;
u16 minor, major;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
omap = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL);
if (!omap)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- omap->base = devm_ioremap_resource(&pdev->dev, mem);
+ omap->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(omap->base))
return PTR_ERR(omap->base);
- match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
- if (match) {
- u32 freq = 100000; /* default to 100000 Hz */
+ if (pdev->dev.of_node) {
+ u32 freq = I2C_MAX_STANDARD_MODE_FREQ;
- pdata = match->data;
+ pdata = device_get_match_data(&pdev->dev);
omap->flags = pdata->flags;
of_property_read_u32(node, "clock-frequency", &freq);
@@ -1367,9 +1390,9 @@ omap_i2c_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(omap->dev);
- r = pm_runtime_get_sync(omap->dev);
+ r = pm_runtime_resume_and_get(omap->dev);
if (r < 0)
- goto err_free_mem;
+ goto err_disable_pm;
/*
* Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
@@ -1388,7 +1411,6 @@ omap_i2c_probe(struct platform_device *pdev)
major = OMAP_I2C_REV_SCHEME_0_MAJOR(omap->rev);
break;
case OMAP_I2C_SCHEME_1:
- /* FALLTHROUGH */
default:
omap->regs = (u8 *)reg_map_ip_v2;
rev = (rev << 16) |
@@ -1431,15 +1453,34 @@ omap_i2c_probe(struct platform_device *pdev)
(1000 * omap->speed / 8);
}
+ if (of_property_present(node, "mux-states")) {
+ struct mux_state *mux_state;
+
+ mux_state = devm_mux_state_get(&pdev->dev, NULL);
+ if (IS_ERR(mux_state)) {
+ r = PTR_ERR(mux_state);
+ dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
+ goto err_put_pm;
+ }
+ omap->mux_state = mux_state;
+ r = mux_state_select(omap->mux_state);
+ if (r) {
+ dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
+ goto err_put_pm;
+ }
+ }
+
/* reset ASAP, clearing any IRQs */
- omap_i2c_init(omap);
+ r = omap_i2c_init(omap);
+ if (r)
+ goto err_mux_state_deselect;
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
IRQF_NO_SUSPEND, pdev->name, omap);
else
r = devm_request_threaded_irq(&pdev->dev, omap->irq,
- omap_i2c_isr, omap_i2c_isr_thread,
+ NULL, omap_i2c_isr_thread,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
pdev->name, omap);
@@ -1452,7 +1493,7 @@ omap_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, omap);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
- strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
+ strscpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
adap->algo = &omap_i2c_algo;
adap->quirks = &omap_i2c_quirks;
adap->dev.parent = &pdev->dev;
@@ -1468,39 +1509,45 @@ omap_i2c_probe(struct platform_device *pdev)
dev_info(omap->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr,
major, minor, omap->speed);
- pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return 0;
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
- pm_runtime_dont_use_autosuspend(omap->dev);
+err_mux_state_deselect:
+ if (omap->mux_state)
+ mux_state_deselect(omap->mux_state);
+err_put_pm:
pm_runtime_put_sync(omap->dev);
+err_disable_pm:
+ pm_runtime_dont_use_autosuspend(omap->dev);
pm_runtime_disable(&pdev->dev);
-err_free_mem:
return r;
}
-static int omap_i2c_remove(struct platform_device *pdev)
+static void omap_i2c_remove(struct platform_device *pdev)
{
struct omap_i2c_dev *omap = platform_get_drvdata(pdev);
int ret;
i2c_del_adapter(&omap->adapter);
+
+ if (omap->mux_state)
+ mux_state_deselect(omap->mux_state);
+
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
- return ret;
+ dev_err(omap->dev, "Failed to resume hardware, skip disable\n");
+ else
+ omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
- omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- return 0;
}
-#ifdef CONFIG_PM
static int omap_i2c_runtime_suspend(struct device *dev)
{
struct omap_i2c_dev *omap = dev_get_drvdata(dev);
@@ -1541,21 +1588,40 @@ static int omap_i2c_runtime_resume(struct device *dev)
return 0;
}
+static int omap_i2c_suspend(struct device *dev)
+{
+ /*
+ * If the controller is autosuspended, there is no way to wakeup it once
+ * runtime pm is disabled (in suspend_late()).
+ * But a device may need the controller up during suspend_noirq() or
+ * resume_noirq().
+ * Wakeup the controller while runtime pm is enabled, so it is available
+ * until its suspend_noirq(), and from resume_noirq().
+ */
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int omap_i2c_resume(struct device *dev)
+{
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
static const struct dev_pm_ops omap_i2c_pm_ops = {
- SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
- omap_i2c_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SYSTEM_SLEEP_PM_OPS(omap_i2c_suspend, omap_i2c_resume)
+ RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
+ omap_i2c_runtime_resume, NULL)
};
-#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
-#else
-#define OMAP_I2C_PM_OPS NULL
-#endif /* CONFIG_PM */
static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
.remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
- .pm = OMAP_I2C_PM_OPS,
+ .pm = pm_ptr(&omap_i2c_pm_ops),
.of_match_table = of_match_ptr(omap_i2c_of_match),
},
};
diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c
index dc2a23f4fb52..c9b62892397a 100644
--- a/drivers/i2c/busses/i2c-opal.c
+++ b/drivers/i2c/busses/i2c-opal.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* IBM OPAL I2C driver
* Copyright (C) 2014 IBM
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.
*/
#include <linux/device.h>
@@ -82,8 +70,8 @@ exit:
return rc;
}
-static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
+static int i2c_opal_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
{
unsigned long opal_id = (unsigned long)adap->algo_data;
struct opal_i2c_request req;
@@ -137,7 +125,7 @@ static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr,
case I2C_SMBUS_BYTE:
req.buffer_ra = cpu_to_be64(__pa(&data->byte));
req.size = cpu_to_be32(1);
- /* Fall through */
+ fallthrough;
case I2C_SMBUS_QUICK:
req.type = (read_write == I2C_SMBUS_READ) ?
OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
@@ -191,9 +179,9 @@ static u32 i2c_opal_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm i2c_opal_algo = {
- .master_xfer = i2c_opal_master_xfer,
- .smbus_xfer = i2c_opal_smbus_xfer,
- .functionality = i2c_opal_func,
+ .xfer = i2c_opal_xfer,
+ .smbus_xfer = i2c_opal_smbus_xfer,
+ .functionality = i2c_opal_func,
};
/*
@@ -232,9 +220,9 @@ static int i2c_opal_probe(struct platform_device *pdev)
adapter->dev.of_node = of_node_get(pdev->dev.of_node);
pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);
if (pname)
- strlcpy(adapter->name, pname, sizeof(adapter->name));
+ strscpy(adapter->name, pname, sizeof(adapter->name));
else
- strlcpy(adapter->name, "opal", sizeof(adapter->name));
+ strscpy(adapter->name, "opal", sizeof(adapter->name));
platform_set_drvdata(pdev, adapter);
rc = i2c_add_adapter(adapter);
@@ -244,13 +232,11 @@ static int i2c_opal_probe(struct platform_device *pdev)
return rc;
}
-static int i2c_opal_remove(struct platform_device *pdev)
+static void i2c_opal_remove(struct platform_device *pdev)
{
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
i2c_del_adapter(adapter);
-
- return 0;
}
static const struct of_device_id i2c_opal_of_match[] = {
@@ -263,7 +249,7 @@ MODULE_DEVICE_TABLE(of, i2c_opal_of_match);
static struct platform_driver i2c_opal_driver = {
.probe = i2c_opal_probe,
- .remove = i2c_opal_remove,
+ .remove = i2c_opal_remove,
.driver = {
.name = "i2c-opal",
.of_match_table = i2c_opal_of_match,
diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c
index b6b5a495118b..84a195e35886 100644
--- a/drivers/i2c/busses/i2c-owl.c
+++ b/drivers/i2c/busses/i2c-owl.c
@@ -14,8 +14,10 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
/* I2C registers */
#define OWL_I2C_REG_CTL 0x0000
@@ -76,6 +78,7 @@
#define OWL_I2C_FIFOCTL_TFR BIT(2)
/* I2Cc_FIFOSTAT Bit Mask */
+#define OWL_I2C_FIFOSTAT_CECB BIT(0)
#define OWL_I2C_FIFOSTAT_RNB BIT(1)
#define OWL_I2C_FIFOSTAT_RFE BIT(2)
#define OWL_I2C_FIFOSTAT_TFF BIT(5)
@@ -83,13 +86,11 @@
#define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8)
/* I2C bus timeout */
-#define OWL_I2C_TIMEOUT msecs_to_jiffies(4 * 1000)
+#define OWL_I2C_TIMEOUT_MS (4 * 1000)
+#define OWL_I2C_TIMEOUT msecs_to_jiffies(OWL_I2C_TIMEOUT_MS)
#define OWL_I2C_MAX_RETRIES 50
-#define OWL_I2C_DEF_SPEED_HZ 100000
-#define OWL_I2C_MAX_SPEED_HZ 400000
-
struct owl_i2c_dev {
struct i2c_adapter adap;
struct i2c_msg *msg;
@@ -164,29 +165,31 @@ static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev)
writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV);
}
-static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
+static void owl_i2c_xfer_data(struct owl_i2c_dev *i2c_dev)
{
- struct owl_i2c_dev *i2c_dev = _dev;
struct i2c_msg *msg = i2c_dev->msg;
- unsigned long flags;
unsigned int stat, fifostat;
- spin_lock_irqsave(&i2c_dev->lock, flags);
-
i2c_dev->err = 0;
- /* Handle NACK from slave */
+ /* Handle NACK from target */
fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT);
if (fifostat & OWL_I2C_FIFOSTAT_RNB) {
i2c_dev->err = -ENXIO;
- goto stop;
+ /* Clear NACK error bit by writing "1" */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOSTAT,
+ OWL_I2C_FIFOSTAT_RNB, true);
+ return;
}
/* Handle bus error */
stat = readl(i2c_dev->base + OWL_I2C_REG_STAT);
if (stat & OWL_I2C_STAT_BEB) {
i2c_dev->err = -EIO;
- goto stop;
+ /* Clear BUS error bit by writing "1" */
+ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
+ OWL_I2C_STAT_BEB, true);
+ return;
}
/* Handle FIFO read */
@@ -204,14 +207,22 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
i2c_dev->base + OWL_I2C_REG_TXDAT);
}
}
+}
+
+static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
+{
+ struct owl_i2c_dev *i2c_dev = _dev;
+
+ spin_lock(&i2c_dev->lock);
+
+ owl_i2c_xfer_data(i2c_dev);
-stop:
/* Clear pending interrupts */
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
OWL_I2C_STAT_IRQP, true);
complete_all(&i2c_dev->msg_complete);
- spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ spin_unlock(&i2c_dev->lock);
return IRQ_HANDLED;
}
@@ -238,8 +249,8 @@ static int owl_i2c_check_bus_busy(struct i2c_adapter *adap)
return 0;
}
-static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
+static int owl_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num, bool atomic)
{
struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
struct i2c_msg *msg;
@@ -283,14 +294,15 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
goto err_exit;
}
- reinit_completion(&i2c_dev->msg_complete);
+ if (!atomic)
+ reinit_completion(&i2c_dev->msg_complete);
- /* Enable I2C controller interrupt */
+ /* Enable/disable I2C controller interrupt */
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
- OWL_I2C_CTL_IRQE, true);
+ OWL_I2C_CTL_IRQE, !atomic);
/*
- * Select: FIFO enable, Master mode, Stop enable, Data count enable,
+ * Select: FIFO enable, controller mode, Stop enable, Data count enable,
* Send start bit
*/
i2c_cmd = OWL_I2C_CMD_SECL | OWL_I2C_CMD_MSS | OWL_I2C_CMD_SE |
@@ -302,7 +314,7 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) |
OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE;
- /* Write slave address */
+ /* Write target address */
addr = i2c_8bit_addr_from_msg(&msgs[0]);
writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
@@ -355,20 +367,33 @@ static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
spin_unlock_irqrestore(&i2c_dev->lock, flags);
- time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
- adap->timeout);
+ if (atomic) {
+ /* Wait for Command Execute Completed or NACK Error bits */
+ ret = readl_poll_timeout_atomic(i2c_dev->base + OWL_I2C_REG_FIFOSTAT,
+ val, val & (OWL_I2C_FIFOSTAT_CECB |
+ OWL_I2C_FIFOSTAT_RNB),
+ 10, OWL_I2C_TIMEOUT_MS * 1000);
+ } else {
+ time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
+ adap->timeout);
+ if (!time_left)
+ ret = -ETIMEDOUT;
+ }
spin_lock_irqsave(&i2c_dev->lock, flags);
- if (time_left == 0) {
+
+ if (ret) {
dev_err(&adap->dev, "Transaction timed out\n");
/* Send stop condition and release the bus */
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB,
true);
- ret = -ETIMEDOUT;
goto err_exit;
}
+ if (atomic)
+ owl_i2c_xfer_data(i2c_dev);
+
ret = i2c_dev->err < 0 ? i2c_dev->err : num;
err_exit:
@@ -382,9 +407,22 @@ unlocked_err_exit:
return ret;
}
+static int owl_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ return owl_i2c_xfer_common(adap, msgs, num, false);
+}
+
+static int owl_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return owl_i2c_xfer_common(adap, msgs, num, true);
+}
+
static const struct i2c_algorithm owl_i2c_algorithm = {
- .master_xfer = owl_i2c_master_xfer,
- .functionality = owl_i2c_func,
+ .xfer = owl_i2c_xfer,
+ .xfer_atomic = owl_i2c_xfer_atomic,
+ .functionality = owl_i2c_func,
};
static const struct i2c_adapter_quirks owl_i2c_quirks = {
@@ -399,50 +437,41 @@ static int owl_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct owl_i2c_dev *i2c_dev;
- struct resource *res;
int ret, irq;
i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(dev, res);
+ i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to get IRQ number\n");
+ if (irq < 0)
return irq;
- }
if (of_property_read_u32(dev->of_node, "clock-frequency",
&i2c_dev->bus_freq))
- i2c_dev->bus_freq = OWL_I2C_DEF_SPEED_HZ;
+ i2c_dev->bus_freq = I2C_MAX_STANDARD_MODE_FREQ;
/* We support only frequencies of 100k and 400k for now */
- if (i2c_dev->bus_freq != OWL_I2C_DEF_SPEED_HZ &&
- i2c_dev->bus_freq != OWL_I2C_MAX_SPEED_HZ) {
+ if (i2c_dev->bus_freq != I2C_MAX_STANDARD_MODE_FREQ &&
+ i2c_dev->bus_freq != I2C_MAX_FAST_MODE_FREQ) {
dev_err(dev, "invalid clock-frequency %d\n", i2c_dev->bus_freq);
return -EINVAL;
}
- i2c_dev->clk = devm_clk_get(dev, NULL);
+ i2c_dev->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(i2c_dev->clk)) {
- dev_err(dev, "failed to get clock\n");
+ dev_err(dev, "failed to enable clock\n");
return PTR_ERR(i2c_dev->clk);
}
- ret = clk_prepare_enable(i2c_dev->clk);
- if (ret)
- return ret;
-
i2c_dev->clk_rate = clk_get_rate(i2c_dev->clk);
if (!i2c_dev->clk_rate) {
dev_err(dev, "input clock rate should not be zero\n");
- ret = -EINVAL;
- goto disable_clk;
+ return -EINVAL;
}
init_completion(&i2c_dev->msg_complete);
@@ -463,18 +492,14 @@ static int owl_i2c_probe(struct platform_device *pdev)
i2c_dev);
if (ret) {
dev_err(dev, "failed to request irq %d\n", irq);
- goto disable_clk;
+ return ret;
}
return i2c_add_adapter(&i2c_dev->adap);
-
-disable_clk:
- clk_disable_unprepare(i2c_dev->clk);
-
- return ret;
}
static const struct of_device_id owl_i2c_of_match[] = {
+ { .compatible = "actions,s500-i2c" },
{ .compatible = "actions,s700-i2c" },
{ .compatible = "actions,s900-i2c" },
{ /* sentinel */ }
@@ -485,7 +510,8 @@ static struct platform_driver owl_i2c_driver = {
.probe = owl_i2c_probe,
.driver = {
.name = "owl-i2c",
- .of_match_table = of_match_ptr(owl_i2c_of_match),
+ .of_match_table = owl_i2c_of_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
module_platform_driver(owl_i2c_driver);
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
deleted file mode 100644
index fa41ff799533..000000000000
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/* ------------------------------------------------------------------------ *
- * i2c-parport-light.c I2C bus over parallel port *
- * ------------------------------------------------------------------------ *
- Copyright (C) 2003-2010 Jean Delvare <jdelvare@suse.de>
-
- Based on older i2c-velleman.c driver
- Copyright (C) 1995-2000 Simon G. Vogl
- With some changes from:
- Frodo Looijaard <frodol@dds.nl>
- Kyösti Mälkki <kmalkki@cc.hut.fi>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- * ------------------------------------------------------------------------ */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/ioport.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/i2c-smbus.h>
-#include <linux/io.h>
-#include "i2c-parport.h"
-
-#define DEFAULT_BASE 0x378
-#define DRVNAME "i2c-parport-light"
-
-static struct platform_device *pdev;
-
-static u16 base;
-module_param_hw(base, ushort, ioport, 0);
-MODULE_PARM_DESC(base, "Base I/O address");
-
-static int irq;
-module_param_hw(irq, int, irq, 0);
-MODULE_PARM_DESC(irq, "IRQ (optional)");
-
-/* ----- Low-level parallel port access ----------------------------------- */
-
-static inline void port_write(unsigned char p, unsigned char d)
-{
- outb(d, base+p);
-}
-
-static inline unsigned char port_read(unsigned char p)
-{
- return inb(base+p);
-}
-
-/* ----- Unified line operation functions --------------------------------- */
-
-static inline void line_set(int state, const struct lineop *op)
-{
- u8 oldval = port_read(op->port);
-
- /* Touch only the bit(s) needed */
- if ((op->inverted && !state) || (!op->inverted && state))
- port_write(op->port, oldval | op->val);
- else
- port_write(op->port, oldval & ~op->val);
-}
-
-static inline int line_get(const struct lineop *op)
-{
- u8 oldval = port_read(op->port);
-
- return ((op->inverted && (oldval & op->val) != op->val)
- || (!op->inverted && (oldval & op->val) == op->val));
-}
-
-/* ----- I2C algorithm call-back functions and structures ----------------- */
-
-static void parport_setscl(void *data, int state)
-{
- line_set(state, &adapter_parm[type].setscl);
-}
-
-static void parport_setsda(void *data, int state)
-{
- line_set(state, &adapter_parm[type].setsda);
-}
-
-static int parport_getscl(void *data)
-{
- return line_get(&adapter_parm[type].getscl);
-}
-
-static int parport_getsda(void *data)
-{
- return line_get(&adapter_parm[type].getsda);
-}
-
-/* Encapsulate the functions above in the correct structure
- Note that getscl will be set to NULL by the attaching code for adapters
- that cannot read SCL back */
-static struct i2c_algo_bit_data parport_algo_data = {
- .setsda = parport_setsda,
- .setscl = parport_setscl,
- .getsda = parport_getsda,
- .getscl = parport_getscl,
- .udelay = 50,
- .timeout = HZ,
-};
-
-/* ----- Driver registration ---------------------------------------------- */
-
-static struct i2c_adapter parport_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON,
- .algo_data = &parport_algo_data,
- .name = "Parallel port adapter (light)",
-};
-
-/* SMBus alert support */
-static struct i2c_smbus_alert_setup alert_data = {
-};
-static struct i2c_client *ara;
-static struct lineop parport_ctrl_irq = {
- .val = (1 << 4),
- .port = PORT_CTRL,
-};
-
-static int i2c_parport_probe(struct platform_device *pdev)
-{
- int err;
-
- /* Reset hardware to a sane state (SCL and SDA high) */
- parport_setsda(NULL, 1);
- parport_setscl(NULL, 1);
- /* Other init if needed (power on...) */
- if (adapter_parm[type].init.val) {
- line_set(1, &adapter_parm[type].init);
- /* Give powered devices some time to settle */
- msleep(100);
- }
-
- parport_adapter.dev.parent = &pdev->dev;
- err = i2c_bit_add_bus(&parport_adapter);
- if (err) {
- dev_err(&pdev->dev, "Unable to register with I2C\n");
- return err;
- }
-
- /* Setup SMBus alert if supported */
- if (adapter_parm[type].smbus_alert && irq) {
- alert_data.irq = irq;
- ara = i2c_setup_smbus_alert(&parport_adapter, &alert_data);
- if (ara)
- line_set(1, &parport_ctrl_irq);
- else
- dev_warn(&pdev->dev, "Failed to register ARA client\n");
- }
-
- return 0;
-}
-
-static int i2c_parport_remove(struct platform_device *pdev)
-{
- if (ara) {
- line_set(0, &parport_ctrl_irq);
- i2c_unregister_device(ara);
- ara = NULL;
- }
- i2c_del_adapter(&parport_adapter);
-
- /* Un-init if needed (power off...) */
- if (adapter_parm[type].init.val)
- line_set(0, &adapter_parm[type].init);
-
- return 0;
-}
-
-static struct platform_driver i2c_parport_driver = {
- .driver = {
- .name = DRVNAME,
- },
- .probe = i2c_parport_probe,
- .remove = i2c_parport_remove,
-};
-
-static int __init i2c_parport_device_add(u16 address)
-{
- int err;
-
- pdev = platform_device_alloc(DRVNAME, -1);
- if (!pdev) {
- err = -ENOMEM;
- printk(KERN_ERR DRVNAME ": Device allocation failed\n");
- goto exit;
- }
-
- err = platform_device_add(pdev);
- if (err) {
- printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
- err);
- goto exit_device_put;
- }
-
- return 0;
-
-exit_device_put:
- platform_device_put(pdev);
-exit:
- return err;
-}
-
-static int __init i2c_parport_init(void)
-{
- int err;
-
- if (type < 0) {
- printk(KERN_ERR DRVNAME ": adapter type unspecified\n");
- return -ENODEV;
- }
-
- if (type >= ARRAY_SIZE(adapter_parm)) {
- printk(KERN_ERR DRVNAME ": invalid type (%d)\n", type);
- return -ENODEV;
- }
-
- if (base == 0) {
- pr_info(DRVNAME ": using default base 0x%x\n", DEFAULT_BASE);
- base = DEFAULT_BASE;
- }
-
- if (!request_region(base, 3, DRVNAME))
- return -EBUSY;
-
- if (irq != 0)
- pr_info(DRVNAME ": using irq %d\n", irq);
-
- if (!adapter_parm[type].getscl.val)
- parport_algo_data.getscl = NULL;
-
- /* Sets global pdev as a side effect */
- err = i2c_parport_device_add(base);
- if (err)
- goto exit_release;
-
- err = platform_driver_register(&i2c_parport_driver);
- if (err)
- goto exit_device;
-
- return 0;
-
-exit_device:
- platform_device_unregister(pdev);
-exit_release:
- release_region(base, 3);
- return err;
-}
-
-static void __exit i2c_parport_exit(void)
-{
- platform_driver_unregister(&i2c_parport_driver);
- platform_device_unregister(pdev);
- release_region(base, 3);
-}
-
-MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
-MODULE_DESCRIPTION("I2C bus over parallel port (light)");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_parport_init);
-module_exit(i2c_parport_exit);
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 319209a07353..3249bbd5eb43 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* ------------------------------------------------------------------------ *
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
@@ -9,15 +10,6 @@
Frodo Looijaard <frodol@dds.nl>
Kyösti Mälkki <kmalkki@cc.hut.fi>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
* ------------------------------------------------------------------------ */
#define pr_fmt(fmt) "i2c-parport: " fmt
@@ -33,7 +25,90 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include "i2c-parport.h"
+
+#define PORT_DATA 0
+#define PORT_STAT 1
+#define PORT_CTRL 2
+
+struct lineop {
+ u8 val;
+ u8 port;
+ u8 inverted;
+};
+
+struct adapter_parm {
+ struct lineop setsda;
+ struct lineop setscl;
+ struct lineop getsda;
+ struct lineop getscl;
+ struct lineop init;
+ unsigned int smbus_alert:1;
+};
+
+static const struct adapter_parm adapter_parm[] = {
+ /* type 0: Philips adapter */
+ {
+ .setsda = { 0x80, PORT_DATA, 1 },
+ .setscl = { 0x08, PORT_CTRL, 0 },
+ .getsda = { 0x80, PORT_STAT, 0 },
+ .getscl = { 0x08, PORT_STAT, 0 },
+ },
+ /* type 1: home brew teletext adapter */
+ {
+ .setsda = { 0x02, PORT_DATA, 0 },
+ .setscl = { 0x01, PORT_DATA, 0 },
+ .getsda = { 0x80, PORT_STAT, 1 },
+ },
+ /* type 2: Velleman K8000 adapter */
+ {
+ .setsda = { 0x02, PORT_CTRL, 1 },
+ .setscl = { 0x08, PORT_CTRL, 1 },
+ .getsda = { 0x10, PORT_STAT, 0 },
+ },
+ /* type 3: ELV adapter */
+ {
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x40, PORT_STAT, 1 },
+ .getscl = { 0x08, PORT_STAT, 1 },
+ },
+ /* type 4: ADM1032 evaluation board */
+ {
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x10, PORT_STAT, 1 },
+ .init = { 0xf0, PORT_DATA, 0 },
+ .smbus_alert = 1,
+ },
+ /* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */
+ {
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x10, PORT_STAT, 1 },
+ },
+ /* type 6: Barco LPT->DVI (K5800236) adapter */
+ {
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x20, PORT_STAT, 0 },
+ .getscl = { 0x40, PORT_STAT, 0 },
+ .init = { 0xfc, PORT_DATA, 0 },
+ },
+ /* type 7: One For All JP1 parallel port adapter */
+ {
+ .setsda = { 0x01, PORT_DATA, 0 },
+ .setscl = { 0x02, PORT_DATA, 0 },
+ .getsda = { 0x80, PORT_STAT, 1 },
+ .init = { 0x04, PORT_DATA, 1 },
+ },
+ /* type 8: VCT-jig */
+ {
+ .setsda = { 0x04, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x40, PORT_STAT, 0 },
+ .getscl = { 0x80, PORT_STAT, 1 },
+ },
+};
/* ----- Device list ------------------------------------------------------ */
@@ -48,9 +123,30 @@ struct i2c_par {
static LIST_HEAD(adapter_list);
static DEFINE_MUTEX(adapter_list_lock);
+
#define MAX_DEVICE 4
static int parport[MAX_DEVICE] = {0, -1, -1, -1};
+module_param_array(parport, int, NULL, 0);
+MODULE_PARM_DESC(parport,
+ "List of parallel ports to bind to, by index.\n"
+ " At most " __stringify(MAX_DEVICE) " devices are supported.\n"
+ " Default is one device connected to parport0.\n"
+);
+static int type = -1;
+module_param(type, int, 0);
+MODULE_PARM_DESC(type,
+ "Type of adapter:\n"
+ " 0 = Philips adapter\n"
+ " 1 = home brew teletext adapter\n"
+ " 2 = Velleman K8000 adapter\n"
+ " 3 = ELV adapter\n"
+ " 4 = ADM1032 evaluation board\n"
+ " 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n"
+ " 6 = Barco LPT->DVI (K5800236) adapter\n"
+ " 7 = One For All JP1 parallel port adapter\n"
+ " 8 = VCT-jig\n"
+);
/* ----- Low-level parallel port access ----------------------------------- */
@@ -171,6 +267,16 @@ static void i2c_parport_attach(struct parport *port)
int i;
struct pardev_cb i2c_parport_cb;
+ if (type < 0) {
+ pr_warn("adapter type unspecified\n");
+ return;
+ }
+
+ if (type >= ARRAY_SIZE(adapter_parm)) {
+ pr_warn("invalid type (%d)\n", type);
+ return;
+ }
+
for (i = 0; i < MAX_DEVICE; i++) {
if (parport[i] == -1)
continue;
@@ -202,7 +308,7 @@ static void i2c_parport_attach(struct parport *port)
/* Fill the rest of the structure */
adapter->adapter.owner = THIS_MODULE;
adapter->adapter.class = I2C_CLASS_HWMON;
- strlcpy(adapter->adapter.name, "Parallel port adapter",
+ strscpy(adapter->adapter.name, "Parallel port adapter",
sizeof(adapter->adapter.name));
adapter->algo_data = parport_algo_data;
/* Slow down if we can't sense SCL */
@@ -237,13 +343,17 @@ static void i2c_parport_attach(struct parport *port)
/* Setup SMBus alert if supported */
if (adapter_parm[type].smbus_alert) {
- adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
- &adapter->alert_data);
- if (adapter->ara)
+ struct i2c_client *ara;
+
+ ara = i2c_new_smbus_alert_device(&adapter->adapter,
+ &adapter->alert_data);
+ if (!IS_ERR(ara)) {
+ adapter->ara = ara;
parport_enable_irq(port);
- else
+ } else {
dev_warn(&adapter->pdev->dev,
"Failed to register ARA client\n");
+ }
}
/* Add the new adapter to the list */
@@ -290,41 +400,9 @@ static struct parport_driver i2c_parport_driver = {
.name = "i2c-parport",
.match_port = i2c_parport_attach,
.detach = i2c_parport_detach,
- .devmodel = true,
};
-
-/* ----- Module loading, unloading and information ------------------------ */
-
-static int __init i2c_parport_init(void)
-{
- if (type < 0) {
- pr_warn("adapter type unspecified\n");
- return -ENODEV;
- }
-
- if (type >= ARRAY_SIZE(adapter_parm)) {
- pr_warn("invalid type (%d)\n", type);
- return -ENODEV;
- }
-
- return parport_register_driver(&i2c_parport_driver);
-}
-
-static void __exit i2c_parport_exit(void)
-{
- parport_unregister_driver(&i2c_parport_driver);
-}
+module_parport_driver(i2c_parport_driver);
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("I2C bus over parallel port");
MODULE_LICENSE("GPL");
-
-module_param_array(parport, int, NULL, 0);
-MODULE_PARM_DESC(parport,
- "List of parallel ports to bind to, by index.\n"
- " Atmost " __stringify(MAX_DEVICE) " devices are supported.\n"
- " Default is one device connected to parport0.\n"
-);
-
-module_init(i2c_parport_init);
-module_exit(i2c_parport_exit);
diff --git a/drivers/i2c/busses/i2c-parport.h b/drivers/i2c/busses/i2c-parport.h
deleted file mode 100644
index 84a6616b072f..000000000000
--- a/drivers/i2c/busses/i2c-parport.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* ------------------------------------------------------------------------ *
- * i2c-parport.h I2C bus over parallel port *
- * ------------------------------------------------------------------------ *
- Copyright (C) 2003-2010 Jean Delvare <jdelvare@suse.de>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- * ------------------------------------------------------------------------ */
-
-#define PORT_DATA 0
-#define PORT_STAT 1
-#define PORT_CTRL 2
-
-struct lineop {
- u8 val;
- u8 port;
- u8 inverted;
-};
-
-struct adapter_parm {
- struct lineop setsda;
- struct lineop setscl;
- struct lineop getsda;
- struct lineop getscl;
- struct lineop init;
- unsigned int smbus_alert:1;
-};
-
-static const struct adapter_parm adapter_parm[] = {
- /* type 0: Philips adapter */
- {
- .setsda = { 0x80, PORT_DATA, 1 },
- .setscl = { 0x08, PORT_CTRL, 0 },
- .getsda = { 0x80, PORT_STAT, 0 },
- .getscl = { 0x08, PORT_STAT, 0 },
- },
- /* type 1: home brew teletext adapter */
- {
- .setsda = { 0x02, PORT_DATA, 0 },
- .setscl = { 0x01, PORT_DATA, 0 },
- .getsda = { 0x80, PORT_STAT, 1 },
- },
- /* type 2: Velleman K8000 adapter */
- {
- .setsda = { 0x02, PORT_CTRL, 1 },
- .setscl = { 0x08, PORT_CTRL, 1 },
- .getsda = { 0x10, PORT_STAT, 0 },
- },
- /* type 3: ELV adapter */
- {
- .setsda = { 0x02, PORT_DATA, 1 },
- .setscl = { 0x01, PORT_DATA, 1 },
- .getsda = { 0x40, PORT_STAT, 1 },
- .getscl = { 0x08, PORT_STAT, 1 },
- },
- /* type 4: ADM1032 evaluation board */
- {
- .setsda = { 0x02, PORT_DATA, 1 },
- .setscl = { 0x01, PORT_DATA, 1 },
- .getsda = { 0x10, PORT_STAT, 1 },
- .init = { 0xf0, PORT_DATA, 0 },
- .smbus_alert = 1,
- },
- /* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */
- {
- .setsda = { 0x02, PORT_DATA, 1 },
- .setscl = { 0x01, PORT_DATA, 1 },
- .getsda = { 0x10, PORT_STAT, 1 },
- },
- /* type 6: Barco LPT->DVI (K5800236) adapter */
- {
- .setsda = { 0x02, PORT_DATA, 1 },
- .setscl = { 0x01, PORT_DATA, 1 },
- .getsda = { 0x20, PORT_STAT, 0 },
- .getscl = { 0x40, PORT_STAT, 0 },
- .init = { 0xfc, PORT_DATA, 0 },
- },
- /* type 7: One For All JP1 parallel port adapter */
- {
- .setsda = { 0x01, PORT_DATA, 0 },
- .setscl = { 0x02, PORT_DATA, 0 },
- .getsda = { 0x80, PORT_STAT, 1 },
- .init = { 0x04, PORT_DATA, 1 },
- },
- /* type 8: VCT-jig */
- {
- .setsda = { 0x04, PORT_DATA, 1 },
- .setscl = { 0x01, PORT_DATA, 1 },
- .getsda = { 0x40, PORT_STAT, 0 },
- .getscl = { 0x80, PORT_STAT, 1 },
- },
-};
-
-static int type = -1;
-module_param(type, int, 0);
-MODULE_PARM_DESC(type,
- "Type of adapter:\n"
- " 0 = Philips adapter\n"
- " 1 = home brew teletext adapter\n"
- " 2 = Velleman K8000 adapter\n"
- " 3 = ELV adapter\n"
- " 4 = ADM1032 evaluation board\n"
- " 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n"
- " 6 = Barco LPT->DVI (K5800236) adapter\n"
- " 7 = One For All JP1 parallel port adapter\n"
- " 8 = VCT-jig\n"
-);
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi-core.c
index 50803e5d995b..f4eca44ed183 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi-core.c
@@ -1,111 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2006-2007 PA Semi, Inc
*
* SMBus host driver for PA Semi PWRficient
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/stddef.h>
#include <linux/sched.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/io.h>
-
-static struct pci_driver pasemi_smb_driver;
+#include <linux/stddef.h>
-struct pasemi_smbus {
- struct pci_dev *dev;
- struct i2c_adapter adapter;
- unsigned long base;
- int size;
-};
+#include "i2c-pasemi-core.h"
/* Register offsets */
#define REG_MTXFIFO 0x00
#define REG_MRXFIFO 0x04
+#define REG_XFSTA 0x0c
#define REG_SMSTA 0x14
+#define REG_IMASK 0x18
#define REG_CTL 0x1c
+#define REG_REV 0x28
/* Register defs */
-#define MTXFIFO_READ 0x00000400
-#define MTXFIFO_STOP 0x00000200
-#define MTXFIFO_START 0x00000100
-#define MTXFIFO_DATA_M 0x000000ff
-
-#define MRXFIFO_EMPTY 0x00000100
-#define MRXFIFO_DATA_M 0x000000ff
-
-#define SMSTA_XEN 0x08000000
-#define SMSTA_MTN 0x00200000
-
-#define CTL_MRR 0x00000400
-#define CTL_MTR 0x00000200
-#define CTL_CLK_M 0x000000ff
+#define MTXFIFO_READ BIT(10)
+#define MTXFIFO_STOP BIT(9)
+#define MTXFIFO_START BIT(8)
+#define MTXFIFO_DATA_M GENMASK(7, 0)
+
+#define MRXFIFO_EMPTY BIT(8)
+#define MRXFIFO_DATA_M GENMASK(7, 0)
+
+#define SMSTA_XIP BIT(28)
+#define SMSTA_XEN BIT(27)
+#define SMSTA_JMD BIT(25)
+#define SMSTA_JAM BIT(24)
+#define SMSTA_MTO BIT(23)
+#define SMSTA_MTA BIT(22)
+#define SMSTA_MTN BIT(21)
+#define SMSTA_MRNE BIT(19)
+#define SMSTA_MTE BIT(16)
+#define SMSTA_TOM BIT(6)
+
+#define CTL_EN BIT(11)
+#define CTL_MRR BIT(10)
+#define CTL_MTR BIT(9)
+#define CTL_UJM BIT(8)
+#define CTL_CLK_M GENMASK(7, 0)
-#define CLK_100K_DIV 84
-#define CLK_400K_DIV 21
+/*
+ * The hardware (supposedly) has a 25ms timeout for clock stretching, thus
+ * use 100ms here which should be plenty.
+ */
+#define PASEMI_TRANSFER_TIMEOUT_MS 100
static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
{
- dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n",
- smbus->base + reg, val);
- outl(val, smbus->base + reg);
+ dev_dbg(smbus->dev, "smbus write reg %x val %08x\n", reg, val);
+ iowrite32(val, smbus->ioaddr + reg);
}
static inline int reg_read(struct pasemi_smbus *smbus, int reg)
{
int ret;
- ret = inl(smbus->base + reg);
- dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n",
- smbus->base + reg, ret);
+ ret = ioread32(smbus->ioaddr + reg);
+ dev_dbg(smbus->dev, "smbus read reg %x val %08x\n", reg, ret);
return ret;
}
#define TXFIFO_WR(smbus, reg) reg_write((smbus), REG_MTXFIFO, (reg))
#define RXFIFO_RD(smbus) reg_read((smbus), REG_MRXFIFO)
-static void pasemi_smb_clear(struct pasemi_smbus *smbus)
+static void pasemi_reset(struct pasemi_smbus *smbus)
+{
+ u32 val = (CTL_MTR | CTL_MRR | CTL_UJM | (smbus->clk_div & CTL_CLK_M));
+
+ if (smbus->hw_rev >= 6)
+ val |= CTL_EN;
+
+ reg_write(smbus, REG_CTL, val);
+ reinit_completion(&smbus->irq_completion);
+}
+
+static int pasemi_smb_clear(struct pasemi_smbus *smbus)
{
unsigned int status;
+ int ret;
+
+ /* First wait for the bus to go idle */
+ ret = readx_poll_timeout(ioread32, smbus->ioaddr + REG_SMSTA,
+ status, !(status & (SMSTA_XIP | SMSTA_JAM)),
+ USEC_PER_MSEC,
+ USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);
- status = reg_read(smbus, REG_SMSTA);
+ if (ret < 0) {
+ dev_err(smbus->dev, "Bus is still stuck (status 0x%08x xfstatus 0x%08x)\n",
+ status, reg_read(smbus, REG_XFSTA));
+ return -EIO;
+ }
+
+ /* If any badness happened or there is data in the FIFOs, reset the FIFOs */
+ if ((status & (SMSTA_MRNE | SMSTA_JMD | SMSTA_MTO | SMSTA_TOM | SMSTA_MTN | SMSTA_MTA)) ||
+ !(status & SMSTA_MTE)) {
+ dev_warn(smbus->dev, "Issuing reset due to status 0x%08x (xfstatus 0x%08x)\n",
+ status, reg_read(smbus, REG_XFSTA));
+ pasemi_reset(smbus);
+ }
+
+ /* Clear the flags */
reg_write(smbus, REG_SMSTA, status);
+
+ return 0;
}
static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
{
- int timeout = 10;
unsigned int status;
- status = reg_read(smbus, REG_SMSTA);
-
- while (!(status & SMSTA_XEN) && timeout--) {
- msleep(1);
+ if (smbus->use_irq) {
+ reinit_completion(&smbus->irq_completion);
+ reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);
+ int ret = wait_for_completion_timeout(
+ &smbus->irq_completion,
+ msecs_to_jiffies(PASEMI_TRANSFER_TIMEOUT_MS));
+ reg_write(smbus, REG_IMASK, 0);
status = reg_read(smbus, REG_SMSTA);
+
+ if (ret < 0) {
+ dev_err(smbus->dev,
+ "Completion wait failed with %d, status 0x%08x\n",
+ ret, status);
+ return ret;
+ } else if (ret == 0) {
+ dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
+ return -ETIME;
+ }
+ } else {
+ int ret = readx_poll_timeout(
+ ioread32, smbus->ioaddr + REG_SMSTA,
+ status, status & SMSTA_XEN,
+ USEC_PER_MSEC,
+ USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);
+
+ if (ret < 0) {
+ dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
+ return -ETIME;
+ }
}
- /* Got NACK? */
- if (status & SMSTA_MTN)
- return -ENXIO;
+ /* Controller timeout? */
+ if (status & SMSTA_TOM) {
+ dev_err(smbus->dev, "Controller timeout, status 0x%08x\n", status);
+ return -EIO;
+ }
- if (timeout < 0) {
- dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status);
- reg_write(smbus, REG_SMSTA, status);
+ /* Peripheral timeout? */
+ if (status & SMSTA_MTO) {
+ dev_err(smbus->dev, "Peripheral timeout, status 0x%08x\n", status);
return -ETIME;
}
+ /* Still stuck in a transaction? */
+ if (status & SMSTA_XIP) {
+ dev_err(smbus->dev, "Bus stuck, status 0x%08x\n", status);
+ return -EIO;
+ }
+
+ /* Arbitration loss? */
+ if (status & SMSTA_MTA) {
+ dev_err(smbus->dev, "Arbitration loss, status 0x%08x\n", status);
+ return -EBUSY;
+ }
+
+ /* Got NACK? */
+ if (status & SMSTA_MTN) {
+ dev_err(smbus->dev, "NACK, status 0x%08x\n", status);
+ return -ENXIO;
+ }
+
/* Clear XEN */
reg_write(smbus, REG_SMSTA, SMSTA_XEN);
@@ -145,13 +223,18 @@ static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,
TXFIFO_WR(smbus, msg->buf[msg->len-1] |
(stop ? MTXFIFO_STOP : 0));
+
+ if (stop) {
+ err = pasemi_smb_waitready(smbus);
+ if (err)
+ goto reset_out;
+ }
}
return 0;
reset_out:
- reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
- (CLK_100K_DIV & CTL_CLK_M)));
+ pasemi_reset(smbus);
return err;
}
@@ -161,9 +244,9 @@ static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
struct pasemi_smbus *smbus = adapter->algo_data;
int ret, i;
- pasemi_smb_clear(smbus);
-
- ret = 0;
+ ret = pasemi_smb_clear(smbus);
+ if (ret)
+ return ret;
for (i = 0; i < num && !ret; i++)
ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));
@@ -184,7 +267,9 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
addr <<= 1;
read_flag = read_write == I2C_SMBUS_READ;
- pasemi_smb_clear(smbus);
+ err = pasemi_smb_clear(smbus);
+ if (err)
+ return err;
switch (size) {
case I2C_SMBUS_QUICK:
@@ -317,8 +402,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
return 0;
reset_out:
- reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
- (CLK_100K_DIV & CTL_CLK_M)));
+ pasemi_reset(smbus);
return err;
}
@@ -331,87 +415,51 @@ static u32 pasemi_smb_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm smbus_algorithm = {
- .master_xfer = pasemi_i2c_xfer,
- .smbus_xfer = pasemi_smb_xfer,
- .functionality = pasemi_smb_func,
+ .xfer = pasemi_i2c_xfer,
+ .smbus_xfer = pasemi_smb_xfer,
+ .functionality = pasemi_smb_func,
};
-static int pasemi_smb_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
{
- struct pasemi_smbus *smbus;
int error;
- if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
- return -ENODEV;
-
- smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL);
- if (!smbus)
- return -ENOMEM;
-
- smbus->dev = dev;
- smbus->base = pci_resource_start(dev, 0);
- smbus->size = pci_resource_len(dev, 0);
-
- if (!request_region(smbus->base, smbus->size,
- pasemi_smb_driver.name)) {
- error = -EBUSY;
- goto out_kfree;
- }
-
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
- "PA Semi SMBus adapter at 0x%lx", smbus->base);
- smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ "PA Semi SMBus adapter (%s)", dev_name(smbus->dev));
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
/* set up the sysfs linkage to our parent device */
- smbus->adapter.dev.parent = &dev->dev;
+ smbus->adapter.dev.parent = smbus->dev;
+ smbus->use_irq = 0;
+ init_completion(&smbus->irq_completion);
- reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
- (CLK_100K_DIV & CTL_CLK_M)));
+ if (smbus->hw_rev != PASEMI_HW_REV_PCI)
+ smbus->hw_rev = reg_read(smbus, REG_REV);
- error = i2c_add_adapter(&smbus->adapter);
- if (error)
- goto out_release_region;
+ reg_write(smbus, REG_IMASK, 0);
- pci_set_drvdata(dev, smbus);
+ pasemi_reset(smbus);
- return 0;
+ error = devm_i2c_add_adapter(smbus->dev, &smbus->adapter);
+ if (error)
+ return error;
- out_release_region:
- release_region(smbus->base, smbus->size);
- out_kfree:
- kfree(smbus);
- return error;
+ return 0;
}
+EXPORT_SYMBOL_GPL(pasemi_i2c_common_probe);
-static void pasemi_smb_remove(struct pci_dev *dev)
+irqreturn_t pasemi_irq_handler(int irq, void *dev_id)
{
- struct pasemi_smbus *smbus = pci_get_drvdata(dev);
+ struct pasemi_smbus *smbus = dev_id;
- i2c_del_adapter(&smbus->adapter);
- release_region(smbus->base, smbus->size);
- kfree(smbus);
+ reg_write(smbus, REG_IMASK, 0);
+ complete(&smbus->irq_completion);
+ return IRQ_HANDLED;
}
-
-static const struct pci_device_id pasemi_smb_ids[] = {
- { PCI_DEVICE(0x1959, 0xa003) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, pasemi_smb_ids);
-
-static struct pci_driver pasemi_smb_driver = {
- .name = "i2c-pasemi",
- .id_table = pasemi_smb_ids,
- .probe = pasemi_smb_probe,
- .remove = pasemi_smb_remove,
-};
-
-module_pci_driver(pasemi_smb_driver);
+EXPORT_SYMBOL_GPL(pasemi_irq_handler);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
+MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");
diff --git a/drivers/i2c/busses/i2c-pasemi-core.h b/drivers/i2c/busses/i2c-pasemi-core.h
new file mode 100644
index 000000000000..88821f4e8a9f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pasemi-core.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/completion.h>
+
+#define PASEMI_HW_REV_PCI -1
+
+struct pasemi_smbus {
+ struct device *dev;
+ struct i2c_adapter adapter;
+ void __iomem *ioaddr;
+ unsigned int clk_div;
+ int hw_rev;
+ int use_irq;
+ struct completion irq_completion;
+};
+
+int pasemi_i2c_common_probe(struct pasemi_smbus *smbus);
+
+irqreturn_t pasemi_irq_handler(int irq, void *dev_id);
diff --git a/drivers/i2c/busses/i2c-pasemi-pci.c b/drivers/i2c/busses/i2c-pasemi-pci.c
new file mode 100644
index 000000000000..b9ccb54ec77e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pasemi-pci.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2006-2007 PA Semi, Inc
+ *
+ * SMBus host driver for PA Semi PWRficient
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+
+#include "i2c-pasemi-core.h"
+
+#define CLK_100K_DIV 84
+#define CLK_400K_DIV 21
+
+static struct pci_driver pasemi_smb_pci_driver;
+
+static int pasemi_smb_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct pasemi_smbus *smbus;
+ unsigned long base;
+ int size;
+ int error;
+
+ if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
+ return -ENODEV;
+
+ smbus = devm_kzalloc(&dev->dev, sizeof(*smbus), GFP_KERNEL);
+ if (!smbus)
+ return -ENOMEM;
+
+ smbus->dev = &dev->dev;
+ base = pci_resource_start(dev, 0);
+ size = pci_resource_len(dev, 0);
+ smbus->clk_div = CLK_100K_DIV;
+
+ /*
+ * The original PASemi PCI controllers don't have a register for
+ * their HW revision.
+ */
+ smbus->hw_rev = PASEMI_HW_REV_PCI;
+
+ if (!devm_request_region(&dev->dev, base, size,
+ pasemi_smb_pci_driver.name))
+ return -EBUSY;
+
+ smbus->ioaddr = pcim_iomap(dev, 0, 0);
+ if (!smbus->ioaddr)
+ return -EBUSY;
+
+ smbus->adapter.class = I2C_CLASS_HWMON;
+ error = pasemi_i2c_common_probe(smbus);
+ if (error)
+ return error;
+
+ pci_set_drvdata(dev, smbus);
+
+ return 0;
+}
+
+static const struct pci_device_id pasemi_smb_pci_ids[] = {
+ { PCI_DEVICE(0x1959, 0xa003) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, pasemi_smb_pci_ids);
+
+static struct pci_driver pasemi_smb_pci_driver = {
+ .name = "i2c-pasemi",
+ .id_table = pasemi_smb_pci_ids,
+ .probe = pasemi_smb_pci_probe,
+};
+
+module_pci_driver(pasemi_smb_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
+MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");
diff --git a/drivers/i2c/busses/i2c-pasemi-platform.c b/drivers/i2c/busses/i2c-pasemi-platform.c
new file mode 100644
index 000000000000..a486a37d3863
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pasemi-platform.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 The Asahi Linux Contributors
+ *
+ * PA Semi PWRficient SMBus host driver for Apple SoCs
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include "i2c-pasemi-core.h"
+
+struct pasemi_platform_i2c_data {
+ struct pasemi_smbus smbus;
+ struct clk *clk_ref;
+};
+
+static int
+pasemi_platform_i2c_calc_clk_div(struct pasemi_platform_i2c_data *data,
+ u32 frequency)
+{
+ unsigned long clk_rate = clk_get_rate(data->clk_ref);
+
+ if (!clk_rate)
+ return -EINVAL;
+
+ data->smbus.clk_div = DIV_ROUND_UP(clk_rate, 16 * frequency);
+ if (data->smbus.clk_div < 4)
+ return dev_err_probe(data->smbus.dev, -EINVAL,
+ "Bus frequency %d is too fast.\n",
+ frequency);
+ if (data->smbus.clk_div > 0xff)
+ return dev_err_probe(data->smbus.dev, -EINVAL,
+ "Bus frequency %d is too slow.\n",
+ frequency);
+
+ return 0;
+}
+
+static int pasemi_platform_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pasemi_platform_i2c_data *data;
+ struct pasemi_smbus *smbus;
+ u32 frequency;
+ int error;
+ int irq_num;
+
+ data = devm_kzalloc(dev, sizeof(struct pasemi_platform_i2c_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ smbus = &data->smbus;
+ smbus->dev = dev;
+
+ smbus->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(smbus->ioaddr))
+ return PTR_ERR(smbus->ioaddr);
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency", &frequency))
+ frequency = I2C_MAX_STANDARD_MODE_FREQ;
+
+ data->clk_ref = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(data->clk_ref))
+ return PTR_ERR(data->clk_ref);
+
+ error = pasemi_platform_i2c_calc_clk_div(data, frequency);
+ if (error)
+ return error;
+
+ smbus->adapter.dev.of_node = pdev->dev.of_node;
+ error = pasemi_i2c_common_probe(smbus);
+ if (error)
+ return error;
+
+ irq_num = platform_get_irq(pdev, 0);
+ error = devm_request_irq(smbus->dev, irq_num, pasemi_irq_handler, 0, "pasemi_apple_i2c", (void *)smbus);
+
+ if (!error)
+ smbus->use_irq = 1;
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static void pasemi_platform_i2c_remove(struct platform_device *pdev) { }
+
+static const struct of_device_id pasemi_platform_i2c_of_match[] = {
+ { .compatible = "apple,t8103-i2c" },
+ { .compatible = "apple,i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pasemi_platform_i2c_of_match);
+
+static struct platform_driver pasemi_platform_i2c_driver = {
+ .driver = {
+ .name = "i2c-apple",
+ .of_match_table = pasemi_platform_i2c_of_match,
+ },
+ .probe = pasemi_platform_i2c_probe,
+ .remove = pasemi_platform_i2c_remove,
+};
+module_platform_driver(pasemi_platform_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
+MODULE_DESCRIPTION("Apple/PASemi SMBus platform driver");
diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c
index 946ac646de2a..0cbf2f509527 100644
--- a/drivers/i2c/busses/i2c-pca-isa.c
+++ b/drivers/i2c/busses/i2c-pca-isa.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-pca-isa.c driver for PCA9564 on ISA boards
* Copyright (C) 2004 Arcom Control Systems
* Copyright (C) 2008 Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -104,7 +95,7 @@ static struct i2c_algo_pca_data pca_isa_data = {
/* .data intentionally left NULL, not needed with ISA */
.write_byte = pca_isa_writebyte,
.read_byte = pca_isa_readbyte,
- .wait_for_completion = pca_isa_waitforcompletion,
+ .wait_for_completion_cb = pca_isa_waitforcompletion,
.reset_chip = pca_isa_resetchip,
};
@@ -170,7 +161,7 @@ static int pca_isa_probe(struct device *dev, unsigned int id)
return -ENODEV;
}
-static int pca_isa_remove(struct device *dev, unsigned int id)
+static void pca_isa_remove(struct device *dev, unsigned int id)
{
i2c_del_adapter(&pca_isa_ops);
@@ -179,8 +170,6 @@ static int pca_isa_remove(struct device *dev, unsigned int id)
free_irq(irq, &pca_isa_ops);
}
release_region(base, IO_SIZE);
-
- return 0;
}
static struct isa_driver pca_isa_driver = {
diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c
index de3fe6e828cb..c0f35ebbe37d 100644
--- a/drivers/i2c/busses/i2c-pca-platform.c
+++ b/drivers/i2c/busses/i2c-pca-platform.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c_pca_platform.c
*
@@ -5,9 +6,6 @@
*
* Copyright (C) 2008 Pengutronix
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -21,11 +19,9 @@
#include <linux/platform_device.h>
#include <linux/i2c-algo-pca.h>
#include <linux/platform_data/i2c-pca-platform.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <asm/irq.h>
@@ -36,8 +32,6 @@ struct i2c_pca_pf_data {
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_algo_pca_data algo_data;
- unsigned long io_base;
- unsigned long io_size;
};
/* Read/Write functions for different register alignments */
@@ -143,7 +137,7 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
int ret = 0;
int irq;
- irq = platform_get_irq(pdev, 0);
+ irq = platform_get_irq_optional(pdev, 0);
/* If irq is 0, we do polling. */
if (irq < 0)
irq = 0;
@@ -152,16 +146,13 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ i2c->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c->reg_base))
return PTR_ERR(i2c->reg_base);
init_waitqueue_head(&i2c->wait);
- i2c->io_base = res->start;
- i2c->io_size = resource_size(res);
i2c->irq = irq;
i2c->adap.nr = pdev->id;
@@ -173,7 +164,7 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = np;
- i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
+ i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(i2c->gpio))
return PTR_ERR(i2c->gpio);
@@ -189,7 +180,7 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
}
i2c->algo_data.data = i2c;
- i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion;
+ i2c->algo_data.wait_for_completion_cb = i2c_pca_pf_waitforcompletion;
if (i2c->gpio)
i2c->algo_data.reset_chip = i2c_pca_pf_resetchip;
else
@@ -229,13 +220,11 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
return 0;
}
-static int i2c_pca_pf_remove(struct platform_device *pdev)
+static void i2c_pca_pf_remove(struct platform_device *pdev)
{
struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
-
- return 0;
}
#ifdef CONFIG_OF
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 90946a8b9a75..ac3bb550303f 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -19,6 +11,7 @@
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
AMD Hudson-2, ML, CZ
+ Hygon CZ
SMSC Victory66
Note: we assume there can only be one device, with one or more
@@ -36,25 +29,14 @@
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#include <linux/slab.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/io.h>
+#include <linux/platform_data/x86/amd-fch.h>
-
-/* PIIX4 SMBus address offsets */
-#define SMBHSTSTS (0 + piix4_smba)
-#define SMBHSLVSTS (1 + piix4_smba)
-#define SMBHSTCNT (2 + piix4_smba)
-#define SMBHSTCMD (3 + piix4_smba)
-#define SMBHSTADD (4 + piix4_smba)
-#define SMBHSTDAT0 (5 + piix4_smba)
-#define SMBHSTDAT1 (6 + piix4_smba)
-#define SMBBLKDAT (7 + piix4_smba)
-#define SMBSLVCNT (8 + piix4_smba)
-#define SMBSHDWCMD (9 + piix4_smba)
-#define SMBSLVEVT (0xA + piix4_smba)
-#define SMBSLVDAT (0xC + piix4_smba)
+#include "i2c-piix4.h"
/* count for request_region */
#define SMBIOSIZE 9
@@ -76,13 +58,14 @@
#define PIIX4_BYTE 0x04
#define PIIX4_BYTE_DATA 0x08
#define PIIX4_WORD_DATA 0x0C
-#define PIIX4_BLOCK_DATA 0x14
/* Multi-port constants */
-#define PIIX4_MAX_ADAPTERS 4
+#define PIIX4_MAX_ADAPTERS 4
+#define HUDSON2_MAIN_PORTS 2 /* HUDSON2, KERNCZ reserves ports 3, 4 */
/* SB800 constants */
#define SB800_PIIX4_SMB_IDX 0xcd6
+#define SB800_PIIX4_SMB_MAP_SIZE 2
#define KERNCZ_IMC_IDX 0x3e
#define KERNCZ_IMC_DATA 0x3f
@@ -98,11 +81,14 @@
#define SB800_PIIX4_PORT_IDX_MASK 0x06
#define SB800_PIIX4_PORT_IDX_SHIFT 1
-/* On kerncz, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */
-#define SB800_PIIX4_PORT_IDX_KERNCZ 0x02
-#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18
+/* SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */
+#define SB800_PIIX4_PORT_IDX_KERNCZ (FCH_PM_DECODEEN + 0x02)
+#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ (FCH_PM_DECODEEN_SMBUS0SEL >> 16)
#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3
+#define SB800_PIIX4_FCH_PM_SIZE 8
+#define SB800_ASF_ACPI_PATH "\\_SB.ASFC"
+
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
@@ -147,7 +133,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
.ident = "IBM",
.matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
},
- { },
+ { }
};
/*
@@ -168,8 +154,74 @@ struct i2c_piix4_adapdata {
bool sb800_main;
bool notify_imc;
u8 port; /* Port number, shifted */
+ struct sb800_mmio_cfg mmio_cfg;
};
+int piix4_sb800_region_request(struct device *dev, struct sb800_mmio_cfg *mmio_cfg)
+{
+ if (mmio_cfg->use_mmio) {
+ void __iomem *addr;
+
+ if (!request_mem_region_muxed(FCH_PM_BASE,
+ SB800_PIIX4_FCH_PM_SIZE,
+ "sb800_piix4_smb")) {
+ dev_err(dev,
+ "SMBus base address memory region 0x%x already in use.\n",
+ FCH_PM_BASE);
+ return -EBUSY;
+ }
+
+ addr = ioremap(FCH_PM_BASE,
+ SB800_PIIX4_FCH_PM_SIZE);
+ if (!addr) {
+ release_mem_region(FCH_PM_BASE,
+ SB800_PIIX4_FCH_PM_SIZE);
+ dev_err(dev, "SMBus base address mapping failed.\n");
+ return -ENOMEM;
+ }
+
+ mmio_cfg->addr = addr;
+
+ return 0;
+ }
+
+ if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE,
+ "sb800_piix4_smb")) {
+ dev_err(dev,
+ "SMBus base address index region 0x%x already in use.\n",
+ SB800_PIIX4_SMB_IDX);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(piix4_sb800_region_request, "PIIX4_SMBUS");
+
+void piix4_sb800_region_release(struct device *dev, struct sb800_mmio_cfg *mmio_cfg)
+{
+ if (mmio_cfg->use_mmio) {
+ iounmap(mmio_cfg->addr);
+ release_mem_region(FCH_PM_BASE,
+ SB800_PIIX4_FCH_PM_SIZE);
+ return;
+ }
+
+ release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE);
+}
+EXPORT_SYMBOL_NS_GPL(piix4_sb800_region_release, "PIIX4_SMBUS");
+
+static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev)
+{
+ /*
+ * cd6h/cd7h port I/O accesses can be disabled on AMD processors
+ * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on
+ * the same processors and is the recommended access method.
+ */
+ return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
+ PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
+ PIIX4_dev->revision >= 0x51);
+}
+
static int piix4_setup(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id)
{
@@ -269,12 +321,61 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
return piix4_smba;
}
+static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev,
+ u8 smb_en,
+ u8 aux,
+ u8 *smb_en_status,
+ unsigned short *piix4_smba)
+{
+ struct sb800_mmio_cfg mmio_cfg;
+ u8 smba_en_lo;
+ u8 smba_en_hi;
+ int retval;
+
+ mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
+ retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
+ if (retval)
+ return retval;
+
+ if (mmio_cfg.use_mmio) {
+ smba_en_lo = ioread8(mmio_cfg.addr);
+ smba_en_hi = ioread8(mmio_cfg.addr + 1);
+ } else {
+ outb_p(smb_en, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
+ smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ }
+
+ piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
+
+ if (!smb_en) {
+ *smb_en_status = smba_en_lo & 0x10;
+ *piix4_smba = smba_en_hi << 8;
+ if (aux)
+ *piix4_smba |= 0x20;
+ } else {
+ *smb_en_status = smba_en_lo & 0x01;
+ *piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
+ }
+
+ if (!*smb_en_status) {
+ dev_err(&PIIX4_dev->dev,
+ "SMBus Host Controller not enabled!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id, u8 aux)
{
unsigned short piix4_smba;
- u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel;
+ u8 smb_en, smb_en_status, port_sel;
u8 i2ccfg, i2ccfg_offset = 0x10;
+ struct sb800_mmio_cfg mmio_cfg;
+ int retval;
/* SB800 and later SMBus does not support forcing address */
if (force || force_addr) {
@@ -289,40 +390,18 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
PIIX4_dev->revision >= 0x41) ||
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
- PIIX4_dev->revision >= 0x49))
+ PIIX4_dev->revision >= 0x49) ||
+ (PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON &&
+ PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS))
smb_en = 0x00;
else
smb_en = (aux) ? 0x28 : 0x2c;
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
- dev_err(&PIIX4_dev->dev,
- "SMB base address index region 0x%x already in use.\n",
- SB800_PIIX4_SMB_IDX);
- return -EBUSY;
- }
-
- outb_p(smb_en, SB800_PIIX4_SMB_IDX);
- smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
- outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
- smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
-
- release_region(SB800_PIIX4_SMB_IDX, 2);
-
- if (!smb_en) {
- smb_en_status = smba_en_lo & 0x10;
- piix4_smba = smba_en_hi << 8;
- if (aux)
- piix4_smba |= 0x20;
- } else {
- smb_en_status = smba_en_lo & 0x01;
- piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
- }
+ retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status,
+ &piix4_smba);
- if (!smb_en_status) {
- dev_err(&PIIX4_dev->dev,
- "SMBus Host Controller not enabled!\n");
- return -ENODEV;
- }
+ if (retval)
+ return retval;
if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
return -ENODEV;
@@ -361,25 +440,25 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
piix4_smba, i2ccfg >> 4);
/* Find which register is used for port selection */
- if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) {
- switch (PIIX4_dev->device) {
- case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS:
+ if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD ||
+ PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) {
+ if (PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS ||
+ (PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
+ PIIX4_dev->revision >= 0x1F)) {
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ;
- break;
- case PCI_DEVICE_ID_AMD_HUDSON2_SMBUS:
- default:
+ } else {
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
- break;
}
} else {
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
- "sb800_piix4_smb")) {
+ mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
+ retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
+ if (retval) {
release_region(piix4_smba, SMBIOSIZE);
- return -EBUSY;
+ return retval;
}
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
@@ -389,7 +468,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
SB800_PIIX4_PORT_IDX;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
}
dev_info(&PIIX4_dev->dev,
@@ -439,10 +518,8 @@ static int piix4_setup_aux(struct pci_dev *PIIX4_dev,
return piix4_smba;
}
-static int piix4_transaction(struct i2c_adapter *piix4_adapter)
+int piix4_transaction(struct i2c_adapter *piix4_adapter, unsigned short piix4_smba)
{
- struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter);
- unsigned short piix4_smba = adapdata->smba;
int temp;
int result = 0;
int timeout = 0;
@@ -493,7 +570,7 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
result = -EIO;
dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be "
"locked until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
+ /* Clock stops and target is stuck in mid-transmission */
}
if (temp & 0x04) {
@@ -514,6 +591,7 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
inb_p(SMBHSTDAT1));
return result;
}
+EXPORT_SYMBOL_NS_GPL(piix4_transaction, "PIIX4_SMBUS");
/* Return negative errno on error. */
static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
@@ -578,7 +656,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
- status = piix4_transaction(adap);
+ status = piix4_transaction(adap, piix4_smba);
if (status)
return status;
@@ -667,6 +745,30 @@ static void piix4_imc_wakeup(void)
release_region(KERNCZ_IMC_IDX, 2);
}
+int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
+{
+ u8 smba_en_lo, val;
+
+ if (mmio_cfg->use_mmio) {
+ smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800);
+ val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
+ if (smba_en_lo != val)
+ iowrite8(val, mmio_cfg->addr + piix4_port_sel_sb800);
+
+ return (smba_en_lo & piix4_port_mask_sb800);
+ }
+
+ outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+
+ val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
+ if (smba_en_lo != val)
+ outb_p(val, SB800_PIIX4_SMB_IDX + 1);
+
+ return (smba_en_lo & piix4_port_mask_sb800);
+}
+EXPORT_SYMBOL_NS_GPL(piix4_sb800_port_sel, "PIIX4_SMBUS");
+
/*
* Handles access to multiple SMBus ports on the SB800.
* The port is selected by bits 2:1 of the smb_en register (0x2c).
@@ -683,12 +785,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
unsigned short piix4_smba = adapdata->smba;
int retries = MAX_TIMEOUT;
int smbslvcnt;
- u8 smba_en_lo;
- u8 port;
+ u8 prev_port;
int retval;
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
- return -EBUSY;
+ retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg);
+ if (retval)
+ return retval;
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
smbslvcnt = inb_p(SMBSLVCNT);
@@ -743,18 +845,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
}
}
- outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
- smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
-
- port = adapdata->port;
- if ((smba_en_lo & piix4_port_mask_sb800) != port)
- outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port,
- SB800_PIIX4_SMB_IDX + 1);
+ prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg);
retval = piix4_access(adap, addr, flags, read_write,
command, size, data);
- outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1);
+ piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg);
/* Release the semaphore */
outb_p(smbslvcnt | 0x20, SMBSLVCNT);
@@ -763,7 +859,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
piix4_imc_wakeup();
release:
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg);
return retval;
}
@@ -794,6 +890,7 @@ static const struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
@@ -811,10 +908,12 @@ MODULE_DEVICE_TABLE (pci, piix4_ids);
static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS];
static struct i2c_adapter *piix4_aux_adapter;
+static int piix4_adapter_count;
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
bool sb800_main, u8 port, bool notify_imc,
- const char *name, struct i2c_adapter **padap)
+ u8 hw_port_nr, const char *name,
+ struct i2c_adapter **padap)
{
struct i2c_adapter *adap;
struct i2c_piix4_adapdata *adapdata;
@@ -827,7 +926,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
}
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->class = I2C_CLASS_HWMON;
adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800
: &smbus_algorithm;
@@ -838,6 +937,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
return -ENOMEM;
}
+ adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev);
adapdata->smba = smba;
adapdata->sb800_main = sb800_main;
adapdata->port = port << piix4_port_shift_sb800;
@@ -846,6 +946,12 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
/* set up the sysfs linkage to our parent device */
adap->dev.parent = &dev->dev;
+ if (has_acpi_companion(&dev->dev)) {
+ acpi_preset_companion(&adap->dev,
+ ACPI_COMPANION(&dev->dev),
+ hw_port_nr);
+ }
+
snprintf(adap->name, sizeof(adap->name),
"SMBus PIIX4 adapter%s at %04x", name, smba);
@@ -859,6 +965,14 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
return retval;
}
+ /*
+ * The AUX bus can not be probed as on some platforms it reports all
+ * devices present and all reads return "0".
+ * This would allow the ee1004 to be probed incorrectly.
+ */
+ if (port == 0)
+ i2c_register_spd_write_enable(adap);
+
*padap = adap;
return 0;
}
@@ -870,8 +984,19 @@ static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba,
int port;
int retval;
- for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) {
+ if (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS ||
+ (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
+ dev->revision >= 0x1F)) {
+ piix4_adapter_count = HUDSON2_MAIN_PORTS;
+ } else {
+ piix4_adapter_count = PIIX4_MAX_ADAPTERS;
+ }
+
+ for (port = 0; port < piix4_adapter_count; port++) {
+ u8 hw_port_nr = port == 0 ? 0 : port + 1;
+
retval = piix4_add_adapter(dev, smba, true, port, notify_imc,
+ hw_port_nr,
piix4_main_port_names_sb800[port],
&piix4_main_adapters[port]);
if (retval < 0)
@@ -900,15 +1025,20 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
bool is_sb800 = false;
+ bool is_asf = false;
+ acpi_status status;
+ acpi_handle handle;
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
dev->revision >= 0x40) ||
- dev->vendor == PCI_VENDOR_ID_AMD) {
+ dev->vendor == PCI_VENDOR_ID_AMD ||
+ dev->vendor == PCI_VENDOR_ID_HYGON) {
bool notify_imc = false;
is_sb800 = true;
- if (dev->vendor == PCI_VENDOR_ID_AMD &&
+ if ((dev->vendor == PCI_VENDOR_ID_AMD ||
+ dev->vendor == PCI_VENDOR_ID_HYGON) &&
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
u8 imc;
@@ -940,10 +1070,11 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
return retval;
/* Try to register main SMBus adapter, give up if we can't */
- retval = piix4_add_adapter(dev, retval, false, 0, false, "",
- &piix4_main_adapters[0]);
+ retval = piix4_add_adapter(dev, retval, false, 0, false, 0,
+ "", &piix4_main_adapters[0]);
if (retval < 0)
return retval;
+ piix4_adapter_count = 1;
}
/* Check for auxiliary SMBus on some AMD chipsets */
@@ -959,15 +1090,22 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
}
+ status = acpi_get_handle(NULL, (acpi_string)SB800_ASF_ACPI_PATH, &handle);
+ if (ACPI_SUCCESS(status))
+ is_asf = true;
+
if (dev->vendor == PCI_VENDOR_ID_AMD &&
- dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) {
- retval = piix4_setup_sb800(dev, id, 1);
+ (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS ||
+ dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) {
+ /* Do not setup AUX port if ASF is enabled */
+ if (!is_asf)
+ retval = piix4_setup_sb800(dev, id, 1);
}
if (retval > 0) {
/* Try to add the aux adapter if it exists,
* piix4_add_adapter will clean up if this fails */
- piix4_add_adapter(dev, retval, false, 0, false,
+ piix4_add_adapter(dev, retval, false, 0, false, 1,
is_sb800 ? piix4_aux_port_name_sb800 : "",
&piix4_aux_adapter);
}
@@ -990,7 +1128,7 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
static void piix4_remove(struct pci_dev *dev)
{
- int port = PIIX4_MAX_ADAPTERS;
+ int port = piix4_adapter_count;
while (--port >= 0) {
if (piix4_main_adapters[port]) {
@@ -1014,7 +1152,7 @@ static struct pci_driver piix4_driver = {
module_pci_driver(piix4_driver);
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
- "Philip Edelbrock <phil@netroedge.com>");
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("PIIX4 SMBus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-piix4.h b/drivers/i2c/busses/i2c-piix4.h
new file mode 100644
index 000000000000..36bc6ce82a27
--- /dev/null
+++ b/drivers/i2c/busses/i2c-piix4.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * PIIX4/SB800 SMBus Interfaces
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Sanket Goswami <Sanket.Goswami@amd.com>
+ */
+
+#ifndef I2C_PIIX4_H
+#define I2C_PIIX4_H
+
+#include <linux/types.h>
+
+/* PIIX4 SMBus address offsets */
+#define SMBHSTSTS (0x00 + piix4_smba)
+#define SMBHSLVSTS (0x01 + piix4_smba)
+#define SMBHSTCNT (0x02 + piix4_smba)
+#define SMBHSTCMD (0x03 + piix4_smba)
+#define SMBHSTADD (0x04 + piix4_smba)
+#define SMBHSTDAT0 (0x05 + piix4_smba)
+#define SMBHSTDAT1 (0x06 + piix4_smba)
+#define SMBBLKDAT (0x07 + piix4_smba)
+#define SMBSLVCNT (0x08 + piix4_smba)
+#define SMBSHDWCMD (0x09 + piix4_smba)
+#define SMBSLVEVT (0x0A + piix4_smba)
+#define SMBSLVDAT (0x0C + piix4_smba)
+
+/* PIIX4 constants */
+#define PIIX4_BLOCK_DATA 0x14
+
+struct sb800_mmio_cfg {
+ void __iomem *addr;
+ bool use_mmio;
+};
+
+int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg);
+int piix4_transaction(struct i2c_adapter *piix4_adapter, unsigned short piix4_smba);
+int piix4_sb800_region_request(struct device *dev, struct sb800_mmio_cfg *mmio_cfg);
+void piix4_sb800_region_release(struct device *dev, struct sb800_mmio_cfg *mmio_cfg);
+
+#endif /* I2C_PIIX4_H */
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
deleted file mode 100644
index 0829cb696d9d..000000000000
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- * Specific bus support for PMC-TWI compliant implementation on MSP71xx.
- *
- * Copyright 2005-2007 PMC-Sierra, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-
-#define DRV_NAME "pmcmsptwi"
-
-#define MSP_TWI_SF_CLK_REG_OFFSET 0x00
-#define MSP_TWI_HS_CLK_REG_OFFSET 0x04
-#define MSP_TWI_CFG_REG_OFFSET 0x08
-#define MSP_TWI_CMD_REG_OFFSET 0x0c
-#define MSP_TWI_ADD_REG_OFFSET 0x10
-#define MSP_TWI_DAT_0_REG_OFFSET 0x14
-#define MSP_TWI_DAT_1_REG_OFFSET 0x18
-#define MSP_TWI_INT_STS_REG_OFFSET 0x1c
-#define MSP_TWI_INT_MSK_REG_OFFSET 0x20
-#define MSP_TWI_BUSY_REG_OFFSET 0x24
-
-#define MSP_TWI_INT_STS_DONE (1 << 0)
-#define MSP_TWI_INT_STS_LOST_ARBITRATION (1 << 1)
-#define MSP_TWI_INT_STS_NO_RESPONSE (1 << 2)
-#define MSP_TWI_INT_STS_DATA_COLLISION (1 << 3)
-#define MSP_TWI_INT_STS_BUSY (1 << 4)
-#define MSP_TWI_INT_STS_ALL 0x1f
-
-#define MSP_MAX_BYTES_PER_RW 8
-#define MSP_MAX_POLL 5
-#define MSP_POLL_DELAY 10
-#define MSP_IRQ_TIMEOUT (MSP_MAX_POLL * MSP_POLL_DELAY)
-
-/* IO Operation macros */
-#define pmcmsptwi_readl __raw_readl
-#define pmcmsptwi_writel __raw_writel
-
-/* TWI command type */
-enum pmcmsptwi_cmd_type {
- MSP_TWI_CMD_WRITE = 0, /* Write only */
- MSP_TWI_CMD_READ = 1, /* Read only */
- MSP_TWI_CMD_WRITE_READ = 2, /* Write then Read */
-};
-
-/* The possible results of the xferCmd */
-enum pmcmsptwi_xfer_result {
- MSP_TWI_XFER_OK = 0,
- MSP_TWI_XFER_TIMEOUT,
- MSP_TWI_XFER_BUSY,
- MSP_TWI_XFER_DATA_COLLISION,
- MSP_TWI_XFER_NO_RESPONSE,
- MSP_TWI_XFER_LOST_ARBITRATION,
-};
-
-/* Corresponds to a PMCTWI clock configuration register */
-struct pmcmsptwi_clock {
- u8 filter; /* Bits 15:12, default = 0x03 */
- u16 clock; /* Bits 9:0, default = 0x001f */
-};
-
-struct pmcmsptwi_clockcfg {
- struct pmcmsptwi_clock standard; /* The standard/fast clock config */
- struct pmcmsptwi_clock highspeed; /* The highspeed clock config */
-};
-
-/* Corresponds to the main TWI configuration register */
-struct pmcmsptwi_cfg {
- u8 arbf; /* Bits 15:12, default=0x03 */
- u8 nak; /* Bits 11:8, default=0x03 */
- u8 add10; /* Bit 7, default=0x00 */
- u8 mst_code; /* Bits 6:4, default=0x00 */
- u8 arb; /* Bit 1, default=0x01 */
- u8 highspeed; /* Bit 0, default=0x00 */
-};
-
-/* A single pmctwi command to issue */
-struct pmcmsptwi_cmd {
- u16 addr; /* The slave address (7 or 10 bits) */
- enum pmcmsptwi_cmd_type type; /* The command type */
- u8 write_len; /* Number of bytes in the write buffer */
- u8 read_len; /* Number of bytes in the read buffer */
- u8 *write_data; /* Buffer of characters to send */
- u8 *read_data; /* Buffer to fill with incoming data */
-};
-
-/* The private data */
-struct pmcmsptwi_data {
- void __iomem *iobase; /* iomapped base for IO */
- int irq; /* IRQ to use (0 disables) */
- struct completion wait; /* Completion for xfer */
- struct mutex lock; /* Used for threadsafeness */
- enum pmcmsptwi_xfer_result last_result; /* result of last xfer */
-};
-
-/* The default settings */
-static const struct pmcmsptwi_clockcfg pmcmsptwi_defclockcfg = {
- .standard = {
- .filter = 0x3,
- .clock = 0x1f,
- },
- .highspeed = {
- .filter = 0x3,
- .clock = 0x1f,
- },
-};
-
-static const struct pmcmsptwi_cfg pmcmsptwi_defcfg = {
- .arbf = 0x03,
- .nak = 0x03,
- .add10 = 0x00,
- .mst_code = 0x00,
- .arb = 0x01,
- .highspeed = 0x00,
-};
-
-static struct pmcmsptwi_data pmcmsptwi_data;
-
-static struct i2c_adapter pmcmsptwi_adapter;
-
-/* inline helper functions */
-static inline u32 pmcmsptwi_clock_to_reg(
- const struct pmcmsptwi_clock *clock)
-{
- return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff);
-}
-
-static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg)
-{
- return ((cfg->arbf & 0xf) << 12) |
- ((cfg->nak & 0xf) << 8) |
- ((cfg->add10 & 0x1) << 7) |
- ((cfg->mst_code & 0x7) << 4) |
- ((cfg->arb & 0x1) << 1) |
- (cfg->highspeed & 0x1);
-}
-
-static inline void pmcmsptwi_reg_to_cfg(u32 reg, struct pmcmsptwi_cfg *cfg)
-{
- cfg->arbf = (reg >> 12) & 0xf;
- cfg->nak = (reg >> 8) & 0xf;
- cfg->add10 = (reg >> 7) & 0x1;
- cfg->mst_code = (reg >> 4) & 0x7;
- cfg->arb = (reg >> 1) & 0x1;
- cfg->highspeed = reg & 0x1;
-}
-
-/*
- * Sets the current clock configuration
- */
-static void pmcmsptwi_set_clock_config(const struct pmcmsptwi_clockcfg *cfg,
- struct pmcmsptwi_data *data)
-{
- mutex_lock(&data->lock);
- pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->standard),
- data->iobase + MSP_TWI_SF_CLK_REG_OFFSET);
- pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->highspeed),
- data->iobase + MSP_TWI_HS_CLK_REG_OFFSET);
- mutex_unlock(&data->lock);
-}
-
-/*
- * Gets the current TWI bus configuration
- */
-static void pmcmsptwi_get_twi_config(struct pmcmsptwi_cfg *cfg,
- struct pmcmsptwi_data *data)
-{
- mutex_lock(&data->lock);
- pmcmsptwi_reg_to_cfg(pmcmsptwi_readl(
- data->iobase + MSP_TWI_CFG_REG_OFFSET), cfg);
- mutex_unlock(&data->lock);
-}
-
-/*
- * Sets the current TWI bus configuration
- */
-static void pmcmsptwi_set_twi_config(const struct pmcmsptwi_cfg *cfg,
- struct pmcmsptwi_data *data)
-{
- mutex_lock(&data->lock);
- pmcmsptwi_writel(pmcmsptwi_cfg_to_reg(cfg),
- data->iobase + MSP_TWI_CFG_REG_OFFSET);
- mutex_unlock(&data->lock);
-}
-
-/*
- * Parses the 'int_sts' register and returns a well-defined error code
- */
-static enum pmcmsptwi_xfer_result pmcmsptwi_get_result(u32 reg)
-{
- if (reg & MSP_TWI_INT_STS_LOST_ARBITRATION) {
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Result: Lost arbitration\n");
- return MSP_TWI_XFER_LOST_ARBITRATION;
- } else if (reg & MSP_TWI_INT_STS_NO_RESPONSE) {
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Result: No response\n");
- return MSP_TWI_XFER_NO_RESPONSE;
- } else if (reg & MSP_TWI_INT_STS_DATA_COLLISION) {
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Result: Data collision\n");
- return MSP_TWI_XFER_DATA_COLLISION;
- } else if (reg & MSP_TWI_INT_STS_BUSY) {
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Result: Bus busy\n");
- return MSP_TWI_XFER_BUSY;
- }
-
- dev_dbg(&pmcmsptwi_adapter.dev, "Result: Operation succeeded\n");
- return MSP_TWI_XFER_OK;
-}
-
-/*
- * In interrupt mode, handle the interrupt.
- * NOTE: Assumes data->lock is held.
- */
-static irqreturn_t pmcmsptwi_interrupt(int irq, void *ptr)
-{
- struct pmcmsptwi_data *data = ptr;
-
- u32 reason = pmcmsptwi_readl(data->iobase +
- MSP_TWI_INT_STS_REG_OFFSET);
- pmcmsptwi_writel(reason, data->iobase + MSP_TWI_INT_STS_REG_OFFSET);
-
- dev_dbg(&pmcmsptwi_adapter.dev, "Got interrupt 0x%08x\n", reason);
- if (!(reason & MSP_TWI_INT_STS_DONE))
- return IRQ_NONE;
-
- data->last_result = pmcmsptwi_get_result(reason);
- complete(&data->wait);
-
- return IRQ_HANDLED;
-}
-
-/*
- * Probe for and register the device and return 0 if there is one.
- */
-static int pmcmsptwi_probe(struct platform_device *pldev)
-{
- struct resource *res;
- int rc = -ENODEV;
-
- /* get the static platform resources */
- res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pldev->dev, "IOMEM resource not found\n");
- goto ret_err;
- }
-
- /* reserve the memory region */
- if (!request_mem_region(res->start, resource_size(res),
- pldev->name)) {
- dev_err(&pldev->dev,
- "Unable to get memory/io address region 0x%08x\n",
- res->start);
- rc = -EBUSY;
- goto ret_err;
- }
-
- /* remap the memory */
- pmcmsptwi_data.iobase = ioremap_nocache(res->start,
- resource_size(res));
- if (!pmcmsptwi_data.iobase) {
- dev_err(&pldev->dev,
- "Unable to ioremap address 0x%08x\n", res->start);
- rc = -EIO;
- goto ret_unreserve;
- }
-
- /* request the irq */
- pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
- if (pmcmsptwi_data.irq) {
- rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
- IRQF_SHARED, pldev->name, &pmcmsptwi_data);
- if (rc == 0) {
- /*
- * Enable 'DONE' interrupt only.
- *
- * If you enable all interrupts, you will get one on
- * error and another when the operation completes.
- * This way you only have to handle one interrupt,
- * but you can still check all result flags.
- */
- pmcmsptwi_writel(MSP_TWI_INT_STS_DONE,
- pmcmsptwi_data.iobase +
- MSP_TWI_INT_MSK_REG_OFFSET);
- } else {
- dev_warn(&pldev->dev,
- "Could not assign TWI IRQ handler "
- "to irq %d (continuing with poll)\n",
- pmcmsptwi_data.irq);
- pmcmsptwi_data.irq = 0;
- }
- }
-
- init_completion(&pmcmsptwi_data.wait);
- mutex_init(&pmcmsptwi_data.lock);
-
- pmcmsptwi_set_clock_config(&pmcmsptwi_defclockcfg, &pmcmsptwi_data);
- pmcmsptwi_set_twi_config(&pmcmsptwi_defcfg, &pmcmsptwi_data);
-
- printk(KERN_INFO DRV_NAME ": Registering MSP71xx I2C adapter\n");
-
- pmcmsptwi_adapter.dev.parent = &pldev->dev;
- platform_set_drvdata(pldev, &pmcmsptwi_adapter);
- i2c_set_adapdata(&pmcmsptwi_adapter, &pmcmsptwi_data);
-
- rc = i2c_add_adapter(&pmcmsptwi_adapter);
- if (rc)
- goto ret_unmap;
-
- return 0;
-
-ret_unmap:
- if (pmcmsptwi_data.irq) {
- pmcmsptwi_writel(0,
- pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
- free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
- }
-
- iounmap(pmcmsptwi_data.iobase);
-
-ret_unreserve:
- release_mem_region(res->start, resource_size(res));
-
-ret_err:
- return rc;
-}
-
-/*
- * Release the device and return 0 if there is one.
- */
-static int pmcmsptwi_remove(struct platform_device *pldev)
-{
- struct resource *res;
-
- i2c_del_adapter(&pmcmsptwi_adapter);
-
- if (pmcmsptwi_data.irq) {
- pmcmsptwi_writel(0,
- pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
- free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
- }
-
- iounmap(pmcmsptwi_data.iobase);
-
- res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- return 0;
-}
-
-/*
- * Polls the 'busy' register until the command is complete.
- * NOTE: Assumes data->lock is held.
- */
-static void pmcmsptwi_poll_complete(struct pmcmsptwi_data *data)
-{
- int i;
-
- for (i = 0; i < MSP_MAX_POLL; i++) {
- u32 val = pmcmsptwi_readl(data->iobase +
- MSP_TWI_BUSY_REG_OFFSET);
- if (val == 0) {
- u32 reason = pmcmsptwi_readl(data->iobase +
- MSP_TWI_INT_STS_REG_OFFSET);
- pmcmsptwi_writel(reason, data->iobase +
- MSP_TWI_INT_STS_REG_OFFSET);
- data->last_result = pmcmsptwi_get_result(reason);
- return;
- }
- udelay(MSP_POLL_DELAY);
- }
-
- dev_dbg(&pmcmsptwi_adapter.dev, "Result: Poll timeout\n");
- data->last_result = MSP_TWI_XFER_TIMEOUT;
-}
-
-/*
- * Do the transfer (low level):
- * May use interrupt-driven or polling, depending on if an IRQ is
- * presently registered.
- * NOTE: Assumes data->lock is held.
- */
-static enum pmcmsptwi_xfer_result pmcmsptwi_do_xfer(
- u32 reg, struct pmcmsptwi_data *data)
-{
- dev_dbg(&pmcmsptwi_adapter.dev, "Writing cmd reg 0x%08x\n", reg);
- pmcmsptwi_writel(reg, data->iobase + MSP_TWI_CMD_REG_OFFSET);
- if (data->irq) {
- unsigned long timeleft = wait_for_completion_timeout(
- &data->wait, MSP_IRQ_TIMEOUT);
- if (timeleft == 0) {
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Result: IRQ timeout\n");
- complete(&data->wait);
- data->last_result = MSP_TWI_XFER_TIMEOUT;
- }
- } else
- pmcmsptwi_poll_complete(data);
-
- return data->last_result;
-}
-
-/*
- * Helper routine, converts 'pmctwi_cmd' struct to register format
- */
-static inline u32 pmcmsptwi_cmd_to_reg(const struct pmcmsptwi_cmd *cmd)
-{
- return ((cmd->type & 0x3) << 8) |
- (((cmd->write_len - 1) & 0x7) << 4) |
- ((cmd->read_len - 1) & 0x7);
-}
-
-/*
- * Do the transfer (high level)
- */
-static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd(
- struct pmcmsptwi_cmd *cmd,
- struct pmcmsptwi_data *data)
-{
- enum pmcmsptwi_xfer_result retval;
-
- mutex_lock(&data->lock);
- dev_dbg(&pmcmsptwi_adapter.dev,
- "Setting address to 0x%04x\n", cmd->addr);
- pmcmsptwi_writel(cmd->addr, data->iobase + MSP_TWI_ADD_REG_OFFSET);
-
- if (cmd->type == MSP_TWI_CMD_WRITE ||
- cmd->type == MSP_TWI_CMD_WRITE_READ) {
- u64 tmp = be64_to_cpup((__be64 *)cmd->write_data);
- tmp >>= (MSP_MAX_BYTES_PER_RW - cmd->write_len) * 8;
- dev_dbg(&pmcmsptwi_adapter.dev, "Writing 0x%016llx\n", tmp);
- pmcmsptwi_writel(tmp & 0x00000000ffffffffLL,
- data->iobase + MSP_TWI_DAT_0_REG_OFFSET);
- if (cmd->write_len > 4)
- pmcmsptwi_writel(tmp >> 32,
- data->iobase + MSP_TWI_DAT_1_REG_OFFSET);
- }
-
- retval = pmcmsptwi_do_xfer(pmcmsptwi_cmd_to_reg(cmd), data);
- if (retval != MSP_TWI_XFER_OK)
- goto xfer_err;
-
- if (cmd->type == MSP_TWI_CMD_READ ||
- cmd->type == MSP_TWI_CMD_WRITE_READ) {
- int i;
- u64 rmsk = ~(0xffffffffffffffffLL << (cmd->read_len * 8));
- u64 tmp = (u64)pmcmsptwi_readl(data->iobase +
- MSP_TWI_DAT_0_REG_OFFSET);
- if (cmd->read_len > 4)
- tmp |= (u64)pmcmsptwi_readl(data->iobase +
- MSP_TWI_DAT_1_REG_OFFSET) << 32;
- tmp &= rmsk;
- dev_dbg(&pmcmsptwi_adapter.dev, "Read 0x%016llx\n", tmp);
-
- for (i = 0; i < cmd->read_len; i++)
- cmd->read_data[i] = tmp >> i;
- }
-
-xfer_err:
- mutex_unlock(&data->lock);
-
- return retval;
-}
-
-/* -- Algorithm functions -- */
-
-/*
- * Sends an i2c command out on the adapter
- */
-static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msg, int num)
-{
- struct pmcmsptwi_data *data = i2c_get_adapdata(adap);
- struct pmcmsptwi_cmd cmd;
- struct pmcmsptwi_cfg oldcfg, newcfg;
- int ret;
-
- if (num == 2) {
- struct i2c_msg *nextmsg = msg + 1;
-
- cmd.type = MSP_TWI_CMD_WRITE_READ;
- cmd.write_len = msg->len;
- cmd.write_data = msg->buf;
- cmd.read_len = nextmsg->len;
- cmd.read_data = nextmsg->buf;
- } else if (msg->flags & I2C_M_RD) {
- cmd.type = MSP_TWI_CMD_READ;
- cmd.read_len = msg->len;
- cmd.read_data = msg->buf;
- cmd.write_len = 0;
- cmd.write_data = NULL;
- } else {
- cmd.type = MSP_TWI_CMD_WRITE;
- cmd.read_len = 0;
- cmd.read_data = NULL;
- cmd.write_len = msg->len;
- cmd.write_data = msg->buf;
- }
-
- cmd.addr = msg->addr;
-
- if (msg->flags & I2C_M_TEN) {
- pmcmsptwi_get_twi_config(&newcfg, data);
- memcpy(&oldcfg, &newcfg, sizeof(oldcfg));
-
- /* Set the special 10-bit address flag */
- newcfg.add10 = 1;
-
- pmcmsptwi_set_twi_config(&newcfg, data);
- }
-
- /* Execute the command */
- ret = pmcmsptwi_xfer_cmd(&cmd, data);
-
- if (msg->flags & I2C_M_TEN)
- pmcmsptwi_set_twi_config(&oldcfg, data);
-
- dev_dbg(&adap->dev, "I2C %s of %d bytes %s\n",
- (msg->flags & I2C_M_RD) ? "read" : "write", msg->len,
- (ret == MSP_TWI_XFER_OK) ? "succeeded" : "failed");
-
- if (ret != MSP_TWI_XFER_OK) {
- /*
- * TODO: We could potentially loop and retry in the case
- * of MSP_TWI_XFER_TIMEOUT.
- */
- return -EIO;
- }
-
- return num;
-}
-
-static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL;
-}
-
-static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
- .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN,
- .max_write_len = MSP_MAX_BYTES_PER_RW,
- .max_read_len = MSP_MAX_BYTES_PER_RW,
- .max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW,
- .max_comb_2nd_msg_len = MSP_MAX_BYTES_PER_RW,
-};
-
-/* -- Initialization -- */
-
-static const struct i2c_algorithm pmcmsptwi_algo = {
- .master_xfer = pmcmsptwi_master_xfer,
- .functionality = pmcmsptwi_i2c_func,
-};
-
-static struct i2c_adapter pmcmsptwi_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
- .algo = &pmcmsptwi_algo,
- .quirks = &pmcmsptwi_i2c_quirks,
- .name = DRV_NAME,
-};
-
-static struct platform_driver pmcmsptwi_driver = {
- .probe = pmcmsptwi_probe,
- .remove = pmcmsptwi_remove,
- .driver = {
- .name = DRV_NAME,
- },
-};
-
-module_platform_driver(pmcmsptwi_driver);
-
-MODULE_DESCRIPTION("PMC MSP TWI/SMBus/I2C driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index 6e0e546ef83f..8daa0008bd05 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -15,7 +15,6 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/timer.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -32,7 +31,6 @@ struct i2c_pnx_mif {
int ret; /* Return value */
int mode; /* Interface mode */
struct completion complete; /* I/O completion */
- struct timer_list timer; /* Timeout */
u8 * buf; /* Data buffer */
int len; /* Length of data buffer */
int order; /* RX Bytes to order via TX */
@@ -97,7 +95,7 @@ enum {
static inline int wait_timeout(struct i2c_pnx_algo_data *data)
{
- long timeout = data->timeout;
+ long timeout = jiffies_to_msecs(data->timeout);
while (timeout > 0 &&
(ioread32(I2C_REG_STS(data)) & mstatus_active)) {
mdelay(1);
@@ -108,7 +106,7 @@ static inline int wait_timeout(struct i2c_pnx_algo_data *data)
static inline int wait_reset(struct i2c_pnx_algo_data *data)
{
- long timeout = data->timeout;
+ long timeout = jiffies_to_msecs(data->timeout);
while (timeout > 0 &&
(ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {
mdelay(1);
@@ -117,28 +115,10 @@ static inline int wait_reset(struct i2c_pnx_algo_data *data)
return (timeout <= 0);
}
-static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
-{
- struct timer_list *timer = &alg_data->mif.timer;
- unsigned long expires = msecs_to_jiffies(alg_data->timeout);
-
- if (expires <= 1)
- expires = 2;
-
- del_timer_sync(timer);
-
- dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n",
- jiffies, expires);
-
- timer->expires = jiffies + expires;
-
- add_timer(timer);
-}
-
/**
* i2c_pnx_start - start a device
* @slave_addr: slave address
- * @adap: pointer to adapter structure
+ * @alg_data: pointer to local driver data structure
*
* Generate a START signal in the desired mode.
*/
@@ -194,7 +174,7 @@ static int i2c_pnx_start(unsigned char slave_addr,
/**
* i2c_pnx_stop - stop a device
- * @adap: pointer to I2C adapter structure
+ * @alg_data: pointer to local driver data structure
*
* Generate a STOP signal to terminate the master transaction.
*/
@@ -223,7 +203,7 @@ static void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data)
/**
* i2c_pnx_master_xmit - transmit data to slave
- * @adap: pointer to I2C adapter structure
+ * @alg_data: pointer to local driver data structure
*
* Sends one byte of data to the slave
*/
@@ -259,8 +239,6 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
I2C_REG_CTL(alg_data));
- del_timer_sync(&alg_data->mif.timer);
-
dev_dbg(&alg_data->adapter.dev,
"%s(): Waking up xfer routine.\n",
__func__);
@@ -276,8 +254,6 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
I2C_REG_CTL(alg_data));
- /* Stop timer. */
- del_timer_sync(&alg_data->mif.timer);
dev_dbg(&alg_data->adapter.dev,
"%s(): Waking up xfer routine after zero-xfer.\n",
__func__);
@@ -293,7 +269,7 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
/**
* i2c_pnx_master_rcv - receive data from slave
- * @adap: pointer to I2C adapter structure
+ * @alg_data: pointer to local driver data structure
*
* Reads one byte data from the slave
*/
@@ -364,8 +340,6 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
mcntrl_drmie | mcntrl_daie);
iowrite32(ctl, I2C_REG_CTL(alg_data));
- /* Kill timer. */
- del_timer_sync(&alg_data->mif.timer);
complete(&alg_data->mif.complete);
}
}
@@ -400,8 +374,6 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
mcntrl_drmie);
iowrite32(ctl, I2C_REG_CTL(alg_data));
- /* Stop timer, to prevent timeout. */
- del_timer_sync(&alg_data->mif.timer);
complete(&alg_data->mif.complete);
} else if (stat & mstatus_nai) {
/* Slave did not acknowledge, generate a STOP */
@@ -419,8 +391,6 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
/* Our return value. */
alg_data->mif.ret = -EIO;
- /* Stop timer, to prevent timeout. */
- del_timer_sync(&alg_data->mif.timer);
complete(&alg_data->mif.complete);
} else {
/*
@@ -453,9 +423,8 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void i2c_pnx_timeout(struct timer_list *t)
+static void i2c_pnx_timeout(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer);
u32 ctl;
dev_err(&alg_data->adapter.dev,
@@ -472,7 +441,6 @@ static void i2c_pnx_timeout(struct timer_list *t)
iowrite32(ctl, I2C_REG_CTL(alg_data));
wait_reset(alg_data);
alg_data->mif.ret = -EIO;
- complete(&alg_data->mif.complete);
}
static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
@@ -514,6 +482,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_msg *pmsg;
int rc = 0, completed = 0, i;
struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ unsigned long time_left;
u32 stat;
dev_dbg(&alg_data->adapter.dev,
@@ -548,7 +517,6 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
__func__, alg_data->mif.mode, alg_data->mif.len);
- i2c_pnx_arm_timer(alg_data);
/* initialize the completion var */
init_completion(&alg_data->mif.complete);
@@ -564,7 +532,10 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
/* Wait for completion */
- wait_for_completion(&alg_data->mif.complete);
+ time_left = wait_for_completion_timeout(&alg_data->mif.complete,
+ alg_data->timeout);
+ if (time_left == 0)
+ i2c_pnx_timeout(alg_data);
if (!(rc = alg_data->mif.ret))
completed++;
@@ -609,11 +580,10 @@ static u32 i2c_pnx_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm pnx_algorithm = {
- .master_xfer = i2c_pnx_xfer,
+ .xfer = i2c_pnx_xfer,
.functionality = i2c_pnx_func,
};
-#ifdef CONFIG_PM_SLEEP
static int i2c_pnx_controller_suspend(struct device *dev)
{
struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
@@ -630,12 +600,9 @@ static int i2c_pnx_controller_resume(struct device *dev)
return clk_prepare_enable(alg_data->clk);
}
-static SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
- i2c_pnx_controller_suspend, i2c_pnx_controller_resume);
-#define PNX_I2C_PM (&i2c_pnx_pm)
-#else
-#define PNX_I2C_PM NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
+ i2c_pnx_controller_suspend,
+ i2c_pnx_controller_resume);
static int i2c_pnx_probe(struct platform_device *pdev)
{
@@ -657,7 +624,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
alg_data->adapter.algo_data = alg_data;
alg_data->adapter.nr = pdev->id;
- alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
+ alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
+ if (alg_data->timeout <= 1)
+ alg_data->timeout = 2;
+
#ifdef CONFIG_OF
alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
if (pdev->dev.of_node) {
@@ -677,14 +647,11 @@ static int i2c_pnx_probe(struct platform_device *pdev)
if (IS_ERR(alg_data->clk))
return PTR_ERR(alg_data->clk);
- timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0);
-
snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
"%s", pdev->name);
/* Register I/O resource */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ alg_data->ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(alg_data->ioaddr))
return PTR_ERR(alg_data->ioaddr);
@@ -720,7 +687,6 @@ static int i2c_pnx_probe(struct platform_device *pdev)
alg_data->irq = platform_get_irq(pdev, 0);
if (alg_data->irq < 0) {
- dev_err(&pdev->dev, "Failed to get IRQ from platform resource\n");
ret = alg_data->irq;
goto out_clock;
}
@@ -734,8 +700,8 @@ static int i2c_pnx_probe(struct platform_device *pdev)
if (ret < 0)
goto out_clock;
- dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",
- alg_data->adapter.name, res->start, alg_data->irq);
+ dev_dbg(&pdev->dev, "%s: Master at %pap, irq %d.\n",
+ alg_data->adapter.name, &res->start, alg_data->irq);
return 0;
@@ -744,20 +710,18 @@ out_clock:
return ret;
}
-static int i2c_pnx_remove(struct platform_device *pdev)
+static void i2c_pnx_remove(struct platform_device *pdev)
{
struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
i2c_del_adapter(&alg_data->adapter);
clk_disable_unprepare(alg_data->clk);
-
- return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id i2c_pnx_of_match[] = {
{ .compatible = "nxp,pnx-i2c" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, i2c_pnx_of_match);
#endif
@@ -766,7 +730,7 @@ static struct platform_driver i2c_pnx_driver = {
.driver = {
.name = "pnx-i2c",
.of_match_table = of_match_ptr(i2c_pnx_of_match),
- .pm = PNX_I2C_PM,
+ .pm = pm_sleep_ptr(&i2c_pnx_pm),
},
.probe = i2c_pnx_probe,
.remove = i2c_pnx_remove,
@@ -782,7 +746,8 @@ static void __exit i2c_adap_pnx_exit(void)
platform_driver_unregister(&i2c_pnx_driver);
}
-MODULE_AUTHOR("Vitaly Wool, Dennis Kovalev <source@mvista.com>");
+MODULE_AUTHOR("Vitaly Wool");
+MODULE_AUTHOR("Dennis Kovalev <source@mvista.com>");
MODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pnx-i2c");
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index 281113c28314..f99a2cc721a8 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
i2c Support for Apple SMU Controller
Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
<benh@kernel.crashing.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
@@ -23,7 +15,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
-#include <asm/prom.h>
+
#include <asm/pmac_low_i2c.h>
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
@@ -84,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
* but I think the current API makes no sense and I don't want
* any driver that I haven't verified for correctness to go
* anywhere near a pmac i2c bus anyway ...
- *
- * I'm also not completely sure what kind of phases to do between
- * the actual command and the data (what I am _supposed_ to do that
- * is). For now, I assume writes are a single stream and reads have
- * a repeat start/addr phase (but not stop in between)
*/
case I2C_SMBUS_BLOCK_DATA:
buf = data->block;
@@ -140,13 +127,13 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
}
/*
- * Generic i2c master transfer entrypoint. This driver only support single
+ * Generic i2c transfer entrypoint. This driver only supports single
* messages (for "lame i2c" transfers). Anything else should use the smbus
* entry point
*/
-static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
- struct i2c_msg *msgs,
- int num)
+static int i2c_powermac_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
{
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
int rc = 0;
@@ -192,41 +179,39 @@ static u32 i2c_powermac_func(struct i2c_adapter * adapter)
/* For now, we only handle smbus */
static const struct i2c_algorithm i2c_powermac_algorithm = {
- .smbus_xfer = i2c_powermac_smbus_xfer,
- .master_xfer = i2c_powermac_master_xfer,
- .functionality = i2c_powermac_func,
+ .smbus_xfer = i2c_powermac_smbus_xfer,
+ .xfer = i2c_powermac_xfer,
+ .functionality = i2c_powermac_func,
};
static const struct i2c_adapter_quirks i2c_powermac_quirks = {
.max_num_msgs = 1,
};
-static int i2c_powermac_remove(struct platform_device *dev)
+static void i2c_powermac_remove(struct platform_device *dev)
{
struct i2c_adapter *adapter = platform_get_drvdata(dev);
i2c_del_adapter(adapter);
memset(adapter, 0, sizeof(*adapter));
-
- return 0;
}
static u32 i2c_powermac_get_addr(struct i2c_adapter *adap,
struct pmac_i2c_bus *bus,
struct device_node *node)
{
- const __be32 *prop;
- int len;
+ u32 prop;
+ int ret;
/* First check for valid "reg" */
- prop = of_get_property(node, "reg", &len);
- if (prop && (len >= sizeof(int)))
- return (be32_to_cpup(prop) & 0xff) >> 1;
+ ret = of_property_read_u32(node, "reg", &prop);
+ if (ret == 0)
+ return (prop & 0xff) >> 1;
/* Then check old-style "i2c-address" */
- prop = of_get_property(node, "i2c-address", &len);
- if (prop && (len >= sizeof(int)))
- return (be32_to_cpup(prop) & 0xff) >> 1;
+ ret = of_property_read_u32(node, "i2c-address", &prop);
+ if (ret == 0)
+ return (prop & 0xff) >> 1;
/* Now handle some devices with missing "reg" properties */
if (of_node_name_eq(node, "cereal"))
@@ -246,10 +231,10 @@ static void i2c_powermac_create_one(struct i2c_adapter *adap,
struct i2c_board_info info = {};
struct i2c_client *newdev;
- strncpy(info.type, type, sizeof(info.type));
+ strscpy(info.type, type, sizeof(info.type));
info.addr = addr;
- newdev = i2c_new_device(adap, &info);
- if (!newdev)
+ newdev = i2c_new_client_device(adap, &info);
+ if (IS_ERR(newdev))
dev_err(&adap->dev,
"i2c-powermac: Failure to register missing %s\n",
type);
@@ -287,18 +272,17 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap,
{
char tmp[16];
- /* Note: we to _NOT_ want the standard
- * i2c drivers to match with any of our powermac stuff
- * unless they have been specifically modified to handle
- * it on a case by case basis. For example, for thermal
- * control, things like lm75 etc... shall match with their
- * corresponding windfarm drivers, _NOT_ the generic ones,
- * so we force a prefix of AAPL, onto the modalias to
- * make that happen
+ /*
+ * Note: we do _NOT_ want the standard i2c drivers to match with any of
+ * our powermac stuff unless they have been specifically modified to
+ * handle it on a case by case basis. For example, for thermal control,
+ * things like lm75 etc... shall match with their corresponding
+ * windfarm drivers, _NOT_ the generic ones, so we force a prefix of
+ * 'MAC', onto the modalias to make that happen
*/
/* First try proper modalias */
- if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) {
+ if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) {
snprintf(type, type_size, "MAC,%s", tmp);
return true;
}
@@ -324,7 +308,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap,
{
struct i2c_client *newdev;
struct device_node *node;
- bool found_onyx = 0;
+ bool found_onyx = false;
/*
* In some cases we end up with the via-pmu node itself, in this
@@ -365,10 +349,10 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap,
/* Fill out the rest of the info structure */
info.addr = addr;
info.irq = irq_of_parse_and_map(node, 0);
- info.of_node = of_node_get(node);
+ info.fwnode = of_fwnode_handle(of_node_get(node));
- newdev = i2c_new_device(adap, &info);
- if (!newdev) {
+ newdev = i2c_new_client_device(adap, &info);
+ if (IS_ERR(newdev)) {
dev_err(&adap->dev, "i2c-powermac: Failure to register"
" %pOF\n", node);
of_node_put(node);
diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c
deleted file mode 100644
index 287088b8c4c8..000000000000
--- a/drivers/i2c/busses/i2c-puv3.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * I2C driver for PKUnity-v3 SoC
- * Code specific to PKUnity SoC and UniCore ISA
- *
- * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
- * Copyright (C) 2001-2010 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <mach/hardware.h>
-
-/*
- * Poll the i2c status register until the specified bit is set.
- * Returns 0 if timed out (100 msec).
- */
-static short poll_status(unsigned long bit)
-{
- int loop_cntr = 1000;
-
- if (bit & I2C_STATUS_TFNF) {
- do {
- udelay(10);
- } while (!(readl(I2C_STATUS) & bit) && (--loop_cntr > 0));
- } else {
- /* RXRDY handler */
- do {
- if (readl(I2C_TAR) == I2C_TAR_EEPROM)
- msleep(20);
- else
- udelay(10);
- } while (!(readl(I2C_RXFLR) & 0xf) && (--loop_cntr > 0));
- }
-
- return (loop_cntr > 0);
-}
-
-static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
-{
- int i2c_reg = *buf;
-
- /* Read data */
- while (length--) {
- if (!poll_status(I2C_STATUS_TFNF)) {
- dev_dbg(&adap->dev, "Tx FIFO Not Full timeout\n");
- return -ETIMEDOUT;
- }
-
- /* send addr */
- writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
-
- /* get ready to next write */
- i2c_reg++;
-
- /* send read CMD */
- writel(I2C_DATACMD_READ, I2C_DATACMD);
-
- /* wait until the Rx FIFO have available */
- if (!poll_status(I2C_STATUS_RFNE)) {
- dev_dbg(&adap->dev, "RXRDY timeout\n");
- return -ETIMEDOUT;
- }
-
- /* read the data to buf */
- *buf = (readl(I2C_DATACMD) & I2C_DATACMD_DAT_MASK);
- buf++;
- }
-
- return 0;
-}
-
-static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
-{
- int i2c_reg = *buf;
-
- /* Do nothing but storing the reg_num to a static variable */
- if (i2c_reg == -1) {
- printk(KERN_WARNING "Error i2c reg\n");
- return -ETIMEDOUT;
- }
-
- if (length == 1)
- return 0;
-
- buf++;
- length--;
- while (length--) {
- /* send addr */
- writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
-
- /* send write CMD */
- writel(*buf | I2C_DATACMD_WRITE, I2C_DATACMD);
-
- /* wait until the Rx FIFO have available */
- msleep(20);
-
- /* read the data to buf */
- i2c_reg++;
- buf++;
- }
-
- return 0;
-}
-
-/*
- * Generic i2c master transfer entrypoint.
- *
- */
-static int puv3_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
- int num)
-{
- int i, ret;
- unsigned char swap;
-
- /* Disable i2c */
- writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
-
- /* Set the work mode and speed*/
- writel(I2C_CON_MASTER | I2C_CON_SPEED_STD | I2C_CON_SLAVEDISABLE, I2C_CON);
-
- writel(pmsg->addr, I2C_TAR);
-
- /* Enable i2c */
- writel(I2C_ENABLE_ENABLE, I2C_ENABLE);
-
- dev_dbg(&adap->dev, "puv3_i2c_xfer: processing %d messages:\n", num);
-
- for (i = 0; i < num; i++) {
- dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
- pmsg->flags & I2C_M_RD ? "read" : "writ",
- pmsg->len, pmsg->len > 1 ? "s" : "",
- pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr);
-
- if (pmsg->len && pmsg->buf) { /* sanity check */
- if (pmsg->flags & I2C_M_RD)
- ret = xfer_read(adap, pmsg->buf, pmsg->len);
- else
- ret = xfer_write(adap, pmsg->buf, pmsg->len);
-
- if (ret)
- return ret;
-
- }
- dev_dbg(&adap->dev, "transfer complete\n");
- pmsg++; /* next message */
- }
-
- /* XXX: fixup be16_to_cpu in bq27x00_battery.c */
- if (pmsg->addr == I2C_TAR_PWIC) {
- swap = pmsg->buf[0];
- pmsg->buf[0] = pmsg->buf[1];
- pmsg->buf[1] = swap;
- }
-
- return i;
-}
-
-/*
- * Return list of supported functionality.
- */
-static u32 puv3_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm puv3_i2c_algorithm = {
- .master_xfer = puv3_i2c_xfer,
- .functionality = puv3_i2c_func,
-};
-
-/*
- * Main initialization routine.
- */
-static int puv3_i2c_probe(struct platform_device *pdev)
-{
- struct i2c_adapter *adapter;
- struct resource *mem;
- int rc;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem)
- return -ENODEV;
-
- if (!request_mem_region(mem->start, resource_size(mem), "puv3_i2c"))
- return -EBUSY;
-
- adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
- if (adapter == NULL) {
- dev_err(&pdev->dev, "can't allocate interface!\n");
- rc = -ENOMEM;
- goto fail_nomem;
- }
- snprintf(adapter->name, sizeof(adapter->name), "PUV3-I2C at 0x%08x",
- mem->start);
- adapter->algo = &puv3_i2c_algorithm;
- adapter->class = I2C_CLASS_HWMON;
- adapter->dev.parent = &pdev->dev;
-
- platform_set_drvdata(pdev, adapter);
-
- adapter->nr = pdev->id;
- rc = i2c_add_numbered_adapter(adapter);
- if (rc)
- goto fail_add_adapter;
-
- dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n");
- return 0;
-
-fail_add_adapter:
- kfree(adapter);
-fail_nomem:
- release_mem_region(mem->start, resource_size(mem));
-
- return rc;
-}
-
-static int puv3_i2c_remove(struct platform_device *pdev)
-{
- struct i2c_adapter *adapter = platform_get_drvdata(pdev);
- struct resource *mem;
-
- i2c_del_adapter(adapter);
-
- put_device(&pdev->dev);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem->start, resource_size(mem));
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int puv3_i2c_suspend(struct device *dev)
-{
- int poll_count;
- /* Disable the IIC */
- writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
- for (poll_count = 0; poll_count < 50; poll_count++) {
- if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE)
- udelay(25);
- }
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL);
-#define PUV3_I2C_PM (&puv3_i2c_pm)
-
-#else
-#define PUV3_I2C_PM NULL
-#endif
-
-static struct platform_driver puv3_i2c_driver = {
- .probe = puv3_i2c_probe,
- .remove = puv3_i2c_remove,
- .driver = {
- .name = "PKUnity-v3-I2C",
- .pm = PUV3_I2C_PM,
- }
-};
-
-module_platform_driver(puv3_i2c_driver);
-
-MODULE_DESCRIPTION("PKUnity v3 I2C driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:puv3_i2c");
diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index 72ea8f4c61aa..af2094720a4d 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* CE4100 PCI-I2C glue code for PXA's driver
* Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- * License: GPL v2
*
* The CE4100's I2C device is more or less the same one as found on PXA.
- * It does not support slave mode, the register slightly moved. This PCI
+ * It does not support target mode, the register slightly moved. This PCI
* device provides three bars, every contains a single I2C controller.
*/
#include <linux/init.h>
@@ -12,7 +12,6 @@
#include <linux/platform_device.h>
#include <linux/platform_data/i2c-pxa.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/of_address.h>
#define CE4100_PCI_I2C_DEVS 3
@@ -105,7 +104,7 @@ static int ce4100_i2c_probe(struct pci_dev *dev,
int i;
struct ce4100_devices *sds;
- ret = pci_enable_device_mem(dev);
+ ret = pcim_enable_device(dev);
if (ret)
return ret;
@@ -114,10 +113,8 @@ static int ce4100_i2c_probe(struct pci_dev *dev,
return -EINVAL;
}
sds = kzalloc(sizeof(*sds), GFP_KERNEL);
- if (!sds) {
- ret = -ENOMEM;
- goto err_mem;
- }
+ if (!sds)
+ return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) {
sds->pdev[i] = add_i2c_device(dev, i);
@@ -133,14 +130,12 @@ static int ce4100_i2c_probe(struct pci_dev *dev,
err_dev_add:
kfree(sds);
-err_mem:
- pci_disable_device(dev);
return ret;
}
static const struct pci_device_id ce4100_i2c_devices[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
- { },
+ { }
};
static struct pci_driver ce4100_i2c_driver = {
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index fbf91d383b40..968a8b8794da 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c_adap_pxa.c
*
@@ -6,10 +7,6 @@
* Copyright (C) 2002 Intrinsyc Software Inc.
* Copyright (C) 2004-2005 Deep Blue Solutions Ltd.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* History:
* Apr 2002: Initial version [CS]
* Jun 2002: Properly separated algo/adap [FB]
@@ -19,26 +16,121 @@
* Dec 2004: Added support for PXA27x and slave device probing [Liam Girdwood]
* Feb 2005: Rework slave mode handling [RMK]
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/time.h>
-#include <linux/sched.h>
+#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/i2c-pxa.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-#include <linux/io.h>
#include <linux/platform_data/i2c-pxa.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+/* I2C register field definitions */
+#define IBMR_SDAS (1 << 0)
+#define IBMR_SCLS (1 << 1)
+
+#define ICR_START (1 << 0) /* start bit */
+#define ICR_STOP (1 << 1) /* stop bit */
+#define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */
+#define ICR_TB (1 << 3) /* transfer byte bit */
+#define ICR_MA (1 << 4) /* master abort */
+#define ICR_SCLE (1 << 5) /* master clock enable */
+#define ICR_IUE (1 << 6) /* unit enable */
+#define ICR_GCD (1 << 7) /* general call disable */
+#define ICR_ITEIE (1 << 8) /* enable tx interrupts */
+#define ICR_IRFIE (1 << 9) /* enable rx interrupts */
+#define ICR_BEIE (1 << 10) /* enable bus error ints */
+#define ICR_SSDIE (1 << 11) /* slave STOP detected int enable */
+#define ICR_ALDIE (1 << 12) /* enable arbitration interrupt */
+#define ICR_SADIE (1 << 13) /* slave address detected int enable */
+#define ICR_UR (1 << 14) /* unit reset */
+#define ICR_FM (1 << 15) /* fast mode */
+#define ICR_HS (1 << 16) /* High Speed mode */
+#define ICR_A3700_FM (1 << 16) /* fast mode for armada-3700 */
+#define ICR_A3700_HS (1 << 17) /* high speed mode for armada-3700 */
+#define ICR_GPIOEN (1 << 19) /* enable GPIO mode for SCL in HS */
+
+#define ISR_RWM (1 << 0) /* read/write mode */
+#define ISR_ACKNAK (1 << 1) /* ack/nak status */
+#define ISR_UB (1 << 2) /* unit busy */
+#define ISR_IBB (1 << 3) /* bus busy */
+#define ISR_SSD (1 << 4) /* slave stop detected */
+#define ISR_ALD (1 << 5) /* arbitration loss detected */
+#define ISR_ITE (1 << 6) /* tx buffer empty */
+#define ISR_IRF (1 << 7) /* rx buffer full */
+#define ISR_GCAD (1 << 8) /* general call address detected */
+#define ISR_SAD (1 << 9) /* slave address detected */
+#define ISR_BED (1 << 10) /* bus error no ACK/NAK */
+
+#define ILCR_SLV_SHIFT 0
+#define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT)
+#define ILCR_FLV_SHIFT 9
+#define ILCR_FLV_MASK (0x1FF << ILCR_FLV_SHIFT)
+#define ILCR_HLVL_SHIFT 18
+#define ILCR_HLVL_MASK (0x1FF << ILCR_HLVL_SHIFT)
+#define ILCR_HLVH_SHIFT 27
+#define ILCR_HLVH_MASK (0x1F << ILCR_HLVH_SHIFT)
+
+#define IWCR_CNT_SHIFT 0
+#define IWCR_CNT_MASK (0x1F << IWCR_CNT_SHIFT)
+#define IWCR_HS_CNT1_SHIFT 5
+#define IWCR_HS_CNT1_MASK (0x1F << IWCR_HS_CNT1_SHIFT)
+#define IWCR_HS_CNT2_SHIFT 10
+#define IWCR_HS_CNT2_MASK (0x1F << IWCR_HS_CNT2_SHIFT)
+
+/* need a longer timeout if we're dealing with the fact we may well be
+ * looking at a multi-master environment
+ */
+#define DEF_TIMEOUT 32
+
+#define NO_SLAVE (-ENXIO)
+#define BUS_ERROR (-EREMOTEIO)
+#define XFER_NAKED (-ECONNREFUSED)
+#define I2C_RETRY (-2000) /* an error has occurred retry transmit */
-#include <asm/irq.h>
+/* ICR initialize bit values
+ *
+ * 15 FM 0 (100 kHz operation)
+ * 14 UR 0 (No unit reset)
+ * 13 SADIE 0 (Disables the unit from interrupting on slave addresses
+ * matching its slave address)
+ * 12 ALDIE 0 (Disables the unit from interrupt when it loses arbitration
+ * in master mode)
+ * 11 SSDIE 0 (Disables interrupts from a slave stop detected, in slave mode)
+ * 10 BEIE 1 (Enable interrupts from detected bus errors, no ACK sent)
+ * 9 IRFIE 1 (Enable interrupts from full buffer received)
+ * 8 ITEIE 1 (Enables the I2C unit to interrupt when transmit buffer empty)
+ * 7 GCD 1 (Disables i2c unit response to general call messages as a slave)
+ * 6 IUE 0 (Disable unit until we change settings)
+ * 5 SCLE 1 (Enables the i2c clock output for master mode (drives SCL)
+ * 4 MA 0 (Only send stop with the ICR stop bit)
+ * 3 TB 0 (We are not transmitting a byte initially)
+ * 2 ACKNAK 0 (Send an ACK after the unit receives a byte)
+ * 1 STOP 0 (Do not send a STOP)
+ * 0 START 0 (Do not send a START)
+ */
+#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
+
+/* I2C status register init values
+ *
+ * 10 BED 1 (Clear bus error detected)
+ * 9 SAD 1 (Clear slave address detected)
+ * 7 IRF 1 (Clear IDBR Receive Full)
+ * 6 ITE 1 (Clear IDBR Transmit Empty)
+ * 5 ALD 1 (Clear Arbitration Loss Detected)
+ * 4 SSD 1 (Clear Slave Stop Detected)
+ */
+#define I2C_ISR_INIT 0x7FF /* status register init */
struct pxa_reg_layout {
u32 ibmr;
@@ -60,12 +152,7 @@ enum pxa_i2c_types {
REGS_A3700,
};
-#define ICR_BUSMODE_FM (1 << 16) /* shifted fast mode for armada-3700 */
-#define ICR_BUSMODE_HS (1 << 17) /* shifted high speed mode for armada-3700 */
-
-/*
- * I2C registers definitions
- */
+/* I2C register layout definitions */
static struct pxa_reg_layout pxa_reg_layout[] = {
[REGS_PXA2XX] = {
.ibmr = 0x00,
@@ -73,6 +160,8 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
.icr = 0x10,
.isr = 0x18,
.isar = 0x20,
+ .fm = ICR_FM,
+ .hs = ICR_HS,
},
[REGS_PXA3XX] = {
.ibmr = 0x00,
@@ -80,6 +169,8 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
.icr = 0x08,
.isr = 0x0c,
.isar = 0x10,
+ .fm = ICR_FM,
+ .hs = ICR_HS,
},
[REGS_CE4100] = {
.ibmr = 0x14,
@@ -87,6 +178,8 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
.icr = 0x00,
.isr = 0x04,
/* no isar register */
+ .fm = ICR_FM,
+ .hs = ICR_HS,
},
[REGS_PXA910] = {
.ibmr = 0x00,
@@ -96,6 +189,8 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
.isar = 0x20,
.ilcr = 0x28,
.iwcr = 0x30,
+ .fm = ICR_FM,
+ .hs = ICR_HS,
},
[REGS_A3700] = {
.ibmr = 0x00,
@@ -103,73 +198,30 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
.icr = 0x08,
.isr = 0x0c,
.isar = 0x10,
- .fm = ICR_BUSMODE_FM,
- .hs = ICR_BUSMODE_HS,
+ .fm = ICR_A3700_FM,
+ .hs = ICR_A3700_HS,
},
};
+static const struct of_device_id i2c_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
+ { .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
+ { .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
+ { .compatible = "marvell,armada-3700-i2c", .data = (void *)REGS_A3700 },
+ {}
+};
+MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
+
static const struct platform_device_id i2c_pxa_id_table[] = {
{ "pxa2xx-i2c", REGS_PXA2XX },
{ "pxa3xx-pwri2c", REGS_PXA3XX },
{ "ce4100-i2c", REGS_CE4100 },
{ "pxa910-i2c", REGS_PXA910 },
{ "armada-3700-i2c", REGS_A3700 },
- { },
+ { }
};
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
-/*
- * I2C bit definitions
- */
-
-#define ICR_START (1 << 0) /* start bit */
-#define ICR_STOP (1 << 1) /* stop bit */
-#define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */
-#define ICR_TB (1 << 3) /* transfer byte bit */
-#define ICR_MA (1 << 4) /* master abort */
-#define ICR_SCLE (1 << 5) /* master clock enable */
-#define ICR_IUE (1 << 6) /* unit enable */
-#define ICR_GCD (1 << 7) /* general call disable */
-#define ICR_ITEIE (1 << 8) /* enable tx interrupts */
-#define ICR_IRFIE (1 << 9) /* enable rx interrupts */
-#define ICR_BEIE (1 << 10) /* enable bus error ints */
-#define ICR_SSDIE (1 << 11) /* slave STOP detected int enable */
-#define ICR_ALDIE (1 << 12) /* enable arbitration interrupt */
-#define ICR_SADIE (1 << 13) /* slave address detected int enable */
-#define ICR_UR (1 << 14) /* unit reset */
-#define ICR_FM (1 << 15) /* fast mode */
-#define ICR_HS (1 << 16) /* High Speed mode */
-#define ICR_GPIOEN (1 << 19) /* enable GPIO mode for SCL in HS */
-
-#define ISR_RWM (1 << 0) /* read/write mode */
-#define ISR_ACKNAK (1 << 1) /* ack/nak status */
-#define ISR_UB (1 << 2) /* unit busy */
-#define ISR_IBB (1 << 3) /* bus busy */
-#define ISR_SSD (1 << 4) /* slave stop detected */
-#define ISR_ALD (1 << 5) /* arbitration loss detected */
-#define ISR_ITE (1 << 6) /* tx buffer empty */
-#define ISR_IRF (1 << 7) /* rx buffer full */
-#define ISR_GCAD (1 << 8) /* general call address detected */
-#define ISR_SAD (1 << 9) /* slave address detected */
-#define ISR_BED (1 << 10) /* bus error no ACK/NAK */
-
-/* bit field shift & mask */
-#define ILCR_SLV_SHIFT 0
-#define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT)
-#define ILCR_FLV_SHIFT 9
-#define ILCR_FLV_MASK (0x1FF << ILCR_FLV_SHIFT)
-#define ILCR_HLVL_SHIFT 18
-#define ILCR_HLVL_MASK (0x1FF << ILCR_HLVL_SHIFT)
-#define ILCR_HLVH_SHIFT 27
-#define ILCR_HLVH_MASK (0x1F << ILCR_HLVH_SHIFT)
-
-#define IWCR_CNT_SHIFT 0
-#define IWCR_CNT_MASK (0x1F << IWCR_CNT_SHIFT)
-#define IWCR_HS_CNT1_SHIFT 5
-#define IWCR_HS_CNT1_MASK (0x1F << IWCR_HS_CNT1_SHIFT)
-#define IWCR_HS_CNT2_SHIFT 10
-#define IWCR_HS_CNT2_MASK (0x1F << IWCR_HS_CNT2_SHIFT)
-
struct pxa_i2c {
spinlock_t lock;
wait_queue_head_t wait;
@@ -183,7 +235,7 @@ struct pxa_i2c {
struct i2c_adapter adap;
struct clk *clk;
#ifdef CONFIG_I2C_PXA_SLAVE
- struct i2c_slave_client *slave;
+ struct i2c_client *slave;
#endif
unsigned int irqlogidx;
@@ -211,6 +263,11 @@ struct pxa_i2c {
bool highmode_enter;
u32 fm_mask;
u32 hs_mask;
+
+ struct i2c_bus_recovery_info recovery;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_default;
+ struct pinctrl_state *pinctrl_recovery;
};
#define _IBMR(i2c) ((i2c)->reg_ibmr)
@@ -238,13 +295,14 @@ struct bits {
static inline void
decode_bits(const char *prefix, const struct bits *bits, int num, u32 val)
{
- printk("%s %08x: ", prefix, val);
+ printk("%s %08x:", prefix, val);
while (num--) {
const char *str = val & bits->mask ? bits->set : bits->unset;
if (str)
- printk("%s ", str);
+ pr_cont(" %s", str);
bits++;
}
+ pr_cont("\n");
}
static const struct bits isr_bits[] = {
@@ -264,9 +322,9 @@ static const struct bits isr_bits[] = {
static void decode_ISR(unsigned int val)
{
decode_bits(KERN_DEBUG "ISR", isr_bits, ARRAY_SIZE(isr_bits), val);
- printk("\n");
}
+#ifdef CONFIG_I2C_PXA_SLAVE
static const struct bits icr_bits[] = {
PXA_BIT(ICR_START, "START", NULL),
PXA_BIT(ICR_STOP, "STOP", NULL),
@@ -285,11 +343,9 @@ static const struct bits icr_bits[] = {
PXA_BIT(ICR_UR, "UR", "ur"),
};
-#ifdef CONFIG_I2C_PXA_SLAVE
static void decode_ICR(unsigned int val)
{
decode_bits(KERN_DEBUG "ICR", icr_bits, ARRAY_SIZE(icr_bits), val);
- printk("\n");
}
#endif
@@ -315,11 +371,10 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why)
dev_err(dev, "IBMR: %08x IDBR: %08x ICR: %08x ISR: %08x\n",
readl(_IBMR(i2c)), readl(_IDBR(i2c)), readl(_ICR(i2c)),
readl(_ISR(i2c)));
- dev_dbg(dev, "log: ");
+ dev_err(dev, "log:");
for (i = 0; i < i2c->irqlogidx; i++)
- pr_debug("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]);
-
- pr_debug("\n");
+ pr_cont(" [%03x:%05x]", i2c->isrlog[i], i2c->icrlog[i]);
+ pr_cont("\n");
}
#else /* ifdef DEBUG */
@@ -334,7 +389,6 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why)
#endif /* ifdef DEBUG / else */
static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret);
-static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id);
static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c)
{
@@ -350,7 +404,7 @@ static void i2c_pxa_abort(struct pxa_i2c *i2c)
return;
}
- while ((i > 0) && (readl(_IBMR(i2c)) & 0x1) == 0) {
+ while ((i > 0) && (readl(_IBMR(i2c)) & IBMR_SDAS) == 0) {
unsigned long icr = readl(_ICR(i2c));
icr &= ~ICR_START;
@@ -371,19 +425,26 @@ static void i2c_pxa_abort(struct pxa_i2c *i2c)
static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c)
{
int timeout = DEF_TIMEOUT;
+ u32 isr;
- while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) {
- if ((readl(_ISR(i2c)) & ISR_SAD) != 0)
+ while (1) {
+ isr = readl(_ISR(i2c));
+ if (!(isr & (ISR_IBB | ISR_UB)))
+ return 0;
+
+ if (isr & ISR_SAD)
timeout += 4;
+ if (!timeout--)
+ break;
+
msleep(2);
show_state(i2c);
}
- if (timeout < 0)
- show_state(i2c);
+ show_state(i2c);
- return timeout < 0 ? I2C_RETRY : 0;
+ return I2C_RETRY;
}
static int i2c_pxa_wait_master(struct pxa_i2c *i2c)
@@ -405,7 +466,8 @@ static int i2c_pxa_wait_master(struct pxa_i2c *i2c)
* quick check of the i2c lines themselves to ensure they've
* gone high...
*/
- if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 && readl(_IBMR(i2c)) == 3) {
+ if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 &&
+ readl(_IBMR(i2c)) == (IBMR_SCLS | IBMR_SDAS)) {
if (i2c_debug > 0)
dev_dbg(&i2c->adap.dev, "%s: done\n", __func__);
return 1;
@@ -505,13 +567,8 @@ static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode)
#define i2c_pxa_set_slave(i2c, err) do { } while (0)
#endif
-static void i2c_pxa_reset(struct pxa_i2c *i2c)
+static void i2c_pxa_do_reset(struct pxa_i2c *i2c)
{
- pr_debug("Resetting I2C Controller Unit\n");
-
- /* abort any transfer currently under way */
- i2c_pxa_abort(i2c);
-
/* reset according to 9.8 */
writel(ICR_UR, _ICR(i2c));
writel(I2C_ISR_INIT, _ISR(i2c));
@@ -530,12 +587,25 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
#endif
i2c_pxa_set_slave(i2c, 0);
+}
+static void i2c_pxa_enable(struct pxa_i2c *i2c)
+{
/* enable unit */
writel(readl(_ICR(i2c)) | ICR_IUE, _ICR(i2c));
udelay(100);
}
+static void i2c_pxa_reset(struct pxa_i2c *i2c)
+{
+ pr_debug("Resetting I2C Controller Unit\n");
+
+ /* abort any transfer currently under way */
+ i2c_pxa_abort(i2c);
+ i2c_pxa_do_reset(i2c);
+ i2c_pxa_enable(i2c);
+}
+
#ifdef CONFIG_I2C_PXA_SLAVE
/*
@@ -547,22 +617,23 @@ static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr)
if (isr & ISR_BED) {
/* what should we do here? */
} else {
- int ret = 0;
+ u8 byte = 0;
if (i2c->slave != NULL)
- ret = i2c->slave->read(i2c->slave->data);
+ i2c_slave_event(i2c->slave, I2C_SLAVE_READ_PROCESSED,
+ &byte);
- writel(ret, _IDBR(i2c));
+ writel(byte, _IDBR(i2c));
writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); /* allow next byte */
}
}
static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr)
{
- unsigned int byte = readl(_IDBR(i2c));
+ u8 byte = readl(_IDBR(i2c));
if (i2c->slave != NULL)
- i2c->slave->write(i2c->slave->data, byte);
+ i2c_slave_event(i2c->slave, I2C_SLAVE_WRITE_RECEIVED, &byte);
writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c));
}
@@ -575,9 +646,18 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr)
dev_dbg(&i2c->adap.dev, "SAD, mode is slave-%cx\n",
(isr & ISR_RWM) ? 'r' : 't');
- if (i2c->slave != NULL)
- i2c->slave->event(i2c->slave->data,
- (isr & ISR_RWM) ? I2C_SLAVE_EVENT_START_READ : I2C_SLAVE_EVENT_START_WRITE);
+ if (i2c->slave != NULL) {
+ if (isr & ISR_RWM) {
+ u8 byte = 0;
+
+ i2c_slave_event(i2c->slave, I2C_SLAVE_READ_REQUESTED,
+ &byte);
+ writel(byte, _IDBR(i2c));
+ } else {
+ i2c_slave_event(i2c->slave, I2C_SLAVE_WRITE_REQUESTED,
+ NULL);
+ }
+ }
/*
* slave could interrupt in the middle of us generating a
@@ -590,7 +670,7 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr)
timeout = 0x10000;
while (1) {
- if ((readl(_IBMR(i2c)) & 2) == 2)
+ if ((readl(_IBMR(i2c)) & IBMR_SCLS) == IBMR_SCLS)
break;
timeout--;
@@ -610,7 +690,7 @@ static void i2c_pxa_slave_stop(struct pxa_i2c *i2c)
dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop)\n");
if (i2c->slave != NULL)
- i2c->slave->event(i2c->slave->data, I2C_SLAVE_EVENT_STOP);
+ i2c_slave_event(i2c->slave, I2C_SLAVE_STOP, NULL);
if (i2c_debug > 2)
dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop) acked\n");
@@ -622,6 +702,38 @@ static void i2c_pxa_slave_stop(struct pxa_i2c *i2c)
if (i2c->msg)
i2c_pxa_master_complete(i2c, I2C_RETRY);
}
+
+static int i2c_pxa_slave_reg(struct i2c_client *slave)
+{
+ struct pxa_i2c *i2c = slave->adapter->algo_data;
+
+ if (i2c->slave)
+ return -EBUSY;
+
+ if (!i2c->reg_isar)
+ return -EAFNOSUPPORT;
+
+ i2c->slave = slave;
+ i2c->slave_addr = slave->addr;
+
+ writel(i2c->slave_addr, _ISAR(i2c));
+
+ return 0;
+}
+
+static int i2c_pxa_slave_unreg(struct i2c_client *slave)
+{
+ struct pxa_i2c *i2c = slave->adapter->algo_data;
+
+ WARN_ON(!i2c->slave);
+
+ i2c->slave_addr = I2C_PXA_SLAVE_ADDR;
+ writel(i2c->slave_addr, _ISAR(i2c));
+
+ i2c->slave = NULL;
+
+ return 0;
+}
#else
static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr)
{
@@ -653,7 +765,7 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr)
timeout = 0x10000;
while (1) {
- if ((readl(_IBMR(i2c)) & 2) == 2)
+ if ((readl(_IBMR(i2c)) & IBMR_SCLS) == IBMR_SCLS)
break;
timeout--;
@@ -678,16 +790,6 @@ static void i2c_pxa_slave_stop(struct pxa_i2c *i2c)
* PXA I2C Master mode
*/
-static inline unsigned int i2c_pxa_addr_byte(struct i2c_msg *msg)
-{
- unsigned int addr = (msg->addr & 0x7f) << 1;
-
- if (msg->flags & I2C_M_RD)
- addr |= 1;
-
- return addr;
-}
-
static inline void i2c_pxa_start_message(struct pxa_i2c *i2c)
{
u32 icr;
@@ -695,8 +797,8 @@ static inline void i2c_pxa_start_message(struct pxa_i2c *i2c)
/*
* Step 1: target slave address into IDBR
*/
- writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
- i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg);
+ i2c->req_slave_addr = i2c_8bit_addr_from_msg(i2c->msg);
+ writel(i2c->req_slave_addr, _IDBR(i2c));
/*
* Step 2: initiate the write.
@@ -709,42 +811,12 @@ static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c)
{
u32 icr;
- /*
- * Clear the STOP and ACK flags
- */
+ /* Clear the START, STOP, ACK, TB and MA flags */
icr = readl(_ICR(i2c));
- icr &= ~(ICR_STOP | ICR_ACKNAK);
+ icr &= ~(ICR_START | ICR_STOP | ICR_ACKNAK | ICR_TB | ICR_MA);
writel(icr, _ICR(i2c));
}
-static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c)
-{
- /* make timeout the same as for interrupt based functions */
- long timeout = 2 * DEF_TIMEOUT;
-
- /*
- * Wait for the bus to become free.
- */
- while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) {
- udelay(1000);
- show_state(i2c);
- }
-
- if (timeout < 0) {
- show_state(i2c);
- dev_err(&i2c->adap.dev,
- "i2c_pxa: timeout waiting for bus free\n");
- return I2C_RETRY;
- }
-
- /*
- * Set master mode.
- */
- writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c));
-
- return 0;
-}
-
/*
* PXA I2C send master code
* 1. Load master code to IDBR and send it.
@@ -754,7 +826,7 @@ static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c)
static int i2c_pxa_send_mastercode(struct pxa_i2c *i2c)
{
u32 icr;
- long timeout;
+ long time_left;
spin_lock_irq(&i2c->lock);
i2c->highmode_enter = true;
@@ -765,146 +837,12 @@ static int i2c_pxa_send_mastercode(struct pxa_i2c *i2c)
writel(icr, _ICR(i2c));
spin_unlock_irq(&i2c->lock);
- timeout = wait_event_timeout(i2c->wait,
- i2c->highmode_enter == false, HZ * 1);
+ time_left = wait_event_timeout(i2c->wait,
+ i2c->highmode_enter == false, HZ * 1);
i2c->highmode_enter = false;
- return (timeout == 0) ? I2C_RETRY : 0;
-}
-
-static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c,
- struct i2c_msg *msg, int num)
-{
- unsigned long timeout = 500000; /* 5 seconds */
- int ret = 0;
-
- ret = i2c_pxa_pio_set_master(i2c);
- if (ret)
- goto out;
-
- i2c->msg = msg;
- i2c->msg_num = num;
- i2c->msg_idx = 0;
- i2c->msg_ptr = 0;
- i2c->irqlogidx = 0;
-
- i2c_pxa_start_message(i2c);
-
- while (i2c->msg_num > 0 && --timeout) {
- i2c_pxa_handler(0, i2c);
- udelay(10);
- }
-
- i2c_pxa_stop_message(i2c);
-
- /*
- * We place the return code in i2c->msg_idx.
- */
- ret = i2c->msg_idx;
-
-out:
- if (timeout == 0) {
- i2c_pxa_scream_blue_murder(i2c, "timeout");
- ret = I2C_RETRY;
- }
-
- return ret;
-}
-
-/*
- * We are protected by the adapter bus mutex.
- */
-static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num)
-{
- long timeout;
- int ret;
-
- /*
- * Wait for the bus to become free.
- */
- ret = i2c_pxa_wait_bus_not_busy(i2c);
- if (ret) {
- dev_err(&i2c->adap.dev, "i2c_pxa: timeout waiting for bus free\n");
- goto out;
- }
-
- /*
- * Set master mode.
- */
- ret = i2c_pxa_set_master(i2c);
- if (ret) {
- dev_err(&i2c->adap.dev, "i2c_pxa_set_master: error %d\n", ret);
- goto out;
- }
-
- if (i2c->high_mode) {
- ret = i2c_pxa_send_mastercode(i2c);
- if (ret) {
- dev_err(&i2c->adap.dev, "i2c_pxa_send_mastercode timeout\n");
- goto out;
- }
- }
-
- spin_lock_irq(&i2c->lock);
-
- i2c->msg = msg;
- i2c->msg_num = num;
- i2c->msg_idx = 0;
- i2c->msg_ptr = 0;
- i2c->irqlogidx = 0;
-
- i2c_pxa_start_message(i2c);
-
- spin_unlock_irq(&i2c->lock);
-
- /*
- * The rest of the processing occurs in the interrupt handler.
- */
- timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
- i2c_pxa_stop_message(i2c);
-
- /*
- * We place the return code in i2c->msg_idx.
- */
- ret = i2c->msg_idx;
-
- if (!timeout && i2c->msg_num) {
- i2c_pxa_scream_blue_murder(i2c, "timeout");
- ret = I2C_RETRY;
- }
-
- out:
- return ret;
-}
-
-static int i2c_pxa_pio_xfer(struct i2c_adapter *adap,
- struct i2c_msg msgs[], int num)
-{
- struct pxa_i2c *i2c = adap->algo_data;
- int ret, i;
-
- /* If the I2C controller is disabled we need to reset it
- (probably due to a suspend/resume destroying state). We do
- this here as we can then avoid worrying about resuming the
- controller before its users. */
- if (!(readl(_ICR(i2c)) & ICR_IUE))
- i2c_pxa_reset(i2c);
-
- for (i = adap->retries; i >= 0; i--) {
- ret = i2c_pxa_do_pio_xfer(i2c, msgs, num);
- if (ret != I2C_RETRY)
- goto out;
-
- if (i2c_debug)
- dev_dbg(&adap->dev, "Retrying transmission\n");
- udelay(100);
- }
- i2c_pxa_scream_blue_murder(i2c, "exhausted retries");
- ret = -EREMOTEIO;
- out:
- i2c_pxa_set_slave(i2c, ret);
- return ret;
+ return (time_left == 0) ? I2C_RETRY : 0;
}
/*
@@ -958,7 +896,7 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
*/
if (isr & ISR_ACKNAK) {
if (i2c->msg_ptr == 0 && i2c->msg_idx == 0)
- ret = I2C_RETRY;
+ ret = NO_SLAVE;
else
ret = XFER_NAKED;
}
@@ -1009,8 +947,8 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
/*
* Write the next address.
*/
- writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
- i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg);
+ i2c->req_slave_addr = i2c_8bit_addr_from_msg(i2c->msg);
+ writel(i2c->req_slave_addr, _IDBR(i2c));
/*
* And trigger a repeated start, and send the byte.
@@ -1018,14 +956,8 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
icr &= ~ICR_ALDIE;
icr |= ICR_START | ICR_TB;
} else {
- if (i2c->msg->len == 0) {
- /*
- * Device probes have a message length of zero
- * and need the bus to be reset before it can
- * be used again.
- */
- i2c_pxa_reset(i2c);
- }
+ if (i2c->msg->len == 0)
+ icr |= ICR_MA;
i2c_pxa_master_complete(i2c, 0);
}
@@ -1113,28 +1045,108 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * We are protected by the adapter bus mutex.
+ */
+static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num)
+{
+ long time_left;
+ int ret;
+
+ /*
+ * Wait for the bus to become free.
+ */
+ ret = i2c_pxa_wait_bus_not_busy(i2c);
+ if (ret) {
+ dev_err(&i2c->adap.dev, "i2c_pxa: timeout waiting for bus free\n");
+ i2c_recover_bus(&i2c->adap);
+ goto out;
+ }
+
+ /*
+ * Set master mode.
+ */
+ ret = i2c_pxa_set_master(i2c);
+ if (ret) {
+ dev_err(&i2c->adap.dev, "i2c_pxa_set_master: error %d\n", ret);
+ goto out;
+ }
+
+ if (i2c->high_mode) {
+ ret = i2c_pxa_send_mastercode(i2c);
+ if (ret) {
+ dev_err(&i2c->adap.dev, "i2c_pxa_send_mastercode timeout\n");
+ goto out;
+ }
+ }
+
+ spin_lock_irq(&i2c->lock);
+
+ i2c->msg = msg;
+ i2c->msg_num = num;
+ i2c->msg_idx = 0;
+ i2c->msg_ptr = 0;
+ i2c->irqlogidx = 0;
+
+ i2c_pxa_start_message(i2c);
+
+ spin_unlock_irq(&i2c->lock);
+
+ /*
+ * The rest of the processing occurs in the interrupt handler.
+ */
+ time_left = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
+ i2c_pxa_stop_message(i2c);
+
+ /*
+ * We place the return code in i2c->msg_idx.
+ */
+ ret = i2c->msg_idx;
+
+ if (!time_left && i2c->msg_num) {
+ i2c_pxa_scream_blue_murder(i2c, "timeout with active message");
+ i2c_recover_bus(&i2c->adap);
+ ret = I2C_RETRY;
+ }
-static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+ out:
+ return ret;
+}
+
+static int i2c_pxa_internal_xfer(struct pxa_i2c *i2c,
+ struct i2c_msg *msgs, int num,
+ int (*xfer)(struct pxa_i2c *,
+ struct i2c_msg *, int num))
{
- struct pxa_i2c *i2c = adap->algo_data;
int ret, i;
- for (i = adap->retries; i >= 0; i--) {
- ret = i2c_pxa_do_xfer(i2c, msgs, num);
- if (ret != I2C_RETRY)
+ for (i = 0; ; ) {
+ ret = xfer(i2c, msgs, num);
+ if (ret != I2C_RETRY && ret != NO_SLAVE)
goto out;
+ if (++i >= i2c->adap.retries)
+ break;
if (i2c_debug)
- dev_dbg(&adap->dev, "Retrying transmission\n");
+ dev_dbg(&i2c->adap.dev, "Retrying transmission\n");
udelay(100);
}
- i2c_pxa_scream_blue_murder(i2c, "exhausted retries");
+ if (ret != NO_SLAVE)
+ i2c_pxa_scream_blue_murder(i2c, "exhausted retries");
ret = -EREMOTEIO;
out:
i2c_pxa_set_slave(i2c, ret);
return ret;
}
+static int i2c_pxa_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct pxa_i2c *i2c = adap->algo_data;
+
+ return i2c_pxa_internal_xfer(i2c, msgs, num, i2c_pxa_do_xfer);
+}
+
static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
@@ -1142,43 +1154,119 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm i2c_pxa_algorithm = {
- .master_xfer = i2c_pxa_xfer,
- .functionality = i2c_pxa_functionality,
+ .xfer = i2c_pxa_xfer,
+ .functionality = i2c_pxa_functionality,
+#ifdef CONFIG_I2C_PXA_SLAVE
+ .reg_slave = i2c_pxa_slave_reg,
+ .unreg_slave = i2c_pxa_slave_unreg,
+#endif
};
-static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
- .master_xfer = i2c_pxa_pio_xfer,
- .functionality = i2c_pxa_functionality,
-};
+/* Non-interrupt mode support */
+static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c)
+{
+ /* make timeout the same as for interrupt based functions */
+ long timeout = 2 * DEF_TIMEOUT;
-static const struct of_device_id i2c_pxa_dt_ids[] = {
- { .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
- { .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
- { .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
- { .compatible = "marvell,armada-3700-i2c", .data = (void *)REGS_A3700 },
- {}
+ /*
+ * Wait for the bus to become free.
+ */
+ while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB))
+ udelay(1000);
+
+ if (timeout < 0) {
+ show_state(i2c);
+ dev_err(&i2c->adap.dev,
+ "i2c_pxa: timeout waiting for bus free (set_master)\n");
+ return I2C_RETRY;
+ }
+
+ /*
+ * Set master mode.
+ */
+ writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c));
+
+ return 0;
+}
+
+static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c,
+ struct i2c_msg *msg, int num)
+{
+ unsigned long timeout = 500000; /* 5 seconds */
+ int ret = 0;
+
+ ret = i2c_pxa_pio_set_master(i2c);
+ if (ret)
+ goto out;
+
+ i2c->msg = msg;
+ i2c->msg_num = num;
+ i2c->msg_idx = 0;
+ i2c->msg_ptr = 0;
+ i2c->irqlogidx = 0;
+
+ i2c_pxa_start_message(i2c);
+
+ while (i2c->msg_num > 0 && --timeout) {
+ i2c_pxa_handler(0, i2c);
+ udelay(10);
+ }
+
+ i2c_pxa_stop_message(i2c);
+
+ /*
+ * We place the return code in i2c->msg_idx.
+ */
+ ret = i2c->msg_idx;
+
+out:
+ if (timeout == 0) {
+ i2c_pxa_scream_blue_murder(i2c, "timeout (do_pio_xfer)");
+ ret = I2C_RETRY;
+ }
+
+ return ret;
+}
+
+static int i2c_pxa_pio_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct pxa_i2c *i2c = adap->algo_data;
+
+ /* If the I2C controller is disabled we need to reset it
+ (probably due to a suspend/resume destroying state). We do
+ this here as we can then avoid worrying about resuming the
+ controller before its users. */
+ if (!(readl(_ICR(i2c)) & ICR_IUE))
+ i2c_pxa_reset(i2c);
+
+ return i2c_pxa_internal_xfer(i2c, msgs, num, i2c_pxa_do_pio_xfer);
+}
+
+static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
+ .xfer = i2c_pxa_pio_xfer,
+ .functionality = i2c_pxa_functionality,
+#ifdef CONFIG_I2C_PXA_SLAVE
+ .reg_slave = i2c_pxa_slave_reg,
+ .unreg_slave = i2c_pxa_slave_unreg,
+#endif
};
-MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
enum pxa_i2c_types *i2c_types)
{
struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *of_id =
- of_match_device(i2c_pxa_dt_ids, &pdev->dev);
- if (!of_id)
+ if (!pdev->dev.of_node)
return 1;
/* For device tree we always use the dynamic or alias-assigned ID */
i2c->adap.nr = -1;
- if (of_get_property(np, "mrvl,i2c-polling", NULL))
- i2c->use_pio = 1;
- if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
- i2c->fast_mode = 1;
+ i2c->use_pio = of_property_read_bool(np, "mrvl,i2c-polling");
+ i2c->fast_mode = of_property_read_bool(np, "mrvl,i2c-fast-mode");
- *i2c_types = (enum pxa_i2c_types)(of_id->data);
+ *i2c_types = (enum pxa_i2c_types)device_get_match_data(&pdev->dev);
return 0;
}
@@ -1203,31 +1291,164 @@ static int i2c_pxa_probe_pdata(struct platform_device *pdev,
return 0;
}
+static void i2c_pxa_prepare_recovery(struct i2c_adapter *adap)
+{
+ struct pxa_i2c *i2c = adap->algo_data;
+ u32 ibmr = readl(_IBMR(i2c));
+
+ /*
+ * Program the GPIOs to reflect the current I2C bus state while
+ * we transition to recovery; this avoids glitching the bus.
+ */
+ gpiod_set_value(i2c->recovery.scl_gpiod, ibmr & IBMR_SCLS);
+ gpiod_set_value(i2c->recovery.sda_gpiod, ibmr & IBMR_SDAS);
+
+ WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery));
+}
+
+static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap)
+{
+ struct pxa_i2c *i2c = adap->algo_data;
+ u32 isr;
+
+ /*
+ * The bus should now be free. Clear up the I2C controller before
+ * handing control of the bus back to avoid the bus changing state.
+ */
+ isr = readl(_ISR(i2c));
+ if (isr & (ISR_UB | ISR_IBB)) {
+ dev_dbg(&i2c->adap.dev,
+ "recovery: resetting controller, ISR=0x%08x\n", isr);
+ i2c_pxa_do_reset(i2c);
+ }
+
+ WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default));
+
+ dev_dbg(&i2c->adap.dev, "recovery: IBMR 0x%08x ISR 0x%08x\n",
+ readl(_IBMR(i2c)), readl(_ISR(i2c)));
+
+ i2c_pxa_enable(i2c);
+}
+
+static int i2c_pxa_init_recovery(struct pxa_i2c *i2c)
+{
+ struct i2c_bus_recovery_info *bri = &i2c->recovery;
+ struct device *dev = i2c->adap.dev.parent;
+
+ /*
+ * When slave mode is enabled, we are not the only master on the bus.
+ * Bus recovery can only be performed when we are the master, which
+ * we can't be certain of. Therefore, when slave mode is enabled, do
+ * not configure bus recovery.
+ */
+ if (IS_ENABLED(CONFIG_I2C_PXA_SLAVE))
+ return 0;
+
+ i2c->pinctrl = devm_pinctrl_get(dev);
+ if (PTR_ERR(i2c->pinctrl) == -ENODEV)
+ i2c->pinctrl = NULL;
+ if (IS_ERR(i2c->pinctrl))
+ return PTR_ERR(i2c->pinctrl);
+
+ if (!i2c->pinctrl)
+ return 0;
+
+ i2c->pinctrl_default = pinctrl_lookup_state(i2c->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ i2c->pinctrl_recovery = pinctrl_lookup_state(i2c->pinctrl, "recovery");
+
+ if (IS_ERR(i2c->pinctrl_default) || IS_ERR(i2c->pinctrl_recovery)) {
+ dev_info(dev, "missing pinmux recovery information: %ld %ld\n",
+ PTR_ERR(i2c->pinctrl_default),
+ PTR_ERR(i2c->pinctrl_recovery));
+ return 0;
+ }
+
+ /*
+ * Claiming GPIOs can influence the pinmux state, and may glitch the
+ * I2C bus. Do this carefully.
+ */
+ bri->scl_gpiod = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
+ if (bri->scl_gpiod == ERR_PTR(-EPROBE_DEFER))
+ return -EPROBE_DEFER;
+ if (IS_ERR(bri->scl_gpiod)) {
+ dev_info(dev, "missing scl gpio recovery information: %pe\n",
+ bri->scl_gpiod);
+ return 0;
+ }
+
+ /*
+ * We have SCL. Pull SCL low and wait a bit so that SDA glitches
+ * have no effect.
+ */
+ gpiod_direction_output(bri->scl_gpiod, 0);
+ udelay(10);
+ bri->sda_gpiod = devm_gpiod_get(dev, "sda", GPIOD_OUT_HIGH_OPEN_DRAIN);
+
+ /* Wait a bit in case of a SDA glitch, and then release SCL. */
+ udelay(10);
+ gpiod_direction_output(bri->scl_gpiod, 1);
+
+ if (bri->sda_gpiod == ERR_PTR(-EPROBE_DEFER))
+ return -EPROBE_DEFER;
+
+ if (IS_ERR(bri->sda_gpiod)) {
+ dev_info(dev, "missing sda gpio recovery information: %pe\n",
+ bri->sda_gpiod);
+ return 0;
+ }
+
+ bri->prepare_recovery = i2c_pxa_prepare_recovery;
+ bri->unprepare_recovery = i2c_pxa_unprepare_recovery;
+ bri->recover_bus = i2c_generic_scl_recovery;
+
+ i2c->adap.bus_recovery_info = bri;
+
+ /*
+ * Claiming GPIOs can change the pinmux state, which confuses the
+ * pinctrl since pinctrl's idea of the current setting is unaffected
+ * by the pinmux change caused by claiming the GPIO. Work around that
+ * by switching pinctrl to the GPIO state here. We do it this way to
+ * avoid glitching the I2C bus.
+ */
+ pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery);
+
+ return pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default);
+}
+
static int i2c_pxa_probe(struct platform_device *dev)
{
struct i2c_pxa_platform_data *plat = dev_get_platdata(&dev->dev);
enum pxa_i2c_types i2c_type;
struct pxa_i2c *i2c;
- struct resource *res = NULL;
+ struct resource *res;
int ret, irq;
i2c = devm_kzalloc(&dev->dev, sizeof(struct pxa_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- i2c->reg_base = devm_ioremap_resource(&dev->dev, res);
+ /* Default adapter num to device id; i2c_pxa_probe_dt can override. */
+ i2c->adap.nr = dev->id;
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.retries = 5;
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &dev->dev;
+#ifdef CONFIG_OF
+ i2c->adap.dev.of_node = dev->dev.of_node;
+#endif
+
+ i2c->reg_base = devm_platform_get_and_ioremap_resource(dev, 0, &res);
if (IS_ERR(i2c->reg_base))
return PTR_ERR(i2c->reg_base);
irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no irq resource: %d\n", irq);
+ if (irq < 0)
return irq;
- }
- /* Default adapter num to device id; i2c_pxa_probe_dt can override. */
- i2c->adap.nr = dev->id;
+ ret = i2c_pxa_init_recovery(i2c);
+ if (ret)
+ return ret;
ret = i2c_pxa_probe_dt(dev, i2c, &i2c_type);
if (ret > 0)
@@ -1235,26 +1456,22 @@ static int i2c_pxa_probe(struct platform_device *dev)
if (ret < 0)
return ret;
- i2c->adap.owner = THIS_MODULE;
- i2c->adap.retries = 5;
-
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
- strlcpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name));
i2c->clk = devm_clk_get(&dev->dev, NULL);
- if (IS_ERR(i2c->clk)) {
- dev_err(&dev->dev, "failed to get the clk: %ld\n", PTR_ERR(i2c->clk));
- return PTR_ERR(i2c->clk);
- }
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&dev->dev, PTR_ERR(i2c->clk),
+ "failed to get the clk\n");
i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr;
i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr;
i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr;
i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr;
- i2c->fm_mask = pxa_reg_layout[i2c_type].fm ? : ICR_FM;
- i2c->hs_mask = pxa_reg_layout[i2c_type].hs ? : ICR_HS;
+ i2c->fm_mask = pxa_reg_layout[i2c_type].fm;
+ i2c->hs_mask = pxa_reg_layout[i2c_type].hs;
if (i2c_type != REGS_CE4100)
i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar;
@@ -1273,10 +1490,6 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->highmode_enter = false;
if (plat) {
-#ifdef CONFIG_I2C_PXA_SLAVE
- i2c->slave_addr = plat->slave_addr;
- i2c->slave = plat->slave;
-#endif
i2c->adap.class = plat->class;
}
@@ -1290,7 +1503,10 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->adap.name);
}
- clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return dev_err_probe(&dev->dev, ret,
+ "failed to enable clock\n");
if (i2c->use_pio) {
i2c->adap.algo = &i2c_pxa_pio_algorithm;
@@ -1307,12 +1523,6 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c_pxa_reset(i2c);
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &dev->dev;
-#ifdef CONFIG_OF
- i2c->adap.dev.of_node = dev->dev.of_node;
-#endif
-
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0)
goto ereqirq;
@@ -1332,18 +1542,15 @@ ereqirq:
return ret;
}
-static int i2c_pxa_remove(struct platform_device *dev)
+static void i2c_pxa_remove(struct platform_device *dev)
{
struct pxa_i2c *i2c = platform_get_drvdata(dev);
i2c_del_adapter(&i2c->adap);
clk_disable_unprepare(i2c->clk);
-
- return 0;
}
-#ifdef CONFIG_PM
static int i2c_pxa_suspend_noirq(struct device *dev)
{
struct pxa_i2c *i2c = dev_get_drvdata(dev);
@@ -1368,17 +1575,12 @@ static const struct dev_pm_ops i2c_pxa_dev_pm_ops = {
.resume_noirq = i2c_pxa_resume_noirq,
};
-#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops)
-#else
-#define I2C_PXA_DEV_PM_OPS NULL
-#endif
-
static struct platform_driver i2c_pxa_driver = {
.probe = i2c_pxa_probe,
.remove = i2c_pxa_remove,
.driver = {
.name = "pxa2xx-i2c",
- .pm = I2C_PXA_DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&i2c_pxa_dev_pm_ops),
.of_match_table = i2c_pxa_dt_ids,
},
.id_table = i2c_pxa_id_table,
@@ -1394,8 +1596,8 @@ static void __exit i2c_adap_pxa_exit(void)
platform_driver_unregister(&i2c_pxa_driver);
}
+MODULE_DESCRIPTION("Intel PXA2XX I2C adapter");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pxa2xx-i2c");
subsys_initcall(i2c_adap_pxa_init);
module_exit(i2c_adap_pxa_exit);
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
new file mode 100644
index 000000000000..884055df1560
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -0,0 +1,865 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+// Copyright (c) 2017-2022 Linaro Limited.
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#define CCI_HW_VERSION 0x0
+#define CCI_RESET_CMD 0x004
+#define CCI_RESET_CMD_MASK 0x0f73f3f7
+#define CCI_RESET_CMD_M0_MASK 0x000003f1
+#define CCI_RESET_CMD_M1_MASK 0x0003f001
+#define CCI_QUEUE_START 0x008
+#define CCI_HALT_REQ 0x034
+#define CCI_HALT_REQ_I2C_M0_Q0Q1 BIT(0)
+#define CCI_HALT_REQ_I2C_M1_Q0Q1 BIT(1)
+
+#define CCI_I2C_Mm_SCL_CTL(m) (0x100 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_0(m) (0x104 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_1(m) (0x108 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_2(m) (0x10c + 0x100 * (m))
+#define CCI_I2C_Mm_MISC_CTL(m) (0x110 + 0x100 * (m))
+
+#define CCI_I2C_Mm_READ_DATA(m) (0x118 + 0x100 * (m))
+#define CCI_I2C_Mm_READ_BUF_LEVEL(m) (0x11c + 0x100 * (m))
+#define CCI_I2C_Mm_Qn_EXEC_WORD_CNT(m, n) (0x300 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_CUR_WORD_CNT(m, n) (0x304 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_CUR_CMD(m, n) (0x308 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_REPORT_STATUS(m, n) (0x30c + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_LOAD_DATA(m, n) (0x310 + 0x200 * (m) + 0x100 * (n))
+
+#define CCI_IRQ_GLOBAL_CLEAR_CMD 0xc00
+#define CCI_IRQ_MASK_0 0xc04
+#define CCI_IRQ_MASK_0_I2C_M0_RD_DONE BIT(0)
+#define CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT BIT(4)
+#define CCI_IRQ_MASK_0_I2C_M0_Q1_REPORT BIT(8)
+#define CCI_IRQ_MASK_0_I2C_M1_RD_DONE BIT(12)
+#define CCI_IRQ_MASK_0_I2C_M1_Q0_REPORT BIT(16)
+#define CCI_IRQ_MASK_0_I2C_M1_Q1_REPORT BIT(20)
+#define CCI_IRQ_MASK_0_RST_DONE_ACK BIT(24)
+#define CCI_IRQ_MASK_0_I2C_M0_Q0Q1_HALT_ACK BIT(25)
+#define CCI_IRQ_MASK_0_I2C_M1_Q0Q1_HALT_ACK BIT(26)
+#define CCI_IRQ_MASK_0_I2C_M0_ERROR 0x18000ee6
+#define CCI_IRQ_MASK_0_I2C_M1_ERROR 0x60ee6000
+#define CCI_IRQ_CLEAR_0 0xc08
+#define CCI_IRQ_STATUS_0 0xc0c
+#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE BIT(0)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT BIT(4)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT BIT(8)
+#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE BIT(12)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT BIT(16)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT BIT(20)
+#define CCI_IRQ_STATUS_0_RST_DONE_ACK BIT(24)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK BIT(25)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK BIT(26)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR BIT(27)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR BIT(28)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR BIT(29)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR BIT(30)
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR 0x18000ee6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR 0x60ee6000
+
+#define CCI_TIMEOUT (msecs_to_jiffies(100))
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+/* Max number of resources + 1 for a NULL terminator */
+#define CCI_RES_MAX 6
+
+#define CCI_I2C_SET_PARAM 1
+#define CCI_I2C_REPORT 8
+#define CCI_I2C_WRITE 9
+#define CCI_I2C_READ 10
+
+#define CCI_I2C_REPORT_IRQ_EN BIT(8)
+
+enum {
+ I2C_MODE_STANDARD,
+ I2C_MODE_FAST,
+ I2C_MODE_FAST_PLUS,
+};
+
+enum cci_i2c_queue_t {
+ QUEUE_0,
+ QUEUE_1
+};
+
+struct hw_params {
+ u16 thigh; /* HIGH period of the SCL clock in clock ticks */
+ u16 tlow; /* LOW period of the SCL clock */
+ u16 tsu_sto; /* set-up time for STOP condition */
+ u16 tsu_sta; /* set-up time for a repeated START condition */
+ u16 thd_dat; /* data hold time */
+ u16 thd_sta; /* hold time (repeated) START condition */
+ u16 tbuf; /* bus free time between a STOP and START condition */
+ u8 scl_stretch_en;
+ u16 trdhld;
+ u16 tsp; /* pulse width of spikes suppressed by the input filter */
+};
+
+struct cci;
+
+struct cci_master {
+ struct i2c_adapter adap;
+ u16 master;
+ u8 mode;
+ int status;
+ struct completion irq_complete;
+ struct cci *cci;
+};
+
+struct cci_data {
+ unsigned int num_masters;
+ struct i2c_adapter_quirks quirks;
+ u16 queue_size[NUM_QUEUES];
+ struct hw_params params[3];
+};
+
+struct cci {
+ struct device *dev;
+ void __iomem *base;
+ unsigned int irq;
+ const struct cci_data *data;
+ struct clk_bulk_data *clocks;
+ int nclocks;
+ struct cci_master master[NUM_MASTERS];
+};
+
+static irqreturn_t cci_isr(int irq, void *dev)
+{
+ struct cci *cci = dev;
+ u32 val, reset = 0;
+ int ret = IRQ_NONE;
+
+ val = readl(cci->base + CCI_IRQ_STATUS_0);
+ writel(val, cci->base + CCI_IRQ_CLEAR_0);
+ writel(0x1, cci->base + CCI_IRQ_GLOBAL_CLEAR_CMD);
+
+ if (val & CCI_IRQ_STATUS_0_RST_DONE_ACK) {
+ complete(&cci->master[0].irq_complete);
+ if (cci->master[1].master)
+ complete(&cci->master[1].irq_complete);
+ ret = IRQ_HANDLED;
+ }
+
+ if (val & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE ||
+ val & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT ||
+ val & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT) {
+ cci->master[0].status = 0;
+ complete(&cci->master[0].irq_complete);
+ ret = IRQ_HANDLED;
+ }
+
+ if (val & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE ||
+ val & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT ||
+ val & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT) {
+ cci->master[1].status = 0;
+ complete(&cci->master[1].irq_complete);
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK)) {
+ reset = CCI_RESET_CMD_M0_MASK;
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK)) {
+ reset = CCI_RESET_CMD_M1_MASK;
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(reset))
+ writel(reset, cci->base + CCI_RESET_CMD);
+
+ if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_ERROR)) {
+ if (val & CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR ||
+ val & CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR)
+ cci->master[0].status = -ENXIO;
+ else
+ cci->master[0].status = -EIO;
+
+ writel(CCI_HALT_REQ_I2C_M0_Q0Q1, cci->base + CCI_HALT_REQ);
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_ERROR)) {
+ if (val & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR ||
+ val & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR)
+ cci->master[1].status = -ENXIO;
+ else
+ cci->master[1].status = -EIO;
+
+ writel(CCI_HALT_REQ_I2C_M1_Q0Q1, cci->base + CCI_HALT_REQ);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int cci_halt(struct cci *cci, u8 master_num)
+{
+ struct cci_master *master;
+ u32 val;
+
+ if (master_num >= cci->data->num_masters) {
+ dev_err(cci->dev, "Unsupported master idx (%u)\n", master_num);
+ return -EINVAL;
+ }
+
+ val = BIT(master_num);
+ master = &cci->master[master_num];
+
+ reinit_completion(&master->irq_complete);
+ writel(val, cci->base + CCI_HALT_REQ);
+
+ if (!wait_for_completion_timeout(&master->irq_complete, CCI_TIMEOUT)) {
+ dev_err(cci->dev, "CCI halt timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int cci_reset(struct cci *cci)
+{
+ /*
+ * we reset the whole controller, here and for implicity use
+ * master[0].xxx for waiting on it.
+ */
+ reinit_completion(&cci->master[0].irq_complete);
+ writel(CCI_RESET_CMD_MASK, cci->base + CCI_RESET_CMD);
+
+ if (!wait_for_completion_timeout(&cci->master[0].irq_complete,
+ CCI_TIMEOUT)) {
+ dev_err(cci->dev, "CCI reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int cci_init(struct cci *cci)
+{
+ u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE |
+ CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT |
+ CCI_IRQ_MASK_0_I2C_M0_Q1_REPORT |
+ CCI_IRQ_MASK_0_I2C_M1_RD_DONE |
+ CCI_IRQ_MASK_0_I2C_M1_Q0_REPORT |
+ CCI_IRQ_MASK_0_I2C_M1_Q1_REPORT |
+ CCI_IRQ_MASK_0_RST_DONE_ACK |
+ CCI_IRQ_MASK_0_I2C_M0_Q0Q1_HALT_ACK |
+ CCI_IRQ_MASK_0_I2C_M1_Q0Q1_HALT_ACK |
+ CCI_IRQ_MASK_0_I2C_M0_ERROR |
+ CCI_IRQ_MASK_0_I2C_M1_ERROR;
+ int i;
+
+ writel(val, cci->base + CCI_IRQ_MASK_0);
+
+ for (i = 0; i < cci->data->num_masters; i++) {
+ int mode = cci->master[i].mode;
+ const struct hw_params *hw;
+
+ if (!cci->master[i].cci)
+ continue;
+
+ hw = &cci->data->params[mode];
+
+ val = hw->thigh << 16 | hw->tlow;
+ writel(val, cci->base + CCI_I2C_Mm_SCL_CTL(i));
+
+ val = hw->tsu_sto << 16 | hw->tsu_sta;
+ writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_0(i));
+
+ val = hw->thd_dat << 16 | hw->thd_sta;
+ writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_1(i));
+
+ val = hw->tbuf;
+ writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_2(i));
+
+ val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp;
+ writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i));
+ }
+
+ return 0;
+}
+
+static int cci_run_queue(struct cci *cci, u8 master, u8 queue)
+{
+ u32 val;
+
+ val = readl(cci->base + CCI_I2C_Mm_Qn_CUR_WORD_CNT(master, queue));
+ writel(val, cci->base + CCI_I2C_Mm_Qn_EXEC_WORD_CNT(master, queue));
+
+ reinit_completion(&cci->master[master].irq_complete);
+ val = BIT(master * 2 + queue);
+ writel(val, cci->base + CCI_QUEUE_START);
+
+ if (!wait_for_completion_timeout(&cci->master[master].irq_complete,
+ CCI_TIMEOUT)) {
+ dev_err(cci->dev, "master %d queue %d timeout\n",
+ master, queue);
+ cci_reset(cci);
+ cci_init(cci);
+ return -ETIMEDOUT;
+ }
+
+ return cci->master[master].status;
+}
+
+static int cci_validate_queue(struct cci *cci, u8 master, u8 queue)
+{
+ u32 val;
+
+ val = readl(cci->base + CCI_I2C_Mm_Qn_CUR_WORD_CNT(master, queue));
+ if (val == cci->data->queue_size[queue])
+ return -EINVAL;
+
+ if (!val)
+ return 0;
+
+ val = CCI_I2C_REPORT | CCI_I2C_REPORT_IRQ_EN;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+ return cci_run_queue(cci, master, queue);
+}
+
+static int cci_i2c_read(struct cci *cci, u16 master,
+ u16 addr, u8 *buf, u16 len)
+{
+ u32 val, words_read, words_exp;
+ u8 queue = QUEUE_1;
+ int i, index = 0, ret;
+ bool first = true;
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * This is to avoid overflow / underflow of queue.
+ */
+ ret = cci_validate_queue(cci, master, queue);
+ if (ret < 0)
+ return ret;
+
+ val = CCI_I2C_SET_PARAM | (addr & 0x7f) << 4;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+ val = CCI_I2C_READ | len << 4;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+ ret = cci_run_queue(cci, master, queue);
+ if (ret < 0)
+ return ret;
+
+ words_read = readl(cci->base + CCI_I2C_Mm_READ_BUF_LEVEL(master));
+ words_exp = len / 4 + 1;
+ if (words_read != words_exp) {
+ dev_err(cci->dev, "words read = %d, words expected = %d\n",
+ words_read, words_exp);
+ return -EIO;
+ }
+
+ do {
+ val = readl(cci->base + CCI_I2C_Mm_READ_DATA(master));
+
+ for (i = 0; i < 4 && index < len; i++) {
+ if (first) {
+ /* The LS byte of this register represents the
+ * first byte read from the slave during a read
+ * access.
+ */
+ first = false;
+ continue;
+ }
+ buf[index++] = (val >> (i * 8)) & 0xff;
+ }
+ } while (--words_read);
+
+ return 0;
+}
+
+static int cci_i2c_write(struct cci *cci, u16 master,
+ u16 addr, u8 *buf, u16 len)
+{
+ u8 queue = QUEUE_0;
+ u8 load[12] = { 0 };
+ int i = 0, j, ret;
+ u32 val;
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * This is to avoid overflow / underflow of queue.
+ */
+ ret = cci_validate_queue(cci, master, queue);
+ if (ret < 0)
+ return ret;
+
+ val = CCI_I2C_SET_PARAM | (addr & 0x7f) << 4;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+ load[i++] = CCI_I2C_WRITE | len << 4;
+
+ for (j = 0; j < len; j++)
+ load[i++] = buf[j];
+
+ for (j = 0; j < i; j += 4) {
+ val = load[j];
+ val |= load[j + 1] << 8;
+ val |= load[j + 2] << 16;
+ val |= load[j + 3] << 24;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+ }
+
+ val = CCI_I2C_REPORT | CCI_I2C_REPORT_IRQ_EN;
+ writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+ return cci_run_queue(cci, master, queue);
+}
+
+static int cci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct cci_master *cci_master = i2c_get_adapdata(adap);
+ struct cci *cci = cci_master->cci;
+ int i, ret;
+
+ ret = pm_runtime_get_sync(cci->dev);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD)
+ ret = cci_i2c_read(cci, cci_master->master,
+ msgs[i].addr, msgs[i].buf,
+ msgs[i].len);
+ else
+ ret = cci_i2c_write(cci, cci_master->master,
+ msgs[i].addr, msgs[i].buf,
+ msgs[i].len);
+
+ if (ret < 0)
+ break;
+ }
+
+ if (!ret)
+ ret = num;
+
+err:
+ pm_runtime_put_autosuspend(cci->dev);
+
+ return ret;
+}
+
+static u32 cci_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm cci_algo = {
+ .xfer = cci_xfer,
+ .functionality = cci_func,
+};
+
+static int cci_enable_clocks(struct cci *cci)
+{
+ return clk_bulk_prepare_enable(cci->nclocks, cci->clocks);
+}
+
+static void cci_disable_clocks(struct cci *cci)
+{
+ clk_bulk_disable_unprepare(cci->nclocks, cci->clocks);
+}
+
+static int __maybe_unused cci_suspend_runtime(struct device *dev)
+{
+ struct cci *cci = dev_get_drvdata(dev);
+
+ cci_disable_clocks(cci);
+ return 0;
+}
+
+static int __maybe_unused cci_resume_runtime(struct device *dev)
+{
+ struct cci *cci = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cci_enable_clocks(cci);
+ if (ret)
+ return ret;
+
+ cci_init(cci);
+ return 0;
+}
+
+static int __maybe_unused cci_suspend(struct device *dev)
+{
+ if (!pm_runtime_suspended(dev))
+ return cci_suspend_runtime(dev);
+
+ return 0;
+}
+
+static int __maybe_unused cci_resume(struct device *dev)
+{
+ cci_resume_runtime(dev);
+ pm_request_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops qcom_cci_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume)
+ SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL)
+};
+
+static int cci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct resource *r;
+ struct cci *cci;
+ int ret, i;
+ u32 val;
+
+ cci = devm_kzalloc(dev, sizeof(*cci), GFP_KERNEL);
+ if (!cci)
+ return -ENOMEM;
+
+ cci->dev = dev;
+ platform_set_drvdata(pdev, cci);
+ cci->data = device_get_match_data(dev);
+ if (!cci->data)
+ return -ENOENT;
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ struct cci_master *master;
+ u32 idx;
+
+ ret = of_property_read_u32(child, "reg", &idx);
+ if (ret) {
+ dev_err(dev, "%pOF invalid 'reg' property", child);
+ continue;
+ }
+
+ if (idx >= cci->data->num_masters) {
+ dev_err(dev, "%pOF invalid 'reg' value: %u (max is %u)",
+ child, idx, cci->data->num_masters - 1);
+ continue;
+ }
+
+ master = &cci->master[idx];
+ master->adap.quirks = &cci->data->quirks;
+ master->adap.algo = &cci_algo;
+ master->adap.dev.parent = dev;
+ master->adap.dev.of_node = of_node_get(child);
+ master->master = idx;
+ master->cci = cci;
+
+ i2c_set_adapdata(&master->adap, master);
+ snprintf(master->adap.name, sizeof(master->adap.name), "Qualcomm-CCI");
+
+ master->mode = I2C_MODE_STANDARD;
+ ret = of_property_read_u32(child, "clock-frequency", &val);
+ if (!ret) {
+ if (val == I2C_MAX_FAST_MODE_FREQ)
+ master->mode = I2C_MODE_FAST;
+ else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
+ master->mode = I2C_MODE_FAST_PLUS;
+ }
+
+ init_completion(&master->irq_complete);
+ }
+
+ /* Memory */
+
+ cci->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
+ if (IS_ERR(cci->base))
+ return PTR_ERR(cci->base);
+
+ /* Clocks */
+
+ ret = devm_clk_bulk_get_all(dev, &cci->clocks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
+ else if (!ret)
+ return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n");
+ cci->nclocks = ret;
+
+ ret = cci_enable_clocks(cci);
+ if (ret < 0)
+ return ret;
+
+ /* Interrupt */
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto disable_clocks;
+ cci->irq = ret;
+
+ ret = devm_request_irq(dev, cci->irq, cci_isr, 0, dev_name(dev), cci);
+ if (ret < 0) {
+ dev_err(dev, "request_irq failed, ret: %d\n", ret);
+ goto disable_clocks;
+ }
+
+ val = readl(cci->base + CCI_HW_VERSION);
+ dev_dbg(dev, "CCI HW version = 0x%08x", val);
+
+ ret = cci_reset(cci);
+ if (ret < 0)
+ goto error;
+
+ ret = cci_init(cci);
+ if (ret < 0)
+ goto error;
+
+ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ for (i = 0; i < cci->data->num_masters; i++) {
+ if (!cci->master[i].cci)
+ continue;
+
+ ret = i2c_add_adapter(&cci->master[i].adap);
+ if (ret < 0) {
+ of_node_put(cci->master[i].adap.dev.of_node);
+ goto error_i2c;
+ }
+ }
+
+ return 0;
+
+error_i2c:
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+
+ for (--i ; i >= 0; i--) {
+ if (cci->master[i].cci) {
+ i2c_del_adapter(&cci->master[i].adap);
+ of_node_put(cci->master[i].adap.dev.of_node);
+ }
+ }
+error:
+ disable_irq(cci->irq);
+disable_clocks:
+ cci_disable_clocks(cci);
+
+ return ret;
+}
+
+static void cci_remove(struct platform_device *pdev)
+{
+ struct cci *cci = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < cci->data->num_masters; i++) {
+ if (cci->master[i].cci) {
+ i2c_del_adapter(&cci->master[i].adap);
+ of_node_put(cci->master[i].adap.dev.of_node);
+ }
+ cci_halt(cci, i);
+ }
+
+ disable_irq(cci->irq);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+}
+
+static const struct cci_data cci_v1_data = {
+ .num_masters = 1,
+ .queue_size = { 64, 16 },
+ .quirks = {
+ .max_write_len = 10,
+ .max_read_len = 12,
+ },
+ .params[I2C_MODE_STANDARD] = {
+ .thigh = 78,
+ .tlow = 114,
+ .tsu_sto = 28,
+ .tsu_sta = 28,
+ .thd_dat = 10,
+ .thd_sta = 77,
+ .tbuf = 118,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 1
+ },
+ .params[I2C_MODE_FAST] = {
+ .thigh = 20,
+ .tlow = 28,
+ .tsu_sto = 21,
+ .tsu_sta = 21,
+ .thd_dat = 13,
+ .thd_sta = 18,
+ .tbuf = 32,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+};
+
+static const struct cci_data cci_v1_5_data = {
+ .num_masters = 2,
+ .queue_size = { 64, 16 },
+ .quirks = {
+ .max_write_len = 10,
+ .max_read_len = 12,
+ },
+ .params[I2C_MODE_STANDARD] = {
+ .thigh = 78,
+ .tlow = 114,
+ .tsu_sto = 28,
+ .tsu_sta = 28,
+ .thd_dat = 10,
+ .thd_sta = 77,
+ .tbuf = 118,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 1
+ },
+ .params[I2C_MODE_FAST] = {
+ .thigh = 20,
+ .tlow = 28,
+ .tsu_sto = 21,
+ .tsu_sta = 21,
+ .thd_dat = 13,
+ .thd_sta = 18,
+ .tbuf = 32,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+};
+
+static const struct cci_data cci_v2_data = {
+ .num_masters = 2,
+ .queue_size = { 64, 16 },
+ .quirks = {
+ .max_write_len = 11,
+ .max_read_len = 12,
+ },
+ .params[I2C_MODE_STANDARD] = {
+ .thigh = 201,
+ .tlow = 174,
+ .tsu_sto = 204,
+ .tsu_sta = 231,
+ .thd_dat = 22,
+ .thd_sta = 162,
+ .tbuf = 227,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ .params[I2C_MODE_FAST] = {
+ .thigh = 38,
+ .tlow = 56,
+ .tsu_sto = 40,
+ .tsu_sta = 40,
+ .thd_dat = 22,
+ .thd_sta = 35,
+ .tbuf = 62,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ .params[I2C_MODE_FAST_PLUS] = {
+ .thigh = 16,
+ .tlow = 22,
+ .tsu_sto = 17,
+ .tsu_sta = 18,
+ .thd_dat = 16,
+ .thd_sta = 15,
+ .tbuf = 24,
+ .scl_stretch_en = 0,
+ .trdhld = 3,
+ .tsp = 3
+ },
+};
+
+static const struct cci_data cci_msm8953_data = {
+ .num_masters = 2,
+ .queue_size = { 64, 16 },
+ .quirks = {
+ .max_write_len = 11,
+ .max_read_len = 12,
+ },
+ .params[I2C_MODE_STANDARD] = {
+ .thigh = 78,
+ .tlow = 114,
+ .tsu_sto = 28,
+ .tsu_sta = 28,
+ .thd_dat = 10,
+ .thd_sta = 77,
+ .tbuf = 118,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 1
+ },
+ .params[I2C_MODE_FAST] = {
+ .thigh = 20,
+ .tlow = 28,
+ .tsu_sto = 21,
+ .tsu_sta = 21,
+ .thd_dat = 13,
+ .thd_sta = 18,
+ .tbuf = 32,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ .params[I2C_MODE_FAST_PLUS] = {
+ .thigh = 16,
+ .tlow = 22,
+ .tsu_sto = 17,
+ .tsu_sta = 18,
+ .thd_dat = 16,
+ .thd_sta = 15,
+ .tbuf = 19,
+ .scl_stretch_en = 1,
+ .trdhld = 3,
+ .tsp = 3
+ },
+};
+
+static const struct of_device_id cci_dt_match[] = {
+ { .compatible = "qcom,msm8226-cci", .data = &cci_v1_data},
+ { .compatible = "qcom,msm8953-cci", .data = &cci_msm8953_data},
+ { .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data},
+ { .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},
+
+
+ /*
+ * Legacy compatibles kept for backwards compatibility.
+ * Do not add any new ones unless they introduce a new config
+ */
+ { .compatible = "qcom,msm8916-cci", .data = &cci_v1_data},
+ { .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
+ { .compatible = "qcom,sm8250-cci", .data = &cci_v2_data},
+ { .compatible = "qcom,sm8450-cci", .data = &cci_v2_data},
+ {}
+};
+MODULE_DEVICE_TABLE(of, cci_dt_match);
+
+static struct platform_driver qcom_cci_driver = {
+ .probe = cci_probe,
+ .remove = cci_remove,
+ .driver = {
+ .name = "i2c-qcom-cci",
+ .of_match_table = cci_dt_match,
+ .pm = &qcom_cci_pm,
+ },
+};
+
+module_platform_driver(qcom_cci_driver);
+
+MODULE_DESCRIPTION("Qualcomm Camera Control Interface driver");
+MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
+MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index db075bc0d952..3a04016db2c3 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -1,19 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
+#include <linux/dma/qcom-gpi-dma.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/qcom-geni-se.h>
+#include <linux/soc/qcom/geni-se.h>
#include <linux/spinlock.h>
+#include <linux/units.h>
#define SE_I2C_TX_TRANS_LEN 0x26c
#define SE_I2C_RX_TRANS_LEN 0x270
@@ -48,6 +51,9 @@
#define LOW_COUNTER_SHFT 10
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
+#define I2C_PACK_TX BIT(0)
+#define I2C_PACK_RX BIT(1)
+
enum geni_i2c_err_code {
GP_IRQ0,
NACK,
@@ -65,13 +71,31 @@ enum geni_i2c_err_code {
<< 5)
#define I2C_AUTO_SUSPEND_DELAY 250
-#define KHZ(freq) (1000 * freq)
#define PACKING_BYTES_PW 4
#define ABORT_TIMEOUT HZ
#define XFER_TIMEOUT HZ
#define RST_TIMEOUT HZ
+#define QCOM_I2C_MIN_NUM_OF_MSGS_MULTI_DESC 2
+
+/**
+ * struct geni_i2c_gpi_multi_desc_xfer - Structure for multi transfer support
+ *
+ * @msg_idx_cnt: Current message index being processed in the transfer
+ * @unmap_msg_cnt: Number of messages that have been unmapped
+ * @irq_cnt: Number of transfer completion interrupts received
+ * @dma_buf: Array of virtual addresses for DMA-safe buffers
+ * @dma_addr: Array of DMA addresses corresponding to the buffers
+ */
+struct geni_i2c_gpi_multi_desc_xfer {
+ u32 msg_idx_cnt;
+ u32 unmap_msg_cnt;
+ u32 irq_cnt;
+ void **dma_buf;
+ dma_addr_t *dma_addr;
+};
+
struct geni_i2c_dev {
struct geni_se se;
u32 tx_wm;
@@ -83,9 +107,27 @@ struct geni_i2c_dev {
int cur_wr;
int cur_rd;
spinlock_t lock;
+ struct clk *core_clk;
u32 clk_freq_out;
const struct geni_i2c_clk_fld *clk_fld;
int suspended;
+ void *dma_buf;
+ size_t xfer_len;
+ dma_addr_t dma_addr;
+ struct dma_chan *tx_c;
+ struct dma_chan *rx_c;
+ bool gpi_mode;
+ bool abort_done;
+ bool is_tx_multi_desc_xfer;
+ u32 num_msgs;
+ struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
+};
+
+struct geni_i2c_desc {
+ bool has_core_clk;
+ char *icc_ddr;
+ bool no_dma_support;
+ unsigned int tx_fifo_depth;
};
struct geni_i2c_err_log {
@@ -97,7 +139,7 @@ static const struct geni_i2c_err_log gi2c_log[] = {
[GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"},
[NACK] = {-ENXIO, "NACK: slv unresponsive, check its power/reset-ln"},
[GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"},
- [BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unepxected start/stop"},
+ [BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unexpected start/stop"},
[ARB_LOST] = {-EAGAIN, "Bus arbitration lost, clock line undriveable"},
[GP_IRQ5] = {-EIO, "Unknown I2C err GP IRQ5"},
[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
@@ -126,22 +168,36 @@ struct geni_i2c_clk_fld {
* clk_freq_out = t / t_cycle
* source_clock = 19.2 MHz
*/
-static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
- {KHZ(100), 7, 10, 11, 26},
- {KHZ(400), 2, 5, 12, 24},
- {KHZ(1000), 1, 3, 9, 18},
+static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = {
+ { I2C_MAX_STANDARD_MODE_FREQ, 7, 10, 12, 26 },
+ { I2C_MAX_FAST_MODE_FREQ, 2, 5, 11, 22 },
+ { I2C_MAX_FAST_MODE_PLUS_FREQ, 1, 2, 8, 18 },
+ {}
+};
+
+/* source_clock = 32 MHz */
+static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
+ { I2C_MAX_STANDARD_MODE_FREQ, 8, 14, 18, 38 },
+ { I2C_MAX_FAST_MODE_FREQ, 4, 3, 9, 19 },
+ { I2C_MAX_FAST_MODE_PLUS_FREQ, 2, 3, 5, 15 },
+ {}
};
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
- int i;
- const struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
+ const struct geni_i2c_clk_fld *itr;
- for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
+ if (clk_get_rate(gi2c->se.clk) == 32 * HZ_PER_MHZ)
+ itr = geni_i2c_clk_map_32mhz;
+ else
+ itr = geni_i2c_clk_map_19p2mhz;
+
+ while (itr->clk_freq_out != 0) {
if (itr->clk_freq_out == gi2c->clk_freq_out) {
gi2c->clk_fld = itr;
return 0;
}
+ itr++;
}
return -EINVAL;
}
@@ -192,9 +248,18 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
- if (err != NACK && err != GENI_ABORT_DONE) {
+ switch (err) {
+ case GENI_ABORT_DONE:
+ gi2c->abort_done = true;
+ break;
+ case NACK:
+ case GENI_TIMEOUT:
+ dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
+ break;
+ default:
dev_err(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
geni_i2c_err_misc(gi2c);
+ break;
}
}
@@ -210,9 +275,8 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
u32 dma;
u32 val;
struct i2c_msg *cur;
- unsigned long flags;
- spin_lock_irqsave(&gi2c->lock, flags);
+ spin_lock(&gi2c->lock);
m_stat = readl_relaxed(base + SE_GENI_M_IRQ_STATUS);
rx_st = readl_relaxed(base + SE_GENI_RX_FIFO_STATUS);
dm_tx_st = readl_relaxed(base + SE_DMA_TX_IRQ_STAT);
@@ -294,28 +358,28 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
complete(&gi2c->done);
- spin_unlock_irqrestore(&gi2c->lock, flags);
+ spin_unlock(&gi2c->lock);
return IRQ_HANDLED;
}
static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
{
- u32 val;
unsigned long time_left = ABORT_TIMEOUT;
unsigned long flags;
spin_lock_irqsave(&gi2c->lock, flags);
geni_i2c_err(gi2c, GENI_TIMEOUT);
gi2c->cur = NULL;
+ gi2c->abort_done = false;
geni_se_abort_m_cmd(&gi2c->se);
spin_unlock_irqrestore(&gi2c->lock, flags);
+
do {
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
- val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
- } while (!(val & M_CMD_ABORT_EN) && time_left);
+ } while (!gi2c->abort_done && time_left);
- if (!(val & M_CMD_ABORT_EN))
+ if (!time_left)
dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
}
@@ -349,14 +413,39 @@ static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
}
+static void geni_i2c_rx_msg_cleanup(struct geni_i2c_dev *gi2c,
+ struct i2c_msg *cur)
+{
+ gi2c->cur_rd = 0;
+ if (gi2c->dma_buf) {
+ if (gi2c->err)
+ geni_i2c_rx_fsm_rst(gi2c);
+ geni_se_rx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
+ i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
+ }
+}
+
+static void geni_i2c_tx_msg_cleanup(struct geni_i2c_dev *gi2c,
+ struct i2c_msg *cur)
+{
+ gi2c->cur_wr = 0;
+ if (gi2c->dma_buf) {
+ if (gi2c->err)
+ geni_i2c_tx_fsm_rst(gi2c);
+ geni_se_tx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
+ i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
+ }
+}
+
static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
u32 m_param)
{
- dma_addr_t rx_dma;
+ dma_addr_t rx_dma = 0;
unsigned long time_left;
void *dma_buf;
struct geni_se *se = &gi2c->se;
size_t len = msg->len;
+ struct i2c_msg *cur;
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
if (dma_buf)
@@ -371,19 +460,18 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
geni_se_select_mode(se, GENI_SE_FIFO);
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
dma_buf = NULL;
+ } else {
+ gi2c->xfer_len = len;
+ gi2c->dma_addr = rx_dma;
+ gi2c->dma_buf = dma_buf;
}
+ cur = gi2c->cur;
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
if (!time_left)
geni_i2c_abort_xfer(gi2c);
- gi2c->cur_rd = 0;
- if (dma_buf) {
- if (gi2c->err)
- geni_i2c_rx_fsm_rst(gi2c);
- geni_se_rx_dma_unprep(se, rx_dma, len);
- i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
- }
+ geni_i2c_rx_msg_cleanup(gi2c, cur);
return gi2c->err;
}
@@ -391,11 +479,12 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
u32 m_param)
{
- dma_addr_t tx_dma;
+ dma_addr_t tx_dma = 0;
unsigned long time_left;
void *dma_buf;
struct geni_se *se = &gi2c->se;
size_t len = msg->len;
+ struct i2c_msg *cur;
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
if (dma_buf)
@@ -410,32 +499,402 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
geni_se_select_mode(se, GENI_SE_FIFO);
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
dma_buf = NULL;
+ } else {
+ gi2c->xfer_len = len;
+ gi2c->dma_addr = tx_dma;
+ gi2c->dma_buf = dma_buf;
}
if (!dma_buf) /* Get FIFO IRQ */
writel_relaxed(1, se->base + SE_GENI_TX_WATERMARK_REG);
+ cur = gi2c->cur;
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
if (!time_left)
geni_i2c_abort_xfer(gi2c);
- gi2c->cur_wr = 0;
- if (dma_buf) {
- if (gi2c->err)
- geni_i2c_tx_fsm_rst(gi2c);
- geni_se_tx_dma_unprep(se, tx_dma, len);
- i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
- }
+ geni_i2c_tx_msg_cleanup(gi2c, cur);
return gi2c->err;
}
+static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result)
+{
+ struct geni_i2c_dev *gi2c = cb;
+ struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer;
+
+ if (result->result != DMA_TRANS_NOERROR) {
+ dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result);
+ gi2c->err = -EIO;
+ } else if (result->residue) {
+ dev_dbg(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue);
+ }
+
+ if (gi2c->is_tx_multi_desc_xfer) {
+ tx_multi_xfer = &gi2c->i2c_multi_desc_config;
+ tx_multi_xfer->irq_cnt++;
+ }
+
+ complete(&gi2c->done);
+}
+
+static void geni_i2c_gpi_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+ void *tx_buf, dma_addr_t tx_addr,
+ void *rx_buf, dma_addr_t rx_addr)
+{
+ if (tx_buf) {
+ dma_unmap_single(gi2c->se.dev->parent, tx_addr, msg->len, DMA_TO_DEVICE);
+ i2c_put_dma_safe_msg_buf(tx_buf, msg, !gi2c->err);
+ }
+
+ if (rx_buf) {
+ dma_unmap_single(gi2c->se.dev->parent, rx_addr, msg->len, DMA_FROM_DEVICE);
+ i2c_put_dma_safe_msg_buf(rx_buf, msg, !gi2c->err);
+ }
+}
+
+/**
+ * geni_i2c_gpi_multi_desc_unmap() - Unmaps DMA buffers post multi message TX transfers
+ * @gi2c: I2C dev handle
+ * @msgs: Array of I2C messages
+ * @peripheral: Pointer to gpi_i2c_config
+ */
+static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
+ struct gpi_i2c_config *peripheral)
+{
+ u32 msg_xfer_cnt, wr_idx = 0;
+ struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer = &gi2c->i2c_multi_desc_config;
+
+ msg_xfer_cnt = gi2c->err ? tx_multi_xfer->msg_idx_cnt : tx_multi_xfer->irq_cnt;
+
+ /* Unmap the processed DMA buffers based on the received interrupt count */
+ for (; tx_multi_xfer->unmap_msg_cnt < msg_xfer_cnt; tx_multi_xfer->unmap_msg_cnt++) {
+ wr_idx = tx_multi_xfer->unmap_msg_cnt;
+ geni_i2c_gpi_unmap(gi2c, &msgs[wr_idx],
+ tx_multi_xfer->dma_buf[wr_idx],
+ tx_multi_xfer->dma_addr[wr_idx],
+ NULL, 0);
+
+ if (tx_multi_xfer->unmap_msg_cnt == gi2c->num_msgs - 1) {
+ kfree(tx_multi_xfer->dma_buf);
+ kfree(tx_multi_xfer->dma_addr);
+ break;
+ }
+ }
+}
+
+/**
+ * geni_i2c_gpi_multi_xfer_timeout_handler() - Handles multi message transfer timeout
+ * @dev: Pointer to the corresponding dev node
+ * @multi_xfer: Pointer to the geni_i2c_gpi_multi_desc_xfer
+ * @transfer_timeout_msecs: Timeout value in milliseconds
+ * @transfer_comp: Completion object of the transfer
+ *
+ * This function waits for the completion of each processed transfer messages
+ * based on the interrupts generated upon transfer completion.
+ *
+ * Return: On success returns 0, -ETIMEDOUT on timeout.
+ */
+static int geni_i2c_gpi_multi_xfer_timeout_handler(struct device *dev,
+ struct geni_i2c_gpi_multi_desc_xfer *multi_xfer,
+ u32 transfer_timeout_msecs,
+ struct completion *transfer_comp)
+{
+ int i;
+ u32 time_left;
+
+ for (i = 0; i < multi_xfer->msg_idx_cnt - 1; i++) {
+ reinit_completion(transfer_comp);
+
+ if (multi_xfer->msg_idx_cnt != multi_xfer->irq_cnt) {
+ time_left = wait_for_completion_timeout(transfer_comp,
+ transfer_timeout_msecs);
+ if (!time_left) {
+ dev_err(dev, "%s: Transfer timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ }
+ }
+ return 0;
+}
+
+static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
+ struct dma_slave_config *config, dma_addr_t *dma_addr_p,
+ void **buf, unsigned int op, struct dma_chan *dma_chan)
+{
+ struct gpi_i2c_config *peripheral;
+ unsigned int flags;
+ void *dma_buf;
+ dma_addr_t addr;
+ enum dma_data_direction map_dirn;
+ enum dma_transfer_direction dma_dirn;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+ struct geni_i2c_gpi_multi_desc_xfer *gi2c_gpi_xfer;
+ dma_cookie_t cookie;
+ u32 msg_idx;
+
+ peripheral = config->peripheral_config;
+ gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config;
+ msg_idx = gi2c_gpi_xfer->msg_idx_cnt;
+
+ dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1);
+ if (!dma_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (op == I2C_WRITE)
+ map_dirn = DMA_TO_DEVICE;
+ else
+ map_dirn = DMA_FROM_DEVICE;
+
+ addr = dma_map_single(gi2c->se.dev->parent, dma_buf,
+ msgs[msg_idx].len, map_dirn);
+ if (dma_mapping_error(gi2c->se.dev->parent, addr)) {
+ i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (gi2c->is_tx_multi_desc_xfer) {
+ flags = DMA_CTRL_ACK;
+
+ /* BEI bit to be cleared for last TRE */
+ if (msg_idx == gi2c->num_msgs - 1)
+ flags |= DMA_PREP_INTERRUPT;
+ } else {
+ flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+ }
+
+ /* set the length as message for rx txn */
+ peripheral->rx_len = msgs[msg_idx].len;
+ peripheral->op = op;
+
+ ret = dmaengine_slave_config(dma_chan, config);
+ if (ret) {
+ dev_err(gi2c->se.dev, "dma config error: %d for op:%d\n", ret, op);
+ goto err_config;
+ }
+
+ peripheral->set_config = 0;
+ peripheral->multi_msg = true;
+
+ if (op == I2C_WRITE)
+ dma_dirn = DMA_MEM_TO_DEV;
+ else
+ dma_dirn = DMA_DEV_TO_MEM;
+
+ desc = dmaengine_prep_slave_single(dma_chan, addr, msgs[msg_idx].len,
+ dma_dirn, flags);
+ if (!desc && !(flags & DMA_PREP_INTERRUPT)) {
+ /* Retry with interrupt if not enough TREs */
+ flags |= DMA_PREP_INTERRUPT;
+ desc = dmaengine_prep_slave_single(dma_chan, addr, msgs[msg_idx].len,
+ dma_dirn, flags);
+ }
+
+ if (!desc) {
+ dev_err(gi2c->se.dev, "prep_slave_sg failed\n");
+ ret = -EIO;
+ goto err_config;
+ }
+
+ desc->callback_result = i2c_gpi_cb_result;
+ desc->callback_param = gi2c;
+
+ if (!((msgs[msg_idx].flags & I2C_M_RD) && op == I2C_WRITE))
+ gi2c_gpi_xfer->msg_idx_cnt++;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ dev_err(gi2c->se.dev,
+ "%s: dmaengine_submit failed (%d)\n", __func__, cookie);
+ ret = -EINVAL;
+ goto err_config;
+ }
+
+ if (gi2c->is_tx_multi_desc_xfer) {
+ gi2c_gpi_xfer->dma_buf[msg_idx] = dma_buf;
+ gi2c_gpi_xfer->dma_addr[msg_idx] = addr;
+
+ dma_async_issue_pending(gi2c->tx_c);
+
+ if ((msg_idx == (gi2c->num_msgs - 1)) || flags & DMA_PREP_INTERRUPT) {
+ ret = geni_i2c_gpi_multi_xfer_timeout_handler(gi2c->se.dev, gi2c_gpi_xfer,
+ XFER_TIMEOUT, &gi2c->done);
+ if (ret) {
+ dev_err(gi2c->se.dev,
+ "I2C multi write msg transfer timeout: %d\n",
+ ret);
+ gi2c->err = ret;
+ return ret;
+ }
+ }
+ } else {
+ /* Non multi descriptor message transfer */
+ *buf = dma_buf;
+ *dma_addr_p = addr;
+ }
+ return 0;
+
+err_config:
+ dma_unmap_single(gi2c->se.dev->parent, addr,
+ msgs[msg_idx].len, map_dirn);
+ i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
+
+out:
+ gi2c->err = ret;
+ return ret;
+}
+
+static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], int num)
+{
+ struct dma_slave_config config = {};
+ struct gpi_i2c_config peripheral = {};
+ int i, ret = 0;
+ unsigned long time_left;
+ dma_addr_t tx_addr, rx_addr;
+ void *tx_buf = NULL, *rx_buf = NULL;
+ struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer;
+ const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
+
+ config.peripheral_config = &peripheral;
+ config.peripheral_size = sizeof(peripheral);
+
+ peripheral.pack_enable = I2C_PACK_TX | I2C_PACK_RX;
+ peripheral.cycle_count = itr->t_cycle_cnt;
+ peripheral.high_count = itr->t_high_cnt;
+ peripheral.low_count = itr->t_low_cnt;
+ peripheral.clk_div = itr->clk_div;
+ peripheral.set_config = 1;
+ peripheral.multi_msg = false;
+
+ gi2c->num_msgs = num;
+ gi2c->is_tx_multi_desc_xfer = false;
+
+ tx_multi_xfer = &gi2c->i2c_multi_desc_config;
+ memset(tx_multi_xfer, 0, sizeof(struct geni_i2c_gpi_multi_desc_xfer));
+
+ /*
+ * If number of write messages are two and higher then
+ * configure hardware for multi descriptor transfers with BEI.
+ */
+ if (num >= QCOM_I2C_MIN_NUM_OF_MSGS_MULTI_DESC) {
+ gi2c->is_tx_multi_desc_xfer = true;
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ /*
+ * Multi descriptor transfer with BEI
+ * support is enabled for write transfers.
+ * TODO: Add BEI optimization support for
+ * read transfers later.
+ */
+ gi2c->is_tx_multi_desc_xfer = false;
+ break;
+ }
+ }
+ }
+
+ if (gi2c->is_tx_multi_desc_xfer) {
+ tx_multi_xfer->dma_buf = kcalloc(num, sizeof(void *), GFP_KERNEL);
+ tx_multi_xfer->dma_addr = kcalloc(num, sizeof(dma_addr_t), GFP_KERNEL);
+ if (!tx_multi_xfer->dma_buf || !tx_multi_xfer->dma_addr) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ gi2c->cur = &msgs[i];
+ gi2c->err = 0;
+ dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);
+
+ peripheral.stretch = 0;
+ if (i < num - 1)
+ peripheral.stretch = 1;
+
+ peripheral.addr = msgs[i].addr;
+ if (i > 0 && (!(msgs[i].flags & I2C_M_RD)))
+ peripheral.multi_msg = false;
+
+ ret = geni_i2c_gpi(gi2c, msgs, &config,
+ &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c);
+ if (ret)
+ goto err;
+
+ if (msgs[i].flags & I2C_M_RD) {
+ ret = geni_i2c_gpi(gi2c, msgs, &config,
+ &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c);
+ if (ret)
+ goto err;
+
+ dma_async_issue_pending(gi2c->rx_c);
+ }
+
+ if (!gi2c->is_tx_multi_desc_xfer) {
+ dma_async_issue_pending(gi2c->tx_c);
+ time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ if (!time_left) {
+ dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__);
+ gi2c->err = -ETIMEDOUT;
+ }
+ }
+
+ if (gi2c->err) {
+ ret = gi2c->err;
+ goto err;
+ }
+
+ if (!gi2c->is_tx_multi_desc_xfer)
+ geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
+ else if (tx_multi_xfer->unmap_msg_cnt != tx_multi_xfer->irq_cnt)
+ geni_i2c_gpi_multi_desc_unmap(gi2c, msgs, &peripheral);
+ }
+
+ return num;
+
+err:
+ dev_err(gi2c->se.dev, "GPI transfer failed: %d\n", ret);
+ dmaengine_terminate_sync(gi2c->rx_c);
+ dmaengine_terminate_sync(gi2c->tx_c);
+ if (gi2c->is_tx_multi_desc_xfer)
+ geni_i2c_gpi_multi_desc_unmap(gi2c, msgs, &peripheral);
+ else
+ geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
+
+ return ret;
+}
+
+static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
+ struct i2c_msg msgs[], int num)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < num; i++) {
+ u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
+
+ m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
+
+ gi2c->cur = &msgs[i];
+ if (msgs[i].flags & I2C_M_RD)
+ ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
+ else
+ ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
+
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
static int geni_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
- int i, ret;
+ int ret;
gi2c->err = 0;
reinit_completion(&gi2c->done);
@@ -449,24 +908,12 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
}
qcom_geni_i2c_conf(gi2c);
- for (i = 0; i < num; i++) {
- u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
-
- m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
- gi2c->cur = &msgs[i];
- if (msgs[i].flags & I2C_M_RD)
- ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
- else
- ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
-
- if (ret)
- break;
- }
- if (ret == 0)
- ret = num;
+ if (gi2c->gpi_mode)
+ ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
+ else
+ ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
- pm_runtime_mark_last_busy(gi2c->se.dev);
pm_runtime_put_autosuspend(gi2c->se.dev);
gi2c->cur = NULL;
gi2c->err = 0;
@@ -479,97 +926,205 @@ static u32 geni_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm geni_i2c_algo = {
- .master_xfer = geni_i2c_xfer,
- .functionality = geni_i2c_func,
+ .xfer = geni_i2c_xfer,
+ .functionality = geni_i2c_func,
};
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id geni_i2c_acpi_match[] = {
+ { "QCOM0220"},
+ { "QCOM0411" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
+#endif
+
+static void release_gpi_dma(struct geni_i2c_dev *gi2c)
+{
+ if (gi2c->rx_c)
+ dma_release_channel(gi2c->rx_c);
+
+ if (gi2c->tx_c)
+ dma_release_channel(gi2c->tx_c);
+}
+
+static int setup_gpi_dma(struct geni_i2c_dev *gi2c)
+{
+ int ret;
+
+ geni_se_select_mode(&gi2c->se, GENI_GPI_DMA);
+ gi2c->tx_c = dma_request_chan(gi2c->se.dev, "tx");
+ if (IS_ERR(gi2c->tx_c)) {
+ ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->tx_c),
+ "Failed to get tx DMA ch\n");
+ goto err_tx;
+ }
+
+ gi2c->rx_c = dma_request_chan(gi2c->se.dev, "rx");
+ if (IS_ERR(gi2c->rx_c)) {
+ ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->rx_c),
+ "Failed to get rx DMA ch\n");
+ goto err_rx;
+ }
+
+ dev_dbg(gi2c->se.dev, "Grabbed GPI dma channels\n");
+ return 0;
+
+err_rx:
+ dma_release_channel(gi2c->tx_c);
+err_tx:
+ return ret;
+}
+
static int geni_i2c_probe(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c;
- struct resource *res;
- u32 proto, tx_depth;
+ u32 proto, tx_depth, fifo_disable;
int ret;
+ struct device *dev = &pdev->dev;
+ const struct geni_i2c_desc *desc = NULL;
- gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
+ gi2c = devm_kzalloc(dev, sizeof(*gi2c), GFP_KERNEL);
if (!gi2c)
return -ENOMEM;
- gi2c->se.dev = &pdev->dev;
- gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
+ gi2c->se.dev = dev;
+ gi2c->se.wrapper = dev_get_drvdata(dev->parent);
+ gi2c->se.base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gi2c->se.base))
return PTR_ERR(gi2c->se.base);
- gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
- if (IS_ERR(gi2c->se.clk)) {
- ret = PTR_ERR(gi2c->se.clk);
- dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
- return ret;
+ desc = device_get_match_data(&pdev->dev);
+
+ if (desc && desc->has_core_clk) {
+ gi2c->core_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(gi2c->core_clk))
+ return PTR_ERR(gi2c->core_clk);
}
- ret = device_property_read_u32(&pdev->dev, "clock-frequency",
- &gi2c->clk_freq_out);
+ gi2c->se.clk = devm_clk_get(dev, "se");
+ if (IS_ERR(gi2c->se.clk) && !has_acpi_companion(dev))
+ return PTR_ERR(gi2c->se.clk);
+
+ ret = device_property_read_u32(dev, "clock-frequency",
+ &gi2c->clk_freq_out);
if (ret) {
- dev_info(&pdev->dev,
- "Bus frequency not specified, default to 100kHz.\n");
- gi2c->clk_freq_out = KHZ(100);
+ dev_info(dev, "Bus frequency not specified, default to 100kHz.\n");
+ gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
}
+ if (has_acpi_companion(dev))
+ ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev));
+
gi2c->irq = platform_get_irq(pdev, 0);
- if (gi2c->irq < 0) {
- dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
+ if (gi2c->irq < 0)
return gi2c->irq;
- }
ret = geni_i2c_clk_map_idx(gi2c);
- if (ret) {
- dev_err(&pdev->dev, "Invalid clk frequency %d Hz: %d\n",
- gi2c->clk_freq_out, ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Invalid clk frequency %d Hz\n",
+ gi2c->clk_freq_out);
gi2c->adap.algo = &geni_i2c_algo;
init_completion(&gi2c->done);
spin_lock_init(&gi2c->lock);
platform_set_drvdata(pdev, gi2c);
- ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
- IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
- if (ret) {
- dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
- gi2c->irq, ret);
- return ret;
- }
- /* Disable the interrupt so that the system can enter low-power mode */
- disable_irq(gi2c->irq);
+
+ /* Keep interrupts disabled initially to allow for low-power modes */
+ ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, IRQF_NO_AUTOEN,
+ dev_name(dev), gi2c);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Request_irq failed: %d\n", gi2c->irq);
+
i2c_set_adapdata(&gi2c->adap, gi2c);
- gi2c->adap.dev.parent = &pdev->dev;
- gi2c->adap.dev.of_node = pdev->dev.of_node;
- strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+ gi2c->adap.dev.parent = dev;
+ gi2c->adap.dev.of_node = dev->of_node;
+ strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+
+ ret = geni_icc_get(&gi2c->se, desc ? desc->icc_ddr : "qup-memory");
+ if (ret)
+ return ret;
+ /*
+ * Set the bus quota for core and cpu to a reasonable value for
+ * register access.
+ * Set quota for DDR based on bus speed.
+ */
+ gi2c->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
+ gi2c->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
+ if (!desc || desc->icc_ddr)
+ gi2c->se.icc_paths[GENI_TO_DDR].avg_bw = Bps_to_icc(gi2c->clk_freq_out);
+
+ ret = geni_icc_set_bw(&gi2c->se);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(gi2c->core_clk);
+ if (ret)
+ return ret;
ret = geni_se_resources_on(&gi2c->se);
if (ret) {
- dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
- return ret;
+ dev_err_probe(dev, ret, "Error turning on resources\n");
+ goto err_clk;
}
proto = geni_se_read_proto(&gi2c->se);
- tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
- if (proto != GENI_SE_I2C) {
- dev_err(&pdev->dev, "Invalid proto %d\n", proto);
- geni_se_resources_off(&gi2c->se);
- return -ENXIO;
- }
- gi2c->tx_wm = tx_depth - 1;
- geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
- geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
- true, true, true);
+ if (proto == GENI_SE_INVALID_PROTO) {
+ ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C);
+ if (ret) {
+ dev_err_probe(dev, ret, "i2c firmware load failed ret: %d\n", ret);
+ goto err_resources;
+ }
+ } else if (proto != GENI_SE_I2C) {
+ ret = dev_err_probe(dev, -ENXIO, "Invalid proto %d\n", proto);
+ goto err_resources;
+ }
+
+ if (desc && desc->no_dma_support)
+ fifo_disable = false;
+ else
+ fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
+
+ if (fifo_disable) {
+ /* FIFO is disabled, so we can only use GPI DMA */
+ gi2c->gpi_mode = true;
+ ret = setup_gpi_dma(gi2c);
+ if (ret)
+ goto err_resources;
+
+ dev_dbg(dev, "Using GPI DMA mode for I2C\n");
+ } else {
+ gi2c->gpi_mode = false;
+ tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
+
+ /* I2C Master Hub Serial Elements doesn't have the HW_PARAM_0 register */
+ if (!tx_depth && desc)
+ tx_depth = desc->tx_fifo_depth;
+
+ if (!tx_depth) {
+ ret = dev_err_probe(dev, -EINVAL,
+ "Invalid TX FIFO depth\n");
+ goto err_resources;
+ }
+
+ gi2c->tx_wm = tx_depth - 1;
+ geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
+ geni_se_config_packing(&gi2c->se, BITS_PER_BYTE,
+ PACKING_BYTES_PW, true, true, true);
+
+ dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+ }
+
+ clk_disable_unprepare(gi2c->core_clk);
ret = geni_se_resources_off(&gi2c->se);
if (ret) {
- dev_err(&pdev->dev, "Error turning off resources %d\n", ret);
- return ret;
+ dev_err_probe(dev, ret, "Error turning off resources\n");
+ goto err_dma;
}
- dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+ ret = geni_icc_disable(&gi2c->se);
+ if (ret)
+ goto err_dma;
gi2c->suspended = 1;
pm_runtime_set_suspended(gi2c->se.dev);
@@ -579,21 +1134,43 @@ static int geni_i2c_probe(struct platform_device *pdev)
ret = i2c_add_adapter(&gi2c->adap);
if (ret) {
- dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret);
+ dev_err_probe(dev, ret, "Error adding i2c adapter\n");
pm_runtime_disable(gi2c->se.dev);
- return ret;
+ goto err_dma;
}
- return 0;
+ dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
+
+ return ret;
+
+err_resources:
+ geni_se_resources_off(&gi2c->se);
+err_clk:
+ clk_disable_unprepare(gi2c->core_clk);
+
+ return ret;
+
+err_dma:
+ release_gpi_dma(gi2c);
+
+ return ret;
}
-static int geni_i2c_remove(struct platform_device *pdev)
+static void geni_i2c_remove(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
i2c_del_adapter(&gi2c->adap);
+ release_gpi_dma(gi2c);
pm_runtime_disable(gi2c->se.dev);
- return 0;
+}
+
+static void geni_i2c_shutdown(struct platform_device *pdev)
+{
+ struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
+
+ /* Make client i2c transfers start failing */
+ i2c_mark_adapter_suspended(&gi2c->adap);
}
static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
@@ -611,7 +1188,9 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
gi2c->suspended = 1;
}
- return 0;
+ clk_disable_unprepare(gi2c->core_clk);
+
+ return geni_icc_disable(&gi2c->se);
}
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
@@ -619,19 +1198,37 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
int ret;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
- ret = geni_se_resources_on(&gi2c->se);
+ ret = geni_icc_enable(&gi2c->se);
if (ret)
return ret;
+ ret = clk_prepare_enable(gi2c->core_clk);
+ if (ret)
+ goto out_icc_disable;
+
+ ret = geni_se_resources_on(&gi2c->se);
+ if (ret)
+ goto out_clk_disable;
+
enable_irq(gi2c->irq);
gi2c->suspended = 0;
+
return 0;
+
+out_clk_disable:
+ clk_disable_unprepare(gi2c->core_clk);
+out_icc_disable:
+ geni_icc_disable(&gi2c->se);
+
+ return ret;
}
static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+ i2c_mark_adapter_suspended(&gi2c->adap);
+
if (!gi2c->suspended) {
geni_i2c_runtime_suspend(dev);
pm_runtime_disable(dev);
@@ -641,14 +1238,30 @@ static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
return 0;
}
+static int __maybe_unused geni_i2c_resume_noirq(struct device *dev)
+{
+ struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_resumed(&gi2c->adap);
+ return 0;
+}
+
static const struct dev_pm_ops geni_i2c_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, geni_i2c_resume_noirq)
SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume,
NULL)
};
+static const struct geni_i2c_desc i2c_master_hub = {
+ .has_core_clk = true,
+ .icc_ddr = NULL,
+ .no_dma_support = true,
+ .tx_fifo_depth = 16,
+};
+
static const struct of_device_id geni_i2c_dt_match[] = {
{ .compatible = "qcom,geni-i2c" },
+ { .compatible = "qcom,geni-i2c-master-hub", .data = &i2c_master_hub },
{}
};
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
@@ -656,10 +1269,12 @@ MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
static struct platform_driver geni_i2c_driver = {
.probe = geni_i2c_probe,
.remove = geni_i2c_remove,
+ .shutdown = geni_i2c_shutdown,
.driver = {
.name = "geni_i2c",
.pm = &geni_i2c_pm_ops,
.of_match_table = geni_i2c_dt_match,
+ .acpi_match_table = ACPI_PTR(geni_i2c_acpi_match),
},
};
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index e09cd0775ae9..a0e076fc5f36 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -14,12 +14,13 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/interconnect.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/scatterlist.h>
/* QUP Registers */
@@ -136,13 +137,8 @@
*/
#define TOUT_MIN 2
-/* I2C Frequency Modes */
-#define I2C_STANDARD_FREQ 100000
-#define I2C_FAST_MODE_FREQ 400000
-#define I2C_FAST_MODE_PLUS_FREQ 1000000
-
/* Default values. Use these if FW query fails */
-#define DEFAULT_CLK_FREQ I2C_STANDARD_FREQ
+#define DEFAULT_CLK_FREQ I2C_MAX_STANDARD_MODE_FREQ
#define DEFAULT_SRC_CLK 20000000
/*
@@ -155,6 +151,8 @@
/* TAG length for DATA READ in RX FIFO */
#define READ_RX_TAGS_LEN 2
+#define QUP_BUS_WIDTH 8
+
static unsigned int scl_freq;
module_param_named(scl_freq, scl_freq, uint, 0444);
MODULE_PARM_DESC(scl_freq, "SCL frequency override");
@@ -232,6 +230,7 @@ struct qup_i2c_dev {
int irq;
struct clk *clk;
struct clk *pclk;
+ struct icc_path *icc_path;
struct i2c_adapter adap;
int clk_ctl;
@@ -260,6 +259,10 @@ struct qup_i2c_dev {
/* To configure when bus is in run state */
u32 config_run;
+ /* bandwidth votes */
+ u32 src_clk_freq;
+ u32 cur_bw_clk_freq;
+
/* dma parameters */
bool is_dma;
/* To check if the current transfer is using DMA */
@@ -449,8 +452,10 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
if (!(status & I2C_STATUS_BUS_ACTIVE))
break;
- if (time_after(jiffies, timeout))
+ if (time_after(jiffies, timeout)) {
ret = -ETIMEDOUT;
+ break;
+ }
usleep_range(len, len * 2);
}
@@ -458,6 +463,23 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
return ret;
}
+static int qup_i2c_vote_bw(struct qup_i2c_dev *qup, u32 clk_freq)
+{
+ u32 needed_peak_bw;
+ int ret;
+
+ if (qup->cur_bw_clk_freq == clk_freq)
+ return 0;
+
+ needed_peak_bw = Bps_to_icc(clk_freq * QUP_BUS_WIDTH);
+ ret = icc_set_bw(qup->icc_path, 0, needed_peak_bw);
+ if (ret)
+ return ret;
+
+ qup->cur_bw_clk_freq = clk_freq;
+ return 0;
+}
+
static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
{
struct qup_i2c_block *blk = &qup->blk;
@@ -628,7 +650,7 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
int err;
if (!qup->btx.dma) {
- qup->btx.dma = dma_request_slave_channel_reason(qup->dev, "tx");
+ qup->btx.dma = dma_request_chan(qup->dev, "tx");
if (IS_ERR(qup->btx.dma)) {
err = PTR_ERR(qup->btx.dma);
qup->btx.dma = NULL;
@@ -638,7 +660,7 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
}
if (!qup->brx.dma) {
- qup->brx.dma = dma_request_slave_channel_reason(qup->dev, "rx");
+ qup->brx.dma = dma_request_chan(qup->dev, "rx");
if (IS_ERR(qup->brx.dma)) {
dev_err(qup->dev, "\n rx channel not available");
err = PTR_ERR(qup->brx.dma);
@@ -783,7 +805,7 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
ret = -EINVAL;
/* abort TX descriptors */
- dmaengine_terminate_all(qup->btx.dma);
+ dmaengine_terminate_sync(qup->btx.dma);
goto desc_err;
}
@@ -798,15 +820,14 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
dma_async_issue_pending(qup->brx.dma);
}
- if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) {
- dev_err(qup->dev, "normal trans timed out\n");
+ if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout))
ret = -ETIMEDOUT;
- }
if (ret || qup->bus_err || qup->qup_err) {
reinit_completion(&qup->xfer);
- if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret) {
dev_err(qup->dev, "change to run state timed out");
goto desc_err;
}
@@ -844,6 +865,10 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int ret = 0;
int idx = 0;
+ ret = qup_i2c_vote_bw(qup, qup->src_clk_freq);
+ if (ret)
+ return ret;
+
enable_irq(qup->irq);
ret = qup_i2c_req_dma(qup);
@@ -961,10 +986,8 @@ static void qup_i2c_conf_v1(struct qup_i2c_dev *qup)
u32 qup_config = I2C_MINI_CORE | I2C_N_VAL;
u32 io_mode = QUP_REPACK_EN;
- blk->is_tx_blk_mode =
- blk->total_tx_len > qup->out_fifo_sz ? true : false;
- blk->is_rx_blk_mode =
- blk->total_rx_len > qup->in_fifo_sz ? true : false;
+ blk->is_tx_blk_mode = blk->total_tx_len > qup->out_fifo_sz;
+ blk->is_rx_blk_mode = blk->total_rx_len > qup->in_fifo_sz;
if (blk->is_tx_blk_mode) {
io_mode |= QUP_OUTPUT_BLK_MODE;
@@ -1116,7 +1139,6 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
ret = num;
out:
- pm_runtime_mark_last_busy(qup->dev);
pm_runtime_put_autosuspend(qup->dev);
return ret;
@@ -1296,7 +1318,7 @@ static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
* 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
* tags to TX FIFO and set tx_tags_sent to true.
* 2. Check if send_last_word is true. It will be set when last few data bytes
- * (less than 4 bytes) are reamining to be written in FIFO because of no FIFO
+ * (less than 4 bytes) are remaining to be written in FIFO because of no FIFO
* space. All this data bytes are available in tx_fifo_data so write this
* in FIFO.
* 3. Write the data to TX FIFO and check for cur_blk_len. If it is non zero
@@ -1533,9 +1555,9 @@ qup_i2c_determine_mode_v2(struct qup_i2c_dev *qup,
qup->use_dma = true;
} else {
qup->blk.is_tx_blk_mode = max_tx_len > qup->out_fifo_sz -
- QUP_MAX_TAGS_LEN ? true : false;
+ QUP_MAX_TAGS_LEN;
qup->blk.is_rx_blk_mode = max_rx_len > qup->in_fifo_sz -
- READ_RX_TAGS_LEN ? true : false;
+ READ_RX_TAGS_LEN;
}
return 0;
@@ -1601,7 +1623,6 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
if (ret == 0)
ret = num;
out:
- pm_runtime_mark_last_busy(qup->dev);
pm_runtime_put_autosuspend(qup->dev);
return ret;
@@ -1609,17 +1630,17 @@ out:
static u32 qup_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL_ALL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm qup_i2c_algo = {
- .master_xfer = qup_i2c_xfer,
- .functionality = qup_i2c_func,
+ .xfer = qup_i2c_xfer,
+ .functionality = qup_i2c_func,
};
static const struct i2c_algorithm qup_i2c_algo_v2 = {
- .master_xfer = qup_i2c_xfer_v2,
- .functionality = qup_i2c_func,
+ .xfer = qup_i2c_xfer_v2,
+ .functionality = qup_i2c_func,
};
/*
@@ -1651,12 +1672,13 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
config = readl(qup->base + QUP_CONFIG);
config |= QUP_CLOCK_AUTO_GATE;
writel(config, qup->base + QUP_CONFIG);
+ qup_i2c_vote_bw(qup, 0);
clk_disable_unprepare(qup->pclk);
}
static const struct acpi_device_id qup_i2c_acpi_match[] = {
{ "QCOM8010"},
- { },
+ { }
};
MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
@@ -1665,7 +1687,6 @@ static int qup_i2c_probe(struct platform_device *pdev)
static const int blk_sizes[] = {4, 16, 32};
struct qup_i2c_dev *qup;
unsigned long one_bit_t;
- struct resource *res;
u32 io_mode, hw_ver, size;
int ret, fs_div, hs_div;
u32 src_clk_freq = DEFAULT_SRC_CLK;
@@ -1692,7 +1713,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
}
}
- if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
+ if (device_is_compatible(&pdev->dev, "qcom,i2c-qup-v1.1.1")) {
qup->adap.algo = &qup_i2c_algo;
qup->adap.quirks = &qup_i2c_quirks;
is_qup_v1 = true;
@@ -1752,25 +1773,32 @@ static int qup_i2c_probe(struct platform_device *pdev)
goto fail_dma;
}
qup->is_dma = true;
+
+ qup->icc_path = devm_of_icc_get(&pdev->dev, NULL);
+ if (IS_ERR(qup->icc_path))
+ return dev_err_probe(&pdev->dev, PTR_ERR(qup->icc_path),
+ "failed to get interconnect path\n");
}
nodma:
/* We support frequencies up to FAST Mode Plus (1MHz) */
- if (!clk_freq || clk_freq > I2C_FAST_MODE_PLUS_FREQ) {
+ if (!clk_freq || clk_freq > I2C_MAX_FAST_MODE_PLUS_FREQ) {
dev_err(qup->dev, "clock frequency not supported %d\n",
clk_freq);
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail_dma;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- qup->base = devm_ioremap_resource(qup->dev, res);
- if (IS_ERR(qup->base))
- return PTR_ERR(qup->base);
+ qup->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(qup->base)) {
+ ret = PTR_ERR(qup->base);
+ goto fail_dma;
+ }
qup->irq = platform_get_irq(pdev, 0);
if (qup->irq < 0) {
- dev_err(qup->dev, "No IRQ defined\n");
- return qup->irq;
+ ret = qup->irq;
+ goto fail_dma;
}
if (has_acpi_companion(qup->dev)) {
@@ -1785,17 +1813,20 @@ nodma:
qup->clk = devm_clk_get(qup->dev, "core");
if (IS_ERR(qup->clk)) {
dev_err(qup->dev, "Could not get core clock\n");
- return PTR_ERR(qup->clk);
+ ret = PTR_ERR(qup->clk);
+ goto fail_dma;
}
qup->pclk = devm_clk_get(qup->dev, "iface");
if (IS_ERR(qup->pclk)) {
dev_err(qup->dev, "Could not get iface clock\n");
- return PTR_ERR(qup->pclk);
+ ret = PTR_ERR(qup->pclk);
+ goto fail_dma;
}
qup_i2c_enable_clocks(qup);
src_clk_freq = clk_get_rate(qup->clk);
}
+ qup->src_clk_freq = src_clk_freq;
/*
* Bootloaders might leave a pending interrupt on certain QUP's,
@@ -1807,12 +1838,12 @@ nodma:
goto fail;
ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
- IRQF_TRIGGER_HIGH, "i2c_qup", qup);
+ IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+ "i2c_qup", qup);
if (ret) {
dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
goto fail;
}
- disable_irq(qup->irq);
hw_ver = readl(qup->base + QUP_HW_VERSION);
dev_dbg(qup->dev, "Revision %x\n", hw_ver);
@@ -1861,7 +1892,7 @@ nodma:
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
hs_div = 3;
- if (clk_freq <= I2C_STANDARD_FREQ) {
+ if (clk_freq <= I2C_MAX_STANDARD_MODE_FREQ) {
fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
} else {
@@ -1888,7 +1919,7 @@ nodma:
qup->adap.dev.of_node = pdev->dev.of_node;
qup->is_last = true;
- strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
+ strscpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(qup->dev);
@@ -1914,7 +1945,7 @@ fail_dma:
return ret;
}
-static int qup_i2c_remove(struct platform_device *pdev)
+static void qup_i2c_remove(struct platform_device *pdev)
{
struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
@@ -1928,10 +1959,8 @@ static int qup_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&qup->adap);
pm_runtime_disable(qup->dev);
pm_runtime_set_suspended(qup->dev);
- return 0;
}
-#ifdef CONFIG_PM
static int qup_i2c_pm_suspend_runtime(struct device *device)
{
struct qup_i2c_dev *qup = dev_get_drvdata(device);
@@ -1949,9 +1978,7 @@ static int qup_i2c_pm_resume_runtime(struct device *device)
qup_i2c_enable_clocks(qup);
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int qup_i2c_suspend(struct device *device)
{
if (!pm_runtime_suspended(device))
@@ -1962,20 +1989,14 @@ static int qup_i2c_suspend(struct device *device)
static int qup_i2c_resume(struct device *device)
{
qup_i2c_pm_resume_runtime(device);
- pm_runtime_mark_last_busy(device);
pm_request_autosuspend(device);
return 0;
}
-#endif
static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(
- qup_i2c_suspend,
- qup_i2c_resume)
- SET_RUNTIME_PM_OPS(
- qup_i2c_pm_suspend_runtime,
- qup_i2c_pm_resume_runtime,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(qup_i2c_suspend, qup_i2c_resume)
+ RUNTIME_PM_OPS(qup_i2c_pm_suspend_runtime,
+ qup_i2c_pm_resume_runtime, NULL)
};
static const struct of_device_id qup_i2c_dt_match[] = {
@@ -1991,7 +2012,7 @@ static struct platform_driver qup_i2c_driver = {
.remove = qup_i2c_remove,
.driver = {
.name = "i2c_qup",
- .pm = &qup_i2c_qup_pm_ops,
+ .pm = pm_ptr(&qup_i2c_qup_pm_ops),
.of_match_table = qup_i2c_dt_match,
.acpi_match_table = ACPI_PTR(qup_i2c_acpi_match),
},
@@ -1999,5 +2020,6 @@ static struct platform_driver qup_i2c_driver = {
module_platform_driver(qup_i2c_driver);
+MODULE_DESCRIPTION("Qualcomm QUP based I2C controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c_qup");
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 254e6219e538..d51884ab99f4 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -2,8 +2,8 @@
/*
* Driver for the Renesas R-Car I2C unit
*
- * Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
- * Copyright (C) 2011-2015 Renesas Electronics Corporation
+ * Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
+ * Copyright (C) 2011-2019 Renesas Electronics Corporation
*
* Copyright (C) 2012-14 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
@@ -19,10 +19,12 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -39,84 +41,106 @@
#define ICSAR 0x1C /* slave address */
#define ICMAR 0x20 /* master address */
#define ICRXTX 0x24 /* data port */
-#define ICDMAER 0x3c /* DMA enable */
-#define ICFBSCR 0x38 /* first bit setup cycle */
+#define ICCCR2 0x28 /* Clock control 2 */
+#define ICMPR 0x2C /* SCL mask control */
+#define ICHPR 0x30 /* SCL HIGH control */
+#define ICLPR 0x34 /* SCL LOW control */
+#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */
+#define ICDMAER 0x3c /* DMA enable (Gen3) */
/* ICSCR */
-#define SDBS (1 << 3) /* slave data buffer select */
-#define SIE (1 << 2) /* slave interface enable */
-#define GCAE (1 << 1) /* general call address enable */
-#define FNA (1 << 0) /* forced non acknowledgment */
+#define SDBS BIT(3) /* slave data buffer select */
+#define SIE BIT(2) /* slave interface enable */
+#define GCAE BIT(1) /* general call address enable */
+#define FNA BIT(0) /* forced non acknowledgment */
/* ICMCR */
-#define MDBS (1 << 7) /* non-fifo mode switch */
-#define FSCL (1 << 6) /* override SCL pin */
-#define FSDA (1 << 5) /* override SDA pin */
-#define OBPC (1 << 4) /* override pins */
-#define MIE (1 << 3) /* master if enable */
-#define TSBE (1 << 2)
-#define FSB (1 << 1) /* force stop bit */
-#define ESG (1 << 0) /* enable start bit gen */
+#define MDBS BIT(7) /* non-fifo mode switch */
+#define FSCL BIT(6) /* override SCL pin */
+#define FSDA BIT(5) /* override SDA pin */
+#define OBPC BIT(4) /* override pins */
+#define MIE BIT(3) /* master if enable */
+#define TSBE BIT(2)
+#define FSB BIT(1) /* force stop bit */
+#define ESG BIT(0) /* enable start bit gen */
/* ICSSR (also for ICSIER) */
-#define GCAR (1 << 6) /* general call received */
-#define STM (1 << 5) /* slave transmit mode */
-#define SSR (1 << 4) /* stop received */
-#define SDE (1 << 3) /* slave data empty */
-#define SDT (1 << 2) /* slave data transmitted */
-#define SDR (1 << 1) /* slave data received */
-#define SAR (1 << 0) /* slave addr received */
+#define GCAR BIT(6) /* general call received */
+#define STM BIT(5) /* slave transmit mode */
+#define SSR BIT(4) /* stop received */
+#define SDE BIT(3) /* slave data empty */
+#define SDT BIT(2) /* slave data transmitted */
+#define SDR BIT(1) /* slave data received */
+#define SAR BIT(0) /* slave addr received */
/* ICMSR (also for ICMIE) */
-#define MNR (1 << 6) /* nack received */
-#define MAL (1 << 5) /* arbitration lost */
-#define MST (1 << 4) /* sent a stop */
-#define MDE (1 << 3)
-#define MDT (1 << 2)
-#define MDR (1 << 1)
-#define MAT (1 << 0) /* slave addr xfer done */
+#define MNR BIT(6) /* nack received */
+#define MAL BIT(5) /* arbitration lost */
+#define MST BIT(4) /* sent a stop */
+#define MDE BIT(3)
+#define MDT BIT(2)
+#define MDR BIT(1)
+#define MAT BIT(0) /* slave addr xfer done */
/* ICDMAER */
-#define RSDMAE (1 << 3) /* DMA Slave Received Enable */
-#define TSDMAE (1 << 2) /* DMA Slave Transmitted Enable */
-#define RMDMAE (1 << 1) /* DMA Master Received Enable */
-#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */
+#define RSDMAE BIT(3) /* DMA Slave Received Enable */
+#define TSDMAE BIT(2) /* DMA Slave Transmitted Enable */
+#define RMDMAE BIT(1) /* DMA Master Received Enable */
+#define TMDMAE BIT(0) /* DMA Master Transmitted Enable */
+
+/* ICCCR2 */
+#define FMPE BIT(7) /* Fast Mode Plus Enable */
+#define CDFD BIT(2) /* CDF Disable */
+#define HLSE BIT(1) /* HIGH/LOW Separate Control Enable */
+#define SME BIT(0) /* SCL Mask Enable */
/* ICFBSCR */
-#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
+#define RCAR_MIN_DMA_LEN 8
+
+/* SCL low/high ratio 5:4 to meet all I2C timing specs (incl safety margin) */
+#define RCAR_SCLD_RATIO 5
+#define RCAR_SCHD_RATIO 4
+/*
+ * SMD should be smaller than SCLD/SCHD and is always around 20 in the docs.
+ * Thus, we simply use 20 which works for low and high speeds.
+ */
+#define RCAR_DEFAULT_SMD 20
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
-#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF)
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
#define RCAR_IRQ_RECV (MNR | MAL | MST | MAT | MDR)
#define RCAR_IRQ_STOP (MST)
-#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0x7F)
-#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0x7F)
-
-#define ID_LAST_MSG (1 << 0)
-#define ID_FIRST_MSG (1 << 1)
-#define ID_DONE (1 << 2)
-#define ID_ARBLOST (1 << 3)
-#define ID_NACK (1 << 4)
+#define ID_LAST_MSG BIT(0)
+#define ID_REP_AFTER_RD BIT(1)
+#define ID_DONE BIT(2)
+#define ID_ARBLOST BIT(3)
+#define ID_NACK BIT(4)
+#define ID_EPROTO BIT(5)
/* persistent flags */
-#define ID_P_REP_AFTER_RD BIT(29)
+#define ID_P_FMPLUS BIT(27)
+#define ID_P_NOT_ATOMIC BIT(28)
+#define ID_P_HOST_NOTIFY BIT(29)
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
#define ID_P_PM_BLOCKED BIT(31)
-#define ID_P_MASK GENMASK(31, 29)
+#define ID_P_MASK GENMASK(31, 27)
+
+#define ID_SLAVE_NACK BIT(0)
enum rcar_i2c_type {
I2C_RCAR_GEN1,
I2C_RCAR_GEN2,
I2C_RCAR_GEN3,
+ I2C_RCAR_GEN4,
};
struct rcar_i2c_priv {
+ u32 flags;
void __iomem *io;
struct i2c_adapter adap;
struct i2c_msg *msg;
@@ -127,7 +151,9 @@ struct rcar_i2c_priv {
int pos;
u32 icccr;
- u32 flags;
+ u16 schd;
+ u16 scld;
+ u8 smd;
u8 recovery_icmcr; /* protected by adapter lock */
enum rcar_i2c_type devtype;
struct i2c_client *slave;
@@ -139,14 +165,15 @@ struct rcar_i2c_priv {
enum dma_data_direction dma_direction;
struct reset_control *rstc;
+ int irq;
+
+ struct i2c_client *host_notify_client;
+ u8 slave_flags;
};
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
-#define LOOP_TIMEOUT 1024
-
-
static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
{
writel(val, priv->io + reg);
@@ -157,13 +184,17 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
return readl(priv->io + reg);
}
+static void rcar_i2c_clear_irq(struct rcar_i2c_priv *priv, u32 val)
+{
+ writel(~val & 0x7f, priv->io + ICMSR);
+}
+
static int rcar_i2c_get_scl(struct i2c_adapter *adap)
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
return !!(rcar_i2c_read(priv, ICMCR) & FSCL);
-
-};
+}
static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val)
{
@@ -175,7 +206,7 @@ static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val)
priv->recovery_icmcr &= ~FSCL;
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
-};
+}
static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
{
@@ -187,15 +218,14 @@ static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
priv->recovery_icmcr &= ~FSDA;
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
-};
+}
static int rcar_i2c_get_bus_free(struct i2c_adapter *adap)
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
return !(rcar_i2c_read(priv, ICMCR) & FSDA);
-
-};
+}
static struct i2c_bus_recovery_info rcar_i2c_bri = {
.get_scl = rcar_i2c_get_scl,
@@ -204,6 +234,7 @@ static struct i2c_bus_recovery_info rcar_i2c_bri = {
.get_bus_free = rcar_i2c_get_bus_free,
.recover_bus = i2c_generic_scl_recovery,
};
+
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
{
/* reset master mode */
@@ -211,58 +242,74 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMCR, MDBS);
rcar_i2c_write(priv, ICMSR, 0);
/* start clock */
- rcar_i2c_write(priv, ICCCR, priv->icccr);
+ if (priv->devtype < I2C_RCAR_GEN3) {
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
+ } else {
+ u32 icccr2 = CDFD | HLSE | SME;
+
+ if (priv->flags & ID_P_FMPLUS)
+ icccr2 |= FMPE;
+
+ rcar_i2c_write(priv, ICCCR2, icccr2);
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
+ rcar_i2c_write(priv, ICMPR, priv->smd);
+ rcar_i2c_write(priv, ICHPR, priv->schd);
+ rcar_i2c_write(priv, ICLPR, priv->scld);
+ rcar_i2c_write(priv, ICFBSCR, TCYC17);
+ }
+}
+
+static void rcar_i2c_reset_slave(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_write(priv, ICSIER, 0);
+ rcar_i2c_write(priv, ICSSR, 0);
+ rcar_i2c_write(priv, ICSCR, SDBS);
+ rcar_i2c_write(priv, ICSAR, 0); /* Gen2: must be 0 if not using slave */
}
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
{
- int i;
+ int ret;
+ u32 val;
- for (i = 0; i < LOOP_TIMEOUT; i++) {
- /* make sure that bus is not busy */
- if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
- return 0;
- udelay(1);
+ ret = readl_poll_timeout(priv->io + ICMCR, val, !(val & FSDA), 10,
+ priv->adap.timeout);
+ if (ret) {
+ /* Waiting did not help, try to recover */
+ priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
+ ret = i2c_recover_bus(&priv->adap);
}
- /* Waiting did not help, try to recover */
- priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
- return i2c_recover_bus(&priv->adap);
+ return ret;
}
-static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
+static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
{
- u32 scgd, cdf, round, ick, sum, scl, cdf_width;
+ u32 cdf, round, ick, sum, scl, cdf_width;
unsigned long rate;
struct device *dev = rcar_i2c_priv_to_dev(priv);
+ struct i2c_timings t = {
+ .bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ,
+ .scl_fall_ns = 35,
+ .scl_rise_ns = 200,
+ .scl_int_delay_ns = 50,
+ };
/* Fall back to previously used values if not supplied */
- t->bus_freq_hz = t->bus_freq_hz ?: 100000;
- t->scl_fall_ns = t->scl_fall_ns ?: 35;
- t->scl_rise_ns = t->scl_rise_ns ?: 200;
- t->scl_int_delay_ns = t->scl_int_delay_ns ?: 50;
-
- switch (priv->devtype) {
- case I2C_RCAR_GEN1:
- cdf_width = 2;
- break;
- case I2C_RCAR_GEN2:
- case I2C_RCAR_GEN3:
- cdf_width = 3;
- break;
- default:
- dev_err(dev, "device type error\n");
- return -EIO;
- }
+ i2c_parse_fw_timings(dev, &t, false);
+ priv->smd = RCAR_DEFAULT_SMD;
/*
* calculate SCL clock
* see
- * ICCCR
+ * ICCCR (and ICCCR2 for Gen3+)
*
* ick = clkp / (1 + CDF)
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
*
+ * for Gen3+:
+ * SCL = clkp / (8 + SMD * 2 + SCLD + SCHD +F[(ticf + tr + intd) * clkp])
+ *
* ick : I2C internal clock < 20 MHz
* ticf : I2C SCL falling time
* tr : I2C SCL rising time
@@ -272,124 +319,152 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timin
*/
rate = clk_get_rate(priv->clk);
cdf = rate / 20000000;
- if (cdf >= 1U << cdf_width) {
- dev_err(dev, "Input clock %lu too high\n", rate);
- return -EIO;
- }
- ick = rate / (cdf + 1);
+ cdf_width = (priv->devtype == I2C_RCAR_GEN1) ? 2 : 3;
+ if (cdf >= 1U << cdf_width)
+ goto err_no_val;
+
+ if (t.bus_freq_hz > I2C_MAX_FAST_MODE_FREQ && priv->devtype >= I2C_RCAR_GEN4)
+ priv->flags |= ID_P_FMPLUS;
+ else
+ priv->flags &= ~ID_P_FMPLUS;
+
+ /* On Gen3+, we use cdf only for the filters, not as a SCL divider */
+ ick = rate / (priv->devtype < I2C_RCAR_GEN3 ? (cdf + 1) : 1);
/*
- * it is impossible to calculate large scale
- * number on u32. separate it
+ * It is impossible to calculate a 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 = t->scl_fall_ns + t->scl_rise_ns + t->scl_int_delay_ns;
- round = (ick + 500000) / 1000000 * sum;
- round = (round + 500) / 1000;
+ sum = t.scl_fall_ns + t.scl_rise_ns + t.scl_int_delay_ns;
+ round = DIV_ROUND_CLOSEST(ick, 1000000);
+ round = DIV_ROUND_CLOSEST(round * sum, 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 <= t->bus_freq_hz)
- goto scgd_find;
- }
- dev_err(dev, "it is impossible to calculate best SCL\n");
- return -EIO;
+ if (priv->devtype < I2C_RCAR_GEN3) {
+ u32 scgd;
+ /*
+ * SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick])
+ * 20 + 8 * SCGD + F[...] = ick / SCL
+ * SCGD = ((ick / SCL) - 20 - F[...]) / 8
+ * Result (= SCL) should be less than bus_speed for hardware safety
+ */
+ scgd = DIV_ROUND_UP(ick, t.bus_freq_hz ?: 1);
+ scgd = DIV_ROUND_UP(scgd - 20 - round, 8);
+ scl = ick / (20 + 8 * scgd + round);
+
+ if (scgd > 0x3f)
+ goto err_no_val;
+
+ dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n",
+ scl, t.bus_freq_hz, rate, round, cdf, scgd);
+
+ priv->icccr = scgd << cdf_width | cdf;
+ } else {
+ u32 x, sum_ratio = RCAR_SCHD_RATIO + RCAR_SCLD_RATIO;
+ /*
+ * SCLD/SCHD ratio and SMD default value are explained above
+ * where they are defined. With these definitions, we can compute
+ * x as a base value for the SCLD/SCHD ratio:
+ *
+ * SCL = clkp / (8 + 2 * SMD + SCLD + SCHD + F[(ticf + tr + intd) * clkp])
+ * SCL = clkp / (8 + 2 * SMD + RCAR_SCLD_RATIO * x
+ * + RCAR_SCHD_RATIO * x + F[...])
+ *
+ * with: sum_ratio = RCAR_SCLD_RATIO + RCAR_SCHD_RATIO
+ *
+ * SCL = clkp / (8 + 2 * smd + sum_ratio * x + F[...])
+ * 8 + 2 * smd + sum_ratio * x + F[...] = clkp / SCL
+ * x = ((clkp / SCL) - 8 - 2 * smd - F[...]) / sum_ratio
+ */
+ x = DIV_ROUND_UP(rate, t.bus_freq_hz ?: 1);
+ x = DIV_ROUND_UP(x - 8 - 2 * priv->smd - round, sum_ratio);
+ scl = rate / (8 + 2 * priv->smd + sum_ratio * x + round);
-scgd_find:
- dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
- scl, t->bus_freq_hz, clk_get_rate(priv->clk), round, cdf, scgd);
+ if (x == 0 || x * RCAR_SCLD_RATIO > 0xffff)
+ goto err_no_val;
- /* keep icccr value */
- priv->icccr = scgd << cdf_width | cdf;
+ priv->icccr = cdf;
+ priv->schd = RCAR_SCHD_RATIO * x;
+ priv->scld = RCAR_SCLD_RATIO * x;
+ if (priv->smd >= priv->schd)
+ priv->smd = priv->schd - 1;
+
+ dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u SCHD %u SCLD %u SMD %u\n",
+ scl, t.bus_freq_hz, rate, round, cdf, priv->schd, priv->scld, priv->smd);
+ }
return 0;
+
+err_no_val:
+ dev_err(dev, "it is impossible to calculate best SCL\n");
+ return -EINVAL;
}
+/*
+ * We don't have a test case but the HW engineers say that the write order of
+ * ICMSR and ICMCR depends on whether we issue START or REP_START. So, ICMSR
+ * handling is outside of this function. First messages clear ICMSR before this
+ * function, interrupt handlers clear the relevant bits after this function.
+ */
static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
{
int read = !!rcar_i2c_is_recv(priv);
+ bool rep_start = !(priv->flags & ID_REP_AFTER_RD);
priv->pos = 0;
+ priv->flags &= ID_P_MASK;
+
if (priv->msgs_left == 1)
priv->flags |= ID_LAST_MSG;
rcar_i2c_write(priv, ICMAR, i2c_8bit_addr_from_msg(priv->msg));
- /*
- * We don't have a test case but the HW engineers say that the write order
- * of ICMSR and ICMCR depends on whether we issue START or REP_START. Since
- * it didn't cause a drawback for me, let's rather be safe than sorry.
- */
- if (priv->flags & ID_FIRST_MSG) {
- rcar_i2c_write(priv, ICMSR, 0);
+ if (priv->flags & ID_P_NOT_ATOMIC)
+ rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
+
+ if (rep_start)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
- } else {
- if (priv->flags & ID_P_REP_AFTER_RD)
- priv->flags &= ~ID_P_REP_AFTER_RD;
- else
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
- rcar_i2c_write(priv, ICMSR, 0);
- }
- rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
+}
+
+static void rcar_i2c_first_msg(struct rcar_i2c_priv *priv,
+ struct i2c_msg *msgs, int num)
+{
+ priv->msg = msgs;
+ priv->msgs_left = num;
+ rcar_i2c_write(priv, ICMSR, 0); /* must be before preparing msg */
+ rcar_i2c_prepare_msg(priv);
}
static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
{
priv->msg++;
priv->msgs_left--;
- priv->flags &= ID_P_MASK;
rcar_i2c_prepare_msg(priv);
+ /* ICMSR handling must come afterwards in the irq handler */
}
-/*
- * interrupt functions
- */
-static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
+static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv, bool terminate)
{
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
? priv->dma_rx : priv->dma_tx;
- /* Disable DMA Master Received/Transmitted */
- rcar_i2c_write(priv, ICDMAER, 0);
-
- /* Reset default delay */
- rcar_i2c_write(priv, ICFBSCR, TCYC06);
+ /* only allowed from thread context! */
+ if (terminate)
+ dmaengine_terminate_sync(chan);
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
sg_dma_len(&priv->sg), priv->dma_direction);
- /* Gen3 can only do one RXDMA per transfer and we just completed it */
- if (priv->devtype == I2C_RCAR_GEN3 &&
+ /* Gen3+ can only do one RXDMA per transfer and we just completed it */
+ if (priv->devtype >= I2C_RCAR_GEN3 &&
priv->dma_direction == DMA_FROM_DEVICE)
priv->flags |= ID_P_NO_RXDMA;
priv->dma_direction = DMA_NONE;
-}
-
-static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
-{
- if (priv->dma_direction == DMA_NONE)
- return;
- else if (priv->dma_direction == DMA_FROM_DEVICE)
- dmaengine_terminate_all(priv->dma_rx);
- else if (priv->dma_direction == DMA_TO_DEVICE)
- dmaengine_terminate_all(priv->dma_tx);
- rcar_i2c_dma_unmap(priv);
+ /* Disable DMA Master Received/Transmitted, must be last! */
+ rcar_i2c_write(priv, ICDMAER, 0);
}
static void rcar_i2c_dma_callback(void *data)
@@ -398,10 +473,10 @@ static void rcar_i2c_dma_callback(void *data)
priv->pos += sg_dma_len(&priv->sg);
- rcar_i2c_dma_unmap(priv);
+ rcar_i2c_cleanup_dma(priv, false);
}
-static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
+static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
{
struct device *dev = rcar_i2c_priv_to_dev(priv);
struct i2c_msg *msg = priv->msg;
@@ -415,9 +490,9 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
int len;
/* Do various checks to see if DMA is feasible at all */
- if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE) ||
- (read && priv->flags & ID_P_NO_RXDMA))
- return;
+ if (!(priv->flags & ID_P_NOT_ATOMIC) || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
+ !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
+ return false;
if (read) {
/*
@@ -437,7 +512,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
if (dma_mapping_error(chan->device->dev, dma_addr)) {
dev_dbg(dev, "dma map failed, using PIO\n");
- return;
+ return false;
}
sg_dma_len(&priv->sg) = len;
@@ -450,8 +525,8 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!txdesc) {
dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
- rcar_i2c_cleanup_dma(priv);
- return;
+ rcar_i2c_cleanup_dma(priv, false);
+ return false;
}
txdesc->callback = rcar_i2c_dma_callback;
@@ -460,13 +535,10 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
cookie = dmaengine_submit(txdesc);
if (dma_submit_error(cookie)) {
dev_dbg(dev, "submitting dma failed, using PIO\n");
- rcar_i2c_cleanup_dma(priv);
- return;
+ rcar_i2c_cleanup_dma(priv, false);
+ return false;
}
- /* Set delay for DMA operations */
- rcar_i2c_write(priv, ICFBSCR, TCYC17);
-
/* Enable DMA Master Received/Transmitted */
if (read)
rcar_i2c_write(priv, ICDMAER, RMDMAE);
@@ -474,14 +546,23 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICDMAER, TMDMAE);
dma_async_issue_pending(chan);
+ return true;
}
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
+ u32 irqs_to_clear = MDE;
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
- if (!(msr & MDE))
+ if (WARN(!(msr & MDE), "spurious irq"))
+ return;
+
+ if (msr & MAT)
+ irqs_to_clear |= MAT;
+
+ /* Check if DMA can be enabled and take over */
+ if (priv->pos == 1 && rcar_i2c_dma(priv))
return;
if (priv->pos < msg->len) {
@@ -494,13 +575,6 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
*/
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
priv->pos++;
-
- /*
- * Try to use DMA to transmit the rest of the data if
- * address transfer phase just finished.
- */
- if (msr & MAT)
- rcar_i2c_dma(priv);
} else {
/*
* The last data was pushed to ICRXTX on _PREV_ empty irq.
@@ -510,31 +584,32 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
* [ICRXTX] -> [SHIFT] -> [I2C bus]
*/
- if (priv->flags & ID_LAST_MSG) {
+ if (priv->flags & ID_LAST_MSG)
/*
* If current msg is the _LAST_ msg,
* prepare stop condition here.
* ID_DONE will be set on STOP irq.
*/
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
- } else {
+ else
rcar_i2c_next_msg(priv);
- return;
- }
}
- rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
+ rcar_i2c_clear_irq(priv, irqs_to_clear);
}
static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
+ bool recv_len_init = priv->pos == 0 && msg->flags & I2C_M_RECV_LEN;
+ u32 irqs_to_clear = MDR;
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
if (!(msr & MDR))
return;
if (msr & MAT) {
+ irqs_to_clear |= MAT;
/*
* Address transfer phase finished, but no data at this point.
* Try to use DMA to receive data.
@@ -542,30 +617,48 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
rcar_i2c_dma(priv);
} else if (priv->pos < msg->len) {
/* get received data */
- msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
+ u8 data = rcar_i2c_read(priv, ICRXTX);
+
+ msg->buf[priv->pos] = data;
+ if (recv_len_init) {
+ if (data == 0 || data > I2C_SMBUS_BLOCK_MAX) {
+ priv->flags |= ID_DONE | ID_EPROTO;
+ return;
+ }
+ msg->len += msg->buf[0];
+ /* Enough data for DMA? */
+ if (rcar_i2c_dma(priv))
+ return;
+ /* new length after RECV_LEN now properly initialized */
+ recv_len_init = false;
+ }
priv->pos++;
}
- /* If next received data is the _LAST_, go to new phase. */
- if (priv->pos + 1 == msg->len) {
+ /*
+ * If next received data is the _LAST_ and we are not waiting for a new
+ * length because of RECV_LEN, then go to a new phase.
+ */
+ if (priv->pos + 1 == msg->len && !recv_len_init) {
if (priv->flags & ID_LAST_MSG) {
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
} else {
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
- priv->flags |= ID_P_REP_AFTER_RD;
+ priv->flags |= ID_REP_AFTER_RD;
}
}
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
rcar_i2c_next_msg(priv);
- else
- rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
+
+ rcar_i2c_clear_irq(priv, irqs_to_clear);
}
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
{
u32 ssr_raw, ssr_filtered;
u8 value;
+ int ret;
ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff;
ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER);
@@ -581,29 +674,37 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICRXTX, value);
rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR);
} else {
- i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ if (ret)
+ priv->slave_flags |= ID_SLAVE_NACK;
+
rcar_i2c_read(priv, ICRXTX); /* dummy read */
rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR);
}
- rcar_i2c_write(priv, ICSSR, ~SAR & 0xff);
+ /* Clear SSR, too, because of old STOPs to other clients than us */
+ rcar_i2c_write(priv, ICSSR, ~(SAR | SSR) & 0xff);
}
/* master sent stop */
if (ssr_filtered & SSR) {
i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
- rcar_i2c_write(priv, ICSIER, SAR | SSR);
+ rcar_i2c_write(priv, ICSCR, SIE | SDBS); /* clear our NACK */
+ priv->slave_flags &= ~ID_SLAVE_NACK;
+ rcar_i2c_write(priv, ICSIER, SAR);
rcar_i2c_write(priv, ICSSR, ~SSR & 0xff);
}
/* master wants to write to us */
if (ssr_filtered & SDR) {
- int ret;
-
value = rcar_i2c_read(priv, ICRXTX);
ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
- /* Send NACK in case of error */
- rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0));
+ if (ret)
+ priv->slave_flags |= ID_SLAVE_NACK;
+
+ /* Send NACK in case of error, but it will come 1 byte late :( */
+ rcar_i2c_write(priv, ICSCR, SIE | SDBS |
+ (priv->slave_flags & ID_SLAVE_NACK ? FNA : 0));
rcar_i2c_write(priv, ICSSR, ~SDR & 0xff);
}
@@ -617,21 +718,19 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
return true;
}
-static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
+/*
+ * This driver has a lock-free design because there are IP cores (at least
+ * R-Car Gen2) which have an inherent race condition in their hardware design.
+ * There, we need to switch to RCAR_BUS_PHASE_DATA as soon as possible after
+ * the interrupt was generated, otherwise an unwanted repeated message gets
+ * generated. It turned out that taking a spinlock at the beginning of the ISR
+ * was already causing repeated messages. Thus, this driver was converted to
+ * the now lockless behaviour. Please keep this in mind when hacking the driver.
+ * R-Car Gen3 seems to have this fixed but earlier versions than R-Car Gen2 are
+ * likely affected. Therefore, we have different interrupt handler entries.
+ */
+static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
{
- struct rcar_i2c_priv *priv = ptr;
- u32 msr, val;
-
- /* Clear START or STOP immediately, except for REPSTART after read */
- if (likely(!(priv->flags & ID_P_REP_AFTER_RD))) {
- val = rcar_i2c_read(priv, ICMCR);
- rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
- }
-
- msr = rcar_i2c_read(priv, ICMSR);
-
- /* Only handle interrupts that are currently enabled */
- msr &= rcar_i2c_read(priv, ICMIER);
if (!msr) {
if (rcar_i2c_slave_irq(priv))
return IRQ_HANDLED;
@@ -648,7 +747,8 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
/* Nack */
if (msr & MNR) {
/* HW automatically sends STOP after received NACK */
- rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
+ if (priv->flags & ID_P_NOT_ATOMIC)
+ rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
priv->flags |= ID_NACK;
goto out;
}
@@ -669,12 +769,50 @@ out:
if (priv->flags & ID_DONE) {
rcar_i2c_write(priv, ICMIER, 0);
rcar_i2c_write(priv, ICMSR, 0);
- wake_up(&priv->wait);
+ if (priv->flags & ID_P_NOT_ATOMIC)
+ wake_up(&priv->wait);
}
return IRQ_HANDLED;
}
+static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
+{
+ struct rcar_i2c_priv *priv = ptr;
+ u32 msr;
+
+ /* Clear START or STOP immediately, except for REPSTART after read */
+ if (likely(!(priv->flags & ID_REP_AFTER_RD)))
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
+
+ /* Only handle interrupts that are currently enabled */
+ msr = rcar_i2c_read(priv, ICMSR);
+ if (priv->flags & ID_P_NOT_ATOMIC)
+ msr &= rcar_i2c_read(priv, ICMIER);
+
+ return rcar_i2c_irq(irq, priv, msr);
+}
+
+static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
+{
+ struct rcar_i2c_priv *priv = ptr;
+ u32 msr;
+
+ /* Only handle interrupts that are currently enabled */
+ msr = rcar_i2c_read(priv, ICMSR);
+ if (priv->flags & ID_P_NOT_ATOMIC)
+ msr &= rcar_i2c_read(priv, ICMIER);
+
+ /*
+ * Clear START or STOP immediately, except for REPSTART after read or
+ * if a spurious interrupt was detected.
+ */
+ if (likely(!(priv->flags & ID_REP_AFTER_RD) && msr))
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
+
+ return rcar_i2c_irq(irq, priv, msr);
+}
+
static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
enum dma_transfer_direction dir,
dma_addr_t port_addr)
@@ -752,20 +890,18 @@ static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
/* I2C is a special case, we need to poll the status of a reset */
static int rcar_i2c_do_reset(struct rcar_i2c_priv *priv)
{
- int i, ret;
+ int ret;
+
+ /* Don't reset if a slave instance is currently running */
+ if (priv->slave)
+ return -EISCONN;
ret = reset_control_reset(priv->rstc);
if (ret)
return ret;
- for (i = 0; i < LOOP_TIMEOUT; i++) {
- ret = reset_control_status(priv->rstc);
- if (ret == 0)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
+ return read_poll_timeout_atomic(reset_control_status, ret, ret == 0, 1,
+ 100, false, priv->rstc);
}
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
@@ -777,6 +913,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
int i, ret;
long time_left;
+ priv->flags |= ID_P_NOT_ATOMIC;
+
pm_runtime_get_sync(dev);
/* Check bus state before init otherwise bus busy info will be lost */
@@ -784,14 +922,12 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
if (ret < 0)
goto out;
- /* Gen3 needs a reset before allowing RXDMA once */
- if (priv->devtype == I2C_RCAR_GEN3) {
- priv->flags |= ID_P_NO_RXDMA;
- if (!IS_ERR(priv->rstc)) {
- ret = rcar_i2c_do_reset(priv);
- if (ret == 0)
- priv->flags &= ~ID_P_NO_RXDMA;
- }
+ /* Gen3+ needs a reset. That also allows RXDMA once */
+ if (priv->devtype >= I2C_RCAR_GEN3) {
+ ret = rcar_i2c_do_reset(priv);
+ if (ret)
+ goto out;
+ priv->flags &= ~ID_P_NO_RXDMA;
}
rcar_i2c_init(priv);
@@ -799,18 +935,14 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
for (i = 0; i < num; i++)
rcar_i2c_request_dma(priv, msgs + i);
- /* init first message */
- priv->msg = msgs;
- priv->msgs_left = num;
- priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
- rcar_i2c_prepare_msg(priv);
+ rcar_i2c_first_msg(priv, msgs, num);
time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
num * adap->timeout);
/* cleanup DMA if it couldn't complete properly due to an error */
if (priv->dma_direction != DMA_NONE)
- rcar_i2c_cleanup_dma(priv);
+ rcar_i2c_cleanup_dma(priv, true);
if (!time_left) {
rcar_i2c_init(priv);
@@ -819,6 +951,67 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
ret = -ENXIO;
} else if (priv->flags & ID_ARBLOST) {
ret = -EAGAIN;
+ } else if (priv->flags & ID_EPROTO) {
+ ret = -EPROTO;
+ } else {
+ ret = num - priv->msgs_left; /* The number of transfer */
+ }
+out:
+ pm_runtime_put(dev);
+
+ if (ret < 0 && ret != -ENXIO)
+ dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+ return ret;
+}
+
+static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ unsigned long j;
+ bool time_left;
+ int ret;
+
+ priv->flags &= ~ID_P_NOT_ATOMIC;
+
+ pm_runtime_get_sync(dev);
+
+ /* Check bus state before init otherwise bus busy info will be lost */
+ ret = rcar_i2c_bus_barrier(priv);
+ if (ret < 0)
+ goto out;
+
+ rcar_i2c_init(priv);
+ rcar_i2c_first_msg(priv, msgs, num);
+
+ j = jiffies + num * adap->timeout;
+ do {
+ u32 msr = rcar_i2c_read(priv, ICMSR);
+
+ msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP;
+
+ if (msr) {
+ if (priv->devtype < I2C_RCAR_GEN3)
+ rcar_i2c_gen2_irq(0, priv);
+ else
+ rcar_i2c_gen3_irq(0, priv);
+ }
+
+ time_left = time_before_eq(jiffies, j);
+ } while (!(priv->flags & ID_DONE) && time_left);
+
+ if (!time_left) {
+ rcar_i2c_init(priv);
+ ret = -ETIMEDOUT;
+ } else if (priv->flags & ID_NACK) {
+ ret = -ENXIO;
+ } else if (priv->flags & ID_ARBLOST) {
+ ret = -EAGAIN;
+ } else if (priv->flags & ID_EPROTO) {
+ ret = -EPROTO;
} else {
ret = num - priv->msgs_left; /* The number of transfer */
}
@@ -847,7 +1040,7 @@ static int rcar_reg_slave(struct i2c_client *slave)
priv->slave = slave;
rcar_i2c_write(priv, ICSAR, slave->addr);
rcar_i2c_write(priv, ICSSR, 0);
- rcar_i2c_write(priv, ICSIER, SAR | SSR);
+ rcar_i2c_write(priv, ICSIER, SAR);
rcar_i2c_write(priv, ICSCR, SIE | SDBS);
return 0;
@@ -859,8 +1052,10 @@ static int rcar_unreg_slave(struct i2c_client *slave)
WARN_ON(!priv->slave);
- rcar_i2c_write(priv, ICSIER, 0);
- rcar_i2c_write(priv, ICSCR, 0);
+ /* ensure no irq is running before clearing ptr */
+ disable_irq(priv->irq);
+ rcar_i2c_reset_slave(priv);
+ enable_irq(priv->irq);
priv->slave = NULL;
@@ -871,21 +1066,29 @@ static int rcar_unreg_slave(struct i2c_client *slave)
static u32 rcar_i2c_func(struct i2c_adapter *adap)
{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+
/*
* This HW can't do:
* I2C_SMBUS_QUICK (setting FSB during START didn't work)
* I2C_M_NOSTART (automatically sends address after START)
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
*/
- return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
- (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ u32 func = I2C_FUNC_I2C | I2C_FUNC_SLAVE |
+ (I2C_FUNC_SMBUS_EMUL_ALL & ~I2C_FUNC_SMBUS_QUICK);
+
+ if (priv->flags & ID_P_HOST_NOTIFY)
+ func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
+
+ return func;
}
static const struct i2c_algorithm rcar_i2c_algo = {
- .master_xfer = rcar_i2c_master_xfer,
- .functionality = rcar_i2c_func,
- .reg_slave = rcar_reg_slave,
- .unreg_slave = rcar_unreg_slave,
+ .xfer = rcar_i2c_master_xfer,
+ .xfer_atomic = rcar_i2c_master_xfer_atomic,
+ .functionality = rcar_i2c_func,
+ .reg_slave = rcar_reg_slave,
+ .unreg_slave = rcar_unreg_slave,
};
static const struct i2c_adapter_quirks rcar_i2c_quirks = {
@@ -902,10 +1105,12 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 },
{ .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 },
- { .compatible = "renesas,i2c-rcar", .data = (void *)I2C_RCAR_GEN1 }, /* Deprecated */
+ /* S4 has no FM+ bit */
+ { .compatible = "renesas,i2c-r8a779f0", .data = (void *)I2C_RCAR_GEN3 },
{ .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
+ { .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN4 },
{},
};
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
@@ -915,8 +1120,12 @@ static int rcar_i2c_probe(struct platform_device *pdev)
struct rcar_i2c_priv *priv;
struct i2c_adapter *adap;
struct device *dev = &pdev->dev;
- struct i2c_timings i2c_t;
- int irq, ret;
+ unsigned long irqflags = 0;
+ irqreturn_t (*irqhandler)(int irq, void *ptr) = rcar_i2c_gen3_irq;
+ int ret;
+
+ /* Otherwise logic will break because some bytes must always use PIO */
+ BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length");
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
if (!priv)
@@ -928,9 +1137,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
- priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- priv->io = devm_ioremap_resource(dev, priv->res);
+ priv->io = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->res);
if (IS_ERR(priv->io))
return PTR_ERR(priv->io);
@@ -947,9 +1154,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
adap->bus_recovery_info = &rcar_i2c_bri;
adap->quirks = &rcar_i2c_quirks;
i2c_set_adapdata(adap, priv);
- strlcpy(adap->name, pdev->name, sizeof(adap->name));
-
- i2c_parse_fw_timings(dev, &i2c_t, false);
+ strscpy(adap->name, pdev->name, sizeof(adap->name));
/* Init DMA */
sg_init_table(&priv->sg, 1);
@@ -959,68 +1164,121 @@ static int rcar_i2c_probe(struct platform_device *pdev)
/* Activate device for clock calculation */
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- ret = rcar_i2c_clock_calculate(priv, &i2c_t);
- if (ret < 0)
- goto out_pm_put;
-
- if (priv->devtype == I2C_RCAR_GEN3) {
- priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (!IS_ERR(priv->rstc)) {
- ret = reset_control_status(priv->rstc);
- if (ret < 0)
- priv->rstc = ERR_PTR(-ENOTSUPP);
- }
+ ret = rcar_i2c_clock_calculate(priv);
+ if (ret < 0) {
+ pm_runtime_put(dev);
+ goto out_pm_disable;
}
+ /* Bring hardware to known state */
+ rcar_i2c_init(priv);
+ rcar_i2c_reset_slave(priv);
+
/* Stay always active when multi-master to keep arbitration working */
if (of_property_read_bool(dev->of_node, "multi-master"))
priv->flags |= ID_P_PM_BLOCKED;
else
pm_runtime_put(dev);
+ if (of_property_read_bool(dev->of_node, "smbus"))
+ priv->flags |= ID_P_HOST_NOTIFY;
+
+ if (priv->devtype < I2C_RCAR_GEN3) {
+ irqflags |= IRQF_NO_THREAD;
+ irqhandler = rcar_i2c_gen2_irq;
+ } else {
+ /* R-Car Gen3+ needs a reset before every transfer */
+ priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(priv->rstc)) {
+ ret = PTR_ERR(priv->rstc);
+ goto out_pm_put;
+ }
+
+ ret = reset_control_status(priv->rstc);
+ if (ret < 0)
+ goto out_pm_put;
+
+ /* hard reset disturbs HostNotify local target, so disable it */
+ priv->flags &= ~ID_P_HOST_NOTIFY;
+ }
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0, dev_name(dev), priv);
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto out_pm_put;
+ priv->irq = ret;
+ ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv);
if (ret < 0) {
- dev_err(dev, "cannot get irq %d\n", irq);
- goto out_pm_disable;
+ dev_err(dev, "cannot get irq %d\n", priv->irq);
+ goto out_pm_put;
}
platform_set_drvdata(pdev, priv);
ret = i2c_add_numbered_adapter(adap);
if (ret < 0)
- goto out_pm_disable;
+ goto out_pm_put;
+
+ if (priv->flags & ID_P_HOST_NOTIFY) {
+ priv->host_notify_client = i2c_new_slave_host_notify_device(adap);
+ if (IS_ERR(priv->host_notify_client)) {
+ ret = PTR_ERR(priv->host_notify_client);
+ goto out_del_device;
+ }
+ }
dev_info(dev, "probed\n");
return 0;
+ out_del_device:
+ i2c_del_adapter(&priv->adap);
out_pm_put:
- pm_runtime_put(dev);
+ if (priv->flags & ID_P_PM_BLOCKED)
+ pm_runtime_put(dev);
out_pm_disable:
pm_runtime_disable(dev);
return ret;
}
-static int rcar_i2c_remove(struct platform_device *pdev)
+static void rcar_i2c_remove(struct platform_device *pdev)
{
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
+ if (priv->host_notify_client)
+ i2c_free_slave_host_notify_device(priv->host_notify_client);
i2c_del_adapter(&priv->adap);
rcar_i2c_release_dma(priv);
if (priv->flags & ID_P_PM_BLOCKED)
pm_runtime_put(dev);
pm_runtime_disable(dev);
+}
+
+static int rcar_i2c_suspend(struct device *dev)
+{
+ struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&priv->adap);
+ return 0;
+}
+static int rcar_i2c_resume(struct device *dev)
+{
+ struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_resumed(&priv->adap);
return 0;
}
+static const struct dev_pm_ops rcar_i2c_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume)
+};
+
static struct platform_driver rcar_i2c_driver = {
.driver = {
.name = "i2c-rcar",
.of_match_table = rcar_i2c_dt_ids,
+ .pm = pm_sleep_ptr(&rcar_i2c_pm_ops),
},
.probe = rcar_i2c_probe,
.remove = rcar_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index b75ff144b570..3e8f126cb7f7 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -12,9 +12,9 @@
*
* 1) The main xfer routine kicks off a transmission by putting the start bit
* (or repeated start) on the bus and enabling the transmit interrupt (TIE)
- * since we need to send the slave address + RW bit in every case.
+ * since we need to send the target address + RW bit in every case.
*
- * 2) TIE sends slave address + RW bit and selects how to continue.
+ * 2) TIE sends target address + RW bit and selects how to continue.
*
* 3a) Write case: We keep utilizing TIE as long as we have data to send. If we
* are done, we switch over to the transmission done interrupt (TEIE) and mark
@@ -34,57 +34,80 @@
* Also check the comments in the interrupt routines for some gory details.
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-
-#define RIIC_ICCR1 0x00
-#define RIIC_ICCR2 0x04
-#define RIIC_ICMR1 0x08
-#define RIIC_ICMR3 0x10
-#define RIIC_ICSER 0x18
-#define RIIC_ICIER 0x1c
-#define RIIC_ICSR2 0x24
-#define RIIC_ICBRL 0x34
-#define RIIC_ICBRH 0x38
-#define RIIC_ICDRT 0x3c
-#define RIIC_ICDRR 0x40
-
-#define ICCR1_ICE 0x80
-#define ICCR1_IICRST 0x40
-#define ICCR1_SOWP 0x10
-
-#define ICCR2_BBSY 0x80
-#define ICCR2_SP 0x08
-#define ICCR2_RS 0x04
-#define ICCR2_ST 0x02
-
-#define ICMR1_CKS_MASK 0x70
-#define ICMR1_BCWP 0x08
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/time.h>
+
+#define ICCR1_ICE BIT(7)
+#define ICCR1_IICRST BIT(6)
+#define ICCR1_SOWP BIT(4)
+#define ICCR1_SCLO BIT(3)
+#define ICCR1_SDAO BIT(2)
+#define ICCR1_SCLI BIT(1)
+#define ICCR1_SDAI BIT(0)
+
+#define ICCR2_BBSY BIT(7)
+#define ICCR2_SP BIT(3)
+#define ICCR2_RS BIT(2)
+#define ICCR2_ST BIT(1)
+
+#define ICMR1_CKS_MASK GENMASK(6, 4)
+#define ICMR1_BCWP BIT(3)
#define ICMR1_CKS(_x) ((((_x) << 4) & ICMR1_CKS_MASK) | ICMR1_BCWP)
-#define ICMR3_RDRFS 0x20
-#define ICMR3_ACKWP 0x10
-#define ICMR3_ACKBT 0x08
+#define ICMR3_RDRFS BIT(5)
+#define ICMR3_ACKWP BIT(4)
+#define ICMR3_ACKBT BIT(3)
+
+#define ICFER_FMPE BIT(7)
-#define ICIER_TIE 0x80
-#define ICIER_TEIE 0x40
-#define ICIER_RIE 0x20
-#define ICIER_NAKIE 0x10
-#define ICIER_SPIE 0x08
+#define ICIER_TIE BIT(7)
+#define ICIER_TEIE BIT(6)
+#define ICIER_RIE BIT(5)
+#define ICIER_NAKIE BIT(4)
+#define ICIER_SPIE BIT(3)
-#define ICSR2_NACKF 0x10
+#define ICSR2_NACKF BIT(4)
+#define ICSR2_STOP BIT(3)
-#define ICBR_RESERVED 0xe0 /* Should be 1 on writes */
+#define ICBR_RESERVED GENMASK(7, 5) /* Should be 1 on writes */
#define RIIC_INIT_MSG -1
+enum riic_reg_list {
+ RIIC_ICCR1 = 0,
+ RIIC_ICCR2,
+ RIIC_ICMR1,
+ RIIC_ICMR3,
+ RIIC_ICFER,
+ RIIC_ICSER,
+ RIIC_ICIER,
+ RIIC_ICSR2,
+ RIIC_ICBRL,
+ RIIC_ICBRH,
+ RIIC_ICDRT,
+ RIIC_ICDRR,
+ RIIC_REG_END,
+};
+
+struct riic_of_data {
+ const u8 *regs;
+ const struct riic_irq_desc *irqs;
+ u8 num_irqs;
+ bool fast_mode_plus;
+};
+
struct riic_dev {
void __iomem *base;
u8 *buf;
@@ -92,9 +115,12 @@ struct riic_dev {
int bytes_left;
int err;
int is_last;
+ const struct riic_of_data *info;
struct completion msg_done;
struct i2c_adapter adapter;
struct clk *clk;
+ struct reset_control *rstc;
+ struct i2c_timings i2c_t;
};
struct riic_irq_desc {
@@ -103,31 +129,61 @@ struct riic_irq_desc {
char *name;
};
+static inline void riic_writeb(struct riic_dev *riic, u8 val, u8 offset)
+{
+ writeb(val, riic->base + riic->info->regs[offset]);
+}
+
+static inline u8 riic_readb(struct riic_dev *riic, u8 offset)
+{
+ return readb(riic->base + riic->info->regs[offset]);
+}
+
static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u8 reg)
{
- writeb((readb(riic->base + reg) & ~clear) | set, riic->base + reg);
+ riic_writeb(riic, (riic_readb(riic, reg) & ~clear) | set, reg);
+}
+
+static int riic_bus_barrier(struct riic_dev *riic)
+{
+ int ret;
+ u8 val;
+
+ /*
+ * The SDA line can still be low even when BBSY = 0. Therefore, after checking
+ * the BBSY flag, also verify that the SDA and SCL lines are not being held low.
+ */
+ ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR2], val,
+ !(val & ICCR2_BBSY), 10, riic->adapter.timeout);
+ if (ret)
+ return i2c_recover_bus(&riic->adapter);
+
+ if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
+ (ICCR1_SDAI | ICCR1_SCLI))
+ return i2c_recover_bus(&riic->adapter);
+
+ return 0;
}
static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct riic_dev *riic = i2c_get_adapdata(adap);
+ struct device *dev = adap->dev.parent;
unsigned long time_left;
int i, ret;
u8 start_bit;
- ret = clk_prepare_enable(riic->clk);
+ ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
- if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) {
- riic->err = -EBUSY;
+ riic->err = riic_bus_barrier(riic);
+ if (riic->err)
goto out;
- }
reinit_completion(&riic->msg_done);
- riic->err = 0;
- writeb(0, riic->base + RIIC_ICSR2);
+ riic_writeb(riic, 0, RIIC_ICSR2);
for (i = 0, start_bit = ICCR2_ST; i < num; i++) {
riic->bytes_left = RIIC_INIT_MSG;
@@ -135,9 +191,9 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
riic->msg = &msgs[i];
riic->is_last = (i == num - 1);
- writeb(ICIER_NAKIE | ICIER_TIE, riic->base + RIIC_ICIER);
+ riic_writeb(riic, ICIER_NAKIE | ICIER_TIE, RIIC_ICIER);
- writeb(start_bit, riic->base + RIIC_ICCR2);
+ riic_writeb(riic, start_bit, RIIC_ICCR2);
time_left = wait_for_completion_timeout(&riic->msg_done, riic->adapter.timeout);
if (time_left == 0)
@@ -150,7 +206,7 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
}
out:
- clk_disable_unprepare(riic->clk);
+ pm_runtime_put_autosuspend(dev);
return riic->err ?: num;
}
@@ -191,7 +247,7 @@ static irqreturn_t riic_tdre_isr(int irq, void *data)
* value could be moved to the shadow shift register right away. So
* this must be after updates to ICIER (where we want to disable TIE)!
*/
- writeb(val, riic->base + RIIC_ICDRT);
+ riic_writeb(riic, val, RIIC_ICDRT);
return IRQ_HANDLED;
}
@@ -200,9 +256,10 @@ static irqreturn_t riic_tend_isr(int irq, void *data)
{
struct riic_dev *riic = data;
- if (readb(riic->base + RIIC_ICSR2) & ICSR2_NACKF) {
+ if (riic_readb(riic, RIIC_ICSR2) & ICSR2_NACKF) {
/* We got a NACKIE */
- readb(riic->base + RIIC_ICDRR); /* dummy read */
+ riic_readb(riic, RIIC_ICDRR); /* dummy read */
+ riic_clear_set_bit(riic, ICSR2_NACKF, 0, RIIC_ICSR2);
riic->err = -ENXIO;
} else if (riic->bytes_left) {
return IRQ_NONE;
@@ -210,7 +267,7 @@ static irqreturn_t riic_tend_isr(int irq, void *data)
if (riic->is_last || riic->err) {
riic_clear_set_bit(riic, ICIER_TEIE, ICIER_SPIE, RIIC_ICIER);
- writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
+ riic_writeb(riic, ICCR2_SP, RIIC_ICCR2);
} else {
/* Transfer is complete, but do not send STOP */
riic_clear_set_bit(riic, ICIER_TEIE, 0, RIIC_ICIER);
@@ -229,7 +286,7 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data)
if (riic->bytes_left == RIIC_INIT_MSG) {
riic->bytes_left = riic->msg->len;
- readb(riic->base + RIIC_ICDRR); /* dummy read */
+ riic_readb(riic, RIIC_ICDRR); /* dummy read */
return IRQ_HANDLED;
}
@@ -237,7 +294,7 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data)
/* STOP must come before we set ACKBT! */
if (riic->is_last) {
riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER);
- writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
+ riic_writeb(riic, ICCR2_SP, RIIC_ICCR2);
}
riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3);
@@ -247,7 +304,7 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data)
}
/* Reading acks the RIE interrupt */
- *riic->buf = readb(riic->base + RIIC_ICDRR);
+ *riic->buf = riic_readb(riic, RIIC_ICDRR);
riic->buf++;
riic->bytes_left--;
@@ -259,43 +316,55 @@ static irqreturn_t riic_stop_isr(int irq, void *data)
struct riic_dev *riic = data;
/* read back registers to confirm writes have fully propagated */
- writeb(0, riic->base + RIIC_ICSR2);
- readb(riic->base + RIIC_ICSR2);
- writeb(0, riic->base + RIIC_ICIER);
- readb(riic->base + RIIC_ICIER);
+ riic_writeb(riic, 0, RIIC_ICSR2);
+ riic_readb(riic, RIIC_ICSR2);
+ riic_writeb(riic, 0, RIIC_ICIER);
+ riic_readb(riic, RIIC_ICIER);
complete(&riic->msg_done);
return IRQ_HANDLED;
}
+static irqreturn_t riic_eei_isr(int irq, void *data)
+{
+ u8 icsr2 = riic_readb(data, RIIC_ICSR2);
+
+ if (icsr2 & ICSR2_NACKF)
+ return riic_tend_isr(irq, data);
+
+ if (icsr2 & ICSR2_STOP)
+ return riic_stop_isr(irq, data);
+
+ return IRQ_NONE;
+}
+
static u32 riic_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm riic_algo = {
- .master_xfer = riic_xfer,
- .functionality = riic_func,
+ .xfer = riic_xfer,
+ .functionality = riic_func,
};
-static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
+static int riic_init_hw(struct riic_dev *riic)
{
int ret;
unsigned long rate;
+ unsigned long ns_per_tick;
int total_ticks, cks, brl, brh;
+ struct i2c_timings *t = &riic->i2c_t;
+ struct device *dev = riic->adapter.dev.parent;
+ bool fast_mode_plus = riic->info->fast_mode_plus;
+ u32 max_freq = fast_mode_plus ? I2C_MAX_FAST_MODE_PLUS_FREQ
+ : I2C_MAX_FAST_MODE_FREQ;
- ret = clk_prepare_enable(riic->clk);
- if (ret)
- return ret;
-
- if (t->bus_freq_hz > 400000) {
- dev_err(&riic->adapter.dev,
- "unsupported bus speed (%dHz). 400000 max\n",
- t->bus_freq_hz);
- clk_disable_unprepare(riic->clk);
- return -EINVAL;
- }
+ if (t->bus_freq_hz > max_freq)
+ return dev_err_probe(dev, -EINVAL,
+ "unsupported bus speed %uHz (%u max)\n",
+ t->bus_freq_hz, max_freq);
rate = clk_get_rate(riic->clk);
@@ -314,9 +383,9 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
* frequency with only 62 clock ticks max (31 high, 31 low).
* Aim for a duty of 60% LOW, 40% HIGH.
*/
- total_ticks = DIV_ROUND_UP(rate, t->bus_freq_hz);
+ total_ticks = DIV_ROUND_UP(rate, t->bus_freq_hz ?: 1);
- for (cks = 0; cks < 7; cks++) {
+ for (cks = 0; cks <= 7; cks++) {
/*
* 60% low time must be less than BRL + 2 + 1
* BRL max register value is 0x1F.
@@ -325,16 +394,13 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
if (brl <= (0x1F + 3))
break;
- total_ticks /= 2;
+ total_ticks = DIV_ROUND_UP(total_ticks, 2);
rate /= 2;
}
- if (brl > (0x1F + 3)) {
- dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
- (unsigned long)t->bus_freq_hz);
- clk_disable_unprepare(riic->clk);
- return -EINVAL;
- }
+ if (brl > (0x1F + 3))
+ return dev_err_probe(dev, -EINVAL, "invalid speed (%uHz). Too slow.\n",
+ t->bus_freq_hz);
brh = total_ticks - brl;
@@ -351,8 +417,9 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
* Remove clock ticks for rise and fall times. Convert ns to clock
* ticks.
*/
- brl -= t->scl_fall_ns / (1000000000 / rate);
- brh -= t->scl_rise_ns / (1000000000 / rate);
+ ns_per_tick = NSEC_PER_SEC / rate;
+ brl -= t->scl_fall_ns / ns_per_tick;
+ brh -= t->scl_rise_ns / ns_per_tick;
/* Adjust for min register values for when SCLE=1 and NFE=1 */
if (brl < 1)
@@ -362,28 +429,79 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
pr_debug("i2c-riic: freq=%lu, duty=%d, fall=%lu, rise=%lu, cks=%d, brl=%d, brh=%d\n",
rate / total_ticks, ((brl + 3) * 100) / (brl + brh + 6),
- t->scl_fall_ns / (1000000000 / rate),
- t->scl_rise_ns / (1000000000 / rate), cks, brl, brh);
+ t->scl_fall_ns / ns_per_tick, t->scl_rise_ns / ns_per_tick, cks, brl, brh);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
/* Changing the order of accessing IICRST and ICE may break things! */
- writeb(ICCR1_IICRST | ICCR1_SOWP, riic->base + RIIC_ICCR1);
+ riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1);
riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1);
- writeb(ICMR1_CKS(cks), riic->base + RIIC_ICMR1);
- writeb(brh | ICBR_RESERVED, riic->base + RIIC_ICBRH);
- writeb(brl | ICBR_RESERVED, riic->base + RIIC_ICBRL);
+ riic_writeb(riic, ICMR1_CKS(cks), RIIC_ICMR1);
+ riic_writeb(riic, brh | ICBR_RESERVED, RIIC_ICBRH);
+ riic_writeb(riic, brl | ICBR_RESERVED, RIIC_ICBRL);
- writeb(0, riic->base + RIIC_ICSER);
- writeb(ICMR3_ACKWP | ICMR3_RDRFS, riic->base + RIIC_ICMR3);
+ riic_writeb(riic, 0, RIIC_ICSER);
+ riic_writeb(riic, ICMR3_ACKWP | ICMR3_RDRFS, RIIC_ICMR3);
- riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
+ if (fast_mode_plus && t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
+ riic_clear_set_bit(riic, 0, ICFER_FMPE, RIIC_ICFER);
- clk_disable_unprepare(riic->clk);
+ riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
+ pm_runtime_put_autosuspend(dev);
return 0;
}
-static struct riic_irq_desc riic_irqs[] = {
+static int riic_get_scl(struct i2c_adapter *adap)
+{
+ struct riic_dev *riic = i2c_get_adapdata(adap);
+
+ return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SCLI);
+}
+
+static int riic_get_sda(struct i2c_adapter *adap)
+{
+ struct riic_dev *riic = i2c_get_adapdata(adap);
+
+ return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI);
+}
+
+static void riic_set_scl(struct i2c_adapter *adap, int val)
+{
+ struct riic_dev *riic = i2c_get_adapdata(adap);
+
+ if (val)
+ riic_clear_set_bit(riic, ICCR1_SOWP, ICCR1_SCLO, RIIC_ICCR1);
+ else
+ riic_clear_set_bit(riic, ICCR1_SOWP | ICCR1_SCLO, 0, RIIC_ICCR1);
+
+ riic_clear_set_bit(riic, 0, ICCR1_SOWP, RIIC_ICCR1);
+}
+
+static void riic_set_sda(struct i2c_adapter *adap, int val)
+{
+ struct riic_dev *riic = i2c_get_adapdata(adap);
+
+ if (val)
+ riic_clear_set_bit(riic, ICCR1_SOWP, ICCR1_SDAO, RIIC_ICCR1);
+ else
+ riic_clear_set_bit(riic, ICCR1_SOWP | ICCR1_SDAO, 0, RIIC_ICCR1);
+
+ riic_clear_set_bit(riic, 0, ICCR1_SOWP, RIIC_ICCR1);
+}
+
+static struct i2c_bus_recovery_info riic_bri = {
+ .recover_bus = i2c_generic_scl_recovery,
+ .get_scl = riic_get_scl,
+ .set_scl = riic_set_scl,
+ .get_sda = riic_get_sda,
+ .set_sda = riic_set_sda,
+};
+
+static const struct riic_irq_desc riic_irqs[] = {
{ .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
{ .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
{ .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" },
@@ -391,83 +509,220 @@ static struct riic_irq_desc riic_irqs[] = {
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
};
+static const struct riic_irq_desc riic_rzt2h_irqs[] = {
+ { .res_num = 0, .isr = riic_eei_isr, .name = "riic-eei" },
+ { .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rxi" },
+ { .res_num = 2, .isr = riic_tdre_isr, .name = "riic-txi" },
+ { .res_num = 3, .isr = riic_tend_isr, .name = "riic-tei" },
+};
+
static int riic_i2c_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct riic_dev *riic;
struct i2c_adapter *adap;
- struct resource *res;
- struct i2c_timings i2c_t;
int i, ret;
- riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
+ riic = devm_kzalloc(dev, sizeof(*riic), GFP_KERNEL);
if (!riic)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- riic->base = devm_ioremap_resource(&pdev->dev, res);
+ riic->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(riic->base))
return PTR_ERR(riic->base);
- riic->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(riic->clk)) {
- dev_err(&pdev->dev, "missing controller clock");
- return PTR_ERR(riic->clk);
- }
+ riic->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(riic->clk))
+ return dev_err_probe(dev, PTR_ERR(riic->clk),
+ "missing controller clock");
- for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, riic_irqs[i].res_num);
- if (!res)
- return -ENODEV;
+ riic->rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(riic->rstc))
+ return dev_err_probe(dev, PTR_ERR(riic->rstc),
+ "failed to acquire deasserted reset\n");
- ret = devm_request_irq(&pdev->dev, res->start, riic_irqs[i].isr,
- 0, riic_irqs[i].name, riic);
- if (ret) {
- dev_err(&pdev->dev, "failed to request irq %s\n", riic_irqs[i].name);
- return ret;
- }
+ riic->info = of_device_get_match_data(dev);
+
+ for (i = 0; i < riic->info->num_irqs; i++) {
+ const struct riic_irq_desc *irq_desc;
+ int irq;
+
+ irq_desc = &riic->info->irqs[i];
+ irq = platform_get_irq(pdev, irq_desc->res_num);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, irq_desc->isr, 0, irq_desc->name, riic);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq %s\n",
+ irq_desc->name);
}
+
adap = &riic->adapter;
i2c_set_adapdata(adap, riic);
- strlcpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name));
+ strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name));
adap->owner = THIS_MODULE;
adap->algo = &riic_algo;
- adap->dev.parent = &pdev->dev;
- adap->dev.of_node = pdev->dev.of_node;
+ adap->dev.parent = dev;
+ adap->dev.of_node = dev->of_node;
+ adap->bus_recovery_info = &riic_bri;
init_completion(&riic->msg_done);
- i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
+ i2c_parse_fw_timings(dev, &riic->i2c_t, true);
- ret = riic_init_hw(riic, &i2c_t);
- if (ret)
- return ret;
+ /* Default 0 to save power. Can be overridden via sysfs for lower latency. */
+ pm_runtime_set_autosuspend_delay(dev, 0);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ ret = riic_init_hw(riic);
+ if (ret)
+ goto out;
ret = i2c_add_adapter(adap);
if (ret)
- return ret;
+ goto out;
platform_set_drvdata(pdev, riic);
- dev_info(&pdev->dev, "registered with %dHz bus speed\n",
- i2c_t.bus_freq_hz);
+ dev_info(dev, "registered with %dHz bus speed\n", riic->i2c_t.bus_freq_hz);
return 0;
+
+out:
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+ return ret;
}
-static int riic_i2c_remove(struct platform_device *pdev)
+static void riic_i2c_remove(struct platform_device *pdev)
{
struct riic_dev *riic = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int ret;
- writeb(0, riic->base + RIIC_ICIER);
+ ret = pm_runtime_resume_and_get(dev);
+ if (!ret) {
+ riic_writeb(riic, 0, RIIC_ICIER);
+ pm_runtime_put(dev);
+ }
i2c_del_adapter(&riic->adapter);
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const u8 riic_rz_a_regs[RIIC_REG_END] = {
+ [RIIC_ICCR1] = 0x00,
+ [RIIC_ICCR2] = 0x04,
+ [RIIC_ICMR1] = 0x08,
+ [RIIC_ICMR3] = 0x10,
+ [RIIC_ICFER] = 0x14,
+ [RIIC_ICSER] = 0x18,
+ [RIIC_ICIER] = 0x1c,
+ [RIIC_ICSR2] = 0x24,
+ [RIIC_ICBRL] = 0x34,
+ [RIIC_ICBRH] = 0x38,
+ [RIIC_ICDRT] = 0x3c,
+ [RIIC_ICDRR] = 0x40,
+};
+
+static const struct riic_of_data riic_rz_a_info = {
+ .regs = riic_rz_a_regs,
+ .irqs = riic_irqs,
+ .num_irqs = ARRAY_SIZE(riic_irqs),
+ .fast_mode_plus = true,
+};
+
+static const struct riic_of_data riic_rz_a1h_info = {
+ .regs = riic_rz_a_regs,
+ .irqs = riic_irqs,
+ .num_irqs = ARRAY_SIZE(riic_irqs),
+};
+
+static const u8 riic_rz_v2h_regs[RIIC_REG_END] = {
+ [RIIC_ICCR1] = 0x00,
+ [RIIC_ICCR2] = 0x01,
+ [RIIC_ICMR1] = 0x02,
+ [RIIC_ICMR3] = 0x04,
+ [RIIC_ICFER] = 0x05,
+ [RIIC_ICSER] = 0x06,
+ [RIIC_ICIER] = 0x07,
+ [RIIC_ICSR2] = 0x09,
+ [RIIC_ICBRL] = 0x10,
+ [RIIC_ICBRH] = 0x11,
+ [RIIC_ICDRT] = 0x12,
+ [RIIC_ICDRR] = 0x13,
+};
+
+static const struct riic_of_data riic_rz_v2h_info = {
+ .regs = riic_rz_v2h_regs,
+ .irqs = riic_irqs,
+ .num_irqs = ARRAY_SIZE(riic_irqs),
+ .fast_mode_plus = true,
+};
+
+static const struct riic_of_data riic_rz_t2h_info = {
+ .regs = riic_rz_v2h_regs,
+ .irqs = riic_rzt2h_irqs,
+ .num_irqs = ARRAY_SIZE(riic_rzt2h_irqs),
+};
+
+static int riic_i2c_suspend(struct device *dev)
+{
+ struct riic_dev *riic = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_suspended(&riic->adapter);
+
+ /* Disable output on SDA, SCL pins. */
+ riic_clear_set_bit(riic, ICCR1_ICE, 0, RIIC_ICCR1);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync(dev);
+
+ return reset_control_assert(riic->rstc);
+}
+
+static int riic_i2c_resume(struct device *dev)
+{
+ struct riic_dev *riic = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_deassert(riic->rstc);
+ if (ret)
+ return ret;
+
+ ret = riic_init_hw(riic);
+ if (ret) {
+ /*
+ * In case this happens there is no way to recover from this
+ * state. The driver will remain loaded. We want to avoid
+ * keeping the reset line de-asserted for no reason.
+ */
+ reset_control_assert(riic->rstc);
+ return ret;
+ }
+
+ i2c_mark_adapter_resumed(&riic->adapter);
return 0;
}
+static const struct dev_pm_ops riic_i2c_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume)
+};
+
static const struct of_device_id riic_i2c_dt_ids[] = {
- { .compatible = "renesas,riic-rz" },
- { /* Sentinel */ },
+ { .compatible = "renesas,riic-r7s72100", .data = &riic_rz_a1h_info, },
+ { .compatible = "renesas,riic-r9a09g057", .data = &riic_rz_v2h_info },
+ { .compatible = "renesas,riic-r9a09g077", .data = &riic_rz_t2h_info },
+ { .compatible = "renesas,riic-rz", .data = &riic_rz_a_info },
+ { /* Sentinel */ }
};
static struct platform_driver riic_i2c_driver = {
@@ -476,6 +731,7 @@ static struct platform_driver riic_i2c_driver = {
.driver = {
.name = "i2c-riic",
.of_match_table = riic_i2c_dt_ids,
+ .pm = pm_ptr(&riic_i2c_pm_ops),
},
};
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index b8a2728dd4b6..d4e9196445c0 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -1,18 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for I2C adapter in Rockchip RK3xxx SoC
*
* Max Schwarz <max.schwarz@online.de>
* based on the patches by Rockchip Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -30,8 +28,8 @@
/* Register Map */
#define REG_CON 0x00 /* control register */
#define REG_CLKDIV 0x04 /* clock divisor register */
-#define REG_MRXADDR 0x08 /* slave address for REGISTER_TX */
-#define REG_MRXRADDR 0x0c /* slave register address for REGISTER_TX */
+#define REG_MRXADDR 0x08 /* target address for REGISTER_TX */
+#define REG_MRXRADDR 0x0c /* target register address for REGISTER_TX */
#define REG_MTXCNT 0x10 /* number of bytes to be transmitted */
#define REG_MRXCNT 0x14 /* number of bytes to be received */
#define REG_IEN 0x18 /* interrupt enable */
@@ -70,8 +68,8 @@ enum {
/* REG_IEN/REG_IPD bits */
#define REG_INT_BTF BIT(0) /* a byte was transmitted */
#define REG_INT_BRF BIT(1) /* a byte was received */
-#define REG_INT_MBTF BIT(2) /* master data transmit finished */
-#define REG_INT_MBRF BIT(3) /* master data receive finished */
+#define REG_INT_MBTF BIT(2) /* controller data transmit finished */
+#define REG_INT_MBRF BIT(3) /* controller data receive finished */
#define REG_INT_START BIT(4) /* START condition generated */
#define REG_INT_STOP BIT(5) /* STOP condition generated */
#define REG_INT_NAKRCV BIT(6) /* NACK received */
@@ -82,7 +80,7 @@ enum {
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
/**
- * struct i2c_spec_values:
+ * struct i2c_spec_values - I2C specification values for various modes
* @min_hold_start_ns: min hold time (repeated) START condition
* @min_low_ns: min LOW period of the SCL clock
* @min_high_ns: min HIGH period of the SCL cloc
@@ -138,7 +136,7 @@ static const struct i2c_spec_values fast_mode_plus_spec = {
};
/**
- * struct rk3x_i2c_calced_timings:
+ * struct rk3x_i2c_calced_timings - calculated V1 timings
* @div_low: Divider output for low
* @div_high: Divider output for high
* @tuning: Used to adjust setup/hold data time,
@@ -161,7 +159,7 @@ enum rk3x_i2c_state {
};
/**
- * struct rk3x_i2c_soc_data:
+ * struct rk3x_i2c_soc_data - SOC-specific data
* @grf_offset: offset inside the grf regmap for setting the i2c type
* @calc_timings: Callback function for i2c timing information calculated
*/
@@ -180,12 +178,13 @@ struct rk3x_i2c_soc_data {
* @clk: function clk for rk3399 or function & Bus clks for others
* @pclk: Bus clk for rk3399
* @clk_rate_nb: i2c clk rate change notify
+ * @irq: irq number
* @t: I2C known timing information
* @lock: spinlock for the i2c bus
* @wait: the waitqueue to wait for i2c transfer
* @busy: the condition for the event to wait for
* @msg: current i2c message
- * @addr: addr of i2c slave device
+ * @addr: addr of i2c target device
* @mode: mode of i2c transfer
* @is_last_msg: flag determines whether it is the last msg in this transfer
* @state: state of i2c transfer
@@ -202,6 +201,7 @@ struct rk3x_i2c {
struct clk *clk;
struct clk *pclk;
struct notifier_block clk_rate_nb;
+ int irq;
/* Settings */
struct i2c_timings t;
@@ -241,7 +241,8 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
}
/**
- * Generate a START condition, which triggers a REG_INT_START interrupt.
+ * rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt.
+ * @i2c: target controller data
*/
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
{
@@ -260,8 +261,8 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
}
/**
- * Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
- *
+ * rk3x_i2c_stop - Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
+ * @i2c: target controller data
* @error: Error code to return in rk3x_i2c_xfer
*/
static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
@@ -300,7 +301,8 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
}
/**
- * Setup a read according to i2c->msg
+ * rk3x_i2c_prepare_read - Setup a read according to i2c->msg
+ * @i2c: target controller data
*/
static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
{
@@ -331,7 +333,8 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
}
/**
- * Fill the transmit buffer with data from i2c->msg
+ * rk3x_i2c_fill_transmit_buf - Fill the transmit buffer with data from i2c->msg
+ * @i2c: target controller data
*/
static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c)
{
@@ -418,15 +421,15 @@ static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
{
unsigned int i;
unsigned int len = i2c->msg->len - i2c->processed;
- u32 uninitialized_var(val);
+ u32 val;
u8 byte;
/* we only care for MBRF here. */
if (!(ipd & REG_INT_MBRF))
return;
- /* ack interrupt */
- i2c_writel(i2c, REG_INT_MBRF, REG_IPD);
+ /* ack interrupt (read also produces a spurious START flag, clear it too) */
+ i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD);
/* Can only handle a maximum of 32 bytes at a time */
if (len > 32)
@@ -534,30 +537,28 @@ out:
}
/**
- * Get timing values of I2C specification
- *
+ * rk3x_i2c_get_spec - Get timing values of I2C specification
* @speed: Desired SCL frequency
*
- * Returns: Matched i2c spec values.
+ * Return: Matched i2c_spec_values.
*/
static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
{
- if (speed <= 100000)
+ if (speed <= I2C_MAX_STANDARD_MODE_FREQ)
return &standard_mode_spec;
- else if (speed <= 400000)
+ else if (speed <= I2C_MAX_FAST_MODE_FREQ)
return &fast_mode_spec;
else
return &fast_mode_plus_spec;
}
/**
- * Calculate divider values for desired SCL frequency
- *
+ * rk3x_i2c_v0_calc_timings - Calculate divider values for desired SCL frequency
* @clk_rate: I2C input clock rate
* @t: Known I2C timing information
* @t_calc: Caculated rk3x private timings that would be written into regs
*
- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
* a best-effort divider value is returned in divs. If the target rate is
* too high, we silently use the highest possible rate.
*/
@@ -581,8 +582,8 @@ static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate,
int ret = 0;
/* Only support standard-mode and fast-mode */
- if (WARN_ON(t->bus_freq_hz > 400000))
- t->bus_freq_hz = 400000;
+ if (WARN_ON(t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ))
+ t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
/* prevent scl_rate_khz from becoming 0 */
if (WARN_ON(t->bus_freq_hz < 1000))
@@ -712,13 +713,12 @@ static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate,
}
/**
- * Calculate timing values for desired SCL frequency
- *
+ * rk3x_i2c_v1_calc_timings - Calculate timing values for desired SCL frequency
* @clk_rate: I2C input clock rate
* @t: Known I2C timing information
* @t_calc: Caculated rk3x private timings that would be written into regs
*
- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
* a best-effort divider value is returned in divs. If the target rate is
* too high, we silently use the highest possible rate.
* The following formulas are v1's method to calculate timings.
@@ -761,8 +761,8 @@ static int rk3x_i2c_v1_calc_timings(unsigned long clk_rate,
int ret = 0;
/* Support standard-mode, fast-mode and fast-mode plus */
- if (WARN_ON(t->bus_freq_hz > 1000000))
- t->bus_freq_hz = 1000000;
+ if (WARN_ON(t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ))
+ t->bus_freq_hz = I2C_MAX_FAST_MODE_PLUS_FREQ;
/* prevent scl_rate_khz from becoming 0 */
if (WARN_ON(t->bus_freq_hz < 1000))
@@ -962,14 +962,14 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
}
/**
- * Setup I2C registers for an I2C operation specified by msgs, num.
- *
- * Must be called with i2c->lock held.
- *
+ * rk3x_i2c_setup - Setup I2C registers for an I2C operation specified by msgs, num.
+ * @i2c: target controller data
* @msgs: I2C msgs to process
* @num: Number of msgs
*
- * returns: Number of I2C msgs processed or negative in case of error
+ * Must be called with i2c->lock held.
+ *
+ * Return: Number of I2C msgs processed or negative in case of error
*/
static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
{
@@ -979,7 +979,7 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
/*
* The I2C adapter can issue a small (len < 4) write packet before
* reading. This speeds up SMBus-style register reads.
- * The MRXADDR/MRXRADDR hold the slave address and the slave register
+ * The MRXADDR/MRXRADDR hold the target address and the target register
* address in this case.
*/
@@ -1016,7 +1016,7 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
addr |= 1; /* set read bit */
/*
- * We have to transmit the slave addr first. Use
+ * We have to transmit the target addr first. Use
* MOD_REGISTER_TX for that purpose.
*/
i2c->mode = REG_CON_MOD_REGISTER_TX;
@@ -1043,11 +1043,25 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
return ret;
}
-static int rk3x_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
+static int rk3x_i2c_wait_xfer_poll(struct rk3x_i2c *i2c)
+{
+ ktime_t timeout = ktime_add_ms(ktime_get(), WAIT_TIMEOUT);
+
+ while (READ_ONCE(i2c->busy) &&
+ ktime_compare(ktime_get(), timeout) < 0) {
+ udelay(5);
+ rk3x_i2c_irq(0, i2c);
+ }
+
+ return !i2c->busy;
+}
+
+static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num, bool polling)
{
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
- unsigned long timeout, flags;
+ unsigned long flags;
+ long time_left;
u32 val;
int ret = 0;
int i;
@@ -1076,17 +1090,23 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
spin_unlock_irqrestore(&i2c->lock, flags);
- rk3x_i2c_start(i2c);
+ if (!polling) {
+ rk3x_i2c_start(i2c);
- timeout = wait_event_timeout(i2c->wait, !i2c->busy,
- msecs_to_jiffies(WAIT_TIMEOUT));
+ time_left = wait_event_timeout(i2c->wait, !i2c->busy,
+ msecs_to_jiffies(WAIT_TIMEOUT));
+ } else {
+ disable_irq(i2c->irq);
+ rk3x_i2c_start(i2c);
- spin_lock_irqsave(&i2c->lock, flags);
+ time_left = rk3x_i2c_wait_xfer_poll(i2c);
- if (timeout == 0) {
- dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n",
- i2c_readl(i2c, REG_IPD), i2c->state);
+ enable_irq(i2c->irq);
+ }
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ if (time_left == 0) {
/* Force a STOP condition without interrupt */
i2c_writel(i2c, 0, REG_IEN);
val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
@@ -1113,6 +1133,18 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
return ret < 0 ? ret : num;
}
+static int rk3x_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return rk3x_i2c_xfer_common(adap, msgs, num, false);
+}
+
+static int rk3x_i2c_xfer_polling(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return rk3x_i2c_xfer_common(adap, msgs, num, true);
+}
+
static __maybe_unused int rk3x_i2c_resume(struct device *dev)
{
struct rk3x_i2c *i2c = dev_get_drvdata(dev);
@@ -1128,8 +1160,9 @@ static u32 rk3x_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm rk3x_i2c_algorithm = {
- .master_xfer = rk3x_i2c_xfer,
- .functionality = rk3x_i2c_func,
+ .xfer = rk3x_i2c_xfer,
+ .xfer_atomic = rk3x_i2c_xfer_polling,
+ .functionality = rk3x_i2c_func,
};
static const struct rk3x_i2c_soc_data rv1108_soc_data = {
@@ -1137,6 +1170,11 @@ static const struct rk3x_i2c_soc_data rv1108_soc_data = {
.calc_timings = rk3x_i2c_v1_calc_timings,
};
+static const struct rk3x_i2c_soc_data rv1126_soc_data = {
+ .grf_offset = 0x118,
+ .calc_timings = rk3x_i2c_v1_calc_timings,
+};
+
static const struct rk3x_i2c_soc_data rk3066_soc_data = {
.grf_offset = 0x154,
.calc_timings = rk3x_i2c_v0_calc_timings,
@@ -1168,6 +1206,10 @@ static const struct of_device_id rk3x_i2c_match[] = {
.data = &rv1108_soc_data
},
{
+ .compatible = "rockchip,rv1126-i2c",
+ .data = &rv1126_soc_data
+ },
+ {
.compatible = "rockchip,rk3066-i2c",
.data = &rk3066_soc_data
},
@@ -1196,7 +1238,6 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct rk3x_i2c *i2c;
- struct resource *mem;
int ret = 0;
int bus_nr;
u32 value;
@@ -1213,7 +1254,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
/* use common interface to get I2C timing properties */
i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);
- strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &rk3x_i2c_algorithm;
i2c->adap.retries = 3;
@@ -1226,8 +1267,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+ i2c->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
@@ -1253,8 +1293,12 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
return -EINVAL;
}
- /* 27+i: write mask, 11+i: value */
- value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
+ /* rv1126 i2c2 uses non-sequential write mask 20, value 4 */
+ if (i2c->soc_data == &rv1126_soc_data && bus_nr == 2)
+ value = BIT(20) | BIT(4);
+ else
+ /* 27+i: write mask, 11+i: value */
+ value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
ret = regmap_write(grf, i2c->soc_data->grf_offset, value);
if (ret != 0) {
@@ -1265,10 +1309,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
/* IRQ setup */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "cannot find rk3x IRQ\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
0, dev_name(&pdev->dev), i2c);
@@ -1277,6 +1319,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
return ret;
}
+ i2c->irq = irq;
+
platform_set_drvdata(pdev, i2c);
if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
@@ -1288,18 +1332,13 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
}
- if (IS_ERR(i2c->clk)) {
- ret = PTR_ERR(i2c->clk);
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
- return ret;
- }
- if (IS_ERR(i2c->pclk)) {
- ret = PTR_ERR(i2c->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
+ "Can't get bus clk\n");
+
+ if (IS_ERR(i2c->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
+ "Can't get periph clk\n");
ret = clk_prepare(i2c->clk);
if (ret < 0) {
@@ -1319,8 +1358,15 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
goto err_pclk;
}
+ ret = clk_enable(i2c->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Can't enable bus clk: %d\n", ret);
+ goto err_clk_notifier;
+ }
+
clk_rate = clk_get_rate(i2c->clk);
rk3x_i2c_adapt_div(i2c, clk_rate);
+ clk_disable(i2c->clk);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
@@ -1337,7 +1383,7 @@ err_clk:
return ret;
}
-static int rk3x_i2c_remove(struct platform_device *pdev)
+static void rk3x_i2c_remove(struct platform_device *pdev)
{
struct rk3x_i2c *i2c = platform_get_drvdata(pdev);
@@ -1346,15 +1392,13 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
clk_unprepare(i2c->pclk);
clk_unprepare(i2c->clk);
-
- return 0;
}
static SIMPLE_DEV_PM_OPS(rk3x_i2c_pm_ops, NULL, rk3x_i2c_resume);
static struct platform_driver rk3x_i2c_driver = {
.probe = rk3x_i2c_probe,
- .remove = rk3x_i2c_remove,
+ .remove = rk3x_i2c_remove,
.driver = {
.name = "rk3x-i2c",
.of_match_table = rk3x_i2c_match,
diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c
index d848cf515234..e0a76fb5bc31 100644
--- a/drivers/i2c/busses/i2c-robotfuzz-osif.c
+++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for RobotFuzz OSIF
*
@@ -7,10 +8,6 @@
* Based on the i2c-tiny-usb by
*
* Copyright (C) 2006 Til Harbaum (Till@Harbaum.org)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
@@ -86,7 +83,7 @@ static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
}
}
- ret = osif_usb_read(adapter, OSIFI2C_STOP, 0, 0, NULL, 0);
+ ret = osif_usb_write(adapter, OSIFI2C_STOP, 0, 0, NULL, 0);
if (ret) {
dev_err(&adapter->dev, "failure sending STOP\n");
return -EREMOTEIO;
@@ -114,9 +111,14 @@ static u32 osif_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
+/* prevent invalid 0-length usb_control_msg */
+static const struct i2c_adapter_quirks osif_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN_READ,
+};
+
static const struct i2c_algorithm osif_algorithm = {
- .master_xfer = osif_xfer,
- .functionality = osif_func,
+ .xfer = osif_xfer,
+ .functionality = osif_func,
};
#define USB_OSIF_VENDOR_ID 0x1964
@@ -146,6 +148,7 @@ static int osif_probe(struct usb_interface *interface,
priv->adapter.owner = THIS_MODULE;
priv->adapter.class = I2C_CLASS_HWMON;
+ priv->adapter.quirks = &osif_quirks;
priv->adapter.algo = &osif_algorithm;
priv->adapter.algo_data = priv;
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
@@ -156,7 +159,7 @@ static int osif_probe(struct usb_interface *interface,
* Set bus frequency. The frequency is:
* 120,000,000 / ( 16 + 2 * div * 4^prescale).
* Using dev = 52, prescale = 0 give 100KHz */
- ret = osif_usb_read(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0,
+ ret = osif_usb_write(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0,
NULL, 0);
if (ret) {
dev_err(&interface->dev, "failure sending bit rate");
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
new file mode 100644
index 000000000000..4723e48cfe18
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/unaligned.h>
+
+enum rtl9300_bus_freq {
+ RTL9300_I2C_STD_FREQ,
+ RTL9300_I2C_FAST_FREQ,
+};
+
+struct rtl9300_i2c;
+
+struct rtl9300_i2c_chan {
+ struct i2c_adapter adap;
+ struct rtl9300_i2c *i2c;
+ enum rtl9300_bus_freq bus_freq;
+ u8 sda_num;
+};
+
+enum rtl9300_i2c_reg_scope {
+ REG_SCOPE_GLOBAL,
+ REG_SCOPE_MASTER,
+};
+
+struct rtl9300_i2c_reg_field {
+ struct reg_field field;
+ enum rtl9300_i2c_reg_scope scope;
+};
+
+enum rtl9300_i2c_reg_fields {
+ F_DATA_WIDTH = 0,
+ F_DEV_ADDR,
+ F_I2C_FAIL,
+ F_I2C_TRIG,
+ F_MEM_ADDR,
+ F_MEM_ADDR_WIDTH,
+ F_RD_MODE,
+ F_RWOP,
+ F_SCL_FREQ,
+ F_SCL_SEL,
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
+
+ /* keep last */
+ F_NUM_FIELDS
+};
+
+struct rtl9300_i2c_drv_data {
+ struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
+ int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
+ u32 data_reg;
+ u8 max_nchan;
+};
+
+#define RTL9300_I2C_MUX_NCHAN 8
+#define RTL9310_I2C_MUX_NCHAN 12
+
+struct rtl9300_i2c {
+ struct regmap *regmap;
+ struct device *dev;
+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
+ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
+ u32 data_reg;
+ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
+};
+
+DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
+
+enum rtl9300_i2c_xfer_type {
+ RTL9300_I2C_XFER_BYTE,
+ RTL9300_I2C_XFER_WORD,
+ RTL9300_I2C_XFER_BLOCK,
+};
+
+struct rtl9300_i2c_xfer {
+ enum rtl9300_i2c_xfer_type type;
+ u16 dev_addr;
+ u8 reg_addr;
+ u8 reg_addr_len;
+ u8 *data;
+ u8 data_len;
+ bool write;
+};
+
+#define RTL9300_I2C_MST_CTRL1 0x0
+#define RTL9300_I2C_MST_CTRL2 0x4
+#define RTL9300_I2C_MST_DATA_WORD0 0x8
+#define RTL9300_I2C_MST_DATA_WORD1 0xc
+#define RTL9300_I2C_MST_DATA_WORD2 0x10
+#define RTL9300_I2C_MST_DATA_WORD3 0x14
+#define RTL9300_I2C_MST_GLB_CTRL 0x384
+
+#define RTL9310_I2C_MST_IF_CTRL 0x1004
+#define RTL9310_I2C_MST_IF_SEL 0x1008
+#define RTL9310_I2C_MST_CTRL 0x0
+#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
+#define RTL9310_I2C_MST_DATA_CTRL 0x8
+
+static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+{
+ int ret;
+
+ ret = regmap_field_write(i2c->fields[F_MEM_ADDR_WIDTH], len);
+ if (ret)
+ return ret;
+
+ return regmap_field_write(i2c->fields[F_MEM_ADDR], reg);
+}
+
+static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+{
+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
+}
+
+static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+{
+ return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl));
+}
+
+static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+{
+ struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+
+ if (i2c->sda_num == chan->sda_num)
+ return 0;
+
+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ if (ret)
+ return ret;
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ ret = drv_data->select_scl(i2c, i2c->scl_num);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
+ BIT(chan->sda_num));
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
+ if (ret)
+ return ret;
+
+ i2c->sda_num = chan->sda_num;
+ return 0;
+}
+
+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+{
+ u32 vals[4] = {};
+ int i, ret;
+
+ if (len > 16)
+ return -EIO;
+
+ ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = vals[i/4] & 0xff;
+ vals[i/4] >>= 8;
+ }
+
+ return 0;
+}
+
+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+{
+ u32 vals[4] = {};
+ int i;
+
+ if (len > 16)
+ return -EIO;
+
+ for (i = 0; i < len; i++) {
+ unsigned int shift = (i % 4) * 8;
+ unsigned int reg = i / 4;
+
+ vals[reg] |= buf[i] << shift;
+ }
+
+ return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
+}
+
+static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
+{
+ return regmap_write(i2c->regmap, i2c->data_reg, data);
+}
+
+static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+{
+ int ret;
+
+ if (xfer->data_len < 1 || xfer->data_len > 16)
+ return -EINVAL;
+
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
+ if (ret)
+ return ret;
+
+ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
+ if (ret)
+ return ret;
+
+ if (xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+ ret = rtl9300_i2c_writel(i2c, *xfer->data);
+ break;
+ case RTL9300_I2C_XFER_WORD:
+ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
+ break;
+ default:
+ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(i2c->fields[F_I2C_FAIL], &val);
+ if (ret)
+ return ret;
+ if (val)
+ return -EIO;
+
+ if (!xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+
+ *xfer->data = val & 0xff;
+ break;
+ case RTL9300_I2C_XFER_WORD:
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+
+ put_unaligned(val & 0xffff, (u16*)xfer->data);
+ break;
+ default:
+ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
+ char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
+ struct rtl9300_i2c *i2c = chan->i2c;
+ struct rtl9300_i2c_xfer xfer = {0};
+ int ret;
+
+ if (addr > 0x7f)
+ return -EINVAL;
+
+ guard(rtl9300_i2c)(i2c);
+
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+ return ret;
+
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+ xfer.reg_addr = command;
+ xfer.reg_addr_len = 1;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
+ xfer.data_len = 1;
+ xfer.reg_addr = 0;
+ xfer.reg_addr_len = 0;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ xfer.data = &data->byte;
+ xfer.data_len = 1;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ xfer.data = (u8 *)&data->word;
+ xfer.data_len = 2;
+ xfer.type = RTL9300_I2C_XFER_WORD;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ xfer.data = &data->block[0];
+ xfer.data_len = data->block[0] + 1;
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ xfer.data = &data->block[1];
+ xfer.data_len = data->block[0];
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
+ break;
+ default:
+ dev_err(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
+ if (ret)
+ return ret;
+
+ return rtl9300_i2c_do_xfer(i2c, &xfer);
+}
+
+static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static const struct i2c_algorithm rtl9300_i2c_algo = {
+ .smbus_xfer = rtl9300_i2c_smbus_xfer,
+ .functionality = rtl9300_i2c_func,
+};
+
+static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
+ .flags = I2C_AQ_NO_CLK_STRETCH | I2C_AQ_NO_ZERO_LEN,
+ .max_read_len = 16,
+ .max_write_len = 16,
+};
+
+static int rtl9300_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rtl9300_i2c *i2c;
+ struct fwnode_handle *child;
+ struct rtl9300_i2c_drv_data *drv_data;
+ struct reg_field fields[F_NUM_FIELDS];
+ u32 clock_freq, scl_num, sda_num;
+ int ret, i = 0;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(i2c->regmap))
+ return PTR_ERR(i2c->regmap);
+ i2c->dev = dev;
+
+ mutex_init(&i2c->lock);
+
+ ret = device_property_read_u32(dev, "reg", &i2c->reg_base);
+ if (ret)
+ return ret;
+
+ ret = device_property_read_u32(dev, "realtek,scl", &scl_num);
+ if (ret || scl_num != 1)
+ scl_num = 0;
+ i2c->scl_num = (u8)scl_num;
+
+ platform_set_drvdata(pdev, i2c);
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ if (device_get_child_node_count(dev) > drv_data->max_nchan)
+ return dev_err_probe(dev, -EINVAL, "Too many channels\n");
+
+ i2c->data_reg = i2c->reg_base + drv_data->data_reg;
+ for (i = 0; i < F_NUM_FIELDS; i++) {
+ fields[i] = drv_data->field_desc[i].field;
+ if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
+ fields[i].reg += i2c->reg_base;
+ }
+ ret = devm_regmap_field_bulk_alloc(dev, i2c->regmap, i2c->fields,
+ fields, F_NUM_FIELDS);
+ if (ret)
+ return ret;
+
+ i = 0;
+ device_for_each_child_node(dev, child) {
+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
+ struct i2c_adapter *adap = &chan->adap;
+
+ ret = fwnode_property_read_u32(child, "reg", &sda_num);
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_u32(child, "clock-frequency", &clock_freq);
+ if (ret)
+ clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
+
+ switch (clock_freq) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ chan->bus_freq = RTL9300_I2C_STD_FREQ;
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ chan->bus_freq = RTL9300_I2C_FAST_FREQ;
+ break;
+ default:
+ dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
+ sda_num, clock_freq);
+ break;
+ }
+
+ chan->sda_num = sda_num;
+ chan->i2c = i2c;
+ adap = &i2c->chans[i].adap;
+ adap->owner = THIS_MODULE;
+ adap->algo = &rtl9300_i2c_algo;
+ adap->quirks = &rtl9300_i2c_quirks;
+ adap->retries = 3;
+ adap->dev.parent = dev;
+ i2c_set_adapdata(adap, chan);
+ adap->dev.of_node = to_of_node(child);
+ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
+ i++;
+
+ ret = devm_i2c_add_adapter(dev, adap);
+ if (ret)
+ return ret;
+ }
+ i2c->sda_num = 0xff;
+
+ /* only use standard read format */
+ ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#define GLB_REG_FIELD(reg, msb, lsb) \
+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_GLOBAL }
+#define MST_REG_FIELD(reg, msb, lsb) \
+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_MASTER }
+
+static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
+ .field_desc = {
+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 8, 31),
+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 4, 6),
+ [F_SCL_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 3, 3),
+ [F_RWOP] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 2, 2),
+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 1, 1),
+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ [F_RD_MODE] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 15, 15),
+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 8, 14),
+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 4, 7),
+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
+ .data_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+};
+
+static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+ .field_desc = {
+ [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13),
+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11),
+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31),
+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17),
+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21),
+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10),
+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8),
+ [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4),
+ [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2),
+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
+ .data_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+};
+
+static const struct of_device_id i2c_rtl9300_dt_ids[] = {
+ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+
+static struct platform_driver rtl9300_i2c_driver = {
+ .probe = rtl9300_i2c_probe,
+ .driver = {
+ .name = "i2c-rtl9300",
+ .of_match_table = i2c_rtl9300_dt_ids,
+ },
+};
+
+module_platform_driver(rtl9300_i2c_driver);
+
+MODULE_DESCRIPTION("RTL9300 I2C controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-rzv2m.c b/drivers/i2c/busses/i2c-rzv2m.c
new file mode 100644
index 000000000000..238714850673
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rzv2m.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Renesas RZ/V2M I2C unit
+ *
+ * Copyright (C) 2016-2022 Renesas Electronics Corporation
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+/* Register offsets */
+#define IICB0DAT 0x00 /* Data Register */
+#define IICB0CTL0 0x08 /* Control Register 0 */
+#define IICB0TRG 0x0C /* Trigger Register */
+#define IICB0STR0 0x10 /* Status Register 0 */
+#define IICB0CTL1 0x20 /* Control Register 1 */
+#define IICB0WL 0x24 /* Low Level Width Setting Reg */
+#define IICB0WH 0x28 /* How Level Width Setting Reg */
+
+/* IICB0CTL0 */
+#define IICB0IICE BIT(7) /* I2C Enable */
+#define IICB0SLWT BIT(1) /* Interrupt Request Timing */
+#define IICB0SLAC BIT(0) /* Acknowledge */
+
+/* IICB0TRG */
+#define IICB0WRET BIT(2) /* Quit Wait Trigger */
+#define IICB0STT BIT(1) /* Create Start Condition Trigger */
+#define IICB0SPT BIT(0) /* Create Stop Condition Trigger */
+
+/* IICB0STR0 */
+#define IICB0SSAC BIT(8) /* Ack Flag */
+#define IICB0SSBS BIT(6) /* Bus Flag */
+#define IICB0SSSP BIT(4) /* Stop Condition Flag */
+
+/* IICB0CTL1 */
+#define IICB0MDSC BIT(7) /* Bus Mode */
+#define IICB0SLSE BIT(1) /* Start condition output */
+
+struct rzv2m_i2c_priv {
+ void __iomem *base;
+ struct i2c_adapter adap;
+ struct clk *clk;
+ int bus_mode;
+ struct completion msg_tia_done;
+ u32 iicb0wl;
+ u32 iicb0wh;
+};
+
+enum bcr_index {
+ RZV2M_I2C_100K = 0,
+ RZV2M_I2C_400K,
+};
+
+struct bitrate_config {
+ unsigned int percent_low;
+ unsigned int min_hold_time_ns;
+};
+
+static const struct bitrate_config bitrate_configs[] = {
+ [RZV2M_I2C_100K] = { 47, 3450 },
+ [RZV2M_I2C_400K] = { 52, 900 },
+};
+
+static inline void bit_setl(void __iomem *addr, u32 val)
+{
+ writel(readl(addr) | val, addr);
+}
+
+static inline void bit_clrl(void __iomem *addr, u32 val)
+{
+ writel(readl(addr) & ~val, addr);
+}
+
+static irqreturn_t rzv2m_i2c_tia_irq_handler(int this_irq, void *dev_id)
+{
+ struct rzv2m_i2c_priv *priv = dev_id;
+
+ complete(&priv->msg_tia_done);
+
+ return IRQ_HANDLED;
+}
+
+/* Calculate IICB0WL and IICB0WH */
+static int rzv2m_i2c_clock_calculate(struct device *dev,
+ struct rzv2m_i2c_priv *priv)
+{
+ const struct bitrate_config *config;
+ unsigned int hold_time_ns;
+ unsigned int total_pclks;
+ unsigned int trf_pclks;
+ unsigned long pclk_hz;
+ struct i2c_timings t;
+ u32 trf_ns;
+
+ i2c_parse_fw_timings(dev, &t, true);
+
+ pclk_hz = clk_get_rate(priv->clk);
+ total_pclks = pclk_hz / t.bus_freq_hz;
+
+ trf_ns = t.scl_rise_ns + t.scl_fall_ns;
+ trf_pclks = mul_u64_u32_div(pclk_hz, trf_ns, NSEC_PER_SEC);
+
+ /* Config setting */
+ switch (t.bus_freq_hz) {
+ case I2C_MAX_FAST_MODE_FREQ:
+ priv->bus_mode = RZV2M_I2C_400K;
+ break;
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ priv->bus_mode = RZV2M_I2C_100K;
+ break;
+ default:
+ dev_err(dev, "transfer speed is invalid\n");
+ return -EINVAL;
+ }
+ config = &bitrate_configs[priv->bus_mode];
+
+ /* IICB0WL = (percent_low / Transfer clock) x PCLK */
+ priv->iicb0wl = total_pclks * config->percent_low / 100;
+ if (priv->iicb0wl > (BIT(10) - 1))
+ return -EINVAL;
+
+ /* IICB0WH = ((percent_high / Transfer clock) x PCLK) - (tR + tF) */
+ priv->iicb0wh = total_pclks - priv->iicb0wl - trf_pclks;
+ if (priv->iicb0wh > (BIT(10) - 1))
+ return -EINVAL;
+
+ /*
+ * Data hold time must be less than 0.9us in fast mode and
+ * 3.45us in standard mode.
+ * Data hold time = IICB0WL[9:2] / PCLK
+ */
+ hold_time_ns = div64_ul((u64)(priv->iicb0wl >> 2) * NSEC_PER_SEC, pclk_hz);
+ if (hold_time_ns > config->min_hold_time_ns) {
+ dev_err(dev, "data hold time %dns is over %dns\n",
+ hold_time_ns, config->min_hold_time_ns);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void rzv2m_i2c_init(struct rzv2m_i2c_priv *priv)
+{
+ u32 i2c_ctl0;
+ u32 i2c_ctl1;
+
+ /* i2c disable */
+ writel(0, priv->base + IICB0CTL0);
+
+ /* IICB0CTL1 setting */
+ i2c_ctl1 = IICB0SLSE;
+ if (priv->bus_mode == RZV2M_I2C_400K)
+ i2c_ctl1 |= IICB0MDSC;
+ writel(i2c_ctl1, priv->base + IICB0CTL1);
+
+ /* IICB0WL IICB0WH setting */
+ writel(priv->iicb0wl, priv->base + IICB0WL);
+ writel(priv->iicb0wh, priv->base + IICB0WH);
+
+ /* i2c enable after setting */
+ i2c_ctl0 = IICB0SLWT | IICB0SLAC | IICB0IICE;
+ writel(i2c_ctl0, priv->base + IICB0CTL0);
+}
+
+static int rzv2m_i2c_write_with_ack(struct rzv2m_i2c_priv *priv, u32 data)
+{
+ unsigned long time_left;
+
+ reinit_completion(&priv->msg_tia_done);
+
+ writel(data, priv->base + IICB0DAT);
+
+ time_left = wait_for_completion_timeout(&priv->msg_tia_done,
+ priv->adap.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ /* Confirm ACK */
+ if ((readl(priv->base + IICB0STR0) & IICB0SSAC) != IICB0SSAC)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int rzv2m_i2c_read_with_ack(struct rzv2m_i2c_priv *priv, u8 *data,
+ bool last)
+{
+ unsigned long time_left;
+ u32 data_tmp;
+
+ reinit_completion(&priv->msg_tia_done);
+
+ /* Interrupt request timing : 8th clock */
+ bit_clrl(priv->base + IICB0CTL0, IICB0SLWT);
+
+ /* Exit the wait state */
+ writel(IICB0WRET, priv->base + IICB0TRG);
+
+ /* Wait for transaction */
+ time_left = wait_for_completion_timeout(&priv->msg_tia_done,
+ priv->adap.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ if (last) {
+ /* Disable ACK */
+ bit_clrl(priv->base + IICB0CTL0, IICB0SLAC);
+
+ /* Read data*/
+ data_tmp = readl(priv->base + IICB0DAT);
+
+ /* Interrupt request timing : 9th clock */
+ bit_setl(priv->base + IICB0CTL0, IICB0SLWT);
+
+ /* Exit the wait state */
+ writel(IICB0WRET, priv->base + IICB0TRG);
+
+ /* Wait for transaction */
+ time_left = wait_for_completion_timeout(&priv->msg_tia_done,
+ priv->adap.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ /* Enable ACK */
+ bit_setl(priv->base + IICB0CTL0, IICB0SLAC);
+ } else {
+ /* Read data */
+ data_tmp = readl(priv->base + IICB0DAT);
+ }
+
+ *data = data_tmp;
+
+ return 0;
+}
+
+static int rzv2m_i2c_send(struct rzv2m_i2c_priv *priv, struct i2c_msg *msg,
+ unsigned int *count)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < msg->len; i++) {
+ ret = rzv2m_i2c_write_with_ack(priv, msg->buf[i]);
+ if (ret < 0)
+ return ret;
+ }
+ *count = i;
+
+ return 0;
+}
+
+static int rzv2m_i2c_receive(struct rzv2m_i2c_priv *priv, struct i2c_msg *msg,
+ unsigned int *count)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < msg->len; i++) {
+ ret = rzv2m_i2c_read_with_ack(priv, &msg->buf[i],
+ (msg->len - 1) == i);
+ if (ret < 0)
+ return ret;
+ }
+ *count = i;
+
+ return 0;
+}
+
+static int rzv2m_i2c_send_address(struct rzv2m_i2c_priv *priv,
+ struct i2c_msg *msg)
+{
+ u32 addr;
+ int ret;
+
+ if (msg->flags & I2C_M_TEN) {
+ /* 10-bit address: Send 1st address(extend code) */
+ addr = i2c_10bit_addr_hi_from_msg(msg);
+ ret = rzv2m_i2c_write_with_ack(priv, addr);
+ if (ret)
+ return ret;
+
+ /* 10-bit address: Send 2nd address */
+ addr = i2c_10bit_addr_lo_from_msg(msg);
+ ret = rzv2m_i2c_write_with_ack(priv, addr);
+ } else {
+ /* 7-bit address */
+ addr = i2c_8bit_addr_from_msg(msg);
+ ret = rzv2m_i2c_write_with_ack(priv, addr);
+ }
+
+ return ret;
+}
+
+static int rzv2m_i2c_stop_condition(struct rzv2m_i2c_priv *priv)
+{
+ u32 value;
+
+ /* Send stop condition */
+ writel(IICB0SPT, priv->base + IICB0TRG);
+ return readl_poll_timeout(priv->base + IICB0STR0,
+ value, value & IICB0SSSP,
+ 100, jiffies_to_usecs(priv->adap.timeout));
+}
+
+static int rzv2m_i2c_xfer_msg(struct rzv2m_i2c_priv *priv,
+ struct i2c_msg *msg, int stop)
+{
+ unsigned int count = 0;
+ int ret, read = !!(msg->flags & I2C_M_RD);
+
+ /* Send start condition */
+ writel(IICB0STT, priv->base + IICB0TRG);
+
+ ret = rzv2m_i2c_send_address(priv, msg);
+ if (!ret) {
+ if (read)
+ ret = rzv2m_i2c_receive(priv, msg, &count);
+ else
+ ret = rzv2m_i2c_send(priv, msg, &count);
+
+ if (!ret && stop)
+ ret = rzv2m_i2c_stop_condition(priv);
+ }
+
+ if (ret == -ENXIO)
+ rzv2m_i2c_stop_condition(priv);
+ else if (ret < 0)
+ rzv2m_i2c_init(priv);
+ else
+ ret = count;
+
+ return ret;
+}
+
+static int rzv2m_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct rzv2m_i2c_priv *priv = i2c_get_adapdata(adap);
+ struct device *dev = priv->adap.dev.parent;
+ unsigned int i;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ if (readl(priv->base + IICB0STR0) & IICB0SSBS) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ /* I2C main transfer */
+ for (i = 0; i < num; i++) {
+ ret = rzv2m_i2c_xfer_msg(priv, &msgs[i], i == (num - 1));
+ if (ret < 0)
+ goto out;
+ }
+ ret = num;
+
+out:
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static u32 rzv2m_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+ I2C_FUNC_10BIT_ADDR;
+}
+
+static int rzv2m_i2c_disable(struct device *dev, struct rzv2m_i2c_priv *priv)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ bit_clrl(priv->base + IICB0CTL0, IICB0IICE);
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static const struct i2c_adapter_quirks rzv2m_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
+static const struct i2c_algorithm rzv2m_i2c_algo = {
+ .xfer = rzv2m_i2c_xfer,
+ .functionality = rzv2m_i2c_func,
+};
+
+static int rzv2m_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzv2m_i2c_priv *priv;
+ struct reset_control *rstc;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Can't get clock\n");
+
+ rstc = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc), "Missing reset ctrl\n");
+ /*
+ * The reset also affects other HW that is not under the control
+ * of Linux. Therefore, all we can do is deassert the reset.
+ */
+ reset_control_deassert(rstc);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rzv2m_i2c_tia_irq_handler, 0,
+ dev_name(dev), priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
+
+ adap = &priv->adap;
+ adap->nr = pdev->id;
+ adap->algo = &rzv2m_i2c_algo;
+ adap->quirks = &rzv2m_i2c_quirks;
+ adap->dev.parent = dev;
+ adap->owner = THIS_MODULE;
+ device_set_node(&adap->dev, dev_fwnode(dev));
+ i2c_set_adapdata(adap, priv);
+ strscpy(adap->name, pdev->name, sizeof(adap->name));
+ init_completion(&priv->msg_tia_done);
+
+ ret = rzv2m_i2c_clock_calculate(dev, priv);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_enable(dev);
+
+ pm_runtime_get_sync(dev);
+ rzv2m_i2c_init(priv);
+ pm_runtime_put(dev);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ rzv2m_i2c_disable(dev, priv);
+ pm_runtime_disable(dev);
+ }
+
+ return ret;
+}
+
+static void rzv2m_i2c_remove(struct platform_device *pdev)
+{
+ struct rzv2m_i2c_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = priv->adap.dev.parent;
+
+ i2c_del_adapter(&priv->adap);
+ rzv2m_i2c_disable(dev, priv);
+ pm_runtime_disable(dev);
+}
+
+static int rzv2m_i2c_suspend(struct device *dev)
+{
+ struct rzv2m_i2c_priv *priv = dev_get_drvdata(dev);
+
+ return rzv2m_i2c_disable(dev, priv);
+}
+
+static int rzv2m_i2c_resume(struct device *dev)
+{
+ struct rzv2m_i2c_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = rzv2m_i2c_clock_calculate(dev, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ rzv2m_i2c_init(priv);
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static const struct of_device_id rzv2m_i2c_ids[] = {
+ { .compatible = "renesas,rzv2m-i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rzv2m_i2c_ids);
+
+static const struct dev_pm_ops rzv2m_i2c_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(rzv2m_i2c_suspend, rzv2m_i2c_resume)
+};
+
+static struct platform_driver rzv2m_i2c_driver = {
+ .driver = {
+ .name = "rzv2m-i2c",
+ .of_match_table = rzv2m_i2c_ids,
+ .pm = pm_sleep_ptr(&rzv2m_i2c_pm_ops),
+ },
+ .probe = rzv2m_i2c_probe,
+ .remove = rzv2m_i2c_remove,
+};
+module_platform_driver(rzv2m_i2c_driver);
+
+MODULE_DESCRIPTION("RZ/V2M I2C bus driver");
+MODULE_AUTHOR("Renesas Electronics Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 2f2e28d60ef5..8138f5ef40f0 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* linux/drivers/i2c/busses/i2c-s3c2410.c
*
* Copyright (C) 2004,2005,2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2410 I2C Controller
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -33,7 +24,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
@@ -85,6 +76,7 @@
#define QUIRK_HDMIPHY (1 << 1)
#define QUIRK_NO_GPIO (1 << 2)
#define QUIRK_POLL (1 << 3)
+#define QUIRK_ATOMIC (1 << 4)
/* Max time to wait for bus to become idle after a xfer (in us) */
#define S3C2410_IDLE_TIMEOUT 5000
@@ -104,7 +96,6 @@ enum s3c24xx_i2c_state {
struct s3c24xx_i2c {
wait_queue_head_t wait;
kernel_ulong_t quirks;
- unsigned int suspended:1;
struct i2c_msg *msg;
unsigned int msg_num;
@@ -123,11 +114,8 @@ struct s3c24xx_i2c {
struct i2c_adapter adap;
struct s3c2410_platform_i2c *pdata;
- int gpios[2];
+ struct gpio_desc *gpios[2];
struct pinctrl *pctrl;
-#if defined(CONFIG_ARM_S3C24XX_CPUFREQ)
- struct notifier_block freq_transition;
-#endif
struct regmap *sysreg;
unsigned int sys_i2c_cfg;
};
@@ -142,15 +130,14 @@ static const struct platform_device_id s3c24xx_driver_ids[] = {
}, {
.name = "s3c2440-hdmiphy-i2c",
.driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
- }, { },
+ }, { }
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
-static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat);
+static void i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat);
#ifdef CONFIG_OF
static const struct of_device_id s3c24xx_i2c_match[] = {
- { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
{ .compatible = "samsung,s3c2440-hdmiphy-i2c",
.data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
@@ -166,12 +153,8 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
*/
static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev)
{
- if (pdev->dev.of_node) {
- const struct of_device_id *match;
-
- match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
- return (kernel_ulong_t)match->data;
- }
+ if (pdev->dev.of_node)
+ return (kernel_ulong_t)of_device_get_match_data(&pdev->dev);
return platform_get_device_id(pdev)->driver_data;
}
@@ -191,7 +174,7 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
if (ret)
i2c->msg_idx = ret;
- if (!(i2c->quirks & QUIRK_POLL))
+ if (!(i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC)))
wake_up(&i2c->wait);
}
@@ -233,8 +216,17 @@ static bool is_ack(struct s3c24xx_i2c *i2c)
int tries;
for (tries = 50; tries; --tries) {
- if (readl(i2c->regs + S3C2410_IICCON)
- & S3C2410_IICCON_IRQPEND) {
+ unsigned long tmp = readl(i2c->regs + S3C2410_IICCON);
+
+ if (!(tmp & S3C2410_IICCON_ACKEN)) {
+ /*
+ * Wait a bit for the bus to stabilize,
+ * delay estimated experimentally.
+ */
+ usleep_range(100, 200);
+ return true;
+ }
+ if (tmp & S3C2410_IICCON_IRQPEND) {
if (!(readl(i2c->regs + S3C2410_IICSTAT)
& S3C2410_IICSTAT_LASTBIT))
return true;
@@ -287,16 +279,6 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
-
- if (i2c->quirks & QUIRK_POLL) {
- while ((i2c->msg_num != 0) && is_ack(i2c)) {
- i2c_s3c_irq_nextbyte(i2c, stat);
- stat = readl(i2c->regs + S3C2410_IICSTAT);
-
- if (stat & S3C2410_IICSTAT_ARBITR)
- dev_err(i2c->dev, "deal with arbitration loss\n");
- }
- }
}
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
@@ -394,11 +376,10 @@ static inline int is_msgend(struct s3c24xx_i2c *i2c)
/*
* process an interrupt and work out what to do
*/
-static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
+static void i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
unsigned long tmp;
unsigned char byte;
- int ret = 0;
switch (i2c->state) {
@@ -445,7 +426,7 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* fall through to the write state, as we will need to
* send a byte as well
*/
-
+ fallthrough;
case STATE_WRITE:
/*
* we are writing data to the device... check for the
@@ -493,7 +474,10 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* forces us to send a new START
* when we change direction
*/
+ dev_dbg(i2c->dev,
+ "missing START before write->read\n");
s3c24xx_i2c_stop(i2c, -EINVAL);
+ break;
}
goto retry_write;
@@ -558,7 +542,7 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
out:
- return ret;
+ return;
}
/*
@@ -700,12 +684,9 @@ static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, int num)
{
- unsigned long timeout;
+ long time_left = 0;
int ret;
- if (i2c->suspended)
- return -EIO;
-
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
@@ -722,24 +703,27 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs);
- if (i2c->quirks & QUIRK_POLL) {
- ret = i2c->msg_idx;
+ if (i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC)) {
+ while ((i2c->msg_num != 0) && is_ack(i2c)) {
+ unsigned long stat = readl(i2c->regs + S3C2410_IICSTAT);
- if (ret != num)
- dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
+ i2c_s3c_irq_nextbyte(i2c, stat);
- goto out;
+ stat = readl(i2c->regs + S3C2410_IICSTAT);
+ if (stat & S3C2410_IICSTAT_ARBITR)
+ dev_err(i2c->dev, "deal with arbitration loss\n");
+ }
+ } else {
+ time_left = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
}
- timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
-
ret = i2c->msg_idx;
/*
* Having these next two as dev_err() makes life very
* noisy when doing an i2cdetect
*/
- if (timeout == 0)
+ if (time_left == 0)
dev_dbg(i2c->dev, "timeout\n");
else if (ret != num)
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
@@ -791,17 +775,33 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
return -EREMOTEIO;
}
+static int s3c24xx_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
+ int ret;
+
+ disable_irq(i2c->irq);
+ i2c->quirks |= QUIRK_ATOMIC;
+ ret = s3c24xx_i2c_xfer(adap, msgs, num);
+ i2c->quirks &= ~QUIRK_ATOMIC;
+ enable_irq(i2c->irq);
+
+ return ret;
+}
+
/* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL_ALL | I2C_FUNC_NOSTART |
I2C_FUNC_PROTOCOL_MANGLING;
}
/* i2c bus registration info */
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
+ .xfer = s3c24xx_i2c_xfer,
+ .xfer_atomic = s3c24xx_i2c_xfer_atomic,
+ .functionality = s3c24xx_i2c_func,
};
/*
@@ -847,11 +847,11 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
int freq;
i2c->clkrate = clkin;
- clkin /= 1000; /* clkin now in KHz */
+ clkin /= 1000; /* clkin now in KHz */
dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency);
- target_frequency = pdata->frequency ? pdata->frequency : 100000;
+ target_frequency = pdata->frequency ?: I2C_MAX_STANDARD_MODE_FREQ;
target_frequency /= 1000; /* Target frequency now in KHz */
@@ -898,115 +898,30 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
return 0;
}
-#if defined(CONFIG_ARM_S3C24XX_CPUFREQ)
-
-#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition)
-
-static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct s3c24xx_i2c *i2c = freq_to_i2c(nb);
- unsigned int got;
- int delta_f;
- int ret;
-
- delta_f = clk_get_rate(i2c->clk) - i2c->clkrate;
-
- /* if we're post-change and the input clock has slowed down
- * or at pre-change and the clock is about to speed up, then
- * adjust our clock rate. <0 is slow, >0 speedup.
- */
-
- if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
- (val == CPUFREQ_PRECHANGE && delta_f > 0)) {
- i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
- ret = s3c24xx_i2c_clockrate(i2c, &got);
- i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
-
- if (ret < 0)
- dev_err(i2c->dev, "cannot find frequency (%d)\n", ret);
- else
- dev_info(i2c->dev, "setting freq %d\n", got);
- }
-
- return 0;
-}
-
-static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
-{
- i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition;
-
- return cpufreq_register_notifier(&i2c->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
-{
- cpufreq_unregister_notifier(&i2c->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-#else
-static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
-{
- return 0;
-}
-
-static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
-{
-}
-#endif
-
#ifdef CONFIG_OF
static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
{
- int idx, gpio, ret;
+ int i;
if (i2c->quirks & QUIRK_NO_GPIO)
return 0;
- for (idx = 0; idx < 2; idx++) {
- gpio = of_get_gpio(i2c->dev->of_node, idx);
- if (!gpio_is_valid(gpio)) {
- dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
- goto free_gpio;
- }
- i2c->gpios[idx] = gpio;
-
- ret = gpio_request(gpio, "i2c-bus");
- if (ret) {
- dev_err(i2c->dev, "gpio [%d] request failed (%d)\n",
- gpio, ret);
- goto free_gpio;
+ for (i = 0; i < 2; i++) {
+ i2c->gpios[i] = devm_gpiod_get_index(i2c->dev, NULL,
+ i, GPIOD_ASIS);
+ if (IS_ERR(i2c->gpios[i])) {
+ dev_err(i2c->dev, "i2c gpio invalid at index %d\n", i);
+ return -EINVAL;
}
}
return 0;
-
-free_gpio:
- while (--idx >= 0)
- gpio_free(i2c->gpios[idx]);
- return -EINVAL;
}
-static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
-{
- unsigned int idx;
-
- if (i2c->quirks & QUIRK_NO_GPIO)
- return;
-
- for (idx = 0; idx < 2; idx++)
- gpio_free(i2c->gpios[idx]);
-}
#else
static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
{
return 0;
}
-
-static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
-{
-}
#endif
/*
@@ -1115,7 +1030,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
else
s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
@@ -1135,9 +1050,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
/* map the registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->regs = devm_ioremap_resource(&pdev->dev, res);
-
+ i2c->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
@@ -1176,8 +1089,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
*/
if (!(i2c->quirks & QUIRK_POLL)) {
i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
+ if (ret < 0) {
clk_unprepare(i2c->clk);
return ret;
}
@@ -1191,13 +1103,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
}
}
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
- clk_unprepare(i2c->clk);
- return ret;
- }
-
/*
* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
@@ -1214,7 +1119,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
pm_runtime_disable(&pdev->dev);
- s3c24xx_i2c_deregister_cpufreq(i2c);
clk_unprepare(i2c->clk);
return ret;
}
@@ -1223,7 +1127,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int s3c24xx_i2c_remove(struct platform_device *pdev)
+static void s3c24xx_i2c_remove(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
@@ -1231,22 +1135,14 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
- s3c24xx_i2c_deregister_cpufreq(i2c);
-
i2c_del_adapter(&i2c->adap);
-
- if (pdev->dev.of_node && IS_ERR(i2c->pctrl))
- s3c24xx_i2c_dt_gpio_free(i2c);
-
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int s3c24xx_i2c_suspend_noirq(struct device *dev)
{
struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
- i2c->suspended = 1;
+ i2c_mark_adapter_suspended(&i2c->adap);
if (!IS_ERR(i2c->sysreg))
regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg);
@@ -1267,30 +1163,23 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
return ret;
s3c24xx_i2c_init(i2c);
clk_disable(i2c->clk);
- i2c->suspended = 0;
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
}
-#endif
-#ifdef CONFIG_PM
static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq,
- s3c24xx_i2c_resume_noirq)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq,
+ s3c24xx_i2c_resume_noirq)
};
-#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops)
-#else
-#define S3C24XX_DEV_PM_OPS NULL
-#endif
-
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.name = "s3c-i2c",
- .pm = S3C24XX_DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&s3c24xx_i2c_dev_pm_ops),
.of_match_table = of_match_ptr(s3c24xx_i2c_match),
},
};
@@ -1308,5 +1197,5 @@ static void __exit i2c_adap_s3c_exit(void)
module_exit(i2c_adap_s3c_exit);
MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index ff3f4553648f..10a5146b3aa5 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -1,28 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SMBus driver for ACPI SMBus CMI
*
* Copyright (C) 2009 Crane Cai <crane.cai@amd.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2.
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
-#define ACPI_SMBUS_HC_CLASS "smbus"
-#define ACPI_SMBUS_HC_DEVICE_NAME "cmi"
-
-/* SMBUS HID definition as supported by Microsoft Windows */
-#define ACPI_SMBUS_MS_HID "SMB0001"
-
-ACPI_MODULE_NAME("smbus_cmi");
-
struct smbus_methods_t {
char *mt_info;
char *mt_sbr;
@@ -35,7 +25,7 @@ struct acpi_smbus_cmi {
u8 cap_info:1;
u8 cap_read:1;
u8 cap_write:1;
- struct smbus_methods_t *methods;
+ const struct smbus_methods_t *methods;
};
static const struct smbus_methods_t smbus_methods = {
@@ -363,29 +353,25 @@ static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
return AE_OK;
}
-static int acpi_smbus_cmi_add(struct acpi_device *device)
+static int smbus_cmi_probe(struct platform_device *device)
{
+ struct device *dev = &device->dev;
struct acpi_smbus_cmi *smbus_cmi;
- const struct acpi_device_id *id;
int ret;
smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
if (!smbus_cmi)
return -ENOMEM;
- smbus_cmi->handle = device->handle;
- strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
- device->driver_data = smbus_cmi;
+ smbus_cmi->handle = ACPI_HANDLE(dev);
+ smbus_cmi->methods = device_get_match_data(dev);
+
+ platform_set_drvdata(device, smbus_cmi);
+
smbus_cmi->cap_info = 0;
smbus_cmi->cap_read = 0;
smbus_cmi->cap_write = 0;
- for (id = acpi_smbus_cmi_ids; id->id[0]; id++)
- if (!strcmp(id->id, acpi_device_hid(device)))
- smbus_cmi->methods =
- (struct smbus_methods_t *) id->driver_data;
-
acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
@@ -395,12 +381,11 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
}
snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
- "SMBus CMI adapter %s",
- acpi_device_name(device));
+ "SMBus CMI adapter %s", dev_name(dev));
smbus_cmi->adapter.owner = THIS_MODULE;
smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
smbus_cmi->adapter.algo_data = smbus_cmi;
- smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ smbus_cmi->adapter.class = I2C_CLASS_HWMON;
smbus_cmi->adapter.dev.parent = &device->dev;
ret = i2c_add_adapter(&smbus_cmi->adapter);
@@ -413,31 +398,26 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
err:
kfree(smbus_cmi);
- device->driver_data = NULL;
return ret;
}
-static int acpi_smbus_cmi_remove(struct acpi_device *device)
+static void smbus_cmi_remove(struct platform_device *device)
{
- struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device);
+ struct acpi_smbus_cmi *smbus_cmi = platform_get_drvdata(device);
i2c_del_adapter(&smbus_cmi->adapter);
kfree(smbus_cmi);
- device->driver_data = NULL;
-
- return 0;
}
-static struct acpi_driver acpi_smbus_cmi_driver = {
- .name = ACPI_SMBUS_HC_DEVICE_NAME,
- .class = ACPI_SMBUS_HC_CLASS,
- .ids = acpi_smbus_cmi_ids,
- .ops = {
- .add = acpi_smbus_cmi_add,
- .remove = acpi_smbus_cmi_remove,
+static struct platform_driver smbus_cmi_driver = {
+ .probe = smbus_cmi_probe,
+ .remove = smbus_cmi_remove,
+ .driver = {
+ .name = "smbus_cmi",
+ .acpi_match_table = acpi_smbus_cmi_ids,
},
};
-module_acpi_driver(acpi_smbus_cmi_driver);
+module_platform_driver(smbus_cmi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index c2005c789d2b..43f33988b98f 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -379,8 +379,8 @@ static u32 sh7760_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm sh7760_i2c_algo = {
- .master_xfer = sh7760_i2c_master_xfer,
- .functionality = sh7760_i2c_func,
+ .xfer = sh7760_i2c_master_xfer,
+ .functionality = sh7760_i2c_func,
};
/* calculate CCR register setting for a desired scl clock. SCL clock is
@@ -443,9 +443,8 @@ static int sh7760_i2c_probe(struct platform_device *pdev)
goto out0;
}
- id = kzalloc(sizeof(struct cami2c), GFP_KERNEL);
+ id = kzalloc(sizeof(*id), GFP_KERNEL);
if (!id) {
- dev_err(&pdev->dev, "no mem for private data\n");
ret = -ENOMEM;
goto out0;
}
@@ -471,11 +470,14 @@ static int sh7760_i2c_probe(struct platform_device *pdev)
goto out2;
}
- id->irq = platform_get_irq(pdev, 0);
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto out3;
+ id->irq = ret;
id->adap.nr = pdev->id;
id->adap.algo = &sh7760_i2c_algo;
- id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ id->adap.class = I2C_CLASS_HWMON;
id->adap.retries = 3;
id->adap.algo_data = id;
id->adap.dev.parent = &pdev->dev;
@@ -533,7 +535,7 @@ out0:
return ret;
}
-static int sh7760_i2c_remove(struct platform_device *pdev)
+static void sh7760_i2c_remove(struct platform_device *pdev)
{
struct cami2c *id = platform_get_drvdata(pdev);
@@ -543,8 +545,6 @@ static int sh7760_i2c_remove(struct platform_device *pdev)
release_resource(id->ioarea);
kfree(id->ioarea);
kfree(id);
-
- return 0;
}
static struct platform_driver sh7760_i2c_drv = {
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index a64f2ff3cb49..dae8967f8749 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -2,8 +2,7 @@
/*
* SuperH Mobile I2C Controller
*
- * Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
- *
+ * Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
* Copyright (C) 2008 Magnus Damm
*
* Portions of the code based on out-of-tree driver i2c-sh7343.c
@@ -21,10 +20,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
/* Transmit operation: */
/* */
@@ -130,6 +130,7 @@ struct sh_mobile_i2c_data {
int sr;
bool send_stop;
bool stop_after_dma;
+ bool atomic_xfer;
struct resource *res;
struct dma_chan *dma_tx;
@@ -146,9 +147,6 @@ struct sh_mobile_dt_config {
#define IIC_FLAG_HAS_ICIC67 (1 << 0)
-#define STANDARD_MODE 100000
-#define FAST_MODE 400000
-
/* Register offsets */
#define ICDR 0x00
#define ICCR 0x04
@@ -271,11 +269,11 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
i2c_clk_khz = clk_get_rate(pd->clk) / 1000 / pd->clks_per_count;
- if (pd->bus_speed == STANDARD_MODE) {
+ if (pd->bus_speed == I2C_MAX_STANDARD_MODE_FREQ) {
tLOW = 47; /* tLOW = 4.7 us */
tHIGH = 40; /* tHD;STA = tHIGH = 4.0 us */
tf = 3; /* tf = 0.3 us */
- } else if (pd->bus_speed == FAST_MODE) {
+ } else if (pd->bus_speed == I2C_MAX_FAST_MODE_FREQ) {
tLOW = 13; /* tLOW = 1.3 us */
tHIGH = 6; /* tHD;STA = tHIGH = 0.6 us */
tf = 3; /* tf = 0.3 us */
@@ -303,13 +301,12 @@ static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd)
return sh_mobile_i2c_check_timing(pd);
}
-static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
- enum sh_mobile_i2c_op op, unsigned char data)
+static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op)
{
unsigned char ret = 0;
unsigned long flags;
- dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data);
+ dev_dbg(pd->dev, "op %d\n", op);
spin_lock_irqsave(&pd->lock, flags);
@@ -317,12 +314,12 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
case OP_START: /* issue start and trigger DTE interrupt */
iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY);
break;
- case OP_TX_FIRST: /* disable DTE interrupt and write data */
+ case OP_TX_FIRST: /* disable DTE interrupt and write client address */
iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
- iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg));
break;
case OP_TX: /* write data */
- iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICDR, pd->msg->buf[pd->pos]);
break;
case OP_TX_STOP: /* issue a stop (or rep_start) */
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
@@ -335,13 +332,15 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
ret = iic_rd(pd, ICDR);
break;
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
- iic_wr(pd, ICIC,
- ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ if (!pd->atomic_xfer)
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
break;
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
- iic_wr(pd, ICIC,
- ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ if (!pd->atomic_xfer)
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
ret = iic_rd(pd, ICDR);
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
break;
@@ -353,34 +352,17 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
return ret;
}
-static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)
-{
- return pd->pos == -1;
-}
-
-static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
- unsigned char *buf)
-{
- switch (pd->pos) {
- case -1:
- *buf = i2c_8bit_addr_from_msg(pd->msg);
- break;
- default:
- *buf = pd->msg->buf[pd->pos];
- }
-}
-
static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
{
- unsigned char data;
-
if (pd->pos == pd->msg->len) {
- i2c_op(pd, OP_TX_STOP, 0);
+ i2c_op(pd, OP_TX_STOP);
return 1;
}
- sh_mobile_i2c_get_data(pd, &data);
- i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data);
+ if (pd->pos == -1)
+ i2c_op(pd, OP_TX_FIRST);
+ else
+ i2c_op(pd, OP_TX);
pd->pos++;
return 0;
@@ -388,48 +370,32 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
{
- unsigned char data;
int real_pos;
- do {
- if (pd->pos <= -1) {
- sh_mobile_i2c_get_data(pd, &data);
-
- if (sh_mobile_i2c_is_first_byte(pd))
- i2c_op(pd, OP_TX_FIRST, data);
- else
- i2c_op(pd, OP_TX, data);
- break;
- }
-
- if (pd->pos == 0) {
- i2c_op(pd, OP_TX_TO_RX, 0);
- break;
- }
-
- real_pos = pd->pos - 2;
-
- if (pd->pos == pd->msg->len) {
- if (pd->stop_after_dma) {
- /* Simulate PIO end condition after DMA transfer */
- i2c_op(pd, OP_RX_STOP, 0);
- pd->pos++;
- break;
- }
-
- if (real_pos < 0) {
- i2c_op(pd, OP_RX_STOP, 0);
- break;
- }
- data = i2c_op(pd, OP_RX_STOP_DATA, 0);
- } else if (real_pos >= 0) {
- data = i2c_op(pd, OP_RX, 0);
+ /* switch from TX (address) to RX (data) adds two interrupts */
+ real_pos = pd->pos - 2;
+
+ if (pd->pos == -1) {
+ i2c_op(pd, OP_TX_FIRST);
+ } else if (pd->pos == 0) {
+ i2c_op(pd, OP_TX_TO_RX);
+ } else if (pd->pos == pd->msg->len) {
+ if (pd->stop_after_dma) {
+ /* Simulate PIO end condition after DMA transfer */
+ i2c_op(pd, OP_RX_STOP);
+ pd->pos++;
+ goto done;
}
- if (real_pos >= 0)
- pd->msg->buf[real_pos] = data;
- } while (0);
+ if (real_pos < 0)
+ i2c_op(pd, OP_RX_STOP);
+ else
+ pd->msg->buf[real_pos] = i2c_op(pd, OP_RX_STOP_DATA);
+ } else if (real_pos >= 0) {
+ pd->msg->buf[real_pos] = i2c_op(pd, OP_RX);
+ }
+ done:
pd->pos++;
return pd->pos == (pd->msg->len + 2);
}
@@ -444,7 +410,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
pd->sr |= sr; /* remember state */
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
- (pd->msg->flags & I2C_M_RD) ? "read" : "write",
+ str_read_write(pd->msg->flags & I2C_M_RD),
pd->pos, pd->msg->len);
/* Kick off TxDMA after preface was done */
@@ -467,7 +433,8 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
if (wakeup) {
pd->sr |= SW_DONE;
- wake_up(&pd->wait);
+ if (!pd->atomic_xfer)
+ wake_up(&pd->wait);
}
/* defeat write posting to avoid spurious WAIT interrupts */
@@ -476,34 +443,26 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd)
+static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd, bool terminate)
{
struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE
? pd->dma_rx : pd->dma_tx;
+ /* only allowed from thread context! */
+ if (terminate)
+ dmaengine_terminate_sync(chan);
+
dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg),
pd->msg->len, pd->dma_direction);
pd->dma_direction = DMA_NONE;
}
-static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd)
-{
- if (pd->dma_direction == DMA_NONE)
- return;
- else if (pd->dma_direction == DMA_FROM_DEVICE)
- dmaengine_terminate_all(pd->dma_rx);
- else if (pd->dma_direction == DMA_TO_DEVICE)
- dmaengine_terminate_all(pd->dma_tx);
-
- sh_mobile_i2c_dma_unmap(pd);
-}
-
static void sh_mobile_i2c_dma_callback(void *data)
{
struct sh_mobile_i2c_data *pd = data;
- sh_mobile_i2c_dma_unmap(pd);
+ sh_mobile_i2c_cleanup_dma(pd, false);
pd->pos = pd->msg->len;
pd->stop_after_dma = true;
@@ -518,7 +477,7 @@ static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev,
char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
int ret;
- chan = dma_request_slave_channel_reason(dev, chan_name);
+ chan = dma_request_chan(dev, chan_name);
if (IS_ERR(chan)) {
dev_dbg(dev, "request_channel failed for %s (%ld)\n", chan_name,
PTR_ERR(chan));
@@ -583,7 +542,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!txdesc) {
dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n");
- sh_mobile_i2c_cleanup_dma(pd);
+ sh_mobile_i2c_cleanup_dma(pd, false);
return;
}
@@ -593,7 +552,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
cookie = dmaengine_submit(txdesc);
if (dma_submit_error(cookie)) {
dev_dbg(pd->dev, "submitting dma failed, using PIO\n");
- sh_mobile_i2c_cleanup_dma(pd);
+ sh_mobile_i2c_cleanup_dma(pd, false);
return;
}
@@ -619,6 +578,9 @@ static void start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
pd->pos = -1;
pd->sr = 0;
+ if (pd->atomic_xfer)
+ return;
+
pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
if (pd->dma_buf)
sh_mobile_i2c_xfer_dma(pd);
@@ -675,15 +637,13 @@ static int poll_busy(struct sh_mobile_i2c_data *pd)
return i ? 0 : -ETIMEDOUT;
}
-static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
- struct i2c_msg *msgs,
- int num)
+static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd,
+ struct i2c_msg *msgs, int num)
{
- struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
struct i2c_msg *msg;
int err = 0;
int i;
- long timeout;
+ long time_left;
/* Wake up device and enable clock */
pm_runtime_get_sync(pd->dev);
@@ -698,20 +658,39 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
start_ch(pd, msg, do_start);
if (do_start)
- i2c_op(pd, OP_START, 0);
-
- /* The interrupt handler takes care of the rest... */
- timeout = wait_event_timeout(pd->wait,
- pd->sr & (ICSR_TACK | SW_DONE),
- adapter->timeout);
-
- /* 'stop_after_dma' tells if DMA transfer was complete */
- i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg, pd->stop_after_dma);
+ i2c_op(pd, OP_START);
+
+ if (pd->atomic_xfer) {
+ unsigned long j = jiffies + pd->adap.timeout;
+
+ time_left = time_before_eq(jiffies, j);
+ while (time_left &&
+ !(pd->sr & (ICSR_TACK | SW_DONE))) {
+ unsigned char sr = iic_rd(pd, ICSR);
+
+ if (sr & (ICSR_AL | ICSR_TACK |
+ ICSR_WAIT | ICSR_DTE)) {
+ sh_mobile_i2c_isr(0, pd);
+ udelay(150);
+ } else {
+ cpu_relax();
+ }
+ time_left = time_before_eq(jiffies, j);
+ }
+ } else {
+ /* The interrupt handler takes care of the rest... */
+ time_left = wait_event_timeout(pd->wait,
+ pd->sr & (ICSR_TACK | SW_DONE),
+ pd->adap.timeout);
+
+ /* 'stop_after_dma' tells if DMA xfer was complete */
+ i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg,
+ pd->stop_after_dma);
+ }
- if (!timeout) {
- dev_err(pd->dev, "Transfer request timed out\n");
+ if (!time_left) {
if (pd->dma_direction != DMA_NONE)
- sh_mobile_i2c_cleanup_dma(pd);
+ sh_mobile_i2c_cleanup_dma(pd, true);
err = -ETIMEDOUT;
break;
@@ -734,14 +713,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
return err ?: num;
}
+static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
+
+ pd->atomic_xfer = false;
+ return sh_mobile_xfer(pd, msgs, num);
+}
+
+static int sh_mobile_i2c_xfer_atomic(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
+
+ pd->atomic_xfer = true;
+ return sh_mobile_xfer(pd, msgs, num);
+}
+
static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
- .functionality = sh_mobile_i2c_func,
- .master_xfer = sh_mobile_i2c_xfer,
+ .functionality = sh_mobile_i2c_func,
+ .xfer = sh_mobile_i2c_xfer,
+ .xfer_atomic = sh_mobile_i2c_xfer_atomic,
};
static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
@@ -749,8 +749,7 @@ static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
};
/*
- * r8a7740 chip has lasting errata on I2C I/O pad reset.
- * this is work-around for it.
+ * r8a7740 has an errata regarding I2C I/O pad reset needing this workaround.
*/
static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
{
@@ -774,7 +773,7 @@ static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
iic_wr(pd, ICCR, ICCR_TRS);
udelay(10);
- return sh_mobile_i2c_init(pd);
+ return sh_mobile_i2c_v2_init(pd);
}
static const struct sh_mobile_dt_config default_dt_config = {
@@ -784,11 +783,6 @@ static const struct sh_mobile_dt_config default_dt_config = {
static const struct sh_mobile_dt_config fast_clock_dt_config = {
.clks_per_count = 2,
- .setup = sh_mobile_i2c_init,
-};
-
-static const struct sh_mobile_dt_config v2_freq_calc_dt_config = {
- .clks_per_count = 2,
.setup = sh_mobile_i2c_v2_init,
};
@@ -801,16 +795,16 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
{ .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
- { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
- { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config },
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
{},
};
@@ -831,20 +825,38 @@ static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd)
static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd)
{
- struct resource *res;
- resource_size_t n;
+ struct device_node *np = dev_of_node(&dev->dev);
int k = 0, ret;
- while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
- for (n = res->start; n <= res->end; n++) {
- ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
- 0, dev_name(&dev->dev), pd);
+ if (np) {
+ int irq;
+
+ while ((irq = platform_get_irq_optional(dev, k)) != -ENXIO) {
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(&dev->dev, irq, sh_mobile_i2c_isr,
+ 0, dev_name(&dev->dev), pd);
if (ret) {
- dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
+ dev_err(&dev->dev, "cannot request IRQ %d\n", irq);
return ret;
}
+ k++;
+ }
+ } else {
+ struct resource *res;
+ resource_size_t n;
+
+ while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
+ for (n = res->start; n <= res->end; n++) {
+ ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
+ 0, dev_name(&dev->dev), pd);
+ if (ret) {
+ dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
+ return ret;
+ }
+ }
+ k++;
}
- k++;
}
return k > 0 ? 0 : -ENOENT;
@@ -854,7 +866,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
{
struct sh_mobile_i2c_data *pd;
struct i2c_adapter *adap;
- struct resource *res;
const struct sh_mobile_dt_config *config;
int ret;
u32 bus_speed;
@@ -876,19 +887,16 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
pd->dev = &dev->dev;
platform_set_drvdata(dev, pd);
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
-
- pd->res = res;
- pd->reg = devm_ioremap_resource(&dev->dev, res);
+ pd->reg = devm_platform_get_and_ioremap_resource(dev, 0, &pd->res);
if (IS_ERR(pd->reg))
return PTR_ERR(pd->reg);
ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed);
- pd->bus_speed = (ret || !bus_speed) ? STANDARD_MODE : bus_speed;
+ pd->bus_speed = (ret || !bus_speed) ? I2C_MAX_STANDARD_MODE_FREQ : bus_speed;
pd->clks_per_count = 1;
/* Newer variants come with two new bits in ICIC */
- if (resource_size(res) > 0x17)
+ if (resource_size(pd->res) > 0x17)
pd->flags |= IIC_FLAG_HAS_ICIC67;
pm_runtime_enable(&dev->dev);
@@ -923,7 +931,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
adap->nr = dev->id;
adap->dev.of_node = dev->dev.of_node;
- strlcpy(adap->name, dev->name, sizeof(adap->name));
+ strscpy(adap->name, dev->name, sizeof(adap->name));
spin_lock_init(&pd->lock);
init_waitqueue_head(&pd->wait);
@@ -939,20 +947,41 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
return 0;
}
-static int sh_mobile_i2c_remove(struct platform_device *dev)
+static void sh_mobile_i2c_remove(struct platform_device *dev)
{
struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
i2c_del_adapter(&pd->adap);
sh_mobile_i2c_release_dma(pd);
pm_runtime_disable(&dev->dev);
+}
+
+static int sh_mobile_i2c_suspend(struct device *dev)
+{
+ struct sh_mobile_i2c_data *pd = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&pd->adap);
+ return 0;
+}
+
+static int sh_mobile_i2c_resume(struct device *dev)
+{
+ struct sh_mobile_i2c_data *pd = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_resumed(&pd->adap);
return 0;
}
+static const struct dev_pm_ops sh_mobile_i2c_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_mobile_i2c_suspend,
+ sh_mobile_i2c_resume)
+};
+
static struct platform_driver sh_mobile_i2c_driver = {
.driver = {
.name = "i2c-sh_mobile",
.of_match_table = sh_mobile_i2c_dt_ids,
+ .pm = pm_sleep_ptr(&sh_mobile_i2c_pm_ops),
},
.probe = sh_mobile_i2c_probe,
.remove = sh_mobile_i2c_remove,
@@ -971,6 +1000,7 @@ static void __exit sh_mobile_i2c_adap_exit(void)
module_exit(sh_mobile_i2c_adap_exit);
MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver");
-MODULE_AUTHOR("Magnus Damm and Wolfram Sang");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_AUTHOR("Wolfram Sang");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c-sh_mobile");
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index 2b6219d86b0f..49f8f4f1b0f0 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2004 Steven J. Hill
* Copyright (C) 2001,2002,2003 Broadcom Corporation
* Copyright (C) 1995-2000 Simon G. Vogl
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -151,7 +142,7 @@ static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
static struct i2c_adapter sibyte_board_adapter[2] = {
{
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = NULL,
.algo_data = &sibyte_board_data[0],
.nr = 0,
@@ -159,7 +150,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = {
},
{
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = NULL,
.algo_data = &sibyte_board_data[1],
.nr = 1,
@@ -189,6 +180,7 @@ static void __exit i2c_sibyte_exit(void)
module_init(i2c_sibyte_init);
module_exit(i2c_sibyte_exit);
-MODULE_AUTHOR("Kip Walker (Broadcom Corp.), Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Kip Walker (Broadcom Corp.)");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-simtec.c b/drivers/i2c/busses/i2c-simtec.c
index adca51a99487..d90606048611 100644
--- a/drivers/i2c/busses/i2c-simtec.c
+++ b/drivers/i2c/busses/i2c-simtec.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* Simtec Generic I2C Controller
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -107,7 +99,7 @@ static int simtec_i2c_probe(struct platform_device *dev)
pd->adap.algo_data = &pd->bit;
pd->adap.dev.parent = &dev->dev;
- strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name));
+ strscpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name));
pd->bit.data = pd;
pd->bit.setsda = simtec_i2c_setsda;
@@ -134,7 +126,7 @@ static int simtec_i2c_probe(struct platform_device *dev)
return ret;
}
-static int simtec_i2c_remove(struct platform_device *dev)
+static void simtec_i2c_remove(struct platform_device *dev)
{
struct simtec_i2c_data *pd = platform_get_drvdata(dev);
@@ -143,8 +135,6 @@ static int simtec_i2c_remove(struct platform_device *dev)
iounmap(pd->reg);
release_mem_region(pd->ioarea->start, resource_size(pd->ioarea));
kfree(pd);
-
- return 0;
}
/* device driver */
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
deleted file mode 100644
index 87197ece0f90..000000000000
--- a/drivers/i2c/busses/i2c-sirf.c
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * I2C bus driver for CSR SiRFprimaII
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
-#define SIRFSOC_I2C_CLK_CTRL 0x00
-#define SIRFSOC_I2C_STATUS 0x0C
-#define SIRFSOC_I2C_CTRL 0x10
-#define SIRFSOC_I2C_IO_CTRL 0x14
-#define SIRFSOC_I2C_SDA_DELAY 0x18
-#define SIRFSOC_I2C_CMD_START 0x1C
-#define SIRFSOC_I2C_CMD_BUF 0x30
-#define SIRFSOC_I2C_DATA_BUF 0x80
-
-#define SIRFSOC_I2C_CMD_BUF_MAX 16
-#define SIRFSOC_I2C_DATA_BUF_MAX 16
-
-#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04)
-#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8))
-#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8)
-
-#define SIRFSOC_I2C_DIV_MASK (0xFFFF)
-
-/* I2C status flags */
-#define SIRFSOC_I2C_STAT_BUSY BIT(0)
-#define SIRFSOC_I2C_STAT_TIP BIT(1)
-#define SIRFSOC_I2C_STAT_NACK BIT(2)
-#define SIRFSOC_I2C_STAT_TR_INT BIT(4)
-#define SIRFSOC_I2C_STAT_STOP BIT(6)
-#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8)
-#define SIRFSOC_I2C_STAT_ERR BIT(9)
-#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16)
-
-/* I2C control flags */
-#define SIRFSOC_I2C_RESET BIT(0)
-#define SIRFSOC_I2C_CORE_EN BIT(1)
-#define SIRFSOC_I2C_MASTER_MODE BIT(2)
-#define SIRFSOC_I2C_CMD_DONE_EN BIT(11)
-#define SIRFSOC_I2C_ERR_INT_EN BIT(12)
-
-#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF)
-#define SIRFSOC_I2C_SCLF_FILTER (3<<8)
-
-#define SIRFSOC_I2C_START_CMD BIT(0)
-
-#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7)
-#define SIRFSOC_I2C_NACK BIT(3)
-#define SIRFSOC_I2C_WRITE BIT(4)
-#define SIRFSOC_I2C_READ BIT(5)
-#define SIRFSOC_I2C_STOP BIT(6)
-#define SIRFSOC_I2C_START BIT(7)
-
-#define SIRFSOC_I2C_DEFAULT_SPEED 100000
-#define SIRFSOC_I2C_ERR_NOACK 1
-#define SIRFSOC_I2C_ERR_TIMEOUT 2
-
-struct sirfsoc_i2c {
- void __iomem *base;
- struct clk *clk;
- u32 cmd_ptr; /* Current position in CMD buffer */
- u8 *buf; /* Buffer passed by user */
- u32 msg_len; /* Message length */
- u32 finished_len; /* number of bytes read/written */
- u32 read_cmd_len; /* number of read cmd sent */
- int msg_read; /* 1 indicates a read message */
- int err_status; /* 1 indicates an error on bus */
-
- u32 sda_delay; /* For suspend/resume */
- u32 clk_div;
- int last; /* Last message in transfer, STOP cmd can be sent */
-
- struct completion done; /* indicates completion of message transfer */
- struct i2c_adapter adapter;
-};
-
-static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic)
-{
- u32 data = 0;
- int i;
-
- for (i = 0; i < siic->read_cmd_len; i++) {
- if (!(i & 0x3))
- data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i);
- siic->buf[siic->finished_len++] =
- (u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >>
- SIRFSOC_I2C_DATA_SHIFT(i));
- }
-}
-
-static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic)
-{
- u32 regval;
- int i = 0;
-
- if (siic->msg_read) {
- while (((siic->finished_len + i) < siic->msg_len)
- && (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) {
- regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0);
- if (((siic->finished_len + i) ==
- (siic->msg_len - 1)) && siic->last)
- regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK;
- writel(regval,
- siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
- i++;
- }
-
- siic->read_cmd_len = i;
- } else {
- while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1)
- && (siic->finished_len < siic->msg_len)) {
- regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0);
- if ((siic->finished_len == (siic->msg_len - 1))
- && siic->last)
- regval |= SIRFSOC_I2C_STOP;
- writel(regval,
- siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
- writel(siic->buf[siic->finished_len++],
- siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
- }
- }
- siic->cmd_ptr = 0;
-
- /* Trigger the transfer */
- writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START);
-}
-
-static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
-{
- struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id;
- u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS);
-
- if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
- /* Error conditions */
- siic->err_status = SIRFSOC_I2C_ERR_NOACK;
- writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
-
- if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
- dev_dbg(&siic->adapter.dev, "ACK not received\n");
- else
- dev_err(&siic->adapter.dev, "I2C error\n");
-
- /*
- * Due to hardware ANOMALY, we need to reset I2C earlier after
- * we get NOACK while accessing non-existing clients, otherwise
- * we will get errors even we access existing clients later
- */
- writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
- siic->base + SIRFSOC_I2C_CTRL);
- while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
- cpu_relax();
-
- complete(&siic->done);
- } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
- /* CMD buffer execution complete */
- if (siic->msg_read)
- i2c_sirfsoc_read_data(siic);
- if (siic->finished_len == siic->msg_len)
- complete(&siic->done);
- else /* Fill a new CMD buffer for left data */
- i2c_sirfsoc_queue_cmd(siic);
-
- writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS);
- }
-
- return IRQ_HANDLED;
-}
-
-static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
- struct i2c_msg *msg)
-{
- unsigned char addr;
- u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE;
-
- /* no data and last message -> add STOP */
- if (siic->last && (msg->len == 0))
- regval |= SIRFSOC_I2C_STOP;
-
- writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
-
- addr = i2c_8bit_addr_from_msg(msg);
-
- /* Reverse direction bit */
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- addr ^= 1;
-
- writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
-}
-
-static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
-{
- u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
- /* timeout waiting for the xfer to finish or fail */
- int timeout = msecs_to_jiffies((msg->len + 1) * 50);
-
- i2c_sirfsoc_set_address(siic, msg);
-
- writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN,
- siic->base + SIRFSOC_I2C_CTRL);
- i2c_sirfsoc_queue_cmd(siic);
-
- if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
- siic->err_status = SIRFSOC_I2C_ERR_TIMEOUT;
- dev_err(&siic->adapter.dev, "Transfer timeout\n");
- }
-
- writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
- siic->base + SIRFSOC_I2C_CTRL);
- writel(0, siic->base + SIRFSOC_I2C_CMD_START);
-
- /* i2c control doesn't response, reset it */
- if (siic->err_status == SIRFSOC_I2C_ERR_TIMEOUT) {
- writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
- siic->base + SIRFSOC_I2C_CTRL);
- while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
- cpu_relax();
- }
- return siic->err_status ? -EAGAIN : 0;
-}
-
-static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
-{
- struct sirfsoc_i2c *siic = adap->algo_data;
- int i, ret;
-
- clk_enable(siic->clk);
-
- for (i = 0; i < num; i++) {
- siic->buf = msgs[i].buf;
- siic->msg_len = msgs[i].len;
- siic->msg_read = !!(msgs[i].flags & I2C_M_RD);
- siic->err_status = 0;
- siic->cmd_ptr = 0;
- siic->finished_len = 0;
- siic->last = (i == (num - 1));
-
- ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]);
- if (ret) {
- clk_disable(siic->clk);
- return ret;
- }
- }
-
- clk_disable(siic->clk);
- return num;
-}
-
-/* I2C algorithms associated with this master controller driver */
-static const struct i2c_algorithm i2c_sirfsoc_algo = {
- .master_xfer = i2c_sirfsoc_xfer,
- .functionality = i2c_sirfsoc_func,
-};
-
-static int i2c_sirfsoc_probe(struct platform_device *pdev)
-{
- struct sirfsoc_i2c *siic;
- struct i2c_adapter *adap;
- struct resource *mem_res;
- struct clk *clk;
- int bitrate;
- int ctrl_speed;
- int irq;
-
- int err;
- u32 regval;
-
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- dev_err(&pdev->dev, "Clock get failed\n");
- goto err_get_clk;
- }
-
- err = clk_prepare(clk);
- if (err) {
- dev_err(&pdev->dev, "Clock prepare failed\n");
- goto err_clk_prep;
- }
-
- err = clk_enable(clk);
- if (err) {
- dev_err(&pdev->dev, "Clock enable failed\n");
- goto err_clk_en;
- }
-
- ctrl_speed = clk_get_rate(clk);
-
- siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL);
- if (!siic) {
- err = -ENOMEM;
- goto out;
- }
- adap = &siic->adapter;
- adap->class = I2C_CLASS_DEPRECATED;
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- siic->base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(siic->base)) {
- err = PTR_ERR(siic->base);
- goto out;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = irq;
- goto out;
- }
- err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0,
- dev_name(&pdev->dev), siic);
- if (err)
- goto out;
-
- adap->algo = &i2c_sirfsoc_algo;
- adap->algo_data = siic;
- adap->retries = 3;
-
- adap->dev.of_node = pdev->dev.of_node;
- adap->dev.parent = &pdev->dev;
- adap->nr = pdev->id;
-
- strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name));
-
- platform_set_drvdata(pdev, adap);
- init_completion(&siic->done);
-
- /* Controller initialisation */
-
- writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
- while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
- cpu_relax();
- writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
- siic->base + SIRFSOC_I2C_CTRL);
-
- siic->clk = clk;
-
- err = of_property_read_u32(pdev->dev.of_node,
- "clock-frequency", &bitrate);
- if (err < 0)
- bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
-
- /*
- * Due to some hardware design issues, we need to tune the formula.
- * Since i2c is open drain interface that allows the slave to
- * stall the transaction by holding the SCL line at '0', the RTL
- * implementation is waiting for SCL feedback from the pin after
- * setting it to High-Z ('1'). This wait adds to the high-time
- * interval counter few cycles of the input synchronization
- * (depending on the SCL_FILTER_REG field), and also the time it
- * takes for the board pull-up resistor to rise the SCL line.
- * For slow SCL settings these additions are negligible,
- * but they start to affect the speed when clock is set to faster
- * frequencies.
- * Through the actual tests, use the different user_div value(which
- * in the divider formula 'Fio / (Fi2c * user_div)') to adapt
- * the different ranges of i2c bus clock frequency, to make the SCL
- * more accurate.
- */
- if (bitrate <= 30000)
- regval = ctrl_speed / (bitrate * 5);
- else if (bitrate > 30000 && bitrate <= 280000)
- regval = (2 * ctrl_speed) / (bitrate * 11);
- else
- regval = ctrl_speed / (bitrate * 6);
-
- writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
- if (regval > 0xFF)
- writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY);
- else
- writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY);
-
- err = i2c_add_numbered_adapter(adap);
- if (err < 0)
- goto out;
-
- clk_disable(clk);
-
- dev_info(&pdev->dev, " I2C adapter ready to operate\n");
-
- return 0;
-
-out:
- clk_disable(clk);
-err_clk_en:
- clk_unprepare(clk);
-err_clk_prep:
- clk_put(clk);
-err_get_clk:
- return err;
-}
-
-static int i2c_sirfsoc_remove(struct platform_device *pdev)
-{
- struct i2c_adapter *adapter = platform_get_drvdata(pdev);
- struct sirfsoc_i2c *siic = adapter->algo_data;
-
- writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
- i2c_del_adapter(adapter);
- clk_unprepare(siic->clk);
- clk_put(siic->clk);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int i2c_sirfsoc_suspend(struct device *dev)
-{
- struct i2c_adapter *adapter = dev_get_drvdata(dev);
- struct sirfsoc_i2c *siic = adapter->algo_data;
-
- clk_enable(siic->clk);
- siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY);
- siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL);
- clk_disable(siic->clk);
- return 0;
-}
-
-static int i2c_sirfsoc_resume(struct device *dev)
-{
- struct i2c_adapter *adapter = dev_get_drvdata(dev);
- struct sirfsoc_i2c *siic = adapter->algo_data;
-
- clk_enable(siic->clk);
- writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
- while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
- cpu_relax();
- writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
- siic->base + SIRFSOC_I2C_CTRL);
- writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL);
- writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY);
- clk_disable(siic->clk);
- return 0;
-}
-
-static const struct dev_pm_ops i2c_sirfsoc_pm_ops = {
- .suspend = i2c_sirfsoc_suspend,
- .resume = i2c_sirfsoc_resume,
-};
-#endif
-
-static const struct of_device_id sirfsoc_i2c_of_match[] = {
- { .compatible = "sirf,prima2-i2c", },
- {},
-};
-MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
-
-static struct platform_driver i2c_sirfsoc_driver = {
- .driver = {
- .name = "sirfsoc_i2c",
-#ifdef CONFIG_PM
- .pm = &i2c_sirfsoc_pm_ops,
-#endif
- .of_match_table = sirfsoc_i2c_of_match,
- },
- .probe = i2c_sirfsoc_probe,
- .remove = i2c_sirfsoc_remove,
-};
-module_platform_driver(i2c_sirfsoc_driver);
-
-MODULE_DESCRIPTION("SiRF SoC I2C master controller driver");
-MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
- "Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index d543a9867ba4..ca06fffb8f61 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/* Note: we assume there can only be one SIS5595 with one SMBus interface */
@@ -183,11 +175,11 @@ static int sis5595_setup(struct pci_dev *SIS5595_dev)
if (force_addr) {
dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base);
- if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)
- != PCIBIOS_SUCCESSFUL)
+ retval = pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base);
+ if (retval != PCIBIOS_SUCCESSFUL)
goto error;
- if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)
- != PCIBIOS_SUCCESSFUL)
+ retval = pci_read_config_word(SIS5595_dev, ACPI_BASE, &a);
+ if (retval != PCIBIOS_SUCCESSFUL)
goto error;
if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
/* doesn't work for some chips! */
@@ -196,16 +188,16 @@ static int sis5595_setup(struct pci_dev *SIS5595_dev)
}
}
- if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
- != PCIBIOS_SUCCESSFUL)
+ retval = pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val);
+ if (retval != PCIBIOS_SUCCESSFUL)
goto error;
if ((val & 0x80) == 0) {
dev_info(&SIS5595_dev->dev, "enabling ACPI\n");
- if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80)
- != PCIBIOS_SUCCESSFUL)
+ retval = pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80);
+ if (retval != PCIBIOS_SUCCESSFUL)
goto error;
- if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
- != PCIBIOS_SUCCESSFUL)
+ retval = pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val);
+ if (retval != PCIBIOS_SUCCESSFUL)
goto error;
if ((val & 0x80) == 0) {
/* doesn't work for some chips? */
@@ -265,7 +257,7 @@ static int sis5595_transaction(struct i2c_adapter *adap)
if (temp & 0x20) {
dev_err(&adap->dev, "Bus collision! SMBus may be locked until "
"next hard reset (or not...)\n");
- /* Clock stops and slave is stuck in mid-transmission */
+ /* Clock stops and target is stuck in mid-transmission */
result = -EIO;
}
@@ -361,7 +353,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis5595_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 1e6805b5cef2..a19c3d251804 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -105,7 +97,7 @@ MODULE_PARM_DESC(high_clock,
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
-/* SMBus base adress */
+/* SMBus base address */
static unsigned short smbus_base;
/* supported chips */
@@ -478,7 +470,7 @@ static int sis630_setup(struct pci_dev *sis630_dev)
if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
sis630_driver.name)) {
dev_err(&sis630_dev->dev,
- "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
+ "I/O Region 0x%04x-0x%04x for SMBus already in use.\n",
smbus_base + SMB_STS,
smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
retval = -EBUSY;
@@ -501,7 +493,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis630_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.retries = 3
};
@@ -517,6 +509,8 @@ MODULE_DEVICE_TABLE(pci, sis630_ids);
static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
+ int ret;
+
if (sis630_setup(dev)) {
dev_err(&dev->dev,
"SIS630 compatible bus not detected, "
@@ -528,9 +522,17 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
sis630_adapter.dev.parent = &dev->dev;
snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
- "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
+ "SMBus SIS630 adapter at %04x", smbus_base + SMB_STS);
+
+ ret = i2c_add_adapter(&sis630_adapter);
+ if (ret)
+ goto release_region;
+
+ return 0;
- return i2c_add_adapter(&sis630_adapter);
+release_region:
+ release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
+ return ret;
}
static void sis630_remove(struct pci_dev *dev)
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index 44b904426073..77529dda6fcd 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -236,7 +228,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis96x_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
index a94e724f51dc..1b490525d8dd 100644
--- a/drivers/i2c/busses/i2c-sprd.c
+++ b/drivers/i2c/busses/i2c-sprd.c
@@ -12,8 +12,8 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -71,6 +71,8 @@
/* timeout (ms) for pm runtime autosuspend */
#define SPRD_I2C_PM_TIMEOUT 1000
+/* timeout (ms) for transfer message */
+#define I2C_XFER_TIMEOUT 1000
/* SPRD i2c data structure */
struct sprd_i2c {
@@ -86,7 +88,6 @@ struct sprd_i2c {
u32 count;
int irq;
int err;
- bool is_suspended;
};
static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
@@ -244,6 +245,7 @@ static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap,
struct i2c_msg *msg, bool is_last_msg)
{
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
+ unsigned long time_left;
i2c_dev->msg = msg;
i2c_dev->buf = msg->buf;
@@ -273,21 +275,21 @@ static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap,
sprd_i2c_opt_start(i2c_dev);
- wait_for_completion(&i2c_dev->complete);
+ time_left = wait_for_completion_timeout(&i2c_dev->complete,
+ msecs_to_jiffies(I2C_XFER_TIMEOUT));
+ if (!time_left)
+ return -ETIMEDOUT;
return i2c_dev->err;
}
-static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs, int num)
+static int sprd_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
{
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
int im, ret;
- if (i2c_dev->is_suspended)
- return -EBUSY;
-
- ret = pm_runtime_get_sync(i2c_dev->dev);
+ ret = pm_runtime_resume_and_get(i2c_dev->dev);
if (ret < 0)
return ret;
@@ -300,7 +302,6 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im++], 1);
err_msg:
- pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return ret < 0 ? ret : im;
@@ -312,7 +313,7 @@ static u32 sprd_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm sprd_i2c_algo = {
- .master_xfer = sprd_i2c_master_xfer,
+ .xfer = sprd_i2c_xfer,
.functionality = sprd_i2c_func,
};
@@ -340,9 +341,9 @@ static void sprd_i2c_set_clk(struct sprd_i2c *i2c_dev, u32 freq)
writel(div1, i2c_dev->base + ADDR_DVD1);
/* Start hold timing = hold time(us) * source clock */
- if (freq == 400000)
+ if (freq == I2C_MAX_FAST_MODE_FREQ)
writel((6 * apb_clk) / 10000000, i2c_dev->base + ADDR_STA0_DVD);
- else if (freq == 100000)
+ else if (freq == I2C_MAX_STANDARD_MODE_FREQ)
writel((4 * apb_clk) / 1000000, i2c_dev->base + ADDR_STA0_DVD);
}
@@ -376,12 +377,12 @@ static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id)
i2c_tran = i2c_dev->count;
/*
- * If we got one ACK from slave when writing data, and we did not
+ * If we got one ACK from target when writing data, and we did not
* finish this transmission (i2c_tran is not zero), then we should
* continue to write data.
*
* For reading data, ack is always true, if i2c_tran is not 0 which
- * means we still need to contine to read data from slave.
+ * means we still need to contine to read data from target.
*/
if (i2c_tran && ack) {
sprd_i2c_data_transfer(i2c_dev);
@@ -391,7 +392,7 @@ static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id)
i2c_dev->err = 0;
/*
- * If we did not get one ACK from slave when writing data, we should
+ * If we did not get one ACK from target when writing data, we should
* return -EIO to notify users.
*/
if (!ack)
@@ -420,10 +421,10 @@ static irqreturn_t sprd_i2c_isr(int irq, void *dev_id)
i2c_tran = i2c_dev->count;
/*
- * If we did not get one ACK from slave when writing data, then we
+ * If we did not get one ACK from target when writing data, then we
* should finish this transmission since we got some errors.
*
- * When writing data, if i2c_tran == 0 which means we have writen
+ * When writing data, if i2c_tran == 0 which means we have written
* done all data, then we can finish this transmission.
*
* When reading data, if conut < rx fifo full threshold, which
@@ -469,9 +470,9 @@ static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev)
i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable");
if (IS_ERR(i2c_dev->clk)) {
- dev_warn(i2c_dev->dev, "i2c%d can't get the enable clock\n",
- i2c_dev->adap.nr);
- i2c_dev->clk = NULL;
+ dev_err(i2c_dev->dev, "i2c%d can't get the enable clock\n",
+ i2c_dev->adap.nr);
+ return PTR_ERR(i2c_dev->clk);
}
return 0;
@@ -481,7 +482,6 @@ static int sprd_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sprd_i2c *i2c_dev;
- struct resource *res;
u32 prop;
int ret;
@@ -491,23 +491,20 @@ static int sprd_i2c_probe(struct platform_device *pdev)
if (!i2c_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(dev, res);
+ i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
i2c_dev->irq = platform_get_irq(pdev, 0);
- if (i2c_dev->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
+ if (i2c_dev->irq < 0)
return i2c_dev->irq;
- }
i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
init_completion(&i2c_dev->complete);
snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
"%s", "sprd-i2c");
- i2c_dev->bus_freq = 100000;
+ i2c_dev->bus_freq = I2C_MAX_STANDARD_MODE_FREQ;
i2c_dev->adap.owner = THIS_MODULE;
i2c_dev->dev = dev;
i2c_dev->adap.retries = 3;
@@ -521,10 +518,14 @@ static int sprd_i2c_probe(struct platform_device *pdev)
i2c_dev->bus_freq = prop;
/* We only support 100k and 400k now, otherwise will return error. */
- if (i2c_dev->bus_freq != 100000 && i2c_dev->bus_freq != 400000)
+ if (i2c_dev->bus_freq != I2C_MAX_STANDARD_MODE_FREQ &&
+ i2c_dev->bus_freq != I2C_MAX_FAST_MODE_FREQ)
return -EINVAL;
- sprd_i2c_clk_init(i2c_dev);
+ ret = sprd_i2c_clk_init(i2c_dev);
+ if (ret)
+ return ret;
+
platform_set_drvdata(pdev, i2c_dev);
ret = clk_prepare_enable(i2c_dev->clk);
@@ -557,7 +558,6 @@ static int sprd_i2c_probe(struct platform_device *pdev)
goto err_rpm_put;
}
- pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return 0;
@@ -568,58 +568,52 @@ err_rpm_put:
return ret;
}
-static int sprd_i2c_remove(struct platform_device *pdev)
+static void sprd_i2c_remove(struct platform_device *pdev)
{
struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev);
int ret;
ret = pm_runtime_get_sync(i2c_dev->dev);
if (ret < 0)
- return ret;
+ dev_err(&pdev->dev, "Failed to resume device (%pe)\n", ERR_PTR(ret));
i2c_del_adapter(&i2c_dev->adap);
- clk_disable_unprepare(i2c_dev->clk);
+
+ if (ret >= 0)
+ clk_disable_unprepare(i2c_dev->clk);
pm_runtime_put_noidle(i2c_dev->dev);
pm_runtime_disable(i2c_dev->dev);
-
- return 0;
}
-static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
+static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
-
- i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = true;
- i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
- return pm_runtime_force_suspend(pdev);
+ i2c_mark_adapter_suspended(&i2c_dev->adap);
+ return pm_runtime_force_suspend(dev);
}
-static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
+static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = false;
- i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
-
- return pm_runtime_force_resume(pdev);
+ i2c_mark_adapter_resumed(&i2c_dev->adap);
+ return pm_runtime_force_resume(dev);
}
-static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev)
+static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
clk_disable_unprepare(i2c_dev->clk);
return 0;
}
-static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev)
+static int __maybe_unused sprd_i2c_runtime_resume(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(i2c_dev->clk);
@@ -643,6 +637,7 @@ static const struct of_device_id sprd_i2c_of_match[] = {
{ .compatible = "sprd,sc9860-i2c", },
{},
};
+MODULE_DEVICE_TABLE(of, sprd_i2c_of_match);
static struct platform_driver sprd_i2c_driver = {
.probe = sprd_i2c_probe,
@@ -654,8 +649,7 @@ static struct platform_driver sprd_i2c_driver = {
},
};
-static int sprd_i2c_init(void)
-{
- return platform_driver_register(&sprd_i2c_driver);
-}
-arch_initcall_sync(sprd_i2c_init);
+module_platform_driver(sprd_i2c_driver);
+
+MODULE_DESCRIPTION("Spreadtrum I2C controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
index 9e62f893958a..97d70e667227 100644
--- a/drivers/i2c/busses/i2c-st.c
+++ b/drivers/i2c/busses/i2c-st.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 STMicroelectronics
*
- * I2C master mode controller driver, used in STMicroelectronics devices.
+ * I2C controller driver, used in STMicroelectronics devices.
*
* Author: Maxime Coquelin <maxime.coquelin@st.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -16,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -153,8 +151,8 @@ struct st_i2c_timings {
/**
* struct st_i2c_client - client specific data
- * @addr: 8-bit slave addr, including r/w bit
- * @count: number of bytes to be transfered
+ * @addr: 8-bit target addr, including r/w bit
+ * @count: number of bytes to be transferred
* @xfered: number of bytes already transferred
* @buf: data buffer
* @result: result of the transfer
@@ -216,7 +214,7 @@ static inline void st_i2c_clr_bits(void __iomem *reg, u32 mask)
*/
static struct st_i2c_timings i2c_timings[] = {
[I2C_MODE_STANDARD] = {
- .rate = 100000,
+ .rate = I2C_MAX_STANDARD_MODE_FREQ,
.rep_start_hold = 4400,
.rep_start_setup = 5170,
.start_hold = 4400,
@@ -225,7 +223,7 @@ static struct st_i2c_timings i2c_timings[] = {
.bus_free_time = 5170,
},
[I2C_MODE_FAST] = {
- .rate = 400000,
+ .rate = I2C_MAX_FAST_MODE_FREQ,
.rep_start_hold = 660,
.rep_start_setup = 660,
.start_hold = 660,
@@ -425,23 +423,20 @@ static void st_i2c_wr_fill_tx_fifo(struct st_i2c_dev *i2c_dev)
tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT);
tx_fstat &= SSC_TX_FSTAT_STATUS;
- if (c->count < (SSC_TXFIFO_SIZE - tx_fstat))
- i = c->count;
- else
- i = SSC_TXFIFO_SIZE - tx_fstat;
-
- for (; i > 0; i--, c->count--, c->buf++)
+ for (i = min(c->count, SSC_TXFIFO_SIZE - tx_fstat);
+ i > 0; i--, c->count--, c->buf++)
st_i2c_write_tx_fifo(i2c_dev, *c->buf);
}
/**
* st_i2c_rd_fill_tx_fifo() - Fill the Tx FIFO in read mode
* @i2c_dev: Controller's private data
+ * @max: Maximum amount of data to fill into the Tx FIFO
*
* This functions fills the Tx FIFO with fixed pattern when
* in read mode to trigger clock.
*/
-static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, int max)
+static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, u32 max)
{
struct st_i2c_client *c = &i2c_dev->client;
u32 tx_fstat, sta;
@@ -454,12 +449,8 @@ static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, int max)
tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT);
tx_fstat &= SSC_TX_FSTAT_STATUS;
- if (max < (SSC_TXFIFO_SIZE - tx_fstat))
- i = max;
- else
- i = SSC_TXFIFO_SIZE - tx_fstat;
-
- for (; i > 0; i--, c->xfered++)
+ for (i = min(max, SSC_TXFIFO_SIZE - tx_fstat);
+ i > 0; i--, c->xfered++)
st_i2c_write_tx_fifo(i2c_dev, 0xff);
}
@@ -526,7 +517,7 @@ static void st_i2c_handle_write(struct st_i2c_dev *i2c_dev)
}
/**
- * st_i2c_handle_write() - Handle FIFO enmpty interrupt in case of read
+ * st_i2c_handle_read() - Handle FIFO empty interrupt in case of read
* @i2c_dev: Controller's private data
*/
static void st_i2c_handle_read(struct st_i2c_dev *i2c_dev)
@@ -560,7 +551,7 @@ static void st_i2c_handle_read(struct st_i2c_dev *i2c_dev)
}
/**
- * st_i2c_isr() - Interrupt routine
+ * st_i2c_isr_thread() - Interrupt routine
* @irq: interrupt number
* @data: Controller's private data
*/
@@ -649,7 +640,7 @@ static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg,
{
struct st_i2c_client *c = &i2c_dev->client;
u32 ctl, i2c, it;
- unsigned long timeout;
+ unsigned long time_left;
int ret;
c->addr = i2c_8bit_addr_from_msg(msg);
@@ -669,7 +660,7 @@ static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg,
i2c |= SSC_I2C_ACKG;
st_i2c_set_bits(i2c_dev->base + SSC_I2C, i2c);
- /* Write slave address */
+ /* Write target address */
st_i2c_write_tx_fifo(i2c_dev, c->addr);
/* Pre-fill Tx fifo with data in case of write */
@@ -687,15 +678,12 @@ static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg,
st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STRTG);
}
- timeout = wait_for_completion_timeout(&i2c_dev->complete,
- i2c_dev->adap.timeout);
+ time_left = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
ret = c->result;
- if (!timeout) {
- dev_err(i2c_dev->dev, "Write to slave 0x%x timed out\n",
- c->addr);
+ if (!time_left)
ret = -ETIMEDOUT;
- }
i2c = SSC_I2C_STOPG | SSC_I2C_REPSTRTG;
st_i2c_clr_bits(i2c_dev->base + SSC_I2C, i2c);
@@ -742,7 +730,6 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap,
return (ret < 0) ? ret : i;
}
-#ifdef CONFIG_PM_SLEEP
static int st_i2c_suspend(struct device *dev)
{
struct st_i2c_dev *i2c_dev = dev_get_drvdata(dev);
@@ -764,11 +751,7 @@ static int st_i2c_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume);
-#define ST_I2C_PM (&st_i2c_pm)
-#else
-#define ST_I2C_PM NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume);
static u32 st_i2c_func(struct i2c_adapter *adap)
{
@@ -776,7 +759,7 @@ static u32 st_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm st_i2c_algo = {
- .master_xfer = st_i2c_xfer,
+ .xfer = st_i2c_xfer,
.functionality = st_i2c_func,
};
@@ -819,8 +802,7 @@ static int st_i2c_probe(struct platform_device *pdev)
if (!i2c_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
@@ -838,7 +820,7 @@ static int st_i2c_probe(struct platform_device *pdev)
i2c_dev->mode = I2C_MODE_STANDARD;
ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
- if ((!ret) && (clk_rate == 400000))
+ if (!ret && (clk_rate == I2C_MAX_FAST_MODE_FREQ))
i2c_dev->mode = I2C_MODE_FAST;
i2c_dev->dev = &pdev->dev;
@@ -883,13 +865,11 @@ static int st_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int st_i2c_remove(struct platform_device *pdev)
+static void st_i2c_remove(struct platform_device *pdev)
{
struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c_dev->adap);
-
- return 0;
}
static const struct of_device_id st_i2c_match[] = {
@@ -903,7 +883,7 @@ static struct platform_driver st_i2c_driver = {
.driver = {
.name = "st-i2c",
.of_match_table = st_i2c_match,
- .pm = ST_I2C_PM,
+ .pm = pm_sleep_ptr(&st_i2c_pm),
},
.probe = st_i2c_probe,
.remove = st_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c
index d75fbcbf02ef..becf8977979f 100644
--- a/drivers/i2c/busses/i2c-stm32.c
+++ b/drivers/i2c/busses/i2c-stm32.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c-stm32.c
*
* Copyright (C) M'boumba Cedric Madianga 2017
* Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
- *
- * License terms: GNU General Public License (GPL), version 2
*/
#include "i2c-stm32.h"
@@ -21,13 +20,15 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* Request and configure I2C TX dma channel */
- dma->chan_tx = dma_request_slave_channel(dev, "tx");
- if (!dma->chan_tx) {
- dev_dbg(dev, "can't request DMA tx channel\n");
- ret = -EINVAL;
+ dma->chan_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(dma->chan_tx)) {
+ ret = PTR_ERR(dma->chan_tx);
+ if (ret != -ENODEV)
+ dev_err_probe(dev, ret, "can't request DMA tx channel\n");
+
goto fail_al;
}
@@ -43,10 +44,12 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
}
/* Request and configure I2C RX dma channel */
- dma->chan_rx = dma_request_slave_channel(dev, "rx");
- if (!dma->chan_rx) {
- dev_err(dev, "can't request DMA rx channel\n");
- ret = -EINVAL;
+ dma->chan_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(dma->chan_rx)) {
+ ret = PTR_ERR(dma->chan_rx);
+ if (ret != -ENODEV)
+ dev_err_probe(dev, ret, "can't request DMA rx channel\n");
+
goto fail_tx;
}
@@ -74,9 +77,8 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
- dev_info(dev, "can't use DMA\n");
- return NULL;
+ return ERR_PTR(ret);
}
void stm32_i2c_dma_free(struct stm32_i2c_dma *dma)
@@ -99,7 +101,6 @@ int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
void *dma_async_param)
{
struct dma_async_tx_descriptor *txdesc;
- struct device *chan_dev;
int ret;
if (rd_wr) {
@@ -113,11 +114,10 @@ int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
}
dma->dma_len = len;
- chan_dev = dma->chan_using->device->dev;
- dma->dma_buf = dma_map_single(chan_dev, buf, dma->dma_len,
+ dma->dma_buf = dma_map_single(dev, buf, dma->dma_len,
dma->dma_data_dir);
- if (dma_mapping_error(chan_dev, dma->dma_buf)) {
+ if (dma_mapping_error(dev, dma->dma_buf)) {
dev_err(dev, "DMA mapping failed\n");
return -EINVAL;
}
@@ -147,7 +147,7 @@ int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma,
return 0;
err:
- dma_unmap_single(chan_dev, dma->dma_buf, dma->dma_len,
+ dma_unmap_single(dev, dma->dma_buf, dma->dma_len,
dma->dma_data_dir);
return ret;
}
diff --git a/drivers/i2c/busses/i2c-stm32.h b/drivers/i2c/busses/i2c-stm32.h
index 868755f82f88..2c21893905a3 100644
--- a/drivers/i2c/busses/i2c-stm32.h
+++ b/drivers/i2c/busses/i2c-stm32.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* i2c-stm32.h
*
diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c
index ba600d77a3f8..b3d56d0aa9d0 100644
--- a/drivers/i2c/busses/i2c-stm32f4.c
+++ b/drivers/i2c/busses/i2c-stm32f4.c
@@ -95,7 +95,7 @@
/**
* struct stm32f4_i2c_msg - client specific data
- * @addr: 8-bit slave addr, including r/w bit
+ * @addr: 8-bit target addr, including r/w bit
* @count: number of bytes to be transferred
* @buf: data buffer
* @result: result of the transfer
@@ -232,10 +232,10 @@ static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
* In standard mode:
* t_scl_high = t_scl_low = CCR * I2C parent clk period
* So to reach 100 kHz, we have:
- * CCR = I2C parent rate / 100 kHz >> 1
+ * CCR = I2C parent rate / (100 kHz * 2)
*
* For example with parent rate = 2 MHz:
- * CCR = 2000000 / (100000 << 1) = 10
+ * CCR = 2000000 / (100000 * 2) = 10
* t_scl_high = t_scl_low = 10 * (1 / 2000000) = 5000 ns
* t_scl_high + t_scl_low = 10000 ns so 100 kHz is reached
*
@@ -243,7 +243,7 @@ static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
* parent rate is not higher than 46 MHz . As a result val
* is at most 8 bits wide and so fits into the CCR bits [11:0].
*/
- val = i2c_dev->parent_rate / (100000 << 1);
+ val = i2c_dev->parent_rate / (I2C_MAX_STANDARD_MODE_FREQ * 2);
} else {
/*
* In fast mode, we compute CCR with duty = 0 as with low
@@ -263,7 +263,7 @@ static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
* parent rate is not higher than 46 MHz . As a result val
* is at most 6 bits wide and so fits into the CCR bits [11:0].
*/
- val = DIV_ROUND_UP(i2c_dev->parent_rate, 400000 * 3);
+ val = DIV_ROUND_UP(i2c_dev->parent_rate, I2C_MAX_FAST_MODE_FREQ * 3);
/* Select Fast mode */
ccr |= STM32F4_I2C_CCR_FS;
@@ -313,7 +313,7 @@ static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev)
}
/**
- * stm32f4_i2c_write_ byte() - Write a byte in the data register
+ * stm32f4_i2c_write_byte() - Write a byte in the data register
* @i2c_dev: Controller's private data
* @byte: Data to write in the register
*/
@@ -480,7 +480,7 @@ static void stm32f4_i2c_handle_rx_done(struct stm32f4_i2c_dev *i2c_dev)
/**
* stm32f4_i2c_handle_rx_addr() - Handle address matched interrupt in case of
- * master receiver
+ * controller receiver
* @i2c_dev: Controller's private data
*/
static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev)
@@ -534,7 +534,7 @@ static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev)
default:
/*
* N-byte reception:
- * Enable ACK, reset POS (ACK postion) and clear ADDR flag.
+ * Enable ACK, reset POS (ACK position) and clear ADDR flag.
* In that way, ACK will be sent as soon as the current byte
* will be received in the shift register
*/
@@ -643,7 +643,7 @@ static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data)
/*
* Acknowledge failure:
- * In master transmitter mode a Stop must be generated by software
+ * In controller transmitter mode a Stop must be generated by software
*/
if (status & STM32F4_I2C_SR1_AF) {
if (!(msg->addr & I2C_M_RD)) {
@@ -681,7 +681,7 @@ static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev,
{
struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg;
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
- unsigned long timeout;
+ unsigned long time_left;
u32 mask;
int ret;
@@ -706,11 +706,11 @@ static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev,
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
}
- timeout = wait_for_completion_timeout(&i2c_dev->complete,
- i2c_dev->adap.timeout);
+ time_left = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
ret = f4_msg->result;
- if (!timeout)
+ if (!time_left)
ret = -ETIMEDOUT;
return ret;
@@ -749,7 +749,7 @@ static u32 stm32f4_i2c_func(struct i2c_adapter *adap)
}
static const struct i2c_algorithm stm32f4_i2c_algo = {
- .master_xfer = stm32f4_i2c_xfer,
+ .xfer = stm32f4_i2c_xfer,
.functionality = stm32f4_i2c_func,
};
@@ -767,8 +767,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
if (!i2c_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
@@ -784,30 +783,24 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
return -EINVAL;
}
- i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+ i2c_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(i2c_dev->clk)) {
- dev_err(&pdev->dev, "Error: Missing controller clock\n");
+ dev_err(&pdev->dev, "Failed to enable clock\n");
return PTR_ERR(i2c_dev->clk);
}
- ret = clk_prepare_enable(i2c_dev->clk);
- if (ret) {
- dev_err(i2c_dev->dev, "Failed to prepare_enable clock\n");
- return ret;
- }
rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rst)) {
- dev_err(&pdev->dev, "Error: Missing controller reset\n");
- ret = PTR_ERR(rst);
- goto clk_free;
- }
+ if (IS_ERR(rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+ "Error: Missing reset ctrl\n");
+
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
i2c_dev->speed = STM32_I2C_SPEED_STANDARD;
ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
- if (!ret && clk_rate >= 400000)
+ if (!ret && clk_rate >= I2C_MAX_FAST_MODE_FREQ)
i2c_dev->speed = STM32_I2C_SPEED_FAST;
i2c_dev->dev = &pdev->dev;
@@ -817,7 +810,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Failed to request irq event %i\n",
irq_event);
- goto clk_free;
+ return ret;
}
ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0,
@@ -825,12 +818,12 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Failed to request irq error %i\n",
irq_error);
- goto clk_free;
+ return ret;
}
ret = stm32f4_i2c_hw_config(i2c_dev);
if (ret)
- goto clk_free;
+ return ret;
adap = &i2c_dev->adap;
i2c_set_adapdata(adap, i2c_dev);
@@ -846,7 +839,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
ret = i2c_add_adapter(adap);
if (ret)
- goto clk_free;
+ return ret;
platform_set_drvdata(pdev, i2c_dev);
@@ -855,21 +848,13 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n");
return 0;
-
-clk_free:
- clk_disable_unprepare(i2c_dev->clk);
- return ret;
}
-static int stm32f4_i2c_remove(struct platform_device *pdev)
+static void stm32f4_i2c_remove(struct platform_device *pdev)
{
struct stm32f4_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c_dev->adap);
-
- clk_unprepare(i2c_dev->clk);
-
- return 0;
}
static const struct of_device_id stm32f4_i2c_match[] = {
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index 13e1213561d4..dc69ed934ec8 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -25,14 +26,15 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include "i2c-stm32.h"
@@ -49,11 +51,17 @@
#define STM32F7_I2C_TXDR 0x28
/* STM32F7 I2C control 1 */
+#define STM32_I2C_CR1_FMP BIT(24)
#define STM32F7_I2C_CR1_PECEN BIT(23)
+#define STM32F7_I2C_CR1_ALERTEN BIT(22)
+#define STM32F7_I2C_CR1_SMBHEN BIT(20)
+#define STM32F7_I2C_CR1_WUPEN BIT(18)
#define STM32F7_I2C_CR1_SBC BIT(16)
#define STM32F7_I2C_CR1_RXDMAEN BIT(15)
#define STM32F7_I2C_CR1_TXDMAEN BIT(14)
#define STM32F7_I2C_CR1_ANFOFF BIT(12)
+#define STM32F7_I2C_CR1_DNF_MASK GENMASK(11, 8)
+#define STM32F7_I2C_CR1_DNF(n) (((n) & 0xf) << 8)
#define STM32F7_I2C_CR1_ERRIE BIT(7)
#define STM32F7_I2C_CR1_TCIE BIT(6)
#define STM32F7_I2C_CR1_STOPIE BIT(5)
@@ -120,6 +128,7 @@
(((n) & STM32F7_I2C_ISR_ADDCODE_MASK) >> 17)
#define STM32F7_I2C_ISR_DIR BIT(16)
#define STM32F7_I2C_ISR_BUSY BIT(15)
+#define STM32F7_I2C_ISR_ALERT BIT(13)
#define STM32F7_I2C_ISR_PECERR BIT(11)
#define STM32F7_I2C_ISR_ARLO BIT(9)
#define STM32F7_I2C_ISR_BERR BIT(8)
@@ -133,6 +142,7 @@
#define STM32F7_I2C_ISR_TXE BIT(0)
/* STM32F7 I2C Interrupt Clear */
+#define STM32F7_I2C_ICR_ALERTCF BIT(13)
#define STM32F7_I2C_ICR_PECCF BIT(11)
#define STM32F7_I2C_ICR_ARLOCF BIT(9)
#define STM32F7_I2C_ICR_BERRCF BIT(8)
@@ -149,12 +159,16 @@
#define STM32F7_I2C_MAX_LEN 0xff
#define STM32F7_I2C_DMA_LEN_MIN 0x16
-#define STM32F7_I2C_MAX_SLAVE 0x2
+enum {
+ STM32F7_SLAVE_HOSTNOTIFY,
+ STM32F7_SLAVE_7_10_BITS_ADDR,
+ STM32F7_SLAVE_7_BITS_ADDR,
+ STM32F7_I2C_MAX_SLAVE
+};
#define STM32F7_I2C_DNF_DEFAULT 0
-#define STM32F7_I2C_DNF_MAX 16
+#define STM32F7_I2C_DNF_MAX 15
-#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
@@ -170,10 +184,24 @@
#define STM32F7_AUTOSUSPEND_DELAY (HZ / 100)
/**
+ * struct stm32f7_i2c_regs - i2c f7 registers backup
+ * @cr1: Control register 1
+ * @cr2: Control register 2
+ * @oar1: Own address 1 register
+ * @oar2: Own address 2 register
+ * @tmgr: Timing register
+ */
+struct stm32f7_i2c_regs {
+ u32 cr1;
+ u32 cr2;
+ u32 oar1;
+ u32 oar2;
+ u32 tmgr;
+};
+
+/**
* struct stm32f7_i2c_spec - private i2c specification timing
* @rate: I2C bus speed (Hz)
- * @rate_min: 80% of I2C bus speed (Hz)
- * @rate_max: 100% 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)
@@ -184,8 +212,6 @@
*/
struct stm32f7_i2c_spec {
u32 rate;
- u32 rate_min;
- u32 rate_max;
u32 fall_max;
u32 rise_max;
u32 hddat_min;
@@ -197,22 +223,22 @@ struct stm32f7_i2c_spec {
/**
* struct stm32f7_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)
+ * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
+ * @single_it_line: Only a single IT line is used for both events/errors
+ * @fmp_cr1_bit: Fast Mode Plus control is done via a bit in CR1
*/
struct stm32f7_i2c_setup {
- enum stm32_i2c_speed speed;
u32 speed_freq;
u32 clock_src;
u32 rise_time;
u32 fall_time;
- u8 dnf;
- bool analog_filter;
+ u32 fmp_clr_offset;
+ bool single_it_line;
+ bool fmp_cr1_bit;
};
/**
@@ -262,13 +288,24 @@ struct stm32f7_i2c_msg {
};
/**
+ * struct stm32f7_i2c_alert - SMBus alert specific data
+ * @setup: platform data for the smbus_alert i2c client
+ * @ara: I2C slave device used to respond to the SMBus Alert with Alert
+ * Response Address
+ */
+struct stm32f7_i2c_alert {
+ struct i2c_smbus_alert_setup setup;
+ struct i2c_client *ara;
+};
+
+/**
* struct stm32f7_i2c_dev - private data of the controller
* @adap: I2C adapter for this controller
* @dev: device for this controller
* @base: virtual memory area
* @complete: completion of I2C message
* @clk: hw i2c clock
- * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
+ * @bus_rate: I2C clock frequency of the controller
* @msg: Pointer to data to be written
* @msg_num: number of I2C messages to be executed
* @msg_id: message identifiant
@@ -277,12 +314,24 @@ struct stm32f7_i2c_msg {
* @timing: I2C computed timings
* @slave: list of slave devices registered on the I2C bus
* @slave_running: slave device currently used
+ * @backup_regs: backup of i2c controller registers (for suspend/resume)
* @slave_dir: transfer direction for the current slave device
* @master_mode: boolean to know in which mode the I2C is running (master or
* slave)
* @dma: dma data
* @use_dma: boolean to know if dma is used in the current transfer
* @regmap: holds SYSCFG phandle for Fast Mode Plus bits
+ * @fmp_sreg: register address for setting Fast Mode Plus bits
+ * @fmp_creg: register address for clearing Fast Mode Plus bits
+ * @fmp_mask: mask for Fast Mode Plus bits in set register
+ * @wakeup_src: boolean to know if the device is a wakeup source
+ * @smbus_mode: states that the controller is configured in SMBus mode
+ * @host_notify_client: SMBus host-notify client
+ * @analog_filter: boolean to indicate enabling of the analog filter
+ * @dnf_dt: value of digital filter requested via dt
+ * @dnf: value of digital filter to apply
+ * @alert: SMBus alert specific data
+ * @atomic: boolean indicating that current transfer is atomic
*/
struct stm32f7_i2c_dev {
struct i2c_adapter adap;
@@ -290,7 +339,7 @@ struct stm32f7_i2c_dev {
void __iomem *base;
struct completion complete;
struct clk *clk;
- int speed;
+ unsigned int bus_rate;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_id;
@@ -299,25 +348,35 @@ struct stm32f7_i2c_dev {
struct stm32f7_i2c_timings timing;
struct i2c_client *slave[STM32F7_I2C_MAX_SLAVE];
struct i2c_client *slave_running;
+ struct stm32f7_i2c_regs backup_regs;
u32 slave_dir;
bool master_mode;
struct stm32_i2c_dma *dma;
bool use_dma;
struct regmap *regmap;
+ u32 fmp_sreg;
+ u32 fmp_creg;
+ u32 fmp_mask;
+ bool wakeup_src;
+ bool smbus_mode;
+ struct i2c_client *host_notify_client;
+ bool analog_filter;
+ u32 dnf_dt;
+ u32 dnf;
+ struct stm32f7_i2c_alert *alert;
+ bool atomic;
};
-/**
+/*
* All these values are coming from I2C Specification, Version 6.0, 4th of
* April 2014.
*
* Table10. Characteristics of the SDA and SCL bus lines for Standard, Fast,
* and Fast-mode Plus I2C-bus devices
*/
-static struct stm32f7_i2c_spec i2c_specs[] = {
- [STM32_I2C_SPEED_STANDARD] = {
- .rate = 100000,
- .rate_min = 80000,
- .rate_max = 100000,
+static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = {
+ {
+ .rate = I2C_MAX_STANDARD_MODE_FREQ,
.fall_max = 300,
.rise_max = 1000,
.hddat_min = 0,
@@ -326,10 +385,8 @@ static struct stm32f7_i2c_spec i2c_specs[] = {
.l_min = 4700,
.h_min = 4000,
},
- [STM32_I2C_SPEED_FAST] = {
- .rate = 400000,
- .rate_min = 320000,
- .rate_max = 400000,
+ {
+ .rate = I2C_MAX_FAST_MODE_FREQ,
.fall_max = 300,
.rise_max = 300,
.hddat_min = 0,
@@ -338,10 +395,8 @@ static struct stm32f7_i2c_spec i2c_specs[] = {
.l_min = 1300,
.h_min = 600,
},
- [STM32_I2C_SPEED_FAST_PLUS] = {
- .rate = 1000000,
- .rate_min = 800000,
- .rate_max = 1000000,
+ {
+ .rate = I2C_MAX_FAST_MODE_PLUS_FREQ,
.fall_max = 100,
.rise_max = 120,
.hddat_min = 0,
@@ -355,8 +410,25 @@ static struct stm32f7_i2c_spec i2c_specs[] = {
static const struct stm32f7_i2c_setup stm32f7_setup = {
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
- .dnf = STM32F7_I2C_DNF_DEFAULT,
- .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
+};
+
+static const struct stm32f7_i2c_setup stm32mp15_setup = {
+ .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
+ .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
+ .fmp_clr_offset = 0x40,
+};
+
+static const struct stm32f7_i2c_setup stm32mp13_setup = {
+ .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
+ .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
+ .fmp_clr_offset = 0x4,
+};
+
+static const struct stm32f7_i2c_setup stm32mp25_setup = {
+ .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
+ .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
+ .single_it_line = true,
+ .fmp_cr1_bit = true,
};
static inline void stm32f7_i2c_set_bits(void __iomem *reg, u32 mask)
@@ -374,10 +446,23 @@ static void stm32f7_i2c_disable_irq(struct stm32f7_i2c_dev *i2c_dev, u32 mask)
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, mask);
}
+static struct stm32f7_i2c_spec *stm32f7_get_specs(u32 rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stm32f7_i2c_specs); i++)
+ if (rate <= stm32f7_i2c_specs[i].rate)
+ return &stm32f7_i2c_specs[i];
+
+ return ERR_PTR(-EINVAL);
+}
+
+#define RATE_MIN(rate) ((rate) * 8 / 10)
static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
struct stm32f7_i2c_setup *setup,
struct stm32f7_i2c_timings *output)
{
+ struct stm32f7_i2c_spec *specs;
u32 p_prev = STM32F7_PRESC_MAX;
u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
setup->clock_src);
@@ -395,50 +480,46 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
u16 p, l, a, h;
int ret = 0;
- if (setup->speed >= STM32_I2C_SPEED_END) {
- dev_err(i2c_dev->dev, "speed out of bound {%d/%d}\n",
- setup->speed, STM32_I2C_SPEED_END - 1);
+ specs = stm32f7_get_specs(setup->speed_freq);
+ if (specs == ERR_PTR(-EINVAL)) {
+ dev_err(i2c_dev->dev, "speed out of bound {%d}\n",
+ setup->speed_freq);
return -EINVAL;
}
- if ((setup->rise_time > i2c_specs[setup->speed].rise_max) ||
- (setup->fall_time > i2c_specs[setup->speed].fall_max)) {
+ if ((setup->rise_time > specs->rise_max) ||
+ (setup->fall_time > specs->fall_max)) {
dev_err(i2c_dev->dev,
"timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
- setup->rise_time, i2c_specs[setup->speed].rise_max,
- setup->fall_time, i2c_specs[setup->speed].fall_max);
+ setup->rise_time, specs->rise_max,
+ setup->fall_time, specs->fall_max);
return -EINVAL;
}
- if (setup->dnf > STM32F7_I2C_DNF_MAX) {
+ i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk);
+ if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
dev_err(i2c_dev->dev,
"DNF out of bound %d/%d\n",
- setup->dnf, STM32F7_I2C_DNF_MAX);
- return -EINVAL;
- }
-
- if (setup->speed_freq > i2c_specs[setup->speed].rate) {
- dev_err(i2c_dev->dev, "ERROR: Freq {%d/%d}\n",
- setup->speed_freq, i2c_specs[setup->speed].rate);
+ i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
return -EINVAL;
}
/* Analog and Digital Filters */
af_delay_min =
- (setup->analog_filter ?
+ (i2c_dev->analog_filter ?
STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
af_delay_max =
- (setup->analog_filter ?
+ (i2c_dev->analog_filter ?
STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
- dnf_delay = setup->dnf * i2cclk;
+ dnf_delay = i2c_dev->dnf * i2cclk;
- sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
- af_delay_min - (setup->dnf + 3) * i2cclk;
+ sdadel_min = specs->hddat_min + setup->fall_time -
+ af_delay_min - (i2c_dev->dnf + 3) * i2cclk;
- sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
- af_delay_max - (setup->dnf + 4) * i2cclk;
+ sdadel_max = specs->vddat_max - setup->rise_time -
+ af_delay_max - (i2c_dev->dnf + 4) * i2cclk;
- scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min;
+ scldel_min = setup->rise_time + specs->sudat_min;
if (sdadel_min < 0)
sdadel_min = 0;
@@ -476,8 +557,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
list_add_tail(&v->node,
&solutions);
+ break;
}
}
+
+ if (p_prev == p)
+ break;
}
}
@@ -489,8 +574,8 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
tsync = af_delay_min + dnf_delay + (2 * i2cclk);
s = NULL;
- clk_max = NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
- clk_min = NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
+ clk_max = NSEC_PER_SEC / RATE_MIN(setup->speed_freq);
+ clk_min = NSEC_PER_SEC / setup->speed_freq;
/*
* Among Prescaler possibilities discovered above figures out SCL Low
@@ -508,7 +593,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
for (l = 0; l < STM32F7_SCLL_MAX; l++) {
u32 tscl_l = (l + 1) * prescaler + tsync;
- if ((tscl_l < i2c_specs[setup->speed].l_min) ||
+ if ((tscl_l < specs->l_min) ||
(i2cclk >=
((tscl_l - af_delay_min - dnf_delay) / 4))) {
continue;
@@ -520,7 +605,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
setup->rise_time + setup->fall_time;
if ((tscl >= clk_min) && (tscl <= clk_max) &&
- (tscl_h >= i2c_specs[setup->speed].h_min) &&
+ (tscl_h >= specs->h_min) &&
(i2cclk < tscl_h)) {
int clk_error = tscl - i2cbus;
@@ -566,13 +651,39 @@ exit:
return ret;
}
+static u32 stm32f7_get_lower_rate(u32 rate)
+{
+ int i = ARRAY_SIZE(stm32f7_i2c_specs);
+
+ while (--i)
+ if (stm32f7_i2c_specs[i].rate < rate)
+ break;
+
+ return stm32f7_i2c_specs[i].rate;
+}
+
static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
struct stm32f7_i2c_setup *setup)
{
+ struct i2c_timings timings, *t = &timings;
int ret = 0;
- setup->speed = i2c_dev->speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ t->bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ t->scl_rise_ns = i2c_dev->setup.rise_time;
+ t->scl_fall_ns = i2c_dev->setup.fall_time;
+
+ i2c_parse_fw_timings(i2c_dev->dev, t, false);
+
+ if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) {
+ dev_err(i2c_dev->dev, "Invalid bus speed (%i>%i)\n",
+ t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ);
+ return -EINVAL;
+ }
+
+ setup->speed_freq = t->bus_freq_hz;
+ i2c_dev->setup.rise_time = t->scl_rise_ns;
+ i2c_dev->setup.fall_time = t->scl_fall_ns;
+ i2c_dev->dnf_dt = t->digital_filter_width_ns;
setup->clock_src = clk_get_rate(i2c_dev->clk);
if (!setup->clock_src) {
@@ -580,23 +691,22 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
return -EINVAL;
}
+ if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
+ i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT;
+
do {
ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
&i2c_dev->timing);
if (ret) {
dev_err(i2c_dev->dev,
"failed to compute I2C timings.\n");
- if (i2c_dev->speed > STM32_I2C_SPEED_STANDARD) {
- i2c_dev->speed--;
- setup->speed = i2c_dev->speed;
- setup->speed_freq =
- i2c_specs[setup->speed].rate;
- dev_warn(i2c_dev->dev,
- "downgrade I2C Speed Freq to (%i)\n",
- i2c_specs[setup->speed].rate);
- } else {
+ if (setup->speed_freq <= I2C_MAX_STANDARD_MODE_FREQ)
break;
- }
+ setup->speed_freq =
+ stm32f7_get_lower_rate(setup->speed_freq);
+ dev_warn(i2c_dev->dev,
+ "downgrade I2C Speed Freq to (%i)\n",
+ setup->speed_freq);
}
} while (ret);
@@ -605,12 +715,17 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
return ret;
}
- dev_dbg(i2c_dev->dev, "I2C Speed(%i), Freq(%i), Clk Source(%i)\n",
- setup->speed, setup->speed_freq, setup->clock_src);
+ i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
+ "i2c-analog-filter");
+
+ dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
+ setup->speed_freq, setup->clock_src);
dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
setup->rise_time, setup->fall_time);
dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
- (setup->analog_filter ? "On" : "Off"), setup->dnf);
+ str_on_off(i2c_dev->analog_filter), i2c_dev->dnf);
+
+ i2c_dev->bus_rate = setup->speed_freq;
return 0;
}
@@ -625,12 +740,16 @@ static void stm32f7_i2c_disable_dma_req(struct stm32f7_i2c_dev *i2c_dev)
static void stm32f7_i2c_dma_callback(void *arg)
{
- struct stm32f7_i2c_dev *i2c_dev = (struct stm32f7_i2c_dev *)arg;
+ struct stm32f7_i2c_dev *i2c_dev = arg;
struct stm32_i2c_dma *dma = i2c_dev->dma;
- struct device *dev = dma->chan_using->device->dev;
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
stm32f7_i2c_disable_dma_req(i2c_dev);
- dma_unmap_single(dev, dma->dma_buf, dma->dma_len, dma->dma_data_dir);
+ dmaengine_terminate_async(dma->chan_using);
+ dma_unmap_single(i2c_dev->dev, dma->dma_buf, dma->dma_len,
+ dma->dma_data_dir);
+ if (!f7_msg->smbus)
+ i2c_put_dma_safe_msg_buf(f7_msg->buf, i2c_dev->msg, true);
complete(&dma->dma_complete);
}
@@ -647,13 +766,20 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
- /* Enable I2C */
- if (i2c_dev->setup.analog_filter)
+ /* Configure the Analog Filter */
+ if (i2c_dev->analog_filter)
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_ANFOFF);
else
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_ANFOFF);
+
+ /* Program the Digital Filter */
+ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_DNF_MASK);
+ stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
+
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_PE);
}
@@ -727,18 +853,14 @@ static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
}
-static int stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
+static void stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
- dev_info(i2c_dev->dev, "Trying to recover bus\n");
-
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_PE);
stm32f7_i2c_hw_config(i2c_dev);
-
- return 0;
}
static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
@@ -753,13 +875,7 @@ static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
if (!ret)
return 0;
- dev_info(i2c_dev->dev, "bus busy\n");
-
- ret = stm32f7_i2c_release_bus(&i2c_dev->adap);
- if (ret) {
- dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
- return ret;
- }
+ stm32f7_i2c_release_bus(&i2c_dev->adap);
return -EBUSY;
}
@@ -769,6 +885,7 @@ static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
+ u8 *dma_buf;
u32 cr1, cr2;
int ret;
@@ -818,16 +935,23 @@ static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
/* Configure DMA or enable RX/TX interrupt */
i2c_dev->use_dma = false;
- if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN) {
- ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
- msg->flags & I2C_M_RD,
- f7_msg->count, f7_msg->buf,
- stm32f7_i2c_dma_callback,
- i2c_dev);
- if (!ret)
- i2c_dev->use_dma = true;
- else
- dev_warn(i2c_dev->dev, "can't use DMA\n");
+ if (i2c_dev->dma && !i2c_dev->atomic) {
+ dma_buf = i2c_get_dma_safe_msg_buf(msg, STM32F7_I2C_DMA_LEN_MIN);
+ if (dma_buf) {
+ f7_msg->buf = dma_buf;
+ ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
+ msg->flags & I2C_M_RD,
+ f7_msg->count, f7_msg->buf,
+ stm32f7_i2c_dma_callback,
+ i2c_dev);
+ if (ret) {
+ dev_warn(i2c_dev->dev, "can't use DMA\n");
+ i2c_put_dma_safe_msg_buf(f7_msg->buf, msg, false);
+ f7_msg->buf = msg->buf;
+ } else {
+ i2c_dev->use_dma = true;
+ }
+ }
}
if (!i2c_dev->use_dma) {
@@ -842,6 +966,9 @@ static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
cr1 |= STM32F7_I2C_CR1_TXDMAEN;
}
+ if (i2c_dev->atomic)
+ cr1 &= ~STM32F7_I2C_ALL_IRQ_MASK; /* Disable all interrupts */
+
/* Configure Start/Repeated Start */
cr2 |= STM32F7_I2C_CR2_START;
@@ -949,6 +1076,9 @@ static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
f7_msg->read_write = I2C_SMBUS_READ;
break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ /* Rely on emulated i2c transfer (through master_xfer) */
+ return -EOPNOTSUPP;
default:
dev_err(dev, "Unsupported smbus protocol %d\n", f7_msg->size);
return -EOPNOTSUPP;
@@ -959,9 +1089,10 @@ static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
/* Configure PEC */
if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) {
cr1 |= STM32F7_I2C_CR1_PECEN;
- cr2 |= STM32F7_I2C_CR2_PECBYTE;
- if (!f7_msg->read_write)
+ if (!f7_msg->read_write) {
+ cr2 |= STM32F7_I2C_CR2_PECBYTE;
f7_msg->count++;
+ }
} else {
cr1 &= ~STM32F7_I2C_CR1_PECEN;
cr2 &= ~STM32F7_I2C_CR2_PECBYTE;
@@ -1049,8 +1180,10 @@ static void stm32f7_i2c_smbus_rep_start(struct stm32f7_i2c_dev *i2c_dev)
f7_msg->stop = true;
/* Add one byte for PEC if needed */
- if (cr1 & STM32F7_I2C_CR1_PECEN)
+ if (cr1 & STM32F7_I2C_CR1_PECEN) {
+ cr2 |= STM32F7_I2C_CR2_PECBYTE;
f7_msg->count++;
+ }
/* Set number of bytes to be transferred */
cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK);
@@ -1186,6 +1319,8 @@ static void stm32f7_i2c_slave_start(struct stm32f7_i2c_dev *i2c_dev)
STM32F7_I2C_CR1_TXIE;
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+ /* Write 1st data byte */
+ writel_relaxed(value, base + STM32F7_I2C_TXDR);
} else {
/* Notify i2c slave that new write transfer is starting */
i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
@@ -1256,11 +1391,20 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
int i;
/*
- * slave[0] supports 7-bit and 10-bit slave address
- * slave[1] supports 7-bit slave address only
+ * slave[STM32F7_SLAVE_HOSTNOTIFY] support only SMBus Host address (0x8)
+ * slave[STM32F7_SLAVE_7_10_BITS_ADDR] supports 7-bit and 10-bit slave address
+ * slave[STM32F7_SLAVE_7_BITS_ADDR] supports 7-bit slave address only
*/
- for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) {
- if (i == 1 && (slave->flags & I2C_CLIENT_PEC))
+ if (i2c_dev->smbus_mode && (slave->addr == 0x08)) {
+ if (i2c_dev->slave[STM32F7_SLAVE_HOSTNOTIFY])
+ goto fail;
+ *id = STM32F7_SLAVE_HOSTNOTIFY;
+ return 0;
+ }
+
+ for (i = STM32F7_I2C_MAX_SLAVE - 1; i > STM32F7_SLAVE_HOSTNOTIFY; i--) {
+ if ((i == STM32F7_SLAVE_7_BITS_ADDR) &&
+ (slave->flags & I2C_CLIENT_TEN))
continue;
if (!i2c_dev->slave[i]) {
*id = i;
@@ -1268,6 +1412,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
}
}
+fail:
dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
return -EINVAL;
@@ -1298,15 +1443,13 @@ static bool stm32f7_i2c_is_slave_busy(struct stm32f7_i2c_dev *i2c_dev)
return i == busy;
}
-static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
+static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev, u32 status)
{
void __iomem *base = i2c_dev->base;
- u32 cr2, status, mask;
+ u32 cr2, mask;
u8 val;
int ret;
- status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
-
/* Slave transmitter mode */
if (status & STM32F7_I2C_ISR_TXIS) {
i2c_slave_event(i2c_dev->slave_running,
@@ -1373,22 +1516,78 @@ static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
return IRQ_HANDLED;
}
-static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
+static irqreturn_t stm32f7_i2c_handle_isr_errs(struct stm32f7_i2c_dev *i2c_dev, u32 status)
{
- struct stm32f7_i2c_dev *i2c_dev = data;
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ u16 addr = f7_msg->addr;
void __iomem *base = i2c_dev->base;
- u32 status, mask;
- int ret = IRQ_HANDLED;
+ struct device *dev = i2c_dev->dev;
- /* Check if the interrupt if for a slave device */
- if (!i2c_dev->master_mode) {
- ret = stm32f7_i2c_slave_isr_event(i2c_dev);
- return ret;
+ /* Bus error */
+ if (status & STM32F7_I2C_ISR_BERR) {
+ dev_err(dev, "Bus error accessing addr 0x%x\n", addr);
+ writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
+ stm32f7_i2c_release_bus(&i2c_dev->adap);
+ f7_msg->result = -EIO;
+ }
+
+ /* Arbitration loss */
+ if (status & STM32F7_I2C_ISR_ARLO) {
+ dev_dbg(dev, "Arbitration loss accessing addr 0x%x\n", addr);
+ writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
+ f7_msg->result = -EAGAIN;
}
+ if (status & STM32F7_I2C_ISR_PECERR) {
+ dev_err(dev, "PEC error in reception accessing addr 0x%x\n", addr);
+ writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
+ f7_msg->result = -EINVAL;
+ }
+
+ if (status & STM32F7_I2C_ISR_ALERT) {
+ dev_dbg(dev, "SMBus alert received\n");
+ writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR);
+ i2c_handle_smbus_alert(i2c_dev->alert->ara);
+ return IRQ_HANDLED;
+ }
+
+ if (!i2c_dev->slave_running) {
+ u32 mask;
+ /* Disable interrupts */
+ if (stm32f7_i2c_is_slave_registered(i2c_dev))
+ mask = STM32F7_I2C_XFER_IRQ_MASK;
+ else
+ mask = STM32F7_I2C_ALL_IRQ_MASK;
+ stm32f7_i2c_disable_irq(i2c_dev, mask);
+ }
+
+ /* Disable dma */
+ if (i2c_dev->use_dma)
+ stm32f7_i2c_dma_callback(i2c_dev);
+
+ i2c_dev->master_mode = false;
+ complete(&i2c_dev->complete);
+
+ return IRQ_HANDLED;
+}
+
+#define STM32F7_ERR_EVENTS (STM32F7_I2C_ISR_BERR | STM32F7_I2C_ISR_ARLO |\
+ STM32F7_I2C_ISR_PECERR | STM32F7_I2C_ISR_ALERT)
+static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
+{
+ struct stm32f7_i2c_dev *i2c_dev = data;
+ u32 status;
+
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+ /*
+ * Check if the interrupt is for a slave device or related
+ * to errors flags (in case of single it line mode)
+ */
+ if (!i2c_dev->master_mode ||
+ (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS)))
+ return IRQ_WAKE_THREAD;
+
/* Tx empty */
if (status & STM32F7_I2C_ISR_TXIS)
stm32f7_i2c_write_tx_data(i2c_dev);
@@ -1397,40 +1596,63 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
if (status & STM32F7_I2C_ISR_RXNE)
stm32f7_i2c_read_rx_data(i2c_dev);
+ /* Wake up the thread if other flags are raised */
+ if (status &
+ (STM32F7_I2C_ISR_NACKF | STM32F7_I2C_ISR_STOPF |
+ STM32F7_I2C_ISR_TC | STM32F7_I2C_ISR_TCR))
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
+{
+ struct stm32f7_i2c_dev *i2c_dev = data;
+ struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
+ void __iomem *base = i2c_dev->base;
+ u32 status, mask;
+ int ret;
+
+ status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+
+ if (!i2c_dev->master_mode)
+ return stm32f7_i2c_slave_isr_event(i2c_dev, status);
+
+ /* Handle errors in case of this handler is used for events/errors */
+ if (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS))
+ return stm32f7_i2c_handle_isr_errs(i2c_dev, status);
+
/* NACK received */
if (status & STM32F7_I2C_ISR_NACKF) {
- dev_dbg(i2c_dev->dev, "<%s>: Receive NACK\n", __func__);
+ dev_dbg(i2c_dev->dev, "<%s>: Receive NACK (addr %x)\n",
+ __func__, f7_msg->addr);
writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
+ if (i2c_dev->use_dma)
+ stm32f7_i2c_dma_callback(i2c_dev);
f7_msg->result = -ENXIO;
}
- /* STOP detection flag */
- if (status & STM32F7_I2C_ISR_STOPF) {
- /* Disable interrupts */
- if (stm32f7_i2c_is_slave_registered(i2c_dev))
- mask = STM32F7_I2C_XFER_IRQ_MASK;
+ if (status & STM32F7_I2C_ISR_TCR) {
+ if (f7_msg->smbus)
+ stm32f7_i2c_smbus_reload(i2c_dev);
else
- mask = STM32F7_I2C_ALL_IRQ_MASK;
- stm32f7_i2c_disable_irq(i2c_dev, mask);
-
- /* Clear STOP flag */
- writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);
-
- if (i2c_dev->use_dma) {
- ret = IRQ_WAKE_THREAD;
- } else {
- i2c_dev->master_mode = false;
- complete(&i2c_dev->complete);
- }
+ stm32f7_i2c_reload(i2c_dev);
}
/* Transfer complete */
if (status & STM32F7_I2C_ISR_TC) {
+ /* Wait for dma transfer completion before sending next message */
+ if (i2c_dev->use_dma && !f7_msg->result) {
+ ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ);
+ if (!ret) {
+ dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
+ stm32f7_i2c_dma_callback(i2c_dev);
+ f7_msg->result = -ETIMEDOUT;
+ }
+ }
if (f7_msg->stop) {
mask = STM32F7_I2C_CR2_STOP;
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
- } else if (i2c_dev->use_dma) {
- ret = IRQ_WAKE_THREAD;
} else if (f7_msg->smbus) {
stm32f7_i2c_smbus_rep_start(i2c_dev);
} else {
@@ -1440,47 +1662,18 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
}
}
- if (status & STM32F7_I2C_ISR_TCR) {
- if (f7_msg->smbus)
- stm32f7_i2c_smbus_reload(i2c_dev);
+ /* STOP detection flag */
+ if (status & STM32F7_I2C_ISR_STOPF) {
+ /* Disable interrupts */
+ if (stm32f7_i2c_is_slave_registered(i2c_dev))
+ mask = STM32F7_I2C_XFER_IRQ_MASK;
else
- stm32f7_i2c_reload(i2c_dev);
- }
-
- return ret;
-}
-
-static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
-{
- struct stm32f7_i2c_dev *i2c_dev = data;
- struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
- struct stm32_i2c_dma *dma = i2c_dev->dma;
- u32 status;
- int ret;
-
- /*
- * Wait for dma transfer completion before sending next message or
- * notity the end of xfer to the client
- */
- ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ);
- if (!ret) {
- dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
- stm32f7_i2c_disable_dma_req(i2c_dev);
- dmaengine_terminate_all(dma->chan_using);
- f7_msg->result = -ETIMEDOUT;
- }
+ mask = STM32F7_I2C_ALL_IRQ_MASK;
+ stm32f7_i2c_disable_irq(i2c_dev, mask);
- status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
+ /* Clear STOP flag */
+ writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);
- if (status & STM32F7_I2C_ISR_TC) {
- if (f7_msg->smbus) {
- stm32f7_i2c_smbus_rep_start(i2c_dev);
- } else {
- i2c_dev->msg_id++;
- i2c_dev->msg++;
- stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg);
- }
- } else {
i2c_dev->master_mode = false;
complete(&i2c_dev->complete);
}
@@ -1488,58 +1681,32 @@ static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
return IRQ_HANDLED;
}
-static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
+static irqreturn_t stm32f7_i2c_isr_error_thread(int irq, void *data)
{
struct stm32f7_i2c_dev *i2c_dev = data;
- struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
- void __iomem *base = i2c_dev->base;
- struct device *dev = i2c_dev->dev;
- struct stm32_i2c_dma *dma = i2c_dev->dma;
- u32 mask, status;
+ u32 status;
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
- /* Bus error */
- if (status & STM32F7_I2C_ISR_BERR) {
- dev_err(dev, "<%s>: Bus error\n", __func__);
- writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
- stm32f7_i2c_release_bus(&i2c_dev->adap);
- f7_msg->result = -EIO;
- }
-
- /* Arbitration loss */
- if (status & STM32F7_I2C_ISR_ARLO) {
- dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
- writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
- f7_msg->result = -EAGAIN;
- }
+ return stm32f7_i2c_handle_isr_errs(i2c_dev, status);
+}
- if (status & STM32F7_I2C_ISR_PECERR) {
- dev_err(dev, "<%s>: PEC error in reception\n", __func__);
- writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
- f7_msg->result = -EINVAL;
- }
+static int stm32f7_i2c_wait_polling(struct stm32f7_i2c_dev *i2c_dev)
+{
+ ktime_t timeout = ktime_add_ms(ktime_get(), i2c_dev->adap.timeout);
- /* Disable interrupts */
- if (stm32f7_i2c_is_slave_registered(i2c_dev))
- mask = STM32F7_I2C_XFER_IRQ_MASK;
- else
- mask = STM32F7_I2C_ALL_IRQ_MASK;
- stm32f7_i2c_disable_irq(i2c_dev, mask);
+ while (ktime_compare(ktime_get(), timeout) < 0) {
+ udelay(5);
+ stm32f7_i2c_isr_event(0, i2c_dev);
- /* Disable dma */
- if (i2c_dev->use_dma) {
- stm32f7_i2c_disable_dma_req(i2c_dev);
- dmaengine_terminate_all(dma->chan_using);
+ if (completion_done(&i2c_dev->complete))
+ return 1;
}
- i2c_dev->master_mode = false;
- complete(&i2c_dev->complete);
-
- return IRQ_HANDLED;
+ return 0;
}
-static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
+static int stm32f7_i2c_xfer_core(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
@@ -1553,7 +1720,7 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
i2c_dev->msg_id = 0;
f7_msg->smbus = false;
- ret = pm_runtime_get_sync(i2c_dev->dev);
+ ret = pm_runtime_resume_and_get(i2c_dev->dev);
if (ret < 0)
return ret;
@@ -1563,25 +1730,60 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
stm32f7_i2c_xfer_msg(i2c_dev, msgs);
- time_left = wait_for_completion_timeout(&i2c_dev->complete,
- i2c_dev->adap.timeout);
+ if (!i2c_dev->atomic)
+ time_left = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
+ else
+ time_left = stm32f7_i2c_wait_polling(i2c_dev);
+
ret = f7_msg->result;
+ if (ret) {
+ if (i2c_dev->use_dma)
+ dmaengine_synchronize(dma->chan_using);
+
+ /*
+ * It is possible that some unsent data have already been
+ * written into TXDR. To avoid sending old data in a
+ * further transfer, flush TXDR in case of any error
+ */
+ writel_relaxed(STM32F7_I2C_ISR_TXE,
+ i2c_dev->base + STM32F7_I2C_ISR);
+ goto pm_free;
+ }
if (!time_left) {
dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
i2c_dev->msg->addr);
if (i2c_dev->use_dma)
- dmaengine_terminate_all(dma->chan_using);
+ dmaengine_terminate_sync(dma->chan_using);
+ stm32f7_i2c_wait_free_bus(i2c_dev);
ret = -ETIMEDOUT;
}
pm_free:
- pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return (ret < 0) ? ret : num;
}
+static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+
+ i2c_dev->atomic = false;
+ return stm32f7_i2c_xfer_core(i2c_adap, msgs, num);
+}
+
+static int stm32f7_i2c_xfer_atomic(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+
+ i2c_dev->atomic = true;
+ return stm32f7_i2c_xfer_core(i2c_adap, msgs, num);
+}
+
static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
@@ -1591,7 +1793,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
struct stm32_i2c_dma *dma = i2c_dev->dma;
struct device *dev = i2c_dev->dev;
- unsigned long timeout;
+ unsigned long time_left;
int i, ret;
f7_msg->addr = addr;
@@ -1599,7 +1801,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
f7_msg->read_write = read_write;
f7_msg->smbus = true;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
@@ -1611,16 +1813,28 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
if (ret)
goto pm_free;
- timeout = wait_for_completion_timeout(&i2c_dev->complete,
- i2c_dev->adap.timeout);
+ time_left = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
ret = f7_msg->result;
- if (ret)
+ if (ret) {
+ if (i2c_dev->use_dma)
+ dmaengine_synchronize(dma->chan_using);
+
+ /*
+ * It is possible that some unsent data have already been
+ * written into TXDR. To avoid sending old data in a
+ * further transfer, flush TXDR in case of any error
+ */
+ writel_relaxed(STM32F7_I2C_ISR_TXE,
+ i2c_dev->base + STM32F7_I2C_ISR);
goto pm_free;
+ }
- if (!timeout) {
+ if (!time_left) {
dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
if (i2c_dev->use_dma)
- dmaengine_terminate_all(dma->chan_using);
+ dmaengine_terminate_sync(dma->chan_using);
+ stm32f7_i2c_wait_free_bus(i2c_dev);
ret = -ETIMEDOUT;
goto pm_free;
}
@@ -1655,11 +1869,28 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
}
pm_free:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
+static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev,
+ bool enable)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 mask = STM32F7_I2C_CR1_WUPEN;
+
+ if (!i2c_dev->wakeup_src)
+ return;
+
+ if (enable) {
+ device_set_wakeup_enable(i2c_dev->dev, true);
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
+ } else {
+ device_set_wakeup_enable(i2c_dev->dev, false);
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask);
+ }
+}
+
static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter);
@@ -1682,11 +1913,20 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
if (ret)
return ret;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
- if (id == 0) {
+ if (!stm32f7_i2c_is_slave_registered(i2c_dev))
+ stm32f7_i2c_enable_wakeup(i2c_dev, true);
+
+ switch (id) {
+ case 0:
+ /* Slave SMBus Host */
+ i2c_dev->slave[id] = slave;
+ break;
+
+ case 1:
/* Configure Own Address 1 */
oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
oar1 &= ~STM32F7_I2C_OAR1_MASK;
@@ -1699,7 +1939,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar1 |= STM32F7_I2C_OAR1_OA1EN;
i2c_dev->slave[id] = slave;
writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
- } else if (id == 1) {
+ break;
+
+ case 2:
/* Configure Own Address 2 */
oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
oar2 &= ~STM32F7_I2C_OAR2_MASK;
@@ -1712,7 +1954,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar2 |= STM32F7_I2C_OAR2_OA2EN;
i2c_dev->slave[id] = slave;
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
- } else {
+ break;
+
+ default:
+ dev_err(dev, "I2C slave id not supported\n");
ret = -ENODEV;
goto pm_free;
}
@@ -1727,7 +1972,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
ret = 0;
pm_free:
- pm_runtime_mark_last_busy(dev);
+ if (!stm32f7_i2c_is_slave_registered(i2c_dev))
+ stm32f7_i2c_enable_wakeup(i2c_dev, false);
+
pm_runtime_put_autosuspend(dev);
return ret;
@@ -1746,64 +1993,167 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
WARN_ON(!i2c_dev->slave[id]);
- ret = pm_runtime_get_sync(i2c_dev->dev);
+ ret = pm_runtime_resume_and_get(i2c_dev->dev);
if (ret < 0)
return ret;
- if (id == 0) {
+ if (id == 1) {
mask = STM32F7_I2C_OAR1_OA1EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
- } else {
+ } else if (id == 2) {
mask = STM32F7_I2C_OAR2_OA2EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
}
i2c_dev->slave[id] = NULL;
- if (!(stm32f7_i2c_is_slave_registered(i2c_dev)))
+ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) {
stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);
+ stm32f7_i2c_enable_wakeup(i2c_dev, false);
+ }
- pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return 0;
}
+static int stm32f7_i2c_write_fm_plus_bits(struct stm32f7_i2c_dev *i2c_dev,
+ bool enable)
+{
+ int ret = 0;
+
+ if (i2c_dev->bus_rate <= I2C_MAX_FAST_MODE_FREQ ||
+ (!i2c_dev->setup.fmp_cr1_bit && IS_ERR_OR_NULL(i2c_dev->regmap)))
+ /* Optional */
+ return 0;
+
+ if (i2c_dev->setup.fmp_cr1_bit) {
+ if (enable)
+ stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32_I2C_CR1_FMP);
+ else
+ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32_I2C_CR1_FMP);
+ } else {
+ if (i2c_dev->fmp_sreg == i2c_dev->fmp_creg)
+ ret = regmap_update_bits(i2c_dev->regmap, i2c_dev->fmp_sreg,
+ i2c_dev->fmp_mask, enable ? i2c_dev->fmp_mask : 0);
+ else
+ ret = regmap_write(i2c_dev->regmap,
+ enable ? i2c_dev->fmp_sreg : i2c_dev->fmp_creg,
+ i2c_dev->fmp_mask);
+ }
+
+ return ret;
+}
+
static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev,
struct stm32f7_i2c_dev *i2c_dev)
{
struct device_node *np = pdev->dev.of_node;
int ret;
- u32 reg, mask;
i2c_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg-fmp");
- if (IS_ERR(i2c_dev->regmap)) {
+ if (IS_ERR(i2c_dev->regmap))
/* Optional */
return 0;
- }
- ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1, &reg);
+ ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1,
+ &i2c_dev->fmp_sreg);
if (ret)
return ret;
- ret = of_property_read_u32_index(np, "st,syscfg-fmp", 2, &mask);
- if (ret)
- return ret;
+ i2c_dev->fmp_creg = i2c_dev->fmp_sreg +
+ i2c_dev->setup.fmp_clr_offset;
+
+ return of_property_read_u32_index(np, "st,syscfg-fmp", 2,
+ &i2c_dev->fmp_mask);
+}
+
+static int stm32f7_i2c_enable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct i2c_adapter *adap = &i2c_dev->adap;
+ void __iomem *base = i2c_dev->base;
+ struct i2c_client *client;
+
+ client = i2c_new_slave_host_notify_device(adap);
+ if (IS_ERR(client))
+ return PTR_ERR(client);
+
+ i2c_dev->host_notify_client = client;
+
+ /* Enable SMBus Host address */
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_SMBHEN);
+
+ return 0;
+}
+
+static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
+{
+ void __iomem *base = i2c_dev->base;
+
+ if (i2c_dev->host_notify_client) {
+ /* Disable SMBus Host address */
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_SMBHEN);
+ i2c_free_slave_host_notify_device(i2c_dev->host_notify_client);
+ }
+}
+
+static int stm32f7_i2c_enable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_alert *alert;
+ struct i2c_adapter *adap = &i2c_dev->adap;
+ struct device *dev = i2c_dev->dev;
+ void __iomem *base = i2c_dev->base;
+
+ alert = devm_kzalloc(dev, sizeof(*alert), GFP_KERNEL);
+ if (!alert)
+ return -ENOMEM;
+
+ alert->ara = i2c_new_smbus_alert_device(adap, &alert->setup);
+ if (IS_ERR(alert->ara))
+ return PTR_ERR(alert->ara);
+
+ i2c_dev->alert = alert;
+
+ /* Enable SMBus Alert */
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_ALERTEN);
+
+ return 0;
+}
+
+static void stm32f7_i2c_disable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_alert *alert = i2c_dev->alert;
+ void __iomem *base = i2c_dev->base;
- return regmap_update_bits(i2c_dev->regmap, reg, mask, mask);
+ if (alert) {
+ /* Disable SMBus Alert */
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_ALERTEN);
+ i2c_unregister_device(alert->ara);
+ }
}
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
- I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
- I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC;
+ struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+
+ u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
+ I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+
+ if (i2c_dev->smbus_mode)
+ func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
+
+ return func;
}
-static struct i2c_algorithm stm32f7_i2c_algo = {
- .master_xfer = stm32f7_i2c_xfer,
+static const struct i2c_algorithm stm32f7_i2c_algo = {
+ .xfer = stm32f7_i2c_xfer,
+ .xfer_atomic = stm32f7_i2c_xfer_atomic,
.smbus_xfer = stm32f7_i2c_smbus_xfer,
.functionality = stm32f7_i2c_func,
.reg_slave = stm32f7_i2c_reg_slave,
@@ -1812,70 +2162,47 @@ static struct i2c_algorithm stm32f7_i2c_algo = {
static int stm32f7_i2c_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct stm32f7_i2c_dev *i2c_dev;
const struct stm32f7_i2c_setup *setup;
struct resource *res;
- u32 irq_error, irq_event, clk_rate, rise_time, fall_time;
struct i2c_adapter *adap;
struct reset_control *rst;
dma_addr_t phy_addr;
- int ret;
+ int irq_error, irq_event, ret;
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
+ setup = of_device_get_match_data(&pdev->dev);
+ if (!setup) {
+ dev_err(&pdev->dev, "Can't get device data\n");
+ return -ENODEV;
+ }
+ i2c_dev->setup = *setup;
+
+ i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c_dev->base))
return PTR_ERR(i2c_dev->base);
phy_addr = (dma_addr_t)res->start;
- irq_event = irq_of_parse_and_map(np, 0);
- if (!irq_event) {
- dev_err(&pdev->dev, "IRQ event missing or invalid\n");
- return -EINVAL;
- }
+ irq_event = platform_get_irq(pdev, 0);
+ if (irq_event < 0)
+ return irq_event;
- irq_error = irq_of_parse_and_map(np, 1);
- if (!irq_error) {
- dev_err(&pdev->dev, "IRQ error missing or invalid\n");
- return -EINVAL;
- }
+ i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node,
+ "wakeup-source");
- i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2c_dev->clk)) {
- dev_err(&pdev->dev, "Error: Missing controller clock\n");
- return PTR_ERR(i2c_dev->clk);
- }
-
- ret = clk_prepare_enable(i2c_dev->clk);
- if (ret) {
- dev_err(&pdev->dev, "Failed to prepare_enable clock\n");
- return ret;
- }
-
- i2c_dev->speed = STM32_I2C_SPEED_STANDARD;
- ret = device_property_read_u32(&pdev->dev, "clock-frequency",
- &clk_rate);
- if (!ret && clk_rate >= 1000000) {
- i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS;
- ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev);
- if (ret)
- goto clk_free;
- } else if (!ret && clk_rate >= 400000) {
- i2c_dev->speed = STM32_I2C_SPEED_FAST;
- } else if (!ret && clk_rate >= 100000) {
- i2c_dev->speed = STM32_I2C_SPEED_STANDARD;
- }
+ i2c_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(i2c_dev->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
+ "Failed to enable controller clock\n");
rst = devm_reset_control_get(&pdev->dev, NULL);
- if (IS_ERR(rst)) {
- dev_err(&pdev->dev, "Error: Missing controller reset\n");
- ret = PTR_ERR(rst);
- goto clk_free;
- }
+ if (IS_ERR(rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+ "Error: Missing reset ctrl\n");
+
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);
@@ -1887,41 +2214,39 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
stm32f7_i2c_isr_event_thread,
IRQF_ONESHOT,
pdev->name, i2c_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request irq event %i\n",
- irq_event);
- goto clk_free;
- }
-
- ret = devm_request_irq(&pdev->dev, irq_error, stm32f7_i2c_isr_error, 0,
- pdev->name, i2c_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request irq error %i\n",
- irq_error);
- goto clk_free;
- }
-
- setup = of_device_get_match_data(&pdev->dev);
- if (!setup) {
- dev_err(&pdev->dev, "Can't get device data\n");
- ret = -ENODEV;
- goto clk_free;
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request irq event\n");
+
+ if (!i2c_dev->setup.single_it_line) {
+ irq_error = platform_get_irq(pdev, 1);
+ if (irq_error < 0)
+ return irq_error;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq_error,
+ NULL,
+ stm32f7_i2c_isr_error_thread,
+ IRQF_ONESHOT,
+ pdev->name, i2c_dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request irq error\n");
}
- i2c_dev->setup = *setup;
-
- ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-rising-time-ns",
- &rise_time);
- if (!ret)
- i2c_dev->setup.rise_time = rise_time;
-
- ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-falling-time-ns",
- &fall_time);
- if (!ret)
- i2c_dev->setup.fall_time = fall_time;
ret = stm32f7_i2c_setup_timing(i2c_dev, &i2c_dev->setup);
if (ret)
- goto clk_free;
+ return ret;
+
+ /* Setup Fast mode plus if necessary */
+ if (i2c_dev->bus_rate > I2C_MAX_FAST_MODE_FREQ) {
+ if (!i2c_dev->setup.fmp_cr1_bit) {
+ ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = stm32f7_i2c_write_fm_plus_bits(i2c_dev, true);
+ if (ret)
+ return ret;
+ }
adap = &i2c_dev->adap;
i2c_set_adapdata(adap, i2c_dev);
@@ -1940,6 +2265,24 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
STM32F7_I2C_TXDR,
STM32F7_I2C_RXDR);
+ if (IS_ERR(i2c_dev->dma)) {
+ ret = PTR_ERR(i2c_dev->dma);
+ /* DMA support is optional, only report other errors */
+ if (ret != -ENODEV)
+ goto fmp_clear;
+ dev_dbg(i2c_dev->dev, "No DMA option: fallback using interrupts\n");
+ i2c_dev->dma = NULL;
+ }
+
+ if (i2c_dev->wakeup_src) {
+ device_set_wakeup_capable(i2c_dev->dev, true);
+
+ ret = dev_pm_set_wake_irq(i2c_dev->dev, irq_event);
+ if (ret) {
+ dev_err(i2c_dev->dev, "Failed to set wake up irq\n");
+ goto clr_wakeup_capable;
+ }
+ }
platform_set_drvdata(pdev, i2c_dev);
@@ -1953,86 +2296,231 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
stm32f7_i2c_hw_config(i2c_dev);
+ i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");
+
ret = i2c_add_adapter(adap);
if (ret)
goto pm_disable;
+ if (i2c_dev->smbus_mode) {
+ ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
+ if (ret) {
+ dev_err(i2c_dev->dev,
+ "failed to enable SMBus Host-Notify protocol (%d)\n",
+ ret);
+ goto i2c_adapter_remove;
+ }
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "smbus-alert")) {
+ ret = stm32f7_i2c_enable_smbus_alert(i2c_dev);
+ if (ret) {
+ dev_err(i2c_dev->dev,
+ "failed to enable SMBus alert protocol (%d)\n",
+ ret);
+ goto i2c_disable_smbus_host;
+ }
+ }
+
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
- pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return 0;
+i2c_disable_smbus_host:
+ stm32f7_i2c_disable_smbus_host(i2c_dev);
+
+i2c_adapter_remove:
+ i2c_del_adapter(adap);
+
pm_disable:
pm_runtime_put_noidle(i2c_dev->dev);
pm_runtime_disable(i2c_dev->dev);
pm_runtime_set_suspended(i2c_dev->dev);
pm_runtime_dont_use_autosuspend(i2c_dev->dev);
-clk_free:
- clk_disable_unprepare(i2c_dev->clk);
+ if (i2c_dev->wakeup_src)
+ dev_pm_clear_wake_irq(i2c_dev->dev);
+
+clr_wakeup_capable:
+ if (i2c_dev->wakeup_src)
+ device_set_wakeup_capable(i2c_dev->dev, false);
+
+ if (i2c_dev->dma) {
+ stm32_i2c_dma_free(i2c_dev->dma);
+ i2c_dev->dma = NULL;
+ }
+
+fmp_clear:
+ stm32f7_i2c_write_fm_plus_bits(i2c_dev, false);
return ret;
}
-static int stm32f7_i2c_remove(struct platform_device *pdev)
+static void stm32f7_i2c_remove(struct platform_device *pdev)
{
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
- if (i2c_dev->dma) {
- stm32_i2c_dma_free(i2c_dev->dma);
- i2c_dev->dma = NULL;
- }
+ stm32f7_i2c_disable_smbus_alert(i2c_dev);
+ stm32f7_i2c_disable_smbus_host(i2c_dev);
i2c_del_adapter(&i2c_dev->adap);
pm_runtime_get_sync(i2c_dev->dev);
- clk_disable_unprepare(i2c_dev->clk);
+ if (i2c_dev->wakeup_src) {
+ dev_pm_clear_wake_irq(i2c_dev->dev);
+ /*
+ * enforce that wakeup is disabled and that the device
+ * is marked as non wakeup capable
+ */
+ device_init_wakeup(i2c_dev->dev, false);
+ }
pm_runtime_put_noidle(i2c_dev->dev);
pm_runtime_disable(i2c_dev->dev);
pm_runtime_set_suspended(i2c_dev->dev);
pm_runtime_dont_use_autosuspend(i2c_dev->dev);
- return 0;
+ if (i2c_dev->dma) {
+ stm32_i2c_dma_free(i2c_dev->dma);
+ i2c_dev->dma = NULL;
+ }
+
+ stm32f7_i2c_write_fm_plus_bits(i2c_dev, false);
}
-#ifdef CONFIG_PM
-static int stm32f7_i2c_runtime_suspend(struct device *dev)
+static int __maybe_unused stm32f7_i2c_runtime_suspend(struct device *dev)
{
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
if (!stm32f7_i2c_is_slave_registered(i2c_dev))
- clk_disable_unprepare(i2c_dev->clk);
+ clk_disable(i2c_dev->clk);
return 0;
}
-static int stm32f7_i2c_runtime_resume(struct device *dev)
+static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
{
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
int ret;
if (!stm32f7_i2c_is_slave_registered(i2c_dev)) {
- ret = clk_prepare_enable(i2c_dev->clk);
+ ret = clk_enable(i2c_dev->clk);
if (ret) {
- dev_err(dev, "failed to prepare_enable clock\n");
+ dev_err(dev, "failed to enable clock\n");
return ret;
}
}
return 0;
}
-#endif
+
+static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
+{
+ int ret;
+ struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
+
+ ret = pm_runtime_resume_and_get(i2c_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ backup_regs->cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
+ backup_regs->cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
+ backup_regs->oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
+ backup_regs->oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
+ backup_regs->tmgr = readl_relaxed(i2c_dev->base + STM32F7_I2C_TIMINGR);
+ stm32f7_i2c_write_fm_plus_bits(i2c_dev, false);
+
+ pm_runtime_put_sync(i2c_dev->dev);
+
+ return ret;
+}
+
+static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
+{
+ u32 cr1;
+ int ret;
+ struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
+
+ ret = pm_runtime_resume_and_get(i2c_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
+ if (cr1 & STM32F7_I2C_CR1_PE)
+ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_PE);
+
+ writel_relaxed(backup_regs->tmgr, i2c_dev->base + STM32F7_I2C_TIMINGR);
+ writel_relaxed(backup_regs->cr1 & ~STM32F7_I2C_CR1_PE,
+ i2c_dev->base + STM32F7_I2C_CR1);
+ if (backup_regs->cr1 & STM32F7_I2C_CR1_PE)
+ stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_PE);
+ writel_relaxed(backup_regs->cr2, i2c_dev->base + STM32F7_I2C_CR2);
+ writel_relaxed(backup_regs->oar1, i2c_dev->base + STM32F7_I2C_OAR1);
+ writel_relaxed(backup_regs->oar2, i2c_dev->base + STM32F7_I2C_OAR2);
+ stm32f7_i2c_write_fm_plus_bits(i2c_dev, true);
+
+ pm_runtime_put_sync(i2c_dev->dev);
+
+ return ret;
+}
+
+static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
+{
+ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ int ret;
+
+ i2c_mark_adapter_suspended(&i2c_dev->adap);
+
+ if (!device_may_wakeup(dev) && !device_wakeup_path(dev)) {
+ ret = stm32f7_i2c_regs_backup(i2c_dev);
+ if (ret < 0) {
+ i2c_mark_adapter_resumed(&i2c_dev->adap);
+ return ret;
+ }
+
+ pinctrl_pm_select_sleep_state(dev);
+ pm_runtime_force_suspend(dev);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
+{
+ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!device_may_wakeup(dev) && !device_wakeup_path(dev)) {
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+ pinctrl_pm_select_default_state(dev);
+
+ ret = stm32f7_i2c_regs_restore(i2c_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ i2c_mark_adapter_resumed(&i2c_dev->adap);
+
+ return 0;
+}
static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
stm32f7_i2c_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(stm32f7_i2c_suspend, stm32f7_i2c_resume)
};
static const struct of_device_id stm32f7_i2c_match[] = {
{ .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup},
+ { .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_setup},
+ { .compatible = "st,stm32mp13-i2c", .data = &stm32mp13_setup},
+ { .compatible = "st,stm32mp25-i2c", .data = &stm32mp25_setup},
{},
};
MODULE_DEVICE_TABLE(of, stm32f7_i2c_match);
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
deleted file mode 100644
index 5503fa171df0..000000000000
--- a/drivers/i2c/busses/i2c-stu300.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * Copyright (C) 2007-2012 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
- * ST DDC I2C master mode driver, used in e.g. U300 series platforms.
- * Author: Linus Walleij <linus.walleij@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
- */
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-/* the name of this kernel module */
-#define NAME "stu300"
-
-/* CR (Control Register) 8bit (R/W) */
-#define I2C_CR (0x00000000)
-#define I2C_CR_RESET_VALUE (0x00)
-#define I2C_CR_RESET_UMASK (0x00)
-#define I2C_CR_DDC1_ENABLE (0x80)
-#define I2C_CR_TRANS_ENABLE (0x40)
-#define I2C_CR_PERIPHERAL_ENABLE (0x20)
-#define I2C_CR_DDC2B_ENABLE (0x10)
-#define I2C_CR_START_ENABLE (0x08)
-#define I2C_CR_ACK_ENABLE (0x04)
-#define I2C_CR_STOP_ENABLE (0x02)
-#define I2C_CR_INTERRUPT_ENABLE (0x01)
-/* SR1 (Status Register 1) 8bit (R/-) */
-#define I2C_SR1 (0x00000004)
-#define I2C_SR1_RESET_VALUE (0x00)
-#define I2C_SR1_RESET_UMASK (0x00)
-#define I2C_SR1_EVF_IND (0x80)
-#define I2C_SR1_ADD10_IND (0x40)
-#define I2C_SR1_TRA_IND (0x20)
-#define I2C_SR1_BUSY_IND (0x10)
-#define I2C_SR1_BTF_IND (0x08)
-#define I2C_SR1_ADSL_IND (0x04)
-#define I2C_SR1_MSL_IND (0x02)
-#define I2C_SR1_SB_IND (0x01)
-/* SR2 (Status Register 2) 8bit (R/-) */
-#define I2C_SR2 (0x00000008)
-#define I2C_SR2_RESET_VALUE (0x00)
-#define I2C_SR2_RESET_UMASK (0x40)
-#define I2C_SR2_MASK (0xBF)
-#define I2C_SR2_SCLFAL_IND (0x80)
-#define I2C_SR2_ENDAD_IND (0x20)
-#define I2C_SR2_AF_IND (0x10)
-#define I2C_SR2_STOPF_IND (0x08)
-#define I2C_SR2_ARLO_IND (0x04)
-#define I2C_SR2_BERR_IND (0x02)
-#define I2C_SR2_DDC2BF_IND (0x01)
-/* CCR (Clock Control Register) 8bit (R/W) */
-#define I2C_CCR (0x0000000C)
-#define I2C_CCR_RESET_VALUE (0x00)
-#define I2C_CCR_RESET_UMASK (0x00)
-#define I2C_CCR_MASK (0xFF)
-#define I2C_CCR_FMSM (0x80)
-#define I2C_CCR_CC_MASK (0x7F)
-/* OAR1 (Own Address Register 1) 8bit (R/W) */
-#define I2C_OAR1 (0x00000010)
-#define I2C_OAR1_RESET_VALUE (0x00)
-#define I2C_OAR1_RESET_UMASK (0x00)
-#define I2C_OAR1_ADD_MASK (0xFF)
-/* OAR2 (Own Address Register 2) 8bit (R/W) */
-#define I2C_OAR2 (0x00000014)
-#define I2C_OAR2_RESET_VALUE (0x40)
-#define I2C_OAR2_RESET_UMASK (0x19)
-#define I2C_OAR2_MASK (0xE6)
-#define I2C_OAR2_FR_25_10MHZ (0x00)
-#define I2C_OAR2_FR_10_1667MHZ (0x20)
-#define I2C_OAR2_FR_1667_2667MHZ (0x40)
-#define I2C_OAR2_FR_2667_40MHZ (0x60)
-#define I2C_OAR2_FR_40_5333MHZ (0x80)
-#define I2C_OAR2_FR_5333_66MHZ (0xA0)
-#define I2C_OAR2_FR_66_80MHZ (0xC0)
-#define I2C_OAR2_FR_80_100MHZ (0xE0)
-#define I2C_OAR2_FR_MASK (0xE0)
-#define I2C_OAR2_ADD_MASK (0x06)
-/* DR (Data Register) 8bit (R/W) */
-#define I2C_DR (0x00000018)
-#define I2C_DR_RESET_VALUE (0x00)
-#define I2C_DR_RESET_UMASK (0xFF)
-#define I2C_DR_D_MASK (0xFF)
-/* ECCR (Extended Clock Control Register) 8bit (R/W) */
-#define I2C_ECCR (0x0000001C)
-#define I2C_ECCR_RESET_VALUE (0x00)
-#define I2C_ECCR_RESET_UMASK (0xE0)
-#define I2C_ECCR_MASK (0x1F)
-#define I2C_ECCR_CC_MASK (0x1F)
-
-/*
- * These events are more or less responses to commands
- * sent into the hardware, presumably reflecting the state
- * of an internal state machine.
- */
-enum stu300_event {
- STU300_EVENT_NONE = 0,
- STU300_EVENT_1,
- STU300_EVENT_2,
- STU300_EVENT_3,
- STU300_EVENT_4,
- STU300_EVENT_5,
- STU300_EVENT_6,
- STU300_EVENT_7,
- STU300_EVENT_8,
- STU300_EVENT_9
-};
-
-enum stu300_error {
- STU300_ERROR_NONE = 0,
- STU300_ERROR_ACKNOWLEDGE_FAILURE,
- STU300_ERROR_BUS_ERROR,
- STU300_ERROR_ARBITRATION_LOST,
- STU300_ERROR_UNKNOWN
-};
-
-/* timeout waiting for the controller to respond */
-#define STU300_TIMEOUT (msecs_to_jiffies(1000))
-
-/*
- * The number of address send athemps tried before giving up.
- * If the first one fails it seems like 5 to 8 attempts are required.
- */
-#define NUM_ADDR_RESEND_ATTEMPTS 12
-
-/* I2C clock speed, in Hz 0-400kHz*/
-static unsigned int scl_frequency = 100000;
-module_param(scl_frequency, uint, 0644);
-
-/**
- * struct stu300_dev - the stu300 driver state holder
- * @pdev: parent platform device
- * @adapter: corresponding I2C adapter
- * @clk: hardware block clock
- * @irq: assigned interrupt line
- * @cmd_issue_lock: this locks the following cmd_ variables
- * @cmd_complete: acknowledge completion for an I2C command
- * @cmd_event: expected event coming in as a response to a command
- * @cmd_err: error code as response to a command
- * @speed: current bus speed in Hz
- * @msg_index: index of current message
- * @msg_len: length of current message
- */
-
-struct stu300_dev {
- struct platform_device *pdev;
- struct i2c_adapter adapter;
- void __iomem *virtbase;
- struct clk *clk;
- int irq;
- spinlock_t cmd_issue_lock;
- struct completion cmd_complete;
- enum stu300_event cmd_event;
- enum stu300_error cmd_err;
- unsigned int speed;
- int msg_index;
- int msg_len;
-};
-
-/* Local forward function declarations */
-static int stu300_init_hw(struct stu300_dev *dev);
-
-/*
- * The block needs writes in both MSW and LSW in order
- * for all data lines to reach their destination.
- */
-static inline void stu300_wr8(u32 value, void __iomem *address)
-{
- writel((value << 16) | value, address);
-}
-
-/*
- * This merely masks off the duplicates which appear
- * in bytes 1-3. You _MUST_ use 32-bit bus access on this
- * device, else it will not work.
- */
-static inline u32 stu300_r8(void __iomem *address)
-{
- return readl(address) & 0x000000FFU;
-}
-
-static void stu300_irq_enable(struct stu300_dev *dev)
-{
- u32 val;
- val = stu300_r8(dev->virtbase + I2C_CR);
- val |= I2C_CR_INTERRUPT_ENABLE;
- /* Twice paranoia (possible HW glitch) */
- stu300_wr8(val, dev->virtbase + I2C_CR);
- stu300_wr8(val, dev->virtbase + I2C_CR);
-}
-
-static void stu300_irq_disable(struct stu300_dev *dev)
-{
- u32 val;
- val = stu300_r8(dev->virtbase + I2C_CR);
- val &= ~I2C_CR_INTERRUPT_ENABLE;
- /* Twice paranoia (possible HW glitch) */
- stu300_wr8(val, dev->virtbase + I2C_CR);
- stu300_wr8(val, dev->virtbase + I2C_CR);
-}
-
-
-/*
- * Tells whether a certain event or events occurred in
- * response to a command. The events represent states in
- * the internal state machine of the hardware. The events
- * are not very well described in the hardware
- * documentation and can only be treated as abstract state
- * machine states.
- *
- * @ret 0 = event has not occurred or unknown error, any
- * other value means the correct event occurred or an error.
- */
-
-static int stu300_event_occurred(struct stu300_dev *dev,
- enum stu300_event mr_event) {
- u32 status1;
- u32 status2;
-
- /* What event happened? */
- status1 = stu300_r8(dev->virtbase + I2C_SR1);
-
- if (!(status1 & I2C_SR1_EVF_IND))
- /* No event at all */
- return 0;
-
- status2 = stu300_r8(dev->virtbase + I2C_SR2);
-
- /* Block any multiple interrupts */
- stu300_irq_disable(dev);
-
- /* Check for errors first */
- if (status2 & I2C_SR2_AF_IND) {
- dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE;
- return 1;
- } else if (status2 & I2C_SR2_BERR_IND) {
- dev->cmd_err = STU300_ERROR_BUS_ERROR;
- return 1;
- } else if (status2 & I2C_SR2_ARLO_IND) {
- dev->cmd_err = STU300_ERROR_ARBITRATION_LOST;
- return 1;
- }
-
- switch (mr_event) {
- case STU300_EVENT_1:
- if (status1 & I2C_SR1_ADSL_IND)
- return 1;
- break;
- case STU300_EVENT_2:
- case STU300_EVENT_3:
- case STU300_EVENT_7:
- case STU300_EVENT_8:
- if (status1 & I2C_SR1_BTF_IND) {
- return 1;
- }
- break;
- case STU300_EVENT_4:
- if (status2 & I2C_SR2_STOPF_IND)
- return 1;
- break;
- case STU300_EVENT_5:
- if (status1 & I2C_SR1_SB_IND)
- /* Clear start bit */
- return 1;
- break;
- case STU300_EVENT_6:
- if (status2 & I2C_SR2_ENDAD_IND) {
- /* First check for any errors */
- return 1;
- }
- break;
- case STU300_EVENT_9:
- if (status1 & I2C_SR1_ADD10_IND)
- return 1;
- break;
- default:
- break;
- }
- /* If we get here, we're on thin ice.
- * Here we are in a status where we have
- * gotten a response that does not match
- * what we requested.
- */
- dev->cmd_err = STU300_ERROR_UNKNOWN;
- dev_err(&dev->pdev->dev,
- "Unhandled interrupt! %d sr1: 0x%x sr2: 0x%x\n",
- mr_event, status1, status2);
- return 0;
-}
-
-static irqreturn_t stu300_irh(int irq, void *data)
-{
- struct stu300_dev *dev = data;
- int res;
-
- /* Just make sure that the block is clocked */
- clk_enable(dev->clk);
-
- /* See if this was what we were waiting for */
- spin_lock(&dev->cmd_issue_lock);
-
- res = stu300_event_occurred(dev, dev->cmd_event);
- if (res || dev->cmd_err != STU300_ERROR_NONE)
- complete(&dev->cmd_complete);
-
- spin_unlock(&dev->cmd_issue_lock);
-
- clk_disable(dev->clk);
-
- return IRQ_HANDLED;
-}
-
-/*
- * Sends a command and then waits for the bits masked by *flagmask*
- * to go high or low by IRQ awaiting.
- */
-static int stu300_start_and_await_event(struct stu300_dev *dev,
- u8 cr_value,
- enum stu300_event mr_event)
-{
- int ret;
-
- if (unlikely(irqs_disabled())) {
- /* TODO: implement polling for this case if need be. */
- WARN(1, "irqs are disabled, cannot poll for event\n");
- return -EIO;
- }
-
- /* Lock command issue, fill in an event we wait for */
- spin_lock_irq(&dev->cmd_issue_lock);
- init_completion(&dev->cmd_complete);
- dev->cmd_err = STU300_ERROR_NONE;
- dev->cmd_event = mr_event;
- spin_unlock_irq(&dev->cmd_issue_lock);
-
- /* Turn on interrupt, send command and wait. */
- cr_value |= I2C_CR_INTERRUPT_ENABLE;
- stu300_wr8(cr_value, dev->virtbase + I2C_CR);
- ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
- STU300_TIMEOUT);
- if (ret < 0) {
- dev_err(&dev->pdev->dev,
- "wait_for_completion_interruptible_timeout() "
- "returned %d waiting for event %04x\n", ret, mr_event);
- return ret;
- }
-
- if (ret == 0) {
- dev_err(&dev->pdev->dev, "controller timed out "
- "waiting for event %d, reinit hardware\n", mr_event);
- (void) stu300_init_hw(dev);
- return -ETIMEDOUT;
- }
-
- if (dev->cmd_err != STU300_ERROR_NONE) {
- dev_err(&dev->pdev->dev, "controller (start) "
- "error %d waiting for event %d, reinit hardware\n",
- dev->cmd_err, mr_event);
- (void) stu300_init_hw(dev);
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * This waits for a flag to be set, if it is not set on entry, an interrupt is
- * configured to wait for the flag using a completion.
- */
-static int stu300_await_event(struct stu300_dev *dev,
- enum stu300_event mr_event)
-{
- int ret;
-
- if (unlikely(irqs_disabled())) {
- /* TODO: implement polling for this case if need be. */
- dev_err(&dev->pdev->dev, "irqs are disabled on this "
- "system!\n");
- return -EIO;
- }
-
- /* Is it already here? */
- spin_lock_irq(&dev->cmd_issue_lock);
- dev->cmd_err = STU300_ERROR_NONE;
- dev->cmd_event = mr_event;
-
- init_completion(&dev->cmd_complete);
-
- /* Turn on the I2C interrupt for current operation */
- stu300_irq_enable(dev);
-
- /* Unlock the command block and wait for the event to occur */
- spin_unlock_irq(&dev->cmd_issue_lock);
-
- ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
- STU300_TIMEOUT);
- if (ret < 0) {
- dev_err(&dev->pdev->dev,
- "wait_for_completion_interruptible_timeout()"
- "returned %d waiting for event %04x\n", ret, mr_event);
- return ret;
- }
-
- if (ret == 0) {
- if (mr_event != STU300_EVENT_6) {
- dev_err(&dev->pdev->dev, "controller "
- "timed out waiting for event %d, reinit "
- "hardware\n", mr_event);
- (void) stu300_init_hw(dev);
- }
- return -ETIMEDOUT;
- }
-
- if (dev->cmd_err != STU300_ERROR_NONE) {
- if (mr_event != STU300_EVENT_6) {
- dev_err(&dev->pdev->dev, "controller "
- "error (await_event) %d waiting for event %d, "
- "reinit hardware\n", dev->cmd_err, mr_event);
- (void) stu300_init_hw(dev);
- }
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * Waits for the busy bit to go low by repeated polling.
- */
-#define BUSY_RELEASE_ATTEMPTS 10
-static int stu300_wait_while_busy(struct stu300_dev *dev)
-{
- unsigned long timeout;
- int i;
-
- for (i = 0; i < BUSY_RELEASE_ATTEMPTS; i++) {
- timeout = jiffies + STU300_TIMEOUT;
-
- while (!time_after(jiffies, timeout)) {
- /* Is not busy? */
- if ((stu300_r8(dev->virtbase + I2C_SR1) &
- I2C_SR1_BUSY_IND) == 0)
- return 0;
- msleep(1);
- }
-
- dev_err(&dev->pdev->dev, "transaction timed out "
- "waiting for device to be free (not busy). "
- "Attempt: %d\n", i+1);
-
- dev_err(&dev->pdev->dev, "base address = "
- "0x%08x, reinit hardware\n", (u32) dev->virtbase);
-
- (void) stu300_init_hw(dev);
- }
-
- dev_err(&dev->pdev->dev, "giving up after %d attempts "
- "to reset the bus.\n", BUSY_RELEASE_ATTEMPTS);
-
- return -ETIMEDOUT;
-}
-
-struct stu300_clkset {
- unsigned long rate;
- u32 setting;
-};
-
-static const struct stu300_clkset stu300_clktable[] = {
- { 0, 0xFFU },
- { 2500000, I2C_OAR2_FR_25_10MHZ },
- { 10000000, I2C_OAR2_FR_10_1667MHZ },
- { 16670000, I2C_OAR2_FR_1667_2667MHZ },
- { 26670000, I2C_OAR2_FR_2667_40MHZ },
- { 40000000, I2C_OAR2_FR_40_5333MHZ },
- { 53330000, I2C_OAR2_FR_5333_66MHZ },
- { 66000000, I2C_OAR2_FR_66_80MHZ },
- { 80000000, I2C_OAR2_FR_80_100MHZ },
- { 100000000, 0xFFU },
-};
-
-
-static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate)
-{
-
- u32 val;
- int i = 0;
-
- /* Locate the appropriate clock setting */
- while (i < ARRAY_SIZE(stu300_clktable) - 1 &&
- stu300_clktable[i].rate < clkrate)
- i++;
-
- if (stu300_clktable[i].setting == 0xFFU) {
- dev_err(&dev->pdev->dev, "too %s clock rate requested "
- "(%lu Hz).\n", i ? "high" : "low", clkrate);
- return -EINVAL;
- }
-
- stu300_wr8(stu300_clktable[i].setting,
- dev->virtbase + I2C_OAR2);
-
- dev_dbg(&dev->pdev->dev, "Clock rate %lu Hz, I2C bus speed %d Hz "
- "virtbase %p\n", clkrate, dev->speed, dev->virtbase);
-
- if (dev->speed > 100000)
- /* Fast Mode I2C */
- val = ((clkrate/dev->speed) - 9)/3 + 1;
- else
- /* Standard Mode I2C */
- val = ((clkrate/dev->speed) - 7)/2 + 1;
-
- /* According to spec the divider must be > 2 */
- if (val < 0x002) {
- dev_err(&dev->pdev->dev, "too low clock rate (%lu Hz).\n",
- clkrate);
- return -EINVAL;
- }
-
- /* We have 12 bits clock divider only! */
- if (val & 0xFFFFF000U) {
- dev_err(&dev->pdev->dev, "too high clock rate (%lu Hz).\n",
- clkrate);
- return -EINVAL;
- }
-
- if (dev->speed > 100000) {
- /* CC6..CC0 */
- stu300_wr8((val & I2C_CCR_CC_MASK) | I2C_CCR_FMSM,
- dev->virtbase + I2C_CCR);
- dev_dbg(&dev->pdev->dev, "set clock divider to 0x%08x, "
- "Fast Mode I2C\n", val);
- } else {
- /* CC6..CC0 */
- stu300_wr8((val & I2C_CCR_CC_MASK),
- dev->virtbase + I2C_CCR);
- dev_dbg(&dev->pdev->dev, "set clock divider to "
- "0x%08x, Standard Mode I2C\n", val);
- }
-
- /* CC11..CC7 */
- stu300_wr8(((val >> 7) & 0x1F),
- dev->virtbase + I2C_ECCR);
-
- return 0;
-}
-
-
-static int stu300_init_hw(struct stu300_dev *dev)
-{
- u32 dummy;
- unsigned long clkrate;
- int ret;
-
- /* Disable controller */
- stu300_wr8(0x00, dev->virtbase + I2C_CR);
- /*
- * Set own address to some default value (0x00).
- * We do not support slave mode anyway.
- */
- stu300_wr8(0x00, dev->virtbase + I2C_OAR1);
- /*
- * The I2C controller only operates properly in 26 MHz but we
- * program this driver as if we didn't know. This will also set the two
- * high bits of the own address to zero as well.
- * There is no known hardware issue with running in 13 MHz
- * However, speeds over 200 kHz are not used.
- */
- clkrate = clk_get_rate(dev->clk);
- ret = stu300_set_clk(dev, clkrate);
-
- if (ret)
- return ret;
- /*
- * Enable block, do it TWICE (hardware glitch)
- * Setting bit 7 can enable DDC mode. (Not used currently.)
- */
- stu300_wr8(I2C_CR_PERIPHERAL_ENABLE,
- dev->virtbase + I2C_CR);
- stu300_wr8(I2C_CR_PERIPHERAL_ENABLE,
- dev->virtbase + I2C_CR);
- /* Make a dummy read of the status register SR1 & SR2 */
- dummy = stu300_r8(dev->virtbase + I2C_SR2);
- dummy = stu300_r8(dev->virtbase + I2C_SR1);
-
- return 0;
-}
-
-
-
-/* Send slave address. */
-static int stu300_send_address(struct stu300_dev *dev,
- struct i2c_msg *msg, int resend)
-{
- u32 val;
- int ret;
-
- if (msg->flags & I2C_M_TEN) {
- /* This is probably how 10 bit addresses look */
- val = (0xf0 | (((u32) msg->addr & 0x300) >> 7)) &
- I2C_DR_D_MASK;
- if (msg->flags & I2C_M_RD)
- /* This is the direction bit */
- val |= 0x01;
- } else {
- val = i2c_8bit_addr_from_msg(msg);
- }
-
- if (resend) {
- if (msg->flags & I2C_M_RD)
- dev_dbg(&dev->pdev->dev, "read resend\n");
- else
- dev_dbg(&dev->pdev->dev, "write resend\n");
- }
-
- stu300_wr8(val, dev->virtbase + I2C_DR);
-
- /* For 10bit addressing, await 10bit request (EVENT 9) */
- if (msg->flags & I2C_M_TEN) {
- ret = stu300_await_event(dev, STU300_EVENT_9);
- /*
- * The slave device wants a 10bit address, send the rest
- * of the bits (the LSBits)
- */
- val = msg->addr & I2C_DR_D_MASK;
- /* This clears "event 9" */
- stu300_wr8(val, dev->virtbase + I2C_DR);
- if (ret != 0)
- return ret;
- }
- /* FIXME: Why no else here? two events for 10bit?
- * Await event 6 (normal) or event 9 (10bit)
- */
-
- if (resend)
- dev_dbg(&dev->pdev->dev, "await event 6\n");
- ret = stu300_await_event(dev, STU300_EVENT_6);
-
- /*
- * Clear any pending EVENT 6 no matter what happened during
- * await_event.
- */
- val = stu300_r8(dev->virtbase + I2C_CR);
- val |= I2C_CR_PERIPHERAL_ENABLE;
- stu300_wr8(val, dev->virtbase + I2C_CR);
-
- return ret;
-}
-
-static int stu300_xfer_msg(struct i2c_adapter *adap,
- struct i2c_msg *msg, int stop)
-{
- u32 cr;
- u32 val;
- u32 i;
- int ret;
- int attempts = 0;
- struct stu300_dev *dev = i2c_get_adapdata(adap);
-
- clk_enable(dev->clk);
-
- /* Remove this if (0) to trace each and every message. */
- if (0) {
- dev_dbg(&dev->pdev->dev, "I2C message to: 0x%04x, len: %d, "
- "flags: 0x%04x, stop: %d\n",
- msg->addr, msg->len, msg->flags, stop);
- }
-
- /*
- * For some reason, sending the address sometimes fails when running
- * on the 13 MHz clock. No interrupt arrives. This is a work around,
- * which tries to restart and send the address up to 10 times before
- * really giving up. Usually 5 to 8 attempts are enough.
- */
- do {
- if (attempts)
- dev_dbg(&dev->pdev->dev, "wait while busy\n");
- /* Check that the bus is free, or wait until some timeout */
- ret = stu300_wait_while_busy(dev);
- if (ret != 0)
- goto exit_disable;
-
- if (attempts)
- dev_dbg(&dev->pdev->dev, "re-int hw\n");
- /*
- * According to ST, there is no problem if the clock is
- * changed between 13 and 26 MHz during a transfer.
- */
- ret = stu300_init_hw(dev);
- if (ret)
- goto exit_disable;
-
- /* Send a start condition */
- cr = I2C_CR_PERIPHERAL_ENABLE;
- /* Setting the START bit puts the block in master mode */
- if (!(msg->flags & I2C_M_NOSTART))
- cr |= I2C_CR_START_ENABLE;
- if ((msg->flags & I2C_M_RD) && (msg->len > 1))
- /* On read more than 1 byte, we need ack. */
- cr |= I2C_CR_ACK_ENABLE;
- /* Check that it gets through */
- if (!(msg->flags & I2C_M_NOSTART)) {
- if (attempts)
- dev_dbg(&dev->pdev->dev, "send start event\n");
- ret = stu300_start_and_await_event(dev, cr,
- STU300_EVENT_5);
- }
-
- if (attempts)
- dev_dbg(&dev->pdev->dev, "send address\n");
-
- if (ret == 0)
- /* Send address */
- ret = stu300_send_address(dev, msg, attempts != 0);
-
- if (ret != 0) {
- attempts++;
- dev_dbg(&dev->pdev->dev, "failed sending address, "
- "retrying. Attempt: %d msg_index: %d/%d\n",
- attempts, dev->msg_index, dev->msg_len);
- }
-
- } while (ret != 0 && attempts < NUM_ADDR_RESEND_ATTEMPTS);
-
- if (attempts < NUM_ADDR_RESEND_ATTEMPTS && attempts > 0) {
- dev_dbg(&dev->pdev->dev, "managed to get address "
- "through after %d attempts\n", attempts);
- } else if (attempts == NUM_ADDR_RESEND_ATTEMPTS) {
- dev_dbg(&dev->pdev->dev, "I give up, tried %d times "
- "to resend address.\n",
- NUM_ADDR_RESEND_ATTEMPTS);
- goto exit_disable;
- }
-
-
- if (msg->flags & I2C_M_RD) {
- /* READ: we read the actual bytes one at a time */
- for (i = 0; i < msg->len; i++) {
- if (i == msg->len-1) {
- /*
- * Disable ACK and set STOP condition before
- * reading last byte
- */
- val = I2C_CR_PERIPHERAL_ENABLE;
-
- if (stop)
- val |= I2C_CR_STOP_ENABLE;
-
- stu300_wr8(val,
- dev->virtbase + I2C_CR);
- }
- /* Wait for this byte... */
- ret = stu300_await_event(dev, STU300_EVENT_7);
- if (ret != 0)
- goto exit_disable;
- /* This clears event 7 */
- msg->buf[i] = (u8) stu300_r8(dev->virtbase + I2C_DR);
- }
- } else {
- /* WRITE: we send the actual bytes one at a time */
- for (i = 0; i < msg->len; i++) {
- /* Write the byte */
- stu300_wr8(msg->buf[i],
- dev->virtbase + I2C_DR);
- /* Check status */
- ret = stu300_await_event(dev, STU300_EVENT_8);
- /* Next write to DR will clear event 8 */
- if (ret != 0) {
- dev_err(&dev->pdev->dev, "error awaiting "
- "event 8 (%d)\n", ret);
- goto exit_disable;
- }
- }
- /* Check NAK */
- if (!(msg->flags & I2C_M_IGNORE_NAK)) {
- if (stu300_r8(dev->virtbase + I2C_SR2) &
- I2C_SR2_AF_IND) {
- dev_err(&dev->pdev->dev, "I2C payload "
- "send returned NAK!\n");
- ret = -EIO;
- goto exit_disable;
- }
- }
- if (stop) {
- /* Send stop condition */
- val = I2C_CR_PERIPHERAL_ENABLE;
- val |= I2C_CR_STOP_ENABLE;
- stu300_wr8(val, dev->virtbase + I2C_CR);
- }
- }
-
- /* Check that the bus is free, or wait until some timeout occurs */
- ret = stu300_wait_while_busy(dev);
- if (ret != 0) {
- dev_err(&dev->pdev->dev, "timeout waiting for transfer "
- "to commence.\n");
- goto exit_disable;
- }
-
- /* Dummy read status registers */
- val = stu300_r8(dev->virtbase + I2C_SR2);
- val = stu300_r8(dev->virtbase + I2C_SR1);
- ret = 0;
-
- exit_disable:
- /* Disable controller */
- stu300_wr8(0x00, dev->virtbase + I2C_CR);
- clk_disable(dev->clk);
- return ret;
-}
-
-static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
-{
- int ret = -1;
- int i;
-
- struct stu300_dev *dev = i2c_get_adapdata(adap);
- dev->msg_len = num;
-
- for (i = 0; i < num; i++) {
- /*
- * Another driver appears to send stop for each message,
- * here we only do that for the last message. Possibly some
- * peripherals require this behaviour, then their drivers
- * have to send single messages in order to get "stop" for
- * each message.
- */
- dev->msg_index = i;
-
- ret = stu300_xfer_msg(adap, &msgs[i], (i == (num - 1)));
-
- if (ret != 0) {
- num = ret;
- break;
- }
- }
-
- return num;
-}
-
-static u32 stu300_func(struct i2c_adapter *adap)
-{
- /* This is the simplest thing you can think of... */
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
-}
-
-static const struct i2c_algorithm stu300_algo = {
- .master_xfer = stu300_xfer,
- .functionality = stu300_func,
-};
-
-static const struct i2c_adapter_quirks stu300_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
-};
-
-static int stu300_probe(struct platform_device *pdev)
-{
- struct stu300_dev *dev;
- struct i2c_adapter *adap;
- struct resource *res;
- int bus_nr;
- int ret = 0;
-
- dev = devm_kzalloc(&pdev->dev, sizeof(struct stu300_dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- bus_nr = pdev->id;
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- dev_err(&pdev->dev, "could not retrieve i2c bus clock\n");
- return PTR_ERR(dev->clk);
- }
-
- dev->pdev = pdev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->virtbase = devm_ioremap_resource(&pdev->dev, res);
- dev_dbg(&pdev->dev, "initialize bus device I2C%d on virtual "
- "base %p\n", bus_nr, dev->virtbase);
- if (IS_ERR(dev->virtbase))
- return PTR_ERR(dev->virtbase);
-
- dev->irq = platform_get_irq(pdev, 0);
- ret = devm_request_irq(&pdev->dev, dev->irq, stu300_irh, 0, NAME, dev);
- if (ret < 0)
- return ret;
-
- dev->speed = scl_frequency;
-
- clk_prepare_enable(dev->clk);
- ret = stu300_init_hw(dev);
- clk_disable(dev->clk);
- if (ret != 0) {
- dev_err(&dev->pdev->dev, "error initializing hardware.\n");
- return -EIO;
- }
-
- /* IRQ event handling initialization */
- spin_lock_init(&dev->cmd_issue_lock);
- dev->cmd_event = STU300_EVENT_NONE;
- dev->cmd_err = STU300_ERROR_NONE;
-
- adap = &dev->adapter;
- adap->owner = THIS_MODULE;
- /* DDC class but actually often used for more generic I2C */
- adap->class = I2C_CLASS_DEPRECATED;
- strlcpy(adap->name, "ST Microelectronics DDC I2C adapter",
- sizeof(adap->name));
- adap->nr = bus_nr;
- adap->algo = &stu300_algo;
- adap->dev.parent = &pdev->dev;
- adap->dev.of_node = pdev->dev.of_node;
- adap->quirks = &stu300_quirks;
-
- i2c_set_adapdata(adap, dev);
-
- /* i2c device drivers may be active on return from add_adapter() */
- ret = i2c_add_numbered_adapter(adap);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, dev);
- dev_info(&pdev->dev, "ST DDC I2C @ %p, irq %d\n",
- dev->virtbase, dev->irq);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int stu300_suspend(struct device *device)
-{
- struct stu300_dev *dev = dev_get_drvdata(device);
-
- /* Turn off everything */
- stu300_wr8(0x00, dev->virtbase + I2C_CR);
- return 0;
-}
-
-static int stu300_resume(struct device *device)
-{
- int ret = 0;
- struct stu300_dev *dev = dev_get_drvdata(device);
-
- clk_enable(dev->clk);
- ret = stu300_init_hw(dev);
- clk_disable(dev->clk);
-
- if (ret != 0)
- dev_err(device, "error re-initializing hardware.\n");
- return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(stu300_pm, stu300_suspend, stu300_resume);
-#define STU300_I2C_PM (&stu300_pm)
-#else
-#define STU300_I2C_PM NULL
-#endif
-
-static int stu300_remove(struct platform_device *pdev)
-{
- struct stu300_dev *dev = platform_get_drvdata(pdev);
-
- i2c_del_adapter(&dev->adapter);
- /* Turn off everything */
- stu300_wr8(0x00, dev->virtbase + I2C_CR);
- return 0;
-}
-
-static const struct of_device_id stu300_dt_match[] = {
- { .compatible = "st,ddci2c" },
- {},
-};
-MODULE_DEVICE_TABLE(of, stu300_dt_match);
-
-static struct platform_driver stu300_i2c_driver = {
- .driver = {
- .name = NAME,
- .pm = STU300_I2C_PM,
- .of_match_table = stu300_dt_match,
- },
- .probe = stu300_probe,
- .remove = stu300_remove,
-
-};
-
-static int __init stu300_init(void)
-{
- return platform_driver_register(&stu300_i2c_driver);
-}
-
-static void __exit stu300_exit(void)
-{
- platform_driver_unregister(&stu300_i2c_driver);
-}
-
-/*
- * The systems using this bus often have very basic devices such
- * as regulators on the I2C bus, so this needs to be loaded early.
- * Therefore it is registered in the subsys_initcall().
- */
-subsys_initcall(stu300_init);
-module_exit(stu300_exit);
-
-MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
-MODULE_DESCRIPTION("ST Micro DDC I2C adapter (" NAME ")");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" NAME);
diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c
index 7c07ce116e38..fb5280b8cf7f 100644
--- a/drivers/i2c/busses/i2c-sun6i-p2wi.c
+++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c
@@ -10,7 +10,7 @@
* The P2WI controller looks like an SMBus controller which only supports byte
* data transfers. But, it differs from standard SMBus protocol on several
* aspects:
- * - it supports only one slave device, and thus drop the address field
+ * - it supports only one target device, and thus drop the address field
* - it adds a parity bit every 8bits of data
* - only one read access is required to read a byte (instead of a write
* followed by a read access in standard SMBus protocol)
@@ -88,7 +88,7 @@ struct p2wi {
void __iomem *regs;
struct clk *clk;
struct reset_control *rstc;
- int slave_addr;
+ int target_addr;
};
static irqreturn_t p2wi_interrupt(int irq, void *dev_id)
@@ -121,7 +121,7 @@ static int p2wi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
struct p2wi *p2wi = i2c_get_adapdata(adap);
unsigned long dlen = P2WI_DLEN_DATA_LENGTH(1);
- if (p2wi->slave_addr >= 0 && addr != p2wi->slave_addr) {
+ if (p2wi->target_addr >= 0 && addr != p2wi->target_addr) {
dev_err(&adap->dev, "invalid P2WI address\n");
return -EINVAL;
}
@@ -186,10 +186,9 @@ static int p2wi_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct device_node *childnp;
unsigned long parent_clk_freq;
- u32 clk_freq = 100000;
- struct resource *r;
+ u32 clk_freq = I2C_MAX_STANDARD_MODE_FREQ;
struct p2wi *p2wi;
- u32 slave_addr;
+ u32 target_addr;
int clk_div;
int irq;
int ret;
@@ -202,8 +201,13 @@ static int p2wi_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (clk_freq == 0) {
+ dev_err(dev, "clock-frequency is set to 0 in DT\n");
+ return -EINVAL;
+ }
+
if (of_get_child_count(np) > 1) {
- dev_err(dev, "P2WI only supports one slave device\n");
+ dev_err(dev, "P2WI only supports one target device\n");
return -EINVAL;
}
@@ -211,47 +215,38 @@ static int p2wi_probe(struct platform_device *pdev)
if (!p2wi)
return -ENOMEM;
- p2wi->slave_addr = -1;
+ p2wi->target_addr = -1;
/*
* Authorize a p2wi node without any children to be able to use an
* i2c-dev from userpace.
- * In this case the slave_addr is set to -1 and won't be checked when
+ * In this case the target_addr is set to -1 and won't be checked when
* launching a P2WI transfer.
*/
childnp = of_get_next_available_child(np, NULL);
if (childnp) {
- ret = of_property_read_u32(childnp, "reg", &slave_addr);
+ ret = of_property_read_u32(childnp, "reg", &target_addr);
if (ret) {
- dev_err(dev, "invalid slave address on node %pOF\n",
+ dev_err(dev, "invalid target address on node %pOF\n",
childnp);
return -EINVAL;
}
- p2wi->slave_addr = slave_addr;
+ p2wi->target_addr = target_addr;
}
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- p2wi->regs = devm_ioremap_resource(dev, r);
+ p2wi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(p2wi->regs))
return PTR_ERR(p2wi->regs);
- strlcpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name));
+ strscpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name));
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to retrieve irq: %d\n", irq);
+ if (irq < 0)
return irq;
- }
- p2wi->clk = devm_clk_get(dev, NULL);
+ p2wi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(p2wi->clk)) {
ret = PTR_ERR(p2wi->clk);
- dev_err(dev, "failed to retrieve clk: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(p2wi->clk);
- if (ret) {
dev_err(dev, "failed to enable clk: %d\n", ret);
return ret;
}
@@ -260,15 +255,15 @@ static int p2wi_probe(struct platform_device *pdev)
p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(p2wi->rstc)) {
- ret = PTR_ERR(p2wi->rstc);
- dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
- goto err_clk_disable;
+ dev_err(dev, "failed to retrieve reset controller: %pe\n",
+ p2wi->rstc);
+ return PTR_ERR(p2wi->rstc);
}
ret = reset_control_deassert(p2wi->rstc);
if (ret) {
dev_err(dev, "failed to deassert reset line: %d\n", ret);
- goto err_clk_disable;
+ return ret;
}
init_completion(&p2wi->complete);
@@ -311,26 +306,20 @@ static int p2wi_probe(struct platform_device *pdev)
err_reset_assert:
reset_control_assert(p2wi->rstc);
-err_clk_disable:
- clk_disable_unprepare(p2wi->clk);
-
return ret;
}
-static int p2wi_remove(struct platform_device *dev)
+static void p2wi_remove(struct platform_device *dev)
{
struct p2wi *p2wi = platform_get_drvdata(dev);
reset_control_assert(p2wi->rstc);
- clk_disable_unprepare(p2wi->clk);
i2c_del_adapter(&p2wi->adapter);
-
- return 0;
}
static struct platform_driver p2wi_driver = {
.probe = p2wi_probe,
- .remove = p2wi_remove,
+ .remove = p2wi_remove,
.driver = {
.name = "i2c-sunxi-p2wi",
.of_match_table = p2wi_of_match_table,
diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c
index 2184b7c3580e..1230f51e1624 100644
--- a/drivers/i2c/busses/i2c-synquacer.c
+++ b/drivers/i2c/busses/i2c-synquacer.c
@@ -67,10 +67,10 @@
/* STANDARD MODE frequency */
#define SYNQUACER_I2C_CLK_MASTER_STD(rate) \
- DIV_ROUND_UP(DIV_ROUND_UP((rate), 100000) - 2, 2)
+ DIV_ROUND_UP(DIV_ROUND_UP((rate), I2C_MAX_STANDARD_MODE_FREQ) - 2, 2)
/* FAST MODE frequency */
#define SYNQUACER_I2C_CLK_MASTER_FAST(rate) \
- DIV_ROUND_UP((DIV_ROUND_UP((rate), 400000) - 2) * 2, 3)
+ DIV_ROUND_UP((DIV_ROUND_UP((rate), I2C_MAX_FAST_MODE_FREQ) - 2) * 2, 3)
/* (clkrate <= 18000000) */
/* calculate the value of CS bits in CCR register on standard mode */
@@ -138,14 +138,11 @@ struct synquacer_i2c {
int irq;
struct device *dev;
void __iomem *base;
- struct clk *pclk;
u32 pclkrate;
u32 speed_khz;
u32 timeout_ms;
enum i2c_state state;
struct i2c_adapter adapter;
-
- bool is_suspended;
};
static inline int is_lastmsg(struct synquacer_i2c *i2c)
@@ -313,12 +310,9 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
struct i2c_msg *msgs, int num)
{
unsigned char bsr;
- unsigned long timeout;
+ unsigned long time_left;
int ret;
- if (i2c->is_suspended)
- return -EBUSY;
-
synquacer_i2c_hw_init(i2c);
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
if (bsr & SYNQUACER_I2C_BSR_BB) {
@@ -340,9 +334,9 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
return ret;
}
- timeout = wait_for_completion_timeout(&i2c->completion,
- msecs_to_jiffies(i2c->timeout_ms));
- if (timeout == 0) {
+ time_left = wait_for_completion_timeout(&i2c->completion,
+ msecs_to_jiffies(i2c->timeout_ms));
+ if (time_left == 0) {
dev_dbg(i2c->dev, "timeout\n");
return -EAGAIN;
}
@@ -356,7 +350,7 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
/* wait 2 clock periods to ensure the stop has been through the bus */
udelay(DIV_ROUND_UP(2 * 1000, i2c->speed_khz));
- return 0;
+ return ret;
}
static irqreturn_t synquacer_i2c_isr(int irq, void *dev_id)
@@ -403,8 +397,7 @@ static irqreturn_t synquacer_i2c_isr(int irq, void *dev_id)
if (i2c->state == STATE_READ)
goto prepare_read;
-
- /* fall through */
+ fallthrough;
case STATE_WRITE:
if (bsr & SYNQUACER_I2C_BSR_LRB) {
@@ -527,11 +520,11 @@ static u32 synquacer_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm synquacer_i2c_algo = {
- .master_xfer = synquacer_i2c_xfer,
- .functionality = synquacer_i2c_functionality,
+ .xfer = synquacer_i2c_xfer,
+ .functionality = synquacer_i2c_functionality,
};
-static struct i2c_adapter synquacer_i2c_ops = {
+static const struct i2c_adapter synquacer_i2c_ops = {
.owner = THIS_MODULE,
.name = "synquacer_i2c-adapter",
.algo = &synquacer_i2c_algo,
@@ -541,7 +534,7 @@ static struct i2c_adapter synquacer_i2c_ops = {
static int synquacer_i2c_probe(struct platform_device *pdev)
{
struct synquacer_i2c *i2c;
- struct resource *r;
+ struct clk *pclk;
u32 bus_speed;
int ret;
@@ -557,55 +550,44 @@ static int synquacer_i2c_probe(struct platform_device *pdev)
device_property_read_u32(&pdev->dev, "socionext,pclk-rate",
&i2c->pclkrate);
- i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(i2c->pclk) && PTR_ERR(i2c->pclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- if (!IS_ERR_OR_NULL(i2c->pclk)) {
- dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk);
+ pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pclk),
+ "failed to get and enable clock\n");
- ret = clk_prepare_enable(i2c->pclk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock (%d)\n",
- ret);
- return ret;
- }
- i2c->pclkrate = clk_get_rate(i2c->pclk);
- }
+ if (pclk)
+ i2c->pclkrate = clk_get_rate(pclk);
if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE ||
- i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) {
- dev_err(&pdev->dev, "PCLK missing or out of range (%d)\n",
- i2c->pclkrate);
- return -EINVAL;
- }
+ i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "PCLK missing or out of range (%d)\n",
+ i2c->pclkrate);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, r);
+ i2c->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
i2c->irq = platform_get_irq(pdev, 0);
- if (i2c->irq < 0) {
- dev_err(&pdev->dev, "no IRQ resource found\n");
- return -ENODEV;
- }
+ if (i2c->irq < 0)
+ return i2c->irq;
ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr,
0, dev_name(&pdev->dev), i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "cannot claim IRQ %d\n", i2c->irq);
i2c->state = STATE_IDLE;
i2c->dev = &pdev->dev;
i2c->adapter = synquacer_i2c_ops;
i2c_set_adapdata(&i2c->adapter, i2c);
i2c->adapter.dev.parent = &pdev->dev;
+ i2c->adapter.dev.of_node = pdev->dev.of_node;
+ ACPI_COMPANION_SET(&i2c->adapter.dev, ACPI_COMPANION(&pdev->dev));
i2c->adapter.nr = pdev->id;
init_completion(&i2c->completion);
- if (bus_speed < 400000)
+ if (bus_speed < I2C_MAX_FAST_MODE_FREQ)
i2c->speed_khz = SYNQUACER_I2C_SPEED_SM;
else
i2c->speed_khz = SYNQUACER_I2C_SPEED_FM;
@@ -613,10 +595,8 @@ static int synquacer_i2c_probe(struct platform_device *pdev)
synquacer_i2c_hw_init(i2c);
ret = i2c_add_numbered_adapter(&i2c->adapter);
- if (ret) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to add bus to i2c core\n");
platform_set_drvdata(pdev, i2c);
@@ -626,18 +606,14 @@ static int synquacer_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int synquacer_i2c_remove(struct platform_device *pdev)
+static void synquacer_i2c_remove(struct platform_device *pdev)
{
struct synquacer_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adapter);
- if (!IS_ERR(i2c->pclk))
- clk_disable_unprepare(i2c->pclk);
-
- return 0;
};
-static const struct of_device_id synquacer_i2c_dt_ids[] = {
+static const struct of_device_id synquacer_i2c_dt_ids[] __maybe_unused = {
{ .compatible = "socionext,synquacer-i2c" },
{ /* sentinel */ }
};
@@ -653,7 +629,7 @@ MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids);
static struct platform_driver synquacer_i2c_driver = {
.probe = synquacer_i2c_probe,
- .remove = synquacer_i2c_remove,
+ .remove = synquacer_i2c_remove,
.driver = {
.name = "synquacer_i2c",
.of_match_table = of_match_ptr(synquacer_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c
index 7c7fc01116a1..cb97f72291bc 100644
--- a/drivers/i2c/busses/i2c-taos-evm.c
+++ b/drivers/i2c/busses/i2c-taos-evm.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the TAOS evaluation modules
- * These devices include an I2C master which can be controlled over the
+ * These devices include an I2C controller which can be controlled over the
* serial port.
*
* Copyright (C) 2007 Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/delay.h>
@@ -47,7 +39,7 @@ struct taos_data {
};
/* TAOS TSL2550 EVM */
-static struct i2c_board_info tsl2550_info = {
+static const struct i2c_board_info tsl2550_info = {
I2C_BOARD_INFO("tsl2550", 0x39),
};
@@ -57,10 +49,10 @@ static struct i2c_client *taos_instantiate_device(struct i2c_adapter *adapter)
if (!strncmp(adapter->name, "TAOS TSL2550 EVM", 16)) {
dev_info(&adapter->dev, "Instantiating device %s at 0x%02x\n",
tsl2550_info.type, tsl2550_info.addr);
- return i2c_new_device(adapter, &tsl2550_info);
+ return i2c_new_client_device(adapter, &tsl2550_info);
}
- return NULL;
+ return ERR_PTR(-ENODEV);
}
static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
@@ -133,7 +125,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
/*
* Voluntarily dropping error code of kstrtou8 since all
* error code that it could return are invalid according
- * to Documentation/i2c/fault-codes.
+ * to Documentation/i2c/fault-codes.rst.
*/
if (kstrtou8(p + 1, 16, &data->byte))
return -EPROTO;
@@ -247,7 +239,7 @@ static int taos_connect(struct serio *serio, struct serio_driver *drv)
dev_err(&serio->dev, "TAOS EVM identification failed\n");
goto exit_close;
}
- strlcpy(adapter->name, name, sizeof(adapter->name));
+ strscpy(adapter->name, name, sizeof(adapter->name));
/* Turn echo off for better performance */
taos->state = TAOS_STATE_EOFF;
diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c
index f6cd35d0a2ac..bb0de6db6391 100644
--- a/drivers/i2c/busses/i2c-tegra-bpmp.c
+++ b/drivers/i2c/busses/i2c-tegra-bpmp.c
@@ -1,21 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/i2c/busses/i2c-tegra-bpmp.c
*
* Copyright (c) 2016 NVIDIA Corporation. All rights reserved.
*
* Author: Shardar Shariff Md <smohammed@nvidia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/err.h>
@@ -23,7 +12,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -49,52 +38,34 @@ struct tegra_bpmp_i2c {
* firmware I2C driver to avoid any issues in future if Linux I2C flags are
* changed.
*/
-static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
+static void tegra_bpmp_xlate_flags(u16 flags, u16 *out)
{
- if (flags & I2C_M_TEN) {
+ if (flags & I2C_M_TEN)
*out |= SERIALI2C_TEN;
- flags &= ~I2C_M_TEN;
- }
- if (flags & I2C_M_RD) {
+ if (flags & I2C_M_RD)
*out |= SERIALI2C_RD;
- flags &= ~I2C_M_RD;
- }
- if (flags & I2C_M_STOP) {
+ if (flags & I2C_M_STOP)
*out |= SERIALI2C_STOP;
- flags &= ~I2C_M_STOP;
- }
- if (flags & I2C_M_NOSTART) {
+ if (flags & I2C_M_NOSTART)
*out |= SERIALI2C_NOSTART;
- flags &= ~I2C_M_NOSTART;
- }
- if (flags & I2C_M_REV_DIR_ADDR) {
+ if (flags & I2C_M_REV_DIR_ADDR)
*out |= SERIALI2C_REV_DIR_ADDR;
- flags &= ~I2C_M_REV_DIR_ADDR;
- }
- if (flags & I2C_M_IGNORE_NAK) {
+ if (flags & I2C_M_IGNORE_NAK)
*out |= SERIALI2C_IGNORE_NAK;
- flags &= ~I2C_M_IGNORE_NAK;
- }
- if (flags & I2C_M_NO_RD_ACK) {
+ if (flags & I2C_M_NO_RD_ACK)
*out |= SERIALI2C_NO_RD_ACK;
- flags &= ~I2C_M_NO_RD_ACK;
- }
- if (flags & I2C_M_RECV_LEN) {
+ if (flags & I2C_M_RECV_LEN)
*out |= SERIALI2C_RECV_LEN;
- flags &= ~I2C_M_RECV_LEN;
- }
-
- return (flags != 0) ? -EINVAL : 0;
}
-/**
+/*
* The serialized I2C format is simply the following:
* [addr little-endian][flags little-endian][len little-endian][data if write]
* [addr little-endian][flags little-endian][len little-endian][data if write]
@@ -108,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
*
* See deserialize_i2c documentation for the data format in the other direction.
*/
-static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
+static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
struct mrq_i2c_request *request,
struct i2c_msg *msgs,
unsigned int num)
{
char *buf = request->xfer.data_buf;
unsigned int i, j, pos = 0;
- int err;
for (i = 0; i < num; i++) {
struct i2c_msg *msg = &msgs[i];
u16 flags = 0;
- err = tegra_bpmp_xlate_flags(msg->flags, &flags);
- if (err < 0)
- return err;
+ tegra_bpmp_xlate_flags(msg->flags, &flags);
buf[pos++] = msg->addr & 0xff;
buf[pos++] = (msg->addr & 0xff00) >> 8;
@@ -139,11 +107,9 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
}
request->xfer.data_size = pos;
-
- return 0;
}
-/**
+/*
* The data in the BPMP -> CPU direction is composed of sequential blocks for
* those messages that have I2C_M_RD. So, for example, if you have:
*
@@ -207,7 +173,8 @@ static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
struct mrq_i2c_request *request,
- struct mrq_i2c_response *response)
+ struct mrq_i2c_response *response,
+ bool atomic)
{
struct tegra_bpmp_message msg;
int err;
@@ -222,16 +189,42 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
msg.rx.data = response;
msg.rx.size = sizeof(*response);
- if (irqs_disabled())
+ if (atomic)
err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
else
err = tegra_bpmp_transfer(i2c->bpmp, &msg);
- return err;
+ if (err < 0) {
+ dev_err(i2c->dev, "failed to transfer message: %d\n", err);
+ return err;
+ }
+
+ if (msg.rx.ret != 0) {
+ if (msg.rx.ret == -BPMP_EAGAIN) {
+ dev_dbg(i2c->dev, "arbitration lost\n");
+ return -EAGAIN;
+ }
+
+ if (msg.rx.ret == -BPMP_ETIMEDOUT) {
+ dev_dbg(i2c->dev, "timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (msg.rx.ret == -BPMP_ENXIO) {
+ dev_dbg(i2c->dev, "NAK\n");
+ return -ENXIO;
+ }
+
+ dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret);
+ return -EIO;
+ }
+
+ return 0;
}
-static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
- struct i2c_msg *msgs, int num)
+static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num,
+ bool atomic)
{
struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
struct mrq_i2c_response response;
@@ -247,13 +240,8 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
memset(&request, 0, sizeof(request));
memset(&response, 0, sizeof(response));
- err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
- if (err < 0) {
- dev_err(i2c->dev, "failed to serialize message: %d\n", err);
- return err;
- }
-
- err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
+ tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
+ err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
if (err < 0) {
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
return err;
@@ -268,6 +256,18 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
return num;
}
+static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, false);
+}
+
+static int tegra_bpmp_i2c_xfer_atomic(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, true);
+}
+
static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
@@ -275,7 +275,8 @@ static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
- .master_xfer = tegra_bpmp_i2c_xfer,
+ .xfer = tegra_bpmp_i2c_xfer,
+ .xfer_atomic = tegra_bpmp_i2c_xfer_atomic,
.functionality = tegra_bpmp_i2c_func,
};
@@ -304,7 +305,7 @@ static int tegra_bpmp_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c->adapter, i2c);
i2c->adapter.owner = THIS_MODULE;
- strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter",
+ strscpy(i2c->adapter.name, "Tegra BPMP I2C adapter",
sizeof(i2c->adapter.name));
i2c->adapter.algo = &tegra_bpmp_i2c_algo;
i2c->adapter.dev.parent = &pdev->dev;
@@ -315,13 +316,11 @@ static int tegra_bpmp_i2c_probe(struct platform_device *pdev)
return i2c_add_adapter(&i2c->adapter);
}
-static int tegra_bpmp_i2c_remove(struct platform_device *pdev)
+static void tegra_bpmp_i2c_remove(struct platform_device *pdev)
{
struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adapter);
-
- return 0;
}
static const struct of_device_id tegra_bpmp_i2c_of_match[] = {
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index c77adbbea0c7..e533460bccc3 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -6,69 +6,67 @@
* Author: Colin Cross <ccross@android.com>
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/io.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/module.h>
-#include <linux/reset.h>
+#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/iopoll.h>
-
-#include <asm/unaligned.h>
+#include <linux/reset.h>
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
#define BYTES_PER_FIFO_WORD 4
#define I2C_CNFG 0x000
-#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
+#define I2C_CNFG_DEBOUNCE_CNT GENMASK(14, 12)
#define I2C_CNFG_PACKET_MODE_EN BIT(10)
#define I2C_CNFG_NEW_MASTER_FSM BIT(11)
#define I2C_CNFG_MULTI_MASTER_MODE BIT(17)
-#define I2C_STATUS 0x01C
+#define I2C_STATUS 0x01c
#define I2C_SL_CNFG 0x020
#define I2C_SL_CNFG_NACK BIT(1)
#define I2C_SL_CNFG_NEWSL BIT(2)
#define I2C_SL_ADDR1 0x02c
#define I2C_SL_ADDR2 0x030
+#define I2C_TLOW_SEXT 0x034
#define I2C_TX_FIFO 0x050
#define I2C_RX_FIFO 0x054
#define I2C_PACKET_TRANSFER_STATUS 0x058
#define I2C_FIFO_CONTROL 0x05c
#define I2C_FIFO_CONTROL_TX_FLUSH BIT(1)
#define I2C_FIFO_CONTROL_RX_FLUSH BIT(0)
-#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5
-#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2
+#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5)
+#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2)
#define I2C_FIFO_STATUS 0x060
-#define I2C_FIFO_STATUS_TX_MASK 0xF0
-#define I2C_FIFO_STATUS_TX_SHIFT 4
-#define I2C_FIFO_STATUS_RX_MASK 0x0F
-#define I2C_FIFO_STATUS_RX_SHIFT 0
+#define I2C_FIFO_STATUS_TX GENMASK(7, 4)
+#define I2C_FIFO_STATUS_RX GENMASK(3, 0)
#define I2C_INT_MASK 0x064
#define I2C_INT_STATUS 0x068
+#define I2C_INT_BUS_CLR_DONE BIT(11)
#define I2C_INT_PACKET_XFER_COMPLETE BIT(7)
-#define I2C_INT_ALL_PACKETS_XFER_COMPLETE BIT(6)
-#define I2C_INT_TX_FIFO_OVERFLOW BIT(5)
-#define I2C_INT_RX_FIFO_UNDERFLOW BIT(4)
#define I2C_INT_NO_ACK BIT(3)
#define I2C_INT_ARBITRATION_LOST BIT(2)
#define I2C_INT_TX_FIFO_DATA_REQ BIT(1)
#define I2C_INT_RX_FIFO_DATA_REQ BIT(0)
#define I2C_CLK_DIVISOR 0x06c
-#define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16
-#define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8
+#define I2C_CLK_DIVISOR_STD_FAST_MODE GENMASK(31, 16)
+#define I2C_CLK_DIVISOR_HSMODE GENMASK(15, 0)
#define DVC_CTRL_REG1 0x000
#define DVC_CTRL_REG1_INTR_EN BIT(10)
-#define DVC_CTRL_REG2 0x004
#define DVC_CTRL_REG3 0x008
#define DVC_CTRL_REG3_SW_PROG BIT(26)
#define DVC_CTRL_REG3_I2C_DONE_INTR_EN BIT(30)
@@ -76,35 +74,55 @@
#define DVC_STATUS_I2C_DONE_INTR BIT(30)
#define I2C_ERR_NONE 0x00
-#define I2C_ERR_NO_ACK 0x01
-#define I2C_ERR_ARBITRATION_LOST 0x02
-#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
+#define I2C_ERR_NO_ACK BIT(0)
+#define I2C_ERR_ARBITRATION_LOST BIT(1)
+#define I2C_ERR_UNKNOWN_INTERRUPT BIT(2)
+#define I2C_ERR_RX_BUFFER_OVERFLOW BIT(3)
-#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
-#define PACKET_HEADER0_PACKET_ID_SHIFT 16
-#define PACKET_HEADER0_CONT_ID_SHIFT 12
-#define PACKET_HEADER0_PROTOCOL_I2C BIT(4)
+#define PACKET_HEADER0_HEADER_SIZE GENMASK(29, 28)
+#define PACKET_HEADER0_PACKET_ID GENMASK(23, 16)
+#define PACKET_HEADER0_CONT_ID GENMASK(15, 12)
+#define PACKET_HEADER0_PROTOCOL GENMASK(7, 4)
+#define PACKET_HEADER0_PROTOCOL_I2C 1
-#define I2C_HEADER_HIGHSPEED_MODE BIT(22)
#define I2C_HEADER_CONT_ON_NAK BIT(21)
-#define I2C_HEADER_SEND_START_BYTE BIT(20)
#define I2C_HEADER_READ BIT(19)
#define I2C_HEADER_10BIT_ADDR BIT(18)
#define I2C_HEADER_IE_ENABLE BIT(17)
#define I2C_HEADER_REPEAT_START BIT(16)
#define I2C_HEADER_CONTINUE_XFER BIT(15)
-#define I2C_HEADER_MASTER_ADDR_SHIFT 12
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
-#define I2C_CONFIG_LOAD 0x08C
+#define I2C_BUS_CLEAR_CNFG 0x084
+#define I2C_BC_SCLK_THRESHOLD GENMASK(23, 16)
+#define I2C_BC_STOP_COND BIT(2)
+#define I2C_BC_TERMINATE BIT(1)
+#define I2C_BC_ENABLE BIT(0)
+#define I2C_BUS_CLEAR_STATUS 0x088
+#define I2C_BC_STATUS BIT(0)
+
+#define I2C_CONFIG_LOAD 0x08c
#define I2C_MSTR_CONFIG_LOAD BIT(0)
-#define I2C_SLV_CONFIG_LOAD BIT(1)
-#define I2C_TIMEOUT_CONFIG_LOAD BIT(2)
#define I2C_CLKEN_OVERRIDE 0x090
#define I2C_MST_CORE_CLKEN_OVR BIT(0)
-#define I2C_CONFIG_LOAD_TIMEOUT 1000000
+#define I2C_INTERFACE_TIMING_0 0x094
+#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8)
+#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0)
+#define I2C_INTERFACE_TIMING_1 0x098
+#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24)
+#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
+#define I2C_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
+#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
+
+#define I2C_HS_INTERFACE_TIMING_0 0x09c
+#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8)
+#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0)
+#define I2C_HS_INTERFACE_TIMING_1 0x0a0
+#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
+#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
+#define I2C_HS_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
#define I2C_MST_FIFO_CONTROL 0x0b4
#define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0)
@@ -113,17 +131,30 @@
#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16)
#define I2C_MST_FIFO_STATUS 0x0b8
-#define I2C_MST_FIFO_STATUS_RX_MASK 0xff
-#define I2C_MST_FIFO_STATUS_RX_SHIFT 0
-#define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000
-#define I2C_MST_FIFO_STATUS_TX_SHIFT 16
+#define I2C_MST_FIFO_STATUS_TX GENMASK(23, 16)
+#define I2C_MST_FIFO_STATUS_RX GENMASK(7, 0)
+
+#define I2C_MASTER_RESET_CNTRL 0x0a8
+
+/* configuration load timeout in microseconds */
+#define I2C_CONFIG_LOAD_TIMEOUT 1000000
+
+/* packet header size in bytes */
+#define I2C_PACKET_HEADER_SIZE 12
/*
- * msg_end_type: The bus control which need to be send at end of transfer.
- * @MSG_END_STOP: Send stop pulse at end of transfer.
- * @MSG_END_REPEAT_START: Send repeat start at end of transfer.
- * @MSG_END_CONTINUE: The following on message is coming and so do not send
- * stop or repeat start.
+ * I2C Controller will use PIO mode for transfers up to 32 bytes in order to
+ * avoid DMA overhead, otherwise external APB DMA controller will be used.
+ * Note that the actual MAX PIO length is 20 bytes because 32 bytes include
+ * I2C_PACKET_HEADER_SIZE.
+ */
+#define I2C_PIO_MODE_PREFERRED_LEN 32
+
+/*
+ * msg_end_type: The bus control which needs to be sent at end of transfer.
+ * @MSG_END_STOP: Send stop pulse.
+ * @MSG_END_REPEAT_START: Send repeat-start.
+ * @MSG_END_CONTINUE: Don't send stop or repeat-start.
*/
enum msg_end_type {
MSG_END_STOP,
@@ -132,17 +163,17 @@ enum msg_end_type {
};
/**
- * struct tegra_i2c_hw_feature : Different HW support on Tegra
- * @has_continue_xfer_support: Continue transfer supports.
+ * struct tegra_i2c_hw_feature : per hardware generation features
+ * @has_continue_xfer_support: continue-transfer supported
* @has_per_pkt_xfer_complete_irq: Has enable/disable capability for transfer
- * complete interrupt per packet basis.
- * @has_single_clk_source: The I2C controller has single clock source. Tegra30
- * and earlier SoCs have two clock sources i.e. div-clk and
- * fast-clk.
+ * completion interrupt on per packet basis.
* @has_config_load_reg: Has the config load register to load the new
* configuration.
* @clk_divisor_hs_mode: Clock divisor in HS mode.
- * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
+ * @clk_divisor_std_mode: Clock divisor in standard mode. It is
+ * applicable if there is no fast clock source i.e. single clock
+ * source.
+ * @clk_divisor_fast_mode: Clock divisor in fast mode. It is
* applicable if there is no fast clock source i.e. single clock
* source.
* @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is
@@ -155,21 +186,50 @@ enum msg_end_type {
* @has_mst_fifo: The I2C controller contains the new MST FIFO interface that
* provides additional features and allows for longer messages to
* be transferred in one go.
- * @quirks: i2c adapter quirks for limiting write/read transfer size and not
+ * @has_mst_reset: The I2C controller contains MASTER_RESET_CTRL register which
+ * provides an alternative to controller reset when configured as
+ * I2C master
+ * @quirks: I2C adapter quirks for limiting write/read transfer size and not
* allowing 0 length transfers.
+ * @supports_bus_clear: Bus Clear support to recover from bus hang during
+ * SDA stuck low from device for some unknown reasons.
+ * @has_apb_dma: Support of APBDMA on corresponding Tegra chip.
+ * @tlow_std_mode: Low period of the clock in standard mode.
+ * @thigh_std_mode: High period of the clock in standard mode.
+ * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes.
+ * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes.
+ * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions
+ * in standard mode.
+ * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop
+ * conditions in fast/fast-plus modes.
+ * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions
+ * in HS mode.
+ * @has_interface_timing_reg: Has interface timing register to program the tuned
+ * timing settings.
*/
struct tegra_i2c_hw_feature {
bool has_continue_xfer_support;
bool has_per_pkt_xfer_complete_irq;
- bool has_single_clk_source;
bool has_config_load_reg;
- int clk_divisor_hs_mode;
- int clk_divisor_std_fast_mode;
- u16 clk_divisor_fast_plus_mode;
+ u32 clk_divisor_hs_mode;
+ u32 clk_divisor_std_mode;
+ u32 clk_divisor_fast_mode;
+ u32 clk_divisor_fast_plus_mode;
bool has_multi_master_mode;
bool has_slcg_override_reg;
bool has_mst_fifo;
+ bool has_mst_reset;
const struct i2c_adapter_quirks *quirks;
+ bool supports_bus_clear;
+ bool has_apb_dma;
+ u32 tlow_std_mode;
+ u32 thigh_std_mode;
+ u32 tlow_fast_fastplus_mode;
+ u32 thigh_fast_fastplus_mode;
+ u32 setup_hold_time_std_mode;
+ u32 setup_hold_time_fast_fast_plus_mode;
+ u32 setup_hold_time_hs_mode;
+ bool has_interface_timing_reg;
};
/**
@@ -178,92 +238,138 @@ struct tegra_i2c_hw_feature {
* @hw: Tegra I2C HW feature
* @adapter: core I2C layer adapter information
* @div_clk: clock reference for div clock of I2C controller
- * @fast_clk: clock reference for fast clock of I2C controller
+ * @clocks: array of I2C controller clocks
+ * @nclocks: number of clocks in the array
* @rst: reset control for the I2C controller
* @base: ioremapped registers cookie
+ * @base_phys: physical base address of the I2C controller
* @cont_id: I2C controller ID, used for packet header
* @irq: IRQ number of transfer complete interrupt
- * @irq_disabled: used to track whether or not the interrupt is enabled
* @is_dvc: identifies the DVC I2C controller, has a different register layout
+ * @is_vi: identifies the VI I2C controller, has a different register layout
* @msg_complete: transfer completion notifier
+ * @msg_buf_remaining: size of unsent data in the message buffer
+ * @msg_len: length of message in current transfer
* @msg_err: error code for completed message
* @msg_buf: pointer to current message data
- * @msg_buf_remaining: size of unsent data in the message buffer
- * @msg_read: identifies read transfers
- * @bus_clk_rate: current I2C bus clock rate
- * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
- * @is_multimaster_mode: track if I2C controller is in multi-master mode
- * @xfer_lock: lock to serialize transfer submission and processing
+ * @msg_read: indicates that the transfer is a read access
+ * @timings: i2c timings information like bus frequency
+ * @multimaster_mode: indicates that I2C controller is in multi-master mode
+ * @dma_chan: DMA channel
+ * @dma_phys: handle to DMA resources
+ * @dma_buf: pointer to allocated DMA buffer
+ * @dma_buf_size: DMA buffer size
+ * @dma_dev: DMA device used for transfers
+ * @dma_mode: indicates active DMA transfer
+ * @dma_complete: DMA completion notifier
+ * @atomic_mode: indicates active atomic transfer
*/
struct tegra_i2c_dev {
struct device *dev;
- const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
- struct clk *div_clk;
- struct clk *fast_clk;
+
+ const struct tegra_i2c_hw_feature *hw;
struct reset_control *rst;
+ unsigned int cont_id;
+ unsigned int irq;
+
+ phys_addr_t base_phys;
void __iomem *base;
- int cont_id;
- int irq;
- bool irq_disabled;
- int is_dvc;
+
+ struct clk_bulk_data clocks[2];
+ unsigned int nclocks;
+
+ struct clk *div_clk;
+ struct i2c_timings timings;
+
struct completion msg_complete;
+ size_t msg_buf_remaining;
+ unsigned int msg_len;
int msg_err;
u8 *msg_buf;
- size_t msg_buf_remaining;
- int msg_read;
- u32 bus_clk_rate;
- u16 clk_divisor_non_hs_mode;
- bool is_multimaster_mode;
- spinlock_t xfer_lock;
+
+ struct completion dma_complete;
+ struct dma_chan *dma_chan;
+ unsigned int dma_buf_size;
+ struct device *dma_dev;
+ dma_addr_t dma_phys;
+ void *dma_buf;
+
+ bool multimaster_mode;
+ bool atomic_mode;
+ bool dma_mode;
+ bool msg_read;
+ bool is_dvc;
+ bool is_vi;
};
+#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && (dev)->is_dvc)
+#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && (dev)->is_vi)
+
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
- unsigned long reg)
+ unsigned int reg)
{
- writel(val, i2c_dev->base + reg);
+ writel_relaxed(val, i2c_dev->base + reg);
}
-static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
+static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
{
- return readl(i2c_dev->base + reg);
+ return readl_relaxed(i2c_dev->base + reg);
}
/*
- * i2c_writel and i2c_readl will offset the register if necessary to talk
- * to the I2C block inside the DVC block
+ * If necessary, i2c_writel() and i2c_readl() will offset the register
+ * in order to talk to the I2C block inside the DVC block.
*/
-static unsigned long tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev,
- unsigned long reg)
+static u32 tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
{
- if (i2c_dev->is_dvc)
+ if (IS_DVC(i2c_dev))
reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40;
+ else if (IS_VI(i2c_dev))
+ reg = 0xc00 + (reg << 2);
+
return reg;
}
-static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
- unsigned long reg)
+static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg)
{
- writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+ writel_relaxed(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
- /* Read back register to make sure that register writes completed */
+ /* read back register to make sure that register writes completed */
if (reg != I2C_TX_FIFO)
- readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+ readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+ else if (IS_VI(i2c_dev))
+ readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, I2C_INT_STATUS));
}
-static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
+static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
{
- return readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+ return readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
}
static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data,
- unsigned long reg, int len)
+ unsigned int reg, unsigned int len)
{
writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
}
+static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data,
+ unsigned int reg, unsigned int len)
+{
+ u32 *data32 = data;
+
+ /*
+ * VI I2C controller has known hardware bug where writes get stuck
+ * when immediate multiple writes happen to TX_FIFO register.
+ * Recommended software work around is to read I2C register after
+ * each write to TX_FIFO register to flush out the data.
+ */
+ while (len--)
+ i2c_writel(i2c_dev, *data32++, reg);
+}
+
static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
- unsigned long reg, int len)
+ unsigned int reg, unsigned int len)
{
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
}
@@ -284,149 +390,113 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
}
-static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
+static void tegra_i2c_dma_complete(void *args)
{
- unsigned long timeout = jiffies + HZ;
- unsigned int offset;
- u32 mask, val;
+ struct tegra_i2c_dev *i2c_dev = args;
- if (i2c_dev->hw->has_mst_fifo) {
- mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
- I2C_MST_FIFO_CONTROL_RX_FLUSH;
- offset = I2C_MST_FIFO_CONTROL;
- } else {
- mask = I2C_FIFO_CONTROL_TX_FLUSH |
- I2C_FIFO_CONTROL_RX_FLUSH;
- offset = I2C_FIFO_CONTROL;
- }
-
- val = i2c_readl(i2c_dev, offset);
- val |= mask;
- i2c_writel(i2c_dev, val, offset);
-
- while (i2c_readl(i2c_dev, offset) & mask) {
- if (time_after(jiffies, timeout)) {
- dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
- return -ETIMEDOUT;
- }
- msleep(1);
- }
- return 0;
+ complete(&i2c_dev->dma_complete);
}
-static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
{
- u32 val;
- int rx_fifo_avail;
- u8 *buf = i2c_dev->msg_buf;
- size_t buf_remaining = i2c_dev->msg_buf_remaining;
- int words_to_transfer;
+ struct dma_async_tx_descriptor *dma_desc;
+ enum dma_transfer_direction dir;
- if (i2c_dev->hw->has_mst_fifo) {
- val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
- rx_fifo_avail = (val & I2C_MST_FIFO_STATUS_RX_MASK) >>
- I2C_MST_FIFO_STATUS_RX_SHIFT;
- } else {
- val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
- rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
- I2C_FIFO_STATUS_RX_SHIFT;
- }
-
- /* Rounds down to not include partial word at the end of buf */
- words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
- if (words_to_transfer > rx_fifo_avail)
- words_to_transfer = rx_fifo_avail;
+ dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len);
- i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer);
+ reinit_completion(&i2c_dev->dma_complete);
- buf += words_to_transfer * BYTES_PER_FIFO_WORD;
- buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
- rx_fifo_avail -= words_to_transfer;
+ dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
- /*
- * If there is a partial word at the end of buf, handle it manually to
- * prevent overwriting past the end of buf
- */
- if (rx_fifo_avail > 0 && buf_remaining > 0) {
- BUG_ON(buf_remaining > 3);
- val = i2c_readl(i2c_dev, I2C_RX_FIFO);
- val = cpu_to_le32(val);
- memcpy(buf, &val, buf_remaining);
- buf_remaining = 0;
- rx_fifo_avail--;
+ dma_desc = dmaengine_prep_slave_single(i2c_dev->dma_chan, i2c_dev->dma_phys,
+ len, dir, DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
+ if (!dma_desc) {
+ dev_err(i2c_dev->dev, "failed to get %s DMA descriptor\n",
+ i2c_dev->msg_read ? "RX" : "TX");
+ return -EINVAL;
}
- BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
- i2c_dev->msg_buf_remaining = buf_remaining;
- i2c_dev->msg_buf = buf;
+ dma_desc->callback = tegra_i2c_dma_complete;
+ dma_desc->callback_param = i2c_dev;
+
+ dmaengine_submit(dma_desc);
+ dma_async_issue_pending(i2c_dev->dma_chan);
return 0;
}
-static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
+static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev)
{
- u32 val;
- int tx_fifo_avail;
- u8 *buf = i2c_dev->msg_buf;
- size_t buf_remaining = i2c_dev->msg_buf_remaining;
- int words_to_transfer;
-
- if (i2c_dev->hw->has_mst_fifo) {
- val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
- tx_fifo_avail = (val & I2C_MST_FIFO_STATUS_TX_MASK) >>
- I2C_MST_FIFO_STATUS_TX_SHIFT;
- } else {
- val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
- tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
- I2C_FIFO_STATUS_TX_SHIFT;
+ if (i2c_dev->dma_buf) {
+ dma_free_coherent(i2c_dev->dma_dev, i2c_dev->dma_buf_size,
+ i2c_dev->dma_buf, i2c_dev->dma_phys);
+ i2c_dev->dma_buf = NULL;
}
- /* Rounds down to not include partial word at the end of buf */
- words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
-
- /* It's very common to have < 4 bytes, so optimize that case. */
- if (words_to_transfer) {
- if (words_to_transfer > tx_fifo_avail)
- words_to_transfer = tx_fifo_avail;
+ if (i2c_dev->dma_chan) {
+ dma_release_channel(i2c_dev->dma_chan);
+ i2c_dev->dma_chan = NULL;
+ }
+}
- /*
- * Update state before writing to FIFO. If this casues us
- * to finish writing all bytes (AKA buf_remaining goes to 0) we
- * have a potential for an interrupt (PACKET_XFER_COMPLETE is
- * not maskable). We need to make sure that the isr sees
- * buf_remaining as 0 and doesn't call us back re-entrantly.
- */
- buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
- tx_fifo_avail -= words_to_transfer;
- i2c_dev->msg_buf_remaining = buf_remaining;
- i2c_dev->msg_buf = buf +
- words_to_transfer * BYTES_PER_FIFO_WORD;
- barrier();
+static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
+{
+ dma_addr_t dma_phys;
+ u32 *dma_buf;
+ int err;
- i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+ if (IS_VI(i2c_dev))
+ return 0;
- buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ if (i2c_dev->hw->has_apb_dma) {
+ if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA)) {
+ dev_dbg(i2c_dev->dev, "APB DMA support not enabled\n");
+ return 0;
+ }
+ } else if (!IS_ENABLED(CONFIG_TEGRA186_GPC_DMA)) {
+ dev_dbg(i2c_dev->dev, "GPC DMA support not enabled\n");
+ return 0;
}
/*
- * If there is a partial word at the end of buf, handle it manually to
- * prevent reading past the end of buf, which could cross a page
- * boundary and fault.
+ * The same channel will be used for both RX and TX.
+ * Keeping the name as "tx" for backward compatibility
+ * with existing devicetrees.
*/
- if (tx_fifo_avail > 0 && buf_remaining > 0) {
- BUG_ON(buf_remaining > 3);
- memcpy(&val, buf, buf_remaining);
- val = le32_to_cpu(val);
+ i2c_dev->dma_chan = dma_request_chan(i2c_dev->dev, "tx");
+ if (IS_ERR(i2c_dev->dma_chan)) {
+ err = PTR_ERR(i2c_dev->dma_chan);
+ i2c_dev->dma_chan = NULL;
+ goto err_out;
+ }
- /* Again update before writing to FIFO to make sure isr sees. */
- i2c_dev->msg_buf_remaining = 0;
- i2c_dev->msg_buf = NULL;
- barrier();
+ i2c_dev->dma_dev = i2c_dev->dma_chan->device->dev;
+ i2c_dev->dma_buf_size = i2c_dev->hw->quirks->max_write_len +
+ I2C_PACKET_HEADER_SIZE;
- i2c_writel(i2c_dev, val, I2C_TX_FIFO);
+ dma_buf = dma_alloc_coherent(i2c_dev->dma_dev, i2c_dev->dma_buf_size,
+ &dma_phys, GFP_KERNEL | __GFP_NOWARN);
+ if (!dma_buf) {
+ dev_err(i2c_dev->dev, "failed to allocate DMA buffer\n");
+ err = -ENOMEM;
+ goto err_out;
}
+ i2c_dev->dma_buf = dma_buf;
+ i2c_dev->dma_phys = dma_phys;
+
return 0;
+
+err_out:
+ tegra_i2c_release_dma(i2c_dev);
+ if (err != -EPROBE_DEFER) {
+ dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err);
+ dev_err(i2c_dev->dev, "falling back to PIO\n");
+ return 0;
+ }
+
+ return err;
}
/*
@@ -434,7 +504,7 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
* block. This block is identical to the rest of the I2C blocks, except that
* it only supports master mode, it has registers moved around, and it needs
* some extra init to get it into I2C mode. The register moves are handled
- * by i2c_readl and i2c_writel
+ * by i2c_readl() and i2c_writel().
*/
static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
{
@@ -450,95 +520,148 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
}
-static int tegra_i2c_runtime_resume(struct device *dev)
+static void tegra_i2c_vi_init(struct tegra_i2c_dev *i2c_dev)
{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- int ret;
+ u32 value;
- ret = pinctrl_pm_select_default_state(i2c_dev->dev);
- if (ret)
- return ret;
+ value = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, 2) |
+ FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, 4);
+ i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_0);
- if (!i2c_dev->hw->has_single_clk_source) {
- ret = clk_enable(i2c_dev->fast_clk);
- if (ret < 0) {
- dev_err(i2c_dev->dev,
- "Enabling fast clk failed, err %d\n", ret);
- return ret;
- }
- }
+ value = FIELD_PREP(I2C_INTERFACE_TIMING_TBUF, 4) |
+ FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STO, 7) |
+ FIELD_PREP(I2C_INTERFACE_TIMING_THD_STA, 4) |
+ FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STA, 4);
+ i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_1);
- ret = clk_enable(i2c_dev->div_clk);
- if (ret < 0) {
- dev_err(i2c_dev->dev,
- "Enabling div clk failed, err %d\n", ret);
- clk_disable(i2c_dev->fast_clk);
- return ret;
- }
+ value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, 3) |
+ FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, 8);
+ i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_0);
- return 0;
+ value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STO, 11) |
+ FIELD_PREP(I2C_HS_INTERFACE_TIMING_THD_STA, 11) |
+ FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STA, 11);
+ i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_1);
+
+ value = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND;
+ i2c_writel(i2c_dev, value, I2C_BUS_CLEAR_CNFG);
+
+ i2c_writel(i2c_dev, 0x0, I2C_TLOW_SEXT);
}
-static int tegra_i2c_runtime_suspend(struct device *dev)
+static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
+ u32 reg, u32 mask, u32 delay_us,
+ u32 timeout_us)
{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
+ u32 val;
- clk_disable(i2c_dev->div_clk);
- if (!i2c_dev->hw->has_single_clk_source)
- clk_disable(i2c_dev->fast_clk);
+ if (!i2c_dev->atomic_mode)
+ return readl_relaxed_poll_timeout(addr, val, !(val & mask),
+ delay_us, timeout_us);
- return pinctrl_pm_select_idle_state(i2c_dev->dev);
+ return readl_relaxed_poll_timeout_atomic(addr, val, !(val & mask),
+ delay_us, timeout_us);
}
-static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
{
- unsigned long reg_offset;
- void __iomem *addr;
- u32 val;
+ u32 mask, val, offset;
int err;
- if (i2c_dev->hw->has_config_load_reg) {
- reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_CONFIG_LOAD);
- addr = i2c_dev->base + reg_offset;
- i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
- if (in_interrupt())
- err = readl_poll_timeout_atomic(addr, val, val == 0,
- 1000, I2C_CONFIG_LOAD_TIMEOUT);
- else
- err = readl_poll_timeout(addr, val, val == 0,
- 1000, I2C_CONFIG_LOAD_TIMEOUT);
+ if (i2c_dev->hw->has_mst_fifo) {
+ mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
+ I2C_MST_FIFO_CONTROL_RX_FLUSH;
+ offset = I2C_MST_FIFO_CONTROL;
+ } else {
+ mask = I2C_FIFO_CONTROL_TX_FLUSH |
+ I2C_FIFO_CONTROL_RX_FLUSH;
+ offset = I2C_FIFO_CONTROL;
+ }
- if (err) {
- dev_warn(i2c_dev->dev,
- "timeout waiting for config load\n");
- return err;
- }
+ val = i2c_readl(i2c_dev, offset);
+ val |= mask;
+ i2c_writel(i2c_dev, val, offset);
+
+ err = tegra_i2c_poll_register(i2c_dev, offset, mask, 1000, 1000000);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to flush FIFO\n");
+ return err;
}
return 0;
}
-static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
{
- u32 val;
int err;
- u32 clk_divisor;
- err = pm_runtime_get_sync(i2c_dev->dev);
- if (err < 0) {
- dev_err(i2c_dev->dev, "runtime resume failed %d\n", err);
+ if (!i2c_dev->hw->has_config_load_reg)
+ return 0;
+
+ i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
+
+ err = tegra_i2c_poll_register(i2c_dev, I2C_CONFIG_LOAD, 0xffffffff,
+ 1000, I2C_CONFIG_LOAD_TIMEOUT);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to load config\n");
return err;
}
- reset_control_assert(i2c_dev->rst);
- udelay(2);
- reset_control_deassert(i2c_dev->rst);
+ return 0;
+}
+
+static int tegra_i2c_master_reset(struct tegra_i2c_dev *i2c_dev)
+{
+ if (!i2c_dev->hw->has_mst_reset)
+ return -EOPNOTSUPP;
+
+ /*
+ * Writing 1 to I2C_MASTER_RESET_CNTRL will reset all internal state of
+ * Master logic including FIFOs. Clear this bit to 0 for normal operation.
+ * SW needs to wait for 2us after assertion and de-assertion of this soft
+ * reset.
+ */
+ i2c_writel(i2c_dev, 0x1, I2C_MASTER_RESET_CNTRL);
+ fsleep(2);
- if (i2c_dev->is_dvc)
+ i2c_writel(i2c_dev, 0x0, I2C_MASTER_RESET_CNTRL);
+ fsleep(2);
+
+ return 0;
+}
+
+static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val, clk_divisor, clk_multiplier, tsu_thd, tlow, thigh, non_hs_mode;
+ struct i2c_timings *t = &i2c_dev->timings;
+ int err;
+
+ /*
+ * Reset the controller before initializing it.
+ * In case if device_reset() returns -ENOENT, i.e. when the reset is
+ * not available, the internal software reset will be used if it is
+ * supported by the controller.
+ */
+ err = device_reset(i2c_dev->dev);
+ if (err == -ENOENT)
+ err = tegra_i2c_master_reset(i2c_dev);
+
+ /*
+ * The reset shouldn't ever fail in practice. The failure will be a
+ * sign of a severe problem that needs to be resolved. Still we don't
+ * want to fail the initialization completely because this may break
+ * kernel boot up since voltage regulators use I2C. Hence, we will
+ * emit a noisy warning on error, which won't stay unnoticed and
+ * won't hose machine entirely.
+ */
+ WARN_ON_ONCE(err);
+
+ if (IS_DVC(i2c_dev))
tegra_dvc_init(i2c_dev);
val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
- (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
+ FIELD_PREP(I2C_CNFG_DEBOUNCE_CNT, 2);
if (i2c_dev->hw->has_multi_master_mode)
val |= I2C_CNFG_MULTI_MASTER_MODE;
@@ -546,13 +669,59 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
- /* Make sure clock divisor programmed correctly */
- clk_divisor = i2c_dev->hw->clk_divisor_hs_mode;
- clk_divisor |= i2c_dev->clk_divisor_non_hs_mode <<
- I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
+ if (IS_VI(i2c_dev))
+ tegra_i2c_vi_init(i2c_dev);
+
+ switch (t->bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_PLUS_FREQ:
+ default:
+ tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
+ thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
+
+ if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
+ non_hs_mode = i2c_dev->hw->clk_divisor_fast_plus_mode;
+ else
+ non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode;
+ break;
+
+ case 0 ... I2C_MAX_STANDARD_MODE_FREQ:
+ tlow = i2c_dev->hw->tlow_std_mode;
+ thigh = i2c_dev->hw->thigh_std_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_std_mode;
+ non_hs_mode = i2c_dev->hw->clk_divisor_std_mode;
+ break;
+ }
+
+ /* make sure clock divisor programmed correctly */
+ clk_divisor = FIELD_PREP(I2C_CLK_DIVISOR_HSMODE,
+ i2c_dev->hw->clk_divisor_hs_mode) |
+ FIELD_PREP(I2C_CLK_DIVISOR_STD_FAST_MODE, non_hs_mode);
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
- if (!i2c_dev->is_dvc) {
+ if (i2c_dev->hw->has_interface_timing_reg) {
+ val = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, thigh) |
+ FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, tlow);
+ i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0);
+ }
+
+ /*
+ * Configure setup and hold times only when tsu_thd is non-zero.
+ * Otherwise, preserve the chip default values.
+ */
+ if (i2c_dev->hw->has_interface_timing_reg && tsu_thd)
+ i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1);
+
+ clk_multiplier = (tlow + thigh + 2) * (non_hs_mode + 1);
+
+ err = clk_set_rate(i2c_dev->div_clk,
+ t->bus_freq_hz * clk_multiplier);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to set div-clk rate: %d\n", err);
+ return err;
+ }
+
+ if (!IS_DVC(i2c_dev) && !IS_VI(i2c_dev)) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL;
@@ -561,35 +730,18 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
}
- if (i2c_dev->hw->has_mst_fifo) {
- val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
- I2C_MST_FIFO_CONTROL_RX_TRIG(1);
- i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
- } else {
- val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
- 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
- i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
- }
-
err = tegra_i2c_flush_fifos(i2c_dev);
if (err)
- goto err;
+ return err;
- if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
+ if (i2c_dev->multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
err = tegra_i2c_wait_for_config_load(i2c_dev);
if (err)
- goto err;
-
- if (i2c_dev->irq_disabled) {
- i2c_dev->irq_disabled = false;
- enable_irq(i2c_dev->irq);
- }
+ return err;
-err:
- pm_runtime_put(i2c_dev->dev);
- return err;
+ return 0;
}
static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev)
@@ -598,11 +750,11 @@ static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev)
/*
* NACK interrupt is generated before the I2C controller generates
- * the STOP condition on the bus. So wait for 2 clock periods
+ * the STOP condition on the bus. So, wait for 2 clock periods
* before disabling the controller so that the STOP condition has
* been delivered properly.
*/
- udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate));
+ udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->timings.bus_freq_hz));
cnfg = i2c_readl(i2c_dev, I2C_CNFG);
if (cnfg & I2C_CNFG_PACKET_MODE_EN)
@@ -611,30 +763,156 @@ static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev)
return tegra_i2c_wait_for_config_load(i2c_dev);
}
+static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
+{
+ size_t buf_remaining = i2c_dev->msg_buf_remaining;
+ unsigned int words_to_transfer, rx_fifo_avail;
+ u8 *buf = i2c_dev->msg_buf;
+ u32 val;
+
+ /*
+ * Catch overflow due to message fully sent before the check for
+ * RX FIFO availability.
+ */
+ if (WARN_ON_ONCE(!(i2c_dev->msg_buf_remaining)))
+ return -EINVAL;
+
+ if (i2c_dev->hw->has_mst_fifo) {
+ val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+ rx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_RX, val);
+ } else {
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ rx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_RX, val);
+ }
+
+ /* round down to exclude partial word at the end of buffer */
+ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
+ if (words_to_transfer > rx_fifo_avail)
+ words_to_transfer = rx_fifo_avail;
+
+ i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer);
+
+ buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
+ rx_fifo_avail -= words_to_transfer;
+
+ /*
+ * If there is a partial word at the end of buffer, handle it
+ * manually to prevent overwriting past the end of buffer.
+ */
+ if (rx_fifo_avail > 0 && buf_remaining > 0) {
+ /*
+ * buf_remaining > 3 check not needed as rx_fifo_avail == 0
+ * when (words_to_transfer was > rx_fifo_avail) earlier
+ * in this function.
+ */
+ val = i2c_readl(i2c_dev, I2C_RX_FIFO);
+ val = cpu_to_le32(val);
+ memcpy(buf, &val, buf_remaining);
+ buf_remaining = 0;
+ rx_fifo_avail--;
+ }
+
+ /* RX FIFO must be drained, otherwise it's an Overflow case. */
+ if (WARN_ON_ONCE(rx_fifo_avail))
+ return -EINVAL;
+
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf;
+
+ return 0;
+}
+
+static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
+{
+ size_t buf_remaining = i2c_dev->msg_buf_remaining;
+ unsigned int words_to_transfer, tx_fifo_avail;
+ u8 *buf = i2c_dev->msg_buf;
+ u32 val;
+
+ if (i2c_dev->hw->has_mst_fifo) {
+ val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
+ tx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_TX, val);
+ } else {
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ tx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_TX, val);
+ }
+
+ /* round down to exclude partial word at the end of buffer */
+ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
+
+ /*
+ * This hunk pushes 4 bytes at a time into the TX FIFO.
+ *
+ * It's very common to have < 4 bytes, hence there is no word
+ * to push if we have less than 4 bytes to transfer.
+ */
+ if (words_to_transfer) {
+ if (words_to_transfer > tx_fifo_avail)
+ words_to_transfer = tx_fifo_avail;
+
+ /*
+ * Update state before writing to FIFO. Note that this may
+ * cause us to finish writing all bytes (AKA buf_remaining
+ * goes to 0), hence we have a potential for an interrupt
+ * (PACKET_XFER_COMPLETE is not maskable), but GIC interrupt
+ * is disabled at this point.
+ */
+ buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
+ tx_fifo_avail -= words_to_transfer;
+
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf + words_to_transfer * BYTES_PER_FIFO_WORD;
+
+ if (IS_VI(i2c_dev))
+ i2c_writesl_vi(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+ else
+ i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+
+ buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ }
+
+ /*
+ * If there is a partial word at the end of buffer, handle it manually
+ * to prevent reading past the end of buffer, which could cross a page
+ * boundary and fault.
+ */
+ if (tx_fifo_avail > 0 && buf_remaining > 0) {
+ /*
+ * buf_remaining > 3 check not needed as tx_fifo_avail == 0
+ * when (words_to_transfer was > tx_fifo_avail) earlier
+ * in this function for non-zero words_to_transfer.
+ */
+ memcpy(&val, buf, buf_remaining);
+ val = le32_to_cpu(val);
+
+ i2c_dev->msg_buf_remaining = 0;
+ i2c_dev->msg_buf = NULL;
+
+ i2c_writel(i2c_dev, val, I2C_TX_FIFO);
+ }
+
+ return 0;
+}
+
static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
{
- u32 status;
const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
struct tegra_i2c_dev *i2c_dev = dev_id;
+ u32 status;
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
- spin_lock(&i2c_dev->xfer_lock);
if (status == 0) {
- dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
+ dev_warn(i2c_dev->dev, "IRQ status 0 %08x %08x %08x\n",
i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
i2c_readl(i2c_dev, I2C_STATUS),
i2c_readl(i2c_dev, I2C_CNFG));
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
-
- if (!i2c_dev->irq_disabled) {
- disable_irq_nosync(i2c_dev->irq);
- i2c_dev->irq_disabled = true;
- }
goto err;
}
- if (unlikely(status & status_err)) {
+ if (status & status_err) {
tegra_i2c_disable_packet_mode(i2c_dev);
if (status & I2C_INT_NO_ACK)
i2c_dev->msg_err |= I2C_ERR_NO_ACK;
@@ -643,113 +921,448 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
goto err;
}
- if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
- if (i2c_dev->msg_buf_remaining)
- tegra_i2c_empty_rx_fifo(i2c_dev);
- else
- BUG();
- }
+ /*
+ * I2C transfer is terminated during the bus clear, so skip
+ * processing the other interrupts.
+ */
+ if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
+ goto err;
- if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
- if (i2c_dev->msg_buf_remaining)
- tegra_i2c_fill_tx_fifo(i2c_dev);
- else
- tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
+ if (!i2c_dev->dma_mode) {
+ if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
+ if (tegra_i2c_empty_rx_fifo(i2c_dev)) {
+ /*
+ * Overflow error condition: message fully sent,
+ * with no XFER_COMPLETE interrupt but hardware
+ * asks to transfer more.
+ */
+ i2c_dev->msg_err |= I2C_ERR_RX_BUFFER_OVERFLOW;
+ goto err;
+ }
+ }
+
+ if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
+ if (i2c_dev->msg_buf_remaining)
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+ else
+ tegra_i2c_mask_irq(i2c_dev,
+ I2C_INT_TX_FIFO_DATA_REQ);
+ }
}
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
- if (i2c_dev->is_dvc)
+ if (IS_DVC(i2c_dev))
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ /*
+ * During message read XFER_COMPLETE interrupt is triggered prior to
+ * DMA completion and during message write XFER_COMPLETE interrupt is
+ * triggered after DMA completion.
+ *
+ * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer,
+ * so forcing msg_buf_remaining to 0 in DMA mode.
+ */
if (status & I2C_INT_PACKET_XFER_COMPLETE) {
- BUG_ON(i2c_dev->msg_buf_remaining);
+ if (i2c_dev->dma_mode)
+ i2c_dev->msg_buf_remaining = 0;
+ /*
+ * Underflow error condition: XFER_COMPLETE before message
+ * fully sent.
+ */
+ if (WARN_ON_ONCE(i2c_dev->msg_buf_remaining)) {
+ i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
+ goto err;
+ }
complete(&i2c_dev->msg_complete);
}
goto done;
err:
- /* An error occurred, mask all interrupts */
- tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
- I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
- I2C_INT_RX_FIFO_DATA_REQ);
+ /* mask all interrupts on error */
+ tegra_i2c_mask_irq(i2c_dev,
+ I2C_INT_NO_ACK |
+ I2C_INT_ARBITRATION_LOST |
+ I2C_INT_PACKET_XFER_COMPLETE |
+ I2C_INT_TX_FIFO_DATA_REQ |
+ I2C_INT_RX_FIFO_DATA_REQ);
+
+ if (i2c_dev->hw->supports_bus_clear)
+ tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
+
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
- if (i2c_dev->is_dvc)
+
+ if (IS_DVC(i2c_dev))
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ if (i2c_dev->dma_mode) {
+ dmaengine_terminate_async(i2c_dev->dma_chan);
+ complete(&i2c_dev->dma_complete);
+ }
+
complete(&i2c_dev->msg_complete);
done:
- spin_unlock(&i2c_dev->xfer_lock);
return IRQ_HANDLED;
}
-static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
- struct i2c_msg *msg, enum msg_end_type end_state)
+static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
+ size_t len)
{
- u32 packet_header;
- u32 int_mask;
- unsigned long time_left;
- unsigned long flags;
+ struct dma_slave_config slv_config = {0};
+ u32 val, reg, dma_burst, reg_offset;
+ int err;
- tegra_i2c_flush_fifos(i2c_dev);
+ if (i2c_dev->hw->has_mst_fifo)
+ reg = I2C_MST_FIFO_CONTROL;
+ else
+ reg = I2C_FIFO_CONTROL;
+
+ if (i2c_dev->dma_mode) {
+ if (len & 0xF)
+ dma_burst = 1;
+ else if (len & 0x10)
+ dma_burst = 4;
+ else
+ dma_burst = 8;
+
+ if (i2c_dev->msg_read) {
+ reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO);
+
+ slv_config.src_addr = i2c_dev->base_phys + reg_offset;
+ slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slv_config.src_maxburst = dma_burst;
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
+ else
+ val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
+ } else {
+ reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO);
+
+ slv_config.dst_addr = i2c_dev->base_phys + reg_offset;
+ slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slv_config.dst_maxburst = dma_burst;
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
+ else
+ val = I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
+ }
+
+ slv_config.device_fc = true;
+ err = dmaengine_slave_config(i2c_dev->dma_chan, &slv_config);
+ if (err) {
+ dev_err(i2c_dev->dev, "DMA config failed: %d\n", err);
+ dev_err(i2c_dev->dev, "falling back to PIO\n");
+
+ tegra_i2c_release_dma(i2c_dev);
+ i2c_dev->dma_mode = false;
+ } else {
+ goto out;
+ }
+ }
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
+ I2C_MST_FIFO_CONTROL_RX_TRIG(1);
+ else
+ val = I2C_FIFO_CONTROL_TX_TRIG(8) |
+ I2C_FIFO_CONTROL_RX_TRIG(1);
+out:
+ i2c_writel(i2c_dev, val, reg);
+}
+
+static unsigned long tegra_i2c_poll_completion(struct tegra_i2c_dev *i2c_dev,
+ struct completion *complete,
+ unsigned int timeout_ms)
+{
+ ktime_t ktime = ktime_get();
+ ktime_t ktimeout = ktime_add_ms(ktime, timeout_ms);
+
+ do {
+ u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS);
+
+ if (status)
+ tegra_i2c_isr(i2c_dev->irq, i2c_dev);
+
+ if (completion_done(complete)) {
+ s64 delta = ktime_ms_delta(ktimeout, ktime);
+
+ return msecs_to_jiffies(delta) ?: 1;
+ }
+
+ ktime = ktime_get();
+
+ } while (ktime_before(ktime, ktimeout));
+
+ return 0;
+}
+
+static unsigned long tegra_i2c_wait_completion(struct tegra_i2c_dev *i2c_dev,
+ struct completion *complete,
+ unsigned int timeout_ms)
+{
+ unsigned long ret;
+
+ if (i2c_dev->atomic_mode) {
+ ret = tegra_i2c_poll_completion(i2c_dev, complete, timeout_ms);
+ } else {
+ enable_irq(i2c_dev->irq);
+ ret = wait_for_completion_timeout(complete,
+ msecs_to_jiffies(timeout_ms));
+ disable_irq(i2c_dev->irq);
+
+ /*
+ * Under some rare circumstances (like running KASAN +
+ * NFS root) CPU, which handles interrupt, may stuck in
+ * uninterruptible state for a significant time. In this
+ * case we will get timeout if I2C transfer is running on
+ * a sibling CPU, despite of IRQ being raised.
+ *
+ * In order to handle this rare condition, the IRQ status
+ * needs to be checked after timeout.
+ */
+ if (ret == 0)
+ ret = tegra_i2c_poll_completion(i2c_dev, complete, 0);
+ }
+
+ return ret;
+}
+
+static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap)
+{
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ u32 val, time_left;
+ int err;
- i2c_dev->msg_buf = msg->buf;
- i2c_dev->msg_buf_remaining = msg->len;
- i2c_dev->msg_err = I2C_ERR_NONE;
- i2c_dev->msg_read = (msg->flags & I2C_M_RD);
reinit_completion(&i2c_dev->msg_complete);
- spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
+ val = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND |
+ I2C_BC_TERMINATE;
+ i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG);
- int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
- tegra_i2c_unmask_irq(i2c_dev, int_mask);
+ err = tegra_i2c_wait_for_config_load(i2c_dev);
+ if (err)
+ return err;
+
+ val |= I2C_BC_ENABLE;
+ i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG);
+ tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
- packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
- PACKET_HEADER0_PROTOCOL_I2C |
- (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
- (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete, 50);
+ tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
- packet_header = msg->len - 1;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ if (time_left == 0) {
+ dev_err(i2c_dev->dev, "failed to clear bus\n");
+ return -ETIMEDOUT;
+ }
+
+ val = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS);
+ if (!(val & I2C_BC_STATUS)) {
+ dev_err(i2c_dev->dev, "un-recovered arbitration lost\n");
+ return -EIO;
+ }
+
+ return -EAGAIN;
+}
+
+static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev,
+ struct i2c_msg *msg,
+ enum msg_end_type end_state)
+{
+ u32 *dma_buf = i2c_dev->dma_buf;
+ u32 packet_header;
+
+ packet_header = FIELD_PREP(PACKET_HEADER0_HEADER_SIZE, 0) |
+ FIELD_PREP(PACKET_HEADER0_PROTOCOL,
+ PACKET_HEADER0_PROTOCOL_I2C) |
+ FIELD_PREP(PACKET_HEADER0_CONT_ID, i2c_dev->cont_id) |
+ FIELD_PREP(PACKET_HEADER0_PACKET_ID, 1);
+
+ if (i2c_dev->dma_mode && !i2c_dev->msg_read)
+ *dma_buf++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+ packet_header = i2c_dev->msg_len - 1;
+
+ if (i2c_dev->dma_mode && !i2c_dev->msg_read)
+ *dma_buf++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = I2C_HEADER_IE_ENABLE;
+
if (end_state == MSG_END_CONTINUE)
packet_header |= I2C_HEADER_CONTINUE_XFER;
else if (end_state == MSG_END_REPEAT_START)
packet_header |= I2C_HEADER_REPEAT_START;
+
if (msg->flags & I2C_M_TEN) {
packet_header |= msg->addr;
packet_header |= I2C_HEADER_10BIT_ADDR;
} else {
packet_header |= msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
}
+
if (msg->flags & I2C_M_IGNORE_NAK)
packet_header |= I2C_HEADER_CONT_ON_NAK;
+
if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
- if (!(msg->flags & I2C_M_RD))
- tegra_i2c_fill_tx_fifo(i2c_dev);
+ if (i2c_dev->dma_mode && !i2c_dev->msg_read)
+ *dma_buf++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+}
+
+static int tegra_i2c_error_recover(struct tegra_i2c_dev *i2c_dev,
+ struct i2c_msg *msg)
+{
+ if (i2c_dev->msg_err == I2C_ERR_NONE)
+ return 0;
+
+ tegra_i2c_init(i2c_dev);
+
+ /* start recovery upon arbitration loss in single master mode */
+ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) {
+ if (!i2c_dev->multimaster_mode)
+ return i2c_recover_bus(&i2c_dev->adapter);
+
+ return -EAGAIN;
+ }
+
+ if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ return 0;
+
+ return -EREMOTEIO;
+ }
+
+ return -EIO;
+}
+
+static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
+ struct i2c_msg *msg,
+ enum msg_end_type end_state)
+{
+ unsigned long time_left, xfer_time = 100;
+ size_t xfer_size;
+ u32 int_mask;
+ int err;
+
+ err = tegra_i2c_flush_fifos(i2c_dev);
+ if (err)
+ return err;
+
+ i2c_dev->msg_buf = msg->buf;
+ i2c_dev->msg_len = msg->len;
+
+ i2c_dev->msg_err = I2C_ERR_NONE;
+ i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
+ reinit_completion(&i2c_dev->msg_complete);
+
+ /*
+ * For SMBUS block read command, read only 1 byte in the first transfer.
+ * Adjust that 1 byte for the next transfer in the msg buffer and msg
+ * length.
+ */
+ if (msg->flags & I2C_M_RECV_LEN) {
+ if (end_state == MSG_END_CONTINUE) {
+ i2c_dev->msg_len = 1;
+ } else {
+ i2c_dev->msg_buf += 1;
+ i2c_dev->msg_len -= 1;
+ }
+ }
+
+ i2c_dev->msg_buf_remaining = i2c_dev->msg_len;
+
+ if (i2c_dev->msg_read)
+ xfer_size = i2c_dev->msg_len;
+ else
+ xfer_size = i2c_dev->msg_len + I2C_PACKET_HEADER_SIZE;
+
+ xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
+
+ i2c_dev->dma_mode = xfer_size > I2C_PIO_MODE_PREFERRED_LEN &&
+ i2c_dev->dma_buf && !i2c_dev->atomic_mode;
+
+ tegra_i2c_config_fifo_trig(i2c_dev, xfer_size);
+
+ /*
+ * Transfer time in mSec = Total bits / transfer rate
+ * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
+ */
+ xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
+ i2c_dev->timings.bus_freq_hz);
+
+ int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
+ if (i2c_dev->dma_mode) {
+ if (i2c_dev->msg_read) {
+ err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+ if (err)
+ return err;
+ }
+ }
+
+ tegra_i2c_push_packet_header(i2c_dev, msg, end_state);
+
+ if (!i2c_dev->msg_read) {
+ if (i2c_dev->dma_mode) {
+ memcpy(i2c_dev->dma_buf + I2C_PACKET_HEADER_SIZE,
+ msg->buf, i2c_dev->msg_len);
+ err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+ if (err)
+ return err;
+ } else {
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+ }
+ }
if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
- if (msg->flags & I2C_M_RD)
- int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
- else if (i2c_dev->msg_buf_remaining)
- int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+
+ if (!i2c_dev->dma_mode) {
+ if (msg->flags & I2C_M_RD)
+ int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+ else if (i2c_dev->msg_buf_remaining)
+ int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+ }
tegra_i2c_unmask_irq(i2c_dev, int_mask);
- spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
- dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+ dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK));
- time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
- TEGRA_I2C_TIMEOUT);
+ if (i2c_dev->dma_mode) {
+ time_left = tegra_i2c_wait_completion(i2c_dev,
+ &i2c_dev->dma_complete,
+ xfer_time);
+
+ /*
+ * Synchronize DMA first, since dmaengine_terminate_sync()
+ * performs synchronization after the transfer's termination
+ * and we want to get a completion if transfer succeeded.
+ */
+ dmaengine_synchronize(i2c_dev->dma_chan);
+ dmaengine_terminate_sync(i2c_dev->dma_chan);
+
+ if (!time_left && !completion_done(&i2c_dev->dma_complete)) {
+ tegra_i2c_init(i2c_dev);
+ return -ETIMEDOUT;
+ }
+
+ if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE)
+ memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, i2c_dev->msg_len);
+ }
+
+ time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete,
+ xfer_time);
+
tegra_i2c_mask_irq(i2c_dev, int_mask);
if (time_left == 0) {
- dev_err(i2c_dev->dev, "i2c transfer timed out\n");
-
tegra_i2c_init(i2c_dev);
return -ETIMEDOUT;
}
@@ -758,29 +1371,25 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
time_left, completion_done(&i2c_dev->msg_complete),
i2c_dev->msg_err);
- if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
- return 0;
+ i2c_dev->dma_mode = false;
- tegra_i2c_init(i2c_dev);
- if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
- if (msg->flags & I2C_M_IGNORE_NAK)
- return 0;
- return -EREMOTEIO;
- }
+ err = tegra_i2c_error_recover(i2c_dev, msg);
+ if (err)
+ return err;
- return -EIO;
+ return 0;
}
static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
- int num)
+ int num)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
- int i;
- int ret = 0;
+ int i, ret;
ret = pm_runtime_get_sync(i2c_dev->dev);
if (ret < 0) {
dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret);
+ pm_runtime_put_noidle(i2c_dev->dev);
return ret;
}
@@ -788,11 +1397,26 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
enum msg_end_type end_type = MSG_END_STOP;
if (i < (num - 1)) {
+ /* check whether follow up message is coming */
if (msgs[i + 1].flags & I2C_M_NOSTART)
end_type = MSG_END_CONTINUE;
else
end_type = MSG_END_REPEAT_START;
}
+ /* If M_RECV_LEN use ContinueXfer to read the first byte */
+ if (msgs[i].flags & I2C_M_RECV_LEN) {
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
+ if (ret)
+ break;
+
+ /* Validate message length before proceeding */
+ if (msgs[i].buf[0] == 0 || msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX)
+ break;
+
+ /* Set the msg length from first byte */
+ msgs[i].len += msgs[i].buf[0];
+ dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
+ }
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
@@ -803,357 +1427,587 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
return ret ?: i;
}
-static u32 tegra_i2c_func(struct i2c_adapter *adap)
+static int tegra_i2c_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
- u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
- I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
+ int ret;
+
+ i2c_dev->atomic_mode = true;
+ ret = tegra_i2c_xfer(adap, msgs, num);
+ i2c_dev->atomic_mode = false;
- if (i2c_dev->hw->has_continue_xfer_support)
- ret |= I2C_FUNC_NOSTART;
return ret;
}
-static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
+static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
- struct device_node *np = i2c_dev->dev->of_node;
- int ret;
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+ I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
- ret = of_property_read_u32(np, "clock-frequency",
- &i2c_dev->bus_clk_rate);
- if (ret)
- i2c_dev->bus_clk_rate = 100000; /* default clock rate */
+ if (i2c_dev->hw->has_continue_xfer_support)
+ ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
- i2c_dev->is_multimaster_mode = of_property_read_bool(np,
- "multi-master");
+ return ret;
}
static const struct i2c_algorithm tegra_i2c_algo = {
- .master_xfer = tegra_i2c_xfer,
- .functionality = tegra_i2c_func,
+ .xfer = tegra_i2c_xfer,
+ .xfer_atomic = tegra_i2c_xfer_atomic,
+ .functionality = tegra_i2c_func,
};
/* payload size is only 12 bit */
static const struct i2c_adapter_quirks tegra_i2c_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
- .max_read_len = 4096,
- .max_write_len = 4096,
+ .max_read_len = SZ_4K,
+ .max_write_len = SZ_4K - I2C_PACKET_HEADER_SIZE,
};
static const struct i2c_adapter_quirks tegra194_i2c_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
+ .max_write_len = SZ_64K - I2C_PACKET_HEADER_SIZE,
+};
+
+static struct i2c_bus_recovery_info tegra_i2c_recovery_info = {
+ .recover_bus = tegra_i2c_issue_bus_clear,
};
static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_continue_xfer_support = false,
.has_per_pkt_xfer_complete_irq = false,
- .has_single_clk_source = false,
.clk_divisor_hs_mode = 3,
- .clk_divisor_std_fast_mode = 0,
+ .clk_divisor_std_mode = 0,
+ .clk_divisor_fast_mode = 0,
.clk_divisor_fast_plus_mode = 0,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
+ .has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = false,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = false,
- .has_single_clk_source = false,
.clk_divisor_hs_mode = 3,
- .clk_divisor_std_fast_mode = 0,
+ .clk_divisor_std_mode = 0,
+ .clk_divisor_fast_mode = 0,
.clk_divisor_fast_plus_mode = 0,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
+ .has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = false,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
- .has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
+ .has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
- .has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
.has_multi_master_mode = false,
.has_slcg_override_reg = true,
.has_mst_fifo = false,
+ .has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = true,
};
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
- .has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
- .has_multi_master_mode = true,
+ .has_multi_master_mode = false,
.has_slcg_override_reg = true,
.has_mst_fifo = false,
+ .has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0,
+ .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_hs_mode = 0,
+ .has_interface_timing_reg = true,
};
-static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
- .has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x16,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
+ .has_multi_master_mode = false,
+ .has_slcg_override_reg = true,
+ .has_mst_fifo = false,
+ .has_mst_reset = false,
+ .quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x3,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0,
+ .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_hs_mode = 0,
+ .has_interface_timing_reg = true,
+};
+
+static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+ .has_continue_xfer_support = true,
+ .has_per_pkt_xfer_complete_irq = true,
+ .clk_divisor_hs_mode = 1,
+ .clk_divisor_std_mode = 0x4f,
+ .clk_divisor_fast_mode = 0x3c,
+ .clk_divisor_fast_plus_mode = 0x16,
+ .has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
.has_mst_fifo = true,
+ .has_mst_reset = true,
.quirks = &tegra194_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x8,
+ .thigh_std_mode = 0x7,
+ .tlow_fast_fastplus_mode = 0x2,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x08080808,
+ .setup_hold_time_fast_fast_plus_mode = 0x02020202,
+ .setup_hold_time_hs_mode = 0x090909,
+ .has_interface_timing_reg = true,
+};
+
+static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
+ .has_continue_xfer_support = true,
+ .has_per_pkt_xfer_complete_irq = true,
+ .clk_divisor_hs_mode = 7,
+ .clk_divisor_std_mode = 0x7a,
+ .clk_divisor_fast_mode = 0x40,
+ .clk_divisor_fast_plus_mode = 0x19,
+ .has_config_load_reg = true,
+ .has_multi_master_mode = true,
+ .has_slcg_override_reg = true,
+ .has_mst_fifo = true,
+ .has_mst_reset = true,
+ .quirks = &tegra194_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x8,
+ .thigh_std_mode = 0x7,
+ .tlow_fast_fastplus_mode = 0x3,
+ .thigh_fast_fastplus_mode = 0x3,
+ .setup_hold_time_std_mode = 0x08080808,
+ .setup_hold_time_fast_fast_plus_mode = 0x02020202,
+ .setup_hold_time_hs_mode = 0x090909,
+ .has_interface_timing_reg = true,
};
-/* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = {
+ { .compatible = "nvidia,tegra256-i2c", .data = &tegra256_i2c_hw, },
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
+ { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
+ { .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_i2c_hw, },
+#endif
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)
{ .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
+#endif
{},
};
MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
+static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
+{
+ struct device_node *np = i2c_dev->dev->of_node;
+ bool multi_mode;
+
+ i2c_parse_fw_timings(i2c_dev->dev, &i2c_dev->timings, true);
+
+ multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master");
+ i2c_dev->multimaster_mode = multi_mode;
+
+ if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
+ of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc"))
+ i2c_dev->is_dvc = true;
+
+ if (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) &&
+ of_device_is_compatible(np, "nvidia,tegra210-i2c-vi"))
+ i2c_dev->is_vi = true;
+}
+
+static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev)
+{
+ int err;
+
+ if (ACPI_HANDLE(i2c_dev->dev))
+ return 0;
+
+ i2c_dev->clocks[i2c_dev->nclocks++].id = "div-clk";
+
+ if (i2c_dev->hw == &tegra20_i2c_hw || i2c_dev->hw == &tegra30_i2c_hw)
+ i2c_dev->clocks[i2c_dev->nclocks++].id = "fast-clk";
+
+ if (IS_VI(i2c_dev))
+ i2c_dev->clocks[i2c_dev->nclocks++].id = "slow";
+
+ err = devm_clk_bulk_get(i2c_dev->dev, i2c_dev->nclocks,
+ i2c_dev->clocks);
+ if (err)
+ return err;
+
+ err = clk_bulk_prepare(i2c_dev->nclocks, i2c_dev->clocks);
+ if (err)
+ return err;
+
+ i2c_dev->div_clk = i2c_dev->clocks[0].clk;
+
+ if (!i2c_dev->multimaster_mode)
+ return 0;
+
+ err = clk_enable(i2c_dev->div_clk);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to enable div-clk: %d\n", err);
+ goto unprepare_clocks;
+ }
+
+ return 0;
+
+unprepare_clocks:
+ clk_bulk_unprepare(i2c_dev->nclocks, i2c_dev->clocks);
+
+ return err;
+}
+
+static void tegra_i2c_release_clocks(struct tegra_i2c_dev *i2c_dev)
+{
+ if (i2c_dev->multimaster_mode)
+ clk_disable(i2c_dev->div_clk);
+
+ clk_bulk_unprepare(i2c_dev->nclocks, i2c_dev->clocks);
+}
+
+static int tegra_i2c_init_hardware(struct tegra_i2c_dev *i2c_dev)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(i2c_dev->dev);
+ if (ret < 0)
+ dev_err(i2c_dev->dev, "runtime resume failed: %d\n", ret);
+ else
+ ret = tegra_i2c_init(i2c_dev);
+
+ pm_runtime_put_sync(i2c_dev->dev);
+
+ return ret;
+}
+
static int tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
struct resource *res;
- struct clk *div_clk;
- struct clk *fast_clk;
- void __iomem *base;
- int irq;
- int ret = 0;
- int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "no irq resource\n");
- return -EINVAL;
- }
- irq = res->start;
-
- div_clk = devm_clk_get(&pdev->dev, "div-clk");
- if (IS_ERR(div_clk)) {
- dev_err(&pdev->dev, "missing controller clock\n");
- return PTR_ERR(div_clk);
- }
+ int err;
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return -ENOMEM;
- i2c_dev->base = base;
- i2c_dev->div_clk = div_clk;
- i2c_dev->adapter.algo = &tegra_i2c_algo;
- i2c_dev->irq = irq;
+ platform_set_drvdata(pdev, i2c_dev);
+
+ init_completion(&i2c_dev->msg_complete);
+ init_completion(&i2c_dev->dma_complete);
+
+ i2c_dev->hw = device_get_match_data(&pdev->dev);
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
- i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c");
- if (IS_ERR(i2c_dev->rst)) {
- dev_err(&pdev->dev, "missing controller reset\n");
- return PTR_ERR(i2c_dev->rst);
- }
+ i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2c_dev->base))
+ return PTR_ERR(i2c_dev->base);
- tegra_i2c_parse_dt(i2c_dev);
+ i2c_dev->base_phys = res->start;
- i2c_dev->hw = of_device_get_match_data(&pdev->dev);
- i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
- "nvidia,tegra20-i2c-dvc");
- i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
- init_completion(&i2c_dev->msg_complete);
- spin_lock_init(&i2c_dev->xfer_lock);
+ err = platform_get_irq(pdev, 0);
+ if (err < 0)
+ return err;
- if (!i2c_dev->hw->has_single_clk_source) {
- fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
- if (IS_ERR(fast_clk)) {
- dev_err(&pdev->dev, "missing fast clock\n");
- return PTR_ERR(fast_clk);
- }
- i2c_dev->fast_clk = fast_clk;
- }
+ i2c_dev->irq = err;
- platform_set_drvdata(pdev, i2c_dev);
+ /* interrupt will be enabled during of transfer time */
+ irq_set_status_flags(i2c_dev->irq, IRQ_NOAUTOEN);
- if (!i2c_dev->hw->has_single_clk_source) {
- ret = clk_prepare(i2c_dev->fast_clk);
- if (ret < 0) {
- dev_err(i2c_dev->dev, "Clock prepare failed %d\n", ret);
- return ret;
- }
- }
+ err = devm_request_threaded_irq(i2c_dev->dev, i2c_dev->irq,
+ NULL, tegra_i2c_isr,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ dev_name(i2c_dev->dev), i2c_dev);
+ if (err)
+ return err;
- i2c_dev->clk_divisor_non_hs_mode =
- i2c_dev->hw->clk_divisor_std_fast_mode;
- if (i2c_dev->hw->clk_divisor_fast_plus_mode &&
- (i2c_dev->bus_clk_rate == 1000000))
- i2c_dev->clk_divisor_non_hs_mode =
- i2c_dev->hw->clk_divisor_fast_plus_mode;
-
- clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
- ret = clk_set_rate(i2c_dev->div_clk,
- i2c_dev->bus_clk_rate * clk_multiplier);
- if (ret) {
- dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret);
- goto unprepare_fast_clk;
- }
+ tegra_i2c_parse_dt(i2c_dev);
- ret = clk_prepare(i2c_dev->div_clk);
- if (ret < 0) {
- dev_err(i2c_dev->dev, "Clock prepare failed %d\n", ret);
- goto unprepare_fast_clk;
- }
+ err = tegra_i2c_init_clocks(i2c_dev);
+ if (err)
+ return err;
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- ret = tegra_i2c_runtime_resume(&pdev->dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "runtime resume failed\n");
- goto unprepare_div_clk;
- }
- }
+ err = tegra_i2c_init_dma(i2c_dev);
+ if (err)
+ goto release_clocks;
- if (i2c_dev->is_multimaster_mode) {
- ret = clk_enable(i2c_dev->div_clk);
- if (ret < 0) {
- dev_err(i2c_dev->dev, "div_clk enable failed %d\n",
- ret);
- goto disable_rpm;
- }
- }
+ /*
+ * VI I2C is in VE power domain which is not always ON and not
+ * IRQ-safe. Thus, IRQ-safe device shouldn't be attached to a
+ * non IRQ-safe domain because this prevents powering off the power
+ * domain.
+ *
+ * VI I2C device shouldn't be marked as IRQ-safe because VI I2C won't
+ * be used for atomic transfers. ACPI device is not IRQ safe also.
+ */
+ if (!IS_VI(i2c_dev) && !has_acpi_companion(i2c_dev->dev))
+ pm_runtime_irq_safe(i2c_dev->dev);
- ret = tegra_i2c_init(i2c_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
- goto disable_div_clk;
- }
+ pm_runtime_enable(i2c_dev->dev);
- ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
- tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
- goto disable_div_clk;
- }
+ err = tegra_i2c_init_hardware(i2c_dev);
+ if (err)
+ goto release_rpm;
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
+ i2c_dev->adapter.dev.of_node = i2c_dev->dev->of_node;
+ i2c_dev->adapter.dev.parent = i2c_dev->dev;
+ i2c_dev->adapter.retries = 1;
+ i2c_dev->adapter.timeout = 6 * HZ;
+ i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
i2c_dev->adapter.owner = THIS_MODULE;
i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
- strlcpy(i2c_dev->adapter.name, dev_name(&pdev->dev),
- sizeof(i2c_dev->adapter.name));
- i2c_dev->adapter.dev.parent = &pdev->dev;
+ i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.nr = pdev->id;
- i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
+ ACPI_COMPANION_SET(&i2c_dev->adapter.dev, ACPI_COMPANION(&pdev->dev));
- ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
- if (ret)
- goto disable_div_clk;
+ if (i2c_dev->hw->supports_bus_clear)
+ i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info;
- return 0;
+ strscpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev),
+ sizeof(i2c_dev->adapter.name));
-disable_div_clk:
- if (i2c_dev->is_multimaster_mode)
- clk_disable(i2c_dev->div_clk);
+ err = i2c_add_numbered_adapter(&i2c_dev->adapter);
+ if (err)
+ goto release_rpm;
-disable_rpm:
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- tegra_i2c_runtime_suspend(&pdev->dev);
+ return 0;
-unprepare_div_clk:
- clk_unprepare(i2c_dev->div_clk);
+release_rpm:
+ pm_runtime_disable(i2c_dev->dev);
-unprepare_fast_clk:
- if (!i2c_dev->hw->has_single_clk_source)
- clk_unprepare(i2c_dev->fast_clk);
+ tegra_i2c_release_dma(i2c_dev);
+release_clocks:
+ tegra_i2c_release_clocks(i2c_dev);
- return ret;
+ return err;
}
-static int tegra_i2c_remove(struct platform_device *pdev)
+static void tegra_i2c_remove(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c_dev->adapter);
+ pm_runtime_force_suspend(i2c_dev->dev);
- if (i2c_dev->is_multimaster_mode)
- clk_disable(i2c_dev->div_clk);
+ tegra_i2c_release_dma(i2c_dev);
+ tegra_i2c_release_clocks(i2c_dev);
+}
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- tegra_i2c_runtime_suspend(&pdev->dev);
+static int __maybe_unused tegra_i2c_runtime_resume(struct device *dev)
+{
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ int err;
+
+ err = pinctrl_pm_select_default_state(dev);
+ if (err)
+ return err;
+
+ err = clk_bulk_enable(i2c_dev->nclocks, i2c_dev->clocks);
+ if (err)
+ return err;
- clk_unprepare(i2c_dev->div_clk);
- if (!i2c_dev->hw->has_single_clk_source)
- clk_unprepare(i2c_dev->fast_clk);
+ /*
+ * VI I2C device is attached to VE power domain which goes through
+ * power ON/OFF during runtime PM resume/suspend, meaning that
+ * controller needs to be re-initialized after power ON.
+ */
+ if (IS_VI(i2c_dev)) {
+ err = tegra_i2c_init(i2c_dev);
+ if (err)
+ goto disable_clocks;
+ }
+
+ return 0;
+
+disable_clocks:
+ clk_bulk_disable(i2c_dev->nclocks, i2c_dev->clocks);
+
+ return err;
+}
+
+static int __maybe_unused tegra_i2c_runtime_suspend(struct device *dev)
+{
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+
+ clk_bulk_disable(i2c_dev->nclocks, i2c_dev->clocks);
+
+ return pinctrl_pm_select_idle_state(dev);
+}
+
+static int __maybe_unused tegra_i2c_suspend(struct device *dev)
+{
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ int err;
+
+ i2c_mark_adapter_suspended(&i2c_dev->adapter);
+
+ if (!pm_runtime_status_suspended(dev)) {
+ err = tegra_i2c_runtime_suspend(dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused tegra_i2c_resume(struct device *dev)
+{
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ int err;
+
+ /*
+ * We need to ensure that clocks are enabled so that registers can be
+ * restored in tegra_i2c_init().
+ */
+ err = tegra_i2c_runtime_resume(dev);
+ if (err)
+ return err;
+
+ err = tegra_i2c_init(i2c_dev);
+ if (err)
+ return err;
+
+ /*
+ * In case we are runtime suspended, disable clocks again so that we
+ * don't unbalance the clock reference counts during the next runtime
+ * resume transition.
+ */
+ if (pm_runtime_status_suspended(dev)) {
+ err = tegra_i2c_runtime_suspend(dev);
+ if (err)
+ return err;
+ }
+
+ i2c_mark_adapter_resumed(&i2c_dev->adapter);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops tegra_i2c_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume)
SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume,
NULL)
};
-#define TEGRA_I2C_PM (&tegra_i2c_pm)
-#else
-#define TEGRA_I2C_PM NULL
-#endif
+
+static const struct acpi_device_id tegra_i2c_acpi_match[] = {
+ {.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw},
+ {.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw},
+ {.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match);
static struct platform_driver tegra_i2c_driver = {
- .probe = tegra_i2c_probe,
- .remove = tegra_i2c_remove,
- .driver = {
- .name = "tegra-i2c",
+ .probe = tegra_i2c_probe,
+ .remove = tegra_i2c_remove,
+ .driver = {
+ .name = "tegra-i2c",
.of_match_table = tegra_i2c_of_match,
- .pm = TEGRA_I2C_PM,
+ .acpi_match_table = tegra_i2c_acpi_match,
+ .pm = &tegra_i2c_pm,
},
};
+module_platform_driver(tegra_i2c_driver);
-static int __init tegra_i2c_init_driver(void)
-{
- return platform_driver_register(&tegra_i2c_driver);
-}
-
-static void __exit tegra_i2c_exit_driver(void)
-{
- platform_driver_unregister(&tegra_i2c_driver);
-}
-
-subsys_initcall(tegra_i2c_init_driver);
-module_exit(tegra_i2c_exit_driver);
-
-MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver");
+MODULE_DESCRIPTION("NVIDIA Tegra I2C Bus Controller driver");
MODULE_AUTHOR("Colin Cross");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
index 19f8eec38717..3959f23fc440 100644
--- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c
+++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
@@ -27,7 +27,8 @@
#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012
-#define SYS_FREQ_DEFAULT 700000000
+#define SYS_FREQ_DEFAULT 800000000
+#define OTX2_REF_FREQ_DEFAULT 100000000
#define TWSI_INT_ENA_W1C 0x1028
#define TWSI_INT_ENA_W1S 0x1030
@@ -71,7 +72,7 @@ static u32 thunderx_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm thunderx_i2c_algo = {
- .master_xfer = octeon_i2c_xfer,
+ .xfer = octeon_i2c_xfer,
.functionality = thunderx_i2c_functionality,
};
@@ -99,7 +100,8 @@ static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c)
i2c->sys_freq = clk_get_rate(i2c->clk);
} else {
/* ACPI */
- device_property_read_u32(dev, "sclk", &i2c->sys_freq);
+ if (device_property_read_u32(dev, "sclk", &i2c->sys_freq))
+ device_property_read_u32(dev, "ioclk", &i2c->sys_freq);
}
skip:
@@ -118,6 +120,8 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
struct device_node *node)
{
+ struct i2c_client *ara;
+
if (!node)
return -EINVAL;
@@ -125,9 +129,12 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
if (!i2c->alert_data.irq)
return -EINVAL;
- i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data);
- if (!i2c->ara)
- return -ENODEV;
+ ara = i2c_new_smbus_alert_device(&i2c->adap, &i2c->alert_data);
+ if (IS_ERR(ara))
+ return PTR_ERR(ara);
+
+ i2c->ara = ara;
+
return 0;
}
@@ -160,6 +167,10 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
i2c->roff.sw_twsi = 0x1000;
i2c->roff.twsi_int = 0x1010;
i2c->roff.sw_twsi_ext = 0x1018;
+ i2c->roff.mode = 0x1038;
+ i2c->roff.block_ctl = 0x1048;
+ i2c->roff.block_sts = 0x1050;
+ i2c->roff.block_fifo = 0x1058;
i2c->dev = dev;
pci_set_drvdata(pdev, i2c);
@@ -167,7 +178,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
if (ret)
return ret;
- ret = pci_request_regions(pdev, DRV_NAME);
+ ret = pcim_request_all_regions(pdev, DRV_NAME);
if (ret)
return ret;
@@ -178,7 +189,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
thunder_i2c_clock_enable(dev, i2c);
ret = device_property_read_u32(dev, "clock-frequency", &i2c->twsi_freq);
if (ret)
- i2c->twsi_freq = 100000;
+ i2c->twsi_freq = I2C_MAX_STANDARD_MODE_FREQ;
init_waitqueue_head(&i2c->queue);
@@ -200,6 +211,12 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
if (ret)
goto error;
+ /*
+ * For OcteonTX2 chips, set reference frequency to 100MHz
+ * as refclk_src in TWSI_MODE register defaults to 100MHz.
+ */
+ if (octeon_i2c_is_otx2(pdev) && IS_LS_FREQ(i2c->twsi_freq))
+ i2c->sys_freq = OTX2_REF_FREQ_DEFAULT;
octeon_i2c_set_clock(i2c);
i2c->adap = thunderx_i2c_ops;
@@ -208,6 +225,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;
i2c->adap.dev.parent = dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
+ i2c->adap.dev.fwnode = dev->fwnode;
snprintf(i2c->adap.name, sizeof(i2c->adap.name),
"Cavium ThunderX i2c adapter at %s", dev_name(dev));
i2c_set_adapdata(&i2c->adap, i2c);
diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c
index a2e3dd715380..57dfe5f1a7d9 100644
--- a/drivers/i2c/busses/i2c-tiny-usb.c
+++ b/drivers/i2c/busses/i2c-tiny-usb.c
@@ -1,19 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* driver for the i2c-tiny-usb adapter - 1.0
* http://www.harbaum.org/till/i2c_tiny_usb
*
* Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/types.h>
/* include interfaces to usb layer */
@@ -58,8 +55,6 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
struct i2c_msg *pmsg;
int i, ret;
- dev_dbg(&adapter->dev, "master xfer %d messages:\n", num);
-
pstatus = kmalloc(sizeof(*pstatus), GFP_KERNEL);
if (!pstatus)
return -ENOMEM;
@@ -77,7 +72,7 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
dev_dbg(&adapter->dev,
" %d: %s (flags %d) %d bytes to 0x%02x\n",
- i, pmsg->flags & I2C_M_RD ? "read" : "write",
+ i, str_read_write(pmsg->flags & I2C_M_RD),
pmsg->flags, pmsg->len, pmsg->addr);
/* and directly send the message */
@@ -88,7 +83,7 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
pmsg->buf, pmsg->len) != pmsg->len) {
dev_err(&adapter->dev,
"failure reading data\n");
- ret = -EREMOTEIO;
+ ret = -EIO;
goto out;
}
} else {
@@ -98,7 +93,7 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
pmsg->buf, pmsg->len) != pmsg->len) {
dev_err(&adapter->dev,
"failure writing data\n");
- ret = -EREMOTEIO;
+ ret = -EIO;
goto out;
}
}
@@ -106,13 +101,13 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
/* read status */
if (usb_read(adapter, CMD_GET_STATUS, 0, 0, pstatus, 1) != 1) {
dev_err(&adapter->dev, "failure reading status\n");
- ret = -EREMOTEIO;
+ ret = -EIO;
goto out;
}
dev_dbg(&adapter->dev, " status = %d\n", *pstatus);
if (*pstatus == STATUS_ADDRESS_NAK) {
- ret = -EREMOTEIO;
+ ret = -ENXIO;
goto out;
}
}
@@ -144,10 +139,15 @@ out:
return ret;
}
+/* prevent invalid 0-length usb_control_msg */
+static const struct i2c_adapter_quirks usb_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN_READ,
+};
+
/* This is the actual algorithm we define */
static const struct i2c_algorithm usb_algorithm = {
- .master_xfer = usb_xfer,
- .functionality = usb_func,
+ .xfer = usb_xfer,
+ .functionality = usb_func,
};
/* ----- end of i2c layer ------------------------------------------------ */
@@ -226,14 +226,16 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface,
int retval = -ENOMEM;
u16 version;
+ if (interface->intf_assoc &&
+ interface->intf_assoc->bFunctionClass != USB_CLASS_VENDOR_SPEC)
+ return -ENODEV;
+
dev_dbg(&interface->dev, "probing usb device\n");
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev)
goto error;
- }
dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
@@ -250,6 +252,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface,
/* setup i2c adapter description */
dev->adapter.owner = THIS_MODULE;
dev->adapter.class = I2C_CLASS_HWMON;
+ dev->adapter.quirks = &usb_quirks;
dev->adapter.algo = &usb_algorithm;
dev->adapter.algo_data = dev;
snprintf(dev->adapter.name, sizeof(dev->adapter.name),
diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index 03da4a539a2f..ca0358e8f928 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/clk.h>
@@ -21,15 +12,15 @@
#include <linux/platform_device.h>
#define UNIPHIER_FI2C_CR 0x00 /* control register */
-#define UNIPHIER_FI2C_CR_MST BIT(3) /* master mode */
+#define UNIPHIER_FI2C_CR_MST BIT(3) /* controller mode */
#define UNIPHIER_FI2C_CR_STA BIT(2) /* start condition */
#define UNIPHIER_FI2C_CR_STO BIT(1) /* stop condition */
#define UNIPHIER_FI2C_CR_NACK BIT(0) /* do not return ACK */
#define UNIPHIER_FI2C_DTTX 0x04 /* TX FIFO */
-#define UNIPHIER_FI2C_DTTX_CMD BIT(8) /* send command (slave addr) */
+#define UNIPHIER_FI2C_DTTX_CMD BIT(8) /* send command (target addr) */
#define UNIPHIER_FI2C_DTTX_RD BIT(0) /* read transaction */
#define UNIPHIER_FI2C_DTRX 0x04 /* RX FIFO */
-#define UNIPHIER_FI2C_SLAD 0x0c /* slave address */
+#define UNIPHIER_FI2C_SLAD 0x0c /* target address */
#define UNIPHIER_FI2C_CYC 0x10 /* clock cycle control */
#define UNIPHIER_FI2C_LCTL 0x14 /* clock low period control */
#define UNIPHIER_FI2C_SSUT 0x18 /* restart/stop setup time control */
@@ -82,8 +73,6 @@
#define UNIPHIER_FI2C_BYTE_WISE BIT(3)
#define UNIPHIER_FI2C_DEFER_STOP_COMP BIT(4)
-#define UNIPHIER_FI2C_DEFAULT_SPEED 100000
-#define UNIPHIER_FI2C_MAX_SPEED 400000
#define UNIPHIER_FI2C_FIFO_SIZE 8
struct uniphier_fi2c_priv {
@@ -107,7 +96,7 @@ static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv,
int fifo_space = UNIPHIER_FI2C_FIFO_SIZE;
/*
- * TX-FIFO stores slave address in it for the first access.
+ * TX-FIFO stores target address in it for the first access.
* Decrement the counter.
*/
if (first)
@@ -117,7 +106,6 @@ static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv,
if (fifo_space-- <= 0)
break;
- dev_dbg(&priv->adap.dev, "write data: %02x\n", *priv->buf);
writel(*priv->buf++, priv->membase + UNIPHIER_FI2C_DTTX);
priv->len--;
}
@@ -133,7 +121,6 @@ static void uniphier_fi2c_drain_rxfifo(struct uniphier_fi2c_priv *priv)
break;
*priv->buf++ = readl(priv->membase + UNIPHIER_FI2C_DTRX);
- dev_dbg(&priv->adap.dev, "read data: %02x\n", priv->buf[-1]);
priv->len--;
}
}
@@ -151,8 +138,6 @@ static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv,
static void uniphier_fi2c_stop(struct uniphier_fi2c_priv *priv)
{
- dev_dbg(&priv->adap.dev, "stop condition\n");
-
priv->enabled_irqs |= UNIPHIER_FI2C_INT_STOP;
uniphier_fi2c_set_irqs(priv);
writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STO,
@@ -169,21 +154,15 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
irq_status = readl(priv->membase + UNIPHIER_FI2C_INT);
irq_status &= priv->enabled_irqs;
- dev_dbg(&priv->adap.dev,
- "interrupt: enabled_irqs=%04x, irq_status=%04x\n",
- priv->enabled_irqs, irq_status);
-
if (irq_status & UNIPHIER_FI2C_INT_STOP)
goto complete;
if (unlikely(irq_status & UNIPHIER_FI2C_INT_AL)) {
- dev_dbg(&priv->adap.dev, "arbitration lost\n");
priv->error = -EAGAIN;
goto complete;
}
if (unlikely(irq_status & UNIPHIER_FI2C_INT_NA)) {
- dev_dbg(&priv->adap.dev, "could not get ACK\n");
priv->error = -ENXIO;
if (priv->flags & UNIPHIER_FI2C_RD) {
/*
@@ -224,18 +203,14 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
if (priv->len <= UNIPHIER_FI2C_FIFO_SIZE &&
!(priv->flags & UNIPHIER_FI2C_BYTE_WISE)) {
- dev_dbg(&priv->adap.dev,
- "enable read byte count IRQ\n");
priv->enabled_irqs |= UNIPHIER_FI2C_INT_RB;
uniphier_fi2c_set_irqs(priv);
priv->flags |= UNIPHIER_FI2C_BYTE_WISE;
}
- if (priv->len <= 1) {
- dev_dbg(&priv->adap.dev, "set NACK\n");
+ if (priv->len <= 1)
writel(UNIPHIER_FI2C_CR_MST |
UNIPHIER_FI2C_CR_NACK,
priv->membase + UNIPHIER_FI2C_CR);
- }
}
goto handled;
@@ -277,7 +252,7 @@ static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr,
/* do not use TX byte counter */
writel(0, priv->membase + UNIPHIER_FI2C_TBC);
- /* set slave address */
+ /* set target address */
writel(UNIPHIER_FI2C_DTTX_CMD | addr << 1,
priv->membase + UNIPHIER_FI2C_DTTX);
/*
@@ -313,7 +288,7 @@ static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr)
uniphier_fi2c_set_irqs(priv);
- /* set slave address with RD bit */
+ /* set target address with RD bit */
writel(UNIPHIER_FI2C_DTTX_CMD | UNIPHIER_FI2C_DTTX_RD | addr << 1,
priv->membase + UNIPHIER_FI2C_DTTX);
}
@@ -335,18 +310,13 @@ static void uniphier_fi2c_recover(struct uniphier_fi2c_priv *priv)
i2c_recover_bus(&priv->adap);
}
-static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
- struct i2c_msg *msg, bool repeat,
- bool stop)
+static int uniphier_fi2c_xfer_one(struct i2c_adapter *adap, struct i2c_msg *msg,
+ bool repeat, bool stop)
{
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
bool is_read = msg->flags & I2C_M_RD;
unsigned long time_left, flags;
- dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, repeat=%d, stop=%d\n",
- is_read ? "receive" : "transmit", msg->addr, msg->len,
- repeat, stop);
-
priv->len = msg->len;
priv->buf = msg->buf;
priv->enabled_irqs = UNIPHIER_FI2C_INT_FAULTS;
@@ -368,9 +338,8 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
else
uniphier_fi2c_tx_init(priv, msg->addr, repeat);
- dev_dbg(&adap->dev, "start condition\n");
/*
- * For a repeated START condition, writing a slave address to the FIFO
+ * For a repeated START condition, writing a target address to the FIFO
* kicks the controller. So, the UNIPHIER_FI2C_CR register should be
* written only for a non-repeated START condition.
*/
@@ -388,11 +357,9 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
spin_unlock_irqrestore(&priv->lock, flags);
if (!time_left) {
- dev_err(&adap->dev, "transaction timeout.\n");
uniphier_fi2c_recover(priv);
return -ETIMEDOUT;
}
- dev_dbg(&adap->dev, "complete\n");
if (unlikely(priv->flags & UNIPHIER_FI2C_DEFER_STOP_COMP)) {
u32 status;
@@ -435,8 +402,7 @@ static int uniphier_fi2c_check_bus_busy(struct i2c_adapter *adap)
return 0;
}
-static int uniphier_fi2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
+static int uniphier_fi2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct i2c_msg *msg, *emsg = msgs + num;
bool repeat = false;
@@ -450,7 +416,7 @@ static int uniphier_fi2c_master_xfer(struct i2c_adapter *adap,
/* Emit STOP if it is the last message or I2C_M_STOP is set. */
bool stop = (msg + 1 == emsg) || (msg->flags & I2C_M_STOP);
- ret = uniphier_fi2c_master_xfer_one(adap, msg, repeat, stop);
+ ret = uniphier_fi2c_xfer_one(adap, msg, repeat, stop);
if (ret)
return ret;
@@ -466,7 +432,7 @@ static u32 uniphier_fi2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm uniphier_fi2c_algo = {
- .master_xfer = uniphier_fi2c_master_xfer,
+ .xfer = uniphier_fi2c_xfer,
.functionality = uniphier_fi2c_functionality,
};
@@ -547,7 +513,6 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_fi2c_priv *priv;
- struct resource *regs;
u32 bus_speed;
unsigned long clk_rate;
int irq, ret;
@@ -556,41 +521,27 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->membase = devm_ioremap_resource(dev, regs);
+ priv->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->membase))
return PTR_ERR(priv->membase);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to get IRQ number\n");
+ if (irq < 0)
return irq;
- }
if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed))
- bus_speed = UNIPHIER_FI2C_DEFAULT_SPEED;
+ bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
- if (!bus_speed || bus_speed > UNIPHIER_FI2C_MAX_SPEED) {
- dev_err(dev, "invalid clock-frequency %d\n", bus_speed);
- return -EINVAL;
- }
+ if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ)
+ return dev_err_probe(dev, -EINVAL, "invalid clock-frequency %d\n", bus_speed);
- priv->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(priv->clk)) {
- dev_err(dev, "failed to get clock\n");
- return PTR_ERR(priv->clk);
- }
-
- ret = clk_prepare_enable(priv->clk);
- if (ret)
- return ret;
+ priv->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "failed to enable clock\n");
clk_rate = clk_get_rate(priv->clk);
- if (!clk_rate) {
- dev_err(dev, "input clock rate should not be zero\n");
- ret = -EINVAL;
- goto disable_clk;
- }
+ if (!clk_rate)
+ return dev_err_probe(dev, -EINVAL, "input clock rate should not be zero\n");
priv->clk_cycle = clk_rate / bus_speed;
init_completion(&priv->comp);
@@ -599,7 +550,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
priv->adap.algo = &uniphier_fi2c_algo;
priv->adap.dev.parent = dev;
priv->adap.dev.of_node = dev->of_node;
- strlcpy(priv->adap.name, "UniPhier FI2C", sizeof(priv->adap.name));
+ strscpy(priv->adap.name, "UniPhier FI2C", sizeof(priv->adap.name));
priv->adap.bus_recovery_info = &uniphier_fi2c_bus_recovery_info;
i2c_set_adapdata(&priv->adap, priv);
platform_set_drvdata(pdev, priv);
@@ -608,27 +559,17 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
pdev->name, priv);
- if (ret) {
- dev_err(dev, "failed to request irq %d\n", irq);
- goto disable_clk;
- }
-
- ret = i2c_add_adapter(&priv->adap);
-disable_clk:
if (ret)
- clk_disable_unprepare(priv->clk);
+ return dev_err_probe(dev, ret, "failed to request irq %d\n", irq);
- return ret;
+ return i2c_add_adapter(&priv->adap);
}
-static int uniphier_fi2c_remove(struct platform_device *pdev)
+static void uniphier_fi2c_remove(struct platform_device *pdev)
{
struct uniphier_fi2c_priv *priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
- clk_disable_unprepare(priv->clk);
-
- return 0;
}
static int __maybe_unused uniphier_fi2c_suspend(struct device *dev)
diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c
index c488e558aef7..9d49a3d5d612 100644
--- a/drivers/i2c/busses/i2c-uniphier.c
+++ b/drivers/i2c/busses/i2c-uniphier.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/clk.h>
@@ -26,13 +17,13 @@
#define UNIPHIER_I2C_DTRM_NACK BIT(8) /* do not return ACK */
#define UNIPHIER_I2C_DTRM_RD BIT(0) /* read transaction */
#define UNIPHIER_I2C_DREC 0x04 /* RX register */
-#define UNIPHIER_I2C_DREC_MST BIT(14) /* 1 = master, 0 = slave */
+#define UNIPHIER_I2C_DREC_MST BIT(14) /* 1 = controller, 0 = target */
#define UNIPHIER_I2C_DREC_TX BIT(13) /* 1 = transmit, 0 = receive */
#define UNIPHIER_I2C_DREC_STS BIT(12) /* stop condition detected */
#define UNIPHIER_I2C_DREC_LRB BIT(11) /* no ACK */
#define UNIPHIER_I2C_DREC_LAB BIT(9) /* arbitration lost */
#define UNIPHIER_I2C_DREC_BBN BIT(8) /* bus not busy */
-#define UNIPHIER_I2C_MYAD 0x08 /* slave address */
+#define UNIPHIER_I2C_MYAD 0x08 /* local target address */
#define UNIPHIER_I2C_CLK 0x0c /* clock frequency control */
#define UNIPHIER_I2C_BRST 0x10 /* bus reset */
#define UNIPHIER_I2C_BRST_FOEN BIT(1) /* normal operation */
@@ -44,9 +35,6 @@
#define UNIPHIER_I2C_NOISE 0x1c /* noise filter control */
#define UNIPHIER_I2C_SETUP 0x20 /* setup time control */
-#define UNIPHIER_I2C_DEFAULT_SPEED 100000
-#define UNIPHIER_I2C_MAX_SPEED 400000
-
struct uniphier_i2c_priv {
struct completion comp;
struct i2c_adapter adap;
@@ -80,18 +68,13 @@ static int uniphier_i2c_xfer_byte(struct i2c_adapter *adap, u32 txdata,
reinit_completion(&priv->comp);
txdata |= UNIPHIER_I2C_DTRM_IRQEN;
- dev_dbg(&adap->dev, "write data: 0x%04x\n", txdata);
writel(txdata, priv->membase + UNIPHIER_I2C_DTRM);
time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);
- if (unlikely(!time_left)) {
- dev_err(&adap->dev, "transaction timeout\n");
+ if (unlikely(!time_left))
return -ETIMEDOUT;
- }
rxdata = readl(priv->membase + UNIPHIER_I2C_DREC);
- dev_dbg(&adap->dev, "read data: 0x%04x\n", rxdata);
-
if (rxdatap)
*rxdatap = rxdata;
@@ -107,14 +90,11 @@ static int uniphier_i2c_send_byte(struct i2c_adapter *adap, u32 txdata)
if (ret)
return ret;
- if (unlikely(rxdata & UNIPHIER_I2C_DREC_LAB)) {
- dev_dbg(&adap->dev, "arbitration lost\n");
+ if (unlikely(rxdata & UNIPHIER_I2C_DREC_LAB))
return -EAGAIN;
- }
- if (unlikely(rxdata & UNIPHIER_I2C_DREC_LRB)) {
- dev_dbg(&adap->dev, "could not get ACK\n");
+
+ if (unlikely(rxdata & UNIPHIER_I2C_DREC_LRB))
return -ENXIO;
- }
return 0;
}
@@ -124,7 +104,6 @@ static int uniphier_i2c_tx(struct i2c_adapter *adap, u16 addr, u16 len,
{
int ret;
- dev_dbg(&adap->dev, "start condition\n");
ret = uniphier_i2c_send_byte(adap, addr << 1 |
UNIPHIER_I2C_DTRM_STA |
UNIPHIER_I2C_DTRM_NACK);
@@ -146,7 +125,6 @@ static int uniphier_i2c_rx(struct i2c_adapter *adap, u16 addr, u16 len,
{
int ret;
- dev_dbg(&adap->dev, "start condition\n");
ret = uniphier_i2c_send_byte(adap, addr << 1 |
UNIPHIER_I2C_DTRM_STA |
UNIPHIER_I2C_DTRM_NACK |
@@ -170,21 +148,17 @@ static int uniphier_i2c_rx(struct i2c_adapter *adap, u16 addr, u16 len,
static int uniphier_i2c_stop(struct i2c_adapter *adap)
{
- dev_dbg(&adap->dev, "stop condition\n");
return uniphier_i2c_send_byte(adap, UNIPHIER_I2C_DTRM_STO |
UNIPHIER_I2C_DTRM_NACK);
}
-static int uniphier_i2c_master_xfer_one(struct i2c_adapter *adap,
- struct i2c_msg *msg, bool stop)
+static int uniphier_i2c_xfer_one(struct i2c_adapter *adap,
+ struct i2c_msg *msg, bool stop)
{
bool is_read = msg->flags & I2C_M_RD;
bool recovery = false;
int ret;
- dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
- is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
-
if (is_read)
ret = uniphier_i2c_rx(adap, msg->addr, msg->len, msg->buf);
else
@@ -237,8 +211,7 @@ static int uniphier_i2c_check_bus_busy(struct i2c_adapter *adap)
return 0;
}
-static int uniphier_i2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
+static int uniphier_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct i2c_msg *msg, *emsg = msgs + num;
int ret;
@@ -251,7 +224,7 @@ static int uniphier_i2c_master_xfer(struct i2c_adapter *adap,
/* Emit STOP if it is the last message or I2C_M_STOP is set. */
bool stop = (msg + 1 == emsg) || (msg->flags & I2C_M_STOP);
- ret = uniphier_i2c_master_xfer_one(adap, msg, stop);
+ ret = uniphier_i2c_xfer_one(adap, msg, stop);
if (ret)
return ret;
}
@@ -265,7 +238,7 @@ static u32 uniphier_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm uniphier_i2c_algo = {
- .master_xfer = uniphier_i2c_master_xfer,
+ .xfer = uniphier_i2c_xfer,
.functionality = uniphier_i2c_functionality,
};
@@ -335,7 +308,6 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_i2c_priv *priv;
- struct resource *regs;
u32 bus_speed;
unsigned long clk_rate;
int irq, ret;
@@ -344,41 +316,27 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->membase = devm_ioremap_resource(dev, regs);
+ priv->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->membase))
return PTR_ERR(priv->membase);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to get IRQ number\n");
+ if (irq < 0)
return irq;
- }
if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed))
- bus_speed = UNIPHIER_I2C_DEFAULT_SPEED;
+ bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
- if (!bus_speed || bus_speed > UNIPHIER_I2C_MAX_SPEED) {
- dev_err(dev, "invalid clock-frequency %d\n", bus_speed);
- return -EINVAL;
- }
+ if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ)
+ return dev_err_probe(dev, -EINVAL, "invalid clock-frequency %d\n", bus_speed);
- priv->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(priv->clk)) {
- dev_err(dev, "failed to get clock\n");
- return PTR_ERR(priv->clk);
- }
-
- ret = clk_prepare_enable(priv->clk);
- if (ret)
- return ret;
+ priv->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "failed to enable clock\n");
clk_rate = clk_get_rate(priv->clk);
- if (!clk_rate) {
- dev_err(dev, "input clock rate should not be zero\n");
- ret = -EINVAL;
- goto disable_clk;
- }
+ if (!clk_rate)
+ return dev_err_probe(dev, -EINVAL, "input clock rate should not be zero\n");
priv->clk_cycle = clk_rate / bus_speed;
init_completion(&priv->comp);
@@ -386,7 +344,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
priv->adap.algo = &uniphier_i2c_algo;
priv->adap.dev.parent = dev;
priv->adap.dev.of_node = dev->of_node;
- strlcpy(priv->adap.name, "UniPhier I2C", sizeof(priv->adap.name));
+ strscpy(priv->adap.name, "UniPhier I2C", sizeof(priv->adap.name));
priv->adap.bus_recovery_info = &uniphier_i2c_bus_recovery_info;
i2c_set_adapdata(&priv->adap, priv);
platform_set_drvdata(pdev, priv);
@@ -395,27 +353,17 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
priv);
- if (ret) {
- dev_err(dev, "failed to request irq %d\n", irq);
- goto disable_clk;
- }
-
- ret = i2c_add_adapter(&priv->adap);
-disable_clk:
if (ret)
- clk_disable_unprepare(priv->clk);
+ return dev_err_probe(dev, ret, "failed to request irq %d\n", irq);
- return ret;
+ return i2c_add_adapter(&priv->adap);
}
-static int uniphier_i2c_remove(struct platform_device *pdev)
+static void uniphier_i2c_remove(struct platform_device *pdev)
{
struct uniphier_i2c_priv *priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
- clk_disable_unprepare(priv->clk);
-
- return 0;
}
static int __maybe_unused uniphier_i2c_suspend(struct device *dev)
diff --git a/drivers/i2c/busses/i2c-usbio.c b/drivers/i2c/busses/i2c-usbio.c
new file mode 100644
index 000000000000..e7799abf6787
--- /dev/null
+++ b/drivers/i2c/busses/i2c-usbio.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Intel Corporation.
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/usb/usbio.h>
+
+#define I2C_RW_OVERHEAD (sizeof(struct usbio_bulk_packet) + sizeof(struct usbio_i2c_rw))
+
+struct usbio_i2c {
+ struct i2c_adapter adap;
+ struct auxiliary_device *adev;
+ struct usbio_i2c_rw *rwbuf;
+ unsigned long quirks;
+ u32 speed;
+ u16 txbuf_len;
+ u16 rxbuf_len;
+};
+
+static const struct acpi_device_id usbio_i2c_acpi_hids[] = {
+ { "INTC1008" }, /* MTL */
+ { "INTC10B3" }, /* ARL */
+ { "INTC10B6" }, /* LNL */
+ { "INTC10D2" }, /* MTL-CVF */
+ { "INTC10E3" }, /* PTL */
+ { }
+};
+
+static const u32 usbio_i2c_speeds[] = {
+ I2C_MAX_STANDARD_MODE_FREQ,
+ I2C_MAX_FAST_MODE_FREQ,
+ I2C_MAX_FAST_MODE_PLUS_FREQ,
+ I2C_MAX_HIGH_SPEED_MODE_FREQ
+};
+
+static void usbio_i2c_uninit(struct i2c_adapter *adap, struct i2c_msg *msg)
+{
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
+ struct usbio_i2c_uninit ubuf;
+
+ ubuf.busid = i2c->adev->id;
+ ubuf.config = cpu_to_le16(msg->addr);
+
+ usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_UNINIT, true,
+ &ubuf, sizeof(ubuf), NULL, 0);
+}
+
+static int usbio_i2c_init(struct i2c_adapter *adap, struct i2c_msg *msg)
+{
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
+ struct usbio_i2c_init ibuf;
+ void *reply_buf;
+ u16 reply_len;
+ int ret;
+
+ ibuf.busid = i2c->adev->id;
+ ibuf.config = cpu_to_le16(msg->addr);
+ ibuf.speed = cpu_to_le32(i2c->speed);
+
+ if (i2c->quirks & USBIO_QUIRK_I2C_NO_INIT_ACK) {
+ reply_buf = NULL;
+ reply_len = 0;
+ } else {
+ reply_buf = &ibuf;
+ reply_len = sizeof(ibuf);
+ }
+
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_INIT, true,
+ &ibuf, sizeof(ibuf), reply_buf, reply_len);
+ if (ret != sizeof(ibuf))
+ return (ret < 0) ? ret : -EIO;
+
+ return 0;
+}
+
+static int usbio_i2c_read(struct i2c_adapter *adap, struct i2c_msg *msg)
+{
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
+ u16 rxchunk = i2c->rxbuf_len - I2C_RW_OVERHEAD;
+ struct usbio_i2c_rw *rbuf = i2c->rwbuf;
+ int ret;
+
+ rbuf->busid = i2c->adev->id;
+ rbuf->config = cpu_to_le16(msg->addr);
+ rbuf->size = cpu_to_le16(msg->len);
+
+ if (msg->len > rxchunk) {
+ /* Need to split the input buffer */
+ u16 len = 0;
+
+ do {
+ if (msg->len - len < rxchunk)
+ rxchunk = msg->len - len;
+
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C,
+ USBIO_I2CCMD_READ, true,
+ rbuf, len == 0 ? sizeof(*rbuf) : 0,
+ rbuf, sizeof(*rbuf) + rxchunk);
+ if (ret < 0)
+ return ret;
+
+ memcpy(&msg->buf[len], rbuf->data, rxchunk);
+ len += rxchunk;
+ } while (msg->len > len);
+
+ return 0;
+ }
+
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_READ, true,
+ rbuf, sizeof(*rbuf), rbuf, sizeof(*rbuf) + msg->len);
+ if (ret != sizeof(*rbuf) + msg->len)
+ return (ret < 0) ? ret : -EIO;
+
+ memcpy(msg->buf, rbuf->data, msg->len);
+
+ return 0;
+}
+
+static int usbio_i2c_write(struct i2c_adapter *adap, struct i2c_msg *msg)
+{
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
+ u16 txchunk = i2c->txbuf_len - I2C_RW_OVERHEAD;
+ struct usbio_i2c_rw *wbuf = i2c->rwbuf;
+ int ret;
+
+ if (msg->len > txchunk) {
+ /* Need to split the output buffer */
+ u16 len = 0;
+
+ do {
+ wbuf->busid = i2c->adev->id;
+ wbuf->config = cpu_to_le16(msg->addr);
+
+ if (i2c->quirks & USBIO_QUIRK_I2C_USE_CHUNK_LEN)
+ wbuf->size = cpu_to_le16(txchunk);
+ else
+ wbuf->size = cpu_to_le16(msg->len);
+
+ memcpy(wbuf->data, &msg->buf[len], txchunk);
+ len += txchunk;
+
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C,
+ USBIO_I2CCMD_WRITE, msg->len == len,
+ wbuf, sizeof(*wbuf) + txchunk,
+ wbuf, sizeof(*wbuf));
+ if (ret < 0)
+ return ret;
+
+ if (msg->len - len < txchunk)
+ txchunk = msg->len - len;
+ } while (msg->len > len);
+
+ return 0;
+ }
+
+ wbuf->busid = i2c->adev->id;
+ wbuf->config = cpu_to_le16(msg->addr);
+ wbuf->size = cpu_to_le16(msg->len);
+ memcpy(wbuf->data, msg->buf, msg->len);
+
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_WRITE, true,
+ wbuf, sizeof(*wbuf) + msg->len, wbuf, sizeof(*wbuf));
+ if (ret != sizeof(*wbuf) || le16_to_cpu(wbuf->size) != msg->len)
+ return (ret < 0) ? ret : -EIO;
+
+ return 0;
+}
+
+static int usbio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
+ int ret;
+
+ usbio_acquire(i2c->adev);
+
+ ret = usbio_i2c_init(adap, msgs);
+ if (ret)
+ goto out_release;
+
+ for (int i = 0; i < num; ret = ++i) {
+ if (msgs[i].flags & I2C_M_RD)
+ ret = usbio_i2c_read(adap, &msgs[i]);
+ else
+ ret = usbio_i2c_write(adap, &msgs[i]);
+
+ if (ret)
+ break;
+ }
+
+ usbio_i2c_uninit(adap, msgs);
+
+out_release:
+ usbio_release(i2c->adev);
+
+ return ret;
+}
+
+static u32 usbio_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_adapter_quirks usbio_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
+ .max_read_len = SZ_4K,
+ .max_write_len = SZ_4K,
+};
+
+static const struct i2c_adapter_quirks usbio_i2c_quirks_max_rw_len52 = {
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
+ .max_read_len = 52,
+ .max_write_len = 52,
+};
+
+static const struct i2c_algorithm usbio_i2c_algo = {
+ .master_xfer = usbio_i2c_xfer,
+ .functionality = usbio_i2c_func,
+};
+
+static int usbio_i2c_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *adev_id)
+{
+ struct usbio_i2c_bus_desc *i2c_desc;
+ struct device *dev = &adev->dev;
+ struct usbio_i2c *i2c;
+ u32 max_speed;
+ int ret;
+
+ i2c_desc = dev_get_platdata(dev);
+ if (!i2c_desc)
+ return -EINVAL;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->adev = adev;
+
+ usbio_acpi_bind(i2c->adev, usbio_i2c_acpi_hids);
+ usbio_get_txrxbuf_len(i2c->adev, &i2c->txbuf_len, &i2c->rxbuf_len);
+
+ i2c->rwbuf = devm_kzalloc(dev, max(i2c->txbuf_len, i2c->rxbuf_len), GFP_KERNEL);
+ if (!i2c->rwbuf)
+ return -ENOMEM;
+
+ i2c->quirks = usbio_get_quirks(i2c->adev);
+
+ max_speed = usbio_i2c_speeds[i2c_desc->caps & USBIO_I2C_BUS_MODE_CAP_MASK];
+ if (max_speed < I2C_MAX_FAST_MODE_FREQ &&
+ (i2c->quirks & USBIO_QUIRK_I2C_ALLOW_400KHZ))
+ max_speed = I2C_MAX_FAST_MODE_FREQ;
+
+ i2c->speed = i2c_acpi_find_bus_speed(dev);
+ if (!i2c->speed)
+ i2c->speed = I2C_MAX_STANDARD_MODE_FREQ;
+ else if (i2c->speed > max_speed) {
+ dev_warn(dev, "Invalid speed %u adjusting to bus max %u\n",
+ i2c->speed, max_speed);
+ i2c->speed = max_speed;
+ }
+
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.class = I2C_CLASS_HWMON;
+ i2c->adap.dev.parent = dev;
+ i2c->adap.algo = &usbio_i2c_algo;
+
+ if (i2c->quirks & USBIO_QUIRK_I2C_MAX_RW_LEN_52)
+ i2c->adap.quirks = &usbio_i2c_quirks_max_rw_len52;
+ else
+ i2c->adap.quirks = &usbio_i2c_quirks;
+
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name), "%s.%d",
+ USBIO_I2C_CLIENT, i2c->adev->id);
+
+ device_set_node(&i2c->adap.dev, dev_fwnode(&adev->dev));
+
+ auxiliary_set_drvdata(adev, i2c);
+ i2c_set_adapdata(&i2c->adap, i2c);
+
+ ret = i2c_add_adapter(&i2c->adap);
+ if (ret)
+ return ret;
+
+ if (has_acpi_companion(&i2c->adap.dev))
+ acpi_dev_clear_dependencies(ACPI_COMPANION(&i2c->adap.dev));
+
+ return 0;
+}
+
+static void usbio_i2c_remove(struct auxiliary_device *adev)
+{
+ struct usbio_i2c *i2c = auxiliary_get_drvdata(adev);
+
+ i2c_del_adapter(&i2c->adap);
+}
+
+static const struct auxiliary_device_id usbio_i2c_id_table[] = {
+ { "usbio.usbio-i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, usbio_i2c_id_table);
+
+static struct auxiliary_driver usbio_i2c_driver = {
+ .name = USBIO_I2C_CLIENT,
+ .probe = usbio_i2c_probe,
+ .remove = usbio_i2c_remove,
+ .id_table = usbio_i2c_id_table
+};
+module_auxiliary_driver(usbio_i2c_driver);
+
+MODULE_DESCRIPTION("Intel USBIO I2C driver");
+MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("USBIO");
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index f1ab2a637ec0..a1ab6ef6f071 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c-versatile.c
*
* Copyright (C) 2006 ARM Ltd.
* written by Russell King, Deep Blue Solutions Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
@@ -67,22 +64,20 @@ static const struct i2c_algo_bit_data i2c_versatile_algo = {
static int i2c_versatile_probe(struct platform_device *dev)
{
struct i2c_versatile *i2c;
- struct resource *r;
int ret;
i2c = devm_kzalloc(&dev->dev, sizeof(struct i2c_versatile), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
- r = platform_get_resource(dev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&dev->dev, r);
+ i2c->base = devm_platform_get_and_ioremap_resource(dev, 0, NULL);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
writel(SCL | SDA, i2c->base + I2C_CONTROLS);
i2c->adap.owner = THIS_MODULE;
- strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
+ strscpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = &dev->dev;
i2c->adap.dev.of_node = dev->dev.of_node;
@@ -99,12 +94,11 @@ static int i2c_versatile_probe(struct platform_device *dev)
return 0;
}
-static int i2c_versatile_remove(struct platform_device *dev)
+static void i2c_versatile_remove(struct platform_device *dev)
{
struct i2c_versatile *i2c = platform_get_drvdata(dev);
i2c_del_adapter(&i2c->adap);
- return 0;
}
static const struct of_device_id i2c_versatile_match[] = {
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 59b1d233ca7b..2c26a57883f2 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -78,7 +70,7 @@ static struct i2c_algo_bit_data bit_data = {
static struct i2c_adapter vt586b_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.name = "VIA i2c",
.algo_data = &bit_data,
};
@@ -97,10 +89,9 @@ static int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
u8 rev;
int res;
- if (pm_io_base) {
- dev_err(&dev->dev, "i2c-via: Will only support one host\n");
- return -ENODEV;
- }
+ if (pm_io_base)
+ return dev_err_probe(&dev->dev, -ENODEV,
+ "Will only support one host\n");
pci_read_config_byte(dev, PM_CFG_REVID, &rev);
@@ -121,10 +112,10 @@ static int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_read_config_word(dev, base, &pm_io_base);
pm_io_base &= (0xff << 8);
- if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name)) {
- dev_err(&dev->dev, "IO 0x%x-0x%x already in use\n", I2C_DIR, I2C_DIR + IOSPACE);
- return -ENODEV;
- }
+ if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name))
+ return dev_err_probe(&dev->dev, -ENODEV,
+ "IO 0x%x-0x%x already in use\n",
+ I2C_DIR, I2C_DIR + IOSPACE);
outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
diff --git a/drivers/i2c/busses/i2c-viai2c-common.c b/drivers/i2c/busses/i2c-viai2c-common.c
new file mode 100644
index 000000000000..5a53ed95a59b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-common.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/of_irq.h>
+#include "i2c-viai2c-common.h"
+
+int viai2c_wait_bus_not_busy(struct viai2c *i2c)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + VIAI2C_TIMEOUT;
+ while (!(readw(i2c->base + VIAI2C_REG_CSR) & VIAI2C_CSR_READY_MASK)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(i2c->dev, "timeout waiting for bus ready\n");
+ return -EBUSY;
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(viai2c_wait_bus_not_busy);
+
+static int viai2c_write(struct viai2c *i2c, struct i2c_msg *pmsg, int last)
+{
+ u16 val, tcr_val = i2c->tcr;
+
+ i2c->last = last;
+
+ if (pmsg->len == 0) {
+ /*
+ * We still need to run through the while (..) once, so
+ * start at -1 and break out early from the loop
+ */
+ i2c->xfered_len = -1;
+ writew(0, i2c->base + VIAI2C_REG_CDR);
+ } else {
+ writew(pmsg->buf[0] & 0xFF, i2c->base + VIAI2C_REG_CDR);
+ }
+
+ if (i2c->platform == VIAI2C_PLAT_WMT && !(pmsg->flags & I2C_M_NOSTART)) {
+ val = readw(i2c->base + VIAI2C_REG_CR);
+ val &= ~VIAI2C_CR_TX_END;
+ val |= VIAI2C_CR_CPU_RDY;
+ writew(val, i2c->base + VIAI2C_REG_CR);
+ }
+
+ reinit_completion(&i2c->complete);
+
+ tcr_val |= pmsg->addr & VIAI2C_TCR_ADDR_MASK;
+
+ writew(tcr_val, i2c->base + VIAI2C_REG_TCR);
+
+ if (i2c->platform == VIAI2C_PLAT_WMT && pmsg->flags & I2C_M_NOSTART) {
+ val = readw(i2c->base + VIAI2C_REG_CR);
+ val |= VIAI2C_CR_CPU_RDY;
+ writew(val, i2c->base + VIAI2C_REG_CR);
+ }
+
+ if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
+ return -ETIMEDOUT;
+
+ return i2c->ret;
+}
+
+static int viai2c_read(struct viai2c *i2c, struct i2c_msg *pmsg, bool first)
+{
+ u16 val, tcr_val = i2c->tcr;
+
+ val = readw(i2c->base + VIAI2C_REG_CR);
+ val &= ~(VIAI2C_CR_TX_END | VIAI2C_CR_RX_END);
+
+ if (i2c->platform == VIAI2C_PLAT_WMT && !(pmsg->flags & I2C_M_NOSTART))
+ val |= VIAI2C_CR_CPU_RDY;
+
+ if (pmsg->len == 1)
+ val |= VIAI2C_CR_RX_END;
+
+ writew(val, i2c->base + VIAI2C_REG_CR);
+
+ reinit_completion(&i2c->complete);
+
+ tcr_val |= VIAI2C_TCR_READ | (pmsg->addr & VIAI2C_TCR_ADDR_MASK);
+
+ writew(tcr_val, i2c->base + VIAI2C_REG_TCR);
+
+ if ((i2c->platform == VIAI2C_PLAT_WMT && (pmsg->flags & I2C_M_NOSTART)) ||
+ (i2c->platform == VIAI2C_PLAT_ZHAOXIN && !first)) {
+ val = readw(i2c->base + VIAI2C_REG_CR);
+ val |= VIAI2C_CR_CPU_RDY;
+ writew(val, i2c->base + VIAI2C_REG_CR);
+ }
+
+ if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
+ return -ETIMEDOUT;
+
+ return i2c->ret;
+}
+
+int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct i2c_msg *pmsg;
+ int i;
+ int ret = 0;
+ struct viai2c *i2c = i2c_get_adapdata(adap);
+
+ i2c->mode = VIAI2C_BYTE_MODE;
+ for (i = 0; ret >= 0 && i < num; i++) {
+ pmsg = &msgs[i];
+ if (i2c->platform == VIAI2C_PLAT_WMT && !(pmsg->flags & I2C_M_NOSTART)) {
+ ret = viai2c_wait_bus_not_busy(i2c);
+ if (ret < 0)
+ return ret;
+ }
+
+ i2c->msg = pmsg;
+ i2c->xfered_len = 0;
+
+ if (pmsg->flags & I2C_M_RD)
+ ret = viai2c_read(i2c, pmsg, i == 0);
+ else
+ ret = viai2c_write(i2c, pmsg, (i + 1) == num);
+ }
+
+ return (ret < 0) ? ret : i;
+}
+EXPORT_SYMBOL_GPL(viai2c_xfer);
+
+/*
+ * Main process of the byte mode xfer
+ *
+ * Return value indicates whether the transfer is complete
+ * 1: all the data has been successfully transferred
+ * 0: there is still data that needs to be transferred
+ * -EIO: error occurred
+ */
+int viai2c_irq_xfer(struct viai2c *i2c)
+{
+ u16 val;
+ struct i2c_msg *msg = i2c->msg;
+ u8 read = msg->flags & I2C_M_RD;
+ void __iomem *base = i2c->base;
+
+ if (read) {
+ msg->buf[i2c->xfered_len] = readw(base + VIAI2C_REG_CDR) >> 8;
+
+ val = readw(base + VIAI2C_REG_CR) | VIAI2C_CR_CPU_RDY;
+ if (i2c->xfered_len == msg->len - 2)
+ val |= VIAI2C_CR_RX_END;
+ writew(val, base + VIAI2C_REG_CR);
+ } else {
+ val = readw(base + VIAI2C_REG_CSR);
+ if (val & VIAI2C_CSR_RCV_NOT_ACK)
+ return -EIO;
+
+ /* I2C_SMBUS_QUICK */
+ if (msg->len == 0) {
+ val = VIAI2C_CR_TX_END | VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE;
+ writew(val, base + VIAI2C_REG_CR);
+ return 1;
+ }
+
+ if ((i2c->xfered_len + 1) == msg->len) {
+ if (i2c->platform == VIAI2C_PLAT_WMT && !i2c->last)
+ writew(VIAI2C_CR_ENABLE, base + VIAI2C_REG_CR);
+ else if (i2c->platform == VIAI2C_PLAT_ZHAOXIN && i2c->last)
+ writeb(VIAI2C_CR_TX_END, base + VIAI2C_REG_CR);
+ } else {
+ writew(msg->buf[i2c->xfered_len + 1] & 0xFF, base + VIAI2C_REG_CDR);
+ writew(VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE, base + VIAI2C_REG_CR);
+ }
+ }
+
+ i2c->xfered_len++;
+
+ return i2c->xfered_len == msg->len;
+}
+EXPORT_SYMBOL_GPL(viai2c_irq_xfer);
+
+int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat)
+{
+ struct viai2c *i2c;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ i2c->platform = plat;
+
+ i2c->dev = &pdev->dev;
+ init_completion(&i2c->complete);
+ platform_set_drvdata(pdev, i2c);
+
+ *pi2c = i2c;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(viai2c_init);
+
+MODULE_DESCRIPTION("Via/Wondermedia/Zhaoxin I2C controller core");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-viai2c-common.h b/drivers/i2c/busses/i2c-viai2c-common.h
new file mode 100644
index 000000000000..00f17733223c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-common.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __I2C_VIAI2C_COMMON_H_
+#define __I2C_VIAI2C_COMMON_H_
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+/* REG_CR Bit fields */
+#define VIAI2C_REG_CR 0x00
+#define VIAI2C_CR_ENABLE BIT(0)
+#define VIAI2C_CR_RX_END BIT(1)
+#define VIAI2C_CR_TX_END BIT(2)
+#define VIAI2C_CR_CPU_RDY BIT(3)
+#define VIAI2C_CR_END_MASK GENMASK(2, 1)
+
+/* REG_TCR Bit fields */
+#define VIAI2C_REG_TCR 0x02
+#define VIAI2C_TCR_HS_MODE BIT(13)
+#define VIAI2C_TCR_READ BIT(14)
+#define VIAI2C_TCR_FAST BIT(15)
+#define VIAI2C_TCR_ADDR_MASK GENMASK(6, 0)
+
+/* REG_CSR Bit fields */
+#define VIAI2C_REG_CSR 0x04
+#define VIAI2C_CSR_RCV_NOT_ACK BIT(0)
+#define VIAI2C_CSR_RCV_ACK_MASK BIT(0)
+#define VIAI2C_CSR_READY_MASK BIT(1)
+
+/* REG_ISR Bit fields */
+#define VIAI2C_REG_ISR 0x06
+#define VIAI2C_ISR_NACK_ADDR BIT(0)
+#define VIAI2C_ISR_BYTE_END BIT(1)
+#define VIAI2C_ISR_SCL_TIMEOUT BIT(2)
+#define VIAI2C_ISR_MASK_ALL GENMASK(2, 0)
+
+/* REG_IMR Bit fields */
+#define VIAI2C_REG_IMR 0x08
+#define VIAI2C_IMR_BYTE BIT(1)
+#define VIAI2C_IMR_ENABLE_ALL GENMASK(2, 0)
+
+#define VIAI2C_REG_CDR 0x0A
+#define VIAI2C_REG_TR 0x0C
+#define VIAI2C_REG_MCR 0x0E
+
+#define VIAI2C_TIMEOUT (msecs_to_jiffies(1000))
+
+enum {
+ VIAI2C_PLAT_WMT,
+ VIAI2C_PLAT_ZHAOXIN
+};
+
+enum {
+ VIAI2C_BYTE_MODE,
+ VIAI2C_FIFO_MODE
+};
+
+struct viai2c {
+ struct i2c_adapter adapter;
+ struct completion complete;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ u16 tcr;
+ int irq;
+ u16 xfered_len;
+ struct i2c_msg *msg;
+ int ret;
+ bool last;
+ unsigned int mode;
+ unsigned int platform;
+ void *pltfm_priv;
+};
+
+int viai2c_wait_bus_not_busy(struct viai2c *i2c);
+int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);
+int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat);
+int viai2c_irq_xfer(struct viai2c *i2c);
+
+#endif
diff --git a/drivers/i2c/busses/i2c-viai2c-wmt.c b/drivers/i2c/busses/i2c-viai2c-wmt.c
new file mode 100644
index 000000000000..2cf3cc0165fb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-wmt.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Wondermedia I2C Controller Driver
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Derived from GPLv2+ licensed source:
+ * - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include "i2c-viai2c-common.h"
+
+#define REG_SLAVE_CR 0x10
+#define REG_SLAVE_SR 0x12
+#define REG_SLAVE_ISR 0x14
+#define REG_SLAVE_IMR 0x16
+#define REG_SLAVE_DR 0x18
+#define REG_SLAVE_TR 0x1A
+
+/* REG_TR */
+#define SCL_TIMEOUT(x) (((x) & 0xFF) << 8)
+#define TR_STD 0x0064
+#define TR_HS 0x0019
+
+/* REG_MCR */
+#define MCR_APB_96M 7
+#define MCR_APB_166M 12
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+ .xfer = viai2c_xfer,
+ .functionality = wmt_i2c_func,
+};
+
+static int wmt_i2c_reset_hardware(struct viai2c *i2c)
+{
+ int err;
+
+ err = clk_prepare_enable(i2c->clk);
+ if (err)
+ return dev_err_probe(i2c->dev, err, "failed to enable clock\n");
+
+ err = clk_set_rate(i2c->clk, 20000000);
+ if (err) {
+ clk_disable_unprepare(i2c->clk);
+ return dev_err_probe(i2c->dev, err, "failed to set clock = 20Mhz\n");
+ }
+
+ writew(0, i2c->base + VIAI2C_REG_CR);
+ writew(MCR_APB_166M, i2c->base + VIAI2C_REG_MCR);
+ writew(VIAI2C_ISR_MASK_ALL, i2c->base + VIAI2C_REG_ISR);
+ writew(VIAI2C_IMR_ENABLE_ALL, i2c->base + VIAI2C_REG_IMR);
+ writew(VIAI2C_CR_ENABLE, i2c->base + VIAI2C_REG_CR);
+ readw(i2c->base + VIAI2C_REG_CSR); /* read clear */
+ writew(VIAI2C_ISR_MASK_ALL, i2c->base + VIAI2C_REG_ISR);
+
+ if (i2c->tcr == VIAI2C_TCR_FAST)
+ writew(SCL_TIMEOUT(128) | TR_HS, i2c->base + VIAI2C_REG_TR);
+ else
+ writew(SCL_TIMEOUT(128) | TR_STD, i2c->base + VIAI2C_REG_TR);
+
+ return 0;
+}
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+ struct viai2c *i2c = data;
+ u8 status;
+
+ /* save the status and write-clear it */
+ status = readw(i2c->base + VIAI2C_REG_ISR);
+ writew(status, i2c->base + VIAI2C_REG_ISR);
+
+ i2c->ret = 0;
+ if (status & VIAI2C_ISR_NACK_ADDR)
+ i2c->ret = -EIO;
+
+ if (status & VIAI2C_ISR_SCL_TIMEOUT)
+ i2c->ret = -ETIMEDOUT;
+
+ if (!i2c->ret)
+ i2c->ret = viai2c_irq_xfer(i2c);
+
+ /* All the data has been successfully transferred or error occurred */
+ if (i2c->ret)
+ complete(&i2c->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct viai2c *i2c;
+ struct i2c_adapter *adap;
+ int err;
+ u32 clk_rate;
+
+ err = viai2c_init(pdev, &i2c, VIAI2C_PLAT_WMT);
+ if (err)
+ return err;
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0)
+ return i2c->irq;
+
+ err = devm_request_irq(&pdev->dev, i2c->irq, wmt_i2c_isr,
+ 0, pdev->name, i2c);
+ if (err)
+ return dev_err_probe(&pdev->dev, err,
+ "failed to request irq %i\n", i2c->irq);
+
+ i2c->clk = of_clk_get(np, 0);
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
+ "unable to request clock\n");
+
+ err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+ if (!err && clk_rate == I2C_MAX_FAST_MODE_FREQ)
+ i2c->tcr = VIAI2C_TCR_FAST;
+
+ adap = &i2c->adapter;
+ i2c_set_adapdata(adap, i2c);
+ strscpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+ adap->owner = THIS_MODULE;
+ adap->algo = &wmt_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
+
+ err = wmt_i2c_reset_hardware(i2c);
+ if (err)
+ return err;
+
+ err = i2c_add_adapter(adap);
+ if (err)
+ /* wmt_i2c_reset_hardware() enables i2c_dev->clk */
+ clk_disable_unprepare(i2c->clk);
+
+ return err;
+}
+
+static void wmt_i2c_remove(struct platform_device *pdev)
+{
+ struct viai2c *i2c = platform_get_drvdata(pdev);
+
+ /* Disable interrupts, clock and delete adapter */
+ writew(0, i2c->base + VIAI2C_REG_IMR);
+ clk_disable_unprepare(i2c->clk);
+ i2c_del_adapter(&i2c->adapter);
+}
+
+static const struct of_device_id wmt_i2c_dt_ids[] = {
+ { .compatible = "wm,wm8505-i2c" },
+ { /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+ .probe = wmt_i2c_probe,
+ .remove = wmt_i2c_remove,
+ .driver = {
+ .name = "wmt-i2c",
+ .of_match_table = wmt_i2c_dt_ids,
+ },
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C controller driver");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
diff --git a/drivers/i2c/busses/i2c-viai2c-zhaoxin.c b/drivers/i2c/busses/i2c-viai2c-zhaoxin.c
new file mode 100644
index 000000000000..95dc64902b7c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-zhaoxin.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright(c) 2024 Shanghai Zhaoxin Semiconductor Corporation.
+ * All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include "i2c-viai2c-common.h"
+
+/*
+ * registers
+ */
+/* Zhaoxin specific register bit fields */
+/* REG_CR Bit fields */
+#define ZXI2C_CR_MST_RST BIT(7)
+#define ZXI2C_CR_FIFO_MODE BIT(14)
+/* REG_ISR/IMR Bit fields */
+#define ZXI2C_IRQ_FIFONACK BIT(4)
+#define ZXI2C_IRQ_FIFOEND BIT(3)
+#define ZXI2C_IRQ_MASK (VIAI2C_ISR_MASK_ALL \
+ | ZXI2C_IRQ_FIFOEND \
+ | ZXI2C_IRQ_FIFONACK)
+/* Zhaoxin specific registers */
+#define ZXI2C_REG_CLK 0x10
+#define ZXI2C_CLK_50M BIT(0)
+#define ZXI2C_REG_REV 0x11
+#define ZXI2C_REG_HCR 0x12
+#define ZXI2C_HCR_RST_FIFO GENMASK(1, 0)
+#define ZXI2C_REG_HTDR 0x13
+#define ZXI2C_REG_HRDR 0x14
+#define ZXI2C_REG_HTLR 0x15
+#define ZXI2C_REG_HRLR 0x16
+#define ZXI2C_REG_HWCNTR 0x18
+#define ZXI2C_REG_HRCNTR 0x19
+
+/* parameters Constants */
+#define ZXI2C_GOLD_FSTP_100K 0xF3
+#define ZXI2C_GOLD_FSTP_400K 0x38
+#define ZXI2C_GOLD_FSTP_1M 0x13
+#define ZXI2C_GOLD_FSTP_3400K 0x37
+#define ZXI2C_HS_CTRL_CODE (0x08 << 8)
+
+#define ZXI2C_FIFO_SIZE 32
+
+struct viai2c_zhaoxin {
+ u8 hrv;
+ u16 tr;
+ u16 mcr;
+ u16 xfer_len;
+};
+
+static int viai2c_fifo_xfer(struct viai2c *i2c)
+{
+ u16 i;
+ u8 tmp;
+ struct i2c_msg *msg = i2c->msg;
+ void __iomem *base = i2c->base;
+ bool read = !!(msg->flags & I2C_M_RD);
+ struct viai2c_zhaoxin *priv = i2c->pltfm_priv;
+
+ /* reset fifo buffer */
+ tmp = ioread8(base + ZXI2C_REG_HCR);
+ iowrite8(tmp | ZXI2C_HCR_RST_FIFO, base + ZXI2C_REG_HCR);
+
+ /* set xfer len */
+ priv->xfer_len = min_t(u16, msg->len - i2c->xfered_len, ZXI2C_FIFO_SIZE);
+ if (read) {
+ iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HRLR);
+ } else {
+ iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HTLR);
+ /* set write data */
+ for (i = 0; i < priv->xfer_len; i++)
+ iowrite8(msg->buf[i2c->xfered_len + i], base + ZXI2C_REG_HTDR);
+ }
+
+ /* prepare to stop transmission */
+ if (priv->hrv && msg->len == (i2c->xfered_len + priv->xfer_len)) {
+ tmp = ioread8(base + VIAI2C_REG_CR);
+ tmp |= read ? VIAI2C_CR_RX_END : VIAI2C_CR_TX_END;
+ iowrite8(tmp, base + VIAI2C_REG_CR);
+ }
+
+ u16 tcr_val = i2c->tcr;
+
+ /* start transmission */
+ tcr_val |= read ? VIAI2C_TCR_READ : 0;
+ writew(tcr_val | msg->addr, base + VIAI2C_REG_TCR);
+
+ return 0;
+}
+
+static int viai2c_fifo_irq_xfer(struct viai2c *i2c)
+{
+ u16 i;
+ u8 tmp;
+ struct i2c_msg *msg = i2c->msg;
+ void __iomem *base = i2c->base;
+ bool read = !!(msg->flags & I2C_M_RD);
+ struct viai2c_zhaoxin *priv = i2c->pltfm_priv;
+
+ /* get the received data */
+ if (read)
+ for (i = 0; i < priv->xfer_len; i++)
+ msg->buf[i2c->xfered_len + i] = ioread8(base + ZXI2C_REG_HRDR);
+
+ i2c->xfered_len += priv->xfer_len;
+ if (i2c->xfered_len == msg->len)
+ return 1;
+
+ /* reset fifo buffer */
+ tmp = ioread8(base + ZXI2C_REG_HCR);
+ iowrite8(tmp | ZXI2C_HCR_RST_FIFO, base + ZXI2C_REG_HCR);
+
+ /* set xfer len */
+ priv->xfer_len = min_t(u16, msg->len - i2c->xfered_len, ZXI2C_FIFO_SIZE);
+ if (read) {
+ iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HRLR);
+ } else {
+ iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HTLR);
+ /* set write data */
+ for (i = 0; i < priv->xfer_len; i++)
+ iowrite8(msg->buf[i2c->xfered_len + i], base + ZXI2C_REG_HTDR);
+ }
+
+ /* prepare to stop transmission */
+ if (priv->hrv && msg->len == (i2c->xfered_len + priv->xfer_len)) {
+ tmp = ioread8(base + VIAI2C_REG_CR);
+ tmp |= read ? VIAI2C_CR_RX_END : VIAI2C_CR_TX_END;
+ iowrite8(tmp, base + VIAI2C_REG_CR);
+ }
+
+ /* continue transmission */
+ tmp = ioread8(base + VIAI2C_REG_CR);
+ iowrite8(tmp |= VIAI2C_CR_CPU_RDY, base + VIAI2C_REG_CR);
+
+ return 0;
+}
+
+static int zxi2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ u8 tmp;
+ int ret;
+ struct viai2c *i2c = (struct viai2c *)i2c_get_adapdata(adap);
+ struct viai2c_zhaoxin *priv = i2c->pltfm_priv;
+ void __iomem *base = i2c->base;
+
+ ret = viai2c_wait_bus_not_busy(i2c);
+ if (ret)
+ return ret;
+
+ tmp = ioread8(base + VIAI2C_REG_CR);
+ tmp &= ~(VIAI2C_CR_RX_END | VIAI2C_CR_TX_END);
+
+ if (num == 1 && msgs->len >= 2 && (priv->hrv || msgs->len <= ZXI2C_FIFO_SIZE)) {
+ /* enable fifo mode */
+ iowrite16(ZXI2C_CR_FIFO_MODE | tmp, base + VIAI2C_REG_CR);
+ /* clear irq status */
+ iowrite8(ZXI2C_IRQ_MASK, base + VIAI2C_REG_ISR);
+ /* enable fifo irq */
+ iowrite8(VIAI2C_ISR_NACK_ADDR | ZXI2C_IRQ_FIFOEND, base + VIAI2C_REG_IMR);
+
+ i2c->msg = msgs;
+ i2c->mode = VIAI2C_FIFO_MODE;
+ priv->xfer_len = 0;
+ i2c->xfered_len = 0;
+
+ viai2c_fifo_xfer(i2c);
+
+ if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
+ return -ETIMEDOUT;
+
+ ret = i2c->ret;
+ } else {
+ /* enable byte mode */
+ iowrite16(tmp, base + VIAI2C_REG_CR);
+ /* clear irq status */
+ iowrite8(ZXI2C_IRQ_MASK, base + VIAI2C_REG_ISR);
+ /* enable byte irq */
+ iowrite8(VIAI2C_ISR_NACK_ADDR | VIAI2C_IMR_BYTE, base + VIAI2C_REG_IMR);
+
+ ret = viai2c_xfer(adap, msgs, num);
+ if (ret == -ETIMEDOUT)
+ iowrite16(tmp | VIAI2C_CR_END_MASK, base + VIAI2C_REG_CR);
+ }
+ /* dis interrupt */
+ iowrite8(0, base + VIAI2C_REG_IMR);
+
+ return ret;
+}
+
+static u32 zxi2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm zxi2c_algorithm = {
+ .xfer = zxi2c_xfer,
+ .functionality = zxi2c_func,
+};
+
+static const struct i2c_adapter_quirks zxi2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
+static const u32 zxi2c_speed_params_table[][3] = {
+ /* speed, ZXI2C_TCR, ZXI2C_FSTP */
+ { I2C_MAX_STANDARD_MODE_FREQ, 0, ZXI2C_GOLD_FSTP_100K },
+ { I2C_MAX_FAST_MODE_FREQ, VIAI2C_TCR_FAST, ZXI2C_GOLD_FSTP_400K },
+ { I2C_MAX_FAST_MODE_PLUS_FREQ, VIAI2C_TCR_FAST, ZXI2C_GOLD_FSTP_1M },
+ { I2C_MAX_HIGH_SPEED_MODE_FREQ, VIAI2C_TCR_HS_MODE | VIAI2C_TCR_FAST,
+ ZXI2C_GOLD_FSTP_3400K },
+};
+
+static void zxi2c_set_bus_speed(struct viai2c *i2c)
+{
+ struct viai2c_zhaoxin *priv = i2c->pltfm_priv;
+
+ iowrite16(priv->tr, i2c->base + VIAI2C_REG_TR);
+ iowrite8(ZXI2C_CLK_50M, i2c->base + ZXI2C_REG_CLK);
+ iowrite16(priv->mcr, i2c->base + VIAI2C_REG_MCR);
+}
+
+static void zxi2c_get_bus_speed(struct viai2c *i2c)
+{
+ u8 i, count;
+ u8 fstp;
+ const u32 *params;
+ struct viai2c_zhaoxin *priv = i2c->pltfm_priv;
+ u32 acpi_speed = i2c_acpi_find_bus_speed(i2c->dev);
+
+ count = ARRAY_SIZE(zxi2c_speed_params_table);
+ for (i = 0; i < count; i++)
+ if (acpi_speed == zxi2c_speed_params_table[i][0])
+ break;
+ /* if not found, use 400k as default */
+ i = i < count ? i : 1;
+
+ params = zxi2c_speed_params_table[i];
+ fstp = ioread8(i2c->base + VIAI2C_REG_TR);
+ if (abs(fstp - params[2]) > 0x10) {
+ /*
+ * if BIOS setting value far from golden value,
+ * use golden value and warn user
+ */
+ dev_warn(i2c->dev, "FW FSTP[%x] might cause wrong timings, dropped\n", fstp);
+ priv->tr = params[2] | 0xff00;
+ } else {
+ priv->tr = fstp | 0xff00;
+ }
+
+ i2c->tcr = params[1];
+ priv->mcr = ioread16(i2c->base + VIAI2C_REG_MCR);
+ /* for Hs-mode, use 0x80 as controller code */
+ if (params[0] == I2C_MAX_HIGH_SPEED_MODE_FREQ)
+ priv->mcr |= ZXI2C_HS_CTRL_CODE;
+
+ dev_info(i2c->dev, "speed mode is %s\n", i2c_freq_mode_string(params[0]));
+}
+
+static irqreturn_t zxi2c_isr(int irq, void *data)
+{
+ struct viai2c *i2c = data;
+ u8 status;
+
+ /* save the status and write-clear it */
+ status = readw(i2c->base + VIAI2C_REG_ISR);
+ if (!status)
+ return IRQ_NONE;
+
+ writew(status, i2c->base + VIAI2C_REG_ISR);
+
+ i2c->ret = 0;
+ if (status & VIAI2C_ISR_NACK_ADDR)
+ i2c->ret = -EIO;
+
+ if (!i2c->ret) {
+ if (i2c->mode == VIAI2C_BYTE_MODE)
+ i2c->ret = viai2c_irq_xfer(i2c);
+ else
+ i2c->ret = viai2c_fifo_irq_xfer(i2c);
+ }
+
+ /* All the data has been successfully transferred or error occurred */
+ if (i2c->ret)
+ complete(&i2c->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int zxi2c_probe(struct platform_device *pdev)
+{
+ int error;
+ struct viai2c *i2c;
+ struct i2c_adapter *adap;
+ struct viai2c_zhaoxin *priv;
+
+ error = viai2c_init(pdev, &i2c, VIAI2C_PLAT_ZHAOXIN);
+ if (error)
+ return error;
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0)
+ return i2c->irq;
+
+ error = devm_request_irq(&pdev->dev, i2c->irq, zxi2c_isr,
+ IRQF_SHARED, pdev->name, i2c);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed to request irq %i\n", i2c->irq);
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ i2c->pltfm_priv = priv;
+
+ zxi2c_get_bus_speed(i2c);
+ zxi2c_set_bus_speed(i2c);
+
+ priv->hrv = ioread8(i2c->base + ZXI2C_REG_REV);
+
+ adap = &i2c->adapter;
+ adap->owner = THIS_MODULE;
+ adap->algo = &zxi2c_algorithm;
+ adap->quirks = &zxi2c_quirks;
+ adap->dev.parent = &pdev->dev;
+ ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
+ snprintf(adap->name, sizeof(adap->name), "zhaoxin-%s-%s",
+ dev_name(pdev->dev.parent), dev_name(i2c->dev));
+ i2c_set_adapdata(adap, i2c);
+
+ return devm_i2c_add_adapter(&pdev->dev, adap);
+}
+
+static int __maybe_unused zxi2c_resume(struct device *dev)
+{
+ struct viai2c *i2c = dev_get_drvdata(dev);
+
+ iowrite8(ZXI2C_CR_MST_RST, i2c->base + VIAI2C_REG_CR);
+ zxi2c_set_bus_speed(i2c);
+
+ return 0;
+}
+
+static const struct dev_pm_ops zxi2c_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, zxi2c_resume)
+};
+
+static const struct acpi_device_id zxi2c_acpi_match[] = {
+ {"IIC1D17", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, zxi2c_acpi_match);
+
+static struct platform_driver zxi2c_driver = {
+ .probe = zxi2c_probe,
+ .driver = {
+ .name = "i2c_zhaoxin",
+ .acpi_match_table = zxi2c_acpi_match,
+ .pm = &zxi2c_pm,
+ },
+};
+
+module_platform_driver(zxi2c_driver);
+
+MODULE_AUTHOR("HansHu@zhaoxin.com");
+MODULE_DESCRIPTION("Shanghai Zhaoxin IIC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 0dc45e12bb1d..c58843609107 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>,
Mark D. Studebaker <mdsxyz123@yahoo.com>
Copyright (C) 2005 - 2008 Jean Delvare <jdelvare@suse.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/*
@@ -236,7 +228,7 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
goto exit_unsupported;
if (read_write == I2C_SMBUS_READ)
outb_p(data->block[0], SMBHSTDAT0);
- /* Fall through */
+ fallthrough;
case I2C_SMBUS_BLOCK_DATA:
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
@@ -312,7 +304,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter vt596_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
};
@@ -338,30 +330,27 @@ static int vt596_probe(struct pci_dev *pdev,
SMBHSTCFG = 0x84;
} else {
/* no matches at all */
- dev_err(&pdev->dev, "Cannot configure "
- "SMBus I/O Base address\n");
- return -ENODEV;
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "Cannot configure "
+ "SMBus I/O Base address\n");
}
}
vt596_smba &= 0xfff0;
- if (vt596_smba == 0) {
- dev_err(&pdev->dev, "SMBus base address "
- "uninitialized - upgrade BIOS or use "
- "force_addr=0xaddr\n");
- return -ENODEV;
- }
+ if (vt596_smba == 0)
+ return dev_err_probe(&pdev->dev, -ENODEV, "SMBus base address "
+ "uninitialized - upgrade BIOS or use "
+ "force_addr=0xaddr\n");
found:
error = acpi_check_region(vt596_smba, 8, vt596_driver.name);
if (error)
return -ENODEV;
- if (!request_region(vt596_smba, 8, vt596_driver.name)) {
- dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n",
- vt596_smba);
- return -ENODEV;
- }
+ if (!request_region(vt596_smba, 8, vt596_driver.name))
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "SMBus region 0x%x already in use!\n",
+ vt596_smba);
pci_read_config_byte(pdev, SMBHSTCFG, &temp);
/* If force_addr is set, we program the new address here. Just to make
@@ -383,10 +372,10 @@ found:
pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
dev_info(&pdev->dev, "Enabling SMBus device\n");
} else {
- dev_err(&pdev->dev, "SMBUS: Error: Host SMBus "
- "controller not enabled! - upgrade BIOS or "
- "use force=1\n");
- error = -ENODEV;
+ error = dev_err_probe(&pdev->dev, -ENODEV,
+ "SMBUS: Error: Host SMBus "
+ "controller not enabled! - "
+ "upgrade BIOS or use force=1\n");
goto release_region;
}
}
@@ -497,9 +486,9 @@ static void __exit i2c_vt596_exit(void)
}
}
-MODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>, "
- "Mark D. Studebaker <mdsxyz123@yahoo.com> and "
- "Jean Delvare <jdelvare@suse.de>");
+MODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("vt82c596 SMBus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-viperboard.c b/drivers/i2c/busses/i2c-viperboard.c
index 7235c7302bb7..f596efcc291c 100644
--- a/drivers/i2c/busses/i2c-viperboard.c
+++ b/drivers/i2c/busses/i2c-viperboard.c
@@ -1,21 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Nano River Technologies viperboard i2c master driver
+ * Nano River Technologies viperboard i2c controller driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -208,7 +204,7 @@ static int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg)
/* copy the received data */
memcpy(msg->buf + start, rmsg, len1);
- /* second read transfer if neccessary */
+ /* second read transfer if necessary */
if (len2 > 0) {
ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2);
if (ret < 0)
@@ -278,14 +274,12 @@ static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
(struct vprbrd_i2c_addr_msg *)vb->buf;
struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf;
- dev_dbg(&i2c->dev, "master xfer %d messages:\n", num);
-
for (i = 0 ; i < num ; i++) {
pmsg = &msgs[i];
dev_dbg(&i2c->dev,
" %d: %s (flags %d) %d bytes to 0x%02x\n",
- i, pmsg->flags & I2C_M_RD ? "read" : "write",
+ i, str_read_write(pmsg->flags & I2C_M_RD),
pmsg->flags, pmsg->len, pmsg->addr);
mutex_lock(&vb->lock);
@@ -350,8 +344,8 @@ static u32 vprbrd_i2c_func(struct i2c_adapter *i2c)
/* This is the actual algorithm we define */
static const struct i2c_algorithm vprbrd_algorithm = {
- .master_xfer = vprbrd_i2c_xfer,
- .functionality = vprbrd_i2c_func,
+ .xfer = vprbrd_i2c_xfer,
+ .functionality = vprbrd_i2c_func,
};
static const struct i2c_adapter_quirks vprbrd_quirks = {
@@ -391,15 +385,13 @@ static int vprbrd_i2c_probe(struct platform_device *pdev)
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
VPRBRD_USB_TIMEOUT_MS);
- if (ret != 1) {
- dev_err(&pdev->dev, "failure setting i2c_bus_freq to %d\n",
- i2c_bus_freq);
- return -EIO;
- }
+ if (ret != 1)
+ return dev_err_probe(&pdev->dev, -EIO,
+ "failure setting i2c_bus_freq to %d\n",
+ i2c_bus_freq);
} else {
- dev_err(&pdev->dev,
- "invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
- return -EIO;
+ return dev_err_probe(&pdev->dev, -EIO,
+ "invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
}
vb_i2c->i2c.dev.parent = &pdev->dev;
@@ -412,18 +404,15 @@ static int vprbrd_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int vprbrd_i2c_remove(struct platform_device *pdev)
+static void vprbrd_i2c_remove(struct platform_device *pdev)
{
struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&vb_i2c->i2c);
-
- return 0;
}
static struct platform_driver vprbrd_i2c_driver = {
.driver.name = "viperboard-i2c",
- .driver.owner = THIS_MODULE,
.probe = vprbrd_i2c_probe,
.remove = vprbrd_i2c_remove,
};
@@ -468,6 +457,6 @@ static void __exit vprbrd_i2c_exit(void)
module_exit(vprbrd_i2c_exit);
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
-MODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard");
+MODULE_DESCRIPTION("I2C controller driver for Nano River Techs Viperboard");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:viperboard-i2c");
diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c
new file mode 100644
index 000000000000..af1381949f50
--- /dev/null
+++ b/drivers/i2c/busses/i2c-virtio.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Virtio I2C Bus Driver
+ *
+ * The Virtio I2C Specification:
+ * https://raw.githubusercontent.com/oasis-tcs/virtio-spec/master/virtio-i2c.tex
+ *
+ * Copyright (c) 2021 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_i2c.h>
+
+/**
+ * struct virtio_i2c - virtio I2C data
+ * @vdev: virtio device for this controller
+ * @adap: I2C adapter for this controller
+ * @vq: the virtio virtqueue for communication
+ */
+struct virtio_i2c {
+ struct virtio_device *vdev;
+ struct i2c_adapter adap;
+ struct virtqueue *vq;
+};
+
+/**
+ * struct virtio_i2c_req - the virtio I2C request structure
+ * @completion: completion of virtio I2C message
+ * @out_hdr: the OUT header of the virtio I2C message
+ * @buf: the buffer into which data is read, or from which it's written
+ * @in_hdr: the IN header of the virtio I2C message
+ */
+struct virtio_i2c_req {
+ struct completion completion;
+ struct virtio_i2c_out_hdr out_hdr ____cacheline_aligned;
+ uint8_t *buf ____cacheline_aligned;
+ struct virtio_i2c_in_hdr in_hdr ____cacheline_aligned;
+};
+
+static void virtio_i2c_msg_done(struct virtqueue *vq)
+{
+ struct virtio_i2c_req *req;
+ unsigned int len;
+
+ while ((req = virtqueue_get_buf(vq, &len)))
+ complete(&req->completion);
+}
+
+static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
+ struct virtio_i2c_req *reqs,
+ struct i2c_msg *msgs, int num)
+{
+ struct scatterlist *sgs[3], out_hdr, msg_buf, in_hdr;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int outcnt = 0, incnt = 0;
+
+ init_completion(&reqs[i].completion);
+
+ /*
+ * Only 7-bit mode supported for this moment. For the address
+ * format, Please check the Virtio I2C Specification.
+ */
+ reqs[i].out_hdr.addr = cpu_to_le16(msgs[i].addr << 1);
+
+ if (msgs[i].flags & I2C_M_RD)
+ reqs[i].out_hdr.flags |= cpu_to_le32(VIRTIO_I2C_FLAGS_M_RD);
+
+ if (i != num - 1)
+ reqs[i].out_hdr.flags |= cpu_to_le32(VIRTIO_I2C_FLAGS_FAIL_NEXT);
+
+ sg_init_one(&out_hdr, &reqs[i].out_hdr, sizeof(reqs[i].out_hdr));
+ sgs[outcnt++] = &out_hdr;
+
+ if (msgs[i].len) {
+ reqs[i].buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
+ if (!reqs[i].buf)
+ break;
+
+ sg_init_one(&msg_buf, reqs[i].buf, msgs[i].len);
+
+ if (msgs[i].flags & I2C_M_RD)
+ sgs[outcnt + incnt++] = &msg_buf;
+ else
+ sgs[outcnt++] = &msg_buf;
+ }
+
+ sg_init_one(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
+ sgs[outcnt + incnt++] = &in_hdr;
+
+ if (virtqueue_add_sgs(vq, sgs, outcnt, incnt, &reqs[i], GFP_KERNEL)) {
+ i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], false);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int virtio_i2c_complete_reqs(struct virtqueue *vq,
+ struct virtio_i2c_req *reqs,
+ struct i2c_msg *msgs, int num)
+{
+ bool failed = false;
+ int i, j = 0;
+
+ for (i = 0; i < num; i++) {
+ struct virtio_i2c_req *req = &reqs[i];
+
+ if (!failed) {
+ if (wait_for_completion_interruptible(&req->completion))
+ failed = true;
+ else if (req->in_hdr.status != VIRTIO_I2C_MSG_OK)
+ failed = true;
+ else
+ j++;
+ }
+
+ i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
+ }
+
+ return j;
+}
+
+static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct virtio_i2c *vi = i2c_get_adapdata(adap);
+ struct virtqueue *vq = vi->vq;
+ struct virtio_i2c_req *reqs;
+ int count;
+
+ reqs = kcalloc(num, sizeof(*reqs), GFP_KERNEL);
+ if (!reqs)
+ return -ENOMEM;
+
+ count = virtio_i2c_prepare_reqs(vq, reqs, msgs, num);
+ if (!count)
+ goto err_free;
+
+ /*
+ * For the case where count < num, i.e. we weren't able to queue all the
+ * msgs, ideally we should abort right away and return early, but some
+ * of the messages are already sent to the remote I2C controller and the
+ * virtqueue will be left in undefined state in that case. We kick the
+ * remote here to clear the virtqueue, so we can try another set of
+ * messages later on.
+ */
+ virtqueue_kick(vq);
+
+ count = virtio_i2c_complete_reqs(vq, reqs, msgs, count);
+
+err_free:
+ kfree(reqs);
+ return count;
+}
+
+static void virtio_i2c_del_vqs(struct virtio_device *vdev)
+{
+ virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
+}
+
+static int virtio_i2c_setup_vqs(struct virtio_i2c *vi)
+{
+ struct virtio_device *vdev = vi->vdev;
+
+ vi->vq = virtio_find_single_vq(vdev, virtio_i2c_msg_done, "msg");
+ return PTR_ERR_OR_ZERO(vi->vq);
+}
+
+static u32 virtio_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm virtio_algorithm = {
+ .xfer = virtio_i2c_xfer,
+ .functionality = virtio_i2c_func,
+};
+
+static int virtio_i2c_probe(struct virtio_device *vdev)
+{
+ struct virtio_i2c *vi;
+ int ret;
+
+ if (!virtio_has_feature(vdev, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST))
+ return dev_err_probe(&vdev->dev, -EINVAL,
+ "Zero-length request feature is mandatory\n");
+
+ vi = devm_kzalloc(&vdev->dev, sizeof(*vi), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vdev->priv = vi;
+ vi->vdev = vdev;
+
+ ret = virtio_i2c_setup_vqs(vi);
+ if (ret)
+ return ret;
+
+ vi->adap.owner = THIS_MODULE;
+ snprintf(vi->adap.name, sizeof(vi->adap.name),
+ "i2c_virtio at virtio bus %d", vdev->index);
+ vi->adap.algo = &virtio_algorithm;
+ vi->adap.dev.parent = &vdev->dev;
+ vi->adap.dev.of_node = vdev->dev.of_node;
+ i2c_set_adapdata(&vi->adap, vi);
+
+ /*
+ * Setup ACPI node for controlled devices which will be probed through
+ * ACPI.
+ */
+ ACPI_COMPANION_SET(&vi->adap.dev, ACPI_COMPANION(vdev->dev.parent));
+
+ ret = i2c_add_adapter(&vi->adap);
+ if (ret)
+ virtio_i2c_del_vqs(vdev);
+
+ return ret;
+}
+
+static void virtio_i2c_remove(struct virtio_device *vdev)
+{
+ struct virtio_i2c *vi = vdev->priv;
+
+ i2c_del_adapter(&vi->adap);
+ virtio_i2c_del_vqs(vdev);
+}
+
+static const struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_I2C_ADAPTER, VIRTIO_DEV_ANY_ID },
+ {}
+};
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+static int virtio_i2c_freeze(struct virtio_device *vdev)
+{
+ virtio_i2c_del_vqs(vdev);
+ return 0;
+}
+
+static int virtio_i2c_restore(struct virtio_device *vdev)
+{
+ return virtio_i2c_setup_vqs(vdev->priv);
+}
+
+static const unsigned int features[] = {
+ VIRTIO_I2C_F_ZERO_LENGTH_REQUEST,
+};
+
+static struct virtio_driver virtio_i2c_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .id_table = id_table,
+ .probe = virtio_i2c_probe,
+ .remove = virtio_i2c_remove,
+ .driver = {
+ .name = "i2c_virtio",
+ },
+ .freeze = pm_sleep_ptr(virtio_i2c_freeze),
+ .restore = pm_sleep_ptr(virtio_i2c_restore),
+};
+module_virtio_driver(virtio_i2c_driver);
+
+MODULE_AUTHOR("Jie Deng <jie.deng@intel.com>");
+MODULE_AUTHOR("Conghui Chen <conghui.chen@intel.com>");
+MODULE_DESCRIPTION("Virtio i2c bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
deleted file mode 100644
index fbd0fd59f312..000000000000
--- a/drivers/i2c/busses/i2c-wmt.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * Wondermedia I2C Master Mode Driver
- *
- * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
- *
- * Derived from GPLv2+ licensed source:
- * - Copyright (C) 2008 WonderMedia Technologies, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, or
- * (at your option) any later version. as published by the Free Software
- * Foundation
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/platform_device.h>
-
-#define REG_CR 0x00
-#define REG_TCR 0x02
-#define REG_CSR 0x04
-#define REG_ISR 0x06
-#define REG_IMR 0x08
-#define REG_CDR 0x0A
-#define REG_TR 0x0C
-#define REG_MCR 0x0E
-#define REG_SLAVE_CR 0x10
-#define REG_SLAVE_SR 0x12
-#define REG_SLAVE_ISR 0x14
-#define REG_SLAVE_IMR 0x16
-#define REG_SLAVE_DR 0x18
-#define REG_SLAVE_TR 0x1A
-
-/* REG_CR Bit fields */
-#define CR_TX_NEXT_ACK 0x0000
-#define CR_ENABLE 0x0001
-#define CR_TX_NEXT_NO_ACK 0x0002
-#define CR_TX_END 0x0004
-#define CR_CPU_RDY 0x0008
-#define SLAV_MODE_SEL 0x8000
-
-/* REG_TCR Bit fields */
-#define TCR_STANDARD_MODE 0x0000
-#define TCR_MASTER_WRITE 0x0000
-#define TCR_HS_MODE 0x2000
-#define TCR_MASTER_READ 0x4000
-#define TCR_FAST_MODE 0x8000
-#define TCR_SLAVE_ADDR_MASK 0x007F
-
-/* REG_ISR Bit fields */
-#define ISR_NACK_ADDR 0x0001
-#define ISR_BYTE_END 0x0002
-#define ISR_SCL_TIMEOUT 0x0004
-#define ISR_WRITE_ALL 0x0007
-
-/* REG_IMR Bit fields */
-#define IMR_ENABLE_ALL 0x0007
-
-/* REG_CSR Bit fields */
-#define CSR_RCV_NOT_ACK 0x0001
-#define CSR_RCV_ACK_MASK 0x0001
-#define CSR_READY_MASK 0x0002
-
-/* REG_TR */
-#define SCL_TIMEOUT(x) (((x) & 0xFF) << 8)
-#define TR_STD 0x0064
-#define TR_HS 0x0019
-
-/* REG_MCR */
-#define MCR_APB_96M 7
-#define MCR_APB_166M 12
-
-#define I2C_MODE_STANDARD 0
-#define I2C_MODE_FAST 1
-
-#define WMT_I2C_TIMEOUT (msecs_to_jiffies(1000))
-
-struct wmt_i2c_dev {
- struct i2c_adapter adapter;
- struct completion complete;
- struct device *dev;
- void __iomem *base;
- struct clk *clk;
- int mode;
- int irq;
- u16 cmd_status;
-};
-
-static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
-{
- unsigned long timeout;
-
- timeout = jiffies + WMT_I2C_TIMEOUT;
- while (!(readw(i2c_dev->base + REG_CSR) & CSR_READY_MASK)) {
- if (time_after(jiffies, timeout)) {
- dev_warn(i2c_dev->dev, "timeout waiting for bus ready\n");
- return -EBUSY;
- }
- msleep(20);
- }
-
- return 0;
-}
-
-static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
-{
- int ret = 0;
-
- if (i2c_dev->cmd_status & ISR_NACK_ADDR)
- ret = -EIO;
-
- if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
- ret = -ETIMEDOUT;
-
- return ret;
-}
-
-static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
- int last)
-{
- struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
- u16 val, tcr_val;
- int ret;
- unsigned long wait_result;
- int xfer_len = 0;
-
- if (!(pmsg->flags & I2C_M_NOSTART)) {
- ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
- if (ret < 0)
- return ret;
- }
-
- if (pmsg->len == 0) {
- /*
- * We still need to run through the while (..) once, so
- * start at -1 and break out early from the loop
- */
- xfer_len = -1;
- writew(0, i2c_dev->base + REG_CDR);
- } else {
- writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
- }
-
- if (!(pmsg->flags & I2C_M_NOSTART)) {
- val = readw(i2c_dev->base + REG_CR);
- val &= ~CR_TX_END;
- writew(val, i2c_dev->base + REG_CR);
-
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_CPU_RDY;
- writew(val, i2c_dev->base + REG_CR);
- }
-
- reinit_completion(&i2c_dev->complete);
-
- if (i2c_dev->mode == I2C_MODE_STANDARD)
- tcr_val = TCR_STANDARD_MODE;
- else
- tcr_val = TCR_FAST_MODE;
-
- tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
-
- writew(tcr_val, i2c_dev->base + REG_TCR);
-
- if (pmsg->flags & I2C_M_NOSTART) {
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_CPU_RDY;
- writew(val, i2c_dev->base + REG_CR);
- }
-
- while (xfer_len < pmsg->len) {
- wait_result = wait_for_completion_timeout(&i2c_dev->complete,
- msecs_to_jiffies(500));
-
- if (wait_result == 0)
- return -ETIMEDOUT;
-
- ret = wmt_check_status(i2c_dev);
- if (ret)
- return ret;
-
- xfer_len++;
-
- val = readw(i2c_dev->base + REG_CSR);
- if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
- dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
- return -EIO;
- }
-
- if (pmsg->len == 0) {
- val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
- writew(val, i2c_dev->base + REG_CR);
- break;
- }
-
- if (xfer_len == pmsg->len) {
- if (last != 1)
- writew(CR_ENABLE, i2c_dev->base + REG_CR);
- } else {
- writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
- REG_CDR);
- writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
- }
- }
-
- return 0;
-}
-
-static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
- int last)
-{
- struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
- u16 val, tcr_val;
- int ret;
- unsigned long wait_result;
- u32 xfer_len = 0;
-
- if (!(pmsg->flags & I2C_M_NOSTART)) {
- ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
- if (ret < 0)
- return ret;
- }
-
- val = readw(i2c_dev->base + REG_CR);
- val &= ~CR_TX_END;
- writew(val, i2c_dev->base + REG_CR);
-
- val = readw(i2c_dev->base + REG_CR);
- val &= ~CR_TX_NEXT_NO_ACK;
- writew(val, i2c_dev->base + REG_CR);
-
- if (!(pmsg->flags & I2C_M_NOSTART)) {
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_CPU_RDY;
- writew(val, i2c_dev->base + REG_CR);
- }
-
- if (pmsg->len == 1) {
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_TX_NEXT_NO_ACK;
- writew(val, i2c_dev->base + REG_CR);
- }
-
- reinit_completion(&i2c_dev->complete);
-
- if (i2c_dev->mode == I2C_MODE_STANDARD)
- tcr_val = TCR_STANDARD_MODE;
- else
- tcr_val = TCR_FAST_MODE;
-
- tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
-
- writew(tcr_val, i2c_dev->base + REG_TCR);
-
- if (pmsg->flags & I2C_M_NOSTART) {
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_CPU_RDY;
- writew(val, i2c_dev->base + REG_CR);
- }
-
- while (xfer_len < pmsg->len) {
- wait_result = wait_for_completion_timeout(&i2c_dev->complete,
- msecs_to_jiffies(500));
-
- if (!wait_result)
- return -ETIMEDOUT;
-
- ret = wmt_check_status(i2c_dev);
- if (ret)
- return ret;
-
- pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
- xfer_len++;
-
- if (xfer_len == pmsg->len - 1) {
- val = readw(i2c_dev->base + REG_CR);
- val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
- writew(val, i2c_dev->base + REG_CR);
- } else {
- val = readw(i2c_dev->base + REG_CR);
- val |= CR_CPU_RDY;
- writew(val, i2c_dev->base + REG_CR);
- }
- }
-
- return 0;
-}
-
-static int wmt_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg msgs[],
- int num)
-{
- struct i2c_msg *pmsg;
- int i, is_last;
- int ret = 0;
-
- for (i = 0; ret >= 0 && i < num; i++) {
- is_last = ((i + 1) == num);
-
- pmsg = &msgs[i];
- if (pmsg->flags & I2C_M_RD)
- ret = wmt_i2c_read(adap, pmsg, is_last);
- else
- ret = wmt_i2c_write(adap, pmsg, is_last);
- }
-
- return (ret < 0) ? ret : i;
-}
-
-static u32 wmt_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART;
-}
-
-static const struct i2c_algorithm wmt_i2c_algo = {
- .master_xfer = wmt_i2c_xfer,
- .functionality = wmt_i2c_func,
-};
-
-static irqreturn_t wmt_i2c_isr(int irq, void *data)
-{
- struct wmt_i2c_dev *i2c_dev = data;
-
- /* save the status and write-clear it */
- i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
- writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
-
- complete(&i2c_dev->complete);
-
- return IRQ_HANDLED;
-}
-
-static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
-{
- int err;
-
- err = clk_prepare_enable(i2c_dev->clk);
- if (err) {
- dev_err(i2c_dev->dev, "failed to enable clock\n");
- return err;
- }
-
- err = clk_set_rate(i2c_dev->clk, 20000000);
- if (err) {
- dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
- clk_disable_unprepare(i2c_dev->clk);
- return err;
- }
-
- writew(0, i2c_dev->base + REG_CR);
- writew(MCR_APB_166M, i2c_dev->base + REG_MCR);
- writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
- writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
- writew(CR_ENABLE, i2c_dev->base + REG_CR);
- readw(i2c_dev->base + REG_CSR); /* read clear */
- writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
-
- if (i2c_dev->mode == I2C_MODE_STANDARD)
- writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR);
- else
- writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR);
-
- return 0;
-}
-
-static int wmt_i2c_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct wmt_i2c_dev *i2c_dev;
- struct i2c_adapter *adap;
- struct resource *res;
- int err;
- u32 clk_rate;
-
- i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
- if (!i2c_dev)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(i2c_dev->base))
- return PTR_ERR(i2c_dev->base);
-
- i2c_dev->irq = irq_of_parse_and_map(np, 0);
- if (!i2c_dev->irq) {
- dev_err(&pdev->dev, "irq missing or invalid\n");
- return -EINVAL;
- }
-
- i2c_dev->clk = of_clk_get(np, 0);
- if (IS_ERR(i2c_dev->clk)) {
- dev_err(&pdev->dev, "unable to request clock\n");
- return PTR_ERR(i2c_dev->clk);
- }
-
- i2c_dev->mode = I2C_MODE_STANDARD;
- err = of_property_read_u32(np, "clock-frequency", &clk_rate);
- if ((!err) && (clk_rate == 400000))
- i2c_dev->mode = I2C_MODE_FAST;
-
- i2c_dev->dev = &pdev->dev;
-
- err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
- "i2c", i2c_dev);
- if (err) {
- dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
- return err;
- }
-
- adap = &i2c_dev->adapter;
- i2c_set_adapdata(adap, i2c_dev);
- strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
- adap->owner = THIS_MODULE;
- adap->algo = &wmt_i2c_algo;
- adap->dev.parent = &pdev->dev;
- adap->dev.of_node = pdev->dev.of_node;
-
- init_completion(&i2c_dev->complete);
-
- err = wmt_i2c_reset_hardware(i2c_dev);
- if (err) {
- dev_err(&pdev->dev, "error initializing hardware\n");
- return err;
- }
-
- err = i2c_add_adapter(adap);
- if (err)
- return err;
-
- platform_set_drvdata(pdev, i2c_dev);
-
- return 0;
-}
-
-static int wmt_i2c_remove(struct platform_device *pdev)
-{
- struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
-
- /* Disable interrupts, clock and delete adapter */
- writew(0, i2c_dev->base + REG_IMR);
- clk_disable_unprepare(i2c_dev->clk);
- i2c_del_adapter(&i2c_dev->adapter);
-
- return 0;
-}
-
-static const struct of_device_id wmt_i2c_dt_ids[] = {
- { .compatible = "wm,wm8505-i2c" },
- { /* Sentinel */ },
-};
-
-static struct platform_driver wmt_i2c_driver = {
- .probe = wmt_i2c_probe,
- .remove = wmt_i2c_remove,
- .driver = {
- .name = "wmt-i2c",
- .of_match_table = wmt_i2c_dt_ids,
- },
-};
-
-module_platform_driver(wmt_i2c_driver);
-
-MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
-MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c
index a7ac746018ad..b29dec66b2c3 100644
--- a/drivers/i2c/busses/i2c-xgene-slimpro.c
+++ b/drivers/i2c/busses/i2c-xgene-slimpro.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* X-Gene SLIMpro I2C Driver
*
@@ -5,22 +6,8 @@
* Author: Feng Kan <fkan@apm.com>
* Author: Hieu Le <hnle@apm.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
* This driver provides support for X-Gene SLIMpro I2C device access
* using the APM X-Gene SLIMpro mailbox driver.
- *
*/
#include <acpi/pcc.h>
#include <linux/acpi.h>
@@ -32,7 +19,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/version.h>
#define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */
#define MAILBOX_I2C_INDEX 0
@@ -105,25 +91,16 @@
#define SLIMPRO_IIC_MSG_DWORD_COUNT 3
-/* PCC related defines */
-#define PCC_SIGNATURE 0x50424300
-#define PCC_STS_CMD_COMPLETE BIT(0)
-#define PCC_STS_SCI_DOORBELL BIT(1)
-#define PCC_STS_ERR BIT(2)
-#define PCC_STS_PLAT_NOTIFY BIT(3)
-#define PCC_CMD_GENERATE_DB_INT BIT(15)
-
struct slimpro_i2c_dev {
struct i2c_adapter adapter;
struct device *dev;
struct mbox_chan *mbox_chan;
+ struct pcc_mbox_chan *pcc_chan;
struct mbox_client mbox_client;
int mbox_idx;
struct completion rd_complete;
u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */
u32 *resp_msg;
- phys_addr_t comm_base_addr;
- void *pcc_comm_addr;
};
#define to_slimpro_i2c_dev(cl) \
@@ -169,15 +146,16 @@ static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg)
static void slimpro_i2c_pcc_rx_cb(struct mbox_client *cl, void *msg)
{
struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl);
- struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct acpi_pcct_shared_memory __iomem *generic_comm_base =
+ ctx->pcc_chan->shmem;
/* Check if platform sends interrupt */
if (!xgene_word_tst_and_clr(&generic_comm_base->status,
- PCC_STS_SCI_DOORBELL))
+ PCC_STATUS_SCI_DOORBELL))
return;
if (xgene_word_tst_and_clr(&generic_comm_base->status,
- PCC_STS_CMD_COMPLETE)) {
+ PCC_STATUS_CMD_COMPLETE)) {
msg = generic_comm_base + 1;
/* Response message msg[1] contains the return value. */
@@ -190,7 +168,8 @@ static void slimpro_i2c_pcc_rx_cb(struct mbox_client *cl, void *msg)
static void slimpro_i2c_pcc_tx_prepare(struct slimpro_i2c_dev *ctx, u32 *msg)
{
- struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct acpi_pcct_shared_memory __iomem *generic_comm_base =
+ ctx->pcc_chan->shmem;
u32 *ptr = (void *)(generic_comm_base + 1);
u16 status;
int i;
@@ -199,10 +178,10 @@ static void slimpro_i2c_pcc_tx_prepare(struct slimpro_i2c_dev *ctx, u32 *msg)
cpu_to_le32(PCC_SIGNATURE | ctx->mbox_idx));
WRITE_ONCE(generic_comm_base->command,
- cpu_to_le16(SLIMPRO_MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INT));
+ cpu_to_le16(SLIMPRO_MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INTR));
status = le16_to_cpu(READ_ONCE(generic_comm_base->status));
- status &= ~PCC_STS_CMD_COMPLETE;
+ status &= ~PCC_STATUS_CMD_COMPLETE;
WRITE_ONCE(generic_comm_base->status, cpu_to_le16(status));
/* Copy the message to the PCC comm space */
@@ -321,6 +300,9 @@ static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip,
u32 msg[3];
int rc;
+ if (writelen > I2C_SMBUS_BLOCK_MAX)
+ return -EINVAL;
+
memcpy(ctx->dma_buffer, data, writelen);
paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen,
DMA_TO_DEVICE);
@@ -475,81 +457,38 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
cl->tx_block = true;
cl->rx_callback = slimpro_i2c_rx_cb;
ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX);
- if (IS_ERR(ctx->mbox_chan)) {
- dev_err(&pdev->dev, "i2c mailbox channel request failed\n");
- return PTR_ERR(ctx->mbox_chan);
- }
+ if (IS_ERR(ctx->mbox_chan))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ctx->mbox_chan),
+ "i2c mailbox channel request failed\n");
} else {
- struct acpi_pcct_hw_reduced *cppc_ss;
+ struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id;
- int version = XGENE_SLIMPRO_I2C_V1;
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
&pdev->dev);
if (!acpi_id)
return -EINVAL;
- version = (int)acpi_id->driver_data;
-
if (device_property_read_u32(&pdev->dev, "pcc-channel",
&ctx->mbox_idx))
ctx->mbox_idx = MAILBOX_I2C_INDEX;
cl->tx_block = false;
cl->rx_callback = slimpro_i2c_pcc_rx_cb;
- ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
- if (IS_ERR(ctx->mbox_chan)) {
- dev_err(&pdev->dev, "PCC mailbox channel request failed\n");
- return PTR_ERR(ctx->mbox_chan);
- }
+ pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
+ if (IS_ERR(pcc_chan))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pcc_chan),
+ "PCC mailbox channel request failed\n");
- /*
- * The PCC mailbox controller driver should
- * have parsed the PCCT (global table of all
- * PCC channels) and stored pointers to the
- * subspace communication region in con_priv.
- */
- cppc_ss = ctx->mbox_chan->con_priv;
- if (!cppc_ss) {
- dev_err(&pdev->dev, "PPC subspace not found\n");
- rc = -ENOENT;
- goto mbox_err;
- }
+ ctx->pcc_chan = pcc_chan;
+ ctx->mbox_chan = pcc_chan->mchan;
if (!ctx->mbox_chan->mbox->txdone_irq) {
- dev_err(&pdev->dev, "PCC IRQ not supported\n");
- rc = -ENOENT;
- goto mbox_err;
- }
-
- /*
- * This is the shared communication region
- * for the OS and Platform to communicate over.
- */
- ctx->comm_base_addr = cppc_ss->base_address;
- if (ctx->comm_base_addr) {
- if (version == XGENE_SLIMPRO_I2C_V2)
- ctx->pcc_comm_addr = memremap(
- ctx->comm_base_addr,
- cppc_ss->length,
- MEMREMAP_WT);
- else
- ctx->pcc_comm_addr = memremap(
- ctx->comm_base_addr,
- cppc_ss->length,
- MEMREMAP_WB);
- } else {
- dev_err(&pdev->dev, "Failed to get PCC comm region\n");
- rc = -ENOENT;
+ rc = dev_err_probe(&pdev->dev, -ENOENT,
+ "PCC IRQ not supported\n");
goto mbox_err;
}
- if (!ctx->pcc_comm_addr) {
- dev_err(&pdev->dev,
- "Failed to ioremap PCC comm region\n");
- rc = -ENOMEM;
- goto mbox_err;
- }
}
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
@@ -575,12 +514,12 @@ mbox_err:
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
- pcc_mbox_free_channel(ctx->mbox_chan);
+ pcc_mbox_free_channel(ctx->pcc_chan);
return rc;
}
-static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
+static void xgene_slimpro_i2c_remove(struct platform_device *pdev)
{
struct slimpro_i2c_dev *ctx = platform_get_drvdata(pdev);
@@ -589,9 +528,7 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
- pcc_mbox_free_channel(ctx->mbox_chan);
-
- return 0;
+ pcc_mbox_free_channel(ctx->pcc_chan);
}
static const struct of_device_id xgene_slimpro_i2c_dt_ids[] = {
@@ -611,7 +548,7 @@ MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids);
static struct platform_driver xgene_slimpro_i2c_driver = {
.probe = xgene_slimpro_i2c_probe,
- .remove = xgene_slimpro_i2c_remove,
+ .remove = xgene_slimpro_i2c_remove,
.driver = {
.name = "xgene-slimpro-i2c",
.of_match_table = of_match_ptr(xgene_slimpro_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 0c51c0ffdda9..28015d77599d 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c-xiic.c
* Copyright (c) 2002-2007 Xilinx Inc.
* Copyright (c) 2009-2010 Intel Corporation
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
* This code was implemented by Mocean Laboratories AB when porting linux
* to the automotive development board Russellville. The copyright holder
* as seen in the header is Intel corporation.
@@ -32,15 +23,19 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/wait.h>
+#include <linux/completion.h>
#include <linux/platform_data/i2c-xiic.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/spinlock.h>
#define DRIVER_NAME "xiic-i2c"
+#define DYNAMIC_MODE_READ_BROKEN_BIT BIT(0)
+#define SMBUS_BLOCK_READ_MIN_LEN 3
enum xilinx_i2c_state {
STATE_DONE,
@@ -53,54 +48,121 @@ enum xiic_endian {
BIG
};
+enum i2c_scl_freq {
+ REG_VALUES_100KHZ = 0,
+ REG_VALUES_400KHZ = 1,
+ REG_VALUES_1MHZ = 2
+};
+
/**
* struct xiic_i2c - Internal representation of the XIIC I2C bus
- * @base: Memory base of the HW registers
- * @wait: Wait queue for callers
- * @adap: Kernel adapter representation
- * @tx_msg: Messages from above to be sent
- * @lock: Mutual exclusion
- * @tx_pos: Current pos in TX message
- * @nmsgs: Number of messages in tx_msg
- * @state: See STATE_
- * @rx_msg: Current RX message
- * @rx_pos: Position within current RX message
+ * @dev: Pointer to device structure
+ * @base: Memory base of the HW registers
+ * @completion: Completion for callers
+ * @adap: Kernel adapter representation
+ * @tx_msg: Messages from above to be sent
+ * @lock: Mutual exclusion
+ * @tx_pos: Current pos in TX message
+ * @nmsgs: Number of messages in tx_msg
+ * @rx_msg: Current RX message
+ * @rx_pos: Position within current RX message
* @endianness: big/little-endian byte order
+ * @clk: Pointer to AXI4-lite input clock
+ * @state: See STATE_
+ * @singlemaster: Indicates bus is single master
+ * @dynamic: Mode of controller
+ * @prev_msg_tx: Previous message is Tx
+ * @quirks: To hold platform specific bug info
+ * @smbus_block_read: Flag to handle block read
+ * @input_clk: Input clock to I2C controller
+ * @i2c_clk: I2C SCL frequency
+ * @atomic: Mode of transfer
+ * @atomic_lock: Lock for atomic transfer mode
+ * @atomic_xfer_state: See STATE_
*/
struct xiic_i2c {
- struct device *dev;
- void __iomem *base;
- wait_queue_head_t wait;
- struct i2c_adapter adap;
- struct i2c_msg *tx_msg;
- struct mutex lock;
- unsigned int tx_pos;
- unsigned int nmsgs;
- enum xilinx_i2c_state state;
- struct i2c_msg *rx_msg;
- int rx_pos;
- enum xiic_endian endianness;
+ struct device *dev;
+ void __iomem *base;
+ struct completion completion;
+ struct i2c_adapter adap;
+ struct i2c_msg *tx_msg;
+ struct mutex lock;
+ unsigned int tx_pos;
+ unsigned int nmsgs;
+ struct i2c_msg *rx_msg;
+ int rx_pos;
+ enum xiic_endian endianness;
struct clk *clk;
+ enum xilinx_i2c_state state;
+ bool singlemaster;
+ bool dynamic;
+ bool prev_msg_tx;
+ u32 quirks;
+ bool smbus_block_read;
+ unsigned long input_clk;
+ unsigned int i2c_clk;
+ bool atomic;
+ spinlock_t atomic_lock; /* Lock for atomic transfer mode */
+ enum xilinx_i2c_state atomic_xfer_state;
};
+struct xiic_version_data {
+ u32 quirks;
+};
+
+/**
+ * struct timing_regs - AXI I2C timing registers that depend on I2C spec
+ * @tsusta: setup time for a repeated START condition
+ * @tsusto: setup time for a STOP condition
+ * @thdsta: hold time for a repeated START condition
+ * @tsudat: setup time for data
+ * @tbuf: bus free time between STOP and START
+ */
+struct timing_regs {
+ unsigned int tsusta;
+ unsigned int tsusto;
+ unsigned int thdsta;
+ unsigned int tsudat;
+ unsigned int tbuf;
+};
+
+/* Reg values in ns derived from I2C spec and AXI I2C PG for different frequencies */
+static const struct timing_regs timing_reg_values[] = {
+ { 5700, 5000, 4300, 550, 5000 }, /* Reg values for 100KHz */
+ { 900, 900, 900, 400, 1600 }, /* Reg values for 400KHz */
+ { 380, 380, 380, 170, 620 }, /* Reg values for 1MHz */
+};
#define XIIC_MSB_OFFSET 0
-#define XIIC_REG_OFFSET (0x100+XIIC_MSB_OFFSET)
+#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 */
+#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 */
+
+/*
+ * Timing register offsets from RegisterBase. These are used only for
+ * setting i2c clock frequency for the line.
+ */
+#define XIIC_TSUSTA_REG_OFFSET (0x28 + XIIC_REG_OFFSET) /* TSUSTA Register */
+#define XIIC_TSUSTO_REG_OFFSET (0x2C + XIIC_REG_OFFSET) /* TSUSTO Register */
+#define XIIC_THDSTA_REG_OFFSET (0x30 + XIIC_REG_OFFSET) /* THDSTA Register */
+#define XIIC_TSUDAT_REG_OFFSET (0x34 + XIIC_REG_OFFSET) /* TSUDAT Register */
+#define XIIC_TBUF_REG_OFFSET (0x38 + XIIC_REG_OFFSET) /* TBUF Register */
+#define XIIC_THIGH_REG_OFFSET (0x3C + XIIC_REG_OFFSET) /* THIGH Register */
+#define XIIC_TLOW_REG_OFFSET (0x40 + XIIC_REG_OFFSET) /* TLOW Register */
+#define XIIC_THDDAT_REG_OFFSET (0x44 + XIIC_REG_OFFSET) /* THDDAT Register */
/* Control Register masks */
#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */
@@ -149,6 +211,9 @@ struct xiic_i2c {
#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */
#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */
+/* Dynamic mode constants */
+#define MAX_READ_LENGTH_DYNAMIC 255 /* Max length for dynamic read */
+
/*
* The following constants define the register offsets for the Interrupt
* registers. There are some holes in the memory map for reserved addresses
@@ -163,6 +228,13 @@ struct xiic_i2c {
#define XIIC_RESET_MASK 0xAUL
#define XIIC_PM_TIMEOUT 1000 /* ms */
+/* timeout waiting for the controller to respond */
+#define XIIC_I2C_TIMEOUT (msecs_to_jiffies(1000))
+/* timeout waiting for the controller finish transfers */
+#define XIIC_XFER_TIMEOUT (msecs_to_jiffies(10000))
+/* timeout waiting for the controller finish transfers in micro seconds */
+#define XIIC_XFER_TIMEOUT_US 10000000
+
/*
* The following constant is used for the device global interrupt enable
* register, to enable all interrupts for the device, this is the only bit
@@ -173,9 +245,32 @@ struct xiic_i2c {
#define xiic_tx_space(i2c) ((i2c)->tx_msg->len - (i2c)->tx_pos)
#define xiic_rx_space(i2c) ((i2c)->rx_msg->len - (i2c)->rx_pos)
-static void xiic_start_xfer(struct xiic_i2c *i2c);
+static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num);
static void __xiic_start_xfer(struct xiic_i2c *i2c);
+static int xiic_i2c_runtime_suspend(struct device *dev)
+{
+ struct xiic_i2c *i2c = dev_get_drvdata(dev);
+
+ clk_disable(i2c->clk);
+
+ return 0;
+}
+
+static int xiic_i2c_runtime_resume(struct device *dev)
+{
+ struct xiic_i2c *i2c = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(i2c->clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
/*
* For the register read and write functions, a little-endian and big-endian
* version are necessary. Endianness is detected during the probe function.
@@ -233,18 +328,21 @@ static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg)
static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask)
{
u32 ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
+
xiic_setreg32(i2c, XIIC_IIER_OFFSET, ier & ~mask);
}
static inline void xiic_irq_en(struct xiic_i2c *i2c, u32 mask)
{
u32 ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
+
xiic_setreg32(i2c, XIIC_IIER_OFFSET, ier | mask);
}
static inline void xiic_irq_clr(struct xiic_i2c *i2c, u32 mask)
{
u32 isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
+
xiic_setreg32(i2c, XIIC_IISR_OFFSET, isr & mask);
}
@@ -254,19 +352,140 @@ static inline void xiic_irq_clr_en(struct xiic_i2c *i2c, u32 mask)
xiic_irq_en(i2c, mask);
}
-static void xiic_clear_rx_fifo(struct xiic_i2c *i2c)
+static int xiic_clear_rx_fifo(struct xiic_i2c *i2c)
{
u8 sr;
+ unsigned long timeout;
+
+ timeout = jiffies + XIIC_I2C_TIMEOUT;
for (sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET);
!(sr & XIIC_SR_RX_FIFO_EMPTY_MASK);
- sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET))
+ sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)) {
xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET);
+ if (time_after(jiffies, timeout)) {
+ dev_err(i2c->dev, "Failed to clear rx fifo\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int xiic_wait_tx_empty(struct xiic_i2c *i2c)
+{
+ u8 isr;
+ unsigned long timeout;
+
+ timeout = jiffies + XIIC_I2C_TIMEOUT;
+ for (isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
+ !(isr & XIIC_INTR_TX_EMPTY_MASK);
+ isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET)) {
+ if (time_after(jiffies, timeout)) {
+ dev_err(i2c->dev, "Timeout waiting at Tx empty\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * xiic_setclk - Sets the configured clock rate
+ * @i2c: Pointer to the xiic device structure
+ *
+ * The timing register values are calculated according to the input clock
+ * frequency and configured scl frequency. For details, please refer the
+ * AXI I2C PG and NXP I2C Spec.
+ * Supported frequencies are 100KHz, 400KHz and 1MHz.
+ *
+ * Return: 0 on success (Supported frequency selected or not configurable in SW)
+ * -EINVAL on failure (scl frequency not supported or THIGH is 0)
+ */
+static int xiic_setclk(struct xiic_i2c *i2c)
+{
+ unsigned int clk_in_mhz;
+ unsigned int index = 0;
+ u32 reg_val;
+
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent,
+ "%s entry, i2c->input_clk: %ld, i2c->i2c_clk: %d\n",
+ __func__, i2c->input_clk, i2c->i2c_clk);
+
+ /* If not specified in DT, do not configure in SW. Rely only on Vivado design */
+ if (!i2c->i2c_clk || !i2c->input_clk)
+ return 0;
+
+ clk_in_mhz = DIV_ROUND_UP(i2c->input_clk, 1000000);
+
+ switch (i2c->i2c_clk) {
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ index = REG_VALUES_1MHZ;
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ index = REG_VALUES_400KHZ;
+ break;
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ index = REG_VALUES_100KHZ;
+ break;
+ default:
+ dev_warn(i2c->adap.dev.parent, "Unsupported scl frequency\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Value to be stored in a register is the number of clock cycles required
+ * for the time duration. So the time is divided by the input clock time
+ * period to get the number of clock cycles required. Refer Xilinx AXI I2C
+ * PG document and I2C specification for further details.
+ */
+
+ /* THIGH - Depends on SCL clock frequency(i2c_clk) as below */
+ reg_val = (DIV_ROUND_UP(i2c->input_clk, 2 * i2c->i2c_clk)) - 7;
+ if (reg_val == 0)
+ return -EINVAL;
+
+ xiic_setreg32(i2c, XIIC_THIGH_REG_OFFSET, reg_val - 1);
+
+ /* TLOW - Value same as THIGH */
+ xiic_setreg32(i2c, XIIC_TLOW_REG_OFFSET, reg_val - 1);
+
+ /* TSUSTA */
+ reg_val = (timing_reg_values[index].tsusta * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUSTA_REG_OFFSET, reg_val - 1);
+
+ /* TSUSTO */
+ reg_val = (timing_reg_values[index].tsusto * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUSTO_REG_OFFSET, reg_val - 1);
+
+ /* THDSTA */
+ reg_val = (timing_reg_values[index].thdsta * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_THDSTA_REG_OFFSET, reg_val - 1);
+
+ /* TSUDAT */
+ reg_val = (timing_reg_values[index].tsudat * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUDAT_REG_OFFSET, reg_val - 1);
+
+ /* TBUF */
+ reg_val = (timing_reg_values[index].tbuf * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TBUF_REG_OFFSET, reg_val - 1);
+
+ /* THDDAT */
+ xiic_setreg32(i2c, XIIC_THDDAT_REG_OFFSET, 1);
+
+ return 0;
}
-static void xiic_reinit(struct xiic_i2c *i2c)
+static int xiic_reinit(struct xiic_i2c *i2c)
{
+ int ret;
+
xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK);
+ ret = xiic_setclk(i2c);
+ if (ret)
+ return ret;
+
/* Set receive Fifo depth to maximum (zero based). */
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1);
@@ -277,12 +496,17 @@ static void xiic_reinit(struct xiic_i2c *i2c)
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK);
/* make sure RX fifo is empty */
- xiic_clear_rx_fifo(i2c);
+ ret = xiic_clear_rx_fifo(i2c);
+ if (ret)
+ return ret;
/* Enable interrupts */
- xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
+ if (!i2c->atomic)
+ xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
xiic_irq_clr_en(i2c, XIIC_INTR_ARB_LOST_MASK);
+
+ return 0;
}
static void xiic_deinit(struct xiic_i2c *i2c)
@@ -296,29 +520,152 @@ static void xiic_deinit(struct xiic_i2c *i2c)
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr & ~XIIC_CR_ENABLE_DEVICE_MASK);
}
+static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
+{
+ u8 rxmsg_len, rfd_set = 0;
+
+ /*
+ * Clear the I2C_M_RECV_LEN flag to avoid setting
+ * message length again
+ */
+ i2c->rx_msg->flags &= ~I2C_M_RECV_LEN;
+
+ /* Set smbus_block_read flag to identify in isr */
+ i2c->smbus_block_read = true;
+
+ /* Read byte from rx fifo and set message length */
+ rxmsg_len = xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET);
+
+ i2c->rx_msg->buf[i2c->rx_pos++] = rxmsg_len;
+
+ /* Check if received length is valid */
+ if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) {
+ /* Set Receive fifo depth */
+ if (rxmsg_len > IIC_RX_FIFO_DEPTH) {
+ /*
+ * When Rx msg len greater than or equal to Rx fifo capacity
+ * Receive fifo depth should set to Rx fifo capacity minus 1
+ */
+ rfd_set = IIC_RX_FIFO_DEPTH - 1;
+ i2c->rx_msg->len = rxmsg_len + 1;
+ } else if ((rxmsg_len == 1) ||
+ (rxmsg_len == 0)) {
+ /*
+ * Minimum of 3 bytes required to exit cleanly. 1 byte
+ * already received, Second byte is being received. Have
+ * to set NACK in read_rx before receiving the last byte
+ */
+ rfd_set = 0;
+ i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN;
+ } else {
+ /*
+ * When Rx msg len less than Rx fifo capacity
+ * Receive fifo depth should set to Rx msg len minus 2
+ */
+ rfd_set = rxmsg_len - 2;
+ i2c->rx_msg->len = rxmsg_len + 1;
+ }
+ xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
+
+ return;
+ }
+
+ /* Invalid message length, trigger STATE_ERROR with tx_msg_len in ISR */
+ i2c->tx_msg->len = 3;
+ i2c->smbus_block_read = false;
+ dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n");
+}
+
static void xiic_read_rx(struct xiic_i2c *i2c)
{
- u8 bytes_in_fifo;
+ u8 bytes_in_fifo, cr = 0, bytes_to_read = 0;
+ u32 bytes_rem = 0;
int i;
bytes_in_fifo = xiic_getreg8(i2c, XIIC_RFO_REG_OFFSET) + 1;
- dev_dbg(i2c->adap.dev.parent,
- "%s entry, bytes in fifo: %d, msg: %d, SR: 0x%x, CR: 0x%x\n",
- __func__, bytes_in_fifo, xiic_rx_space(i2c),
- xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
- xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent,
+ "%s entry, bytes in fifo: %d, rem: %d, SR: 0x%x, CR: 0x%x\n",
+ __func__, bytes_in_fifo, xiic_rx_space(i2c),
+ xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
+ xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
if (bytes_in_fifo > xiic_rx_space(i2c))
bytes_in_fifo = xiic_rx_space(i2c);
- for (i = 0; i < bytes_in_fifo; i++)
+ bytes_to_read = bytes_in_fifo;
+
+ if (!i2c->dynamic) {
+ bytes_rem = xiic_rx_space(i2c) - bytes_in_fifo;
+
+ /* Set msg length if smbus_block_read */
+ if (i2c->rx_msg->flags & I2C_M_RECV_LEN) {
+ xiic_smbus_block_read_setup(i2c);
+ return;
+ }
+
+ if (bytes_rem > IIC_RX_FIFO_DEPTH) {
+ bytes_to_read = bytes_in_fifo;
+ } else if (bytes_rem > 1) {
+ bytes_to_read = bytes_rem - 1;
+ } else if (bytes_rem == 1) {
+ bytes_to_read = 1;
+ /* Set NACK in CR to indicate slave transmitter */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr |
+ XIIC_CR_NO_ACK_MASK);
+ } else if (bytes_rem == 0) {
+ bytes_to_read = bytes_in_fifo;
+
+ /* Generate stop on the bus if it is last message */
+ if (i2c->nmsgs == 1) {
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
+ ~XIIC_CR_MSMS_MASK);
+ }
+
+ /* Make TXACK=0, clean up for next transaction */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
+ ~XIIC_CR_NO_ACK_MASK);
+ }
+ }
+
+ /* Read the fifo */
+ for (i = 0; i < bytes_to_read; i++) {
i2c->rx_msg->buf[i2c->rx_pos++] =
xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET);
+ }
- xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET,
- (xiic_rx_space(i2c) > IIC_RX_FIFO_DEPTH) ?
- IIC_RX_FIFO_DEPTH - 1 : xiic_rx_space(i2c) - 1);
+ if (i2c->dynamic) {
+ u8 bytes;
+
+ /* Receive remaining bytes if less than fifo depth */
+ bytes = min_t(u8, xiic_rx_space(i2c), IIC_RX_FIFO_DEPTH);
+ bytes--;
+ xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, bytes);
+ }
+}
+
+static bool xiic_error_check(struct xiic_i2c *i2c)
+{
+ bool status = false;
+ u32 pend, isr, ier;
+
+ isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
+ ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
+ pend = isr & ier;
+
+ if ((pend & XIIC_INTR_ARB_LOST_MASK) ||
+ ((pend & XIIC_INTR_TX_ERROR_MASK) &&
+ !(pend & XIIC_INTR_RX_FULL_MASK))) {
+ xiic_reinit(i2c);
+ status = true;
+ if (i2c->tx_msg || i2c->rx_msg)
+ i2c->atomic_xfer_state = STATE_ERROR;
+ }
+ return status;
}
static int xiic_tx_fifo_space(struct xiic_i2c *i2c)
@@ -334,27 +681,48 @@ static void xiic_fill_tx_fifo(struct xiic_i2c *i2c)
len = (len > fifo_space) ? fifo_space : len;
- dev_dbg(i2c->adap.dev.parent, "%s entry, len: %d, fifo space: %d\n",
- __func__, len, fifo_space);
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent, "%s entry, len: %d, fifo space: %d\n",
+ __func__, len, fifo_space);
while (len--) {
u16 data = i2c->tx_msg->buf[i2c->tx_pos++];
- if ((xiic_tx_space(i2c) == 0) && (i2c->nmsgs == 1)) {
+
+ if (!xiic_tx_space(i2c) && i2c->nmsgs == 1) {
/* last message in transfer -> STOP */
- data |= XIIC_TX_DYN_STOP_MASK;
- dev_dbg(i2c->adap.dev.parent, "%s TX STOP\n", __func__);
+ if (i2c->dynamic) {
+ data |= XIIC_TX_DYN_STOP_MASK;
+ } else {
+ u8 cr;
+ int status;
+
+ /* Wait till FIFO is empty so STOP is sent last */
+ status = xiic_wait_tx_empty(i2c);
+ if (status)
+ return;
+
+ /* Write to CR to stop */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
+ ~XIIC_CR_MSMS_MASK);
+ }
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent, "%s TX STOP\n", __func__);
}
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
+
+ if (i2c->atomic && xiic_error_check(i2c))
+ return;
}
}
-static void xiic_wakeup(struct xiic_i2c *i2c, int code)
+static void xiic_wakeup(struct xiic_i2c *i2c, enum xilinx_i2c_state code)
{
i2c->tx_msg = NULL;
i2c->rx_msg = NULL;
i2c->nmsgs = 0;
i2c->state = code;
- wake_up(&i2c->wait);
+ complete(&i2c->completion);
}
static irqreturn_t xiic_process(int irq, void *dev_id)
@@ -362,6 +730,10 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
struct xiic_i2c *i2c = dev_id;
u32 pend, isr, ier;
u32 clr = 0;
+ int xfer_more = 0;
+ int wakeup_req = 0;
+ enum xilinx_i2c_state wakeup_code = STATE_DONE;
+ int ret;
/* Get the interrupt Status from the IPIF. There is no clearing of
* interrupts in the IPIF. Interrupts must be cleared at the source.
@@ -378,12 +750,14 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
dev_dbg(i2c->adap.dev.parent, "%s: SR: 0x%x, msg: %p, nmsgs: %d\n",
__func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
i2c->tx_msg, i2c->nmsgs);
-
+ dev_dbg(i2c->adap.dev.parent, "%s, ISR: 0x%x, CR: 0x%x\n",
+ __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
+ xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
/* Service requesting interrupt */
if ((pend & XIIC_INTR_ARB_LOST_MASK) ||
- ((pend & XIIC_INTR_TX_ERROR_MASK) &&
- !(pend & XIIC_INTR_RX_FULL_MASK))) {
+ ((pend & XIIC_INTR_TX_ERROR_MASK) &&
+ !(pend & XIIC_INTR_RX_FULL_MASK))) {
/* bus arbritration lost, or...
* Transmit error _OR_ RX completed
* if this happens when RX_FULL is not set
@@ -396,12 +770,20 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
* fifos and the next message is a TX with len 0 (only addr)
* reset the IP instead of just flush fifos
*/
- xiic_reinit(i2c);
+ ret = xiic_reinit(i2c);
+ if (ret < 0)
+ dev_dbg(i2c->adap.dev.parent, "reinit failed\n");
- if (i2c->rx_msg)
- xiic_wakeup(i2c, STATE_ERROR);
- if (i2c->tx_msg)
- xiic_wakeup(i2c, STATE_ERROR);
+ if (i2c->rx_msg) {
+ wakeup_req = 1;
+ wakeup_code = STATE_ERROR;
+ }
+ if (i2c->tx_msg) {
+ wakeup_req = 1;
+ wakeup_code = STATE_ERROR;
+ }
+ /* don't try to handle other events */
+ goto out;
}
if (pend & XIIC_INTR_RX_FULL_MASK) {
/* Receive register/FIFO is full */
@@ -435,27 +817,10 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
i2c->tx_msg++;
dev_dbg(i2c->adap.dev.parent,
"%s will start next...\n", __func__);
-
- __xiic_start_xfer(i2c);
+ xfer_more = 1;
}
}
}
- if (pend & XIIC_INTR_BNB_MASK) {
- /* IIC bus has transitioned to not busy */
- clr |= XIIC_INTR_BNB_MASK;
-
- /* The bus is not busy, disable BusNotBusy interrupt */
- xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK);
-
- if (!i2c->tx_msg)
- goto out;
-
- if ((i2c->nmsgs == 1) && !i2c->rx_msg &&
- xiic_tx_space(i2c) == 0)
- xiic_wakeup(i2c, STATE_DONE);
- else
- xiic_wakeup(i2c, STATE_ERROR);
- }
if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) {
/* Transmit register/FIFO is empty or ½ empty */
@@ -468,17 +833,20 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
goto out;
}
- xiic_fill_tx_fifo(i2c);
-
- /* current message sent and there is space in the fifo */
- if (!xiic_tx_space(i2c) && xiic_tx_fifo_space(i2c) >= 2) {
+ if (xiic_tx_space(i2c)) {
+ xiic_fill_tx_fifo(i2c);
+ } else {
+ /* current message fully written */
dev_dbg(i2c->adap.dev.parent,
"%s end of message sent, nmsgs: %d\n",
__func__, i2c->nmsgs);
- if (i2c->nmsgs > 1) {
+ /* Don't move onto the next message until the TX FIFO empties,
+ * to ensure that a NAK is not missed.
+ */
+ if (i2c->nmsgs > 1 && (pend & XIIC_INTR_TX_EMPTY_MASK)) {
i2c->nmsgs--;
i2c->tx_msg++;
- __xiic_start_xfer(i2c);
+ xfer_more = 1;
} else {
xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
@@ -486,16 +854,45 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
"%s Got TX IRQ but no more to do...\n",
__func__);
}
- } else if (!xiic_tx_space(i2c) && (i2c->nmsgs == 1))
- /* current frame is sent and is last,
- * make sure to disable tx half
- */
- xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
+ }
}
+
+ if (pend & XIIC_INTR_BNB_MASK) {
+ /* IIC bus has transitioned to not busy */
+ clr |= XIIC_INTR_BNB_MASK;
+
+ /* The bus is not busy, disable BusNotBusy interrupt */
+ xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK);
+
+ if (i2c->tx_msg && i2c->smbus_block_read) {
+ i2c->smbus_block_read = false;
+ /* Set requested message len=1 to indicate STATE_DONE */
+ i2c->tx_msg->len = 1;
+ }
+
+ if (!i2c->tx_msg)
+ goto out;
+
+ wakeup_req = 1;
+
+ if (i2c->nmsgs == 1 && !i2c->rx_msg &&
+ xiic_tx_space(i2c) == 0)
+ wakeup_code = STATE_DONE;
+ else
+ wakeup_code = STATE_ERROR;
+ }
+
out:
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
+ if (xfer_more)
+ __xiic_start_xfer(i2c);
+ if (wakeup_req)
+ xiic_wakeup(i2c, wakeup_code);
+
+ WARN_ON(xfer_more && wakeup_req);
+
mutex_unlock(&i2c->lock);
return IRQ_HANDLED;
}
@@ -507,58 +904,164 @@ static int xiic_bus_busy(struct xiic_i2c *i2c)
return (sr & XIIC_SR_BUS_BUSY_MASK) ? -EBUSY : 0;
}
-static int xiic_busy(struct xiic_i2c *i2c)
+static int xiic_wait_not_busy(struct xiic_i2c *i2c)
{
int tries = 3;
int err;
- if (i2c->tx_msg)
- return -EBUSY;
-
/* for instance if previous transfer was terminated due to TX error
* it might be that the bus is on it's way to become available
* give it at most 3 ms to wake
*/
err = xiic_bus_busy(i2c);
while (err && tries--) {
- msleep(1);
+ if (i2c->atomic)
+ udelay(1000);
+ else
+ usleep_range(1000, 1100);
err = xiic_bus_busy(i2c);
}
return err;
}
+static void xiic_recv_atomic(struct xiic_i2c *i2c)
+{
+ while (xiic_rx_space(i2c)) {
+ if (xiic_getreg32(i2c, XIIC_IISR_OFFSET) & XIIC_INTR_RX_FULL_MASK) {
+ xiic_read_rx(i2c);
+
+ /* Clear Rx full and Tx error interrupts. */
+ xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK |
+ XIIC_INTR_TX_ERROR_MASK);
+ }
+ if (xiic_error_check(i2c))
+ return;
+ }
+
+ i2c->rx_msg = NULL;
+ xiic_irq_clr_en(i2c, XIIC_INTR_TX_ERROR_MASK);
+
+ /* send next message if this wasn't the last. */
+ if (i2c->nmsgs > 1) {
+ i2c->nmsgs--;
+ i2c->tx_msg++;
+ __xiic_start_xfer(i2c);
+ }
+}
+
static void xiic_start_recv(struct xiic_i2c *i2c)
{
- u8 rx_watermark;
+ u16 rx_watermark;
+ u8 cr = 0, rfd_set = 0;
struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
- unsigned long flags;
- /* Clear and enable Rx full interrupt. */
- xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK);
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
+ __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
+ xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
+
+ /* Disable Tx interrupts */
+ xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK | XIIC_INTR_TX_EMPTY_MASK);
+
+ if (i2c->dynamic) {
+ u8 bytes;
+ u16 val;
+
+ /* Clear and enable Rx full interrupt. */
+ xiic_irq_clr_en(i2c, 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 indicate 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;
+ bytes = min_t(u8, rx_watermark, IIC_RX_FIFO_DEPTH);
- /* 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;
- xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rx_watermark - 1);
+ if (rx_watermark > 0)
+ bytes--;
+ xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, bytes);
- local_irq_save(flags);
- if (!(msg->flags & I2C_M_NOSTART))
/* write the address */
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
- i2c_8bit_addr_from_msg(msg) | XIIC_TX_DYN_START_MASK);
+ i2c_8bit_addr_from_msg(msg) |
+ XIIC_TX_DYN_START_MASK);
+
+ /* If last message, include dynamic stop bit with length */
+ val = (i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0;
+ val |= msg->len;
- xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK);
+ xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, val);
- xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
- msg->len | ((i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0));
- local_irq_restore(flags);
+ xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK);
+ } else {
+ /*
+ * If previous message is Tx, make sure that Tx FIFO is empty
+ * before starting a new transfer as the repeated start in
+ * standard mode can corrupt the transaction if there are
+ * still bytes to be transmitted in FIFO
+ */
+ if (i2c->prev_msg_tx) {
+ int status;
+
+ status = xiic_wait_tx_empty(i2c);
+ if (status)
+ return;
+ }
+
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+
+ /* Set Receive fifo depth */
+ rx_watermark = msg->len;
+ if (rx_watermark > IIC_RX_FIFO_DEPTH) {
+ rfd_set = IIC_RX_FIFO_DEPTH - 1;
+ } else if (rx_watermark == 1) {
+ rfd_set = rx_watermark - 1;
+
+ /* Set No_ACK, except for smbus_block_read */
+ if (!(i2c->rx_msg->flags & I2C_M_RECV_LEN)) {
+ /* Handle single byte transfer separately */
+ cr |= XIIC_CR_NO_ACK_MASK;
+ }
+ } else if (rx_watermark == 0) {
+ rfd_set = rx_watermark;
+ } else {
+ rfd_set = rx_watermark - 2;
+ }
+ /* Check if RSTA should be set */
+ if (cr & XIIC_CR_MSMS_MASK) {
+ /* Already a master, RSTA should be set */
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
+ XIIC_CR_REPEATED_START_MASK) &
+ ~(XIIC_CR_DIR_IS_TX_MASK));
+ }
+
+ xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
+
+ /* Clear and enable Rx full and transmit complete interrupts */
+ xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK |
+ XIIC_INTR_TX_ERROR_MASK);
+
+ /* Write the address */
+ xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
+ i2c_8bit_addr_from_msg(msg));
+
+ /* Write to Control Register,to start transaction in Rx mode */
+ if ((cr & XIIC_CR_MSMS_MASK) == 0) {
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
+ XIIC_CR_MSMS_MASK)
+ & ~(XIIC_CR_DIR_IS_TX_MASK));
+ }
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent, "%s end, ISR: 0x%x, CR: 0x%x\n",
+ __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
+ xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
+ }
if (i2c->nmsgs == 1)
/* very last, enable bus not busy as well */
@@ -566,108 +1069,255 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
/* the message is tx:ed */
i2c->tx_pos = msg->len;
+
+ i2c->prev_msg_tx = false;
+
+ /* Enable interrupts */
+ if (!i2c->atomic)
+ xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
+ else
+ xiic_recv_atomic(i2c);
+}
+
+static void xiic_send_rem_atomic(struct xiic_i2c *i2c)
+{
+ while (xiic_tx_space(i2c)) {
+ if (xiic_tx_fifo_space(i2c)) {
+ u16 data;
+
+ data = i2c->tx_msg->buf[i2c->tx_pos];
+ i2c->tx_pos++;
+ if (!xiic_tx_space(i2c) && i2c->nmsgs == 1) {
+ /* last message in transfer -> STOP */
+ if (i2c->dynamic) {
+ data |= XIIC_TX_DYN_STOP_MASK;
+ } else {
+ u8 cr;
+ int status;
+
+ /* Wait till FIFO is empty so STOP is sent last */
+ status = xiic_wait_tx_empty(i2c);
+ if (status)
+ return;
+
+ /* Write to CR to stop */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
+ ~XIIC_CR_MSMS_MASK);
+ }
+ }
+ xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
+ }
+ if (xiic_error_check(i2c))
+ return;
+ }
+
+ if (i2c->nmsgs > 1) {
+ i2c->nmsgs--;
+ i2c->tx_msg++;
+ __xiic_start_xfer(i2c);
+ } else {
+ xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
+ }
}
static void xiic_start_send(struct xiic_i2c *i2c)
{
+ u8 cr = 0;
+ u16 data;
struct i2c_msg *msg = i2c->tx_msg;
- xiic_irq_clr(i2c, XIIC_INTR_TX_ERROR_MASK);
-
- dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d",
- __func__, msg, msg->len);
- dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
- __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
- xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
+ if (!i2c->atomic) {
+ dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d",
+ __func__, msg, msg->len);
+ dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
+ __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
+ xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
+ }
- if (!(msg->flags & I2C_M_NOSTART)) {
+ if (i2c->dynamic) {
/* write the address */
- u16 data = i2c_8bit_addr_from_msg(msg) |
- XIIC_TX_DYN_START_MASK;
- if ((i2c->nmsgs == 1) && msg->len == 0)
+ data = i2c_8bit_addr_from_msg(msg) |
+ XIIC_TX_DYN_START_MASK;
+
+ if (i2c->nmsgs == 1 && msg->len == 0)
/* no data and last message -> add STOP */
data |= XIIC_TX_DYN_STOP_MASK;
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
- }
- xiic_fill_tx_fifo(i2c);
+ /* Clear any pending Tx empty, Tx Error and then enable them */
+ xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK |
+ XIIC_INTR_TX_ERROR_MASK |
+ XIIC_INTR_BNB_MASK |
+ ((i2c->nmsgs > 1 || xiic_tx_space(i2c)) ?
+ XIIC_INTR_TX_HALF_MASK : 0));
- /* Clear any pending Tx empty, Tx Error and then enable them. */
- xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK |
- XIIC_INTR_BNB_MASK);
-}
+ xiic_fill_tx_fifo(i2c);
+ } else {
+ /*
+ * If previous message is Tx, make sure that Tx FIFO is empty
+ * before starting a new transfer as the repeated start in
+ * standard mode can corrupt the transaction if there are
+ * still bytes to be transmitted in FIFO
+ */
+ if (i2c->prev_msg_tx) {
+ int status;
-static irqreturn_t xiic_isr(int irq, void *dev_id)
-{
- struct xiic_i2c *i2c = dev_id;
- u32 pend, isr, ier;
- irqreturn_t ret = IRQ_NONE;
- /* Do not processes a devices interrupts if the device has no
- * interrupts pending
- */
+ status = xiic_wait_tx_empty(i2c);
+ if (status)
+ return;
+ }
+ /* Check if RSTA should be set */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ if (cr & XIIC_CR_MSMS_MASK) {
+ /* Already a master, RSTA should be set */
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
+ XIIC_CR_REPEATED_START_MASK |
+ XIIC_CR_DIR_IS_TX_MASK) &
+ ~(XIIC_CR_NO_ACK_MASK));
+ }
- dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__);
+ /* Write address to FIFO */
+ data = i2c_8bit_addr_from_msg(msg);
+ xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
- isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
- ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
- pend = isr & ier;
- if (pend)
- ret = IRQ_WAKE_THREAD;
+ /* Fill fifo */
+ xiic_fill_tx_fifo(i2c);
- return ret;
+ if ((cr & XIIC_CR_MSMS_MASK) == 0) {
+ /* Start Tx by writing to CR */
+ cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
+ xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr |
+ XIIC_CR_MSMS_MASK |
+ XIIC_CR_DIR_IS_TX_MASK);
+ }
+
+ /* Clear any pending Tx empty, Tx Error and then enable them */
+ xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK |
+ XIIC_INTR_TX_ERROR_MASK |
+ XIIC_INTR_BNB_MASK);
+ }
+
+ i2c->prev_msg_tx = true;
+
+ if (i2c->atomic && !i2c->atomic_xfer_state)
+ xiic_send_rem_atomic(i2c);
}
static void __xiic_start_xfer(struct xiic_i2c *i2c)
{
- int first = 1;
int fifo_space = xiic_tx_fifo_space(i2c);
- dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, fifos space: %d\n",
- __func__, i2c->tx_msg, fifo_space);
+
+ if (!i2c->atomic)
+ dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, fifos space: %d\n",
+ __func__, i2c->tx_msg, fifo_space);
if (!i2c->tx_msg)
return;
+ if (i2c->atomic && xiic_error_check(i2c))
+ return;
+
i2c->rx_pos = 0;
i2c->tx_pos = 0;
i2c->state = STATE_START;
- while ((fifo_space >= 2) && (first || (i2c->nmsgs > 1))) {
- if (!first) {
- i2c->nmsgs--;
- i2c->tx_msg++;
- i2c->tx_pos = 0;
- } else
- first = 0;
-
- if (i2c->tx_msg->flags & I2C_M_RD) {
- /* we dont date putting several reads in the FIFO */
- xiic_start_recv(i2c);
- return;
- } else {
- xiic_start_send(i2c);
- if (xiic_tx_space(i2c) != 0) {
- /* the message could not be completely sent */
- break;
- }
- }
+ if (i2c->tx_msg->flags & I2C_M_RD) {
+ /* we dont date putting several reads in the FIFO */
+ xiic_start_recv(i2c);
+ } else {
+ xiic_start_send(i2c);
+ }
+}
+
+static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num)
+{
+ bool broken_read, max_read_len, smbus_blk_read;
+ int ret, count;
- fifo_space = xiic_tx_fifo_space(i2c);
+ if (i2c->atomic)
+ spin_lock(&i2c->atomic_lock);
+ else
+ mutex_lock(&i2c->lock);
+
+ if (i2c->tx_msg || i2c->rx_msg) {
+ dev_err(i2c->adap.dev.parent,
+ "cannot start a transfer while busy\n");
+ ret = -EBUSY;
+ goto out;
}
- /* there are more messages or the current one could not be completely
- * put into the FIFO, also enable the half empty interrupt
+ i2c->atomic_xfer_state = STATE_DONE;
+
+ /* In single master mode bus can only be busy, when in use by this
+ * driver. If the register indicates bus being busy for some reason we
+ * should ignore it, since bus will never be released and i2c will be
+ * stuck forever.
*/
- if (i2c->nmsgs > 1 || xiic_tx_space(i2c))
- xiic_irq_clr_en(i2c, XIIC_INTR_TX_HALF_MASK);
+ if (!i2c->singlemaster) {
+ ret = xiic_wait_not_busy(i2c);
+ if (ret) {
+ /* If the bus is stuck in a busy state, such as due to spurious low
+ * pulses on the bus causing a false start condition to be detected,
+ * then try to recover by re-initializing the controller and check
+ * again if the bus is still busy.
+ */
+ dev_warn(i2c->adap.dev.parent, "I2C bus busy timeout, reinitializing\n");
+ ret = xiic_reinit(i2c);
+ if (ret)
+ goto out;
+ ret = xiic_wait_not_busy(i2c);
+ if (ret)
+ goto out;
+ }
+ }
-}
+ i2c->tx_msg = msgs;
+ i2c->rx_msg = NULL;
+ i2c->nmsgs = num;
-static void xiic_start_xfer(struct xiic_i2c *i2c)
-{
- mutex_lock(&i2c->lock);
- xiic_reinit(i2c);
- __xiic_start_xfer(i2c);
- mutex_unlock(&i2c->lock);
+ if (!i2c->atomic)
+ init_completion(&i2c->completion);
+
+ /* Decide standard mode or Dynamic mode */
+ i2c->dynamic = true;
+
+ /* Initialize prev message type */
+ i2c->prev_msg_tx = false;
+
+ /*
+ * Scan through nmsgs, use dynamic mode when none of the below three
+ * conditions occur. We need standard mode even if one condition holds
+ * true in the entire array of messages in a single transfer.
+ * If read transaction as dynamic mode is broken for delayed reads
+ * in xlnx,axi-iic-2.0 / xlnx,xps-iic-2.00.a IP versions.
+ * If read length is > 255 bytes.
+ * If smbus_block_read transaction.
+ */
+ for (count = 0; count < i2c->nmsgs; count++) {
+ broken_read = (i2c->quirks & DYNAMIC_MODE_READ_BROKEN_BIT) &&
+ (i2c->tx_msg[count].flags & I2C_M_RD);
+ max_read_len = (i2c->tx_msg[count].flags & I2C_M_RD) &&
+ (i2c->tx_msg[count].len > MAX_READ_LENGTH_DYNAMIC);
+ smbus_blk_read = (i2c->tx_msg[count].flags & I2C_M_RECV_LEN);
+
+ if (broken_read || max_read_len || smbus_blk_read) {
+ i2c->dynamic = false;
+ break;
+ }
+ }
+
+ ret = xiic_reinit(i2c);
+ if (!ret)
+ __xiic_start_xfer(i2c);
+
+out:
+ if (i2c->atomic)
+ spin_unlock(&i2c->atomic_lock);
+ else
+ mutex_unlock(&i2c->lock);
+
+ return ret;
}
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
@@ -678,58 +1328,104 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__,
xiic_getreg8(i2c, XIIC_SR_REG_OFFSET));
- err = pm_runtime_get_sync(i2c->dev);
+ err = pm_runtime_resume_and_get(i2c->dev);
if (err < 0)
return err;
- err = xiic_busy(i2c);
- if (err)
+ err = xiic_start_xfer(i2c, msgs, num);
+ if (err < 0)
goto out;
- i2c->tx_msg = msgs;
- i2c->nmsgs = num;
-
- xiic_start_xfer(i2c);
-
- if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
- (i2c->state == STATE_DONE), HZ)) {
- err = (i2c->state == STATE_DONE) ? num : -EIO;
- goto out;
- } else {
+ err = wait_for_completion_timeout(&i2c->completion, XIIC_XFER_TIMEOUT);
+ mutex_lock(&i2c->lock);
+ if (err == 0) { /* Timeout */
i2c->tx_msg = NULL;
i2c->rx_msg = NULL;
i2c->nmsgs = 0;
err = -ETIMEDOUT;
- goto out;
+ } else {
+ err = (i2c->state == STATE_DONE) ? num : -EIO;
}
+ mutex_unlock(&i2c->lock);
+
out:
- pm_runtime_mark_last_busy(i2c->dev);
pm_runtime_put_autosuspend(i2c->dev);
return err;
}
+static int xiic_xfer_atomic(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct xiic_i2c *i2c = i2c_get_adapdata(adap);
+ u32 status_reg;
+ int err;
+
+ err = xiic_i2c_runtime_resume(i2c->dev);
+ if (err)
+ return err;
+
+ i2c->atomic = true;
+ err = xiic_start_xfer(i2c, msgs, num);
+ if (err < 0)
+ return err;
+
+ err = readl_poll_timeout_atomic(i2c->base + XIIC_SR_REG_OFFSET,
+ status_reg, !(status_reg & XIIC_SR_BUS_BUSY_MASK),
+ 1, XIIC_XFER_TIMEOUT_US);
+
+ if (err) /* Timeout */
+ err = -ETIMEDOUT;
+
+ spin_lock(&i2c->atomic_lock);
+ if (err || i2c->state) {
+ i2c->tx_msg = NULL;
+ i2c->rx_msg = NULL;
+ i2c->nmsgs = 0;
+ }
+
+ err = (i2c->atomic_xfer_state == STATE_DONE) ? num : -EIO;
+ spin_unlock(&i2c->atomic_lock);
+
+ i2c->atomic = false;
+ xiic_i2c_runtime_suspend(i2c->dev);
+
+ return err;
+}
+
static u32 xiic_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
}
static const struct i2c_algorithm xiic_algorithm = {
- .master_xfer = xiic_xfer,
+ .xfer = xiic_xfer,
+ .xfer_atomic = xiic_xfer_atomic,
.functionality = xiic_func,
};
static const struct i2c_adapter xiic_adapter = {
.owner = THIS_MODULE,
- .name = DRIVER_NAME,
.class = I2C_CLASS_DEPRECATED,
.algo = &xiic_algorithm,
};
+#if defined(CONFIG_OF)
+static const struct xiic_version_data xiic_2_00 = {
+ .quirks = DYNAMIC_MODE_READ_BROKEN_BIT,
+};
+
+static const struct of_device_id xiic_of_match[] = {
+ { .compatible = "xlnx,xps-iic-2.00.a", .data = &xiic_2_00 },
+ { .compatible = "xlnx,axi-iic-2.1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xiic_of_match);
+#endif
static int xiic_i2c_probe(struct platform_device *pdev)
{
struct xiic_i2c *i2c;
struct xiic_i2c_platform_data *pdata;
+ const struct of_device_id *match;
struct resource *res;
int ret, irq;
u8 i;
@@ -739,8 +1435,14 @@ static int xiic_i2c_probe(struct platform_device *pdev)
if (!i2c)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ match = of_match_node(xiic_of_match, pdev->dev.of_node);
+ if (match && match->data) {
+ const struct xiic_version_data *data = match->data;
+
+ i2c->quirks = data->quirks;
+ }
+
+ i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
@@ -756,34 +1458,43 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name),
+ DRIVER_NAME " %s", pdev->name);
mutex_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
+ spin_lock_init(&i2c->atomic_lock);
+
+ i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
+ "failed to enable input clock.\n");
- i2c->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2c->clk)) {
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(i2c->clk);
- }
- ret = clk_prepare_enable(i2c->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable clock.\n");
- return ret;
- }
i2c->dev = &pdev->dev;
- pm_runtime_enable(i2c->dev);
pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT);
pm_runtime_use_autosuspend(i2c->dev);
pm_runtime_set_active(i2c->dev);
- ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
+ pm_runtime_enable(i2c->dev);
+
+ /* SCL frequency configuration */
+ i2c->input_clk = clk_get_rate(i2c->clk);
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &i2c->i2c_clk);
+ /* If clock-frequency not specified in DT, do not configure in SW */
+ if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ)
+ i2c->i2c_clk = 0;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
xiic_process, IRQF_ONESHOT,
pdev->name, i2c);
if (ret < 0) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- goto err_clk_dis;
+ dev_err_probe(&pdev->dev, ret, "Cannot claim IRQ\n");
+ goto err_pm_disable;
}
+ i2c->singlemaster =
+ of_property_read_bool(pdev->dev.of_node, "single-master");
+
/*
* Detect endianness
* Try to reset the TX FIFO. Then check the EMPTY flag. If it is not
@@ -796,31 +1507,38 @@ static int xiic_i2c_probe(struct platform_device *pdev)
if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK))
i2c->endianness = BIG;
- xiic_reinit(i2c);
+ ret = xiic_reinit(i2c);
+ if (ret < 0) {
+ dev_err_probe(&pdev->dev, ret, "Cannot xiic_reinit\n");
+ goto err_pm_disable;
+ }
/* add i2c adapter to i2c tree */
ret = i2c_add_adapter(&i2c->adap);
if (ret) {
xiic_deinit(i2c);
- goto err_clk_dis;
+ goto err_pm_disable;
}
if (pdata) {
/* add in known devices to the bus */
for (i = 0; i < pdata->num_devices; i++)
- i2c_new_device(&i2c->adap, pdata->devices + i);
+ i2c_new_client_device(&i2c->adap, pdata->devices + i);
}
+ dev_dbg(&pdev->dev, "mmio %08lx irq %d scl clock frequency %d\n",
+ (unsigned long)res->start, irq, i2c->i2c_clk);
+
return 0;
-err_clk_dis:
- pm_runtime_set_suspended(&pdev->dev);
+err_pm_disable:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(i2c->clk);
+ pm_runtime_set_suspended(&pdev->dev);
+
return ret;
}
-static int xiic_i2c_remove(struct platform_device *pdev)
+static void xiic_i2c_remove(struct platform_device *pdev)
{
struct xiic_i2c *i2c = platform_get_drvdata(pdev);
int ret;
@@ -828,56 +1546,28 @@ static int xiic_i2c_remove(struct platform_device *pdev)
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
- ret = clk_prepare_enable(i2c->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable clock.\n");
- return ret;
- }
- xiic_deinit(i2c);
- clk_disable_unprepare(i2c->clk);
- pm_runtime_disable(&pdev->dev);
-
- return 0;
-}
+ ret = pm_runtime_get_sync(i2c->dev);
-#if defined(CONFIG_OF)
-static const struct of_device_id xiic_of_match[] = {
- { .compatible = "xlnx,xps-iic-2.00.a", },
- {},
-};
-MODULE_DEVICE_TABLE(of, xiic_of_match);
-#endif
-
-static int __maybe_unused xiic_i2c_runtime_suspend(struct device *dev)
-{
- struct xiic_i2c *i2c = dev_get_drvdata(dev);
-
- clk_disable(i2c->clk);
-
- return 0;
-}
-
-static int __maybe_unused xiic_i2c_runtime_resume(struct device *dev)
-{
- struct xiic_i2c *i2c = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_enable(i2c->clk);
- if (ret) {
- dev_err(dev, "Cannot enable clock.\n");
- return ret;
- }
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Failed to activate device for removal (%pe)\n",
+ ERR_PTR(ret));
+ else
+ xiic_deinit(i2c);
- return 0;
+ pm_runtime_put_sync(i2c->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
}
static const struct dev_pm_ops xiic_dev_pm_ops = {
SET_RUNTIME_PM_OPS(xiic_i2c_runtime_suspend,
xiic_i2c_runtime_resume, NULL)
};
+
static struct platform_driver xiic_i2c_driver = {
.probe = xiic_i2c_probe,
- .remove = xiic_i2c_remove,
+ .remove = xiic_i2c_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(xiic_of_match),
@@ -887,7 +1577,7 @@ static struct platform_driver xiic_i2c_driver = {
module_platform_driver(xiic_i2c_driver);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("info@mocean-labs.com");
MODULE_DESCRIPTION("Xilinx I2C bus driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c
index 8a873975cf12..ddb1c3e8bc9d 100644
--- a/drivers/i2c/busses/i2c-xlp9xx.c
+++ b/drivers/i2c/busses/i2c-xlp9xx.c
@@ -71,8 +71,6 @@
#define XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT 1
#define XLP9XX_I2C_IP_CLK_FREQ 133000000UL
-#define XLP9XX_I2C_DEFAULT_FREQ 100000
-#define XLP9XX_I2C_HIGH_FREQ 400000
#define XLP9XX_I2C_FIFO_SIZE 0x80U
#define XLP9XX_I2C_TIMEOUT_MS 1000
#define XLP9XX_I2C_BUSY_TIMEOUT 50
@@ -454,7 +452,7 @@ static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter)
}
static const struct i2c_algorithm xlp9xx_i2c_algo = {
- .master_xfer = xlp9xx_i2c_xfer,
+ .xfer = xlp9xx_i2c_xfer,
.functionality = xlp9xx_i2c_functionality,
};
@@ -476,12 +474,12 @@ static int xlp9xx_i2c_get_frequency(struct platform_device *pdev,
err = device_property_read_u32(&pdev->dev, "clock-frequency", &freq);
if (err) {
- freq = XLP9XX_I2C_DEFAULT_FREQ;
+ freq = I2C_MAX_STANDARD_MODE_FREQ;
dev_dbg(&pdev->dev, "using default frequency %u\n", freq);
- } else if (freq == 0 || freq > XLP9XX_I2C_HIGH_FREQ) {
+ } else if (freq == 0 || freq > I2C_MAX_FAST_MODE_FREQ) {
dev_warn(&pdev->dev, "invalid frequency %u, using default\n",
freq);
- freq = XLP9XX_I2C_DEFAULT_FREQ;
+ freq = I2C_MAX_STANDARD_MODE_FREQ;
}
priv->clk_hz = freq;
@@ -491,12 +489,16 @@ static int xlp9xx_i2c_get_frequency(struct platform_device *pdev,
static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv,
struct platform_device *pdev)
{
+ struct i2c_client *ara;
+
if (!priv->alert_data.irq)
return -EINVAL;
- priv->ara = i2c_setup_smbus_alert(&priv->adapter, &priv->alert_data);
- if (!priv->ara)
- return -ENODEV;
+ ara = i2c_new_smbus_alert_device(&priv->adapter, &priv->alert_data);
+ if (IS_ERR(ara))
+ return PTR_ERR(ara);
+
+ priv->ara = ara;
return 0;
}
@@ -504,23 +506,19 @@ static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv,
static int xlp9xx_i2c_probe(struct platform_device *pdev)
{
struct xlp9xx_i2c_dev *priv;
- struct resource *res;
int err = 0;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->irq = platform_get_irq(pdev, 0);
- if (priv->irq <= 0) {
- dev_err(&pdev->dev, "invalid irq!\n");
+ if (priv->irq < 0)
return priv->irq;
- }
/* SMBAlert irq */
priv->alert_data.irq = platform_get_irq(pdev, 1);
if (priv->alert_data.irq <= 0)
@@ -531,10 +529,8 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev)
err = devm_request_irq(&pdev->dev, priv->irq, xlp9xx_i2c_isr, 0,
pdev->name, priv);
- if (err) {
- dev_err(&pdev->dev, "IRQ request failed!\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(&pdev->dev, err, "IRQ request failed!\n");
init_completion(&priv->msg_complete);
priv->adapter.dev.parent = &pdev->dev;
@@ -561,7 +557,7 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int xlp9xx_i2c_remove(struct platform_device *pdev)
+static void xlp9xx_i2c_remove(struct platform_device *pdev)
{
struct xlp9xx_i2c_dev *priv;
@@ -570,16 +566,8 @@ static int xlp9xx_i2c_remove(struct platform_device *pdev)
synchronize_irq(priv->irq);
i2c_del_adapter(&priv->adapter);
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, 0);
-
- return 0;
}
-static const struct of_device_id xlp9xx_i2c_of_match[] = {
- { .compatible = "netlogic,xlp980-i2c", },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, xlp9xx_i2c_of_match);
-
#ifdef CONFIG_ACPI
static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = {
{"BRCM9007", 0},
@@ -594,7 +582,6 @@ static struct platform_driver xlp9xx_i2c_driver = {
.remove = xlp9xx_i2c_remove,
.driver = {
.name = "xlp9xx-i2c",
- .of_match_table = xlp9xx_i2c_of_match,
.acpi_match_table = ACPI_PTR(xlp9xx_i2c_acpi_ids),
},
};
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
deleted file mode 100644
index 34cd4b308540..000000000000
--- a/drivers/i2c/busses/i2c-xlr.c
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * Copyright 2011, Netlogic Microsystems Inc.
- * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-
-/* XLR I2C REGISTERS */
-#define XLR_I2C_CFG 0x00
-#define XLR_I2C_CLKDIV 0x01
-#define XLR_I2C_DEVADDR 0x02
-#define XLR_I2C_ADDR 0x03
-#define XLR_I2C_DATAOUT 0x04
-#define XLR_I2C_DATAIN 0x05
-#define XLR_I2C_STATUS 0x06
-#define XLR_I2C_STARTXFR 0x07
-#define XLR_I2C_BYTECNT 0x08
-#define XLR_I2C_HDSTATIM 0x09
-
-/* Sigma Designs additional registers */
-#define XLR_I2C_INT_EN 0x09
-#define XLR_I2C_INT_STAT 0x0a
-
-/* XLR I2C REGISTERS FLAGS */
-#define XLR_I2C_BUS_BUSY 0x01
-#define XLR_I2C_SDOEMPTY 0x02
-#define XLR_I2C_RXRDY 0x04
-#define XLR_I2C_ACK_ERR 0x08
-#define XLR_I2C_ARB_STARTERR 0x30
-
-/* Register Values */
-#define XLR_I2C_CFG_ADDR 0xF8
-#define XLR_I2C_CFG_NOADDR 0xFA
-#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
-#define XLR_I2C_STARTXFR_RD 0x01 /* Read */
-#define XLR_I2C_STARTXFR_WR 0x00 /* Write */
-
-#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
-
-/*
- * On XLR/XLS, we need to use __raw_ IO to read the I2C registers
- * because they are in the big-endian MMIO area on the SoC.
- *
- * The readl/writel implementation on XLR/XLS byteswaps, because
- * those are for its little-endian PCI space (see arch/mips/Kconfig).
- */
-static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val)
-{
- __raw_writel(val, base + reg);
-}
-
-static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
-{
- return __raw_readl(base + reg);
-}
-
-#define XLR_I2C_FLAG_IRQ 1
-
-struct xlr_i2c_config {
- u32 flags; /* optional feature support */
- u32 status_busy; /* value of STATUS[0] when busy */
- u32 cfg_extra; /* extra CFG bits to set */
-};
-
-struct xlr_i2c_private {
- struct i2c_adapter adap;
- u32 __iomem *iobase;
- int irq;
- int pos;
- struct i2c_msg *msg;
- const struct xlr_i2c_config *cfg;
- wait_queue_head_t wait;
- struct clk *clk;
-};
-
-static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 status)
-{
- return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
-}
-
-static int xlr_i2c_idle(struct xlr_i2c_private *priv)
-{
- return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
-}
-
-static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
-{
- int status;
- int t;
-
- t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
- msecs_to_jiffies(timeout));
- if (!t)
- return -ETIMEDOUT;
-
- status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
-
- return status & XLR_I2C_ACK_ERR ? -EIO : 0;
-}
-
-static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
-{
- struct i2c_msg *msg = priv->msg;
-
- if (status & XLR_I2C_SDOEMPTY)
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
- msg->buf[priv->pos++]);
-}
-
-static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
-{
- struct i2c_msg *msg = priv->msg;
-
- if (status & XLR_I2C_RXRDY)
- msg->buf[priv->pos++] =
- xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
-}
-
-static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
-{
- struct xlr_i2c_private *priv = dev_id;
- struct i2c_msg *msg = priv->msg;
- u32 int_stat, status;
-
- int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
- if (!int_stat)
- return IRQ_NONE;
-
- xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
-
- if (!msg)
- return IRQ_HANDLED;
-
- status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
-
- if (priv->pos < msg->len) {
- if (msg->flags & I2C_M_RD)
- xlr_i2c_rx_irq(priv, status);
- else
- xlr_i2c_tx_irq(priv, status);
- }
-
- if (!xlr_i2c_busy(priv, status))
- wake_up(&priv->wait);
-
- return IRQ_HANDLED;
-}
-
-static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
- u8 *buf, u16 addr)
-{
- struct i2c_adapter *adap = &priv->adap;
- unsigned long timeout, stoptime, checktime;
- u32 i2c_status;
- int pos, timedout;
- u8 offset;
- u32 xfer;
-
- offset = buf[0];
- xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
- XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
-
- timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
- stoptime = jiffies + timeout;
- timedout = 0;
-
- if (len == 1) {
- xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
- xfer = XLR_I2C_STARTXFR_ND;
- pos = 1;
- } else {
- xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
- xfer = XLR_I2C_STARTXFR_WR;
- pos = 2;
- }
-
- priv->pos = pos;
-
-retry:
- /* retry can only happen on the first byte */
- xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
-
- if (priv->irq > 0)
- return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
-
- while (!timedout) {
- checktime = jiffies;
- i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
-
- if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
-
- /* reset timeout on successful xmit */
- stoptime = jiffies + timeout;
- }
- timedout = time_after(checktime, stoptime);
-
- if (i2c_status & XLR_I2C_ARB_STARTERR) {
- if (timedout)
- break;
- goto retry;
- }
-
- if (i2c_status & XLR_I2C_ACK_ERR)
- return -EIO;
-
- if (!xlr_i2c_busy(priv, i2c_status) && pos >= len)
- return 0;
- }
- dev_err(&adap->dev, "I2C transmit timeout\n");
- return -ETIMEDOUT;
-}
-
-static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
-{
- struct i2c_adapter *adap = &priv->adap;
- u32 i2c_status;
- unsigned long timeout, stoptime, checktime;
- int nbytes, timedout;
-
- xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
- XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
-
- priv->pos = 0;
-
- timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
- stoptime = jiffies + timeout;
- timedout = 0;
- nbytes = 0;
-retry:
- xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
-
- if (priv->irq > 0)
- return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
-
- while (!timedout) {
- checktime = jiffies;
- i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
- if (i2c_status & XLR_I2C_RXRDY) {
- if (nbytes >= len)
- return -EIO; /* should not happen */
-
- buf[nbytes++] =
- xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
-
- /* reset timeout on successful read */
- stoptime = jiffies + timeout;
- }
-
- timedout = time_after(checktime, stoptime);
- if (i2c_status & XLR_I2C_ARB_STARTERR) {
- if (timedout)
- break;
- goto retry;
- }
-
- if (i2c_status & XLR_I2C_ACK_ERR)
- return -EIO;
-
- if (!xlr_i2c_busy(priv, i2c_status))
- return 0;
- }
-
- dev_err(&adap->dev, "I2C receive timeout\n");
- return -ETIMEDOUT;
-}
-
-static int xlr_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- struct i2c_msg *msg;
- int i;
- int ret = 0;
- struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
-
- ret = clk_enable(priv->clk);
- if (ret)
- return ret;
-
- if (priv->irq)
- xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0xf);
-
-
- for (i = 0; ret == 0 && i < num; i++) {
- msg = &msgs[i];
- priv->msg = msg;
- if (msg->flags & I2C_M_RD)
- ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
- msg->addr);
- else
- ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0],
- msg->addr);
- }
-
- if (priv->irq)
- xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
-
- clk_disable(priv->clk);
- priv->msg = NULL;
-
- return (ret != 0) ? ret : num;
-}
-
-static u32 xlr_func(struct i2c_adapter *adap)
-{
- /* Emulate SMBUS over I2C */
- return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
-}
-
-static const struct i2c_algorithm xlr_i2c_algo = {
- .master_xfer = xlr_i2c_xfer,
- .functionality = xlr_func,
-};
-
-static const struct i2c_adapter_quirks xlr_i2c_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
-};
-
-static const struct xlr_i2c_config xlr_i2c_config_default = {
- .status_busy = XLR_I2C_BUS_BUSY,
- .cfg_extra = 0,
-};
-
-static const struct xlr_i2c_config xlr_i2c_config_tangox = {
- .flags = XLR_I2C_FLAG_IRQ,
- .status_busy = 0,
- .cfg_extra = 1 << 8,
-};
-
-static const struct of_device_id xlr_i2c_dt_ids[] = {
- {
- .compatible = "sigma,smp8642-i2c",
- .data = &xlr_i2c_config_tangox,
- },
- { }
-};
-MODULE_DEVICE_TABLE(of, xlr_i2c_dt_ids);
-
-static int xlr_i2c_probe(struct platform_device *pdev)
-{
- const struct of_device_id *match;
- struct xlr_i2c_private *priv;
- struct resource *res;
- struct clk *clk;
- unsigned long clk_rate;
- unsigned long clk_div;
- u32 busfreq;
- int irq;
- int ret;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- match = of_match_device(xlr_i2c_dt_ids, &pdev->dev);
- if (match)
- priv->cfg = match->data;
- else
- priv->cfg = &xlr_i2c_config_default;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->iobase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(priv->iobase))
- return PTR_ERR(priv->iobase);
-
- irq = platform_get_irq(pdev, 0);
-
- if (irq > 0 && (priv->cfg->flags & XLR_I2C_FLAG_IRQ)) {
- priv->irq = irq;
-
- xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, 0xf);
-
- ret = devm_request_irq(&pdev->dev, priv->irq, xlr_i2c_irq,
- IRQF_SHARED, dev_name(&pdev->dev),
- priv);
- if (ret)
- return ret;
-
- init_waitqueue_head(&priv->wait);
- }
-
- if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
- &busfreq))
- busfreq = 100000;
-
- clk = devm_clk_get(&pdev->dev, NULL);
- if (!IS_ERR(clk)) {
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
- clk_rate = clk_get_rate(clk);
- clk_div = DIV_ROUND_UP(clk_rate, 2 * busfreq);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_CLKDIV, clk_div);
-
- clk_disable(clk);
- priv->clk = clk;
- }
-
- priv->adap.dev.parent = &pdev->dev;
- priv->adap.dev.of_node = pdev->dev.of_node;
- priv->adap.owner = THIS_MODULE;
- priv->adap.algo_data = priv;
- priv->adap.algo = &xlr_i2c_algo;
- priv->adap.quirks = &xlr_i2c_quirks;
- priv->adap.nr = pdev->id;
- priv->adap.class = I2C_CLASS_HWMON;
- snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
-
- i2c_set_adapdata(&priv->adap, priv);
- ret = i2c_add_numbered_adapter(&priv->adap);
- if (ret < 0)
- return ret;
-
- platform_set_drvdata(pdev, priv);
- dev_info(&priv->adap.dev, "Added I2C Bus.\n");
- return 0;
-}
-
-static int xlr_i2c_remove(struct platform_device *pdev)
-{
- struct xlr_i2c_private *priv;
-
- priv = platform_get_drvdata(pdev);
- i2c_del_adapter(&priv->adap);
- clk_unprepare(priv->clk);
-
- return 0;
-}
-
-static struct platform_driver xlr_i2c_driver = {
- .probe = xlr_i2c_probe,
- .remove = xlr_i2c_remove,
- .driver = {
- .name = "xlr-i2cbus",
- .of_match_table = xlr_i2c_dt_ids,
- },
-};
-
-module_platform_driver(xlr_i2c_driver);
-
-MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>");
-MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:xlr-i2cbus");
diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c
deleted file mode 100644
index b8f9e020d80e..000000000000
--- a/drivers/i2c/busses/i2c-zx2967.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Copyright (C) 2017 Sanechips Technology Co., Ltd.
- * Copyright 2017 Linaro Ltd.
- *
- * Author: Baoyou Xie <baoyou.xie@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#define REG_CMD 0x04
-#define REG_DEVADDR_H 0x0C
-#define REG_DEVADDR_L 0x10
-#define REG_CLK_DIV_FS 0x14
-#define REG_CLK_DIV_HS 0x18
-#define REG_WRCONF 0x1C
-#define REG_RDCONF 0x20
-#define REG_DATA 0x24
-#define REG_STAT 0x28
-
-#define I2C_STOP 0
-#define I2C_MASTER BIT(0)
-#define I2C_ADDR_MODE_TEN BIT(1)
-#define I2C_IRQ_MSK_ENABLE BIT(3)
-#define I2C_RW_READ BIT(4)
-#define I2C_CMB_RW_EN BIT(5)
-#define I2C_START BIT(6)
-
-#define I2C_ADDR_LOW_MASK GENMASK(6, 0)
-#define I2C_ADDR_LOW_SHIFT 0
-#define I2C_ADDR_HI_MASK GENMASK(2, 0)
-#define I2C_ADDR_HI_SHIFT 7
-
-#define I2C_WFIFO_RESET BIT(7)
-#define I2C_RFIFO_RESET BIT(7)
-
-#define I2C_IRQ_ACK_CLEAR BIT(7)
-#define I2C_INT_MASK GENMASK(6, 0)
-
-#define I2C_TRANS_DONE BIT(0)
-#define I2C_SR_EDEVICE BIT(1)
-#define I2C_SR_EDATA BIT(2)
-
-#define I2C_FIFO_MAX 16
-
-#define I2C_TIMEOUT msecs_to_jiffies(1000)
-
-#define DEV(i2c) ((i2c)->adap.dev.parent)
-
-struct zx2967_i2c {
- struct i2c_adapter adap;
- struct clk *clk;
- struct completion complete;
- u32 clk_freq;
- void __iomem *reg_base;
- size_t residue;
- int irq;
- int msg_rd;
- u8 *cur_trans;
- u8 access_cnt;
- bool is_suspended;
- int error;
-};
-
-static void zx2967_i2c_writel(struct zx2967_i2c *i2c,
- u32 val, unsigned long reg)
-{
- writel_relaxed(val, i2c->reg_base + reg);
-}
-
-static u32 zx2967_i2c_readl(struct zx2967_i2c *i2c, unsigned long reg)
-{
- return readl_relaxed(i2c->reg_base + reg);
-}
-
-static void zx2967_i2c_writesb(struct zx2967_i2c *i2c,
- void *data, unsigned long reg, int len)
-{
- writesb(i2c->reg_base + reg, data, len);
-}
-
-static void zx2967_i2c_readsb(struct zx2967_i2c *i2c,
- void *data, unsigned long reg, int len)
-{
- readsb(i2c->reg_base + reg, data, len);
-}
-
-static void zx2967_i2c_start_ctrl(struct zx2967_i2c *i2c)
-{
- u32 status;
- u32 ctl;
-
- status = zx2967_i2c_readl(i2c, REG_STAT);
- status |= I2C_IRQ_ACK_CLEAR;
- zx2967_i2c_writel(i2c, status, REG_STAT);
-
- ctl = zx2967_i2c_readl(i2c, REG_CMD);
- if (i2c->msg_rd)
- ctl |= I2C_RW_READ;
- else
- ctl &= ~I2C_RW_READ;
- ctl &= ~I2C_CMB_RW_EN;
- ctl |= I2C_START;
- zx2967_i2c_writel(i2c, ctl, REG_CMD);
-}
-
-static void zx2967_i2c_flush_fifos(struct zx2967_i2c *i2c)
-{
- u32 offset;
- u32 val;
-
- if (i2c->msg_rd) {
- offset = REG_RDCONF;
- val = I2C_RFIFO_RESET;
- } else {
- offset = REG_WRCONF;
- val = I2C_WFIFO_RESET;
- }
-
- val |= zx2967_i2c_readl(i2c, offset);
- zx2967_i2c_writel(i2c, val, offset);
-}
-
-static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c *i2c, u32 size)
-{
- u8 val[I2C_FIFO_MAX] = {0};
- int i;
-
- if (size > I2C_FIFO_MAX) {
- dev_err(DEV(i2c), "fifo size %d over the max value %d\n",
- size, I2C_FIFO_MAX);
- return -EINVAL;
- }
-
- zx2967_i2c_readsb(i2c, val, REG_DATA, size);
- for (i = 0; i < size; i++) {
- *i2c->cur_trans++ = val[i];
- i2c->residue--;
- }
-
- barrier();
-
- return 0;
-}
-
-static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c *i2c)
-{
- size_t residue = i2c->residue;
- u8 *buf = i2c->cur_trans;
-
- if (residue == 0) {
- dev_err(DEV(i2c), "residue is %d\n", (int)residue);
- return -EINVAL;
- }
-
- if (residue <= I2C_FIFO_MAX) {
- zx2967_i2c_writesb(i2c, buf, REG_DATA, residue);
-
- /* Again update before writing to FIFO to make sure isr sees. */
- i2c->residue = 0;
- i2c->cur_trans = NULL;
- } else {
- zx2967_i2c_writesb(i2c, buf, REG_DATA, I2C_FIFO_MAX);
- i2c->residue -= I2C_FIFO_MAX;
- i2c->cur_trans += I2C_FIFO_MAX;
- }
-
- barrier();
-
- return 0;
-}
-
-static int zx2967_i2c_reset_hardware(struct zx2967_i2c *i2c)
-{
- u32 val;
- u32 clk_div;
-
- val = I2C_MASTER | I2C_IRQ_MSK_ENABLE;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-
- clk_div = clk_get_rate(i2c->clk) / i2c->clk_freq - 1;
- zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_FS);
- zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_HS);
-
- zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_WRCONF);
- zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_RDCONF);
- zx2967_i2c_writel(i2c, 1, REG_RDCONF);
-
- zx2967_i2c_flush_fifos(i2c);
-
- return 0;
-}
-
-static void zx2967_i2c_isr_clr(struct zx2967_i2c *i2c)
-{
- u32 status;
-
- status = zx2967_i2c_readl(i2c, REG_STAT);
- status |= I2C_IRQ_ACK_CLEAR;
- zx2967_i2c_writel(i2c, status, REG_STAT);
-}
-
-static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id)
-{
- u32 status;
- struct zx2967_i2c *i2c = (struct zx2967_i2c *)dev_id;
-
- status = zx2967_i2c_readl(i2c, REG_STAT) & I2C_INT_MASK;
- zx2967_i2c_isr_clr(i2c);
-
- if (status & I2C_SR_EDEVICE)
- i2c->error = -ENXIO;
- else if (status & I2C_SR_EDATA)
- i2c->error = -EIO;
- else if (status & I2C_TRANS_DONE)
- i2c->error = 0;
- else
- goto done;
-
- complete(&i2c->complete);
-done:
- return IRQ_HANDLED;
-}
-
-static void zx2967_set_addr(struct zx2967_i2c *i2c, u16 addr)
-{
- u16 val;
-
- val = (addr >> I2C_ADDR_LOW_SHIFT) & I2C_ADDR_LOW_MASK;
- zx2967_i2c_writel(i2c, val, REG_DEVADDR_L);
-
- val = (addr >> I2C_ADDR_HI_SHIFT) & I2C_ADDR_HI_MASK;
- zx2967_i2c_writel(i2c, val, REG_DEVADDR_H);
- if (val)
- val = zx2967_i2c_readl(i2c, REG_CMD) | I2C_ADDR_MODE_TEN;
- else
- val = zx2967_i2c_readl(i2c, REG_CMD) & ~I2C_ADDR_MODE_TEN;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-}
-
-static int zx2967_i2c_xfer_bytes(struct zx2967_i2c *i2c, u32 bytes)
-{
- unsigned long time_left;
- int rd = i2c->msg_rd;
- int ret;
-
- reinit_completion(&i2c->complete);
-
- if (rd) {
- zx2967_i2c_writel(i2c, bytes - 1, REG_RDCONF);
- } else {
- ret = zx2967_i2c_fill_tx_fifo(i2c);
- if (ret)
- return ret;
- }
-
- zx2967_i2c_start_ctrl(i2c);
-
- time_left = wait_for_completion_timeout(&i2c->complete,
- I2C_TIMEOUT);
- if (time_left == 0)
- return -ETIMEDOUT;
-
- if (i2c->error)
- return i2c->error;
-
- return rd ? zx2967_i2c_empty_rx_fifo(i2c, bytes) : 0;
-}
-
-static int zx2967_i2c_xfer_msg(struct zx2967_i2c *i2c,
- struct i2c_msg *msg)
-{
- int ret;
- int i;
-
- zx2967_i2c_flush_fifos(i2c);
-
- i2c->cur_trans = msg->buf;
- i2c->residue = msg->len;
- i2c->access_cnt = msg->len / I2C_FIFO_MAX;
- i2c->msg_rd = msg->flags & I2C_M_RD;
-
- for (i = 0; i < i2c->access_cnt; i++) {
- ret = zx2967_i2c_xfer_bytes(i2c, I2C_FIFO_MAX);
- if (ret)
- return ret;
- }
-
- if (i2c->residue > 0) {
- ret = zx2967_i2c_xfer_bytes(i2c, i2c->residue);
- if (ret)
- return ret;
- }
-
- i2c->residue = 0;
- i2c->access_cnt = 0;
-
- return 0;
-}
-
-static int zx2967_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
- int ret;
- int i;
-
- if (i2c->is_suspended)
- return -EBUSY;
-
- zx2967_set_addr(i2c, msgs->addr);
-
- for (i = 0; i < num; i++) {
- ret = zx2967_i2c_xfer_msg(i2c, &msgs[i]);
- if (ret)
- return ret;
- }
-
- return num;
-}
-
-static void
-zx2967_smbus_xfer_prepare(struct zx2967_i2c *i2c, u16 addr,
- char read_write, u8 command, int size,
- union i2c_smbus_data *data)
-{
- u32 val;
-
- val = zx2967_i2c_readl(i2c, REG_RDCONF);
- val |= I2C_RFIFO_RESET;
- zx2967_i2c_writel(i2c, val, REG_RDCONF);
- zx2967_set_addr(i2c, addr);
- val = zx2967_i2c_readl(i2c, REG_CMD);
- val &= ~I2C_RW_READ;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-
- switch (size) {
- case I2C_SMBUS_BYTE:
- zx2967_i2c_writel(i2c, command, REG_DATA);
- break;
- case I2C_SMBUS_BYTE_DATA:
- zx2967_i2c_writel(i2c, command, REG_DATA);
- if (read_write == I2C_SMBUS_WRITE)
- zx2967_i2c_writel(i2c, data->byte, REG_DATA);
- break;
- case I2C_SMBUS_WORD_DATA:
- zx2967_i2c_writel(i2c, command, REG_DATA);
- if (read_write == I2C_SMBUS_WRITE) {
- zx2967_i2c_writel(i2c, (data->word >> 8), REG_DATA);
- zx2967_i2c_writel(i2c, (data->word & 0xff),
- REG_DATA);
- }
- break;
- }
-}
-
-static int zx2967_smbus_xfer_read(struct zx2967_i2c *i2c, int size,
- union i2c_smbus_data *data)
-{
- unsigned long time_left;
- u8 buf[2];
- u32 val;
-
- reinit_completion(&i2c->complete);
-
- val = zx2967_i2c_readl(i2c, REG_CMD);
- val |= I2C_CMB_RW_EN;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-
- val = zx2967_i2c_readl(i2c, REG_CMD);
- val |= I2C_START;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-
- time_left = wait_for_completion_timeout(&i2c->complete,
- I2C_TIMEOUT);
- if (time_left == 0)
- return -ETIMEDOUT;
-
- if (i2c->error)
- return i2c->error;
-
- switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
- val = zx2967_i2c_readl(i2c, REG_DATA);
- data->byte = val;
- break;
- case I2C_SMBUS_WORD_DATA:
- case I2C_SMBUS_PROC_CALL:
- buf[0] = zx2967_i2c_readl(i2c, REG_DATA);
- buf[1] = zx2967_i2c_readl(i2c, REG_DATA);
- data->word = (buf[0] << 8) | buf[1];
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static int zx2967_smbus_xfer_write(struct zx2967_i2c *i2c)
-{
- unsigned long time_left;
- u32 val;
-
- reinit_completion(&i2c->complete);
- val = zx2967_i2c_readl(i2c, REG_CMD);
- val |= I2C_START;
- zx2967_i2c_writel(i2c, val, REG_CMD);
-
- time_left = wait_for_completion_timeout(&i2c->complete,
- I2C_TIMEOUT);
- if (time_left == 0)
- return -ETIMEDOUT;
-
- if (i2c->error)
- return i2c->error;
-
- return 0;
-}
-
-static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data)
-{
- struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
-
- if (size == I2C_SMBUS_QUICK)
- read_write = I2C_SMBUS_WRITE;
-
- switch (size) {
- case I2C_SMBUS_QUICK:
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
- case I2C_SMBUS_WORD_DATA:
- zx2967_smbus_xfer_prepare(i2c, addr, read_write,
- command, size, data);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- if (read_write == I2C_SMBUS_READ)
- return zx2967_smbus_xfer_read(i2c, size, data);
-
- return zx2967_smbus_xfer_write(i2c);
-}
-
-static u32 zx2967_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C |
- I2C_FUNC_SMBUS_QUICK |
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA |
- I2C_FUNC_SMBUS_PROC_CALL |
- I2C_FUNC_SMBUS_I2C_BLOCK;
-}
-
-static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
-{
- struct zx2967_i2c *i2c = dev_get_drvdata(dev);
-
- i2c->is_suspended = true;
- clk_disable_unprepare(i2c->clk);
-
- return 0;
-}
-
-static int __maybe_unused zx2967_i2c_resume(struct device *dev)
-{
- struct zx2967_i2c *i2c = dev_get_drvdata(dev);
-
- i2c->is_suspended = false;
- clk_prepare_enable(i2c->clk);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(zx2967_i2c_dev_pm_ops,
- zx2967_i2c_suspend, zx2967_i2c_resume);
-
-static const struct i2c_algorithm zx2967_i2c_algo = {
- .master_xfer = zx2967_i2c_xfer,
- .smbus_xfer = zx2967_smbus_xfer,
- .functionality = zx2967_i2c_func,
-};
-
-static const struct i2c_adapter_quirks zx2967_i2c_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
-};
-
-static const struct of_device_id zx2967_i2c_of_match[] = {
- { .compatible = "zte,zx296718-i2c", },
- { },
-};
-MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match);
-
-static int zx2967_i2c_probe(struct platform_device *pdev)
-{
- struct zx2967_i2c *i2c;
- void __iomem *reg_base;
- struct resource *res;
- struct clk *clk;
- int ret;
-
- i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
- if (!i2c)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(reg_base))
- return PTR_ERR(reg_base);
-
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "missing controller clock");
- return PTR_ERR(clk);
- }
-
- ret = clk_prepare_enable(clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable i2c_clk\n");
- return ret;
- }
-
- ret = device_property_read_u32(&pdev->dev, "clock-frequency",
- &i2c->clk_freq);
- if (ret) {
- dev_err(&pdev->dev, "missing clock-frequency");
- return ret;
- }
-
- ret = platform_get_irq(pdev, 0);
- if (ret < 0)
- return ret;
-
- i2c->irq = ret;
- i2c->reg_base = reg_base;
- i2c->clk = clk;
-
- init_completion(&i2c->complete);
- platform_set_drvdata(pdev, i2c);
-
- ret = zx2967_i2c_reset_hardware(i2c);
- if (ret) {
- dev_err(&pdev->dev, "failed to initialize i2c controller\n");
- goto err_clk_unprepare;
- }
-
- ret = devm_request_irq(&pdev->dev, i2c->irq,
- zx2967_i2c_isr, 0, dev_name(&pdev->dev), i2c);
- if (ret) {
- dev_err(&pdev->dev, "failed to request irq %i\n", i2c->irq);
- goto err_clk_unprepare;
- }
-
- i2c_set_adapdata(&i2c->adap, i2c);
- strlcpy(i2c->adap.name, "zx2967 i2c adapter",
- sizeof(i2c->adap.name));
- i2c->adap.algo = &zx2967_i2c_algo;
- i2c->adap.quirks = &zx2967_i2c_quirks;
- i2c->adap.nr = pdev->id;
- i2c->adap.dev.parent = &pdev->dev;
- i2c->adap.dev.of_node = pdev->dev.of_node;
-
- ret = i2c_add_numbered_adapter(&i2c->adap);
- if (ret)
- goto err_clk_unprepare;
-
- return 0;
-
-err_clk_unprepare:
- clk_disable_unprepare(i2c->clk);
- return ret;
-}
-
-static int zx2967_i2c_remove(struct platform_device *pdev)
-{
- struct zx2967_i2c *i2c = platform_get_drvdata(pdev);
-
- i2c_del_adapter(&i2c->adap);
- clk_disable_unprepare(i2c->clk);
-
- return 0;
-}
-
-static struct platform_driver zx2967_i2c_driver = {
- .probe = zx2967_i2c_probe,
- .remove = zx2967_i2c_remove,
- .driver = {
- .name = "zx2967_i2c",
- .of_match_table = zx2967_i2c_of_match,
- .pm = &zx2967_i2c_dev_pm_ops,
- },
-};
-module_platform_driver(zx2967_i2c_driver);
-
-MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
-MODULE_DESCRIPTION("ZTE ZX2967 I2C Bus Controller driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index e0923bee8d1f..06cf221557f2 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
@@ -8,15 +9,6 @@
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -159,7 +151,7 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
case state_repeat_start:
outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
- /* fallthrough */
+ fallthrough;
case state_quick:
if (iface->address_byte & 1) {
@@ -435,7 +427,7 @@ static struct scx200_acb_iface *scx200_create_iface(const char *text,
snprintf(adapter->name, sizeof(adapter->name), "%s ACB%d", text, index);
adapter->owner = THIS_MODULE;
adapter->algo = &scx200_acb_algorithm;
- adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adapter->class = I2C_CLASS_HWMON;
adapter->dev.parent = dev;
mutex_init(&iface->mutex);
@@ -508,10 +500,8 @@ static int scx200_probe(struct platform_device *pdev)
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!res) {
- dev_err(&pdev->dev, "can't fetch device resource info\n");
- return -ENODEV;
- }
+ if (!res)
+ return dev_err_probe(&pdev->dev, -ENODEV, "can't fetch device resource info\n");
iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev);
if (!iface)
@@ -531,14 +521,12 @@ static void scx200_cleanup_iface(struct scx200_acb_iface *iface)
kfree(iface);
}
-static int scx200_remove(struct platform_device *pdev)
+static void scx200_remove(struct platform_device *pdev)
{
struct scx200_acb_iface *iface;
iface = platform_get_drvdata(pdev);
scx200_cleanup_iface(iface);
-
- return 0;
}
static struct platform_driver scx200_pci_driver = {
diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c
new file mode 100644
index 000000000000..dd194476b118
--- /dev/null
+++ b/drivers/i2c/i2c-atr.c
@@ -0,0 +1,954 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * I2C Address Translator
+ *
+ * Copyright (c) 2019,2022 Luca Ceresoli <luca@lucaceresoli.net>
+ * Copyright (c) 2022,2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+ *
+ * Originally based on i2c-mux.c
+ */
+
+#include <linux/i2c-atr.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/lockdep.h>
+
+#define ATR_MAX_ADAPTERS 100 /* Just a sanity limit */
+#define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */
+
+/**
+ * struct i2c_atr_alias_pair - Holds the alias assigned to a client address.
+ * @node: List node
+ * @addr: Address of the client on the child bus.
+ * @alias: I2C alias address assigned by the driver.
+ * This is the address that will be used to issue I2C transactions
+ * on the parent (physical) bus.
+ * @fixed: Alias pair cannot be replaced during dynamic address attachment.
+ * This flag is necessary for situations where a single I2C transaction
+ * contains more distinct target addresses than the ATR channel can handle.
+ * It marks addresses that have already been attached to an alias so
+ * that their alias pair is not evicted by a subsequent address in the same
+ * transaction.
+ *
+ */
+struct i2c_atr_alias_pair {
+ struct list_head node;
+ bool fixed;
+ u16 addr;
+ u16 alias;
+};
+
+/**
+ * struct i2c_atr_alias_pool - Pool of client aliases available for an ATR.
+ * @size: Total number of aliases
+ * @shared: Indicates if this alias pool is shared by multiple channels
+ *
+ * @lock: Lock protecting @aliases and @use_mask
+ * @aliases: Array of aliases, must hold exactly @size elements
+ * @use_mask: Mask of used aliases
+ */
+struct i2c_atr_alias_pool {
+ size_t size;
+ bool shared;
+
+ /* Protects aliases and use_mask */
+ spinlock_t lock;
+ u16 *aliases;
+ unsigned long *use_mask;
+};
+
+/**
+ * struct i2c_atr_chan - Data for a channel.
+ * @adap: The &struct i2c_adapter for the channel
+ * @atr: The parent I2C ATR
+ * @chan_id: The ID of this channel
+ * @alias_pairs_lock: Mutex protecting @alias_pairs
+ * @alias_pairs_lock_key: Lock key for @alias_pairs_lock
+ * @alias_pairs: List of @struct i2c_atr_alias_pair containing the
+ * assigned aliases
+ * @alias_pool: Pool of available client aliases
+ *
+ * @orig_addrs_lock: Mutex protecting @orig_addrs
+ * @orig_addrs_lock_key: Lock key for @orig_addrs_lock
+ * @orig_addrs: Buffer used to store the original addresses during transmit
+ * @orig_addrs_size: Size of @orig_addrs
+ */
+struct i2c_atr_chan {
+ struct i2c_adapter adap;
+ struct i2c_atr *atr;
+ u32 chan_id;
+
+ /* Lock alias_pairs during attach/detach */
+ struct mutex alias_pairs_lock;
+ struct lock_class_key alias_pairs_lock_key;
+ struct list_head alias_pairs;
+ struct i2c_atr_alias_pool *alias_pool;
+
+ /* Lock orig_addrs during xfer */
+ struct mutex orig_addrs_lock;
+ struct lock_class_key orig_addrs_lock_key;
+ u16 *orig_addrs;
+ unsigned int orig_addrs_size;
+};
+
+/**
+ * struct i2c_atr - The I2C ATR instance
+ * @parent: The parent &struct i2c_adapter
+ * @dev: The device that owns the I2C ATR instance
+ * @ops: &struct i2c_atr_ops
+ * @priv: Private driver data, set with i2c_atr_set_driver_data()
+ * @algo: The &struct i2c_algorithm for adapters
+ * @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations)
+ * @lock_key: Lock key for @lock
+ * @max_adapters: Maximum number of adapters this I2C ATR can have
+ * @flags: Flags for ATR
+ * @alias_pool: Optional common pool of available client aliases
+ * @i2c_nb: Notifier for remote client add & del events
+ * @adapter: Array of adapters
+ */
+struct i2c_atr {
+ struct i2c_adapter *parent;
+ struct device *dev;
+ const struct i2c_atr_ops *ops;
+
+ void *priv;
+
+ struct i2c_algorithm algo;
+ /* lock for the I2C bus segment (see struct i2c_lock_operations) */
+ struct mutex lock;
+ struct lock_class_key lock_key;
+ int max_adapters;
+ u32 flags;
+
+ struct i2c_atr_alias_pool *alias_pool;
+
+ struct notifier_block i2c_nb;
+
+ struct i2c_adapter *adapter[] __counted_by(max_adapters);
+};
+
+static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, bool shared)
+{
+ struct i2c_atr_alias_pool *alias_pool;
+ int ret;
+
+ alias_pool = kzalloc(sizeof(*alias_pool), GFP_KERNEL);
+ if (!alias_pool)
+ return ERR_PTR(-ENOMEM);
+
+ alias_pool->size = num_aliases;
+
+ alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL);
+ if (!alias_pool->aliases) {
+ ret = -ENOMEM;
+ goto err_free_alias_pool;
+ }
+
+ alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
+ if (!alias_pool->use_mask) {
+ ret = -ENOMEM;
+ goto err_free_aliases;
+ }
+
+ alias_pool->shared = shared;
+
+ spin_lock_init(&alias_pool->lock);
+
+ return alias_pool;
+
+err_free_aliases:
+ kfree(alias_pool->aliases);
+err_free_alias_pool:
+ kfree(alias_pool);
+ return ERR_PTR(ret);
+}
+
+static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool)
+{
+ bitmap_free(alias_pool->use_mask);
+ kfree(alias_pool->aliases);
+ kfree(alias_pool);
+}
+
+/* Must be called with alias_pairs_lock held */
+static struct i2c_atr_alias_pair *i2c_atr_create_c2a(struct i2c_atr_chan *chan,
+ u16 alias, u16 addr)
+{
+ struct i2c_atr_alias_pair *c2a;
+
+ lockdep_assert_held(&chan->alias_pairs_lock);
+
+ c2a = kzalloc(sizeof(*c2a), GFP_KERNEL);
+ if (!c2a)
+ return NULL;
+
+ c2a->addr = addr;
+ c2a->alias = alias;
+
+ list_add(&c2a->node, &chan->alias_pairs);
+
+ return c2a;
+}
+
+/* Must be called with alias_pairs_lock held */
+static void i2c_atr_destroy_c2a(struct i2c_atr_alias_pair **pc2a)
+{
+ list_del(&(*pc2a)->node);
+ kfree(*pc2a);
+ *pc2a = NULL;
+}
+
+static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool)
+{
+ unsigned long idx;
+ u16 alias;
+
+ spin_lock(&alias_pool->lock);
+
+ idx = find_first_zero_bit(alias_pool->use_mask, alias_pool->size);
+ if (idx >= alias_pool->size) {
+ spin_unlock(&alias_pool->lock);
+ return -EBUSY;
+ }
+
+ set_bit(idx, alias_pool->use_mask);
+
+ alias = alias_pool->aliases[idx];
+
+ spin_unlock(&alias_pool->lock);
+ return alias;
+}
+
+static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias)
+{
+ unsigned int idx;
+
+ spin_lock(&alias_pool->lock);
+
+ for (idx = 0; idx < alias_pool->size; ++idx) {
+ if (alias_pool->aliases[idx] == alias) {
+ clear_bit(idx, alias_pool->use_mask);
+ spin_unlock(&alias_pool->lock);
+ return;
+ }
+ }
+
+ spin_unlock(&alias_pool->lock);
+}
+
+static struct i2c_atr_alias_pair *
+i2c_atr_find_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
+{
+ struct i2c_atr_alias_pair *c2a;
+
+ lockdep_assert_held(&chan->alias_pairs_lock);
+
+ list_for_each_entry(c2a, &chan->alias_pairs, node) {
+ if (c2a->addr == addr)
+ return c2a;
+ }
+
+ return NULL;
+}
+
+static struct i2c_atr_alias_pair *
+i2c_atr_create_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
+{
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_atr_alias_pair *c2a;
+ u16 alias;
+ int ret;
+
+ lockdep_assert_held(&chan->alias_pairs_lock);
+
+ ret = i2c_atr_reserve_alias(chan->alias_pool);
+ if (ret < 0)
+ return NULL;
+
+ alias = ret;
+
+ c2a = i2c_atr_create_c2a(chan, alias, addr);
+ if (!c2a)
+ goto err_release_alias;
+
+ ret = atr->ops->attach_addr(atr, chan->chan_id, c2a->addr, c2a->alias);
+ if (ret) {
+ dev_err(atr->dev, "failed to attach 0x%02x on channel %d: err %d\n",
+ addr, chan->chan_id, ret);
+ goto err_del_c2a;
+ }
+
+ return c2a;
+
+err_del_c2a:
+ i2c_atr_destroy_c2a(&c2a);
+err_release_alias:
+ i2c_atr_release_alias(chan->alias_pool, alias);
+ return NULL;
+}
+
+static struct i2c_atr_alias_pair *
+i2c_atr_replace_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
+{
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_atr_alias_pair *c2a;
+ struct list_head *alias_pairs;
+ bool found = false;
+ u16 alias;
+ int ret;
+
+ lockdep_assert_held(&chan->alias_pairs_lock);
+
+ alias_pairs = &chan->alias_pairs;
+
+ if (unlikely(list_empty(alias_pairs)))
+ return NULL;
+
+ list_for_each_entry_reverse(c2a, alias_pairs, node) {
+ if (!c2a->fixed) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ atr->ops->detach_addr(atr, chan->chan_id, c2a->addr);
+ c2a->addr = addr;
+
+ list_move(&c2a->node, alias_pairs);
+
+ alias = c2a->alias;
+
+ ret = atr->ops->attach_addr(atr, chan->chan_id, c2a->addr, c2a->alias);
+ if (ret) {
+ dev_err(atr->dev, "failed to attach 0x%02x on channel %d: err %d\n",
+ addr, chan->chan_id, ret);
+ i2c_atr_destroy_c2a(&c2a);
+ i2c_atr_release_alias(chan->alias_pool, alias);
+ return NULL;
+ }
+
+ return c2a;
+}
+
+static struct i2c_atr_alias_pair *
+i2c_atr_get_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
+{
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_atr_alias_pair *c2a;
+
+ c2a = i2c_atr_find_mapping_by_addr(chan, addr);
+ if (c2a)
+ return c2a;
+
+ if (atr->flags & I2C_ATR_F_STATIC)
+ return NULL;
+
+ c2a = i2c_atr_create_mapping_by_addr(chan, addr);
+ if (c2a)
+ return c2a;
+
+ return i2c_atr_replace_mapping_by_addr(chan, addr);
+}
+
+/*
+ * Replace all message addresses with their aliases, saving the original
+ * addresses.
+ *
+ * This function is internal for use in i2c_atr_master_xfer(). It must be
+ * followed by i2c_atr_unmap_msgs() to restore the original addresses.
+ */
+static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_atr *atr = chan->atr;
+ static struct i2c_atr_alias_pair *c2a;
+ int i, ret = 0;
+
+ /* Ensure we have enough room to save the original addresses */
+ if (unlikely(chan->orig_addrs_size < num)) {
+ u16 *new_buf;
+
+ /* We don't care about old data, hence no realloc() */
+ new_buf = kmalloc_array(num, sizeof(*new_buf), GFP_KERNEL);
+ if (!new_buf)
+ return -ENOMEM;
+
+ kfree(chan->orig_addrs);
+ chan->orig_addrs = new_buf;
+ chan->orig_addrs_size = num;
+ }
+
+ mutex_lock(&chan->alias_pairs_lock);
+
+ for (i = 0; i < num; i++) {
+ chan->orig_addrs[i] = msgs[i].addr;
+
+ c2a = i2c_atr_get_mapping_by_addr(chan, msgs[i].addr);
+
+ if (!c2a) {
+ if (atr->flags & I2C_ATR_F_PASSTHROUGH)
+ continue;
+
+ dev_err(atr->dev, "client 0x%02x not mapped!\n",
+ msgs[i].addr);
+
+ while (i--)
+ msgs[i].addr = chan->orig_addrs[i];
+
+ ret = -ENXIO;
+ goto out_unlock;
+ }
+
+ // Prevent c2a from being overwritten by another client in this transaction
+ c2a->fixed = true;
+
+ msgs[i].addr = c2a->alias;
+ }
+
+out_unlock:
+ mutex_unlock(&chan->alias_pairs_lock);
+ return ret;
+}
+
+/*
+ * Restore all message address aliases with the original addresses. This
+ * function is internal for use in i2c_atr_master_xfer() and for this reason it
+ * needs no null and size checks on orig_addr.
+ *
+ * @see i2c_atr_map_msgs()
+ */
+static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_atr_alias_pair *c2a;
+ int i;
+
+ for (i = 0; i < num; i++)
+ msgs[i].addr = chan->orig_addrs[i];
+
+ mutex_lock(&chan->alias_pairs_lock);
+
+ if (unlikely(list_empty(&chan->alias_pairs)))
+ goto out_unlock;
+
+ // unfix c2a entries so that subsequent transfers can reuse their aliases
+ list_for_each_entry(c2a, &chan->alias_pairs, node) {
+ c2a->fixed = false;
+ }
+
+out_unlock:
+ mutex_unlock(&chan->alias_pairs_lock);
+}
+
+static int i2c_atr_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_atr_chan *chan = adap->algo_data;
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_adapter *parent = atr->parent;
+ int ret;
+
+ /* Translate addresses */
+ mutex_lock(&chan->orig_addrs_lock);
+
+ ret = i2c_atr_map_msgs(chan, msgs, num);
+ if (ret < 0)
+ goto err_unlock;
+
+ /* Perform the transfer */
+ ret = i2c_transfer(parent, msgs, num);
+
+ /* Restore addresses */
+ i2c_atr_unmap_msgs(chan, msgs, num);
+
+err_unlock:
+ mutex_unlock(&chan->orig_addrs_lock);
+
+ return ret;
+}
+
+static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_atr_chan *chan = adap->algo_data;
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_adapter *parent = atr->parent;
+ struct i2c_atr_alias_pair *c2a;
+ u16 alias;
+
+ mutex_lock(&chan->alias_pairs_lock);
+
+ c2a = i2c_atr_get_mapping_by_addr(chan, addr);
+
+ if (!c2a && !(atr->flags & I2C_ATR_F_PASSTHROUGH)) {
+ dev_err(atr->dev, "client 0x%02x not mapped!\n", addr);
+ mutex_unlock(&chan->alias_pairs_lock);
+ return -ENXIO;
+ }
+
+ alias = c2a ? c2a->alias : addr;
+
+ mutex_unlock(&chan->alias_pairs_lock);
+
+ return i2c_smbus_xfer(parent, alias, flags, read_write, command,
+ size, data);
+}
+
+static u32 i2c_atr_functionality(struct i2c_adapter *adap)
+{
+ struct i2c_atr_chan *chan = adap->algo_data;
+ struct i2c_adapter *parent = chan->atr->parent;
+
+ return parent->algo->functionality(parent);
+}
+
+static void i2c_atr_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
+{
+ struct i2c_atr_chan *chan = adapter->algo_data;
+ struct i2c_atr *atr = chan->atr;
+
+ mutex_lock(&atr->lock);
+}
+
+static int i2c_atr_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
+{
+ struct i2c_atr_chan *chan = adapter->algo_data;
+ struct i2c_atr *atr = chan->atr;
+
+ return mutex_trylock(&atr->lock);
+}
+
+static void i2c_atr_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
+{
+ struct i2c_atr_chan *chan = adapter->algo_data;
+ struct i2c_atr *atr = chan->atr;
+
+ mutex_unlock(&atr->lock);
+}
+
+static const struct i2c_lock_operations i2c_atr_lock_ops = {
+ .lock_bus = i2c_atr_lock_bus,
+ .trylock_bus = i2c_atr_trylock_bus,
+ .unlock_bus = i2c_atr_unlock_bus,
+};
+
+static int i2c_atr_attach_addr(struct i2c_adapter *adapter,
+ u16 addr)
+{
+ struct i2c_atr_chan *chan = adapter->algo_data;
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_atr_alias_pair *c2a;
+ int ret = 0;
+
+ mutex_lock(&chan->alias_pairs_lock);
+
+ c2a = i2c_atr_create_mapping_by_addr(chan, addr);
+ if (!c2a && !(atr->flags & I2C_ATR_F_STATIC))
+ c2a = i2c_atr_replace_mapping_by_addr(chan, addr);
+
+ if (!c2a) {
+ dev_err(atr->dev, "failed to find a free alias\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ dev_dbg(atr->dev, "chan%u: using alias 0x%02x for addr 0x%02x\n",
+ chan->chan_id, c2a->alias, addr);
+
+out_unlock:
+ mutex_unlock(&chan->alias_pairs_lock);
+ return ret;
+}
+
+static void i2c_atr_detach_addr(struct i2c_adapter *adapter,
+ u16 addr)
+{
+ struct i2c_atr_chan *chan = adapter->algo_data;
+ struct i2c_atr *atr = chan->atr;
+ struct i2c_atr_alias_pair *c2a;
+
+ atr->ops->detach_addr(atr, chan->chan_id, addr);
+
+ mutex_lock(&chan->alias_pairs_lock);
+
+ c2a = i2c_atr_find_mapping_by_addr(chan, addr);
+ if (!c2a) {
+ mutex_unlock(&chan->alias_pairs_lock);
+ return;
+ }
+
+ i2c_atr_release_alias(chan->alias_pool, c2a->alias);
+
+ dev_dbg(atr->dev,
+ "chan%u: detached alias 0x%02x from addr 0x%02x\n",
+ chan->chan_id, c2a->alias, addr);
+
+ i2c_atr_destroy_c2a(&c2a);
+
+ mutex_unlock(&chan->alias_pairs_lock);
+}
+
+static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *device)
+{
+ struct i2c_atr *atr = container_of(nb, struct i2c_atr, i2c_nb);
+ struct device *dev = device;
+ struct i2c_client *client;
+ u32 chan_id;
+ int ret;
+
+ client = i2c_verify_client(dev);
+ if (!client)
+ return NOTIFY_DONE;
+
+ /* Is the client in one of our adapters? */
+ for (chan_id = 0; chan_id < atr->max_adapters; ++chan_id) {
+ if (client->adapter == atr->adapter[chan_id])
+ break;
+ }
+
+ if (chan_id == atr->max_adapters)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ ret = i2c_atr_attach_addr(client->adapter, client->addr);
+ if (ret)
+ dev_err(atr->dev,
+ "Failed to attach remote client '%s': %d\n",
+ dev_name(dev), ret);
+ break;
+
+ case BUS_NOTIFY_REMOVED_DEVICE:
+ i2c_atr_detach_addr(client->adapter, client->addr);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
+{
+ struct i2c_atr_alias_pool *alias_pool;
+ struct device *dev = atr->dev;
+ size_t num_aliases;
+ unsigned int i;
+ u32 *aliases32;
+ int ret;
+
+ if (!fwnode_property_present(dev_fwnode(dev), "i2c-alias-pool")) {
+ num_aliases = 0;
+ } else {
+ ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool");
+ if (ret < 0) {
+ dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n",
+ ret);
+ return ret;
+ }
+
+ num_aliases = ret;
+ }
+
+ alias_pool = i2c_atr_alloc_alias_pool(num_aliases, true);
+ if (IS_ERR(alias_pool)) {
+ ret = PTR_ERR(alias_pool);
+ dev_err(dev, "Failed to allocate alias pool, err %d\n", ret);
+ return ret;
+ }
+
+ atr->alias_pool = alias_pool;
+
+ if (!alias_pool->size)
+ return 0;
+
+ aliases32 = kcalloc(num_aliases, sizeof(*aliases32), GFP_KERNEL);
+ if (!aliases32) {
+ ret = -ENOMEM;
+ goto err_free_alias_pool;
+ }
+
+ ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-alias-pool",
+ aliases32, num_aliases);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read 'i2c-alias-pool' property: %d\n",
+ ret);
+ goto err_free_aliases32;
+ }
+
+ for (i = 0; i < num_aliases; i++) {
+ if (!(aliases32[i] & 0xffff0000)) {
+ alias_pool->aliases[i] = aliases32[i];
+ continue;
+ }
+
+ dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n");
+ ret = -EINVAL;
+ goto err_free_aliases32;
+ }
+
+ kfree(aliases32);
+
+ dev_dbg(dev, "i2c-alias-pool has %zu aliases\n", alias_pool->size);
+
+ return 0;
+
+err_free_aliases32:
+ kfree(aliases32);
+err_free_alias_pool:
+ i2c_atr_free_alias_pool(alias_pool);
+ return ret;
+}
+
+struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
+ const struct i2c_atr_ops *ops, int max_adapters,
+ u32 flags)
+{
+ struct i2c_atr *atr;
+ int ret;
+
+ if (max_adapters > ATR_MAX_ADAPTERS)
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->attach_addr || !ops->detach_addr)
+ return ERR_PTR(-EINVAL);
+
+ atr = kzalloc(struct_size(atr, adapter, max_adapters), GFP_KERNEL);
+ if (!atr)
+ return ERR_PTR(-ENOMEM);
+
+ lockdep_register_key(&atr->lock_key);
+ mutex_init_with_key(&atr->lock, &atr->lock_key);
+
+ atr->parent = parent;
+ atr->dev = dev;
+ atr->ops = ops;
+ atr->max_adapters = max_adapters;
+ atr->flags = flags;
+
+ if (parent->algo->master_xfer)
+ atr->algo.xfer = i2c_atr_master_xfer;
+ if (parent->algo->smbus_xfer)
+ atr->algo.smbus_xfer = i2c_atr_smbus_xfer;
+ atr->algo.functionality = i2c_atr_functionality;
+
+ ret = i2c_atr_parse_alias_pool(atr);
+ if (ret)
+ goto err_destroy_mutex;
+
+ atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call;
+ ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb);
+ if (ret)
+ goto err_free_alias_pool;
+
+ return atr;
+
+err_free_alias_pool:
+ i2c_atr_free_alias_pool(atr->alias_pool);
+err_destroy_mutex:
+ mutex_destroy(&atr->lock);
+ lockdep_unregister_key(&atr->lock_key);
+ kfree(atr);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_new, "I2C_ATR");
+
+void i2c_atr_delete(struct i2c_atr *atr)
+{
+ unsigned int i;
+
+ for (i = 0; i < atr->max_adapters; ++i)
+ WARN_ON(atr->adapter[i]);
+
+ bus_unregister_notifier(&i2c_bus_type, &atr->i2c_nb);
+ i2c_atr_free_alias_pool(atr->alias_pool);
+ mutex_destroy(&atr->lock);
+ lockdep_unregister_key(&atr->lock_key);
+ kfree(atr);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, "I2C_ATR");
+
+int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc)
+{
+ struct fwnode_handle *bus_handle = desc->bus_handle;
+ struct i2c_adapter *parent = atr->parent;
+ char symlink_name[ATR_MAX_SYMLINK_LEN];
+ struct device *dev = atr->dev;
+ u32 chan_id = desc->chan_id;
+ struct i2c_atr_chan *chan;
+ int ret, idx;
+
+ if (chan_id >= atr->max_adapters) {
+ dev_err(dev, "No room for more i2c-atr adapters\n");
+ return -EINVAL;
+ }
+
+ if (atr->adapter[chan_id]) {
+ dev_err(dev, "Adapter %d already present\n", chan_id);
+ return -EEXIST;
+ }
+
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ if (!desc->parent)
+ desc->parent = dev;
+
+ chan->atr = atr;
+ chan->chan_id = chan_id;
+ INIT_LIST_HEAD(&chan->alias_pairs);
+ lockdep_register_key(&chan->alias_pairs_lock_key);
+ lockdep_register_key(&chan->orig_addrs_lock_key);
+ mutex_init_with_key(&chan->alias_pairs_lock, &chan->alias_pairs_lock_key);
+ mutex_init_with_key(&chan->orig_addrs_lock, &chan->orig_addrs_lock_key);
+
+ snprintf(chan->adap.name, sizeof(chan->adap.name), "i2c-%d-atr-%d",
+ i2c_adapter_id(parent), chan_id);
+ chan->adap.owner = THIS_MODULE;
+ chan->adap.algo = &atr->algo;
+ chan->adap.algo_data = chan;
+ chan->adap.dev.parent = desc->parent;
+ chan->adap.retries = parent->retries;
+ chan->adap.timeout = parent->timeout;
+ chan->adap.quirks = parent->quirks;
+ chan->adap.lock_ops = &i2c_atr_lock_ops;
+
+ if (bus_handle) {
+ device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle));
+ } else {
+ struct fwnode_handle *atr_node;
+ struct fwnode_handle *child;
+ u32 reg;
+
+ atr_node = device_get_named_child_node(dev, "i2c-atr");
+
+ fwnode_for_each_child_node(atr_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ continue;
+ if (chan_id == reg)
+ break;
+ }
+
+ device_set_node(&chan->adap.dev, child);
+ fwnode_handle_put(atr_node);
+ }
+
+ if (desc->num_aliases > 0) {
+ chan->alias_pool = i2c_atr_alloc_alias_pool(desc->num_aliases, false);
+ if (IS_ERR(chan->alias_pool)) {
+ ret = PTR_ERR(chan->alias_pool);
+ goto err_fwnode_put;
+ }
+
+ for (idx = 0; idx < desc->num_aliases; idx++)
+ chan->alias_pool->aliases[idx] = desc->aliases[idx];
+ } else {
+ chan->alias_pool = atr->alias_pool;
+ }
+
+ atr->adapter[chan_id] = &chan->adap;
+
+ ret = i2c_add_adapter(&chan->adap);
+ if (ret) {
+ dev_err(dev, "failed to add atr-adapter %u (error=%d)\n",
+ chan_id, ret);
+ goto err_free_alias_pool;
+ }
+
+ snprintf(symlink_name, sizeof(symlink_name), "channel-%u",
+ chan->chan_id);
+
+ ret = sysfs_create_link(&chan->adap.dev.kobj, &dev->kobj, "atr_device");
+ if (ret)
+ dev_warn(dev, "can't create symlink to atr device\n");
+ ret = sysfs_create_link(&dev->kobj, &chan->adap.dev.kobj, symlink_name);
+ if (ret)
+ dev_warn(dev, "can't create symlink for channel %u\n", chan_id);
+
+ dev_dbg(dev, "Added ATR child bus %d\n", i2c_adapter_id(&chan->adap));
+
+ return 0;
+
+err_free_alias_pool:
+ if (!chan->alias_pool->shared)
+ i2c_atr_free_alias_pool(chan->alias_pool);
+err_fwnode_put:
+ fwnode_handle_put(dev_fwnode(&chan->adap.dev));
+ mutex_destroy(&chan->orig_addrs_lock);
+ mutex_destroy(&chan->alias_pairs_lock);
+ lockdep_unregister_key(&chan->orig_addrs_lock_key);
+ lockdep_unregister_key(&chan->alias_pairs_lock_key);
+ kfree(chan);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_add_adapter, "I2C_ATR");
+
+void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id)
+{
+ char symlink_name[ATR_MAX_SYMLINK_LEN];
+ struct i2c_adapter *adap;
+ struct i2c_atr_chan *chan;
+ struct fwnode_handle *fwnode;
+ struct device *dev = atr->dev;
+
+ adap = atr->adapter[chan_id];
+ if (!adap)
+ return;
+
+ chan = adap->algo_data;
+ fwnode = dev_fwnode(&adap->dev);
+
+ dev_dbg(dev, "Removing ATR child bus %d\n", i2c_adapter_id(adap));
+
+ snprintf(symlink_name, sizeof(symlink_name), "channel-%u",
+ chan->chan_id);
+ sysfs_remove_link(&dev->kobj, symlink_name);
+ sysfs_remove_link(&chan->adap.dev.kobj, "atr_device");
+
+ i2c_del_adapter(adap);
+
+ if (!chan->alias_pool->shared)
+ i2c_atr_free_alias_pool(chan->alias_pool);
+
+ atr->adapter[chan_id] = NULL;
+
+ fwnode_handle_put(fwnode);
+ mutex_destroy(&chan->orig_addrs_lock);
+ mutex_destroy(&chan->alias_pairs_lock);
+ lockdep_unregister_key(&chan->orig_addrs_lock_key);
+ lockdep_unregister_key(&chan->alias_pairs_lock_key);
+ kfree(chan->orig_addrs);
+ kfree(chan);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_del_adapter, "I2C_ATR");
+
+void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data)
+{
+ atr->priv = data;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_set_driver_data, "I2C_ATR");
+
+void *i2c_atr_get_driver_data(struct i2c_atr *atr)
+{
+ return atr->priv;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_get_driver_data, "I2C_ATR");
+
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
+MODULE_DESCRIPTION("I2C Address Translator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index 509a6007cdf6..4df8ad092df3 100644
--- a/drivers/i2c/i2c-boardinfo.c
+++ b/drivers/i2c/i2c-boardinfo.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-boardinfo.c - collect pre-declarations of I2C devices
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/export.h>
@@ -56,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
*
* The board info passed can safely be __initdata, but be careful of embedded
* pointers (for platform_data, functions, etc) since that won't be copied.
- * Device properties are deep-copied though.
*/
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
@@ -81,16 +71,6 @@ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsig
devinfo->busnum = busnum;
devinfo->board_info = *info;
- if (info->properties) {
- devinfo->board_info.properties =
- property_entries_dup(info->properties);
- if (IS_ERR(devinfo->board_info.properties)) {
- status = PTR_ERR(devinfo->board_info.properties);
- kfree(devinfo);
- break;
- }
- }
-
if (info->resources) {
devinfo->board_info.resources =
kmemdup(info->resources,
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 272800692088..ed90858a27b7 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux I2C core ACPI support code
*
* Copyright (C) 2014 Intel Corp, Author: Lan Tianyu <tianyu.lan@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
*/
#include <linux/acpi.h>
@@ -30,7 +26,7 @@ struct gsb_buffer {
union {
u16 wdata;
u8 bdata;
- u8 data[0];
+ DECLARE_FLEX_ARRAY(u8, data);
};
} __packed;
@@ -43,6 +39,7 @@ struct i2c_acpi_lookup {
int index;
u32 speed;
u32 min_speed;
+ u32 force_speed;
};
/**
@@ -72,6 +69,38 @@ bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
}
EXPORT_SYMBOL_GPL(i2c_acpi_get_i2c_resource);
+static int i2c_acpi_resource_count(struct acpi_resource *ares, void *data)
+{
+ struct acpi_resource_i2c_serialbus *sb;
+ int *count = data;
+
+ if (i2c_acpi_get_i2c_resource(ares, &sb))
+ *count = *count + 1;
+
+ return 1;
+}
+
+/**
+ * i2c_acpi_client_count - Count the number of I2cSerialBus resources
+ * @adev: ACPI device
+ *
+ * Returns the number of I2cSerialBus resources in the ACPI-device's
+ * resource-list; or a negative error code.
+ */
+int i2c_acpi_client_count(struct acpi_device *adev)
+{
+ int ret, count = 0;
+ LIST_HEAD(r);
+
+ ret = acpi_dev_get_resources(adev, &r, i2c_acpi_resource_count, &count);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&r);
+ return count;
+}
+EXPORT_SYMBOL_GPL(i2c_acpi_client_count);
+
static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
{
struct i2c_acpi_lookup *lookup = data;
@@ -108,6 +137,11 @@ static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
{}
};
+struct i2c_acpi_irq_context {
+ int irq;
+ bool wake_capable;
+};
+
static int i2c_acpi_do_lookup(struct acpi_device *adev,
struct i2c_acpi_lookup *lookup)
{
@@ -115,10 +149,12 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev,
struct list_head resource_list;
int ret;
- if (acpi_bus_get_status(adev) || !adev->status.present ||
- acpi_device_enumerated(adev))
+ if (acpi_bus_get_status(adev))
return -EINVAL;
+ if (!acpi_dev_ready_for_enumeration(adev))
+ return -ENODEV;
+
if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0)
return -ENODEV;
@@ -137,13 +173,67 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev,
return 0;
}
+static int i2c_acpi_add_irq_resource(struct acpi_resource *ares, void *data)
+{
+ struct i2c_acpi_irq_context *irq_ctx = data;
+ struct resource r;
+
+ if (irq_ctx->irq > 0)
+ return 1;
+
+ if (!acpi_dev_resource_interrupt(ares, 0, &r))
+ return 1;
+
+ irq_ctx->irq = i2c_dev_irq_from_resources(&r, 1);
+ irq_ctx->wake_capable = r.flags & IORESOURCE_IRQ_WAKECAPABLE;
+
+ return 1; /* No need to add resource to the list */
+}
+
+/**
+ * i2c_acpi_get_irq - get device IRQ number from ACPI
+ * @client: Pointer to the I2C client device
+ * @wake_capable: Set to true if the IRQ is wake capable
+ *
+ * Find the IRQ number used by a specific client device.
+ *
+ * Return: The IRQ number or an error code.
+ */
+int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&client->dev);
+ struct list_head resource_list;
+ struct i2c_acpi_irq_context irq_ctx = {
+ .irq = -ENOENT,
+ };
+ int ret;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ ret = acpi_dev_get_resources(adev, &resource_list,
+ i2c_acpi_add_irq_resource, &irq_ctx);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (irq_ctx.irq == -ENOENT)
+ irq_ctx.irq = acpi_dev_gpio_irq_wake_get(adev, 0, &irq_ctx.wake_capable);
+
+ if (irq_ctx.irq < 0)
+ return irq_ctx.irq;
+
+ if (wake_capable)
+ *wake_capable = irq_ctx.wake_capable;
+
+ return irq_ctx.irq;
+}
+
static int i2c_acpi_get_info(struct acpi_device *adev,
struct i2c_board_info *info,
struct i2c_adapter *adapter,
acpi_handle *adapter_handle)
{
- struct list_head resource_list;
- struct resource_entry *entry;
struct i2c_acpi_lookup lookup;
int ret;
@@ -151,19 +241,23 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
lookup.info = info;
lookup.index = -1;
+ if (acpi_device_enumerated(adev))
+ return -EINVAL;
+
ret = i2c_acpi_do_lookup(adev, &lookup);
if (ret)
return ret;
if (adapter) {
/* The adapter must match the one in I2cSerialBus() connector */
- if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle)
+ if (!device_match_acpi_handle(&adapter->dev, lookup.adapter_handle))
return -ENODEV;
} else {
struct acpi_device *adapter_adev;
/* The adapter must be present */
- if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev))
+ adapter_adev = acpi_fetch_acpi_dev(lookup.adapter_handle);
+ if (!adapter_adev)
return -ENODEV;
if (acpi_bus_get_status(adapter_adev) ||
!adapter_adev->status.present)
@@ -174,21 +268,6 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
if (adapter_handle)
*adapter_handle = lookup.adapter_handle;
- /* Then fill IRQ number if any */
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
- if (ret < 0)
- return -EINVAL;
-
- resource_list_for_each_entry(entry, &resource_list) {
- if (resource_type(entry->res) == IORESOURCE_IRQ) {
- info->irq = entry->res->start;
- break;
- }
- }
-
- acpi_dev_free_resource_list(&resource_list);
-
acpi_set_modalias(adev, dev_name(&adev->dev), info->type,
sizeof(info->type));
@@ -199,28 +278,28 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter,
struct acpi_device *adev,
struct i2c_board_info *info)
{
+ /*
+ * Skip registration on boards where the ACPI tables are
+ * known to contain bogus I2C devices.
+ */
+ if (acpi_quirk_skip_i2c_client_enumeration(adev))
+ return;
+
adev->power.flags.ignore_parent = true;
acpi_device_set_enumerated(adev);
- if (!i2c_new_device(adapter, info)) {
+ if (IS_ERR(i2c_new_client_device(adapter, info)))
adev->power.flags.ignore_parent = false;
- dev_err(&adapter->dev,
- "failed to add I2C device %s from ACPI\n",
- dev_name(&adev->dev));
- }
}
static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_adapter *adapter = data;
- struct acpi_device *adev;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct i2c_board_info info;
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (i2c_acpi_get_info(adev, &info, adapter, NULL))
+ if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL))
return AE_OK;
i2c_acpi_register_device(adapter, adev, &info);
@@ -240,6 +319,7 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
*/
void i2c_acpi_register_devices(struct i2c_adapter *adap)
{
+ struct acpi_device *adev;
acpi_status status;
if (!has_acpi_companion(&adap->dev))
@@ -251,28 +331,57 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap)
adap, NULL);
if (ACPI_FAILURE(status))
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
-}
-const struct acpi_device_id *
-i2c_acpi_match_device(const struct acpi_device_id *matches,
- struct i2c_client *client)
-{
- if (!(client && matches))
- return NULL;
+ if (!adap->dev.parent)
+ return;
+
+ adev = ACPI_COMPANION(adap->dev.parent);
+ if (!adev)
+ return;
- return acpi_match_device(matches, &client->dev);
+ acpi_dev_clear_dependencies(adev);
}
+static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
+ /*
+ * These Silead touchscreen controllers only work at 400KHz, for
+ * some reason they do not work at 100KHz. On some devices the ACPI
+ * tables list another device at their bus as only being capable
+ * of 100KHz, testing has shown that these other devices work fine
+ * at 400KHz (as can be expected of any recent i2c hw) so we force
+ * the speed of the bus to 400 KHz if a Silead device is present.
+ */
+ { "MSSL1680", 0 },
+ {}
+};
+
+static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = {
+ /*
+ * When a 400KHz freq is used on this model of ELAN touchpad in Linux,
+ * excessive smoothing (similar to when the touchpad's firmware detects
+ * a noisy signal) is sometimes applied. As some devices' (e.g, Lenovo
+ * V15 G4) ACPI tables specify a 400KHz frequency for this device and
+ * some I2C busses (e.g, Designware I2C) default to a 400KHz freq,
+ * force the speed to 100KHz as a workaround.
+ *
+ * For future investigation: This problem may be related to the default
+ * HCNT/LCNT values given by some busses' drivers, because they are not
+ * specified in the aforementioned devices' ACPI tables, and because
+ * the device works without issues on Windows at what is expected to be
+ * a 400KHz frequency. The root cause of the issue is not known.
+ */
+ { "DLL0945", 0 },
+ { "ELAN06FA", 0 },
+ {}
+};
+
static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_acpi_lookup *lookup = data;
- struct acpi_device *adev;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (i2c_acpi_do_lookup(adev, lookup))
+ if (!adev || i2c_acpi_do_lookup(adev, lookup))
return AE_OK;
if (lookup->search_handle != lookup->adapter_handle)
@@ -281,6 +390,12 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
if (lookup->speed <= lookup->min_speed)
lookup->min_speed = lookup->speed;
+ if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0)
+ lookup->force_speed = I2C_MAX_FAST_MODE_FREQ;
+
+ if (acpi_match_device_ids(adev, i2c_acpi_force_100khz_device_ids) == 0)
+ lookup->force_speed = I2C_MAX_STANDARD_MODE_FREQ;
+
return AE_OK;
}
@@ -318,41 +433,44 @@ u32 i2c_acpi_find_bus_speed(struct device *dev)
return 0;
}
- return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
+ if (lookup.force_speed) {
+ if (lookup.force_speed != lookup.min_speed)
+ dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n",
+ lookup.min_speed, lookup.force_speed);
+ return lookup.force_speed;
+ } else if (lookup.min_speed != UINT_MAX) {
+ return lookup.min_speed;
+ } else {
+ return 0;
+ }
}
EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);
-static int i2c_acpi_find_match_adapter(struct device *dev, void *data)
+struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle)
{
- struct i2c_adapter *adapter = i2c_verify_adapter(dev);
+ struct i2c_adapter *adapter;
+ struct device *dev;
+
+ dev = bus_find_device(&i2c_bus_type, NULL, handle, device_match_acpi_handle);
+ if (!dev)
+ return NULL;
+ adapter = i2c_verify_adapter(dev);
if (!adapter)
- return 0;
+ put_device(dev);
- return ACPI_HANDLE(dev) == (acpi_handle)data;
+ return adapter;
}
+EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle);
-static int i2c_acpi_find_match_device(struct device *dev, void *data)
+static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev)
{
- return ACPI_COMPANION(dev) == data;
+ return i2c_find_device_by_fwnode(acpi_fwnode_handle(adev));
}
-static struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle)
+static struct i2c_adapter *i2c_acpi_find_adapter_by_adev(struct acpi_device *adev)
{
- struct device *dev;
-
- dev = bus_find_device(&i2c_bus_type, NULL, handle,
- i2c_acpi_find_match_adapter);
- return dev ? i2c_verify_adapter(dev) : NULL;
-}
-
-static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev)
-{
- struct device *dev;
-
- dev = bus_find_device(&i2c_bus_type, NULL, adev,
- i2c_acpi_find_match_device);
- return dev ? i2c_verify_client(dev) : NULL;
+ return i2c_find_adapter_by_fwnode(acpi_fwnode_handle(adev));
}
static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value,
@@ -374,17 +492,24 @@ static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value,
break;
i2c_acpi_register_device(adapter, adev, &info);
+ put_device(&adapter->dev);
break;
case ACPI_RECONFIG_DEVICE_REMOVE:
if (!acpi_device_enumerated(adev))
break;
client = i2c_acpi_find_client_by_adev(adev);
- if (!client)
- break;
+ if (client) {
+ i2c_unregister_device(client);
+ put_device(&client->dev);
+ }
+
+ adapter = i2c_acpi_find_adapter_by_adev(adev);
+ if (adapter) {
+ acpi_unbind_one(&adapter->dev);
+ put_device(&adapter->dev);
+ }
- i2c_unregister_device(client);
- put_device(&client->dev);
break;
}
@@ -396,8 +521,8 @@ struct notifier_block i2c_acpi_notifier = {
};
/**
- * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource
- * @dev: Device owning the ACPI resources to get the client from
+ * i2c_acpi_new_device_by_fwnode - Create i2c-client for the Nth I2cSerialBus resource
+ * @fwnode: fwnode with the ACPI resources to get the client from
* @index: Index of ACPI resource to get
* @info: describes the I2C device; note this is modified (addr gets set)
* Context: can sleep
@@ -407,24 +532,25 @@ struct notifier_block i2c_acpi_notifier = {
* resources, in that case this function can be used to create an i2c-client
* for other I2cSerialBus resources in the Current Resource Settings table.
*
- * Also see i2c_new_device, which this function calls to create the i2c-client.
+ * Also see i2c_new_client_device, which this function calls to create the
+ * i2c-client.
*
* Returns a pointer to the new i2c-client, or error pointer in case of failure.
* Specifically, -EPROBE_DEFER is returned if the adapter is not found.
*/
-struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
- struct i2c_board_info *info)
+struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,
+ int index,
+ struct i2c_board_info *info)
{
struct i2c_acpi_lookup lookup;
struct i2c_adapter *adapter;
- struct i2c_client *client;
struct acpi_device *adev;
LIST_HEAD(resource_list);
int ret;
- adev = ACPI_COMPANION(dev);
+ adev = to_acpi_device_node(fwnode);
if (!adev)
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ENODEV);
memset(&lookup, 0, sizeof(lookup));
lookup.info = info;
@@ -445,13 +571,19 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
if (!adapter)
return ERR_PTR(-EPROBE_DEFER);
- client = i2c_new_device(adapter, info);
- if (!client)
- return ERR_PTR(-ENODEV);
+ return i2c_new_client_device(adapter, info);
+}
+EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode);
+
+bool i2c_acpi_waive_d0_probe(struct device *dev)
+{
+ struct i2c_driver *driver = to_i2c_driver(dev->driver);
+ struct acpi_device *adev = ACPI_COMPANION(dev);
- return client;
+ return driver->flags & I2C_DRV_ACPI_WAIVE_D0_PROBE &&
+ adev && adev->power.state_for_enumeration >= adev->power.state;
}
-EXPORT_SYMBOL_GPL(i2c_acpi_new_device);
+EXPORT_SYMBOL_GPL(i2c_acpi_waive_d0_probe);
#ifdef CONFIG_ACPI_I2C_OPREGION
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
@@ -685,7 +817,6 @@ int i2c_acpi_install_space_handler(struct i2c_adapter *adapter)
return -ENOMEM;
}
- acpi_walk_dep_device_list(handle);
return 0;
}
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 28460f6a60cc..ae7e9c8b65a6 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux I2C core
*
@@ -6,16 +7,7 @@
* Mux support by Rodolfo Giometti <giometti@enneenne.com> and
* Michael Lawnick <michael.lawnick.ext@nsn.com>
*
- * Copyright (C) 2013-2017 Wolfram Sang <wsa@the-dreams.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * Copyright (C) 2013-2017 Wolfram Sang <wsa@kernel.org>
*/
#define pr_fmt(fmt) "i2c-core: " fmt
@@ -24,6 +16,7 @@
#include <linux/acpi.h>
#include <linux/clk/clk-conf.h>
#include <linux/completion.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -32,20 +25,23 @@
#include <linux/i2c-smbus.h>
#include <linux/idr.h>
#include <linux/init.h>
-#include <linux/irqflags.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
#include <linux/property.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include "i2c-core.h"
@@ -72,6 +68,8 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
static bool is_registered;
+static struct dentry *i2c_debugfs_root;
+
int i2c_transfer_trace_reg(void)
{
static_branch_inc(&i2c_trace_msg_key);
@@ -83,6 +81,27 @@ void i2c_transfer_trace_unreg(void)
static_branch_dec(&i2c_trace_msg_key);
}
+const char *i2c_freq_mode_string(u32 bus_freq_hz)
+{
+ switch (bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ return "Standard Mode (100 kHz)";
+ case I2C_MAX_FAST_MODE_FREQ:
+ return "Fast Mode (400 kHz)";
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ return "Fast Mode Plus (1.0 MHz)";
+ case I2C_MAX_TURBO_MODE_FREQ:
+ return "Turbo Mode (1.4 MHz)";
+ case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ return "High Speed Mode (3.4 MHz)";
+ case I2C_MAX_ULTRA_FAST_MODE_FREQ:
+ return "Ultra Fast Mode (5.0 MHz)";
+ default:
+ return "Unknown Mode";
+ }
+}
+EXPORT_SYMBOL_GPL(i2c_freq_mode_string);
+
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
@@ -98,10 +117,29 @@ const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
}
EXPORT_SYMBOL_GPL(i2c_match_id);
-static int i2c_device_match(struct device *dev, struct device_driver *drv)
+const void *i2c_get_match_data(const struct i2c_client *client)
+{
+ struct i2c_driver *driver = to_i2c_driver(client->dev.driver);
+ const struct i2c_device_id *match;
+ const void *data;
+
+ data = device_get_match_data(&client->dev);
+ if (!data) {
+ match = i2c_match_id(driver->id_table, client);
+ if (!match)
+ return NULL;
+
+ data = (const void *)match->driver_data;
+ }
+
+ return data;
+}
+EXPORT_SYMBOL(i2c_get_match_data);
+
+static int i2c_device_match(struct device *dev, const struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
- struct i2c_driver *driver;
+ const struct i2c_driver *driver;
/* Attempt an OF style match */
@@ -121,9 +159,9 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
return 0;
}
-static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int i2c_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct i2c_client *client = to_i2c_client(dev);
+ const struct i2c_client *client = to_i2c_client(dev);
int rc;
rc = of_device_uevent_modalias(dev, env);
@@ -185,19 +223,22 @@ static int i2c_generic_bus_free(struct i2c_adapter *adap)
int i2c_generic_scl_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- int i = 0, scl = 1, ret;
+ int i = 0, scl = 1, ret = 0;
if (bri->prepare_recovery)
bri->prepare_recovery(adap);
+ if (bri->pinctrl)
+ pinctrl_select_state(bri->pinctrl, bri->pins_gpio);
/*
* If we can set SDA, we will always create a STOP to ensure additional
* pulses will do no harm. This is achieved by letting SDA follow SCL
* half a cycle later. Check the 'incomplete_write_byte' fault injector
- * for details.
+ * for details. Note that we must honour tsu:sto, 4us, but lets use 5us
+ * here for simplicity.
*/
bri->set_scl(adap, scl);
- ndelay(RECOVERY_NDELAY / 2);
+ ndelay(RECOVERY_NDELAY);
if (bri->set_sda)
bri->set_sda(adap, scl);
ndelay(RECOVERY_NDELAY / 2);
@@ -219,7 +260,13 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
scl = !scl;
bri->set_scl(adap, scl);
/* Creating STOP again, see above */
- ndelay(RECOVERY_NDELAY / 2);
+ if (scl) {
+ /* Honour minimum tsu:sto */
+ ndelay(RECOVERY_NDELAY);
+ } else {
+ /* Honour minimum tf and thd:dat */
+ ndelay(RECOVERY_NDELAY / 2);
+ }
if (bri->set_sda)
bri->set_sda(adap, scl);
ndelay(RECOVERY_NDELAY / 2);
@@ -237,6 +284,8 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
if (bri->unprepare_recovery)
bri->unprepare_recovery(adap);
+ if (bri->pinctrl)
+ pinctrl_select_state(bri->pinctrl, bri->pins_default);
return ret;
}
@@ -245,23 +294,149 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
int i2c_recover_bus(struct i2c_adapter *adap)
{
if (!adap->bus_recovery_info)
- return -EOPNOTSUPP;
+ return -EBUSY;
dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
return adap->bus_recovery_info->recover_bus(adap);
}
EXPORT_SYMBOL_GPL(i2c_recover_bus);
-static void i2c_init_recovery(struct i2c_adapter *adap)
+static void i2c_gpio_init_pinctrl_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+ struct device *dev = &adap->dev;
+ struct pinctrl *p = bri->pinctrl ?: dev_pinctrl(dev->parent);
+
+ bri->pinctrl = p;
+
+ /*
+ * we can't change states without pinctrl, so remove the states if
+ * populated
+ */
+ if (!p) {
+ bri->pins_default = NULL;
+ bri->pins_gpio = NULL;
+ return;
+ }
+
+ if (!bri->pins_default) {
+ bri->pins_default = pinctrl_lookup_state(p,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(bri->pins_default)) {
+ dev_dbg(dev, PINCTRL_STATE_DEFAULT " state not found for GPIO recovery\n");
+ bri->pins_default = NULL;
+ }
+ }
+ if (!bri->pins_gpio) {
+ bri->pins_gpio = pinctrl_lookup_state(p, "gpio");
+ if (IS_ERR(bri->pins_gpio))
+ bri->pins_gpio = pinctrl_lookup_state(p, "recovery");
+
+ if (IS_ERR(bri->pins_gpio)) {
+ dev_dbg(dev, "no gpio or recovery state found for GPIO recovery\n");
+ bri->pins_gpio = NULL;
+ }
+ }
+
+ /* for pinctrl state changes, we need all the information */
+ if (bri->pins_default && bri->pins_gpio) {
+ dev_info(dev, "using pinctrl states for GPIO recovery");
+ } else {
+ bri->pinctrl = NULL;
+ bri->pins_default = NULL;
+ bri->pins_gpio = NULL;
+ }
+}
+
+static int i2c_gpio_init_generic_recovery(struct i2c_adapter *adap)
+{
+ struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+ struct device *dev = &adap->dev;
+ struct gpio_desc *gpiod;
+ int ret = 0;
+
+ /*
+ * don't touch the recovery information if the driver is not using
+ * generic SCL recovery
+ */
+ if (bri->recover_bus && bri->recover_bus != i2c_generic_scl_recovery)
+ return 0;
+
+ /*
+ * pins might be taken as GPIO, so we should inform pinctrl about
+ * this and move the state to GPIO
+ */
+ if (bri->pinctrl)
+ pinctrl_select_state(bri->pinctrl, bri->pins_gpio);
+
+ /*
+ * if there is incomplete or no recovery information, see if generic
+ * GPIO recovery is available
+ */
+ if (!bri->scl_gpiod) {
+ gpiod = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
+ if (PTR_ERR(gpiod) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto cleanup_pinctrl_state;
+ }
+ if (!IS_ERR(gpiod)) {
+ bri->scl_gpiod = gpiod;
+ bri->recover_bus = i2c_generic_scl_recovery;
+ dev_info(dev, "using generic GPIOs for recovery\n");
+ }
+ }
+
+ /* SDA GPIOD line is optional, so we care about DEFER only */
+ if (!bri->sda_gpiod) {
+ /*
+ * We have SCL. Pull SCL low and wait a bit so that SDA glitches
+ * have no effect.
+ */
+ gpiod_direction_output(bri->scl_gpiod, 0);
+ udelay(10);
+ gpiod = devm_gpiod_get(dev, "sda", GPIOD_IN);
+
+ /* Wait a bit in case of a SDA glitch, and then release SCL. */
+ udelay(10);
+ gpiod_direction_output(bri->scl_gpiod, 1);
+
+ if (PTR_ERR(gpiod) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto cleanup_pinctrl_state;
+ }
+ if (!IS_ERR(gpiod))
+ bri->sda_gpiod = gpiod;
+ }
+
+cleanup_pinctrl_state:
+ /* change the state of the pins back to their default state */
+ if (bri->pinctrl)
+ pinctrl_select_state(bri->pinctrl, bri->pins_default);
+
+ return ret;
+}
+
+static int i2c_gpio_init_recovery(struct i2c_adapter *adap)
+{
+ i2c_gpio_init_pinctrl_recovery(adap);
+ return i2c_gpio_init_generic_recovery(adap);
+}
+
+static int i2c_init_recovery(struct i2c_adapter *adap)
+{
+ struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+ bool is_error_level = true;
char *err_str;
if (!bri)
- return;
+ return 0;
+
+ if (i2c_gpio_init_recovery(adap) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
if (!bri->recover_bus) {
- err_str = "no recover_bus() found";
+ err_str = "no suitable method provided";
+ is_error_level = false;
goto err;
}
@@ -274,10 +449,7 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
if (gpiod_get_direction(bri->sda_gpiod) == 0)
bri->set_sda = set_sda_gpio_value;
}
- return;
- }
-
- if (bri->recover_bus == i2c_generic_scl_recovery) {
+ } else if (bri->recover_bus == i2c_generic_scl_recovery) {
/* Generic SCL recovery */
if (!bri->set_scl || !bri->get_scl) {
err_str = "no {get|set}_scl() found";
@@ -289,10 +461,15 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
}
}
- return;
+ return 0;
err:
- dev_err(&adap->dev, "Not using recovery: %s\n", err_str);
+ if (is_error_level)
+ dev_err(&adap->dev, "Not using recovery: %s\n", err_str);
+ else
+ dev_dbg(&adap->dev, "Not using recovery: %s\n", err_str);
adap->bus_recovery_info = NULL;
+
+ return -EINVAL;
}
static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
@@ -313,30 +490,40 @@ static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
static int i2c_device_probe(struct device *dev)
{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
+ bool do_power_on;
int status;
if (!client)
return 0;
- driver = to_i2c_driver(dev->driver);
+ client->irq = client->init_irq;
- if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
+ if (!client->irq) {
int irq = -ENOENT;
if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
dev_dbg(dev, "Using Host Notify IRQ\n");
+ /* Keep adapter active when Host Notify is required */
+ pm_runtime_get_sync(&client->adapter->dev);
irq = i2c_smbus_host_notify_to_irq(client);
- } else if (dev->of_node) {
- irq = of_irq_get_byname(dev->of_node, "irq");
+ } else if (is_of_node(fwnode)) {
+ irq = fwnode_irq_get_byname(fwnode, "irq");
if (irq == -EINVAL || irq == -ENODATA)
- irq = of_irq_get(dev->of_node, 0);
- } else if (ACPI_COMPANION(dev)) {
- irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
+ irq = fwnode_irq_get(fwnode, 0);
+ } else if (is_acpi_device_node(fwnode)) {
+ bool wake_capable;
+
+ irq = i2c_acpi_get_irq(client, &wake_capable);
+ if (irq > 0 && wake_capable)
+ client->flags |= I2C_CLIENT_WAKE;
+ }
+ if (irq == -EPROBE_DEFER) {
+ status = dev_err_probe(dev, irq, "can't get irq\n");
+ goto put_sync_adapter;
}
- if (irq == -EPROBE_DEFER)
- return irq;
if (irq < 0)
irq = 0;
@@ -344,22 +531,26 @@ static int i2c_device_probe(struct device *dev)
client->irq = irq;
}
+ driver = to_i2c_driver(dev->driver);
+
/*
* An I2C ID table is not mandatory, if and only if, a suitable OF
* or ACPI ID table is supplied for the probing device.
*/
if (!driver->id_table &&
- !i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&
- !i2c_of_match_device(dev->driver->of_match_table, client))
- return -ENODEV;
+ !acpi_driver_match_device(dev, dev->driver) &&
+ !i2c_of_match_device(dev->driver->of_match_table, client)) {
+ status = -ENODEV;
+ goto put_sync_adapter;
+ }
if (client->flags & I2C_CLIENT_WAKE) {
- int wakeirq = -ENOENT;
+ int wakeirq;
- if (dev->of_node) {
- wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
- if (wakeirq == -EPROBE_DEFER)
- return wakeirq;
+ wakeirq = fwnode_irq_get_byname(fwnode, "wakeup");
+ if (wakeirq == -EPROBE_DEFER) {
+ status = dev_err_probe(dev, wakeirq, "can't get wakeirq\n");
+ goto put_sync_adapter;
}
device_init_wakeup(&client->dev, true);
@@ -377,62 +568,79 @@ static int i2c_device_probe(struct device *dev)
dev_dbg(dev, "probe\n");
- status = of_clk_set_defaults(dev->of_node, false);
+ status = of_clk_set_defaults(to_of_node(fwnode), false);
if (status < 0)
goto err_clear_wakeup_irq;
- status = dev_pm_domain_attach(&client->dev, true);
+ do_power_on = !i2c_acpi_waive_d0_probe(dev);
+ status = dev_pm_domain_attach(&client->dev, PD_FLAG_DETACH_POWER_OFF |
+ (do_power_on ? PD_FLAG_ATTACH_POWER_ON : 0));
if (status)
goto err_clear_wakeup_irq;
- /*
- * When there are no more users of probe(),
- * rename probe_new to probe.
- */
- if (driver->probe_new)
- status = driver->probe_new(client);
- else if (driver->probe)
- status = driver->probe(client,
- i2c_match_id(driver->id_table, client));
+ client->devres_group_id = devres_open_group(&client->dev, NULL,
+ GFP_KERNEL);
+ if (!client->devres_group_id) {
+ status = -ENOMEM;
+ goto err_clear_wakeup_irq;
+ }
+
+ client->debugfs = debugfs_create_dir(dev_name(&client->dev),
+ client->adapter->debugfs);
+
+ if (driver->probe)
+ status = driver->probe(client);
else
status = -EINVAL;
+ /*
+ * Note that we are not closing the devres group opened above so
+ * even resources that were attached to the device after probe is
+ * run are released when i2c_device_remove() is executed. This is
+ * needed as some drivers would allocate additional resources,
+ * for example when updating firmware.
+ */
+
if (status)
- goto err_detach_pm_domain;
+ goto err_release_driver_resources;
return 0;
-err_detach_pm_domain:
- dev_pm_domain_detach(&client->dev, true);
+err_release_driver_resources:
+ debugfs_remove_recursive(client->debugfs);
+ devres_release_group(&client->dev, client->devres_group_id);
err_clear_wakeup_irq:
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
+put_sync_adapter:
+ if (client->flags & I2C_CLIENT_HOST_NOTIFY)
+ pm_runtime_put_sync(&client->adapter->dev);
+
return status;
}
-static int i2c_device_remove(struct device *dev)
+static void i2c_device_remove(struct device *dev)
{
- struct i2c_client *client = i2c_verify_client(dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver;
- int status = 0;
-
- if (!client || !dev->driver)
- return 0;
driver = to_i2c_driver(dev->driver);
if (driver->remove) {
dev_dbg(dev, "remove\n");
- status = driver->remove(client);
+
+ driver->remove(client);
}
- dev_pm_domain_detach(&client->dev, true);
+ debugfs_remove_recursive(client->debugfs);
+
+ devres_release_group(&client->dev, client->devres_group_id);
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
client->irq = 0;
-
- return status;
+ if (client->flags & I2C_CLIENT_HOST_NOTIFY)
+ pm_runtime_put(&client->adapter->dev);
}
static void i2c_device_shutdown(struct device *dev)
@@ -445,6 +653,8 @@ static void i2c_device_shutdown(struct device *dev)
driver = to_i2c_driver(dev->driver);
if (driver->shutdown)
driver->shutdown(client);
+ else if (client->irq > 0)
+ disable_irq(client->irq);
}
static void i2c_client_dev_release(struct device *dev)
@@ -453,15 +663,15 @@ static void i2c_client_dev_release(struct device *dev)
}
static ssize_t
-show_name(struct device *dev, struct device_attribute *attr, char *buf)
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static ssize_t
-show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
int len;
@@ -470,13 +680,13 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
if (len != -ENODEV)
return len;
- len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
+ len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
if (len != -ENODEV)
return len;
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
}
-static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+static DEVICE_ATTR_RO(modalias);
static struct attribute *i2c_dev_attrs[] = {
&dev_attr_name.attr,
@@ -486,7 +696,7 @@ static struct attribute *i2c_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(i2c_dev);
-struct bus_type i2c_bus_type = {
+const struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
@@ -495,7 +705,7 @@ struct bus_type i2c_bus_type = {
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
-struct device_type i2c_client_type = {
+const struct device_type i2c_client_type = {
.groups = i2c_dev_groups,
.uevent = i2c_device_uevent,
.release = i2c_client_dev_release,
@@ -683,8 +893,8 @@ static void i2c_dev_set_name(struct i2c_adapter *adap,
i2c_encode_flags_to_addr(client));
}
-static int i2c_dev_irq_from_resources(const struct resource *resources,
- unsigned int num_resources)
+int i2c_dev_irq_from_resources(const struct resource *resources,
+ unsigned int num_resources)
{
struct irq_data *irqd;
int i;
@@ -709,8 +919,29 @@ static int i2c_dev_irq_from_resources(const struct resource *resources,
return 0;
}
+/*
+ * Serialize device instantiation in case it can be instantiated explicitly
+ * and by auto-detection
+ */
+static int i2c_lock_addr(struct i2c_adapter *adap, unsigned short addr,
+ unsigned short flags)
+{
+ if (!(flags & I2C_CLIENT_TEN) &&
+ test_and_set_bit(addr, adap->addrs_in_instantiation))
+ return -EBUSY;
+
+ return 0;
+}
+
+static void i2c_unlock_addr(struct i2c_adapter *adap, unsigned short addr,
+ unsigned short flags)
+{
+ if (!(flags & I2C_CLIENT_TEN))
+ clear_bit(addr, adap->addrs_in_instantiation);
+}
+
/**
- * i2c_new_device - instantiate an i2c device
+ * i2c_new_client_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
@@ -723,17 +954,19 @@ static int i2c_dev_irq_from_resources(const struct resource *resources,
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
- * i2c_unregister_device(); or NULL to indicate an error.
+ * i2c_unregister_device(); or an ERR_PTR to describe the error.
*/
struct i2c_client *
-i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
+i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
- struct i2c_client *client;
- int status;
+ struct fwnode_handle *fwnode = info->fwnode;
+ struct i2c_client *client;
+ bool need_put = false;
+ int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
- return NULL;
+ return ERR_PTR(-ENOMEM);
client->adapter = adap;
@@ -741,12 +974,12 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->flags = info->flags;
client->addr = info->addr;
- client->irq = info->irq;
- if (!client->irq)
- client->irq = i2c_dev_irq_from_resources(info->resources,
+ client->init_irq = info->irq;
+ if (!client->init_irq)
+ client->init_irq = i2c_dev_irq_from_resources(info->resources,
info->num_resources);
- strlcpy(client->name, info->type, sizeof(client->name));
+ strscpy(client->name, info->type, sizeof(client->name));
status = i2c_check_addr_validity(client->addr, client->flags);
if (status) {
@@ -755,6 +988,10 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
goto out_err_silent;
}
+ status = i2c_lock_addr(adap, client->addr, client->flags);
+ if (status)
+ goto out_err_silent;
+
/* Check for address business */
status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
if (status)
@@ -763,80 +1000,119 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
- client->dev.of_node = of_node_get(info->of_node);
- client->dev.fwnode = info->fwnode;
- i2c_dev_set_name(adap, client, info);
+ device_enable_async_suspend(&client->dev);
+
+ device_set_node(&client->dev, fwnode_handle_get(fwnode));
- if (info->properties) {
- status = device_add_properties(&client->dev, info->properties);
+ if (info->swnode) {
+ status = device_add_software_node(&client->dev, info->swnode);
if (status) {
dev_err(&adap->dev,
- "Failed to add properties to client %s: %d\n",
+ "Failed to add software node to client %s: %d\n",
client->name, status);
- goto out_err_put_of_node;
+ goto out_err_put_fwnode;
}
}
+ i2c_dev_set_name(adap, client, info);
status = device_register(&client->dev);
if (status)
- goto out_free_props;
+ goto out_remove_swnode;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
+ i2c_unlock_addr(adap, client->addr, client->flags);
+
return client;
-out_free_props:
- if (info->properties)
- device_remove_properties(&client->dev);
-out_err_put_of_node:
- of_node_put(info->of_node);
+out_remove_swnode:
+ device_remove_software_node(&client->dev);
+ need_put = true;
+out_err_put_fwnode:
+ fwnode_handle_put(fwnode);
out_err:
dev_err(&adap->dev,
"Failed to register i2c client %s at 0x%02x (%d)\n",
client->name, client->addr, status);
+ i2c_unlock_addr(adap, client->addr, client->flags);
out_err_silent:
- kfree(client);
- return NULL;
+ if (need_put)
+ put_device(&client->dev);
+ else
+ kfree(client);
+ return ERR_PTR(status);
}
-EXPORT_SYMBOL_GPL(i2c_new_device);
-
+EXPORT_SYMBOL_GPL(i2c_new_client_device);
/**
- * i2c_unregister_device - reverse effect of i2c_new_device()
- * @client: value returned from i2c_new_device()
+ * i2c_unregister_device - reverse effect of i2c_new_*_device()
+ * @client: value returned from i2c_new_*_device()
* Context: can sleep
*/
void i2c_unregister_device(struct i2c_client *client)
{
- if (!client)
+ struct fwnode_handle *fwnode;
+
+ if (IS_ERR_OR_NULL(client))
return;
- if (client->dev.of_node) {
- of_node_clear_flag(client->dev.of_node, OF_POPULATED);
- of_node_put(client->dev.of_node);
- }
+ fwnode = dev_fwnode(&client->dev);
+ if (is_of_node(fwnode))
+ of_node_clear_flag(to_of_node(fwnode), OF_POPULATED);
+ else if (is_acpi_device_node(fwnode))
+ acpi_device_clear_enumerated(to_acpi_device_node(fwnode));
+
+ /*
+ * If the primary fwnode is a software node it is free-ed by
+ * device_remove_software_node() below, avoid double-free.
+ */
+ if (!is_software_node(fwnode))
+ fwnode_handle_put(fwnode);
- if (ACPI_COMPANION(&client->dev))
- acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
+ device_remove_software_node(&client->dev);
device_unregister(&client->dev);
}
EXPORT_SYMBOL_GPL(i2c_unregister_device);
+/**
+ * i2c_find_device_by_fwnode() - find an i2c_client for the fwnode
+ * @fwnode: &struct fwnode_handle corresponding to the &struct i2c_client
+ *
+ * Look up and return the &struct i2c_client corresponding to the @fwnode.
+ * If no client can be found, or @fwnode is NULL, this returns NULL.
+ *
+ * The user must call put_device(&client->dev) once done with the i2c client.
+ */
+struct i2c_client *i2c_find_device_by_fwnode(struct fwnode_handle *fwnode)
+{
+ struct i2c_client *client;
+ struct device *dev;
+
+ if (!fwnode)
+ return NULL;
-static const struct i2c_device_id dummy_id[] = {
- { "dummy", 0 },
- { },
-};
+ dev = bus_find_device_by_fwnode(&i2c_bus_type, fwnode);
+ if (!dev)
+ return NULL;
-static int dummy_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- return 0;
+ client = i2c_verify_client(dev);
+ if (!client)
+ put_device(dev);
+
+ return client;
}
+EXPORT_SYMBOL(i2c_find_device_by_fwnode);
+
+
+static const struct i2c_device_id dummy_id[] = {
+ { "dummy", },
+ { "smbus_host_notify", },
+ { }
+};
-static int dummy_remove(struct i2c_client *client)
+static int dummy_probe(struct i2c_client *client)
{
return 0;
}
@@ -844,12 +1120,11 @@ static int dummy_remove(struct i2c_client *client)
static struct i2c_driver dummy_driver = {
.driver.name = "dummy",
.probe = dummy_probe,
- .remove = dummy_remove,
.id_table = dummy_id,
};
/**
- * i2c_new_dummy - return a new i2c device bound to a dummy driver
+ * i2c_new_dummy_device - return a new i2c device bound to a dummy driver
* @adapter: the adapter managing the device
* @address: seven bit address to be used
* Context: can sleep
@@ -864,20 +1139,54 @@ static struct i2c_driver dummy_driver = {
* different driver.
*
* This returns the new i2c client, which should be saved for later use with
- * i2c_unregister_device(); or NULL to indicate an error.
+ * i2c_unregister_device(); or an ERR_PTR to describe the error.
*/
-struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)
+struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address)
{
struct i2c_board_info info = {
I2C_BOARD_INFO("dummy", address),
};
- return i2c_new_device(adapter, &info);
+ return i2c_new_client_device(adapter, &info);
+}
+EXPORT_SYMBOL_GPL(i2c_new_dummy_device);
+
+static void devm_i2c_release_dummy(void *client)
+{
+ i2c_unregister_device(client);
}
-EXPORT_SYMBOL_GPL(i2c_new_dummy);
/**
- * i2c_new_secondary_device - Helper to get the instantiated secondary address
+ * devm_i2c_new_dummy_device - return a new i2c device bound to a dummy driver
+ * @dev: device the managed resource is bound to
+ * @adapter: the adapter managing the device
+ * @address: seven bit address to be used
+ * Context: can sleep
+ *
+ * This is the device-managed version of @i2c_new_dummy_device. It returns the
+ * new i2c client or an ERR_PTR in case of an error.
+ */
+struct i2c_client *devm_i2c_new_dummy_device(struct device *dev,
+ struct i2c_adapter *adapter,
+ u16 address)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = i2c_new_dummy_device(adapter, address);
+ if (IS_ERR(client))
+ return client;
+
+ ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return client;
+}
+EXPORT_SYMBOL_GPL(devm_i2c_new_dummy_device);
+
+/**
+ * i2c_new_ancillary_device - Helper to get the instantiated secondary address
* and create the associated device
* @client: Handle to the primary client
* @name: Handle to specify which secondary address to get
@@ -896,9 +1205,9 @@ EXPORT_SYMBOL_GPL(i2c_new_dummy);
* cell whose "reg-names" value matches the slave name.
*
* This returns the new i2c client, which should be saved for later use with
- * i2c_unregister_device(); or NULL to indicate an error.
+ * i2c_unregister_device(); or an ERR_PTR to describe the error.
*/
-struct i2c_client *i2c_new_secondary_device(struct i2c_client *client,
+struct i2c_client *i2c_new_ancillary_device(struct i2c_client *client,
const char *name,
u16 default_addr)
{
@@ -906,16 +1215,14 @@ struct i2c_client *i2c_new_secondary_device(struct i2c_client *client,
u32 addr = default_addr;
int i;
- if (np) {
- i = of_property_match_string(np, "reg-names", name);
- if (i >= 0)
- of_property_read_u32_index(np, "reg", i, &addr);
- }
+ i = of_property_match_string(np, "reg-names", name);
+ if (i >= 0)
+ of_property_read_u32_index(np, "reg", i, &addr);
dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr);
- return i2c_new_dummy(client->adapter, addr);
+ return i2c_new_dummy_device(client->adapter, addr);
}
-EXPORT_SYMBOL_GPL(i2c_new_secondary_device);
+EXPORT_SYMBOL_GPL(i2c_new_ancillary_device);
/* ------------------------------------------------------------------------- */
@@ -930,9 +1237,11 @@ static void i2c_adapter_dev_release(struct device *dev)
unsigned int i2c_adapter_depth(struct i2c_adapter *adapter)
{
unsigned int depth = 0;
+ struct device *parent;
- while ((adapter = i2c_parent_is_i2c_adapter(adapter)))
- depth++;
+ for (parent = adapter->dev.parent; parent; parent = parent->parent)
+ if (parent->type == &i2c_adapter_type)
+ depth++;
WARN_ONCE(depth >= MAX_LOCKDEP_SUBCLASSES,
"adapter depth exceeds lockdep subclass limit\n");
@@ -952,8 +1261,8 @@ EXPORT_SYMBOL_GPL(i2c_adapter_depth);
* the user to provide incorrect parameters.
*/
static ssize_t
-i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+new_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_board_info info;
@@ -995,9 +1304,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
info.flags |= I2C_CLIENT_SLAVE;
}
- client = i2c_new_device(adap, &info);
- if (!client)
- return -EINVAL;
+ client = i2c_new_client_device(adap, &info);
+ if (IS_ERR(client))
+ return PTR_ERR(client);
/* Keep track of the added device */
mutex_lock(&adap->userspace_clients_lock);
@@ -1008,7 +1317,7 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
+static DEVICE_ATTR_WO(new_device);
/*
* And of course let the users delete the devices they instantiated, if
@@ -1020,8 +1329,8 @@ static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
* the user to delete the wrong device.
*/
static ssize_t
-i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+delete_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_client *client, *next;
@@ -1064,7 +1373,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
return res;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
- i2c_sysfs_delete_device);
+ delete_device_store);
static struct attribute *i2c_adapter_attrs[] = {
&dev_attr_name.attr,
@@ -1074,7 +1383,7 @@ static struct attribute *i2c_adapter_attrs[] = {
};
ATTRIBUTE_GROUPS(i2c_adapter);
-struct device_type i2c_adapter_type = {
+const struct device_type i2c_adapter_type = {
.groups = i2c_adapter_groups,
.release = i2c_adapter_dev_release,
};
@@ -1097,19 +1406,14 @@ struct i2c_adapter *i2c_verify_adapter(struct device *dev)
}
EXPORT_SYMBOL(i2c_verify_adapter);
-#ifdef CONFIG_I2C_COMPAT
-static struct class_compat *i2c_adapter_compat_class;
-#endif
-
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,
- &devinfo->board_info))
+ if (devinfo->busnum == adapter->nr &&
+ IS_ERR(i2c_new_client_device(adapter, &devinfo->board_info)))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
@@ -1172,7 +1476,7 @@ static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
return 0;
- domain = irq_domain_create_linear(adap->dev.fwnode,
+ domain = irq_domain_create_linear(adap->dev.parent->fwnode,
I2C_ADDR_7BITS_COUNT,
&i2c_host_notify_irq_ops, adap);
if (!domain)
@@ -1200,11 +1504,13 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
if (!adap)
return -EINVAL;
+ dev_dbg(&adap->dev, "Detected HostNotify from address 0x%02x", addr);
+
irq = irq_find_mapping(adap->host_notify_domain, addr);
if (irq <= 0)
return -ENXIO;
- generic_handle_irq(irq);
+ generic_handle_irq_safe(irq);
return 0;
}
@@ -1232,6 +1538,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
if (!adap->lock_ops)
adap->lock_ops = &i2c_adapter_lock_ops;
+ adap->locked_flags = 0;
rt_mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->mux_lock);
mutex_init(&adap->userspace_clients_lock);
@@ -1252,36 +1559,40 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
- res = device_register(&adap->dev);
+ device_initialize(&adap->dev);
+
+ /*
+ * This adapter can be used as a parent immediately after device_add(),
+ * setup runtime-pm (especially ignore-children) before hand.
+ */
+ device_enable_async_suspend(&adap->dev);
+ pm_runtime_no_callbacks(&adap->dev);
+ pm_suspend_ignore_children(&adap->dev, true);
+ pm_runtime_enable(&adap->dev);
+
+ res = device_add(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
+ put_device(&adap->dev);
goto out_list;
}
- res = of_i2c_setup_smbus_alert(adap);
+ adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root);
+
+ res = i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;
- dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
-
- pm_runtime_no_callbacks(&adap->dev);
- pm_suspend_ignore_children(&adap->dev, true);
- pm_runtime_enable(&adap->dev);
-
-#ifdef CONFIG_I2C_COMPAT
- res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
- adap->dev.parent);
- if (res)
- dev_warn(&adap->dev,
- "Failed to create compatibility class link\n");
-#endif
+ res = i2c_init_recovery(adap);
+ if (res == -EPROBE_DEFER)
+ goto out_reg;
- i2c_init_recovery(adap);
+ dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
- i2c_acpi_register_devices(adap);
i2c_acpi_install_space_handler(adap);
+ i2c_acpi_register_devices(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
@@ -1294,6 +1605,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
return 0;
out_reg:
+ debugfs_remove_recursive(adap->debugfs);
init_completion(&adap->dev_released);
device_unregister(&adap->dev);
wait_for_completion(&adap->dev_released);
@@ -1343,12 +1655,10 @@ int i2c_add_adapter(struct i2c_adapter *adapter)
struct device *dev = &adapter->dev;
int id;
- if (dev->of_node) {
- id = of_alias_get_id(dev->of_node, "i2c");
- if (id >= 0) {
- adapter->nr = id;
- return __i2c_add_numbered_adapter(adapter);
- }
+ id = of_alias_get_id(dev->of_node, "i2c");
+ if (id >= 0) {
+ adapter->nr = id;
+ return __i2c_add_numbered_adapter(adapter);
}
mutex_lock(&core_lock);
@@ -1483,11 +1793,6 @@ void i2c_del_adapter(struct i2c_adapter *adap)
device_for_each_child(&adap->dev, NULL, __unregister_client);
device_for_each_child(&adap->dev, NULL, __unregister_dummy);
-#ifdef CONFIG_I2C_COMPAT
- class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
- adap->dev.parent);
-#endif
-
/* device name is gone after device_unregister */
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
@@ -1495,6 +1800,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
i2c_host_notify_irq_teardown(adap);
+ debugfs_remove_recursive(adap->debugfs);
+
/* wait until all references to the device are gone
*
* FIXME: This is old code and should ideally be replaced by an
@@ -1517,63 +1824,159 @@ void i2c_del_adapter(struct i2c_adapter *adap)
}
EXPORT_SYMBOL(i2c_del_adapter);
+static void devm_i2c_del_adapter(void *adapter)
+{
+ i2c_del_adapter(adapter);
+}
+
+/**
+ * devm_i2c_add_adapter - device-managed variant of i2c_add_adapter()
+ * @dev: managing device for adding this I2C adapter
+ * @adapter: the adapter to add
+ * Context: can sleep
+ *
+ * Add adapter with dynamic bus number, same with i2c_add_adapter()
+ * but the adapter will be auto deleted on driver detach.
+ */
+int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter)
+{
+ int ret;
+
+ ret = i2c_add_adapter(adapter);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_i2c_del_adapter, adapter);
+}
+EXPORT_SYMBOL_GPL(devm_i2c_add_adapter);
+
+static int i2c_dev_or_parent_fwnode_match(struct device *dev, const void *data)
+{
+ if (dev_fwnode(dev) == data)
+ return 1;
+
+ if (dev->parent && dev_fwnode(dev->parent) == data)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * i2c_find_adapter_by_fwnode() - find an i2c_adapter for the fwnode
+ * @fwnode: &struct fwnode_handle corresponding to the &struct i2c_adapter
+ *
+ * Look up and return the &struct i2c_adapter corresponding to the @fwnode.
+ * If no adapter can be found, or @fwnode is NULL, this returns NULL.
+ *
+ * The user must call put_device(&adapter->dev) once done with the i2c adapter.
+ */
+struct i2c_adapter *i2c_find_adapter_by_fwnode(struct fwnode_handle *fwnode)
+{
+ struct i2c_adapter *adapter;
+ struct device *dev;
+
+ if (!fwnode)
+ return NULL;
+
+ dev = bus_find_device(&i2c_bus_type, NULL, fwnode,
+ i2c_dev_or_parent_fwnode_match);
+ if (!dev)
+ return NULL;
+
+ adapter = i2c_verify_adapter(dev);
+ if (!adapter)
+ put_device(dev);
+
+ return adapter;
+}
+EXPORT_SYMBOL(i2c_find_adapter_by_fwnode);
+
+/**
+ * i2c_get_adapter_by_fwnode() - find an i2c_adapter for the fwnode
+ * @fwnode: &struct fwnode_handle corresponding to the &struct i2c_adapter
+ *
+ * Look up and return the &struct i2c_adapter corresponding to the @fwnode,
+ * and increment the adapter module's use count. If no adapter can be found,
+ * or @fwnode is NULL, this returns NULL.
+ *
+ * The user must call i2c_put_adapter(adapter) once done with the i2c adapter.
+ * Note that this is different from i2c_find_adapter_by_node().
+ */
+struct i2c_adapter *i2c_get_adapter_by_fwnode(struct fwnode_handle *fwnode)
+{
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_find_adapter_by_fwnode(fwnode);
+ if (!adapter)
+ return NULL;
+
+ if (!try_module_get(adapter->owner)) {
+ put_device(&adapter->dev);
+ adapter = NULL;
+ }
+
+ return adapter;
+}
+EXPORT_SYMBOL(i2c_get_adapter_by_fwnode);
+
+static void i2c_parse_timing(struct device *dev, char *prop_name, u32 *cur_val_p,
+ u32 def_val, bool use_def)
+{
+ int ret;
+
+ ret = device_property_read_u32(dev, prop_name, cur_val_p);
+ if (ret && use_def)
+ *cur_val_p = def_val;
+
+ dev_dbg(dev, "%s: %u\n", prop_name, *cur_val_p);
+}
+
/**
* i2c_parse_fw_timings - get I2C related timing parameters from firmware
* @dev: The device to scan for I2C timing properties
* @t: the i2c_timings struct to be filled with values
* @use_defaults: bool to use sane defaults derived from the I2C specification
- * when properties are not found, otherwise use 0
+ * when properties are not found, otherwise don't update
*
* Scan the device for the generic I2C properties describing timing parameters
* for the signal and fill the given struct with the results. If a property was
* not found and use_defaults was true, then maximum timings are assumed which
* are derived from the I2C specification. If use_defaults is not used, the
- * results will be 0, so drivers can apply their own defaults later. The latter
- * is mainly intended for avoiding regressions of existing drivers which want
- * to switch to this function. New drivers almost always should use the defaults.
+ * results will be as before, so drivers can apply their own defaults before
+ * calling this helper. The latter is mainly intended for avoiding regressions
+ * of existing drivers which want to switch to this function. New drivers
+ * almost always should use the defaults.
*/
-
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
{
- int ret;
-
- memset(t, 0, sizeof(*t));
-
- ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
- if (ret && use_defaults)
- t->bus_freq_hz = 100000;
-
- ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 100000)
- t->scl_rise_ns = 1000;
- else if (t->bus_freq_hz <= 400000)
- t->scl_rise_ns = 300;
- else
- t->scl_rise_ns = 120;
- }
+ bool u = use_defaults;
+ u32 d;
- ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 400000)
- t->scl_fall_ns = 300;
- else
- t->scl_fall_ns = 120;
- }
+ i2c_parse_timing(dev, "clock-frequency", &t->bus_freq_hz,
+ I2C_MAX_STANDARD_MODE_FREQ, u);
- device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
+ d = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 :
+ t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
+ i2c_parse_timing(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns, d, u);
- ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
- if (ret && use_defaults)
- t->sda_fall_ns = t->scl_fall_ns;
+ d = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
+ i2c_parse_timing(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns, d, u);
- device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
+ i2c_parse_timing(dev, "i2c-scl-internal-delay-ns",
+ &t->scl_int_delay_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns,
+ t->scl_fall_ns, u);
+ i2c_parse_timing(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-digital-filter-width-ns",
+ &t->digital_filter_width_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-analog-filter-cutoff-frequency",
+ &t->analog_filter_cutoff_freq_hz, 0, u);
}
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
/* ------------------------------------------------------------------------- */
-int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
+int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data))
{
int res;
@@ -1649,38 +2052,6 @@ EXPORT_SYMBOL(i2c_del_driver);
/* ------------------------------------------------------------------------- */
-/**
- * i2c_use_client - increments the reference count of the i2c client structure
- * @client: the client being referenced
- *
- * Each live reference to a client should be refcounted. The driver model does
- * that automatically as part of driver binding, so that most drivers don't
- * need to do this explicitly: they hold a reference until they're unbound
- * from the device.
- *
- * A pointer to the client with the incremented reference counter is returned.
- */
-struct i2c_client *i2c_use_client(struct i2c_client *client)
-{
- if (client && get_device(&client->dev))
- return client;
- return NULL;
-}
-EXPORT_SYMBOL(i2c_use_client);
-
-/**
- * i2c_release_client - release a use of the i2c client structure
- * @client: the client being no longer referenced
- *
- * Must be called when a user of a client is finished with it.
- */
-void i2c_release_client(struct i2c_client *client)
-{
- if (client)
- put_device(&client->dev);
-}
-EXPORT_SYMBOL(i2c_release_client);
-
struct i2c_cmd_arg {
unsigned cmd;
void *arg;
@@ -1728,13 +2099,8 @@ static int __init i2c_init(void)
is_registered = true;
-#ifdef CONFIG_I2C_COMPAT
- i2c_adapter_compat_class = class_compat_register("i2c-adapter");
- if (!i2c_adapter_compat_class) {
- retval = -ENOMEM;
- goto bus_err;
- }
-#endif
+ i2c_debugfs_root = debugfs_create_dir("i2c", NULL);
+
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
@@ -1747,10 +2113,6 @@ static int __init i2c_init(void)
return 0;
class_err:
-#ifdef CONFIG_I2C_COMPAT
- class_compat_unregister(i2c_adapter_compat_class);
-bus_err:
-#endif
is_registered = false;
bus_unregister(&i2c_bus_type);
return retval;
@@ -1763,9 +2125,7 @@ static void __exit i2c_exit(void)
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
i2c_del_driver(&dummy_driver);
-#ifdef CONFIG_I2C_COMPAT
- class_compat_unregister(i2c_adapter_compat_class);
-#endif
+ debugfs_remove_recursive(i2c_debugfs_root);
bus_unregister(&i2c_bus_type);
tracepoint_synchronize_unregister();
}
@@ -1788,7 +2148,7 @@ static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *
{
dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n",
err_msg, msg->addr, msg->len,
- msg->flags & I2C_M_RD ? "read" : "write");
+ str_read_write(msg->flags & I2C_M_RD));
return -EOPNOTSUPP;
}
@@ -1856,16 +2216,25 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs,
* Returns negative errno, else the number of messages executed.
*
* Adapter lock must be held when calling this function. No debug logging
- * takes place. adap->algo->master_xfer existence isn't checked.
+ * takes place.
*/
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
+ if (!adap->algo->master_xfer) {
+ dev_dbg(&adap->dev, "I2C level transfers not supported\n");
+ return -EOPNOTSUPP;
+ }
+
if (WARN_ON(!msgs || num < 1))
return -EINVAL;
+ ret = __i2c_check_suspended(adap);
+ if (ret)
+ return ret;
+
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
@@ -1886,7 +2255,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
- ret = adap->algo->master_xfer(adap, msgs, num);
+ if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
+ ret = adap->algo->master_xfer_atomic(adap, msgs, num);
+ else
+ ret = adap->algo->master_xfer(adap, msgs, num);
+
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
@@ -1921,11 +2294,6 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
- if (!adap->algo->master_xfer) {
- dev_dbg(&adap->dev, "I2C level transfers not supported\n");
- return -EOPNOTSUPP;
- }
-
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
@@ -1942,14 +2310,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
- if (in_atomic() || irqs_disabled()) {
- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
- if (!ret)
- /* I2C activity is ongoing. */
- return -EAGAIN;
- } else {
- i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
- }
+ ret = __i2c_lock_bus_helper(adap);
+ if (ret)
+ return ret;
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
@@ -2020,6 +2383,20 @@ int i2c_get_device_id(const struct i2c_client *client,
}
EXPORT_SYMBOL_GPL(i2c_get_device_id);
+/**
+ * i2c_client_get_device_id - get the driver match table entry of a device
+ * @client: the device to query. The device must be bound to a driver
+ *
+ * Returns a pointer to the matching entry if found, NULL otherwise.
+ */
+const struct i2c_device_id *i2c_client_get_device_id(const struct i2c_client *client)
+{
+ const struct i2c_driver *drv = to_i2c_driver(client->dev.driver);
+
+ return i2c_match_id(drv->id_table, client);
+}
+EXPORT_SYMBOL_GPL(i2c_client_get_device_id);
+
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
@@ -2113,13 +2490,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
dev_warn(&adapter->dev,
"This adapter will soon drop class based instantiation of devices. "
"Please make sure client 0x%02x gets instantiated by other means. "
- "Check 'Documentation/i2c/instantiating-devices' for details.\n",
+ "Check 'Documentation/i2c/instantiating-devices.rst' for details.\n",
info.addr);
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
- client = i2c_new_device(adapter, &info);
- if (client)
+ client = i2c_new_client_device(adapter, &info);
+ if (!IS_ERR(client))
list_add_tail(&client->detected, &driver->clients);
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
@@ -2133,7 +2510,6 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
- int adap_id = i2c_adapter_id(adapter);
address_list = driver->address_list;
if (!driver->detect || !address_list)
@@ -2143,7 +2519,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
if (adapter->class == I2C_CLASS_DEPRECATED) {
dev_dbg(&adapter->dev,
"This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. "
- "If you need it, check 'Documentation/i2c/instantiating-devices' for alternatives.\n",
+ "If you need it, check 'Documentation/i2c/instantiating-devices.rst' for alternatives.\n",
driver->driver.name);
return 0;
}
@@ -2153,15 +2529,16 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
return 0;
/* Set up a temporary client to help detect callback */
- temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ temp_client = kzalloc(sizeof(*temp_client), GFP_KERNEL);
if (!temp_client)
return -ENOMEM;
+
temp_client->adapter = adapter;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev,
"found normal entry for adapter %d, addr 0x%02x\n",
- adap_id, address_list[i]);
+ i2c_adapter_id(adapter), address_list[i]);
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (unlikely(err))
@@ -2169,6 +2546,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
}
kfree(temp_client);
+
return err;
}
@@ -2180,10 +2558,10 @@ int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr)
EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read);
struct i2c_client *
-i2c_new_probed_device(struct i2c_adapter *adap,
- struct i2c_board_info *info,
- unsigned short const *addr_list,
- int (*probe)(struct i2c_adapter *, unsigned short addr))
+i2c_new_scanned_device(struct i2c_adapter *adap,
+ struct i2c_board_info *info,
+ unsigned short const *addr_list,
+ int (*probe)(struct i2c_adapter *adap, unsigned short addr))
{
int i;
@@ -2213,13 +2591,13 @@ i2c_new_probed_device(struct i2c_adapter *adap,
if (addr_list[i] == I2C_CLIENT_END) {
dev_dbg(&adap->dev, "Probing failed, no device found\n");
- return NULL;
+ return ERR_PTR(-ENODEV);
}
info->addr = addr_list[i];
- return i2c_new_device(adap, info);
+ return i2c_new_client_device(adap, info);
}
-EXPORT_SYMBOL_GPL(i2c_new_probed_device);
+EXPORT_SYMBOL_GPL(i2c_new_scanned_device);
struct i2c_adapter *i2c_get_adapter(int nr)
{
@@ -2246,15 +2624,17 @@ void i2c_put_adapter(struct i2c_adapter *adap)
if (!adap)
return;
- put_device(&adap->dev);
module_put(adap->owner);
+ /* Should be last, otherwise we risk use-after-free with 'adap' */
+ put_device(&adap->dev);
}
EXPORT_SYMBOL(i2c_put_adapter);
/**
* i2c_get_dma_safe_msg_buf() - get a DMA safe buffer for the given i2c_msg
* @msg: the message to be checked
- * @threshold: the minimum number of bytes for which using DMA makes sense
+ * @threshold: the minimum number of bytes for which using DMA makes sense.
+ * Should at least be 1.
*
* Return: NULL if a DMA safe buffer was not obtained. Use msg->buf with PIO.
* Or a valid pointer to be used with DMA. After use, release it by
@@ -2264,7 +2644,11 @@ EXPORT_SYMBOL(i2c_put_adapter);
*/
u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold)
{
- if (msg->len < threshold)
+ /* also skip 0-length msgs for bogus thresholds of 0 */
+ if (!threshold)
+ pr_debug("DMA buffer for addr=0x%02x with length 0 is bogus\n",
+ msg->addr);
+ if (msg->len < threshold || msg->len == 0)
return NULL;
if (msg->flags & I2C_M_DMA_SAFE)
diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
new file mode 100644
index 000000000000..0a66267e4836
--- /dev/null
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux I2C core OF component prober code
+ *
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/i2c-of-prober.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+
+/*
+ * Some devices, such as Google Hana Chromebooks, are produced by multiple
+ * vendors each using their preferred components. Such components are all
+ * in the device tree. Instead of having all of them enabled and having each
+ * driver separately try and probe its device while fighting over shared
+ * resources, they can be marked as "fail-needs-probe" and have a prober
+ * figure out which one is actually used beforehand.
+ *
+ * This prober assumes such drop-in parts are on the same I2C bus, have
+ * non-conflicting addresses, and can be directly probed by seeing which
+ * address responds.
+ *
+ * TODO:
+ * - Support I2C muxes
+ */
+
+static struct device_node *i2c_of_probe_get_i2c_node(struct device *dev, const char *type)
+{
+ struct device_node *node __free(device_node) = of_find_node_by_name(NULL, type);
+ if (!node) {
+ dev_err(dev, "Could not find %s device node\n", type);
+ return NULL;
+ }
+
+ struct device_node *i2c_node __free(device_node) = of_get_parent(node);
+ if (!of_node_name_eq(i2c_node, "i2c")) {
+ dev_err(dev, "%s device isn't on I2C bus\n", type);
+ return NULL;
+ }
+
+ if (!of_device_is_available(i2c_node)) {
+ dev_err(dev, "I2C controller not available\n");
+ return NULL;
+ }
+
+ return no_free_ptr(i2c_node);
+}
+
+static int i2c_of_probe_enable_node(struct device *dev, struct device_node *node)
+{
+ int ret;
+
+ dev_dbg(dev, "Enabling %pOF\n", node);
+
+ struct of_changeset *ocs __free(kfree) = kzalloc(sizeof(*ocs), GFP_KERNEL);
+ if (!ocs)
+ return -ENOMEM;
+
+ of_changeset_init(ocs);
+ ret = of_changeset_update_prop_string(ocs, node, "status", "okay");
+ if (ret)
+ return ret;
+
+ ret = of_changeset_apply(ocs);
+ if (ret) {
+ /* ocs needs to be explicitly cleaned up before being freed. */
+ of_changeset_destroy(ocs);
+ } else {
+ /*
+ * ocs is intentionally kept around as it needs to
+ * exist as long as the change is applied.
+ */
+ void *ptr __always_unused = no_free_ptr(ocs);
+ }
+
+ return ret;
+}
+
+static const struct i2c_of_probe_ops i2c_of_probe_dummy_ops;
+
+/**
+ * i2c_of_probe_component() - probe for devices of "type" on the same i2c bus
+ * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages.
+ * @cfg: Pointer to the &struct i2c_of_probe_cfg containing callbacks and other options
+ * for the prober.
+ * @ctx: Context data for callbacks.
+ *
+ * Probe for possible I2C components of the same "type" (&i2c_of_probe_cfg->type)
+ * on the same I2C bus that have their status marked as "fail-needs-probe".
+ *
+ * Assumes that across the entire device tree the only instances of nodes
+ * with "type" prefixed node names (not including the address portion) are
+ * the ones that need handling for second source components. In other words,
+ * if "type" is "touchscreen", then all device nodes named "touchscreen*"
+ * are the ones that need probing. There cannot be another "touchscreen*"
+ * node that is already enabled.
+ *
+ * Assumes that for each "type" of component, only one actually exists. In
+ * other words, only one matching and existing device will be enabled.
+ *
+ * Context: Process context only. Does non-atomic I2C transfers.
+ * Should only be used from a driver probe function, as the function
+ * can return -EPROBE_DEFER if the I2C adapter or other resources
+ * are unavailable.
+ * Return: 0 on success or no-op, error code otherwise.
+ * A no-op can happen when it seems like the device tree already
+ * has components of the type to be probed already enabled. This
+ * can happen when the device tree had not been updated to mark
+ * the status of the to-be-probed components as "fail-needs-probe".
+ * Or this function was already run with the same parameters and
+ * succeeded in enabling a component. The latter could happen if
+ * the user had multiple types of components to probe, and one of
+ * them down the list caused a deferred probe. This is expected
+ * behavior.
+ */
+int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx)
+{
+ const struct i2c_of_probe_ops *ops;
+ const char *type;
+ struct i2c_adapter *i2c;
+ int ret;
+
+ ops = cfg->ops ?: &i2c_of_probe_dummy_ops;
+ type = cfg->type;
+
+ struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type);
+ if (!i2c_node)
+ return -ENODEV;
+
+ /*
+ * If any devices of the given "type" are already enabled then this function is a no-op.
+ * Either the device tree hasn't been modified to work with this probe function, or the
+ * function had already run before and enabled some component.
+ */
+ for_each_child_of_node_with_prefix(i2c_node, node, type)
+ if (of_device_is_available(node))
+ return 0;
+
+ i2c = of_get_i2c_adapter_by_node(i2c_node);
+ if (!i2c)
+ return dev_err_probe(dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n");
+
+ /* Grab and enable resources */
+ ret = 0;
+ if (ops->enable)
+ ret = ops->enable(dev, i2c_node, ctx);
+ if (ret)
+ goto out_put_i2c_adapter;
+
+ for_each_child_of_node_with_prefix(i2c_node, node, type) {
+ union i2c_smbus_data data;
+ u32 addr;
+
+ if (of_property_read_u32(node, "reg", &addr))
+ continue;
+ if (i2c_smbus_xfer(i2c, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data) < 0)
+ continue;
+
+ /* Found a device that is responding */
+ if (ops->cleanup_early)
+ ops->cleanup_early(dev, ctx);
+ ret = i2c_of_probe_enable_node(dev, node);
+ break;
+ }
+
+ if (ops->cleanup)
+ ops->cleanup(dev, ctx);
+out_put_i2c_adapter:
+ i2c_put_adapter(i2c);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_component, "I2C_OF_PROBER");
+
+static int i2c_of_probe_simple_get_supply(struct device *dev, struct device_node *node,
+ struct i2c_of_probe_simple_ctx *ctx)
+{
+ const char *supply_name;
+ struct regulator *supply;
+
+ /*
+ * It's entirely possible for the component's device node to not have the
+ * regulator supplies. While it does not make sense from a hardware perspective,
+ * the supplies could be always on or otherwise not modeled in the device tree,
+ * but the device would still work.
+ */
+ supply_name = ctx->opts->supply_name;
+ if (!supply_name)
+ return 0;
+
+ supply = of_regulator_get_optional(dev, node, supply_name);
+ if (IS_ERR(supply)) {
+ return dev_err_probe(dev, PTR_ERR(supply),
+ "Failed to get regulator supply \"%s\" from %pOF\n",
+ supply_name, node);
+ }
+
+ ctx->supply = supply;
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_put_supply(struct i2c_of_probe_simple_ctx *ctx)
+{
+ regulator_put(ctx->supply);
+ ctx->supply = NULL;
+}
+
+static int i2c_of_probe_simple_enable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->supply)
+ return 0;
+
+ dev_dbg(dev, "Enabling regulator supply \"%s\"\n", ctx->opts->supply_name);
+
+ ret = regulator_enable(ctx->supply);
+ if (ret)
+ return ret;
+
+ if (ctx->opts->post_power_on_delay_ms)
+ msleep(ctx->opts->post_power_on_delay_ms);
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ if (!ctx->supply)
+ return;
+
+ dev_dbg(dev, "Disabling regulator supply \"%s\"\n", ctx->opts->supply_name);
+
+ regulator_disable(ctx->supply);
+}
+
+static int i2c_of_probe_simple_get_gpiod(struct device *dev, struct device_node *node,
+ struct i2c_of_probe_simple_ctx *ctx)
+{
+ struct fwnode_handle *fwnode = of_fwnode_handle(node);
+ struct gpio_desc *gpiod;
+ const char *con_id;
+
+ /* NULL signals no GPIO needed */
+ if (!ctx->opts->gpio_name)
+ return 0;
+
+ /* An empty string signals an unnamed GPIO */
+ if (!ctx->opts->gpio_name[0])
+ con_id = NULL;
+ else
+ con_id = ctx->opts->gpio_name;
+
+ gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober");
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ ctx->gpiod = gpiod;
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_put_gpiod(struct i2c_of_probe_simple_ctx *ctx)
+{
+ gpiod_put(ctx->gpiod);
+ ctx->gpiod = NULL;
+}
+
+static int i2c_of_probe_simple_set_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->gpiod)
+ return 0;
+
+ dev_dbg(dev, "Configuring GPIO\n");
+
+ ret = gpiod_direction_output(ctx->gpiod, ctx->opts->gpio_assert_to_enable);
+ if (ret)
+ return ret;
+
+ if (ctx->opts->post_gpio_config_delay_ms)
+ msleep(ctx->opts->post_gpio_config_delay_ms);
+
+ return 0;
+}
+
+static void i2c_of_probe_simple_disable_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+{
+ gpiod_set_value(ctx->gpiod, !ctx->opts->gpio_assert_to_enable);
+}
+
+/**
+ * i2c_of_probe_simple_enable - Simple helper for I2C OF prober to get and enable resources
+ * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages
+ * @bus_node: Pointer to the &struct device_node of the I2C adapter.
+ * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
+ *
+ * If &i2c_of_probe_simple_opts->supply_name is given, request the named regulator supply.
+ * If &i2c_of_probe_simple_opts->gpio_name is given, request the named GPIO. Or if it is
+ * the empty string, request the unnamed GPIO.
+ * If a regulator supply was found, enable that regulator.
+ * If a GPIO line was found, configure the GPIO line to output and set value
+ * according to given options.
+ *
+ * Return: %0 on success or no-op, or a negative error number on failure.
+ */
+int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus_node, void *data)
+{
+ struct i2c_of_probe_simple_ctx *ctx = data;
+ struct device_node *node;
+ const char *compat;
+ int ret;
+
+ dev_dbg(dev, "Requesting resources for components under I2C bus %pOF\n", bus_node);
+
+ if (!ctx || !ctx->opts)
+ return -EINVAL;
+
+ compat = ctx->opts->res_node_compatible;
+ if (!compat)
+ return -EINVAL;
+
+ node = of_get_compatible_child(bus_node, compat);
+ if (!node)
+ return dev_err_probe(dev, -ENODEV, "No device compatible with \"%s\" found\n",
+ compat);
+
+ ret = i2c_of_probe_simple_get_supply(dev, node, ctx);
+ if (ret)
+ goto out_put_node;
+
+ ret = i2c_of_probe_simple_get_gpiod(dev, node, ctx);
+ if (ret)
+ goto out_put_supply;
+
+ ret = i2c_of_probe_simple_enable_regulator(dev, ctx);
+ if (ret)
+ goto out_put_gpiod;
+
+ ret = i2c_of_probe_simple_set_gpio(dev, ctx);
+ if (ret)
+ goto out_disable_regulator;
+
+ return 0;
+
+out_disable_regulator:
+ i2c_of_probe_simple_disable_regulator(dev, ctx);
+out_put_gpiod:
+ i2c_of_probe_simple_put_gpiod(ctx);
+out_put_supply:
+ i2c_of_probe_simple_put_supply(ctx);
+out_put_node:
+ of_node_put(node);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_enable, "I2C_OF_PROBER");
+
+/**
+ * i2c_of_probe_simple_cleanup_early - \
+ * Simple helper for I2C OF prober to release GPIOs before component is enabled
+ * @dev: Pointer to the &struct device of the caller; unused.
+ * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
+ *
+ * GPIO descriptors are exclusive and have to be released before the
+ * actual driver probes so that the latter can acquire them.
+ */
+void i2c_of_probe_simple_cleanup_early(struct device *dev, void *data)
+{
+ struct i2c_of_probe_simple_ctx *ctx = data;
+
+ i2c_of_probe_simple_put_gpiod(ctx);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup_early, "I2C_OF_PROBER");
+
+/**
+ * i2c_of_probe_simple_cleanup - Clean up and release resources for I2C OF prober simple helpers
+ * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages
+ * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context.
+ *
+ * * If a GPIO line was found and not yet released, set its value to the opposite of that
+ * set in i2c_of_probe_simple_enable() and release it.
+ * * If a regulator supply was found, disable that regulator and release it.
+ */
+void i2c_of_probe_simple_cleanup(struct device *dev, void *data)
+{
+ struct i2c_of_probe_simple_ctx *ctx = data;
+
+ /* GPIO operations here are no-ops if i2c_of_probe_simple_cleanup_early was called. */
+ i2c_of_probe_simple_disable_gpio(dev, ctx);
+ i2c_of_probe_simple_put_gpiod(ctx);
+
+ i2c_of_probe_simple_disable_regulator(dev, ctx);
+ i2c_of_probe_simple_put_supply(ctx);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup, "I2C_OF_PROBER");
+
+struct i2c_of_probe_ops i2c_of_probe_simple_ops = {
+ .enable = i2c_of_probe_simple_enable,
+ .cleanup_early = i2c_of_probe_simple_cleanup_early,
+ .cleanup = i2c_of_probe_simple_cleanup,
+};
+EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_ops, "I2C_OF_PROBER");
diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 6cb7ad608bcd..eb7fb202355f 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux I2C core OF support code
*
* Copyright (C) 2008 Jochen Friedrich <jochen@scram.de>
* based on a previous patch from Jon Smirl <jonsmirl@gmail.com>
*
- * Copyright (C) 2013, 2018 Wolfram Sang <wsa@the-dreams.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
+ * Copyright (C) 2013, 2018 Wolfram Sang <wsa@kernel.org>
*/
#include <dt-bindings/i2c/i2c.h>
@@ -19,6 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/sysfs.h>
#include "i2c-core.h"
@@ -30,7 +27,7 @@ int of_i2c_get_board_info(struct device *dev, struct device_node *node,
memset(info, 0, sizeof(*info));
- if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
+ if (of_alias_from_compatible(node, info->type, sizeof(info->type)) < 0) {
dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
return -EINVAL;
}
@@ -52,12 +49,12 @@ int of_i2c_get_board_info(struct device *dev, struct device_node *node,
}
info->addr = addr;
- info->of_node = node;
+ info->fwnode = of_fwnode_handle(node);
if (of_property_read_bool(node, "host-notify"))
info->flags |= I2C_CLIENT_HOST_NOTIFY;
- if (of_get_property(node, "wakeup-source", NULL))
+ if (of_property_read_bool(node, "wakeup-source"))
info->flags |= I2C_CLIENT_WAKE;
return 0;
@@ -77,11 +74,10 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
if (ret)
return ERR_PTR(ret);
- client = i2c_new_device(adap, &info);
- if (!client) {
+ client = i2c_new_client_device(adap, &info);
+ if (IS_ERR(client))
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
- return ERR_PTR(-EINVAL);
- }
+
return client;
}
@@ -116,65 +112,6 @@ void of_i2c_register_devices(struct i2c_adapter *adap)
of_node_put(bus);
}
-static int of_dev_node_match(struct device *dev, void *data)
-{
- return dev->of_node == data;
-}
-
-/* must call put_device() when done with returned i2c_client device */
-struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
-{
- struct device *dev;
- struct i2c_client *client;
-
- dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
- if (!dev)
- return NULL;
-
- client = i2c_verify_client(dev);
- if (!client)
- put_device(dev);
-
- return client;
-}
-EXPORT_SYMBOL(of_find_i2c_device_by_node);
-
-/* must call put_device() when done with returned i2c_adapter device */
-struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
-{
- struct device *dev;
- struct i2c_adapter *adapter;
-
- dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
- if (!dev)
- return NULL;
-
- adapter = i2c_verify_adapter(dev);
- if (!adapter)
- put_device(dev);
-
- return adapter;
-}
-EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
-
-/* must call i2c_put_adapter() when done with returned i2c_adapter device */
-struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node)
-{
- struct i2c_adapter *adapter;
-
- adapter = of_find_i2c_adapter_by_node(node);
- if (!adapter)
- return NULL;
-
- if (!try_module_get(adapter->owner)) {
- put_device(&adapter->dev);
- adapter = NULL;
- }
-
- return adapter;
-}
-EXPORT_SYMBOL(of_get_i2c_adapter_by_node);
-
static const struct of_device_id*
i2c_of_match_device_sysfs(const struct of_device_id *matches,
struct i2c_client *client)
@@ -219,7 +156,6 @@ const struct of_device_id
return i2c_of_match_device_sysfs(matches, client);
}
-EXPORT_SYMBOL_GPL(i2c_of_match_device);
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
@@ -240,15 +176,20 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
+ /*
+ * Clear the flag before adding the device so that fw_devlink
+ * doesn't skip adding consumers to this device.
+ */
+ rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE;
client = of_i2c_register_device(adap, rd->dn);
- put_device(&adap->dev);
-
if (IS_ERR(client)) {
dev_err(&adap->dev, "failed to create client for '%pOF'\n",
rd->dn);
+ put_device(&adap->dev);
of_node_clear_flag(rd->dn, OF_POPULATED);
return notifier_from_errno(PTR_ERR(client));
}
+ put_device(&adap->dev);
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* already depopulated? */
diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c
index 47a9f70a24a9..02ca55c2246b 100644
--- a/drivers/i2c/i2c-core-slave.c
+++ b/drivers/i2c/i2c-core-slave.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux I2C core slave support code
*
* Copyright (C) 2014 by Wolfram Sang <wsa@sang-engineering.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
*/
#include <dt-bindings/i2c/i2c.h>
@@ -15,17 +11,19 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/of.h>
+#include <linux/property.h>
#include "i2c-core.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/i2c_slave.h>
+
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
{
int ret;
- if (!client || !slave_cb) {
- WARN(1, "insufficient data\n");
+ if (WARN(IS_ERR_OR_NULL(client) || !slave_cb, "insufficient data\n"))
return -EINVAL;
- }
if (!(client->flags & I2C_CLIENT_SLAVE))
dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n",
@@ -64,6 +62,9 @@ int i2c_slave_unregister(struct i2c_client *client)
{
int ret;
+ if (IS_ERR_OR_NULL(client))
+ return -EINVAL;
+
if (!client->adapter->algo->unreg_slave) {
dev_err(&client->dev, "%s: not supported by adapter\n", __func__);
return -EOPNOTSUPP;
@@ -82,6 +83,18 @@ int i2c_slave_unregister(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
+int i2c_slave_event(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ int ret = client->slave_cb(client, event, val);
+
+ if (trace_i2c_slave_enabled())
+ trace_i2c_slave(client, event, val, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i2c_slave_event);
+
/**
* i2c_detect_slave_mode - detect operation mode
* @dev: The device owning the bus
@@ -96,18 +109,17 @@ EXPORT_SYMBOL_GPL(i2c_slave_unregister);
*/
bool i2c_detect_slave_mode(struct device *dev)
{
- if (IS_BUILTIN(CONFIG_OF) && dev->of_node) {
- struct device_node *child;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+
+ if (is_of_node(fwnode)) {
u32 reg;
- for_each_child_of_node(dev->of_node, child) {
- of_property_read_u32(child, "reg", &reg);
- if (reg & I2C_OWN_SLAVE_ADDRESS) {
- of_node_put(child);
+ fwnode_for_each_child_node_scoped(fwnode, child) {
+ fwnode_property_read_u32(child, "reg", &reg);
+ if (reg & I2C_OWN_SLAVE_ADDRESS)
return true;
- }
}
- } else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) {
+ } else if (is_acpi_device_node(fwnode)) {
dev_dbg(dev, "ACPI slave is not supported yet\n");
}
return false;
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index 9cd66cabb84f..71eb1ef56f0c 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -1,24 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux I2C core SMBus and SMBus emulation code
*
* This file contains the SMBus functions which are always included in the I2C
* core because they can be emulated via I2C. SMBus specific extensions
- * (e.g. smbalert) are handled in a seperate i2c-smbus module.
+ * (e.g. smbalert) are handled in a separate i2c-smbus module.
*
* All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
* SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
* Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
+#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
+
+#include "i2c-core.h"
#define CREATE_TRACE_POINTS
#include <trace/events/smbus.h>
@@ -39,8 +39,15 @@ static u8 crc8(u16 data)
return (u8)(data >> 8);
}
-/* Incremental CRC8 over count bytes in the array pointed to by p */
-static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
+/**
+ * i2c_smbus_pec - Incremental CRC8 over the given input data array
+ * @crc: previous return crc8 value
+ * @p: pointer to data buffer.
+ * @count: number of bytes in data buffer.
+ *
+ * Incremental CRC8 over count bytes in the array pointed to by p
+ */
+u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
{
int i;
@@ -48,6 +55,7 @@ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
crc = crc8((crc ^ p[i]) << 8);
return crc;
}
+EXPORT_SYMBOL(i2c_smbus_pec);
/* Assume a 7-bit address, which is reasonable for SMBus */
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
@@ -115,7 +123,7 @@ EXPORT_SYMBOL(i2c_smbus_read_byte);
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
- I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
+ I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}
EXPORT_SYMBOL(i2c_smbus_write_byte);
@@ -325,8 +333,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
*/
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
- int num = read_write == I2C_SMBUS_READ ? 2 : 1;
- int i;
+ int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1;
u8 partial_pec = 0;
int status;
struct i2c_msg msg[2] = {
@@ -342,6 +349,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
.buf = msgbuf1,
},
};
+ bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
+ && size != I2C_SMBUS_I2C_BLOCK_DATA);
msgbuf0[0] = command;
switch (size) {
@@ -350,13 +359,13 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
/* Special case: The read/write field is used as data */
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
I2C_M_RD : 0);
- num = 1;
+ nmsgs = 1;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
/* Special case: only a read! */
msg[0].flags = I2C_M_RD | flags;
- num = 1;
+ nmsgs = 1;
}
break;
case I2C_SMBUS_BYTE_DATA:
@@ -377,7 +386,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
}
break;
case I2C_SMBUS_PROC_CALL:
- num = 2; /* Special case */
+ nmsgs = 2; /* Special case */
read_write = I2C_SMBUS_READ;
msg[0].len = 3;
msg[1].len = 2;
@@ -400,12 +409,11 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
}
i2c_smbus_try_get_dmabuf(&msg[0], command);
- for (i = 1; i < msg[0].len; i++)
- msg[0].buf[i] = data->block[i - 1];
+ memcpy(msg[0].buf + 1, data->block, msg[0].len - 1);
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
- num = 2; /* Another special case */
+ nmsgs = 2; /* Another special case */
read_write = I2C_SMBUS_READ;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
dev_err(&adapter->dev,
@@ -416,8 +424,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
msg[0].len = data->block[0] + 2;
i2c_smbus_try_get_dmabuf(&msg[0], command);
- for (i = 1; i < msg[0].len; i++)
- msg[0].buf[i] = data->block[i - 1];
+ memcpy(msg[0].buf + 1, data->block, msg[0].len - 1);
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by
@@ -427,7 +434,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
case I2C_SMBUS_I2C_BLOCK_DATA:
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
dev_err(&adapter->dev, "Invalid block %s size %d\n",
- read_write == I2C_SMBUS_READ ? "read" : "write",
+ str_read_write(read_write == I2C_SMBUS_READ),
data->block[0]);
return -EINVAL;
}
@@ -439,8 +446,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
msg[0].len = data->block[0] + 1;
i2c_smbus_try_get_dmabuf(&msg[0], command);
- for (i = 1; i <= data->block[0]; i++)
- msg[0].buf[i] = data->block[i];
+ memcpy(msg[0].buf + 1, data->block + 1, data->block[0]);
}
break;
default:
@@ -448,33 +454,31 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
return -EOPNOTSUPP;
}
- i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
- && size != I2C_SMBUS_I2C_BLOCK_DATA);
- if (i) {
+ if (wants_pec) {
/* Compute PEC if first message is a write */
if (!(msg[0].flags & I2C_M_RD)) {
- if (num == 1) /* Write only */
+ if (nmsgs == 1) /* Write only */
i2c_smbus_add_pec(&msg[0]);
else /* Write followed by read */
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
}
/* Ask for PEC if last message is a read */
- if (msg[num-1].flags & I2C_M_RD)
- msg[num-1].len++;
+ if (msg[nmsgs - 1].flags & I2C_M_RD)
+ msg[nmsgs - 1].len++;
}
- status = __i2c_transfer(adapter, msg, num);
+ status = __i2c_transfer(adapter, msg, nmsgs);
if (status < 0)
goto cleanup;
- if (status != num) {
+ if (status != nmsgs) {
status = -EIO;
goto cleanup;
}
status = 0;
/* Check PEC if last message is a read */
- if (i && (msg[num-1].flags & I2C_M_RD)) {
- status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
+ if (wants_pec && (msg[nmsgs - 1].flags & I2C_M_RD)) {
+ status = i2c_smbus_check_pec(partial_pec, &msg[nmsgs - 1]);
if (status < 0)
goto cleanup;
}
@@ -492,13 +496,18 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
- for (i = 0; i < data->block[0]; i++)
- data->block[i + 1] = msg[1].buf[i];
+ memcpy(data->block + 1, msg[1].buf, data->block[0]);
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
- for (i = 0; i < msg[1].buf[0] + 1; i++)
- data->block[i] = msg[1].buf[i];
+ if (msg[1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&adapter->dev,
+ "Invalid block size returned: %d\n",
+ msg[1].buf[0]);
+ status = -EPROTO;
+ goto cleanup;
+ }
+ memcpy(data->block, msg[1].buf, msg[1].buf[0] + 1);
break;
}
@@ -530,7 +539,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
{
s32 res;
- i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
+ res = __i2c_lock_bus_helper(adapter);
+ if (res)
+ return res;
+
res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
command, protocol, data);
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
@@ -543,10 +555,17 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write,
u8 command, int protocol, union i2c_smbus_data *data)
{
+ int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data);
unsigned long orig_jiffies;
int try;
s32 res;
+ res = __i2c_check_suspended(adapter);
+ if (res)
+ return res;
+
/* If enabled, the following two tracepoints are conditional on
* read_write and protocol.
*/
@@ -557,13 +576,20 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
- if (adapter->algo->smbus_xfer) {
+ xfer_func = adapter->algo->smbus_xfer;
+ if (i2c_in_atomic_xfer_mode()) {
+ if (adapter->algo->smbus_xfer_atomic)
+ xfer_func = adapter->algo->smbus_xfer_atomic;
+ else if (adapter->algo->master_xfer_atomic)
+ xfer_func = NULL; /* fallback to I2C emulation */
+ }
+
+ if (xfer_func) {
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
- res = adapter->algo->smbus_xfer(adapter, addr, flags,
- read_write, command,
- protocol, data);
+ res = xfer_func(adapter, addr, flags, read_write,
+ command, protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
@@ -585,7 +611,7 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
trace:
/* If enabled, the reply tracepoint is conditional on read_write. */
trace_smbus_reply(adapter, addr, flags, read_write,
- command, protocol, data);
+ command, protocol, data, res);
trace_smbus_result(adapter, addr, flags, read_write,
command, protocol, res);
@@ -651,7 +677,7 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
/**
- * i2c_setup_smbus_alert - Setup SMBus alert support
+ * i2c_new_smbus_alert_device - get ara client for SMBus alert support
* @adapter: the target adapter
* @setup: setup data for the SMBus alert handler
* Context: can sleep
@@ -661,45 +687,41 @@ EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
* Handling can be done either through our IRQ handler, or by the
* adapter (from its handler, periodic polling, or whatever).
*
- * NOTE that if we manage the IRQ, we *MUST* know if it's level or
- * edge triggered in order to hand it to the workqueue correctly.
- * If triggering the alert seems to wedge the system, you probably
- * should have said it's level triggered.
- *
* This returns the ara client, which should be saved for later use with
- * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
- * to indicate an error.
+ * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an
+ * ERRPTR to indicate an error.
*/
-struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
- struct i2c_smbus_alert_setup *setup)
+struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
+ struct i2c_smbus_alert_setup *setup)
{
struct i2c_board_info ara_board_info = {
I2C_BOARD_INFO("smbus_alert", 0x0c),
.platform_data = setup,
};
- return i2c_new_device(adapter, &ara_board_info);
+ return i2c_new_client_device(adapter, &ara_board_info);
}
-EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
+EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device);
-#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
-int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
+#if IS_ENABLED(CONFIG_I2C_SMBUS)
+int i2c_setup_smbus_alert(struct i2c_adapter *adapter)
{
- struct i2c_client *client;
+ struct device *parent = adapter->dev.parent;
int irq;
- irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
- "smbus_alert");
- if (irq == -EINVAL || irq == -ENODATA)
+ /* Adapter instantiated without parent, skip the SMBus alert setup */
+ if (!parent)
return 0;
- else if (irq < 0)
+
+ /* Report serious errors */
+ irq = device_property_match_string(parent, "interrupt-names", "smbus_alert");
+ if (irq < 0 && irq != -EINVAL && irq != -ENODATA)
return irq;
- client = i2c_setup_smbus_alert(adapter, NULL);
- if (!client)
- return -ENODEV;
+ /* Skip setup when no irq was found */
+ if (irq < 0 && !device_property_present(parent, "smbalert-gpios"))
+ return 0;
- return 0;
+ return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
}
-EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
#endif
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
index 37576f50fe20..4797ba88331c 100644
--- a/drivers/i2c/i2c-core.h
+++ b/drivers/i2c/i2c-core.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* i2c-core.h - interfaces internal to the I2C framework
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+#include <linux/kconfig.h>
#include <linux/rwsem.h>
struct i2c_devinfo {
@@ -28,19 +20,56 @@ extern struct list_head __i2c_board_list;
extern int __i2c_first_dynamic_bus_num;
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
+int i2c_dev_irq_from_resources(const struct resource *resources,
+ unsigned int num_resources);
+
+/*
+ * We only allow atomic transfers for very late communication, e.g. to access a
+ * PMIC when powering down. Atomic transfers are a corner case and not for
+ * generic use!
+ */
+static inline bool i2c_in_atomic_xfer_mode(void)
+{
+ return system_state > SYSTEM_RUNNING &&
+ (IS_ENABLED(CONFIG_PREEMPT_COUNT) ? !preemptible() : irqs_disabled());
+}
+
+static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
+{
+ int ret = 0;
+
+ if (i2c_in_atomic_xfer_mode()) {
+ WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic,
+ "No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev));
+ ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN;
+ } else {
+ i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
+ }
+
+ return ret;
+}
+
+static inline int __i2c_check_suspended(struct i2c_adapter *adap)
+{
+ if (test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)) {
+ if (!test_and_set_bit(I2C_ALF_SUSPEND_REPORTED, &adap->locked_flags))
+ dev_WARN(&adap->dev, "Transfer while suspended\n");
+ return -ESHUTDOWN;
+ }
+
+ return 0;
+}
#ifdef CONFIG_ACPI
-const struct acpi_device_id *
-i2c_acpi_match_device(const struct acpi_device_id *matches,
- struct i2c_client *client);
void i2c_acpi_register_devices(struct i2c_adapter *adap);
+
+int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable);
#else /* CONFIG_ACPI */
static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { }
-static inline const struct acpi_device_id *
-i2c_acpi_match_device(const struct acpi_device_id *matches,
- struct i2c_client *client)
+
+static inline int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable)
{
- return NULL;
+ return 0;
}
#endif /* CONFIG_ACPI */
extern struct notifier_block i2c_acpi_notifier;
@@ -55,7 +84,25 @@ static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) {
#ifdef CONFIG_OF
void of_i2c_register_devices(struct i2c_adapter *adap);
+const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches,
+ struct i2c_client *client);
+
#else
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
+static inline
+const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches,
+ struct i2c_client *client)
+{
+ return NULL;
+}
#endif
extern struct notifier_block i2c_of_notifier;
+
+#if IS_ENABLED(CONFIG_I2C_SMBUS)
+int i2c_setup_smbus_alert(struct i2c_adapter *adap);
+#else
+static inline int i2c_setup_smbus_alert(struct i2c_adapter *adap)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index ccd76c71af09..e9577f920286 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
i2c-dev.c - i2c-bus driver, char device interface
@@ -5,15 +6,6 @@
Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
@@ -22,7 +14,10 @@
/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/cdev.h>
+#include <linux/compat.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
@@ -35,7 +30,6 @@
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
-#include <linux/compat.h>
/*
* An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a
@@ -48,11 +42,11 @@
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
- struct device *dev;
+ struct device dev;
struct cdev cdev;
};
-#define I2C_MINORS MINORMASK
+#define I2C_MINORS (MINORMASK + 1)
static LIST_HEAD(i2c_dev_list);
static DEFINE_SPINLOCK(i2c_dev_list_lock);
@@ -76,8 +70,7 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
struct i2c_dev *i2c_dev;
if (adap->nr >= I2C_MINORS) {
- printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
- adap->nr);
+ pr_err("Out of device minors (%d)\n", adap->nr);
return ERR_PTR(-ENODEV);
}
@@ -92,12 +85,14 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
return i2c_dev;
}
-static void put_i2c_dev(struct i2c_dev *i2c_dev)
+static void put_i2c_dev(struct i2c_dev *i2c_dev, bool del_cdev)
{
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list);
spin_unlock(&i2c_dev_list_lock);
- kfree(i2c_dev);
+ if (del_cdev)
+ cdev_device_del(&i2c_dev->cdev, &i2c_dev->dev);
+ put_device(&i2c_dev->dev);
}
static ssize_t name_show(struct device *dev,
@@ -107,7 +102,7 @@ static ssize_t name_show(struct device *dev,
if (!i2c_dev)
return -ENODEV;
- return sprintf(buf, "%s\n", i2c_dev->adap->name);
+ return sysfs_emit(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR_RO(name);
@@ -144,19 +139,23 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
struct i2c_client *client = file->private_data;
+ /* Adapter must support I2C transfers */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
if (count > 8192)
count = 8192;
- tmp = kmalloc(count, GFP_KERNEL);
+ tmp = kzalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
- pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
- iminor(file_inode(file)), count);
+ pr_debug("i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count);
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
- ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
+ if (copy_to_user(buf, tmp, ret))
+ ret = -EFAULT;
kfree(tmp);
return ret;
}
@@ -168,6 +167,10 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
char *tmp;
struct i2c_client *client = file->private_data;
+ /* Adapter must support I2C transfers */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
if (count > 8192)
count = 8192;
@@ -175,8 +178,7 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
if (IS_ERR(tmp))
return PTR_ERR(tmp);
- pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
- iminor(file_inode(file)), count);
+ pr_debug("i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count);
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
@@ -244,11 +246,13 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
u8 __user **data_ptrs;
int i, res;
+ /* Adapter must support I2C transfers */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL);
- if (data_ptrs == NULL) {
- kfree(msgs);
+ if (!data_ptrs)
return -ENOMEM;
- }
res = 0;
for (i = 0; i < nmsgs; i++) {
@@ -283,6 +287,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
msgs[i].len < 1 || msgs[i].buf[0] < 1 ||
msgs[i].len < msgs[i].buf[0] +
I2C_SMBUS_BLOCK_MAX) {
+ i++;
res = -EINVAL;
break;
}
@@ -295,7 +300,6 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
for (j = 0; j < i; ++j)
kfree(msgs[j].buf);
kfree(data_ptrs);
- kfree(msgs);
return res;
}
@@ -309,7 +313,6 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
kfree(msgs[i].buf);
}
kfree(data_ptrs);
- kfree(msgs);
return res;
}
@@ -439,23 +442,31 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case I2C_RDWR: {
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
+ int res;
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
- /* Put an arbitrary limit on the number of messages that can
- * be sent at once */
+ if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
+ return -EINVAL;
+
+ /*
+ * Put an arbitrary limit on the number of messages that can
+ * be sent at once
+ */
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
return -EINVAL;
- rdwr_pa = memdup_user(rdwr_arg.msgs,
- rdwr_arg.nmsgs * sizeof(struct i2c_msg));
+ rdwr_pa = memdup_array_user(rdwr_arg.msgs,
+ rdwr_arg.nmsgs, sizeof(struct i2c_msg));
if (IS_ERR(rdwr_pa))
return PTR_ERR(rdwr_pa);
- return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
+ res = i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
+ kfree(rdwr_pa);
+ return res;
}
case I2C_SMBUS: {
@@ -526,15 +537,18 @@ static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned lo
return put_user(funcs, (compat_ulong_t __user *)arg);
case I2C_RDWR: {
struct i2c_rdwr_ioctl_data32 rdwr_arg;
- struct i2c_msg32 *p;
+ struct i2c_msg32 __user *p;
struct i2c_msg *rdwr_pa;
- int i;
+ int i, res;
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data32 __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
+ if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
+ return -EINVAL;
+
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
return -EINVAL;
@@ -554,11 +568,13 @@ static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned lo
.addr = umsg.addr,
.flags = umsg.flags,
.len = umsg.len,
- .buf = compat_ptr(umsg.buf)
+ .buf = (__force __u8 *)compat_ptr(umsg.buf),
};
}
- return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
+ res = i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
+ kfree(rdwr_pa);
+ return res;
}
case I2C_SMBUS: {
struct i2c_smbus_ioctl_data32 data32;
@@ -622,7 +638,6 @@ static int i2cdev_release(struct inode *inode, struct file *file)
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
@@ -633,66 +648,75 @@ static const struct file_operations i2cdev_fops = {
/* ------------------------------------------------------------------------- */
-static struct class *i2c_dev_class;
+static const struct class i2c_dev_class = {
+ .name = "i2c-dev",
+ .dev_groups = i2c_groups,
+};
+
+static void i2cdev_dev_release(struct device *dev)
+{
+ struct i2c_dev *i2c_dev;
+
+ i2c_dev = container_of(dev, struct i2c_dev, dev);
+ kfree(i2c_dev);
+}
-static int i2cdev_attach_adapter(struct device *dev, void *dummy)
+static int i2cdev_attach_adapter(struct device *dev)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
- return 0;
+ return NOTIFY_DONE;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
- return PTR_ERR(i2c_dev);
+ return NOTIFY_DONE;
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
i2c_dev->cdev.owner = THIS_MODULE;
- res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
+
+ device_initialize(&i2c_dev->dev);
+ i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
+ i2c_dev->dev.class = &i2c_dev_class;
+ i2c_dev->dev.parent = &adap->dev;
+ i2c_dev->dev.release = i2cdev_dev_release;
+
+ res = dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr);
if (res)
- goto error_cdev;
-
- /* register this i2c device with the driver core */
- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
- MKDEV(I2C_MAJOR, adap->nr), NULL,
- "i2c-%d", adap->nr);
- if (IS_ERR(i2c_dev->dev)) {
- res = PTR_ERR(i2c_dev->dev);
- goto error;
- }
+ goto err_put_i2c_dev;
- pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
- adap->name, adap->nr);
- return 0;
-error:
- cdev_del(&i2c_dev->cdev);
-error_cdev:
- put_i2c_dev(i2c_dev);
- return res;
+ res = cdev_device_add(&i2c_dev->cdev, &i2c_dev->dev);
+ if (res)
+ goto err_put_i2c_dev;
+
+ pr_debug("adapter [%s] registered as minor %d\n", adap->name, adap->nr);
+ return NOTIFY_OK;
+
+err_put_i2c_dev:
+ put_i2c_dev(i2c_dev, false);
+ return NOTIFY_DONE;
}
-static int i2cdev_detach_adapter(struct device *dev, void *dummy)
+static int i2cdev_detach_adapter(struct device *dev)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
if (dev->type != &i2c_adapter_type)
- return 0;
+ return NOTIFY_DONE;
adap = to_i2c_adapter(dev);
i2c_dev = i2c_dev_get_by_minor(adap->nr);
if (!i2c_dev) /* attach_adapter must have failed */
- return 0;
+ return NOTIFY_DONE;
- cdev_del(&i2c_dev->cdev);
- put_i2c_dev(i2c_dev);
- device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
+ put_i2c_dev(i2c_dev, true);
- pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
- return 0;
+ pr_debug("adapter [%s] unregistered\n", adap->name);
+ return NOTIFY_OK;
}
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
@@ -702,12 +726,12 @@ static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
- return i2cdev_attach_adapter(dev, NULL);
+ return i2cdev_attach_adapter(dev);
case BUS_NOTIFY_DEL_DEVICE:
- return i2cdev_detach_adapter(dev, NULL);
+ return i2cdev_detach_adapter(dev);
}
- return 0;
+ return NOTIFY_DONE;
}
static struct notifier_block i2cdev_notifier = {
@@ -716,6 +740,18 @@ static struct notifier_block i2cdev_notifier = {
/* ------------------------------------------------------------------------- */
+static int __init i2c_dev_attach_adapter(struct device *dev, void *dummy)
+{
+ i2cdev_attach_adapter(dev);
+ return 0;
+}
+
+static int __exit i2c_dev_detach_adapter(struct device *dev, void *dummy)
+{
+ i2cdev_detach_adapter(dev);
+ return 0;
+}
+
/*
* module load/unload record keeping
*/
@@ -724,18 +760,15 @@ static int __init i2c_dev_init(void)
{
int res;
- printk(KERN_INFO "i2c /dev entries driver\n");
+ pr_info("i2c /dev entries driver\n");
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
- i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
- if (IS_ERR(i2c_dev_class)) {
- res = PTR_ERR(i2c_dev_class);
+ res = class_register(&i2c_dev_class);
+ if (res)
goto out_unreg_chrdev;
- }
- i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
@@ -743,29 +776,29 @@ static int __init i2c_dev_init(void)
goto out_unreg_class;
/* Bind to already existing adapters right away */
- i2c_for_each_dev(NULL, i2cdev_attach_adapter);
+ i2c_for_each_dev(NULL, i2c_dev_attach_adapter);
return 0;
out_unreg_class:
- class_destroy(i2c_dev_class);
+ class_unregister(&i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
- printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
+ pr_err("Driver Initialisation failed\n");
return res;
}
static void __exit i2c_dev_exit(void)
{
bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);
- i2c_for_each_dev(NULL, i2cdev_detach_adapter);
- class_destroy(i2c_dev_class);
+ i2c_for_each_dev(NULL, i2c_dev_detach_adapter);
+ class_unregister(&i2c_dev_class);
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
}
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
- "Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C /dev entries driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index f330690b4125..d59644e50f14 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/sysfs.h>
/* multiplexer per channel data */
struct i2c_mux_priv {
@@ -126,19 +127,6 @@ static u32 i2c_mux_functionality(struct i2c_adapter *adap)
return parent->algo->functionality(parent);
}
-/* Return all parent classes, merged */
-static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent)
-{
- unsigned int class = 0;
-
- do {
- class |= parent->class;
- parent = i2c_parent_is_i2c_adapter(parent);
- } while (parent);
-
- return class;
-}
-
static void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
{
struct i2c_mux_priv *priv = adapter->algo_data;
@@ -242,10 +230,10 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
int (*deselect)(struct i2c_mux_core *, u32))
{
struct i2c_mux_core *muxc;
+ size_t mux_size;
- muxc = devm_kzalloc(dev, sizeof(*muxc)
- + max_adapters * sizeof(muxc->adapter[0])
- + sizeof_priv, GFP_KERNEL);
+ mux_size = struct_size(muxc, adapter, max_adapters);
+ muxc = devm_kzalloc(dev, size_add(mux_size, sizeof_priv), GFP_KERNEL);
if (!muxc)
return NULL;
if (sizeof_priv)
@@ -253,12 +241,9 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
muxc->parent = parent;
muxc->dev = dev;
- if (flags & I2C_MUX_LOCKED)
- muxc->mux_locked = true;
- if (flags & I2C_MUX_ARBITRATOR)
- muxc->arbitrator = true;
- if (flags & I2C_MUX_GATE)
- muxc->gate = true;
+ muxc->mux_locked = !!(flags & I2C_MUX_LOCKED);
+ muxc->arbitrator = !!(flags & I2C_MUX_ARBITRATOR);
+ muxc->gate = !!(flags & I2C_MUX_GATE);
muxc->select = select;
muxc->deselect = deselect;
muxc->max_adapters = max_adapters;
@@ -280,8 +265,7 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = {
};
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
- u32 force_nr, u32 chan_id,
- unsigned int class)
+ u32 force_nr, u32 chan_id)
{
struct i2c_adapter *parent = muxc->parent;
struct i2c_mux_priv *priv;
@@ -306,16 +290,22 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
*/
if (parent->algo->master_xfer) {
if (muxc->mux_locked)
- priv->algo.master_xfer = i2c_mux_master_xfer;
+ priv->algo.xfer = i2c_mux_master_xfer;
else
- priv->algo.master_xfer = __i2c_mux_master_xfer;
+ priv->algo.xfer = __i2c_mux_master_xfer;
}
+ if (parent->algo->master_xfer_atomic)
+ priv->algo.xfer_atomic = priv->algo.master_xfer;
+
if (parent->algo->smbus_xfer) {
if (muxc->mux_locked)
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
else
priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
}
+ if (parent->algo->smbus_xfer_atomic)
+ priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer;
+
priv->algo.functionality = i2c_mux_functionality;
/* Now fill out new adapter structure */
@@ -333,14 +323,6 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
else
priv->adap.lock_ops = &i2c_parent_lock_ops;
- /* Sanity check on class */
- if (i2c_mux_parent_classes(parent) & class)
- dev_err(&parent->dev,
- "Segment %d behind mux can't share classes with ancestors\n",
- chan_id);
- else
- priv->adap.class = class;
-
/*
* Try to populate the mux adapter's of_node, expands to
* nothing if !CONFIG_OF.
diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c
index b2039f94c9d8..6bc2ef650a74 100644
--- a/drivers/i2c/i2c-slave-eeprom.c
+++ b/drivers/i2c/i2c-slave-eeprom.c
@@ -1,19 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C slave mode EEPROM simulator
*
* Copyright (C) 2014 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
* Copyright (C) 2014 by Renesas Electronics Corporation
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
- *
- * Because most IP blocks can only detect one I2C slave address anyhow, this
- * driver does not support simulating EEPROM types which take more than one
- * address. It is prepared to simulate bigger EEPROMs with an internal 16 bit
- * pointer, yet implementation is deferred until the need actually arises.
+ * Because most slave IP cores can only detect one I2C slave address anyhow,
+ * this driver does not support simulating EEPROM types which take more than
+ * one address.
+ */
+
+/*
+ * FIXME: What to do if only 8 bits of a 16 bit address are sent?
+ * The ST-M24C64 sends only 0xff then. Needs verification with other
+ * EEPROMs, though. We currently use the 8 bit as a valid address.
*/
+#include <linux/bitfield.h>
+#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -24,12 +28,20 @@
struct eeprom_data {
struct bin_attribute bin;
- bool first_write;
spinlock_t buffer_lock;
- u8 buffer_idx;
+ u16 buffer_idx;
+ u16 address_mask;
+ u8 num_address_bytes;
+ u8 idx_write_cnt;
+ bool read_only;
u8 buffer[];
};
+#define I2C_SLAVE_BYTELEN GENMASK(15, 0)
+#define I2C_SLAVE_FLAG_ADDR16 BIT(16)
+#define I2C_SLAVE_FLAG_RO BIT(17)
+#define I2C_SLAVE_DEVICE_MAGIC(_len, _flags) ((_flags) | ((_len) - 1))
+
static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
@@ -37,23 +49,27 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
switch (event) {
case I2C_SLAVE_WRITE_RECEIVED:
- if (eeprom->first_write) {
- eeprom->buffer_idx = *val;
- eeprom->first_write = false;
+ if (eeprom->idx_write_cnt < eeprom->num_address_bytes) {
+ if (eeprom->idx_write_cnt == 0)
+ eeprom->buffer_idx = 0;
+ eeprom->buffer_idx = *val | (eeprom->buffer_idx << 8);
+ eeprom->idx_write_cnt++;
} else {
- spin_lock(&eeprom->buffer_lock);
- eeprom->buffer[eeprom->buffer_idx++] = *val;
- spin_unlock(&eeprom->buffer_lock);
+ if (!eeprom->read_only) {
+ spin_lock(&eeprom->buffer_lock);
+ eeprom->buffer[eeprom->buffer_idx++ & eeprom->address_mask] = *val;
+ spin_unlock(&eeprom->buffer_lock);
+ }
}
break;
case I2C_SLAVE_READ_PROCESSED:
/* The previous byte made it to the bus, get next one */
eeprom->buffer_idx++;
- /* fallthrough */
+ fallthrough;
case I2C_SLAVE_READ_REQUESTED:
spin_lock(&eeprom->buffer_lock);
- *val = eeprom->buffer[eeprom->buffer_idx];
+ *val = eeprom->buffer[eeprom->buffer_idx & eeprom->address_mask];
spin_unlock(&eeprom->buffer_lock);
/*
* Do not increment buffer_idx here, because we don't know if
@@ -64,7 +80,7 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
case I2C_SLAVE_STOP:
case I2C_SLAVE_WRITE_REQUESTED:
- eeprom->first_write = true;
+ eeprom->idx_write_cnt = 0;
break;
default:
@@ -75,12 +91,12 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
}
static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+ const struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
struct eeprom_data *eeprom;
unsigned long flags;
- eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ eeprom = dev_get_drvdata(kobj_to_dev(kobj));
spin_lock_irqsave(&eeprom->buffer_lock, flags);
memcpy(buf, &eeprom->buffer[off], count);
@@ -90,12 +106,12 @@ static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj
}
static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+ const struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
struct eeprom_data *eeprom;
unsigned long flags;
- eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ eeprom = dev_get_drvdata(kobj_to_dev(kobj));
spin_lock_irqsave(&eeprom->buffer_lock, flags);
memcpy(&eeprom->buffer[off], buf, count);
@@ -104,20 +120,48 @@ static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kob
return count;
}
-static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int i2c_slave_init_eeprom_data(struct eeprom_data *eeprom, struct i2c_client *client,
+ unsigned int size)
{
+ const struct firmware *fw;
+ const char *eeprom_data;
+ int ret = device_property_read_string(&client->dev, "firmware-name", &eeprom_data);
+
+ if (!ret) {
+ ret = request_firmware_into_buf(&fw, eeprom_data, &client->dev,
+ eeprom->buffer, size);
+ if (ret)
+ return ret;
+ release_firmware(fw);
+ } else {
+ /* An empty eeprom typically has all bits set to 1 */
+ memset(eeprom->buffer, 0xff, size);
+ }
+ return 0;
+}
+
+static int i2c_slave_eeprom_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct eeprom_data *eeprom;
int ret;
- unsigned size = id->driver_data;
+ unsigned int size = FIELD_GET(I2C_SLAVE_BYTELEN, id->driver_data) + 1;
+ unsigned int flag_addr16 = FIELD_GET(I2C_SLAVE_FLAG_ADDR16, id->driver_data);
eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL);
if (!eeprom)
return -ENOMEM;
- eeprom->first_write = true;
+ eeprom->num_address_bytes = flag_addr16 ? 2 : 1;
+ eeprom->address_mask = size - 1;
+ eeprom->read_only = FIELD_GET(I2C_SLAVE_FLAG_RO, id->driver_data);
spin_lock_init(&eeprom->buffer_lock);
i2c_set_clientdata(client, eeprom);
+ ret = i2c_slave_init_eeprom_data(eeprom, client, size);
+ if (ret)
+ return ret;
+
sysfs_bin_attr_init(&eeprom->bin);
eeprom->bin.attr.name = "slave-eeprom";
eeprom->bin.attr.mode = S_IRUSR | S_IWUSR;
@@ -138,18 +182,23 @@ static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_de
return 0;
};
-static int i2c_slave_eeprom_remove(struct i2c_client *client)
+static void i2c_slave_eeprom_remove(struct i2c_client *client)
{
struct eeprom_data *eeprom = i2c_get_clientdata(client);
i2c_slave_unregister(client);
sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
-
- return 0;
}
static const struct i2c_device_id i2c_slave_eeprom_id[] = {
- { "slave-24c02", 2048 / 8 },
+ { "slave-24c02", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) },
+ { "slave-24c02ro", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) },
+ { "slave-24c32", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) },
+ { "slave-24c32ro", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) },
+ { "slave-24c64", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) },
+ { "slave-24c64ro", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) },
+ { "slave-24c512", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) },
+ { "slave-24c512ro", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) },
{ }
};
MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id);
diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c
new file mode 100644
index 000000000000..6de4307050dd
--- /dev/null
+++ b/drivers/i2c/i2c-slave-testunit.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C slave mode testunit
+ *
+ * Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
+ * Copyright (C) 2020 by Renesas Electronics Corporation
+ */
+
+#include <generated/utsrelease.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
+
+#define TU_VERSION_MAX_LENGTH 128
+
+enum testunit_cmds {
+ TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
+ TU_CMD_SMBUS_HOST_NOTIFY,
+ TU_CMD_SMBUS_BLOCK_PROC_CALL,
+ TU_CMD_GET_VERSION_WITH_REP_START,
+ TU_CMD_SMBUS_ALERT_REQUEST,
+ TU_NUM_CMDS
+};
+
+enum testunit_regs {
+ TU_REG_CMD,
+ TU_REG_DATAL,
+ TU_REG_DATAH,
+ TU_REG_DELAY,
+ TU_NUM_REGS
+};
+
+enum testunit_flags {
+ TU_FLAG_IN_PROCESS,
+ TU_FLAG_NACK,
+};
+
+struct testunit_data {
+ unsigned long flags;
+ u8 regs[TU_NUM_REGS];
+ u8 reg_idx;
+ u8 read_idx;
+ struct i2c_client *client;
+ struct delayed_work worker;
+ struct gpio_desc *gpio;
+ struct completion alert_done;
+};
+
+static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
+
+static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ struct testunit_data *tu = i2c_get_clientdata(client);
+
+ switch (event) {
+ case I2C_SLAVE_READ_PROCESSED:
+ gpiod_set_value(tu->gpio, 0);
+ fallthrough;
+ case I2C_SLAVE_READ_REQUESTED:
+ *val = tu->regs[TU_REG_DATAL];
+ break;
+
+ case I2C_SLAVE_STOP:
+ complete(&tu->alert_done);
+ break;
+
+ case I2C_SLAVE_WRITE_REQUESTED:
+ case I2C_SLAVE_WRITE_RECEIVED:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ struct testunit_data *tu = i2c_get_clientdata(client);
+ bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
+ tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
+ bool is_get_version = tu->reg_idx == 3 &&
+ tu->regs[TU_REG_CMD] == TU_CMD_GET_VERSION_WITH_REP_START;
+ int ret = 0;
+
+ switch (event) {
+ case I2C_SLAVE_WRITE_REQUESTED:
+ if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) {
+ ret = -EBUSY;
+ break;
+ }
+
+ memset(tu->regs, 0, TU_NUM_REGS);
+ tu->reg_idx = 0;
+ tu->read_idx = 0;
+ break;
+
+ case I2C_SLAVE_WRITE_RECEIVED:
+ if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) {
+ ret = -EBUSY;
+ break;
+ }
+
+ if (tu->reg_idx < TU_NUM_REGS)
+ tu->regs[tu->reg_idx] = *val;
+ else
+ ret = -EMSGSIZE;
+
+ if (tu->reg_idx <= TU_NUM_REGS)
+ tu->reg_idx++;
+
+ /* TU_REG_CMD always written at this point */
+ if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
+ ret = -EINVAL;
+
+ break;
+
+ case I2C_SLAVE_STOP:
+ if (tu->reg_idx == TU_NUM_REGS) {
+ set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
+ queue_delayed_work(system_long_wq, &tu->worker,
+ msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
+ }
+
+ /*
+ * Reset reg_idx to avoid that work gets queued again in case of
+ * STOP after a following read message. But do not clear TU regs
+ * here because we still need them in the workqueue!
+ */
+ tu->reg_idx = 0;
+
+ clear_bit(TU_FLAG_NACK, &tu->flags);
+ break;
+
+ case I2C_SLAVE_READ_PROCESSED:
+ /* Advance until we reach the NUL character */
+ if (is_get_version && tu_version_info[tu->read_idx] != 0)
+ tu->read_idx++;
+ else if (is_proc_call && tu->regs[TU_REG_DATAH])
+ tu->regs[TU_REG_DATAH]--;
+
+ fallthrough;
+
+ case I2C_SLAVE_READ_REQUESTED:
+ if (is_get_version)
+ *val = tu_version_info[tu->read_idx];
+ else if (is_proc_call)
+ *val = tu->regs[TU_REG_DATAH];
+ else
+ *val = test_bit(TU_FLAG_IN_PROCESS, &tu->flags) ?
+ tu->regs[TU_REG_CMD] : 0;
+ break;
+ }
+
+ /* If an error occurred somewhen, we NACK everything until next STOP */
+ if (ret)
+ set_bit(TU_FLAG_NACK, &tu->flags);
+
+ return ret;
+}
+
+static void i2c_slave_testunit_work(struct work_struct *work)
+{
+ struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
+ unsigned long time_left;
+ struct i2c_msg msg;
+ u8 msgbuf[256];
+ u16 orig_addr;
+ int ret = 0;
+
+ msg.addr = I2C_CLIENT_END;
+ msg.buf = msgbuf;
+
+ switch (tu->regs[TU_REG_CMD]) {
+ case TU_CMD_READ_BYTES:
+ msg.addr = tu->regs[TU_REG_DATAL];
+ msg.flags = I2C_M_RD;
+ msg.len = tu->regs[TU_REG_DATAH];
+ break;
+
+ case TU_CMD_SMBUS_HOST_NOTIFY:
+ msg.addr = 0x08;
+ msg.flags = 0;
+ msg.len = 3;
+ msgbuf[0] = tu->client->addr;
+ msgbuf[1] = tu->regs[TU_REG_DATAL];
+ msgbuf[2] = tu->regs[TU_REG_DATAH];
+ break;
+
+ case TU_CMD_SMBUS_ALERT_REQUEST:
+ if (!tu->gpio) {
+ ret = -ENOENT;
+ break;
+ }
+ i2c_slave_unregister(tu->client);
+ orig_addr = tu->client->addr;
+ tu->client->addr = 0x0c;
+ ret = i2c_slave_register(tu->client, i2c_slave_testunit_smbalert_cb);
+ if (ret)
+ goto out_smbalert;
+
+ reinit_completion(&tu->alert_done);
+ gpiod_set_value(tu->gpio, 1);
+ time_left = wait_for_completion_timeout(&tu->alert_done, HZ);
+ if (!time_left)
+ ret = -ETIMEDOUT;
+
+ i2c_slave_unregister(tu->client);
+out_smbalert:
+ tu->client->addr = orig_addr;
+ i2c_slave_register(tu->client, i2c_slave_testunit_slave_cb);
+ break;
+
+ default:
+ break;
+ }
+
+ if (msg.addr != I2C_CLIENT_END) {
+ ret = i2c_transfer(tu->client->adapter, &msg, 1);
+ /* convert '0 msgs transferred' to errno */
+ ret = (ret == 0) ? -EIO : ret;
+ }
+
+ if (ret < 0)
+ dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret);
+
+ clear_bit(TU_FLAG_IN_PROCESS, &tu->flags);
+}
+
+static int i2c_slave_testunit_probe(struct i2c_client *client)
+{
+ struct testunit_data *tu;
+
+ tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL);
+ if (!tu)
+ return -ENOMEM;
+
+ tu->client = client;
+ i2c_set_clientdata(client, tu);
+ init_completion(&tu->alert_done);
+ INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
+
+ tu->gpio = devm_gpiod_get_index_optional(&client->dev, NULL, 0, GPIOD_OUT_LOW);
+ if (IS_ERR(tu->gpio))
+ return PTR_ERR(tu->gpio);
+
+ if (gpiod_cansleep(tu->gpio)) {
+ dev_err(&client->dev, "GPIO access which may sleep is not allowed\n");
+ return -EDEADLK;
+ }
+
+ if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
+ tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
+
+ return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
+};
+
+static void i2c_slave_testunit_remove(struct i2c_client *client)
+{
+ struct testunit_data *tu = i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&tu->worker);
+ i2c_slave_unregister(client);
+}
+
+static const struct i2c_device_id i2c_slave_testunit_id[] = {
+ { "slave-testunit" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id);
+
+static struct i2c_driver i2c_slave_testunit_driver = {
+ .driver = {
+ .name = "i2c-slave-testunit",
+ },
+ .probe = i2c_slave_testunit_probe,
+ .remove = i2c_slave_testunit_remove,
+ .id_table = i2c_slave_testunit_id,
+};
+module_i2c_driver(i2c_slave_testunit_driver);
+
+MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
+MODULE_DESCRIPTION("I2C slave mode test unit");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index 5a1dd7f13bac..0316b347f9e7 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -1,27 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c-smbus.c - SMBus extensions to the I2C protocol
*
* Copyright (C) 2008 David Brownell
- * Copyright (C) 2010 Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2010-2019 Jean Delvare <jdelvare@suse.de>
*/
#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_irq.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -42,6 +35,7 @@ static int smbus_do_alert(struct device *dev, void *addrp)
struct i2c_client *client = i2c_verify_client(dev);
struct alert_data *data = addrp;
struct i2c_driver *driver;
+ int ret;
if (!client || client->addr != data->addr)
return 0;
@@ -55,16 +49,47 @@ static int smbus_do_alert(struct device *dev, void *addrp)
device_lock(dev);
if (client->dev.driver) {
driver = to_i2c_driver(client->dev.driver);
- if (driver->alert)
+ if (driver->alert) {
+ /* Stop iterating after we find the device */
driver->alert(client, data->type, data->data);
- else
+ ret = -EBUSY;
+ } else {
dev_warn(&client->dev, "no driver alert()!\n");
- } else
+ ret = -EOPNOTSUPP;
+ }
+ } else {
dev_dbg(&client->dev, "alert with no driver\n");
+ ret = -ENODEV;
+ }
+ device_unlock(dev);
+
+ return ret;
+}
+
+/* Same as above, but call back all drivers with alert handler */
+
+static int smbus_do_alert_force(struct device *dev, void *addrp)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ struct alert_data *data = addrp;
+ struct i2c_driver *driver;
+
+ if (!client || (client->flags & I2C_CLIENT_TEN))
+ return 0;
+
+ /*
+ * Drivers should either disable alerts, or provide at least
+ * a minimal handler. Lock so the driver won't change.
+ */
+ device_lock(dev);
+ if (client->dev.driver) {
+ driver = to_i2c_driver(client->dev.driver);
+ if (driver->alert)
+ driver->alert(client, data->type, data->data);
+ }
device_unlock(dev);
- /* Stop iterating after we find the device */
- return -EBUSY;
+ return 0;
}
/*
@@ -75,7 +100,7 @@ static irqreturn_t smbus_alert(int irq, void *d)
{
struct i2c_smbus_alert *alert = d;
struct i2c_client *ara;
- unsigned short prev_addr = 0; /* Not a valid address */
+ unsigned short prev_addr = I2C_CLIENT_END; /* Not a valid address */
ara = alert->ara;
@@ -99,17 +124,28 @@ static irqreturn_t smbus_alert(int irq, void *d)
data.addr = status >> 1;
data.type = I2C_PROTOCOL_SMBUS_ALERT;
- if (data.addr == prev_addr) {
- dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
- "0x%02x, skipping\n", data.addr);
- break;
- }
dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
data.addr, data.data);
/* Notify driver for the device which issued the alert */
- device_for_each_child(&ara->adapter->dev, &data,
- smbus_do_alert);
+ status = device_for_each_child(&ara->adapter->dev, &data,
+ smbus_do_alert);
+ /*
+ * If we read the same address more than once, and the alert
+ * was not handled by a driver, it won't do any good to repeat
+ * the loop because it will never terminate. Try again, this
+ * time calling the alert handlers of all devices connected to
+ * the bus, and abort the loop afterwards. If this helps, we
+ * are all set. If it doesn't, there is nothing else we can do,
+ * so we might as well abort the loop.
+ * Note: This assumes that a driver with alert handler handles
+ * the alert properly and clears it if necessary.
+ */
+ if (data.addr == prev_addr && status != -EBUSY) {
+ device_for_each_child(&ara->adapter->dev, &data,
+ smbus_do_alert_force);
+ break;
+ }
prev_addr = data.addr;
}
@@ -127,12 +163,13 @@ static void smbalert_work(struct work_struct *work)
}
/* Setup SMBALERT# infrastructure */
-static int smbalert_probe(struct i2c_client *ara,
- const struct i2c_device_id *id)
+static int smbalert_probe(struct i2c_client *ara)
{
struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
struct i2c_smbus_alert *alert;
struct i2c_adapter *adapter = ara->adapter;
+ unsigned long irqflags = IRQF_SHARED | IRQF_ONESHOT;
+ struct gpio_desc *gpiod;
int res, irq;
alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
@@ -143,19 +180,27 @@ static int smbalert_probe(struct i2c_client *ara,
if (setup) {
irq = setup->irq;
} else {
- irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
- if (irq <= 0)
- return irq;
+ irq = fwnode_irq_get_byname(dev_fwnode(adapter->dev.parent),
+ "smbus_alert");
+ if (irq <= 0) {
+ gpiod = devm_gpiod_get(adapter->dev.parent, "smbalert", GPIOD_IN);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ irq = gpiod_to_irq(gpiod);
+ if (irq <= 0)
+ return irq;
+
+ irqflags |= IRQF_TRIGGER_FALLING;
+ }
}
INIT_WORK(&alert->alert, smbalert_work);
alert->ara = ara;
if (irq > 0) {
- res = devm_request_threaded_irq(&ara->dev, irq,
- NULL, smbus_alert,
- IRQF_SHARED | IRQF_ONESHOT,
- "smbus_alert", alert);
+ res = devm_request_threaded_irq(&ara->dev, irq, NULL, smbus_alert,
+ irqflags, "smbus_alert", alert);
if (res)
return res;
}
@@ -167,16 +212,15 @@ static int smbalert_probe(struct i2c_client *ara,
}
/* IRQ and memory resources are managed so they are freed automatically */
-static int smbalert_remove(struct i2c_client *ara)
+static void smbalert_remove(struct i2c_client *ara)
{
struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
cancel_work_sync(&alert->alert);
- return 0;
}
static const struct i2c_device_id smbalert_ids[] = {
- { "smbus_alert", 0 },
+ { "smbus_alert" },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, smbalert_ids);
@@ -200,7 +244,7 @@ static struct i2c_driver smbalert_driver = {
* corresponding I2C device driver's alert function.
*
* It is assumed that ara is a valid i2c client previously returned by
- * i2c_setup_smbus_alert().
+ * i2c_new_smbus_alert_device().
*/
int i2c_handle_smbus_alert(struct i2c_client *ara)
{
@@ -212,6 +256,239 @@ EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
module_i2c_driver(smbalert_driver);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+#define SMBUS_HOST_NOTIFY_LEN 3
+struct i2c_slave_host_notify_status {
+ u8 index;
+ u8 addr;
+};
+
+static int i2c_slave_host_notify_cb(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ struct i2c_slave_host_notify_status *status = client->dev.platform_data;
+
+ switch (event) {
+ case I2C_SLAVE_WRITE_RECEIVED:
+ /* We only retrieve the first byte received (addr)
+ * since there is currently no support to retrieve the data
+ * parameter from the client.
+ */
+ if (status->index == 0)
+ status->addr = *val;
+ if (status->index < U8_MAX)
+ status->index++;
+ break;
+ case I2C_SLAVE_STOP:
+ if (status->index == SMBUS_HOST_NOTIFY_LEN)
+ i2c_handle_smbus_host_notify(client->adapter,
+ status->addr);
+ fallthrough;
+ case I2C_SLAVE_WRITE_REQUESTED:
+ status->index = 0;
+ break;
+ case I2C_SLAVE_READ_REQUESTED:
+ case I2C_SLAVE_READ_PROCESSED:
+ *val = 0xff;
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * i2c_new_slave_host_notify_device - get a client for SMBus host-notify support
+ * @adapter: the target adapter
+ * Context: can sleep
+ *
+ * Setup handling of the SMBus host-notify protocol on a given I2C bus segment.
+ *
+ * Handling is done by creating a device and its callback and handling data
+ * received via the SMBus host-notify address (0x8)
+ *
+ * This returns the client, which should be ultimately freed using
+ * i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error.
+ */
+struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
+{
+ struct i2c_board_info host_notify_board_info = {
+ I2C_BOARD_INFO("smbus_host_notify", 0x08),
+ .flags = I2C_CLIENT_SLAVE,
+ };
+ struct i2c_slave_host_notify_status *status;
+ struct i2c_client *client;
+ int ret;
+
+ status = kzalloc(sizeof(struct i2c_slave_host_notify_status),
+ GFP_KERNEL);
+ if (!status)
+ return ERR_PTR(-ENOMEM);
+
+ host_notify_board_info.platform_data = status;
+
+ client = i2c_new_client_device(adapter, &host_notify_board_info);
+ if (IS_ERR(client)) {
+ kfree(status);
+ return client;
+ }
+
+ ret = i2c_slave_register(client, i2c_slave_host_notify_cb);
+ if (ret) {
+ i2c_unregister_device(client);
+ kfree(status);
+ return ERR_PTR(ret);
+ }
+
+ return client;
+}
+EXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device);
+
+/**
+ * i2c_free_slave_host_notify_device - free the client for SMBus host-notify
+ * support
+ * @client: the client to free
+ * Context: can sleep
+ *
+ * Free the i2c_client allocated via i2c_new_slave_host_notify_device
+ */
+void i2c_free_slave_host_notify_device(struct i2c_client *client)
+{
+ if (IS_ERR_OR_NULL(client))
+ return;
+
+ i2c_slave_unregister(client);
+ kfree(client->dev.platform_data);
+ i2c_unregister_device(client);
+}
+EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
+#endif
+
+/*
+ * SPD is not part of SMBus but we include it here for convenience as the
+ * target systems are the same.
+ * Restrictions to automatic SPD instantiation:
+ * - Only works if all filled slots have the same memory type
+ * - Only works for (LP)DDR memory types up to DDR5
+ * - Only works on systems with 1 to 8 memory slots
+ */
+#if IS_ENABLED(CONFIG_DMI)
+static void i2c_register_spd(struct i2c_adapter *adap, bool write_disabled)
+{
+ int n, slot_count = 0, dimm_count = 0;
+ u16 handle;
+ u8 common_mem_type = 0x0, mem_type;
+ u64 mem_size;
+ bool instantiate = true;
+ const char *name;
+
+ while ((handle = dmi_memdev_handle(slot_count)) != 0xffff) {
+ slot_count++;
+
+ /* Skip empty slots */
+ mem_size = dmi_memdev_size(handle);
+ if (!mem_size)
+ continue;
+
+ /* Skip undefined memory type */
+ mem_type = dmi_memdev_type(handle);
+ if (mem_type <= 0x02) /* Invalid, Other, Unknown */
+ continue;
+
+ if (!common_mem_type) {
+ /* First filled slot */
+ common_mem_type = mem_type;
+ } else {
+ /* Check that all filled slots have the same type */
+ if (mem_type != common_mem_type) {
+ dev_warn(&adap->dev,
+ "Different memory types mixed, not instantiating SPD\n");
+ return;
+ }
+ }
+ dimm_count++;
+ }
+
+ /* No useful DMI data, bail out */
+ if (!dimm_count)
+ return;
+
+ /*
+ * The max number of SPD EEPROMs that can be addressed per bus is 8.
+ * If more slots are present either muxed or multiple busses are
+ * necessary or the additional slots are ignored.
+ */
+ slot_count = min(slot_count, 8);
+
+ /*
+ * Memory types could be found at section 7.18.2 (Memory Device — Type), table 78
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf
+ */
+ switch (common_mem_type) {
+ case 0x12: /* DDR */
+ case 0x13: /* DDR2 */
+ case 0x18: /* DDR3 */
+ case 0x1B: /* LPDDR */
+ case 0x1C: /* LPDDR2 */
+ case 0x1D: /* LPDDR3 */
+ name = "spd";
+ break;
+ case 0x1A: /* DDR4 */
+ case 0x1E: /* LPDDR4 */
+ name = "ee1004";
+ break;
+ case 0x22: /* DDR5 */
+ case 0x23: /* LPDDR5 */
+ name = "spd5118";
+ instantiate = !write_disabled;
+ break;
+ default:
+ dev_info(&adap->dev,
+ "Memory type 0x%02x not supported yet, not instantiating SPD\n",
+ common_mem_type);
+ return;
+ }
+
+ /*
+ * We don't know in which slots the memory modules are. We could
+ * try to guess from the slot names, but that would be rather complex
+ * and unreliable, so better probe all possible addresses until we
+ * have found all memory modules.
+ */
+ for (n = 0; n < slot_count && dimm_count; n++) {
+ struct i2c_board_info info;
+ unsigned short addr_list[2];
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strscpy(info.type, name, I2C_NAME_SIZE);
+ addr_list[0] = 0x50 + n;
+ addr_list[1] = I2C_CLIENT_END;
+
+ if (!instantiate)
+ continue;
+
+ if (!IS_ERR(i2c_new_scanned_device(adap, &info, addr_list, NULL))) {
+ dev_info(&adap->dev,
+ "Successfully instantiated SPD at 0x%hx\n",
+ addr_list[0]);
+ dimm_count--;
+ }
+ }
+}
+
+void i2c_register_spd_write_disable(struct i2c_adapter *adap)
+{
+ i2c_register_spd(adap, true);
+}
+EXPORT_SYMBOL_GPL(i2c_register_spd_write_disable);
+
+void i2c_register_spd_write_enable(struct i2c_adapter *adap)
+{
+ i2c_register_spd(adap, false);
+}
+EXPORT_SYMBOL_GPL(i2c_register_spd_write_enable);
+
+#endif
+
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("SMBus protocol extensions support");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c
index f31ec0861979..09e7b7bf4c5f 100644
--- a/drivers/i2c/i2c-stub.c
+++ b/drivers/i2c/i2c-stub.c
@@ -1,21 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
i2c-stub.c - I2C/SMBus chip emulator
Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com>
Copyright (C) 2007-2014 Jean Delvare <jdelvare@suse.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
*/
-#define DEBUG 1
#define pr_fmt(fmt) "i2c-stub: " fmt
#include <linux/errno.h>
@@ -317,7 +308,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter stub_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "SMBus stub driver",
};
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 52a4a922e7e6..6d2f66810cdc 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Multiplexer I2C chip drivers configuration
#
@@ -8,7 +9,7 @@ menu "Multiplexer I2C Chip support"
config I2C_ARB_GPIO_CHALLENGE
tristate "GPIO-based I2C arbitration"
depends on GPIOLIB || COMPILE_TEST
- depends on OF || COMPILE_TEST
+ depends on OF
help
If you say yes to this option, support will be included for an
I2C multimaster arbitration scheme using GPIOs and a challenge &
@@ -33,7 +34,7 @@ config I2C_MUX_GPIO
config I2C_MUX_GPMUX
tristate "General Purpose I2C multiplexer"
select MULTIPLEXER
- depends on OF || COMPILE_TEST
+ depends on OF
help
If you say yes to this option, support will be included for a
general purpose I2C multiplexer. This driver provides access to
@@ -64,11 +65,11 @@ config I2C_MUX_PCA9541
will be called i2c-mux-pca9541.
config I2C_MUX_PCA954x
- tristate "NXP PCA954x and PCA984x I2C Mux/switches"
+ tristate "NXP PCA954x/PCA984x and Maxim MAX735x/MAX736x I2C Mux/switches"
depends on GPIOLIB || COMPILE_TEST
help
- If you say yes here you get support for the NXP PCA954x
- and PCA984x I2C mux/switch devices.
+ If you say yes here you get support for NXP PCA954x/PCA984x
+ and Maxim MAX735x/MAX736x I2C mux/switch devices.
This driver can also be built as a module. If so, the module
will be called i2c-mux-pca954x.
@@ -76,7 +77,7 @@ config I2C_MUX_PCA954x
config I2C_MUX_PINCTRL
tristate "pinctrl-based I2C multiplexer"
depends on PINCTRL
- depends on OF || COMPILE_TEST
+ depends on OF
help
If you say yes to this option, support will be included for an I2C
multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing.
@@ -108,14 +109,30 @@ config I2C_DEMUX_PINCTRL
want to change the I2C master at run-time depending on features.
config I2C_MUX_MLXCPLD
- tristate "Mellanox CPLD based I2C multiplexer"
- help
- If you say yes to this option, support will be included for a
- CPLD based I2C multiplexer. This driver provides access to
- I2C busses connected through a MUX, which is controlled
- by a CPLD register.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-mux-mlxcpld.
+ tristate "Mellanox CPLD based I2C multiplexer"
+ help
+ If you say yes to this option, support will be included for a
+ CPLD based I2C multiplexer. This driver provides access to
+ I2C busses connected through a MUX, which is controlled
+ by a CPLD register.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-mlxcpld.
+
+config I2C_MUX_MULE
+ tristate "Theobroma Systems Mule I2C device multiplexer"
+ depends on OF && SENSORS_AMC6821
+ help
+ Mule is an MCU that emulates a set of I2C devices, among which
+ devices that are reachable through an I2C-mux. The devices on the mux
+ can be selected by writing the appropriate device number to an I2C
+ configuration register.
+
+ If you say yes to this option, support will be included for a
+ Theobroma Systems Mule I2C multiplexer. This driver provides access to
+ I2C devices connected on this mux.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-mule.
endmenu
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 6d9d865e8518..4b24f49515a7 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o
obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o
obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
+obj-$(CONFIG_I2C_MUX_MULE) += i2c-mux-mule.o
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
index 812b8cff265f..d6ef91b888c6 100644
--- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
+++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
@@ -1,26 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* GPIO-based I2C Arbitration Using a Challenge & Response Mechanism
*
* Copyright (C) 2012 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -28,29 +18,23 @@
/**
* struct i2c_arbitrator_data - Driver data for I2C arbitrator
*
- * @our_gpio: GPIO we'll use to claim.
- * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
- * this then consider it released.
- * @their_gpio: GPIO that the other side will use to claim.
- * @their_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
- * this then consider it released.
+ * @our_gpio: GPIO descriptor we'll use to claim.
+ * @their_gpio: GPIO descriptor that the other side will use to claim.
* @slew_delay_us: microseconds to wait for a GPIO to go high.
* @wait_retry_us: we'll attempt another claim after this many microseconds.
* @wait_free_us: we'll give up after this many microseconds.
*/
struct i2c_arbitrator_data {
- int our_gpio;
- int our_gpio_release;
- int their_gpio;
- int their_gpio_release;
+ struct gpio_desc *our_gpio;
+ struct gpio_desc *their_gpio;
unsigned int slew_delay_us;
unsigned int wait_retry_us;
unsigned int wait_free_us;
};
-/**
+/*
* i2c_arbitrator_select - claim the I2C bus
*
* Use the GPIO-based signalling protocol; return -EBUSY if we fail.
@@ -64,15 +48,15 @@ static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan)
stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
do {
/* Indicate that we want to claim the bus */
- gpio_set_value(arb->our_gpio, !arb->our_gpio_release);
+ gpiod_set_value(arb->our_gpio, 1);
udelay(arb->slew_delay_us);
/* Wait for the other master to release it */
stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
while (time_before(jiffies, stop_retry)) {
- int gpio_val = !!gpio_get_value(arb->their_gpio);
+ int gpio_val = gpiod_get_value(arb->their_gpio);
- if (gpio_val == arb->their_gpio_release) {
+ if (!gpio_val) {
/* We got it, so return */
return 0;
}
@@ -81,19 +65,19 @@ static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan)
}
/* It didn't release, so give up, wait, and try again */
- gpio_set_value(arb->our_gpio, arb->our_gpio_release);
+ gpiod_set_value(arb->our_gpio, 0);
usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
} while (time_before(jiffies, stop_time));
/* Give up, release our claim */
- gpio_set_value(arb->our_gpio, arb->our_gpio_release);
+ gpiod_set_value(arb->our_gpio, 0);
udelay(arb->slew_delay_us);
dev_err(muxc->dev, "Could not claim bus, timeout\n");
return -EBUSY;
}
-/**
+/*
* i2c_arbitrator_deselect - release the I2C bus
*
* Release the I2C bus using the GPIO-based signalling protocol.
@@ -103,7 +87,7 @@ static int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan)
const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
/* Release the bus and wait for the other master to notice */
- gpio_set_value(arb->our_gpio, arb->our_gpio_release);
+ gpiod_set_value(arb->our_gpio, 0);
udelay(arb->slew_delay_us);
return 0;
@@ -116,8 +100,7 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
struct device_node *parent_np;
struct i2c_mux_core *muxc;
struct i2c_arbitrator_data *arb;
- enum of_gpio_flags gpio_flags;
- unsigned long out_init;
+ struct gpio_desc *dummy;
int ret;
/* We only support probing from device tree; no platform_data */
@@ -138,45 +121,28 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, muxc);
- /* Request GPIOs */
- ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags);
- if (!gpio_is_valid(ret)) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Error getting our-claim-gpio\n");
- return ret;
- }
- arb->our_gpio = ret;
- arb->our_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
- out_init = (gpio_flags & OF_GPIO_ACTIVE_LOW) ?
- GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
- ret = devm_gpio_request_one(dev, arb->our_gpio, out_init,
- "our-claim-gpio");
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Error requesting our-claim-gpio\n");
- return ret;
+ /* Request GPIOs, our GPIO as unclaimed to begin with */
+ arb->our_gpio = devm_gpiod_get(dev, "our-claim", GPIOD_OUT_LOW);
+ if (IS_ERR(arb->our_gpio)) {
+ dev_err(dev, "could not get \"our-claim\" GPIO (%ld)\n",
+ PTR_ERR(arb->our_gpio));
+ return PTR_ERR(arb->our_gpio);
}
- ret = of_get_named_gpio_flags(np, "their-claim-gpios", 0, &gpio_flags);
- if (!gpio_is_valid(ret)) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Error getting their-claim-gpio\n");
- return ret;
- }
- arb->their_gpio = ret;
- arb->their_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
- ret = devm_gpio_request_one(dev, arb->their_gpio, GPIOF_IN,
- "their-claim-gpio");
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Error requesting their-claim-gpio\n");
- return ret;
+ arb->their_gpio = devm_gpiod_get(dev, "their-claim", GPIOD_IN);
+ if (IS_ERR(arb->their_gpio)) {
+ dev_err(dev, "could not get \"their-claim\" GPIO (%ld)\n",
+ PTR_ERR(arb->their_gpio));
+ return PTR_ERR(arb->their_gpio);
}
/* At the moment we only support a single two master (us + 1 other) */
- if (gpio_is_valid(of_get_named_gpio(np, "their-claim-gpios", 1))) {
+ dummy = devm_gpiod_get_index(dev, "their-claim", 1, GPIOD_IN);
+ if (!IS_ERR(dummy)) {
dev_err(dev, "Only one other master is supported\n");
return -EINVAL;
+ } else if (PTR_ERR(dummy) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
}
/* Arbitration parameters */
@@ -201,20 +167,19 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
}
/* Actually add the mux adapter */
- ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
+ ret = i2c_mux_add_adapter(muxc, 0, 0);
if (ret)
i2c_put_adapter(muxc->parent);
return ret;
}
-static int i2c_arbitrator_remove(struct platform_device *pdev)
+static void i2c_arbitrator_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
- return 0;
}
static const struct of_device_id i2c_arbitrator_of_match[] = {
@@ -225,7 +190,7 @@ MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
static struct platform_driver i2c_arbitrator_driver = {
.probe = i2c_arbitrator_probe,
- .remove = i2c_arbitrator_remove,
+ .remove = i2c_arbitrator_remove,
.driver = {
.name = "i2c-arb-gpio-challenge",
.of_match_table = i2c_arbitrator_of_match,
diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c
index 035032e20327..f2a1f4744978 100644
--- a/drivers/i2c/muxes/i2c-demux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Pinctrl based I2C DeMultiplexer
*
* Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
* Copyright (C) 2015-16 by Renesas Electronics Corporation
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
- *
* See the bindings doc for DTS setup and the sysfs doc for usage information.
* (look for filenames containing 'i2c-demux-pinctrl' in Documentation/)
*/
@@ -35,7 +32,7 @@ struct i2c_demux_pinctrl_priv {
const char *bus_name;
struct i2c_adapter cur_adap;
struct i2c_algorithm algo;
- struct i2c_demux_pinctrl_chan chan[];
+ struct i2c_demux_pinctrl_chan chan[] __counted_by(num_chan);
};
static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
@@ -64,14 +61,14 @@ static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 ne
if (ret)
goto err;
- adap = of_find_i2c_adapter_by_node(priv->chan[new_chan].parent_np);
+ adap = of_get_i2c_adapter_by_node(priv->chan[new_chan].parent_np);
if (!adap) {
ret = -ENODEV;
goto err_with_revert;
}
/*
- * Check if there are pinctrl states at all. Note: we cant' use
+ * Check if there are pinctrl states at all. Note: we can't use
* devm_pinctrl_get_select() because we need to distinguish between
* the -ENODEV from devm_pinctrl_get() and pinctrl_lookup_state().
*/
@@ -98,7 +95,9 @@ static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 ne
priv->cur_chan = new_chan;
/* Now fill out current adapter structure. cur_chan must be up to date */
- priv->algo.master_xfer = i2c_demux_master_xfer;
+ priv->algo.xfer = i2c_demux_master_xfer;
+ if (adap->algo->master_xfer_atomic)
+ priv->algo.xfer_atomic = i2c_demux_master_xfer;
priv->algo.functionality = i2c_demux_functionality;
snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
@@ -168,9 +167,9 @@ static ssize_t available_masters_show(struct device *dev,
int count = 0, i;
for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++)
- count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%pOF%c",
- i, priv->chan[i].parent_np,
- i == priv->num_chan - 1 ? '\n' : ' ');
+ count += sysfs_emit_at(buf, count, "%d:%pOF%c",
+ i, priv->chan[i].parent_np,
+ i == priv->num_chan - 1 ? '\n' : ' ');
return count;
}
@@ -219,14 +218,16 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
return -EINVAL;
}
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv)
- + num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, struct_size(priv, chan, num_chan),
+ GFP_KERNEL);
props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL);
if (!priv || !props)
return -ENOMEM;
+ priv->num_chan = num_chan;
+
err = of_property_read_string(np, "i2c-bus-name", &priv->bus_name);
if (err)
return err;
@@ -244,25 +245,29 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
props[i].name = devm_kstrdup(&pdev->dev, "status", GFP_KERNEL);
props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL);
+ if (!props[i].name || !props[i].value) {
+ err = -ENOMEM;
+ goto err_rollback;
+ }
props[i].length = 3;
of_changeset_init(&priv->chan[i].chgset);
of_changeset_update_property(&priv->chan[i].chgset, adap_np, &props[i]);
}
- priv->num_chan = num_chan;
priv->dev = &pdev->dev;
-
platform_set_drvdata(pdev, priv);
pm_runtime_no_callbacks(&pdev->dev);
/* switch to first parent as active master */
- i2c_demux_activate_master(priv, 0);
+ err = i2c_demux_activate_master(priv, 0);
+ if (err)
+ goto err_rollback;
err = device_create_file(&pdev->dev, &dev_attr_available_masters);
if (err)
- goto err_rollback;
+ goto err_rollback_activation;
err = device_create_file(&pdev->dev, &dev_attr_current_master);
if (err)
@@ -272,6 +277,8 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
err_rollback_available:
device_remove_file(&pdev->dev, &dev_attr_available_masters);
+err_rollback_activation:
+ i2c_demux_deactivate_master(priv);
err_rollback:
for (j = 0; j < i; j++) {
of_node_put(priv->chan[j].parent_np);
@@ -281,7 +288,7 @@ err_rollback:
return err;
}
-static int i2c_demux_pinctrl_remove(struct platform_device *pdev)
+static void i2c_demux_pinctrl_remove(struct platform_device *pdev)
{
struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev);
int i;
@@ -295,8 +302,6 @@ static int i2c_demux_pinctrl_remove(struct platform_device *pdev)
of_node_put(priv->chan[i].parent_np);
of_changeset_destroy(&priv->chan[i].chgset);
}
-
- return 0;
}
static const struct of_device_id i2c_demux_pinctrl_of_match[] = {
@@ -311,7 +316,7 @@ static struct platform_driver i2c_demux_pinctrl_driver = {
.of_match_table = i2c_demux_pinctrl_of_match,
},
.probe = i2c_demux_pinctrl_probe,
- .remove = i2c_demux_pinctrl_remove,
+ .remove = i2c_demux_pinctrl_remove,
};
module_platform_driver(i2c_demux_pinctrl_driver);
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 13882a2a4f60..9b46b84e84fb 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -1,37 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C multiplexer using GPIO API
*
* Peter Korsgaard <peter.korsgaard@barco.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/platform_data/i2c-mux-gpio.h>
#include <linux/platform_device.h>
-#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include "../../gpio/gpiolib.h"
-#include <linux/of_gpio.h>
struct gpiomux {
struct i2c_mux_gpio_platform_data data;
- unsigned gpio_base;
+ int ngpios;
struct gpio_desc **gpios;
};
-static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
+static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned int val)
{
DECLARE_BITMAP(values, BITS_PER_TYPE(val));
values[0] = val;
- gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL,
- values);
+ gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values);
}
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
@@ -40,6 +38,9 @@ static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
i2c_mux_gpio_set(mux, chan);
+ if (mux->data.settle_time)
+ fsleep(mux->data.settle_time);
+
return 0;
}
@@ -52,89 +53,77 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
return 0;
}
-static int match_gpio_chip_by_label(struct gpio_chip *chip,
- void *data)
+static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
+ struct platform_device *pdev)
{
- return !strcmp(chip->label, data);
-}
+ struct device *dev = &pdev->dev;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct device_node *np = dev->of_node;
+ struct device_node *adapter_np;
+ struct i2c_adapter *adapter = NULL;
+ struct fwnode_handle *child;
+ unsigned int *values;
+ int rc, i = 0;
+
+ if (is_of_node(fwnode)) {
+ if (!np)
+ return -ENODEV;
+
+ adapter_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (!adapter_np) {
+ dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
+ return -ENODEV;
+ }
+ adapter = of_find_i2c_adapter_by_node(adapter_np);
+ of_node_put(adapter_np);
-#ifdef CONFIG_OF
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *adapter_np, *child;
- struct i2c_adapter *adapter;
- unsigned *values, *gpios;
- int i = 0, ret;
-
- if (!np)
- return -ENODEV;
-
- adapter_np = of_parse_phandle(np, "i2c-parent", 0);
- if (!adapter_np) {
- dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
- return -ENODEV;
+ } else if (is_acpi_node(fwnode)) {
+ /*
+ * In ACPI land the mux should be a direct child of the i2c
+ * bus it muxes.
+ */
+ acpi_handle dev_handle = ACPI_HANDLE(dev->parent);
+
+ adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
}
- adapter = of_find_i2c_adapter_by_node(adapter_np);
- of_node_put(adapter_np);
+
if (!adapter)
return -EPROBE_DEFER;
mux->data.parent = i2c_adapter_id(adapter);
put_device(&adapter->dev);
- mux->data.n_values = of_get_child_count(np);
-
- values = devm_kcalloc(&pdev->dev,
+ mux->data.n_values = device_get_child_node_count(dev);
+ values = devm_kcalloc(dev,
mux->data.n_values, sizeof(*mux->data.values),
GFP_KERNEL);
if (!values) {
- dev_err(&pdev->dev, "Cannot allocate values array");
+ dev_err(dev, "Cannot allocate values array");
return -ENOMEM;
}
- for_each_child_of_node(np, child) {
- of_property_read_u32(child, "reg", values + i);
+ device_for_each_child_node(dev, child) {
+ if (is_of_node(child)) {
+ fwnode_property_read_u32(child, "reg", values + i);
+ } else if (is_acpi_node(child)) {
+ rc = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), values + i);
+ if (rc) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, rc, "Cannot get address\n");
+ }
+ }
+
i++;
}
mux->data.values = values;
- if (of_property_read_u32(np, "idle-state", &mux->data.idle))
+ if (device_property_read_u32(dev, "idle-state", &mux->data.idle))
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
- mux->data.n_gpios = of_gpio_named_count(np, "mux-gpios");
- if (mux->data.n_gpios < 0) {
- dev_err(&pdev->dev, "Missing mux-gpios property in the DT.\n");
- return -EINVAL;
- }
-
- gpios = devm_kcalloc(&pdev->dev,
- mux->data.n_gpios, sizeof(*mux->data.gpios),
- GFP_KERNEL);
- if (!gpios) {
- dev_err(&pdev->dev, "Cannot allocate gpios array");
- return -ENOMEM;
- }
-
- for (i = 0; i < mux->data.n_gpios; i++) {
- ret = of_get_named_gpio(np, "mux-gpios", i);
- if (ret < 0)
- return ret;
- gpios[i] = ret;
- }
-
- mux->data.gpios = gpios;
+ device_property_read_u32(dev, "settle-time-us", &mux->data.settle_time);
return 0;
}
-#else
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
-{
- return 0;
-}
-#endif
static int i2c_mux_gpio_probe(struct platform_device *pdev)
{
@@ -142,15 +131,15 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
struct gpiomux *mux;
struct i2c_adapter *parent;
struct i2c_adapter *root;
- unsigned initial_state, gpio_base;
- int i, ret;
+ unsigned int initial_state;
+ int i, ngpios, ret;
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
if (!dev_get_platdata(&pdev->dev)) {
- ret = i2c_mux_gpio_probe_dt(mux, pdev);
+ ret = i2c_mux_gpio_probe_fw(mux, pdev);
if (ret < 0)
return ret;
} else {
@@ -158,29 +147,19 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
sizeof(mux->data));
}
- /*
- * If a GPIO chip name is provided, the GPIO pin numbers provided are
- * relative to its base GPIO number. Otherwise they are absolute.
- */
- if (mux->data.gpio_chip) {
- struct gpio_chip *gpio;
-
- gpio = gpiochip_find(mux->data.gpio_chip,
- match_gpio_chip_by_label);
- if (!gpio)
- return -EPROBE_DEFER;
-
- gpio_base = gpio->base;
- } else {
- gpio_base = 0;
+ ngpios = gpiod_count(&pdev->dev, "mux");
+ if (ngpios <= 0) {
+ dev_err(&pdev->dev, "no valid gpios provided\n");
+ return ngpios ?: -EINVAL;
}
+ mux->ngpios = ngpios;
parent = i2c_get_adapter(mux->data.parent);
if (!parent)
return -EPROBE_DEFER;
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios), 0,
+ array_size(ngpios, sizeof(*mux->gpios)), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
@@ -194,7 +173,6 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
root = i2c_root_adapter(&parent->dev);
muxc->mux_locked = true;
- mux->gpio_base = gpio_base;
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
initial_state = mux->data.idle;
@@ -203,35 +181,30 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
initial_state = mux->data.values[0];
}
- for (i = 0; i < mux->data.n_gpios; i++) {
- struct device *gpio_dev;
- struct gpio_desc *gpio_desc;
-
- ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio");
- if (ret) {
- dev_err(&pdev->dev, "Failed to request GPIO %d\n",
- mux->data.gpios[i]);
- goto err_request_gpio;
- }
-
- ret = gpio_direction_output(gpio_base + mux->data.gpios[i],
- initial_state & (1 << i));
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to set direction of GPIO %d to output\n",
- mux->data.gpios[i]);
- i++; /* gpio_request above succeeded, so must free */
- goto err_request_gpio;
+ for (i = 0; i < ngpios; i++) {
+ struct gpio_device *gdev;
+ struct device *dev;
+ struct gpio_desc *gpiod;
+ enum gpiod_flags flag;
+
+ if (initial_state & BIT(i))
+ flag = GPIOD_OUT_HIGH;
+ else
+ flag = GPIOD_OUT_LOW;
+ gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag);
+ if (IS_ERR(gpiod)) {
+ ret = PTR_ERR(gpiod);
+ goto alloc_failed;
}
- gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]);
- mux->gpios[i] = gpio_desc;
+ mux->gpios[i] = gpiod;
if (!muxc->mux_locked)
continue;
- gpio_dev = &gpio_desc->gdev->dev;
- muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
+ gdev = gpiod_to_gpio_device(gpiod);
+ dev = gpio_device_to_device(gdev);
+ muxc->mux_locked = i2c_root_adapter(dev) == root;
}
if (muxc->mux_locked)
@@ -239,9 +212,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
for (i = 0; i < mux->data.n_values; i++) {
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
- unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
- ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
+ ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i]);
if (ret)
goto add_adapter_failed;
}
@@ -253,30 +225,18 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
add_adapter_failed:
i2c_mux_del_adapters(muxc);
- i = mux->data.n_gpios;
-err_request_gpio:
- for (; i > 0; i--)
- gpio_free(gpio_base + mux->data.gpios[i - 1]);
alloc_failed:
i2c_put_adapter(parent);
return ret;
}
-static int i2c_mux_gpio_remove(struct platform_device *pdev)
+static void i2c_mux_gpio_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
- struct gpiomux *mux = i2c_mux_priv(muxc);
- int i;
i2c_mux_del_adapters(muxc);
-
- for (i = 0; i < mux->data.n_gpios; i++)
- gpio_free(mux->gpio_base + mux->data.gpios[i]);
-
i2c_put_adapter(muxc->parent);
-
- return 0;
}
static const struct of_device_id i2c_mux_gpio_of_match[] = {
@@ -287,7 +247,7 @@ MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match);
static struct platform_driver i2c_mux_gpio_driver = {
.probe = i2c_mux_gpio_probe,
- .remove = i2c_mux_gpio_remove,
+ .remove = i2c_mux_gpio_remove,
.driver = {
.name = "i2c-mux-gpio",
.of_match_table = i2c_mux_gpio_of_match,
diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c
index f60b670deff7..ab8e11661052 100644
--- a/drivers/i2c/muxes/i2c-mux-gpmux.c
+++ b/drivers/i2c/muxes/i2c-mux-gpmux.c
@@ -1,20 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* General Purpose I2C multiplexer
*
* Copyright (C) 2017 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
struct mux {
@@ -55,7 +52,7 @@ static struct i2c_adapter *mux_parent_adapter(struct device *dev)
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
- parent = of_find_i2c_adapter_by_node(parent_np);
+ parent = of_get_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
@@ -88,18 +85,14 @@ static int i2c_mux_probe(struct platform_device *pdev)
return -ENOMEM;
mux->control = devm_mux_control_get(dev, NULL);
- if (IS_ERR(mux->control)) {
- if (PTR_ERR(mux->control) != -EPROBE_DEFER)
- dev_err(dev, "failed to get control-mux\n");
- return PTR_ERR(mux->control);
- }
+ if (IS_ERR(mux->control))
+ return dev_err_probe(dev, PTR_ERR(mux->control),
+ "failed to get control-mux\n");
parent = mux_parent_adapter(dev);
- if (IS_ERR(parent)) {
- if (PTR_ERR(parent) != -EPROBE_DEFER)
- dev_err(dev, "failed to get i2c-parent adapter\n");
- return PTR_ERR(parent);
- }
+ if (IS_ERR(parent))
+ return dev_err_probe(dev, PTR_ERR(parent),
+ "failed to get i2c-parent adapter\n");
children = of_get_child_count(np);
@@ -131,7 +124,7 @@ static int i2c_mux_probe(struct platform_device *pdev)
goto err_children;
}
- ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
+ ret = i2c_mux_add_adapter(muxc, 0, chan);
if (ret)
goto err_children;
}
@@ -141,6 +134,7 @@ static int i2c_mux_probe(struct platform_device *pdev)
return 0;
err_children:
+ of_node_put(child);
i2c_mux_del_adapters(muxc);
err_parent:
i2c_put_adapter(parent);
@@ -148,19 +142,17 @@ err_parent:
return ret;
}
-static int i2c_mux_remove(struct platform_device *pdev)
+static void i2c_mux_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
-
- return 0;
}
static struct platform_driver i2c_mux_driver = {
.probe = i2c_mux_probe,
- .remove = i2c_mux_remove,
+ .remove = i2c_mux_remove,
.driver = {
.name = "i2c-mux-gpmux",
.of_match_table = i2c_mux_of_match,
diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c
index 83a714605cd6..50fbc0d06e62 100644
--- a/drivers/i2c/muxes/i2c-mux-ltc4306.c
+++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch
*
* Copyright (C) 2017 Analog Devices Inc.
*
- * Licensed under the GPL-2.
- *
* Based on: i2c-mux-pca954x.c
*
* Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf
@@ -16,7 +15,6 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -63,7 +61,7 @@ static const struct chip_desc chips[] = {
static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg)
{
- return (reg == LTC_REG_CONFIG) ? true : false;
+ return reg == LTC_REG_CONFIG;
}
static const struct regmap_config ltc4306_regmap_config = {
@@ -87,13 +85,13 @@ static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(val & BIT(1 - offset));
}
-static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
+static int ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct ltc4306 *data = gpiochip_get_data(chip);
- regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset),
- value ? BIT(5 - offset) : 0);
+ return regmap_update_bits(data->regmap, LTC_REG_CONFIG,
+ BIT(5 - offset), value ? BIT(5 - offset) : 0);
}
static int ltc4306_gpio_get_direction(struct gpio_chip *chip,
@@ -281,7 +279,7 @@ static int ltc4306_probe(struct i2c_client *client)
/* Now create an adapter for each channel */
for (num = 0; num < chip->nchans; num++) {
- ret = i2c_mux_add_adapter(muxc, 0, num, 0);
+ ret = i2c_mux_add_adapter(muxc, 0, num);
if (ret) {
i2c_mux_del_adapters(muxc);
return ret;
@@ -295,21 +293,19 @@ static int ltc4306_probe(struct i2c_client *client)
return 0;
}
-static int ltc4306_remove(struct i2c_client *client)
+static void ltc4306_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
i2c_mux_del_adapters(muxc);
-
- return 0;
}
static struct i2c_driver ltc4306_driver = {
.driver = {
.name = "ltc4306",
- .of_match_table = of_match_ptr(ltc4306_of_match),
+ .of_match_table = ltc4306_of_match,
},
- .probe_new = ltc4306_probe,
+ .probe = ltc4306_probe,
.remove = ltc4306_remove,
.id_table = ltc4306_id,
};
diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
index 5ed55ca4fe93..1c2debcf379c 100644
--- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c
+++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
@@ -1,35 +1,8 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*
- * drivers/i2c/muxes/i2c-mux-mlxcpld.c
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ * Mellanox i2c mux driver
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * Copyright (C) 2016-2020 Mellanox Technologies
*/
#include <linux/device.h>
@@ -38,19 +11,19 @@
#include <linux/io.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/platform_data/x86/mlxcpld.h>
+#include <linux/platform_data/mlxcpld.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#define CPLD_MUX_MAX_NCHANS 8
-
/* mlxcpld_mux - mux control structure:
- * @last_chan - last register value
+ * @last_val - last selected register value or -1 if mux deselected
* @client - I2C device client
+ * @pdata: platform data
*/
struct mlxcpld_mux {
- u8 last_chan;
+ int last_val;
struct i2c_client *client;
+ struct mlxcpld_mux_plat_data pdata;
};
/* MUX logic description.
@@ -81,37 +54,50 @@ struct mlxcpld_mux {
*
*/
-static const struct i2c_device_id mlxcpld_mux_id[] = {
- { "mlxcpld_mux_module", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id);
-
/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
* for this as they will try to lock adapter a second time.
*/
static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
- struct i2c_client *client, u8 val)
+ struct mlxcpld_mux *mux, u32 val)
{
- struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
- union i2c_smbus_data data = { .byte = val };
-
- return __i2c_smbus_xfer(adap, client->addr, client->flags,
- I2C_SMBUS_WRITE, pdata->sel_reg_addr,
- I2C_SMBUS_BYTE_DATA, &data);
+ struct i2c_client *client = mux->client;
+ union i2c_smbus_data data;
+ struct i2c_msg msg;
+ u8 buf[3];
+
+ switch (mux->pdata.reg_size) {
+ case 1:
+ data.byte = val;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, mux->pdata.sel_reg_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
+ case 2:
+ buf[0] = mux->pdata.sel_reg_addr >> 8;
+ buf[1] = mux->pdata.sel_reg_addr;
+ buf[2] = val;
+ msg.addr = client->addr;
+ msg.buf = buf;
+ msg.len = mux->pdata.reg_size + 1;
+ msg.flags = 0;
+ return __i2c_transfer(adap, &msg, 1);
+ default:
+ return -EINVAL;
+ }
}
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
- struct mlxcpld_mux *data = i2c_mux_priv(muxc);
- struct i2c_client *client = data->client;
- u8 regval = chan + 1;
+ struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
+ u32 regval = chan;
int err = 0;
+ if (mux->pdata.reg_size == 1)
+ regval += 1;
+
/* Only select the channel if its different from the last channel */
- if (data->last_chan != regval) {
- err = mlxcpld_mux_reg_write(muxc->parent, client, regval);
- data->last_chan = err < 0 ? 0 : regval;
+ if (mux->last_val != regval) {
+ err = mlxcpld_mux_reg_write(muxc->parent, mux, regval);
+ mux->last_val = err < 0 ? -1 : regval;
}
return err;
@@ -119,56 +105,64 @@ static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
{
- struct mlxcpld_mux *data = i2c_mux_priv(muxc);
- struct i2c_client *client = data->client;
+ struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
/* Deselect active channel */
- data->last_chan = 0;
+ mux->last_val = -1;
- return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan);
+ return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
}
/* Probe/reomove functions */
-static int mlxcpld_mux_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int mlxcpld_mux_probe(struct platform_device *pdev)
{
- struct i2c_adapter *adap = client->adapter;
- struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
+ struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct i2c_client *client = to_i2c_client(pdev->dev.parent);
struct i2c_mux_core *muxc;
- int num, force;
struct mlxcpld_mux *data;
- int err;
+ int num, err;
+ u32 func;
if (!pdata)
return -EINVAL;
- if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ switch (pdata->reg_size) {
+ case 1:
+ func = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+ break;
+ case 2:
+ func = I2C_FUNC_I2C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, func))
return -ENODEV;
- muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS,
+ muxc = i2c_mux_alloc(client->adapter, &pdev->dev, pdata->num_adaps,
sizeof(*data), 0, mlxcpld_mux_select_chan,
mlxcpld_mux_deselect);
if (!muxc)
return -ENOMEM;
+ platform_set_drvdata(pdev, muxc);
data = i2c_mux_priv(muxc);
- i2c_set_clientdata(client, muxc);
data->client = client;
- data->last_chan = 0; /* force the first selection */
+ memcpy(&data->pdata, pdata, sizeof(*pdata));
+ data->last_val = -1; /* force the first selection */
/* Create an adapter for each channel. */
- for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) {
- if (num >= pdata->num_adaps)
- /* discard unconfigured channels */
- break;
-
- force = pdata->adap_ids[num];
-
- err = i2c_mux_add_adapter(muxc, force, num, 0);
+ for (num = 0; num < pdata->num_adaps; num++) {
+ err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num]);
if (err)
goto virt_reg_failed;
}
+ /* Notify caller when all channels' adapters are created. */
+ if (pdata->completion_notify)
+ pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
+
return 0;
virt_reg_failed:
@@ -176,26 +170,24 @@ virt_reg_failed:
return err;
}
-static int mlxcpld_mux_remove(struct i2c_client *client)
+static void mlxcpld_mux_remove(struct platform_device *pdev)
{
- struct i2c_mux_core *muxc = i2c_get_clientdata(client);
+ struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
- return 0;
}
-static struct i2c_driver mlxcpld_mux_driver = {
- .driver = {
- .name = "mlxcpld-mux",
+static struct platform_driver mlxcpld_mux_driver = {
+ .driver = {
+ .name = "i2c-mux-mlxcpld",
},
- .probe = mlxcpld_mux_probe,
- .remove = mlxcpld_mux_remove,
- .id_table = mlxcpld_mux_id,
+ .probe = mlxcpld_mux_probe,
+ .remove = mlxcpld_mux_remove,
};
-module_i2c_driver(mlxcpld_mux_driver);
+module_platform_driver(mlxcpld_mux_driver);
-MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
+MODULE_AUTHOR("Michael Shych <michaels@mellanox.com>");
MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:i2c-mux-mlxcpld");
diff --git a/drivers/i2c/muxes/i2c-mux-mule.c b/drivers/i2c/muxes/i2c-mux-mule.c
new file mode 100644
index 000000000000..d3b32b794172
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-mule.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Theobroma Systems Mule I2C device multiplexer
+ *
+ * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <linux/i2c-mux.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#define MULE_I2C_MUX_CONFIG_REG 0xff
+#define MULE_I2C_MUX_DEFAULT_DEV 0x0
+
+struct mule_i2c_reg_mux {
+ struct regmap *regmap;
+};
+
+static int mule_i2c_mux_select(struct i2c_mux_core *muxc, u32 dev)
+{
+ struct mule_i2c_reg_mux *mux = muxc->priv;
+
+ return regmap_write(mux->regmap, MULE_I2C_MUX_CONFIG_REG, dev);
+}
+
+static int mule_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 dev)
+{
+ return mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
+}
+
+static void mule_i2c_mux_remove(void *data)
+{
+ struct i2c_mux_core *muxc = data;
+
+ i2c_mux_del_adapters(muxc);
+
+ mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
+}
+
+static int mule_i2c_mux_probe(struct platform_device *pdev)
+{
+ struct device *mux_dev = &pdev->dev;
+ struct mule_i2c_reg_mux *priv;
+ struct i2c_client *client;
+ struct i2c_mux_core *muxc;
+ unsigned int readback;
+ int ndev, ret;
+ bool old_fw;
+
+ /* Count devices on the mux */
+ ndev = of_get_child_count(mux_dev->of_node);
+ dev_dbg(mux_dev, "%d devices on the mux\n", ndev);
+
+ client = to_i2c_client(mux_dev->parent);
+
+ muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv),
+ I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect);
+ if (!muxc)
+ return -ENOMEM;
+
+ priv = i2c_mux_priv(muxc);
+
+ priv->regmap = dev_get_regmap(mux_dev->parent, NULL);
+ if (!priv->regmap)
+ return dev_err_probe(mux_dev, -ENODEV,
+ "No parent i2c register map\n");
+
+ platform_set_drvdata(pdev, muxc);
+
+ /*
+ * MULE_I2C_MUX_DEFAULT_DEV is guaranteed to exist on all old and new
+ * mule fw. Mule fw without mux support will accept write ops to the
+ * config register, but readback returns 0xff (register not updated).
+ */
+ ret = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
+ if (ret)
+ return dev_err_probe(mux_dev, ret,
+ "Failed to write config register\n");
+
+ ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback);
+ if (ret)
+ return dev_err_probe(mux_dev, ret,
+ "Failed to read config register\n");
+
+ old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV);
+
+ ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc);
+ if (ret)
+ return dev_err_probe(mux_dev, ret,
+ "Failed to register mux remove\n");
+
+ /* Create device adapters */
+ for_each_child_of_node_scoped(mux_dev->of_node, dev) {
+ u32 reg;
+
+ ret = of_property_read_u32(dev, "reg", &reg);
+ if (ret)
+ return dev_err_probe(mux_dev, ret,
+ "No reg property found for %s\n",
+ of_node_full_name(dev));
+
+ if (old_fw && reg != 0) {
+ dev_warn(mux_dev,
+ "Mux is not supported, please update Mule FW\n");
+ continue;
+ }
+
+ ret = mule_i2c_mux_select(muxc, reg);
+ if (ret) {
+ dev_warn(mux_dev,
+ "Device %d not supported, please update Mule FW\n", reg);
+ continue;
+ }
+
+ ret = i2c_mux_add_adapter(muxc, 0, reg);
+ if (ret)
+ return ret;
+ }
+
+ mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
+
+ return 0;
+}
+
+static const struct of_device_id mule_i2c_mux_of_match[] = {
+ { .compatible = "tsd,mule-i2c-mux", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match);
+
+static struct platform_driver mule_i2c_mux_driver = {
+ .driver = {
+ .name = "mule-i2c-mux",
+ .of_match_table = mule_i2c_mux_of_match,
+ },
+ .probe = mule_i2c_mux_probe,
+};
+
+module_platform_driver(mule_i2c_mux_driver);
+
+MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
+MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
index 9e75d6b9140b..3d8002caf703 100644
--- a/drivers/i2c/muxes/i2c-mux-pca9541.c
+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
@@ -16,13 +16,13 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/jiffies.h>
#include <linux/module.h>
-#include <linux/platform_data/pca954x.h>
#include <linux/slab.h>
/*
@@ -43,30 +43,26 @@
#define PCA9541_CONTROL 0x01
#define PCA9541_ISTAT 0x02
-#define PCA9541_CTL_MYBUS (1 << 0)
-#define PCA9541_CTL_NMYBUS (1 << 1)
-#define PCA9541_CTL_BUSON (1 << 2)
-#define PCA9541_CTL_NBUSON (1 << 3)
-#define PCA9541_CTL_BUSINIT (1 << 4)
-#define PCA9541_CTL_TESTON (1 << 6)
-#define PCA9541_CTL_NTESTON (1 << 7)
-
-#define PCA9541_ISTAT_INTIN (1 << 0)
-#define PCA9541_ISTAT_BUSINIT (1 << 1)
-#define PCA9541_ISTAT_BUSOK (1 << 2)
-#define PCA9541_ISTAT_BUSLOST (1 << 3)
-#define PCA9541_ISTAT_MYTEST (1 << 6)
-#define PCA9541_ISTAT_NMYTEST (1 << 7)
+#define PCA9541_CTL_MYBUS BIT(0)
+#define PCA9541_CTL_NMYBUS BIT(1)
+#define PCA9541_CTL_BUSON BIT(2)
+#define PCA9541_CTL_NBUSON BIT(3)
+#define PCA9541_CTL_BUSINIT BIT(4)
+#define PCA9541_CTL_TESTON BIT(6)
+#define PCA9541_CTL_NTESTON BIT(7)
+
+#define PCA9541_ISTAT_INTIN BIT(0)
+#define PCA9541_ISTAT_BUSINIT BIT(1)
+#define PCA9541_ISTAT_BUSOK BIT(2)
+#define PCA9541_ISTAT_BUSLOST BIT(3)
+#define PCA9541_ISTAT_MYTEST BIT(6)
+#define PCA9541_ISTAT_NMYTEST BIT(7)
#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON)
#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS)
#define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS)
#define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON)
-/* arbitration timeouts, in jiffies */
-#define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */
-#define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure */
-
/* arbitration retry delays, in us */
#define SELECT_DELAY_SHORT 50
#define SELECT_DELAY_LONG 1000
@@ -78,7 +74,7 @@ struct pca9541 {
};
static const struct i2c_device_id pca9541_id[] = {
- {"pca9541", 0},
+ { "pca9541" },
{}
};
@@ -229,6 +225,9 @@ static int pca9541_arbitrate(struct i2c_client *client)
*/
data->select_timeout = SELECT_DELAY_LONG;
if (time_is_before_eq_jiffies(data->arb_timeout)) {
+ dev_warn(&client->dev,
+ "Arbitration timeout on I2C bus, forcing bus ownership\n");
+
/* Time is up, take the bus and reset it. */
pca9541_reg_write(client,
PCA9541_CONTROL,
@@ -251,10 +250,10 @@ static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
struct pca9541 *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int ret;
- unsigned long timeout = jiffies + ARB2_TIMEOUT;
+ unsigned long timeout = jiffies + (2 * client->adapter->timeout);
/* give up after this time */
- data->arb_timeout = jiffies + ARB_TIMEOUT;
+ data->arb_timeout = jiffies + client->adapter->timeout;
/* force bus ownership after this time */
do {
@@ -267,6 +266,7 @@ static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
else
msleep(data->select_timeout / 1000);
} while (time_is_after_eq_jiffies(timeout));
+ dev_warn(&client->dev, "Failed to acquire I2C bus, timed out\n");
return -ETIMEDOUT;
}
@@ -283,14 +283,11 @@ static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan)
/*
* I2C init/probing/exit functions
*/
-static int pca9541_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pca9541_probe(struct i2c_client *client)
{
struct i2c_adapter *adap = client->adapter;
- struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct i2c_mux_core *muxc;
struct pca9541 *data;
- int force;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -306,9 +303,6 @@ static int pca9541_probe(struct i2c_client *client,
/* Create mux adapter */
- force = 0;
- if (pdata)
- force = pdata->modes[0].adap_id;
muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data),
I2C_MUX_ARBITRATOR,
pca9541_select_chan, pca9541_release_chan);
@@ -320,7 +314,7 @@ static int pca9541_probe(struct i2c_client *client,
i2c_set_clientdata(client, muxc);
- ret = i2c_mux_add_adapter(muxc, force, 0, 0);
+ ret = i2c_mux_add_adapter(muxc, 0, 0);
if (ret)
return ret;
@@ -330,12 +324,11 @@ static int pca9541_probe(struct i2c_client *client,
return 0;
}
-static int pca9541_remove(struct i2c_client *client)
+static void pca9541_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
i2c_mux_del_adapters(muxc);
- return 0;
}
static struct i2c_driver pca9541_driver = {
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index bfabf985e830..b9f370c9f018 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -1,15 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* I2C multiplexer
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
*
- * This module supports the PCA954x and PCA954x series of I2C multiplexer/switch
+ * This module supports the PCA954x and PCA984x series of I2C multiplexer/switch
* chips made by NXP Semiconductors.
* This includes the:
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
* PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
+ * It's also compatible to Maxims MAX735x I2C switch chips, which are controlled
+ * as the NXP PCA9548 and the MAX736x chips that act like the PCA9544.
+ *
+ * This includes the:
+ * MAX7356, MAX7357, MAX7358, MAX7367, MAX7368 and MAX7369
+ *
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
* four, or eight downstream busses or channels; which of these
@@ -29,10 +36,6 @@
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/device.h>
@@ -43,19 +46,39 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/platform_data/pca954x.h>
#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <dt-bindings/mux/mux.h>
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
+/*
+ * MAX7357's configuration register is writeable after POR, but
+ * can be locked by setting the basic mode bit. MAX7358 configuration
+ * register is locked by default and needs to be unlocked first.
+ * The configuration register holds the following settings:
+ */
+#define MAX7357_CONF_INT_ENABLE BIT(0)
+#define MAX7357_CONF_FLUSH_OUT BIT(1)
+#define MAX7357_CONF_RELEASE_INT BIT(2)
+#define MAX7357_CONF_DISCON_SINGLE_CHAN BIT(4)
+#define MAX7357_CONF_PRECONNECT_TEST BIT(7)
+
+#define MAX7357_POR_DEFAULT_CONF MAX7357_CONF_INT_ENABLE
+
enum pca_type {
+ max_7356,
+ max_7357,
+ max_7358,
+ max_7367,
+ max_7368,
+ max_7369,
pca_9540,
pca_9542,
pca_9543,
@@ -85,16 +108,63 @@ struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
- u8 deselect;
+ /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
+ s32 idle_state;
+
struct i2c_client *client;
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
+ struct regulator *supply;
+
+ struct gpio_desc *reset_gpio;
+ struct reset_control *reset_cont;
};
-/* Provide specs for the PCA954x types we know about */
+/* Provide specs for the MAX735x, PCA954x and PCA984x types we know about */
static const struct chip_desc chips[] = {
+ [max_7356] = {
+ .nchans = 8,
+ .muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ },
+ [max_7357] = {
+ .nchans = 8,
+ .muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ /*
+ * No interrupt controller support. The interrupt
+ * provides information about stuck channels.
+ */
+ },
+ [max_7358] = {
+ .nchans = 8,
+ .muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ /*
+ * No interrupt controller support. The interrupt
+ * provides information about stuck channels.
+ */
+ },
+ [max_7367] = {
+ .nchans = 4,
+ .muxtype = pca954x_isswi,
+ .has_irq = 1,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ },
+ [max_7368] = {
+ .nchans = 4,
+ .muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ },
+ [max_7369] = {
+ .nchans = 4,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ .has_irq = 1,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ },
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
@@ -180,6 +250,12 @@ static const struct chip_desc chips[] = {
};
static const struct i2c_device_id pca954x_id[] = {
+ { "max7356", max_7356 },
+ { "max7357", max_7357 },
+ { "max7358", max_7358 },
+ { "max7367", max_7367 },
+ { "max7368", max_7368 },
+ { "max7369", max_7369 },
{ "pca9540", pca_9540 },
{ "pca9542", pca_9542 },
{ "pca9543", pca_9543 },
@@ -196,8 +272,13 @@ static const struct i2c_device_id pca954x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
-#ifdef CONFIG_OF
static const struct of_device_id pca954x_of_match[] = {
+ { .compatible = "maxim,max7356", .data = &chips[max_7356] },
+ { .compatible = "maxim,max7357", .data = &chips[max_7357] },
+ { .compatible = "maxim,max7358", .data = &chips[max_7358] },
+ { .compatible = "maxim,max7367", .data = &chips[max_7367] },
+ { .compatible = "maxim,max7368", .data = &chips[max_7368] },
+ { .compatible = "maxim,max7369", .data = &chips[max_7369] },
{ .compatible = "nxp,pca9540", .data = &chips[pca_9540] },
{ .compatible = "nxp,pca9542", .data = &chips[pca_9542] },
{ .compatible = "nxp,pca9543", .data = &chips[pca_9543] },
@@ -213,7 +294,6 @@ static const struct of_device_id pca954x_of_match[] = {
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
-#endif
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
@@ -227,20 +307,23 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
I2C_SMBUS_BYTE, &dummy);
}
+static u8 pca954x_regval(struct pca954x *data, u8 chan)
+{
+ /* We make switches look like muxes, not sure how to be smarter. */
+ if (data->chip->muxtype == pca954x_ismux)
+ return chan | data->chip->enable;
+ else
+ return 1 << chan;
+}
+
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
- const struct chip_desc *chip = data->chip;
u8 regval;
int ret = 0;
- /* we make switches look like muxes, not sure how to be smarter */
- if (chip->muxtype == pca954x_ismux)
- regval = chan | chip->enable;
- else
- regval = 1 << chan;
-
+ regval = pca954x_regval(data, chan);
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
@@ -254,33 +337,86 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
+ s32 idle_state;
+
+ idle_state = READ_ONCE(data->idle_state);
+ if (idle_state >= 0)
+ /* Set the mux back to a predetermined channel */
+ return pca954x_select_chan(muxc, idle_state);
+
+ if (idle_state == MUX_IDLE_DISCONNECT) {
+ /* Deselect active channel */
+ data->last_chan = 0;
+ return pca954x_reg_write(muxc->parent, client,
+ data->last_chan);
+ }
- if (!(data->deselect & (1 << chan)))
- return 0;
+ /* otherwise leave as-is */
- /* Deselect active channel */
- data->last_chan = 0;
- return pca954x_reg_write(muxc->parent, client, data->last_chan);
+ return 0;
}
+static ssize_t idle_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_mux_core *muxc = i2c_get_clientdata(client);
+ struct pca954x *data = i2c_mux_priv(muxc);
+
+ return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
+}
+
+static ssize_t idle_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_mux_core *muxc = i2c_get_clientdata(client);
+ struct pca954x *data = i2c_mux_priv(muxc);
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
+ (val < 0 || val >= data->chip->nchans))
+ return -EINVAL;
+
+ i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
+
+ WRITE_ONCE(data->idle_state, val);
+ /*
+ * Set the mux into a state consistent with the new
+ * idle_state.
+ */
+ if (data->last_chan || val != MUX_IDLE_DISCONNECT)
+ ret = pca954x_deselect_mux(muxc, 0);
+
+ i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
+
+ return ret < 0 ? ret : count;
+}
+
+static DEVICE_ATTR_RW(idle_state);
+
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{
struct pca954x *data = dev_id;
- unsigned int child_irq;
- int ret, i, handled = 0;
+ unsigned long pending;
+ int ret, i;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return IRQ_NONE;
- for (i = 0; i < data->chip->nchans; i++) {
- if (ret & BIT(PCA954X_IRQ_OFFSET + i)) {
- child_irq = irq_linear_revmap(data->irq, i);
- handle_nested_irq(child_irq);
- handled++;
- }
- }
- return handled ? IRQ_HANDLED : IRQ_NONE;
+ pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1);
+ for_each_set_bit(i, &pending, data->chip->nchans)
+ handle_nested_irq(irq_find_mapping(data->irq, i));
+
+ return IRQ_RETVAL(pending);
}
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
@@ -306,9 +442,8 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
raw_spin_lock_init(&data->lock);
- data->irq = irq_domain_add_linear(client->dev.of_node,
- data->chip->nchans,
- &irq_domain_simple_ops, data);
+ data->irq = irq_domain_create_linear(dev_fwnode(&client->dev), data->chip->nchans,
+ &irq_domain_simple_ops, data);
if (!data->irq)
return -ENODEV;
@@ -331,6 +466,8 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc)
struct pca954x *data = i2c_mux_priv(muxc);
int c, irq;
+ regulator_disable(data->supply);
+
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
@@ -341,21 +478,89 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc)
i2c_mux_del_adapters(muxc);
}
+static int pca954x_init(struct i2c_client *client, struct pca954x *data)
+{
+ int ret;
+
+ if (data->idle_state >= 0)
+ data->last_chan = pca954x_regval(data, data->idle_state);
+ else
+ data->last_chan = 0; /* Disconnect multiplexer */
+
+ if (device_is_compatible(&client->dev, "maxim,max7357")) {
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+ u8 conf = MAX7357_POR_DEFAULT_CONF;
+ /*
+ * The interrupt signal is shared with the reset pin. Release the
+ * interrupt after 1.6 seconds to allow using the pin as reset.
+ */
+ conf |= MAX7357_CONF_RELEASE_INT;
+
+ if (device_property_read_bool(&client->dev, "maxim,isolate-stuck-channel"))
+ conf |= MAX7357_CONF_DISCON_SINGLE_CHAN;
+ if (device_property_read_bool(&client->dev,
+ "maxim,send-flush-out-sequence"))
+ conf |= MAX7357_CONF_FLUSH_OUT;
+ if (device_property_read_bool(&client->dev,
+ "maxim,preconnection-wiggle-test-enable"))
+ conf |= MAX7357_CONF_PRECONNECT_TEST;
+
+ ret = i2c_smbus_write_byte_data(client, data->last_chan, conf);
+ } else {
+ dev_warn(&client->dev, "Write byte data not supported."
+ "Cannot enable enhanced mode features\n");
+ ret = i2c_smbus_write_byte(client, data->last_chan);
+ }
+ } else {
+ ret = i2c_smbus_write_byte(client, data->last_chan);
+ }
+
+ if (ret < 0)
+ data->last_chan = 0;
+
+ return ret;
+}
+
+static int pca954x_get_reset(struct device *dev, struct pca954x *data)
+{
+ data->reset_cont = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(data->reset_cont))
+ return dev_err_probe(dev, PTR_ERR(data->reset_cont),
+ "Failed to get reset\n");
+ else if (data->reset_cont)
+ return 0;
+
+ /*
+ * fallback to legacy reset-gpios
+ */
+ data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->reset_gpio)) {
+ return dev_err_probe(dev, PTR_ERR(data->reset_gpio),
+ "Failed to get reset gpio");
+ }
+
+ return 0;
+}
+
+static void pca954x_reset_deassert(struct pca954x *data)
+{
+ if (data->reset_cont)
+ reset_control_deassert(data->reset_cont);
+ else
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+}
+
/*
* I2C init/probing/exit functions
*/
-static int pca954x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pca954x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct i2c_adapter *adap = client->adapter;
- struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
- struct device_node *np = dev->of_node;
- bool idle_disconnect_dt;
- struct gpio_desc *gpio;
- int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
+ int num;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
@@ -370,18 +575,28 @@ static int pca954x_probe(struct i2c_client *client,
i2c_set_clientdata(client, muxc);
data->client = client;
- /* Reset the mux if a reset GPIO is specified. */
- gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(gpio))
- return PTR_ERR(gpio);
- if (gpio) {
+ data->supply = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(data->supply))
+ return dev_err_probe(dev, PTR_ERR(data->supply),
+ "Failed to request regulator\n");
+
+ ret = regulator_enable(data->supply);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable vdd supply\n");
+
+ ret = pca954x_get_reset(dev, data);
+ if (ret)
+ goto fail_cleanup;
+
+ if (data->reset_cont || data->reset_gpio) {
udelay(1);
- gpiod_set_value_cansleep(gpio, 0);
+ pca954x_reset_deassert(data);
/* Give the chip some time to recover. */
udelay(1);
}
- data->chip = of_device_get_match_data(dev);
+ data->chip = device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
@@ -390,7 +605,7 @@ static int pca954x_probe(struct i2c_client *client,
ret = i2c_get_device_id(client, &id);
if (ret && ret != -EOPNOTSUPP)
- return ret;
+ goto fail_cleanup;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
@@ -398,48 +613,37 @@ static int pca954x_probe(struct i2c_client *client,
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
- return -ENODEV;
+ ret = -ENODEV;
+ goto fail_cleanup;
}
}
- /* Write the mux register at addr to verify
+ data->idle_state = MUX_IDLE_AS_IS;
+ if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
+ if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
+ data->idle_state = MUX_IDLE_DISCONNECT;
+ }
+
+ /*
+ * Write the mux register at addr to verify
* that the mux is in fact present. This also
- * initializes the mux to disconnected state.
+ * initializes the mux to a channel
+ * or disconnected state.
*/
- if (i2c_smbus_write_byte(client, 0) < 0) {
+ ret = pca954x_init(client, data);
+ if (ret < 0) {
dev_warn(dev, "probe failed\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto fail_cleanup;
}
- data->last_chan = 0; /* force the first selection */
-
- idle_disconnect_dt = np &&
- of_property_read_bool(np, "i2c-mux-idle-disconnect");
-
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
- bool idle_disconnect_pd = false;
-
- force = 0; /* dynamic adap number */
- class = 0; /* no class by default */
- if (pdata) {
- if (num < pdata->num_modes) {
- /* force static number */
- force = pdata->modes[num].adap_id;
- class = pdata->modes[num].class;
- } else
- /* discard unconfigured channels */
- break;
- idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
- }
- data->deselect |= (idle_disconnect_pd ||
- idle_disconnect_dt) << num;
-
- ret = i2c_mux_add_adapter(muxc, force, num, class);
+ ret = i2c_mux_add_adapter(muxc, 0, num);
if (ret)
goto fail_cleanup;
}
@@ -453,6 +657,12 @@ static int pca954x_probe(struct i2c_client *client,
goto fail_cleanup;
}
+ /*
+ * The attr probably isn't going to be needed in most cases,
+ * so don't fail completely on error.
+ */
+ device_create_file(dev, &dev_attr_idle_state);
+
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
@@ -464,33 +674,36 @@ fail_cleanup:
return ret;
}
-static int pca954x_remove(struct i2c_client *client)
+static void pca954x_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
+ device_remove_file(&client->dev, &dev_attr_idle_state);
+
pca954x_cleanup(muxc);
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
+ int ret;
- data->last_chan = 0;
- return i2c_smbus_write_byte(client, 0);
+ ret = pca954x_init(client, data);
+ if (ret < 0)
+ dev_err(&client->dev, "failed to verify mux presence\n");
+
+ return ret;
}
-#endif
-static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "pca954x",
- .pm = &pca954x_pm,
- .of_match_table = of_match_ptr(pca954x_of_match),
+ .pm = pm_sleep_ptr(&pca954x_pm),
+ .of_match_table = pca954x_of_match,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c
index cc6818aabab5..fc686a350ae8 100644
--- a/drivers/i2c/muxes/i2c-mux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C multiplexer using pinctrl API
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/i2c.h>
@@ -27,7 +16,7 @@
struct i2c_mux_pinctrl {
struct pinctrl *pinctrl;
- struct pinctrl_state **states;
+ struct pinctrl_state *states[];
};
static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
@@ -73,7 +62,7 @@ static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev)
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
- parent = of_find_i2c_adapter_by_node(parent_np);
+ parent = of_get_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
@@ -104,14 +93,13 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
return PTR_ERR(parent);
muxc = i2c_mux_alloc(parent, dev, num_names,
- sizeof(*mux) + num_names * sizeof(*mux->states),
+ struct_size(mux, states, num_names),
0, i2c_mux_pinctrl_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto err_put_parent;
}
mux = i2c_mux_priv(muxc);
- mux->states = (struct pinctrl_state **)(mux + 1);
platform_set_drvdata(pdev, muxc);
@@ -163,7 +151,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
/* Do not add any adapter for the idle state (if it's there at all). */
for (i = 0; i < num_names - !!muxc->deselect; i++) {
- ret = i2c_mux_add_adapter(muxc, 0, i, 0);
+ ret = i2c_mux_add_adapter(muxc, 0, i);
if (ret)
goto err_del_adapter;
}
@@ -178,14 +166,12 @@ err_put_parent:
return ret;
}
-static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
+static void i2c_mux_pinctrl_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
-
- return 0;
}
static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
@@ -197,10 +183,10 @@ MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
static struct platform_driver i2c_mux_pinctrl_driver = {
.driver = {
.name = "i2c-mux-pinctrl",
- .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
+ .of_match_table = i2c_mux_pinctrl_of_match,
},
.probe = i2c_mux_pinctrl_probe,
- .remove = i2c_mux_pinctrl_remove,
+ .remove = i2c_mux_pinctrl_remove,
};
module_platform_driver(i2c_mux_pinctrl_driver);
diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c
index 5653295b01cd..1e566ea92bc9 100644
--- a/drivers/i2c/muxes/i2c-mux-reg.c
+++ b/drivers/i2c/muxes/i2c-mux-reg.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* I2C multiplexer using a single register
*
* Copyright 2015 Freescale Semiconductor
* York Sun <yorksun@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/i2c.h>
@@ -163,7 +159,6 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
struct regmux *mux;
struct i2c_adapter *parent;
struct resource *res;
- unsigned int class;
int i, ret, nr;
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
@@ -175,13 +170,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
sizeof(mux->data));
} else {
ret = i2c_mux_reg_probe_dt(mux, pdev);
- if (ret == -EPROBE_DEFER)
- return ret;
-
- if (ret < 0) {
- dev_err(&pdev->dev, "Error parsing device tree");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "Error parsing device tree");
}
parent = i2c_get_adapter(mux->data.parent);
@@ -191,13 +182,12 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
if (!mux->data.reg) {
dev_info(&pdev->dev,
"Register not set, using platform resource\n");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mux->data.reg_size = resource_size(res);
- mux->data.reg = devm_ioremap_resource(&pdev->dev, res);
+ mux->data.reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mux->data.reg)) {
ret = PTR_ERR(mux->data.reg);
goto err_put_parent;
}
+ mux->data.reg_size = resource_size(res);
}
if (mux->data.reg_size != 4 && mux->data.reg_size != 2 &&
@@ -222,9 +212,8 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
for (i = 0; i < mux->data.n_values; i++) {
nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
- class = mux->data.classes ? mux->data.classes[i] : 0;
- ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
+ ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i]);
if (ret)
goto err_del_mux_adapters;
}
@@ -242,14 +231,12 @@ err_put_parent:
return ret;
}
-static int i2c_mux_reg_remove(struct platform_device *pdev)
+static void i2c_mux_reg_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
-
- return 0;
}
static const struct of_device_id i2c_mux_reg_of_match[] = {
@@ -260,10 +247,10 @@ MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match);
static struct platform_driver i2c_mux_reg_driver = {
.probe = i2c_mux_reg_probe,
- .remove = i2c_mux_reg_remove,
+ .remove = i2c_mux_reg_remove,
.driver = {
.name = "i2c-mux-reg",
- .of_match_table = of_match_ptr(i2c_mux_reg_of_match),
+ .of_match_table = i2c_mux_reg_of_match,
},
};