From 8d836581f9b1f57ceaa2b47654754ef1260b410b Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 16 Sep 2026 15:00:32 +0200 Subject: ata: libata-scsi: fix ata_dsm_trim_pages() kernel-doc make htmldocs fails with: Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301: ERROR: Unexpected indentation. [docutils] The Return: section of ata_dsm_trim_pages() ends its first sentence with a colon and continues with an indented bullet list. reStructuredText requires a blank line before an indented block, so docutils chokes on the list. Simply adding the missing blank line does not work either: Return: is a kernel-doc "special section", which is terminated by the first blank line, so the bullet list would end up in the Description section, detached from the sentence introducing it. Spell the two bounds out as prose instead, so that the Return: section stays self-contained. Documentation-only change. Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND") Reported-by: Thomas Huth Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/ Reviewed-by: Damien Le Moal Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/i2c/Kconfig | 165 ++ drivers/i2c/Makefile | 23 + drivers/i2c/algos/Kconfig | 18 + drivers/i2c/algos/Makefile | 10 + drivers/i2c/algos/i2c-algo-bit.c | 689 +++++++ drivers/i2c/algos/i2c-algo-pca.c | 561 ++++++ drivers/i2c/algos/i2c-algo-pcf.c | 369 ++++ drivers/i2c/algos/i2c-algo-pcf.h | 65 + drivers/i2c/busses/Kconfig | 1605 +++++++++++++++ drivers/i2c/busses/Makefile | 166 ++ drivers/i2c/busses/i2c-acorn.c | 96 + drivers/i2c/busses/i2c-ali1535.c | 537 +++++ drivers/i2c/busses/i2c-ali1563.c | 442 +++++ drivers/i2c/busses/i2c-ali15x3.c | 518 +++++ drivers/i2c/busses/i2c-altera.c | 496 +++++ drivers/i2c/busses/i2c-amd-asf-plat.c | 373 ++++ drivers/i2c/busses/i2c-amd-mp2-pci.c | 479 +++++ drivers/i2c/busses/i2c-amd-mp2-plat.c | 362 ++++ drivers/i2c/busses/i2c-amd-mp2.h | 213 ++ drivers/i2c/busses/i2c-amd756.c | 400 ++++ drivers/i2c/busses/i2c-amd8111.c | 478 +++++ drivers/i2c/busses/i2c-aspeed.c | 1115 +++++++++++ drivers/i2c/busses/i2c-at91-core.c | 357 ++++ drivers/i2c/busses/i2c-at91-master.c | 921 +++++++++ drivers/i2c/busses/i2c-at91-slave.c | 142 ++ drivers/i2c/busses/i2c-at91.h | 191 ++ drivers/i2c/busses/i2c-au1550.c | 379 ++++ drivers/i2c/busses/i2c-axxia.c | 823 ++++++++ drivers/i2c/busses/i2c-bcm-iproc.c | 1262 ++++++++++++ drivers/i2c/busses/i2c-bcm-kona.c | 886 +++++++++ drivers/i2c/busses/i2c-bcm2835.c | 536 +++++ drivers/i2c/busses/i2c-brcmstb.c | 752 +++++++ drivers/i2c/busses/i2c-cadence.c | 1672 ++++++++++++++++ drivers/i2c/busses/i2c-cbus-gpio.c | 282 +++ drivers/i2c/busses/i2c-ccgx-ucsi.c | 31 + drivers/i2c/busses/i2c-ccgx-ucsi.h | 11 + drivers/i2c/busses/i2c-cgbc.c | 406 ++++ drivers/i2c/busses/i2c-cht-wc.c | 559 ++++++ drivers/i2c/busses/i2c-cp2615.c | 338 ++++ drivers/i2c/busses/i2c-cpm.c | 715 +++++++ drivers/i2c/busses/i2c-cros-ec-tunnel.c | 322 +++ drivers/i2c/busses/i2c-davinci.c | 915 +++++++++ drivers/i2c/busses/i2c-designware-amdisp.c | 195 ++ drivers/i2c/busses/i2c-designware-amdpsp.c | 310 +++ drivers/i2c/busses/i2c-designware-baytrail.c | 44 + drivers/i2c/busses/i2c-designware-common.c | 1066 ++++++++++ drivers/i2c/busses/i2c-designware-core.h | 350 ++++ drivers/i2c/busses/i2c-designware-master.c | 1053 ++++++++++ drivers/i2c/busses/i2c-designware-pcidrv.c | 388 ++++ drivers/i2c/busses/i2c-designware-platdrv.c | 335 ++++ drivers/i2c/busses/i2c-designware-slave.c | 196 ++ drivers/i2c/busses/i2c-digicolor.c | 376 ++++ drivers/i2c/busses/i2c-diolan-u2c.c | 512 +++++ drivers/i2c/busses/i2c-dln2.c | 256 +++ drivers/i2c/busses/i2c-eg20t.c | 889 +++++++++ drivers/i2c/busses/i2c-elektor.c | 321 +++ drivers/i2c/busses/i2c-emev2.c | 440 +++++ drivers/i2c/busses/i2c-exynos5.c | 1052 ++++++++++ drivers/i2c/busses/i2c-fsi.c | 775 ++++++++ drivers/i2c/busses/i2c-gpio.c | 703 +++++++ drivers/i2c/busses/i2c-gxp.c | 607 ++++++ drivers/i2c/busses/i2c-highlander.c | 476 +++++ drivers/i2c/busses/i2c-hisi.c | 542 +++++ drivers/i2c/busses/i2c-hix5hd2.c | 523 +++++ drivers/i2c/busses/i2c-hydra.c | 150 ++ drivers/i2c/busses/i2c-i801.c | 1769 +++++++++++++++++ drivers/i2c/busses/i2c-ibm_iic.c | 794 ++++++++ drivers/i2c/busses/i2c-ibm_iic.h | 118 ++ drivers/i2c/busses/i2c-icy.c | 215 ++ drivers/i2c/busses/i2c-img-scb.c | 1503 ++++++++++++++ drivers/i2c/busses/i2c-imx-lpi2c.c | 1774 +++++++++++++++++ drivers/i2c/busses/i2c-imx.c | 2065 +++++++++++++++++++ drivers/i2c/busses/i2c-iop3xx.c | 539 +++++ drivers/i2c/busses/i2c-iop3xx.h | 99 + drivers/i2c/busses/i2c-isch.c | 316 +++ drivers/i2c/busses/i2c-ismt.c | 994 ++++++++++ drivers/i2c/busses/i2c-jz4780.c | 864 ++++++++ drivers/i2c/busses/i2c-k1.c | 944 +++++++++ drivers/i2c/busses/i2c-keba.c | 594 ++++++ drivers/i2c/busses/i2c-kempld.c | 396 ++++ drivers/i2c/busses/i2c-ljca.c | 343 ++++ drivers/i2c/busses/i2c-lpc2k.c | 482 +++++ drivers/i2c/busses/i2c-ls2x-v2.c | 544 ++++++ drivers/i2c/busses/i2c-ls2x.c | 377 ++++ drivers/i2c/busses/i2c-mchp-pci1xxxx.c | 1212 ++++++++++++ drivers/i2c/busses/i2c-meson.c | 579 ++++++ drivers/i2c/busses/i2c-microchip-corei2c.c | 648 ++++++ drivers/i2c/busses/i2c-mlxbf.c | 2449 +++++++++++++++++++++++ drivers/i2c/busses/i2c-mlxcpld.c | 605 ++++++ drivers/i2c/busses/i2c-mpc.c | 954 +++++++++ drivers/i2c/busses/i2c-mt65xx.c | 1573 +++++++++++++++ drivers/i2c/busses/i2c-mt7621.c | 342 ++++ drivers/i2c/busses/i2c-mv64xxx.c | 1112 +++++++++++ drivers/i2c/busses/i2c-mxs.c | 908 +++++++++ drivers/i2c/busses/i2c-nct6694.c | 196 ++ drivers/i2c/busses/i2c-nforce2.c | 426 ++++ drivers/i2c/busses/i2c-nomadik.c | 1238 ++++++++++++ drivers/i2c/busses/i2c-npcm7xx.c | 2621 +++++++++++++++++++++++++ drivers/i2c/busses/i2c-nvidia-gpu.c | 375 ++++ drivers/i2c/busses/i2c-ocores.c | 783 ++++++++ drivers/i2c/busses/i2c-octeon-core.c | 1002 ++++++++++ drivers/i2c/busses/i2c-octeon-core.h | 259 +++ drivers/i2c/busses/i2c-octeon-platdrv.c | 283 +++ drivers/i2c/busses/i2c-omap.c | 1632 ++++++++++++++++ drivers/i2c/busses/i2c-opal.c | 276 +++ drivers/i2c/busses/i2c-owl.c | 522 +++++ drivers/i2c/busses/i2c-parport.c | 408 ++++ drivers/i2c/busses/i2c-pasemi-core.c | 465 +++++ drivers/i2c/busses/i2c-pasemi-core.h | 26 + drivers/i2c/busses/i2c-pasemi-pci.c | 86 + drivers/i2c/busses/i2c-pasemi-platform.c | 113 ++ drivers/i2c/busses/i2c-pca-isa.c | 201 ++ drivers/i2c/busses/i2c-pca-platform.c | 252 +++ drivers/i2c/busses/i2c-piix4.c | 1158 +++++++++++ drivers/i2c/busses/i2c-piix4.h | 44 + drivers/i2c/busses/i2c-pnx.c | 756 +++++++ drivers/i2c/busses/i2c-powermac.c | 449 +++++ drivers/i2c/busses/i2c-pxa-pci.c | 150 ++ drivers/i2c/busses/i2c-pxa.c | 1624 +++++++++++++++ drivers/i2c/busses/i2c-qcom-cci.c | 835 ++++++++ drivers/i2c/busses/i2c-qcom-geni.c | 1336 +++++++++++++ drivers/i2c/busses/i2c-qup.c | 2042 +++++++++++++++++++ drivers/i2c/busses/i2c-rcar.c | 1300 ++++++++++++ drivers/i2c/busses/i2c-riic.c | 776 ++++++++ drivers/i2c/busses/i2c-rk3x.c | 1413 +++++++++++++ drivers/i2c/busses/i2c-robotfuzz-osif.c | 200 ++ drivers/i2c/busses/i2c-rtl9300.c | 670 +++++++ drivers/i2c/busses/i2c-rzv2m.c | 538 +++++ drivers/i2c/busses/i2c-s3c2410.c | 1206 ++++++++++++ drivers/i2c/busses/i2c-scmi.c | 424 ++++ drivers/i2c/busses/i2c-sh7760.c | 562 ++++++ drivers/i2c/busses/i2c-sh_mobile.c | 1006 ++++++++++ drivers/i2c/busses/i2c-sibyte.c | 186 ++ drivers/i2c/busses/i2c-simtec.c | 155 ++ drivers/i2c/busses/i2c-sis5595.c | 422 ++++ drivers/i2c/busses/i2c-sis630.c | 554 ++++++ drivers/i2c/busses/i2c-sis96x.c | 314 +++ drivers/i2c/busses/i2c-sprd.c | 652 ++++++ drivers/i2c/busses/i2c-st.c | 890 +++++++++ drivers/i2c/busses/i2c-stm32.c | 153 ++ drivers/i2c/busses/i2c-stm32.h | 58 + drivers/i2c/busses/i2c-stm32f4.c | 866 ++++++++ drivers/i2c/busses/i2c-stm32f7.c | 2529 ++++++++++++++++++++++++ drivers/i2c/busses/i2c-sun6i-p2wi.c | 315 +++ drivers/i2c/busses/i2c-synquacer.c | 644 ++++++ drivers/i2c/busses/i2c-taos-evm.c | 311 +++ drivers/i2c/busses/i2c-tegra-bpmp.c | 345 ++++ drivers/i2c/busses/i2c-tegra.c | 2511 ++++++++++++++++++++++++ drivers/i2c/busses/i2c-thunderx-pcidrv.c | 277 +++ drivers/i2c/busses/i2c-tiny-usb.c | 302 +++ drivers/i2c/busses/i2c-uniphier-f.c | 621 ++++++ drivers/i2c/busses/i2c-uniphier.c | 415 ++++ drivers/i2c/busses/i2c-usbio.c | 322 +++ drivers/i2c/busses/i2c-versatile.c | 134 ++ drivers/i2c/busses/i2c-via.c | 154 ++ drivers/i2c/busses/i2c-viai2c-common.c | 203 ++ drivers/i2c/busses/i2c-viai2c-common.h | 85 + drivers/i2c/busses/i2c-viai2c-wmt.c | 177 ++ drivers/i2c/busses/i2c-viai2c-zhaoxin.c | 366 ++++ drivers/i2c/busses/i2c-viapro.c | 496 +++++ drivers/i2c/busses/i2c-viperboard.c | 462 +++++ drivers/i2c/busses/i2c-virtio.c | 280 +++ drivers/i2c/busses/i2c-xgene-slimpro.c | 564 ++++++ drivers/i2c/busses/i2c-xiic.c | 1570 +++++++++++++++ drivers/i2c/busses/i2c-xlp9xx.c | 593 ++++++ drivers/i2c/busses/scx200_acb.c | 596 ++++++ drivers/i2c/i2c-atr.c | 945 +++++++++ drivers/i2c/i2c-boardinfo.c | 93 + drivers/i2c/i2c-core-acpi.c | 849 ++++++++ drivers/i2c/i2c-core-base.c | 2720 ++++++++++++++++++++++++++ drivers/i2c/i2c-core-of-prober.c | 415 ++++ drivers/i2c/i2c-core-of.c | 213 ++ drivers/i2c/i2c-core-slave.c | 127 ++ drivers/i2c/i2c-core-smbus.c | 740 +++++++ drivers/i2c/i2c-core.h | 109 ++ drivers/i2c/i2c-dev.c | 806 ++++++++ drivers/i2c/i2c-mux.c | 442 +++++ drivers/i2c/i2c-slave-eeprom.c | 218 +++ drivers/i2c/i2c-slave-testunit.c | 290 +++ drivers/i2c/i2c-smbus.c | 493 +++++ drivers/i2c/i2c-stub.c | 417 ++++ drivers/i2c/muxes/Kconfig | 138 ++ drivers/i2c/muxes/Makefile | 19 + drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 205 ++ drivers/i2c/muxes/i2c-demux-pinctrl.c | 327 ++++ drivers/i2c/muxes/i2c-mux-gpio.c | 262 +++ drivers/i2c/muxes/i2c-mux-gpmux.c | 165 ++ drivers/i2c/muxes/i2c-mux-ltc4306.c | 317 +++ drivers/i2c/muxes/i2c-mux-mlxcpld.c | 193 ++ drivers/i2c/muxes/i2c-mux-mule.c | 147 ++ drivers/i2c/muxes/i2c-mux-pca9541.c | 348 ++++ drivers/i2c/muxes/i2c-mux-pca954x.c | 717 +++++++ drivers/i2c/muxes/i2c-mux-pinctrl.c | 196 ++ drivers/i2c/muxes/i2c-mux-reg.c | 248 +++ 194 files changed, 115685 insertions(+) create mode 100644 drivers/i2c/Kconfig create mode 100644 drivers/i2c/Makefile create mode 100644 drivers/i2c/algos/Kconfig create mode 100644 drivers/i2c/algos/Makefile create mode 100644 drivers/i2c/algos/i2c-algo-bit.c create mode 100644 drivers/i2c/algos/i2c-algo-pca.c create mode 100644 drivers/i2c/algos/i2c-algo-pcf.c create mode 100644 drivers/i2c/algos/i2c-algo-pcf.h create mode 100644 drivers/i2c/busses/Kconfig create mode 100644 drivers/i2c/busses/Makefile create mode 100644 drivers/i2c/busses/i2c-acorn.c create mode 100644 drivers/i2c/busses/i2c-ali1535.c create mode 100644 drivers/i2c/busses/i2c-ali1563.c create mode 100644 drivers/i2c/busses/i2c-ali15x3.c create mode 100644 drivers/i2c/busses/i2c-altera.c create mode 100644 drivers/i2c/busses/i2c-amd-asf-plat.c create mode 100644 drivers/i2c/busses/i2c-amd-mp2-pci.c create mode 100644 drivers/i2c/busses/i2c-amd-mp2-plat.c create mode 100644 drivers/i2c/busses/i2c-amd-mp2.h create mode 100644 drivers/i2c/busses/i2c-amd756.c create mode 100644 drivers/i2c/busses/i2c-amd8111.c create mode 100644 drivers/i2c/busses/i2c-aspeed.c create mode 100644 drivers/i2c/busses/i2c-at91-core.c create mode 100644 drivers/i2c/busses/i2c-at91-master.c create mode 100644 drivers/i2c/busses/i2c-at91-slave.c create mode 100644 drivers/i2c/busses/i2c-at91.h create mode 100644 drivers/i2c/busses/i2c-au1550.c create mode 100644 drivers/i2c/busses/i2c-axxia.c create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c create mode 100644 drivers/i2c/busses/i2c-bcm-kona.c create mode 100644 drivers/i2c/busses/i2c-bcm2835.c create mode 100644 drivers/i2c/busses/i2c-brcmstb.c create mode 100644 drivers/i2c/busses/i2c-cadence.c create mode 100644 drivers/i2c/busses/i2c-cbus-gpio.c create mode 100644 drivers/i2c/busses/i2c-ccgx-ucsi.c create mode 100644 drivers/i2c/busses/i2c-ccgx-ucsi.h create mode 100644 drivers/i2c/busses/i2c-cgbc.c create mode 100644 drivers/i2c/busses/i2c-cht-wc.c create mode 100644 drivers/i2c/busses/i2c-cp2615.c create mode 100644 drivers/i2c/busses/i2c-cpm.c create mode 100644 drivers/i2c/busses/i2c-cros-ec-tunnel.c create mode 100644 drivers/i2c/busses/i2c-davinci.c create mode 100644 drivers/i2c/busses/i2c-designware-amdisp.c create mode 100644 drivers/i2c/busses/i2c-designware-amdpsp.c create mode 100644 drivers/i2c/busses/i2c-designware-baytrail.c create mode 100644 drivers/i2c/busses/i2c-designware-common.c create mode 100644 drivers/i2c/busses/i2c-designware-core.h create mode 100644 drivers/i2c/busses/i2c-designware-master.c create mode 100644 drivers/i2c/busses/i2c-designware-pcidrv.c create mode 100644 drivers/i2c/busses/i2c-designware-platdrv.c create mode 100644 drivers/i2c/busses/i2c-designware-slave.c create mode 100644 drivers/i2c/busses/i2c-digicolor.c create mode 100644 drivers/i2c/busses/i2c-diolan-u2c.c create mode 100644 drivers/i2c/busses/i2c-dln2.c create mode 100644 drivers/i2c/busses/i2c-eg20t.c create mode 100644 drivers/i2c/busses/i2c-elektor.c create mode 100644 drivers/i2c/busses/i2c-emev2.c create mode 100644 drivers/i2c/busses/i2c-exynos5.c create mode 100644 drivers/i2c/busses/i2c-fsi.c create mode 100644 drivers/i2c/busses/i2c-gpio.c create mode 100644 drivers/i2c/busses/i2c-gxp.c create mode 100644 drivers/i2c/busses/i2c-highlander.c create mode 100644 drivers/i2c/busses/i2c-hisi.c create mode 100644 drivers/i2c/busses/i2c-hix5hd2.c create mode 100644 drivers/i2c/busses/i2c-hydra.c create mode 100644 drivers/i2c/busses/i2c-i801.c create mode 100644 drivers/i2c/busses/i2c-ibm_iic.c create mode 100644 drivers/i2c/busses/i2c-ibm_iic.h create mode 100644 drivers/i2c/busses/i2c-icy.c create mode 100644 drivers/i2c/busses/i2c-img-scb.c create mode 100644 drivers/i2c/busses/i2c-imx-lpi2c.c create mode 100644 drivers/i2c/busses/i2c-imx.c create mode 100644 drivers/i2c/busses/i2c-iop3xx.c create mode 100644 drivers/i2c/busses/i2c-iop3xx.h create mode 100644 drivers/i2c/busses/i2c-isch.c create mode 100644 drivers/i2c/busses/i2c-ismt.c create mode 100644 drivers/i2c/busses/i2c-jz4780.c create mode 100644 drivers/i2c/busses/i2c-k1.c create mode 100644 drivers/i2c/busses/i2c-keba.c create mode 100644 drivers/i2c/busses/i2c-kempld.c create mode 100644 drivers/i2c/busses/i2c-ljca.c create mode 100644 drivers/i2c/busses/i2c-lpc2k.c create mode 100644 drivers/i2c/busses/i2c-ls2x-v2.c create mode 100644 drivers/i2c/busses/i2c-ls2x.c create mode 100644 drivers/i2c/busses/i2c-mchp-pci1xxxx.c create mode 100644 drivers/i2c/busses/i2c-meson.c create mode 100644 drivers/i2c/busses/i2c-microchip-corei2c.c create mode 100644 drivers/i2c/busses/i2c-mlxbf.c create mode 100644 drivers/i2c/busses/i2c-mlxcpld.c create mode 100644 drivers/i2c/busses/i2c-mpc.c create mode 100644 drivers/i2c/busses/i2c-mt65xx.c create mode 100644 drivers/i2c/busses/i2c-mt7621.c create mode 100644 drivers/i2c/busses/i2c-mv64xxx.c create mode 100644 drivers/i2c/busses/i2c-mxs.c create mode 100644 drivers/i2c/busses/i2c-nct6694.c create mode 100644 drivers/i2c/busses/i2c-nforce2.c create mode 100644 drivers/i2c/busses/i2c-nomadik.c create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c create mode 100644 drivers/i2c/busses/i2c-ocores.c create mode 100644 drivers/i2c/busses/i2c-octeon-core.c create mode 100644 drivers/i2c/busses/i2c-octeon-core.h create mode 100644 drivers/i2c/busses/i2c-octeon-platdrv.c create mode 100644 drivers/i2c/busses/i2c-omap.c create mode 100644 drivers/i2c/busses/i2c-opal.c create mode 100644 drivers/i2c/busses/i2c-owl.c create mode 100644 drivers/i2c/busses/i2c-parport.c create mode 100644 drivers/i2c/busses/i2c-pasemi-core.c create mode 100644 drivers/i2c/busses/i2c-pasemi-core.h create mode 100644 drivers/i2c/busses/i2c-pasemi-pci.c create mode 100644 drivers/i2c/busses/i2c-pasemi-platform.c create mode 100644 drivers/i2c/busses/i2c-pca-isa.c create mode 100644 drivers/i2c/busses/i2c-pca-platform.c create mode 100644 drivers/i2c/busses/i2c-piix4.c create mode 100644 drivers/i2c/busses/i2c-piix4.h create mode 100644 drivers/i2c/busses/i2c-pnx.c create mode 100644 drivers/i2c/busses/i2c-powermac.c create mode 100644 drivers/i2c/busses/i2c-pxa-pci.c create mode 100644 drivers/i2c/busses/i2c-pxa.c create mode 100644 drivers/i2c/busses/i2c-qcom-cci.c create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c create mode 100644 drivers/i2c/busses/i2c-qup.c create mode 100644 drivers/i2c/busses/i2c-rcar.c create mode 100644 drivers/i2c/busses/i2c-riic.c create mode 100644 drivers/i2c/busses/i2c-rk3x.c create mode 100644 drivers/i2c/busses/i2c-robotfuzz-osif.c create mode 100644 drivers/i2c/busses/i2c-rtl9300.c create mode 100644 drivers/i2c/busses/i2c-rzv2m.c create mode 100644 drivers/i2c/busses/i2c-s3c2410.c create mode 100644 drivers/i2c/busses/i2c-scmi.c create mode 100644 drivers/i2c/busses/i2c-sh7760.c create mode 100644 drivers/i2c/busses/i2c-sh_mobile.c create mode 100644 drivers/i2c/busses/i2c-sibyte.c create mode 100644 drivers/i2c/busses/i2c-simtec.c create mode 100644 drivers/i2c/busses/i2c-sis5595.c create mode 100644 drivers/i2c/busses/i2c-sis630.c create mode 100644 drivers/i2c/busses/i2c-sis96x.c create mode 100644 drivers/i2c/busses/i2c-sprd.c create mode 100644 drivers/i2c/busses/i2c-st.c create mode 100644 drivers/i2c/busses/i2c-stm32.c create mode 100644 drivers/i2c/busses/i2c-stm32.h create mode 100644 drivers/i2c/busses/i2c-stm32f4.c create mode 100644 drivers/i2c/busses/i2c-stm32f7.c create mode 100644 drivers/i2c/busses/i2c-sun6i-p2wi.c create mode 100644 drivers/i2c/busses/i2c-synquacer.c create mode 100644 drivers/i2c/busses/i2c-taos-evm.c create mode 100644 drivers/i2c/busses/i2c-tegra-bpmp.c create mode 100644 drivers/i2c/busses/i2c-tegra.c create mode 100644 drivers/i2c/busses/i2c-thunderx-pcidrv.c create mode 100644 drivers/i2c/busses/i2c-tiny-usb.c create mode 100644 drivers/i2c/busses/i2c-uniphier-f.c create mode 100644 drivers/i2c/busses/i2c-uniphier.c create mode 100644 drivers/i2c/busses/i2c-usbio.c create mode 100644 drivers/i2c/busses/i2c-versatile.c create mode 100644 drivers/i2c/busses/i2c-via.c create mode 100644 drivers/i2c/busses/i2c-viai2c-common.c create mode 100644 drivers/i2c/busses/i2c-viai2c-common.h create mode 100644 drivers/i2c/busses/i2c-viai2c-wmt.c create mode 100644 drivers/i2c/busses/i2c-viai2c-zhaoxin.c create mode 100644 drivers/i2c/busses/i2c-viapro.c create mode 100644 drivers/i2c/busses/i2c-viperboard.c create mode 100644 drivers/i2c/busses/i2c-virtio.c create mode 100644 drivers/i2c/busses/i2c-xgene-slimpro.c create mode 100644 drivers/i2c/busses/i2c-xiic.c create mode 100644 drivers/i2c/busses/i2c-xlp9xx.c create mode 100644 drivers/i2c/busses/scx200_acb.c create mode 100644 drivers/i2c/i2c-atr.c create mode 100644 drivers/i2c/i2c-boardinfo.c create mode 100644 drivers/i2c/i2c-core-acpi.c create mode 100644 drivers/i2c/i2c-core-base.c create mode 100644 drivers/i2c/i2c-core-of-prober.c create mode 100644 drivers/i2c/i2c-core-of.c create mode 100644 drivers/i2c/i2c-core-slave.c create mode 100644 drivers/i2c/i2c-core-smbus.c create mode 100644 drivers/i2c/i2c-core.h create mode 100644 drivers/i2c/i2c-dev.c create mode 100644 drivers/i2c/i2c-mux.c create mode 100644 drivers/i2c/i2c-slave-eeprom.c create mode 100644 drivers/i2c/i2c-slave-testunit.c create mode 100644 drivers/i2c/i2c-smbus.c create mode 100644 drivers/i2c/i2c-stub.c create mode 100644 drivers/i2c/muxes/Kconfig create mode 100644 drivers/i2c/muxes/Makefile create mode 100644 drivers/i2c/muxes/i2c-arb-gpio-challenge.c create mode 100644 drivers/i2c/muxes/i2c-demux-pinctrl.c create mode 100644 drivers/i2c/muxes/i2c-mux-gpio.c create mode 100644 drivers/i2c/muxes/i2c-mux-gpmux.c create mode 100644 drivers/i2c/muxes/i2c-mux-ltc4306.c create mode 100644 drivers/i2c/muxes/i2c-mux-mlxcpld.c create mode 100644 drivers/i2c/muxes/i2c-mux-mule.c create mode 100644 drivers/i2c/muxes/i2c-mux-pca9541.c create mode 100644 drivers/i2c/muxes/i2c-mux-pca954x.c create mode 100644 drivers/i2c/muxes/i2c-mux-pinctrl.c create mode 100644 drivers/i2c/muxes/i2c-mux-reg.c (limited to 'drivers/i2c') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig new file mode 100644 index 000000000..c232054fd --- /dev/null +++ b/drivers/i2c/Kconfig @@ -0,0 +1,165 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# I2C subsystem configuration +# + +menu "I2C support" + +config I2C + tristate "I2C support" + select RT_MUTEXES + select IRQ_DOMAIN + 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 + information is contained in the directory , + especially in the file called "summary" there. + + Both I2C and SMBus are supported here. You will need this for + hardware sensors support, and also for Video For Linux support. + + If you want I2C support, you should say Y here and also to the + specific driver for your bus adapter(s) below. + + This I2C support can also be built as a module. If so, the module + will be called i2c-core. + +config ACPI_I2C_OPREGION + bool "ACPI I2C Operation region support" + depends on I2C=y && ACPI + default y + help + Say Y here if you want to enable ACPI I2C operation region support. + Operation Regions allow firmware (BIOS) code to access I2C slave devices, + such as smart batteries through an I2C host controller driver. + +if I2C + +config I2C_BOARDINFO + bool + default 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 . + + This support is also available as a module. If so, the module + will be called i2c-dev. + +config I2C_MUX + tristate "I2C bus multiplexing support" + help + Say Y here if you want the I2C core to support the ability to + handle multiplexed I2C bus topologies, by presenting each + multiplexed segment as a I2C adapter. + + This support is also available as a module. If so, the module + will be called 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 + help + Some I2C bus drivers require so-called "I2C algorithm" modules + to work. These are basically software-only abstractions of generic + I2C interfaces. This option will autoselect them so that you don't + have to care. + + Unselect this only if you need to enable additional helper + modules, for example for use with external I2C bus drivers. + + In doubt, say Y. + +config I2C_SMBUS + tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO + help + Say Y here if you want support for SMBus extensions to the I2C + specification. At the moment, two extensions are supported: + the SMBus Alert protocol and the SMBus Host Notify protocol. + + This support is also available as a module. If so, the module + will be called i2c-smbus. + +source "drivers/i2c/algos/Kconfig" +source "drivers/i2c/busses/Kconfig" + +config I2C_STUB + tristate "I2C/SMBus Test Stub" + depends on m + 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 . + + 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 + +config I2C_DEBUG_CORE + bool "I2C Core debugging messages" + help + Say Y here if you want the I2C core to produce a bunch of debug + messages to the system log. Select this if you are having a + problem with I2C support and want to see more of what is going on. + +config I2C_DEBUG_ALGO + bool "I2C Algorithm debugging messages" + help + Say Y here if you want the I2C algorithm drivers to produce a bunch + of debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. + +config I2C_DEBUG_BUS + bool "I2C Bus debugging messages" + depends on HAS_IOMEM + help + Say Y here if you want the I2C bus drivers to produce a bunch of + debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. + +endif # I2C + +endmenu diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile new file mode 100644 index 000000000..d27de18de --- /dev/null +++ b/drivers/i2c/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the i2c core. +# + +obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o +obj-$(CONFIG_I2C) += i2c-core.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_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 new file mode 100644 index 000000000..aea523529 --- /dev/null +++ b/drivers/i2c/algos/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# I2C algorithm drivers configuration +# + +menu "I2C Algorithms" + visible if !I2C_HELPER_AUTO + +config I2C_ALGOBIT + tristate "I2C bit-banging interfaces" + +config I2C_ALGOPCF + tristate "I2C PCF 8584 interfaces" + +config I2C_ALGOPCA + tristate "I2C PCA 9564 interfaces" + +endmenu diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile new file mode 100644 index 000000000..9b319a30d --- /dev/null +++ b/drivers/i2c/algos/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the i2c algorithms +# + +obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o +obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o + +ccflags-$(CONFIG_I2C_DEBUG_ALGO) := -DDEBUG diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c new file mode 100644 index 000000000..7ca565cbf --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters + * + * Copyright (C) 1995-2000 Simon G. Vogl + * + * With some changes from Frodo Looijaard , Kyösti Mälkki + * and Jean Delvare + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ----- global defines ----------------------------------------------- */ + +#ifdef DEBUG +#define bit_dbg(level, dev, format, args...) \ + do { \ + if (i2c_debug >= level) \ + dev_dbg(dev, format, ##args); \ + } while (0) +#else +#define bit_dbg(level, dev, format, args...) \ + do {} while (0) +#endif /* DEBUG */ + +/* ----- global variables --------------------------------------------- */ + +static int bit_test; /* see if the line-setting functions work */ +module_param(bit_test, int, S_IRUGO); +MODULE_PARM_DESC(bit_test, "lines testing - 0 off; 1 report; 2 fail if stuck"); + +#ifdef DEBUG +static int i2c_debug = 1; +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2 verbose; 3 very verbose"); +#endif + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap, val) adap->setsda(adap->data, val) +#define setscl(adap, val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 1); + udelay((adap->udelay + 1) / 2); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap, 0); + udelay(adap->udelay / 2); +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static int sclhi(struct i2c_algo_bit_data *adap) +{ + unsigned long start; + + setscl(adap, 1); + + /* Not all adapters have scl sense line... */ + if (!adap->getscl) + goto done; + + start = jiffies; + while (!getscl(adap)) { + /* This hw knows how to read the clock line, so we wait + * until it actually gets high. This is safer as some + * chips may hold it low ("clock stretching") while they + * are processing data internally. + */ + if (time_after(jiffies, start + adap->timeout)) { + /* Test one last time, as we may have been preempted + * between last check and timeout test. + */ + if (getscl(adap)) + break; + return -ETIMEDOUT; + } + cpu_relax(); + } +#ifdef DEBUG + if (jiffies != start && i2c_debug >= 3) + pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go high\n", + jiffies - start); +#endif + +done: + udelay(adap->udelay); + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdahi(adap); + sclhi(adap); + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + setsda(adap, 1); + udelay(adap->udelay); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occurred (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + for (i = 7; i >= 0; i--) { + sb = (c >> i) & 1; + setsda(adap, sb); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timed out */ + bit_dbg(1, &i2c_adap->dev, + "i2c_outb: 0x%02x, timeout at bit #%d\n", + (int)c, i); + return -ETIMEDOUT; + } + /* FIXME do arbitration here: + * if (sb && !getsda(adap)) -> ouch! Get out of here. + * + * Report a unique code, so higher level code can retry + * the whole (combined) message and *NOT* issue STOP. + */ + scllo(adap); + } + sdahi(adap); + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, + "i2c_outb: 0x%02x, timeout at ack\n", (int)c); + return -ETIMEDOUT; + } + + /* 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 = !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"); + + scllo(adap); + return ack; + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata = 0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + sdahi(adap); + for (i = 0; i < 8; i++) { + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, + "i2c_inb: timeout at bit #%d\n", + 7 - i); + return -ETIMEDOUT; + } + indata *= 2; + if (getsda(adap)) + indata |= 0x01; + setscl(adap, 0); + udelay(i == 7 ? adap->udelay / 2 : adap->udelay); + } + /* assert: scl is low */ + return indata; +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_adapter *i2c_adap) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + const char *name = i2c_adap->name; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + 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: SCL is write-only, testing not possible\n", name); + + sda = adap->getsda ? getsda(adap) : 1; + scl = adap->getscl ? getscl(adap) : 1; + if (!scl || !sda) { + pr_warn("%s: bus seems to be busy (scl=%d, sda=%d)\n", name, scl, sda); + goto bailout; + } + + sdalo(adap); + if (adap->getsda && getsda(adap)) { + pr_warn("%s: SDA stuck high!\n", name); + goto bailout; + } + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL unexpected low while pulling SDA low!\n", name); + goto bailout; + } + + sdahi(adap); + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA stuck low!\n", name); + goto bailout; + } + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL unexpected low while pulling SDA high!\n", name); + goto bailout; + } + + scllo(adap); + if (adap->getscl && getscl(adap)) { + pr_warn("%s: SCL stuck high!\n", name); + goto bailout; + } + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA unexpected low while pulling SCL low!\n", name); + goto bailout; + } + + sclhi(adap); + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL stuck low!\n", name); + goto bailout; + } + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA unexpected low while pulling SCL high!\n", name); + goto bailout; + } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + pr_info("%s: Test OK\n", name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret = 0; + + for (i = 0; i <= retries; i++) { + ret = i2c_outb(i2c_adap, addr); + if (ret == 1 || i == retries) + break; + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + udelay(adap->udelay); + yield(); + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + } + if (i && ret) + bit_dbg(1, &i2c_adap->dev, + "Used %d tries to %s client at 0x%02x: %s\n", i + 1, + addr & 1 ? "read from" : "write to", addr >> 1, + ret == 1 ? "success" : "failed, timeout?"); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + const unsigned char *temp = msg->buf; + int count = msg->len; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + int retval; + int wrcount = 0; + + while (count > 0) { + retval = i2c_outb(i2c_adap, *temp); + + /* OK/ACK; or ignored NAK */ + if ((retval > 0) || (nak_ok && (retval == 0))) { + count--; + temp++; + wrcount++; + + /* A slave NAKing the master means the slave didn't like + * something about the data it saw. For example, maybe + * the SMBus PEC was wrong. + */ + } else if (retval == 0) { + dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n"); + return -EIO; + + /* Timeout; or (someday) lost arbitration + * + * FIXME Lost ARB implies retrying the transaction from + * the first message, after the "winning" master issues + * its STOP. As a rule, upper layer code has no reason + * to know or care about this ... it is *NOT* an error. + */ + } else { + dev_err(&i2c_adap->dev, "sendbytes: error %d\n", + retval); + return retval; + } + } + return wrcount; +} + +static int acknak(struct i2c_adapter *i2c_adap, int is_ack) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: sda is high */ + if (is_ack) /* send ack */ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timeout */ + dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n"); + return -ETIMEDOUT; + } + scllo(adap); + return 0; +} + +static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + int inval; + int rdcount = 0; /* counts bytes read */ + 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); + if (inval >= 0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + break; + } + + temp++; + count--; + + /* Some SMBus transactions require that we receive the + transaction length as the first read byte. */ + if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) { + if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) { + if (!(flags & I2C_M_NO_RD_ACK)) + acknak(i2c_adap, 0); + dev_err(&i2c_adap->dev, + "readbytes: invalid block length (%d)\n", + inval); + return -EPROTO; + } + /* The original count value accounts for the extra + bytes, that is, either 1 for a regular transaction, + or 2 for a PEC transaction. */ + count += inval; + msg->len += inval; + } + + bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", + inval, + (flags & I2C_M_NO_RD_ACK) + ? "(no ack/nak)" + : (count ? "A" : "NA")); + + if (!(flags & I2C_M_NO_RD_ACK)) { + inval = acknak(i2c_adap, count); + if (inval < 0) + return inval; + } + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set + * -x an error occurred (like: -ENXIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + unsigned short flags = msg->flags; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret, retries; + + retries = nak_ok ? 0 : i2c_adap->retries; + + if (flags & I2C_M_TEN) { + /* a ten bit address */ + addr = 0xf0 | ((msg->addr >> 7) & 0x06); + bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at extended address code\n"); + return -ENXIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap, msg->addr & 0xff); + if ((ret != 1) && !nak_ok) { + /* the chip did not ack / xmission error occurred */ + dev_err(&i2c_adap->dev, "died at 2nd address code\n"); + return -ENXIO; + } + if (flags & I2C_M_RD) { + bit_dbg(3, &i2c_adap->dev, + "emitting repeated start condition\n"); + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at repeated address code\n"); + return -EIO; + } + } + } else { /* normal 7bit address */ + addr = i2c_8bit_addr_from_msg(msg); + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) + return -ENXIO; + } + + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret; + unsigned short nak_ok; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return ret; + } + + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; + if (!(pmsg->flags & I2C_M_NOSTART)) { + if (i) { + if (msgs[i - 1].flags & I2C_M_STOP) { + bit_dbg(3, &i2c_adap->dev, + "emitting enforced stop/start condition\n"); + i2c_stop(adap); + i2c_start(adap); + } else { + bit_dbg(3, &i2c_adap->dev, + "emitting repeated start condition\n"); + i2c_repstart(adap); + } + } + ret = bit_doAddress(i2c_adap, pmsg); + if ((ret != 0) && !nak_ok) { + bit_dbg(1, &i2c_adap->dev, + "NAK from device addr 0x%02x msg #%d\n", + msgs[i].addr, i); + goto bailout; + } + } + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", + ret, str_plural(ret)); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EIO; + goto bailout; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", + ret, str_plural(ret)); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EIO; + goto bailout; + } + } + } + ret = i; + +bailout: + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + 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_ALL | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +const struct i2c_algorithm i2c_bit_algo = { + .xfer = bit_xfer, + .xfer_atomic = bit_xfer_atomic, + .functionality = bit_func, +}; +EXPORT_SYMBOL(i2c_bit_algo); + +static const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = { + .flags = I2C_AQ_NO_CLK_STRETCH, +}; + +/* + * registering functions to load algorithms at runtime + */ +static int __i2c_bit_add_bus(struct i2c_adapter *adap, + int (*add_adapter)(struct i2c_adapter *)) +{ + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + int ret; + + if (bit_test && !bit_adap->skip_bit_test) { + ret = test_bus(adap); + if (bit_test >= 2 && ret < 0) + return -ENODEV; + } + + /* register new adapter to i2c module... */ + adap->algo = &i2c_bit_algo; + adap->retries = 3; + if (bit_adap->getscl == NULL) + adap->quirks = &i2c_bit_quirk_no_clk_stretch; + + /* + * We tried forcing SCL/SDA to an initial state here. But that caused a + * regression, sadly. Check Bugzilla #200045 for details. + */ + + ret = add_adapter(adap); + if (ret < 0) + return ret; + + 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; +} + +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_bus); + +int i2c_bit_add_numbered_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_numbered_bus); + +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 000000000..ee86df4cf --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -0,0 +1,561 @@ +// 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEB1(fmt, args...) do { if (i2c_debug >= 1) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB2(fmt, args...) do { if (i2c_debug >= 2) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB3(fmt, args...) do { if (i2c_debug >= 3) \ + printk(KERN_DEBUG fmt, ## args); } while (0) + +static int i2c_debug; + +#define pca_outw(adap, reg, val) adap->write_byte(adap->data, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap->data, reg) + +#define pca_status(adap) pca_inw(adap, I2C_PCA_STA) +#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_cb(adap->data) + +static void pca_reset(struct i2c_algo_pca_data *adap) +{ + if (adap->chip == I2C_PCA_CHIP_9665) { + /* Ignore the reset function from the module, + * we can use the parallel bus reset. + */ + 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); + } +} + +/* + * Generate a start condition on the i2c bus. + * + * returns after the start condition has occurred + */ +static int pca_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a repeated start condition on the i2c bus + * + * return after the repeated start condition has occurred + */ +static int pca_repeated_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== REPEATED START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a stop condition on the i2c bus + * + * returns after the stop condition has been generated + * + * STOPs do not generate an interrupt or set the SI flag, since the + * part returns the idle state (0xf8). Hence we don't need to + * pca_wait here. + */ +static void pca_stop(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== STOP\n"); + sta |= I2C_PCA_CON_STO; + sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); +} + +/* + * Send the slave address and R/W bit + * + * returns after the address has been sent + */ +static int pca_address(struct i2c_algo_pca_data *adap, + struct i2c_msg *msg) +{ + int sta = pca_get_con(adap); + int addr = i2c_8bit_addr_from_msg(msg); + + DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", + msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); + + pca_outw(adap, I2C_PCA_DAT, addr); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Transmit a byte. + * + * Returns after the byte has been transmitted + */ +static int pca_tx_byte(struct i2c_algo_pca_data *adap, + __u8 b) +{ + int sta = pca_get_con(adap); + DEB2("=== WRITE %#04x\n", b); + pca_outw(adap, I2C_PCA_DAT, b); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Receive a byte + * + * returns immediately. + */ +static void pca_rx_byte(struct i2c_algo_pca_data *adap, + __u8 *b, int ack) +{ + *b = pca_inw(adap, I2C_PCA_DAT); + DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); +} + +/* + * Setup ACK or NACK for next received byte and wait for it to arrive. + * + * Returns after next byte has arrived. + */ +static int pca_rx_ack(struct i2c_algo_pca_data *adap, + int ack) +{ + int sta = pca_get_con(adap); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); + + if (ack) + sta |= I2C_PCA_CON_AA; + + pca_set_con(adap, sta); + return pca_wait(adap); +} + +static int pca_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pca_data *adap = i2c_adap->algo_data; + struct i2c_msg *msg = NULL; + int curmsg; + int numbytes = 0; + int state; + int ret; + int completed = 1; + unsigned long timeout = jiffies + i2c_adap->timeout; + + while ((state = pca_status(adap)) != 0xf8) { + if (time_before(jiffies, timeout)) { + msleep(10); + } else { + dev_dbg(&i2c_adap->dev, "bus is not idle. status is " + "%#04x\n", state); + return -EBUSY; + } + } + + DEB1("{{{ XFER %d messages\n", num); + + if (i2c_debug >= 2) { + for (curmsg = 0; curmsg < num; curmsg++) { + int addr, i; + msg = &msgs[curmsg]; + + addr = (0x7f & msg->addr) ; + + if (msg->flags & I2C_M_RD) + printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", + curmsg, msg->len, addr, (addr << 1) | 1); + else { + printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", + curmsg, msg->len, addr, addr << 1, + msg->len == 0 ? "" : ", "); + for (i = 0; i < msg->len; i++) + printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); + printk("]\n"); + } + } + } + + curmsg = 0; + ret = -EIO; + while (curmsg < num) { + state = pca_status(adap); + + DEB3("STATE is 0x%02x\n", state); + msg = &msgs[curmsg]; + + switch (state) { + case 0xf8: /* On reset or stop the bus is idle */ + completed = pca_start(adap); + break; + + case 0x08: /* A START condition has been transmitted */ + case 0x10: /* A repeated start condition has been transmitted */ + completed = pca_address(adap, msg); + break; + + case 0x18: /* SLA+W has been transmitted; ACK has been received */ + case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ + if (numbytes < msg->len) { + completed = pca_tx_byte(adap, + msg->buf[numbytes]); + numbytes++; + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+W\n"); + pca_stop(adap); + ret = -ENXIO; + goto out; + + case 0x40: /* SLA+R has been transmitted; ACK has been received */ + completed = pca_rx_ack(adap, msg->len > 1); + break; + + case 0x50: /* Data bytes has been received; ACK has been returned */ + if (numbytes < msg->len) { + pca_rx_byte(adap, &msg->buf[numbytes], 1); + numbytes++; + completed = pca_rx_ack(adap, + numbytes < msg->len - 1); + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+R\n"); + pca_stop(adap); + ret = -ENXIO; + goto out; + + case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after data byte\n"); + pca_stop(adap); + goto out; + + case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ + DEB2("Arbitration lost\n"); + /* + * The PCA9564 data sheet (2006-09-01) says "A + * START condition will be transmitted when the + * bus becomes free (STOP or SCL and SDA high)" + * when the STA bit is set (p. 11). + * + * In case this won't work, try pca_reset() + * instead. + */ + pca_start(adap); + goto out; + + case 0x58: /* Data byte has been received; NOT ACK has been returned */ + if (numbytes == msg->len - 1) { + pca_rx_byte(adap, &msg->buf[numbytes], 0); + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + } else { + DEB2("NOT ACK sent after data byte received. " + "Not final byte. numbytes %d. len %d\n", + numbytes, msg->len); + pca_stop(adap); + goto out; + } + break; + case 0x70: /* Bus error - SDA stuck low */ + DEB2("BUS ERROR - SDA Stuck low\n"); + pca_reset(adap); + goto out; + 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; + case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ + DEB2("BUS ERROR - Illegal START or STOP\n"); + pca_reset(adap); + goto out; + default: + dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state); + break; + } + + if (!completed) + goto out; + } + + ret = curmsg; + out: + DEB1("}}} transferred %d/%d messages. " + "status is %#04x. control is %#04x\n", + curmsg, num, pca_status(adap), + pca_get_con(adap)); + return ret; +} + +static u32 pca_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm pca_algo = { + .xfer = pca_xfer, + .functionality = pca_func, +}; + +static unsigned int pca_probe_chip(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + /* The trick here is to check if there is an indirect register + * available. If there is one, we will read the value we first + * wrote on I2C_PCA_IADR. Otherwise, we will read the last value + * we wrote on I2C_PCA_ADR + */ + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + pca_outw(pca_data, I2C_PCA_IND, 0xAA); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ITO); + pca_outw(pca_data, I2C_PCA_IND, 0x00); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + if (pca_inw(pca_data, I2C_PCA_IND) == 0xAA) { + printk(KERN_INFO "%s: PCA9665 detected.\n", adap->name); + pca_data->chip = I2C_PCA_CHIP_9665; + } else { + printk(KERN_INFO "%s: PCA9564 detected.\n", adap->name); + pca_data->chip = I2C_PCA_CHIP_9564; + } + return pca_data->chip; +} + +static int pca_init(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + + adap->algo = &pca_algo; + + if (pca_probe_chip(adap) == I2C_PCA_CHIP_9564) { + static int freqs[] = {330, 288, 217, 146, 88, 59, 44, 36}; + int clock; + + if (pca_data->i2c_clock > 7) { + switch (pca_data->i2c_clock) { + case 330000: + pca_data->i2c_clock = I2C_PCA_CON_330kHz; + break; + case 288000: + pca_data->i2c_clock = I2C_PCA_CON_288kHz; + break; + case 217000: + pca_data->i2c_clock = I2C_PCA_CON_217kHz; + break; + case 146000: + pca_data->i2c_clock = I2C_PCA_CON_146kHz; + break; + case 88000: + pca_data->i2c_clock = I2C_PCA_CON_88kHz; + break; + case 59000: + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + break; + case 44000: + pca_data->i2c_clock = I2C_PCA_CON_44kHz; + break; + case 36000: + pca_data->i2c_clock = I2C_PCA_CON_36kHz; + break; + default: + printk(KERN_WARNING + "%s: Invalid I2C clock speed selected." + " Using default 59kHz.\n", adap->name); + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + } + } else { + printk(KERN_WARNING "%s: " + "Choosing the clock frequency based on " + "index is deprecated." + " Use the nominal frequency.\n", adap->name); + } + + clock = pca_clock(pca_data); + printk(KERN_INFO "%s: Clock frequency is %dkHz\n", + adap->name, freqs[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; + int tlow, thi; + /* Values can be found on PCA9665 datasheet section 7.3.2.6 */ + int min_tlow, min_thi; + /* These values are the maximum raise and fall values allowed + * by the I2C operation mode (Standard, Fast or Fast+) + * They are used (added) below to calculate the clock dividers + * of PCA9665. Note that they are slightly different of the + * real maximum, to allow the change on mode exactly on the + * maximum clock rate for each mode + */ + int raise_fall_time; + + if (pca_data->i2c_clock > 1265800) { + printk(KERN_WARNING "%s: I2C clock speed too high." + " Using 1265.8kHz.\n", adap->name); + pca_data->i2c_clock = 1265800; + } + + if (pca_data->i2c_clock < 60300) { + printk(KERN_WARNING "%s: I2C clock speed too low." + " Using 60.3kHz.\n", adap->name); + pca_data->i2c_clock = 60300; + } + + /* To avoid integer overflow, use clock/100 for calculations */ + clock = pca_clock(pca_data) / 100; + + 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 > 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 > I2C_MAX_STANDARD_MODE_FREQ) { + mode = I2C_PCA_MODE_FAST; + min_tlow = 44; + min_thi = 20; + raise_fall_time = 58; /* Raise 29e-8s, Fall 29e-8s */ + } else { + mode = I2C_PCA_MODE_STD; + min_tlow = 157; + min_thi = 134; + raise_fall_time = 127; /* Raise 29e-8s, Fall 98e-8s */ + } + + /* The minimum clock that respects the thi/tlow = 134/157 is + * 64800 Hz. Below that, we have to fix the tlow to 255 and + * calculate the thi factor. + */ + if (clock < 648) { + tlow = 255; + thi = 1000000 - clock * raise_fall_time; + thi /= (I2C_PCA_OSC_PER * clock) - tlow; + } else { + tlow = (1000000 - clock * raise_fall_time) * min_tlow; + tlow /= I2C_PCA_OSC_PER * clock * (min_thi + min_tlow); + 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); + } + udelay(500); /* 500 us for oscillator to stabilise */ + + return 0; +} + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pca_add_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_bus); + +int i2c_pca_add_numbered_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_numbered_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_numbered_bus); + +MODULE_AUTHOR("Ian Campbell "); +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("I2C-Bus PCA9564/PCA9665 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, 0); diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c new file mode 100644 index 000000000..a87ecea7f --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -0,0 +1,369 @@ +// 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 + * + * With some changes from Kyösti Mälkki and + * Frodo Looijaard , and also from Martin Bailey + * + * + * Partially rewriten by Oleg I. Vdovikin to handle multiple + * messages, proper stop/repstart signaling during receive, added detect code + */ + +#include +#include +#include +#include +#include +#include +#include +#include "i2c-algo-pcf.h" + + +#define DEF_TIMEOUT 16 + +/* setting states on the bus with the right timing: */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + +/* other auxiliary functions */ + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_STOP); +} + +static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status) +{ + /* + * Cleanup from LAB -- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + /* + * We pause for a time period sufficient for any running + * I2C transaction to complete -- the arbitration logic won't + * work properly until the next START is seen. + * It is assumed the bus driver or client has set a proper value. + * + * REVISIT: should probably use msleep instead of mdelay if we + * know we can sleep. + */ + if (adap->lab_mdelay) + mdelay(adap->lab_mdelay); + +} + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) +{ + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + + while (!(status & I2C_PCF_BB) && --timeout) { + udelay(100); /* wait for 100 us */ + status = get_pcf(adap, 1); + } + + if (timeout == 0) { + printk(KERN_ERR "Timeout waiting for Bus Busy\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) +{ + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + + while ((*status & I2C_PCF_PIN) && --timeout) { + adap->waitforpin(adap->data); + *status = get_pcf(adap, 1); + } + if (*status & I2C_PCF_LAB) { + handle_lab(adap, status); + return -EINTR; + } + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + * + * vdovikin: added detect code for PCF8584 + */ +static int pcf_init_8584(struct i2c_algo_pcf_data *adap) +{ + unsigned char temp; + + /* 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 + */ + 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 */ + 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 */ + 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 */ + 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 */ + 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"); + + return 0; +} + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount = 0; wrcount < count; ++wrcount) { + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - no ack.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + } + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + + return wrcount; +} + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, + int count, int last) +{ + int i, status; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wfp; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + + wfp = wait_for_pin(adap, &status); + if (wfp) { + if (wfp == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n"); + return -1; + } + + if ((status & I2C_PCF_LRB) && (i != count)) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_read: i2c_inb, No ack.\n"); + return -1; + } + + if (i == count - 1) { + set_pcf(adap, 1, I2C_PCF_ESO); + } else if (i == count) { + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + } + + if (i) + buf[i - 1] = i2c_inb(adap); + else + i2c_inb(adap); /* dummy read */ + } + + return i - 1; +} + + +static void pcf_send_address(struct i2c_algo_pcf_data *adap, + struct i2c_msg *msg) +{ + unsigned char addr = i2c_8bit_addr_from_msg(msg); + + if (msg->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + i2c_outb(adap, addr); +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i; + int timeout, status; + + if (adap->xfer_begin) + adap->xfer_begin(adap->data); + + /* Check for bus busy */ + timeout = wait_for_bb(adap); + if (timeout) { + i = -EIO; + goto out; + } + + for (i = 0; i < num; i++) { + int ret; + + pmsg = &msgs[i]; + pcf_send_address(adap, pmsg); + + /* Send START */ + if (i == 0) + i2c_start(adap); + + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + i = -EINTR; + goto out; + } + i2c_stop(adap); + i = -EREMOTEIO; + goto out; + } + + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + i = -EREMOTEIO; + goto out; + } + + + if (pmsg->flags & I2C_M_RD) { + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + } else { + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + } + + if (ret < 0) + goto out; + } + +out: + if (adap->xfer_end) + adap->xfer_end(adap->data); + return i; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* exported algorithm data: */ +static const struct i2c_algorithm pcf_algo = { + .xfer = pcf_xfer, + .functionality = pcf_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + int rval; + + /* register new adapter to i2c module... */ + adap->algo = &pcf_algo; + + rval = pcf_init_8584(pcf_adap); + if (rval) + return rval; + + rval = i2c_add_adapter(adap); + + return rval; +} +EXPORT_SYMBOL(i2c_pcf_add_bus); + +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h new file mode 100644 index 000000000..2448ab192 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif /* I2C_PCF8584_H */ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig new file mode 100644 index 000000000..d7b895083 --- /dev/null +++ b/drivers/i2c/busses/Kconfig @@ -0,0 +1,1605 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Sensor device configuration +# + +menu "I2C Hardware Bus support" + depends on HAS_IOMEM + +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 && 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 + controller is part of the 7101 device, which is an ACPI-compliant + Power Management Unit (PMU). + + This driver can also be built as a module. If so, the module + will be called i2c-ali1535. + +config I2C_ALI1563 + tristate "ALI 1563" + 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 + controller is part of the 7101 device, which is an ACPI-compliant + Power Management Unit (PMU). + + This driver can also be built as a module. If so, the module + will be called i2c-ali1563. + +config I2C_ALI15X3 + tristate "ALI 15x3" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-ali15x3. + +config I2C_AMD756 + tristate "AMD 756/766/768/8111 and nVidia nForce" + 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 + support for the first (SMBus 1.0) I2C interface of the AMD 8111 and + the nVidia nForce I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-amd756. + +config I2C_AMD8111 + tristate "AMD 8111" + 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. + + 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 + help + Say Y here to include support for the high-speed I2C controller + used in HiSilicon hix5hd2 SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-hix5hd2. + +config I2C_I801 + tristate "Intel 82801 (ICH/PCH)" + depends on PCI && HAS_IOPORT + select P2SB if X86 + select CHECK_SIGNATURE if X86 && DMI + select I2C_SMBUS + help + If you say yes to this option, support will be included for the Intel + 801 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset are supported: + 82801AA + 82801AB + 82801BA + 82801CA/CAM + 82801DB + 82801EB/ER (ICH5/ICH5R) + 6300ESB + ICH6 + ICH7 + ESB2 + ICH8 + ICH9 + EP80579 (Tolapai) + ICH10 + 5/3400 Series (PCH) + 6 Series (PCH) + Patsburg (PCH) + DH89xxCC (PCH) + Panther Point (PCH) + Lynx Point (PCH) + Avoton (SOC) + Wellsburg (PCH) + Coleto Creek (PCH) + Wildcat Point (PCH) + BayTrail (SOC) + Braswell (SOC) + Sunrise Point (PCH) + Kaby Lake (PCH) + DNV (SOC) + Broxton (SOC) + Lewisburg (PCH) + Gemini Lake (SOC) + 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) + Nova Lake (PCH) + + 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 && HAS_IOPORT + select LPC_SCH + help + Say Y here if you want to use SMBus controller on the Intel SCH + based systems. + + This driver can also be built as a module. If so, the module + will be called i2c-isch. + +config I2C_ISMT + tristate "Intel iSMT SMBus Controller" + depends on PCI && X86 + help + If you say yes to this option, support will be included for the Intel + iSMT SMBus host controller interface. + + This driver can also be built as a module. If so, the module will be + called i2c-ismt. + +config I2C_PIIX4 + tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" + 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 + versions of the chipset are supported (note that Serverworks is part + of Broadcom): + Intel PIIX4 + Intel 440MX + ATI IXP200 + ATI IXP300 + ATI IXP400 + ATI SB600 + ATI SB700/SP5100 + ATI SB800 + AMD Hudson-2 + AMD ML + AMD CZ + Hygon CZ + Serverworks OSB4 + Serverworks CSB5 + Serverworks CSB6 + Serverworks HT-1000 + Serverworks HT-1100 + SMSC Victory66 + + Some AMD chipsets contain two PIIX4-compatible SMBus + controllers. This driver will attempt to use both controllers + on the SB700/SP5100, if they have been initialized by the BIOS. + + This driver can also be built as a module. If so, the module + will be called i2c-piix4. + +config I2C_CHT_WC + tristate "Intel Cherry Trail Whiskey Cove PMIC smbus controller" + depends on INTEL_SOC_PMIC_CHTWC + help + If you say yes to this option, support will be included for the + SMBus controller found in the Intel Cherry Trail Whiskey Cove PMIC + found on some Intel Cherry Trail systems. + + Note this controller is hooked up to a TI bq24292i charger-IC, + combined with a FUSB302 Type-C port-controller as such it is advised + to also select CONFIG_TYPEC_FUSB302=m. + +config I2C_NFORCE2 + tristate "Nvidia nForce2, nForce3 and nForce4" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-nforce2. + +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 + Type-C controller. This driver can also be built as a module called + i2c-nvidia-gpu. + +config I2C_SIS5595 + tristate "SiS 5595" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-sis5595. + +config I2C_SIS630 + tristate "SiS 630/730/964" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-sis630. + +config I2C_SIS96X + tristate "SiS 96x" + 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 + chipsets are supported: + 645/961 + 645DX/961 + 645DX/962 + 648/961 + 650/961 + 735 + 745 + + This driver can also be built as a module. If so, the module + will be called i2c-sis96x. + +config I2C_VIA + tristate "VIA VT82C586B" + 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 + + 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 && 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 + chipsets are supported: + VT82C596A/B + VT82C686A/B + VT8231 + VT8233/A + VT8235 + VT8237R/A/S + VT8251 + CX700 + VX800/VX820 + VX855/VX875 + VX900 + + This driver can also be built as a module. If so, the module + will be called 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 + tristate "SMBus Control Method Interface" + help + This driver supports the SMBus Control Method Interface. It needs the + BIOS to declare ACPI control methods as described in the SMBus Control + Method Interface specification. + + To compile this driver as a module, choose M here: + the module will be called i2c-scmi. + +endif # ACPI + +comment "Mac SMBus host controller drivers" + depends on PPC_CHRP || PPC_PMAC + +config I2C_HYDRA + tristate "CHRP Apple Hydra Mac I/O I2C interface" + depends on PCI && PPC_CHRP + select I2C_ALGOBIT + help + This supports the use of the I2C interface in the Apple Hydra Mac + I/O chip on some CHRP machines (e.g. the LongTrail). Say Y if you + have such a machine. + + This support is also available as a module. If so, the module + will be called i2c-hydra. + +config I2C_POWERMAC + tristate "Powermac I2C interface" + depends on PPC_PMAC + default y + help + This exposes the various PowerMac i2c interfaces to the linux i2c + layer and to userland. It is used by various drivers on the PowerMac + platform, and should generally be enabled. + + This support is also available as a module. If so, the module + will be called i2c-powermac. + +comment "I2C system bus drivers (mostly embedded / system-on-chip)" + +config I2C_ALTERA + tristate "Altera Soft IP I2C" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-altera. + +config I2C_ASPEED + tristate "Aspeed I2C Controller" + depends on ARCH_ASPEED || COMPILE_TEST + help + If you say yes to this option, support will be included for the + Aspeed I2C controller. + + This driver can also be built as a module. If so, the module + will be called i2c-aspeed. + +config I2C_AT91 + tristate "Atmel AT91 I2C Two-Wire interface (TWI)" + depends on ARCH_MICROCHIP || MCHP_LAN966X_PCI || COMPILE_TEST + help + This supports the use of the I2C interface on Atmel AT91 + processors. + + A serious problem is that there is no documented way to issue + repeated START conditions for more than two messages, as needed + to support combined I2C messages. Use the i2c-gpio driver + unless your system can cope with this limitation. + + Caution! at91rm9200, at91sam9261, at91sam9260, at91sam9263 devices + don't have clock stretching in transmission mode. For that reason, + you can encounter underrun issues causing premature stop sendings if + 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 + help + If you say yes to this option, support will be included for the + Au1550/Au1200/Au1300 SMBus interface. + + This driver can also be built as a module. If so, the module + will be called i2c-au1550. + +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. + + Please note that this controller is limited to transfers of maximum + 255 bytes in length. Any attempt to to a larger transfer will return + an error. + +config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" + 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. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called i2c-bcm2835. + +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. + + If you don't know what to do here, say N. + +config I2C_BCM_KONA + tristate "BCM Kona I2C adapter" + 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. + + If you do not need KONA I2C interface, say N. + +config I2C_BRCMSTB + tristate "BRCM Settop/DSL I2C controller" + 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. + + If you do not need I2C interface, say N. + +config I2C_CADENCE + tristate "Cadence I2C Controller" + 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. + +config I2C_CBUS_GPIO + tristate "CBUS I2C driver" + depends on GPIOLIB || COMPILE_TEST + help + Support for CBUS access using I2C API. Mostly relevant for Nokia + Internet Tablets (770, N800 and N810). + + 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 + help + This supports the use of the I2C interface on Freescale + processors with CPM1 or CPM2. + + This driver can also be built as a module. If so, the module + will be called i2c-cpm. + +config I2C_DAVINCI + tristate "DaVinci I2C driver" + depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST + help + Support for TI DaVinci I2C controller driver. + + This driver can also be built as a module. If so, the module + will be called i2c-davinci. + + Please note that this driver might be needed to bring up other + devices such as DaVinci NIC. + For details please see http://www.ti.com/davinci + +config I2C_DESIGNWARE_CORE + 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, and the I2C slave when enabled (select + I2C_SLAVE). + + 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_PLATFORM + tristate "Synopsys DesignWare Platform driver" + depends on (ACPI && COMMON_CLK) || !ACPI + default I2C_DESIGNWARE_CORE + help + If you say yes to this option, support will be included for the + 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_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 + AMDISP Synopsys DesignWare I2C adapter. + + This driver can also be built as a module. If so, the module + will be called amd_isp_i2c_designware. + +config I2C_DESIGNWARE_AMDPSP + bool "AMD PSP I2C semaphore support" + depends on ACPI + depends on CRYPTO_DEV_SP_PSP + depends on PCI + 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 + This driver enables managed host access to the selected I2C bus shared + between AMD CPU and AMD PSP. + + 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 + This driver enables managed host access to the PMIC I2C bus on select + Intel BayTrail platforms using the X-Powers AXP288 PMIC. It allows + the host to request uninterrupted access to the PMIC's I2C bus from + 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 || 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_EG20T + tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) + help + This driver is for PCH(Platform controller Hub) I2C of EG20T which + is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH I2C bus device. + + This driver also can be used for LAPIS Semiconductor IOH(Input/ + Output Hub), ML7213, ML7223 and ML7831. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is + for MP(Media Phone) use and ML7831 IOH is for general purpose use. + ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. + +config I2C_EMEV2 + tristate "EMMA Mobile series I2C adapter" + depends on HAVE_CLK + select I2C_SLAVE + help + If you say yes to this option, support will be included for the + I2C interface on the Renesas Electronics EM/EV family of processors. + +config I2C_EXYNOS5 + 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 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" + depends on GPIOLIB || COMPILE_TEST + select I2C_ALGOBIT + help + This is a very simple bitbanging I2C driver utilizing the + arch-neutral GPIO API to control the SCL and SDA lines. + +config I2C_GPIO_FAULT_INJECTOR + bool "GPIO-based fault injector" + depends on I2C_GPIO + help + This adds some functionality to the i2c-gpio driver which can inject + 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 || 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 + boards, particularly the R0P7780LC0011RL and R0P7785LC0011RL + FPGAs. This is wholly unrelated to the SoC I2C. + + 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 + help + Say Y here if you want to use IIC peripheral found on + embedded IBM PPC 4xx based systems. + + This driver can also be built as a module. If so, the module + will be called i2c-ibm_iic. + +config I2C_IMG + tristate "Imagination Technologies I2C SCB Controller" + depends on MIPS || COMPILE_TEST + help + Say Y here if you want to use the IMG I2C SCB controller, + available on the TZ1090 and other IMG SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-img-scb. + +config I2C_IMX + tristate "IMX I2C interface" + 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/S32G, Layerscape or ColdFire processors. + + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-imx-lpi2c. + +config I2C_IOP3XX + 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 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 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 && COMMON_CLK + default ARCH_SPACEMIT + 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 + help + This enables support for the I2C bus interface on some Kontron ETX + and COMexpress (ETXexpress) modules. + + This driver can also be built as a module. If so, the module + will be called i2c-kempld. + +config I2C_LPC2K + tristate "I2C bus support for NXP LPC2K/LPC178x/18xx/43xx" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) + help + This driver supports the I2C interface found several NXP + devices including LPC2xxx, LPC178x/7x and LPC18xx/43xx. + + 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_LS2X_V2 + tristate "Loongson-2 Fast Speed I2C adapter" + depends on LOONGARCH || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes to this option, support will be included for the + I2C interface on the Loongson-2K0300 SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-ls2x-v2. + +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 + help + If you say yes to this option, support will be included for the + built-in I2C interface on the MPC107, Tsi107, MPC512x, MPC52xx, + MPC8240, MPC8245, MPC83xx, MPC85xx and MPC8641 family processors. + + This driver can also be built as a module. If so, the module + will be called i2c-mpc. + +config I2C_MT65XX + tristate "MediaTek I2C adapter" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This selects the MediaTek(R) Integrated Inter Circuit bus driver + for MT65xx and MT81xx. + 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 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. + This driver is also used for Allwinner SoCs I2C controllers. + + This driver can also be built as a module. If so, the module + will be called i2c-mv64xxx. + +config I2C_MXS + tristate "Freescale i.MX28 I2C interface" + depends on SOC_IMX28 || COMPILE_TEST + select STMP_DEVICE + help + Say Y here if you want to use the I2C bus controller on + the Freescale i.MX28 processors. + + This driver can also be built as a module. If so, the module + will be called i2c-mxs. + +config I2C_NOMADIK + tristate "ST-Ericsson Nomadik/Ux500 I2C Controller" + 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 + If you say yes to this option, support will be included for the + OpenCores I2C controller. For details see + http://www.opencores.org/projects.cgi/web/i2c/overview + + This driver can also be built as a module. If so, the module + will be called i2c-ocores. + +config I2C_OMAP + tristate "OMAP I2C adapter" + 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. + Like OMAP1510/1610/1710/5912 and OMAP242x. + For details see http://www.ti.com/omap. + +config I2C_OWL + tristate "Actions Semiconductor Owl I2C Controller" + depends on ARCH_ACTIONS || COMPILE_TEST + help + Say Y here if you want to use the I2C bus controller on + the Actions Semiconductor Owl SoC's. + +config I2C_PASEMI + tristate "PA Semi SMBus interface" + depends on PPC_PASEMI && PCI + 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 + help + This driver supports a memory mapped Philips PCA9564/PCA9665 + parallel bus to I2C bus controller. + + This driver can also be built as a module. If so, the module + will be called i2c-pca-platform. + +config I2C_PNX + tristate "I2C bus support for Philips PNX and NXP LPC targets" + depends on ARCH_LPC32XX || COMPILE_TEST + help + This driver supports the Philips IP3204 I2C IP block master and/or + slave controller + + This driver can also be built as a module. If so, the module + will be called i2c-pnx. + +config I2C_PXA + tristate "Intel PXA2XX I2C adapter" + 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 + will be called i2c-pxa. + +config I2C_PXA_PCI + def_bool I2C_PXA && X86_32 && PCI && OF + +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 GENI based I2C controller" + depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_GENI_SE + help + This driver supports GENI serial engine based I2C controller in + master mode on the Qualcomm Technologies Inc.'s SoCs. If you say + yes to this option, support will be included for the built-in I2C + interface on the Qualcomm Technologies Inc.'s SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-qcom-geni. + +config I2C_QUP + tristate "Qualcomm QUP based I2C controller" + 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. + + This driver can also be built as a module. If so, the module + will be called i2c-qup. + +config I2C_RIIC + tristate "Renesas RIIC adapter" + depends on ARCH_RENESAS || COMPILE_TEST + help + If you say yes to this option, support will be included for the + Renesas RIIC I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-riic. + +config I2C_RK3X + tristate "Rockchip RK3xxx I2C adapter" + depends on OF && COMMON_CLK + help + Say Y here to include support for the I2C adapter in Rockchip RK3xxx + SoCs. + + This driver can also be built as a module. If so, the module will + be called i2c-rk3x. + +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 + 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 "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 (S3C, S5Pv210, Exynos). + +config I2C_SH7760 + tristate "Renesas SH7760 I2C Controller" + depends on CPU_SUBTYPE_SH7760 + help + This driver supports the 2 I2C interfaces on the Renesas SH7760. + + This driver can also be built as a module. If so, the module + will be called i2c-sh7760. + +config I2C_SH_MOBILE + tristate "SuperH Mobile I2C Controller" + depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST + help + If you say yes to this option, support will be included for the + built-in I2C interface on the Renesas SH-Mobile processor. + + This driver can also be built as a module. If so, the module + will be called i2c-sh_mobile. + +config I2C_SIMTEC + tristate "Simtec Generic I2C interface" + select I2C_ALGOBIT + help + If you say yes to this option, support will be included for + the Simtec Generic I2C interface. This driver is for the + simple I2C bus used on newer Simtec products for general + I2C, such as DDC on the Simtec BBD2016A. + + This driver can also be built as a module. If so, the module + will be called i2c-simtec. + +config I2C_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 || COMPILE_TEST + help + Enable this option to add support for STMicroelectronics SoCs + hardware SSC (Synchronous Serial Controller) as an I2C controller. + + This driver can also be built as module. If so, the module + will be called i2c-st. + +config I2C_STM32F4 + tristate "STMicroelectronics STM32F4 I2C support" + depends on ARCH_STM32 || COMPILE_TEST + help + Enable this option to add support for STM32 I2C controller embedded + in STM32F4 SoCs. + + This driver can also be built as module. If so, the module + will be called i2c-stm32f4. + +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. + + This driver can also be built as module. If so, the module + will be called i2c-stm32f7. + +config I2C_SUN6I_P2WI + tristate "Allwinner sun6i internal P2WI controller" + depends on RESET_CONTROLLER + depends on MACH_SUN6I || COMPILE_TEST + help + If you say yes to this option, support will be included for the + P2WI (Push/Pull 2 Wire Interface) controller embedded in some sunxi + SOCs. + The P2WI looks like an SMBus controller (which supports only byte + accesses), except that it only supports one slave device. + This interface is used to connect to specific PMIC devices (like the + AXP221). + +config I2C_SYNQUACER + tristate "Socionext SynQuacer I2C controller" + depends on ARCH_SYNQUACER || COMPILE_TEST + help + Say Y here to include support for the I2C controller used in some + Fujitsu and Socionext SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-synquacer. + +config I2C_TEGRA + tristate "NVIDIA Tegra internal I2C controller" + depends on ARCH_TEGRA || COMPILE_TEST + depends on PINCTRL + # ARCH_TEGRA implies PINCTRL, but the COMPILE_TEST side doesn't. + 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 || 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. + + This I2C driver is a 'virtual' I2C driver. The real driver is part + of the BPMP firmware, and this driver merely communicates with that + real driver. + +config I2C_UNIPHIER + tristate "UniPhier FIFO-less I2C controller" + depends on ARCH_UNIPHIER || COMPILE_TEST + help + If you say yes to this option, support will be included for + the UniPhier FIFO-less I2C interface embedded in PH1-LD4, PH1-sLD8, + or older UniPhier SoCs. + +config I2C_UNIPHIER_F + tristate "UniPhier FIFO-builtin I2C controller" + depends on ARCH_UNIPHIER || COMPILE_TEST + help + If you say yes to this option, support will be included for + the UniPhier FIFO-builtin I2C interface embedded in PH1-Pro4, + PH1-Pro5, or newer UniPhier SoCs. + +config I2C_VERSATILE + tristate "ARM Versatile/Realview I2C bus support" + depends on ARCH_MPS2 || ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST + select I2C_ALGOBIT + help + Say yes if you want to support the I2C serial bus on ARMs Versatile + range of platforms. + + This driver can also be built as a module. If so, the module + will be called i2c-versatile. + +config I2C_WMT + tristate "Wondermedia WM8xxx SoC I2C bus support" + depends on ARCH_VT8500 || COMPILE_TEST + help + Say yes if you want to support the I2C bus on Wondermedia 8xxx-series + SoCs. + + This driver can also be built as a module. If so, the module will be + called i2c-wmt. + +config I2C_OCTEON + tristate "Cavium OCTEON I2C bus support" + depends on CAVIUM_OCTEON_SOC + help + Say yes if you want to support the I2C serial bus on Cavium + OCTEON SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-octeon. + +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && (ARM64 || COMPILE_TEST) + select I2C_SMBUS + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + +config I2C_XILINX + tristate "Xilinx I2C Controller" + depends on HAS_IOMEM + help + If you say yes to this option, support will be included for the + Xilinx I2C controller. + + This driver can also be built as a module. If so, the module + will be called xilinx_i2c. + +config I2C_XLP9XX + tristate "Cavium ThunderX2 I2C support" + depends on ARCH_THUNDER2 || COMPILE_TEST + help + This driver enables support for the on-chip I2C interface of + 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. + +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. + + This driver can also be built as a module. If so, the module + will be called i2c-rcar. + +comment "External I2C/SMBus adapter drivers" + +config I2C_DIOLAN_U2C + tristate "Diolan U2C-12 USB adapter" + depends on USB + help + If you say yes to this option, support will be included for Diolan + U2C-12, a USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-diolan-u2c. + +config I2C_DLN2 + 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_LJCA + tristate "I2C functionality of Intel La Jolla Cove Adapter" + depends on USB_LJCA + default USB_LJCA + help + 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. + +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. + + This driver can also be built as a module. If so, the module will + be called i2c-nct6694. + +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 driver can also be built as a module. If so, the module + will be called i2c_usbio. + +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. Please + read the file Documentation/i2c/busses/i2c-parport.rst for details. + + This support is also available as a module. If so, the module + will be called i2c-parport. + +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 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" + depends on USB + help + If you say yes to this option, support will be included for the + RobotFuzz Open Source InterFace USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-osif. + +config I2C_TAOS_EVM + tristate "TAOS evaluation module" + depends on TTY + select SERIO + select SERIO_SERPORT + help + This supports TAOS evaluation modules on serial port. In order to + use this driver, you will need the inputattach tool, which is part + of the input-utils package. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called i2c-taos-evm. + +config I2C_TINY_USB + tristate "Tiny-USB adapter" + depends on USB + help + If you say yes to this option, support will be included for the + i2c-tiny-usb, a simple do-it-yourself USB to I2C interface. See + http://www.harbaum.org/till/i2c_tiny_usb for hardware details. + + This driver can also be built as a module. If so, the module + will be called i2c-tiny-usb. + +config I2C_VIPERBOARD + tristate "Viperboard I2C master support" + depends on MFD_VIPERBOARD && USB + 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. + +comment "Other I2C/SMBus bus drivers" + +config I2C_ACORN + tristate "Acorn IOC/IOMD I2C bus support" + depends on ARCH_ACORN + default y + select I2C_ALGOBIT + help + Say yes if you want to support the I2C bus on Acorn platforms. + + If you don't know, say Y. + +config I2C_ELEKTOR + tristate "Elektor ISA card" + depends on ISA && HAS_IOPORT_MAP + select I2C_ALGOPCF + help + This supports the PCF8584 ISA bus I2C adapter. Say Y if you own + such an adapter. + + 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 || (ARM64 && ACPI) || COMPILE_TEST + depends on HAS_IOPORT + help + This exposes the Mellanox platform I2C busses to the linux I2C layer + 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 + called as i2c-mlxcpld. + +config I2C_PCA_ISA + tristate "PCA9564/PCA9665 on an ISA bus" + depends on ISA + select I2C_ALGOPCA + help + This driver supports ISA boards using the Philips PCA9564/PCA9665 + parallel bus to I2C bus controller. + + This driver can also be built as a module. If so, the module + will be called i2c-pca-isa. + + This device is almost undetectable and using this driver on a + system which doesn't have this device will result in long + delays when I2C/SMBus chip drivers are loaded (e.g. at boot + time). If unsure, say N. + +config I2C_SIBYTE + tristate "SiByte SMBus interface" + depends on SIBYTE_SB1xxx_SOC + help + Supports the SiByte SOC on-chip I2C interfaces (2 channels). + +config I2C_CROS_EC_TUNNEL + tristate "ChromeOS EC tunnel I2C bus" + 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 + connected there. This will work whatever the interface used to + talk to the EC (SPI, I2C or LPC). + +config I2C_XGENE_SLIMPRO + tristate "APM X-Gene SoC I2C SLIMpro devices support" + depends on ARCH_XGENE && MAILBOX + help + Enable I2C bus access using the APM X-Gene SoC SLIMpro + co-processor. The I2C device access the I2C bus via the X-Gene + to SLIMpro (On chip coprocessor) mailbox mechanism. + If unsure, say N. + +config SCx200_ACB + tristate "Geode ACCESS.bus support" + 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. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called scx200_acb. + +config I2C_OPAL + tristate "IBM OPAL I2C driver" + depends on PPC_POWERNV + default y + help + This exposes the PowerNV platform i2c busses to the linux i2c layer, + the driver is based on the OPAL interfaces. + + This driver can also be built as a module. If so, the module will be + called as i2c-opal. + +config I2C_FSI + tristate "FSI I2C driver" + depends on FSI + help + Driver for FSI bus attached I2C masters. These are I2C masters that + are connected to the system over an FSI bus, instead of the more + common PCI or MMIO interface. + + 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 new file mode 100644 index 000000000..3755c54b3 --- /dev/null +++ b/drivers/i2c/busses/Makefile @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the i2c bus drivers. +# + +# 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_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_NVIDIA_GPU) += i2c-nvidia-gpu.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o +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 +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-y := i2c-designware-common.o +i2c-designware-core-y += i2c-designware-master.o +i2c-designware-core-$(CONFIG_I2C_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_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_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 +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_LS2X_V2) += i2c-ls2x-v2.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-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_PNX) += i2c-pnx.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_SPRD) += i2c-sprd.o +obj-$(CONFIG_I2C_ST) += i2c-st.o +obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o +obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.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 +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-viai2c-wmt.o i2c-viai2c-common.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.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_XLP9XX) += i2c-xlp9xx.o +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.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_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 +obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o + +# Other I2C/SMBus bus drivers +obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o +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 +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 new file mode 100644 index 000000000..34bbbb8c5 --- /dev/null +++ b/drivers/i2c/busses/i2c-acorn.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ARM IOC/IOMD i2c driver. + * + * Copyright (C) 2000 Russell King + * + * On Acorn machines, the following i2c devices are on the bus: + * - PCF8583 real time clock & static RAM + */ +#include +#include +#include +#include + +#include +#include + +#define FORCE_ONES 0xdc +#define SCL 0x02 +#define SDA 0x01 + +/* + * We must preserve all non-i2c output bits in IOC_CONTROL. + * Note also that we need to preserve the value of SCL and + * SDA outputs as well (which may be different from the + * values read back from IOC_CONTROL). + */ +static u_int force_ones; + +static void ioc_setscl(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SCL; + else + ones &= ~SCL; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static void ioc_setsda(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SDA; + else + ones &= ~SDA; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static int ioc_getscl(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SCL) != 0; +} + +static int ioc_getsda(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SDA) != 0; +} + +static struct i2c_algo_bit_data ioc_data = { + .setsda = ioc_setsda, + .setscl = ioc_setscl, + .getsda = ioc_getsda, + .getscl = ioc_getscl, + .udelay = 80, + .timeout = HZ, +}; + +static struct i2c_adapter ioc_ops = { + .nr = 0, + .name = "ioc", + .algo_data = &ioc_data, +}; + +static int __init i2c_ioc_init(void) +{ + force_ones = FORCE_ONES | SCL | SDA; + + return i2c_bit_add_numbered_bus(&ioc_ops); +} + +module_init(i2c_ioc_init); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("ARM IOC/IOMD i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c new file mode 100644 index 000000000..1eac35838 --- /dev/null +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Frodo Looijaard , + * Philip Edelbrock , + * Mark D. Studebaker , + * Dan Eaton and + * Stephen Rousset +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1535 South Bridge. + + The M1535 is a South bridge for portable systems. + It is very similar to the M15x3 South bridges also produced + by Acer Labs Inc. Some of the registers within the part + have moved and some have been redefined slightly. Additionally, + the sequencing of the SMBus transactions has been modified + to be more consistent with the sequencing recommended by + the manufacturer and observed through testing. These + changes are reflected in this driver and can be identified + by comparing this driver to the i2c-ali15x3 driver. + For an overview of these chips see http://www.acerlabs.com + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + + This driver does not use interrupts. +*/ + + +/* Note: we assume there can only be one ALI1535, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ALI1535 SMBus address offsets */ +#define SMBHSTSTS (0 + ali1535_smba) +#define SMBHSTTYP (1 + ali1535_smba) +#define SMBHSTPORT (2 + ali1535_smba) +#define SMBHSTCMD (7 + ali1535_smba) +#define SMBHSTADD (3 + ali1535_smba) +#define SMBHSTDAT0 (4 + ali1535_smba) +#define SMBHSTDAT1 (5 + ali1535_smba) +#define SMBBLKDAT (6 + ali1535_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBREV 0x008 +#define SMBCFG 0x0D1 +#define SMBBA 0x0E2 +#define SMBHSTCFG 0x0F0 +#define SMBCLK 0x0F2 + +/* Other settings */ +#define MAX_TIMEOUT 500 /* times 1/100 sec */ +#define ALI1535_SMB_IOSIZE 32 + +#define ALI1535_SMB_DEFAULTBASE 0x8040 + +/* ALI1535 address lock bits */ +#define ALI1535_LOCK 0x06 /* dwe */ + +/* ALI1535 command constants */ +#define ALI1535_QUICK 0x00 +#define ALI1535_BYTE 0x10 +#define ALI1535_BYTE_DATA 0x20 +#define ALI1535_WORD_DATA 0x30 +#define ALI1535_BLOCK_DATA 0x40 +#define ALI1535_I2C_READ 0x60 + +#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ + /* I2C read */ +#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ +#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define ALI1535_KILL 0x04 /* Kill Command (write) */ +#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ + +#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ + /* of 10-bit address in I2C */ + /* Read Command */ + +/* ALI1535 status register bits */ +#define ALI1535_STS_IDLE 0x04 +#define ALI1535_STS_BUSY 0x08 /* host busy */ +#define ALI1535_STS_DONE 0x10 /* transaction complete */ +#define ALI1535_STS_DEV 0x20 /* device error */ +#define ALI1535_STS_BUSERR 0x40 /* bus error */ +#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ +#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ + +#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ + +/* ALI1535 device address register bits */ +#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ + /* Address field */ + /* -> Write = 0 */ + /* -> Read = 1 */ +#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ + +static struct pci_driver ali1535_driver; +static unsigned long ali1535_smba; +static unsigned short ali1535_offset; + +/* Detect whether a ALI1535 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +static int ali1535_setup(struct pci_dev *dev) +{ + int retval; + unsigned char temp; + + /* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses + */ + + retval = pci_enable_device(dev); + if (retval) { + dev_err(&dev->dev, "ALI1535_smb can't enable device\n"); + goto exit; + } + + /* Determine the address of the SMBus area */ + pci_read_config_word(dev, SMBBA, &ali1535_offset); + dev_dbg(&dev->dev, "ALI1535_smb is at offset 0x%04x\n", ali1535_offset); + ali1535_offset &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); + if (ali1535_offset == 0) { + dev_warn(&dev->dev, + "ALI1535_smb region uninitialized - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit; + } + + if (pci_resource_flags(dev, 0) & IORESOURCE_IO) + ali1535_smba = pci_resource_start(dev, 0) + ali1535_offset; + else + ali1535_smba = ali1535_offset; + + retval = acpi_check_region(ali1535_smba, ALI1535_SMB_IOSIZE, + ali1535_driver.name); + if (retval) + goto exit; + + if (!request_region(ali1535_smba, ALI1535_SMB_IOSIZE, + ali1535_driver.name)) { + dev_err(&dev->dev, "ALI1535_smb region 0x%lx already in use!\n", + ali1535_smba); + retval = -EBUSY; + goto exit; + } + + /* check if whole device is enabled */ + pci_read_config_byte(dev, SMBCFG, &temp); + if ((temp & ALI1535_SMBIO_EN) == 0) { + dev_err(&dev->dev, "SMB device not enabled - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit_free; + } + + /* Is SMB Host controller enabled? */ + pci_read_config_byte(dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + dev_err(&dev->dev, "SMBus controller not enabled - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit_free; + } + + /* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(dev, SMBCLK, 0x20); + + /* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + dev_dbg(&dev->dev, "ALI1535 using Interrupt 9 for SMBus.\n"); + */ + pci_read_config_byte(dev, SMBREV, &temp); + dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&dev->dev, "ALI1535_smba = 0x%lx\n", ali1535_smba); + + return 0; + +exit_free: + release_region(ali1535_smba, ALI1535_SMB_IOSIZE); +exit: + return retval; +} + +static int ali1535_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, TYP=%02x, " + "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI1535_STS_BUSY) { + /* If the host controller is still busy, it may have timed out + * in the previous transaction, resulting in a "SMBus Timeout" + * printk. I've tried the following to reset a stuck busy bit. + * 1. Reset the controller with an KILL command. (this + * doesn't seem to clear the controller if an external + * device is hung) + * 2. Reset the controller and the other SMBus devices with a + * T_OUT command. (this clears the host busy bit if an + * external device is hung, but it comes back upon a new + * access to a device) + * 3. Disable and reenable the controller in SMBHSTCFG. Worst + * case, nothing seems to work except power reset. + */ + + /* Try resetting entire SMB bus, including other devices - This + * may not work either - it clears the BUSY bit but then the + * BUSY bit may come back on when you try and use the chip + * again. If that's the case you are stuck. + */ + dev_info(&adap->dev, + "Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI1535_T_OUT, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + } + + /* now check the error bits and the busy bit */ + if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + temp = inb_p(SMBHSTSTS); + if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* This is probably going to be correctable only by a + * power reset as one of the bits now appears to be + * stuck */ + /* This may be a bus or device with electrical problems. */ + dev_err(&adap->dev, + "SMBus reset failed! (0x%02x) - controller or " + "device on bus is probably hung\n", temp); + return -EBUSY; + } + } else { + /* check and clear done bit */ + if (temp & ALI1535_STS_DONE) + outb_p(temp, SMBHSTSTS); + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTPORT); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + usleep_range(1000, 2000); + temp = inb_p(SMBHSTSTS); + } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) + result = -ETIMEDOUT; + + if (temp & ALI1535_STS_FAIL) { + result = -EIO; + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + } + + /* Unfortunately the ALI SMB controller maps "no response" and "bus + * collision" into a single bit. No response is the usual case so don't + * do a printk. This means that bus collisions go unreported. + */ + if (temp & ALI1535_STS_BUSERR) { + result = -ENXIO; + dev_dbg(&adap->dev, + "Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); + } + + /* haven't ever seen this */ + if (temp & ALI1535_STS_DEV) { + result = -EIO; + dev_err(&adap->dev, "Error: device error\n"); + } + + /* check to see if the "command complete" indication is set */ + if (!(temp & ALI1535_STS_DONE)) + result = -ETIMEDOUT; + + dev_dbg(&adap->dev, "Transaction (post): STS=%02x, TYP=%02x, " + "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* take consequent actions for error conditions */ + if (!(temp & ALI1535_STS_DONE)) { + /* issue "kill" to reset host controller */ + outb_p(ALI1535_KILL, SMBHSTTYP); + outb_p(0xFF, SMBHSTSTS); + } else if (temp & ALI1535_STS_ERR) { + /* issue "timeout" to reset all devices on bus */ + outb_p(ALI1535_T_OUT, SMBHSTTYP); + outb_p(0xFF, SMBHSTSTS); + } + + return result; +} + +/* Return negative errno on error. */ +static s32 ali1535_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int i, len; + int temp; + int timeout; + s32 result = 0; + + /* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); + timeout++) { + usleep_range(1000, 2000); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) + dev_warn(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); + + /* clear status register (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_QUICK; + outb_p(size, SMBHSTTYP); /* output command */ + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE; + outb_p(size, SMBHSTTYP); /* output command */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_WORD_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BLOCK_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + result = -EOPNOTSUPP; + goto EXIT; + } + + result = ali1535_transaction(adap); + if (result) + goto EXIT; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) { + result = 0; + goto EXIT; + } + + switch (size) { + case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI1535_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); + dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); + } + break; + } +EXIT: + return result; +} + + +static u32 ali1535_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_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ali1535_access, + .functionality = ali1535_func, +}; + +static struct i2c_adapter ali1535_adapter = { + .owner = THIS_MODULE, + .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"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + ali1535_adapter.dev.parent = &dev->dev; + + snprintf(ali1535_adapter.name, sizeof(ali1535_adapter.name), + "SMBus ALI1535 adapter at %04x", ali1535_offset); + 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 = { + .name = "ali1535_smbus", + .id_table = ali1535_ids, + .probe = ali1535_probe, + .remove = ali1535_remove, +}; + +module_pci_driver(ali1535_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_AUTHOR("Philip Edelbrock "); +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_AUTHOR("Dan Eaton "); +MODULE_DESCRIPTION("ALI1535 SMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c new file mode 100644 index 000000000..ee4fd66de --- /dev/null +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge + * + * Copyright (C) 2004 Patrick Mochel + * 2005 Rudolf Marek + * + * The 1563 southbridge is deceptively similar to the 1533, with a + * few notable exceptions. One of those happens to be the fact they + * upgraded the i2c core to be 2.0 compliant, and happens to be almost + * identical to the i2c controller found in the Intel 801 south + * bridges. + * + * This driver is based on a mix of the 15x3, 1535, and i801 drivers, + * with a little help from the ALi 1563 spec. + */ + +#include +#include +#include +#include +#include + +#define ALI1563_MAX_TIMEOUT 500 +#define ALI1563_SMBBA 0x80 +#define ALI1563_SMB_IOEN 1 +#define ALI1563_SMB_HOSTEN 2 +#define ALI1563_SMB_IOSIZE 16 + +#define SMB_HST_STS (ali1563_smba + 0) +#define SMB_HST_CNTL1 (ali1563_smba + 1) +#define SMB_HST_CNTL2 (ali1563_smba + 2) +#define SMB_HST_CMD (ali1563_smba + 3) +#define SMB_HST_ADD (ali1563_smba + 4) +#define SMB_HST_DAT0 (ali1563_smba + 5) +#define SMB_HST_DAT1 (ali1563_smba + 6) +#define SMB_BLK_DAT (ali1563_smba + 7) + +#define HST_STS_BUSY 0x01 +#define HST_STS_INTR 0x02 +#define HST_STS_DEVERR 0x04 +#define HST_STS_BUSERR 0x08 +#define HST_STS_FAIL 0x10 +#define HST_STS_DONE 0x80 +#define HST_STS_BAD 0x1c + + +#define HST_CNTL1_TIMEOUT 0x80 +#define HST_CNTL1_LAST 0x40 + +#define HST_CNTL2_KILL 0x04 +#define HST_CNTL2_START 0x40 +#define HST_CNTL2_QUICK 0x00 +#define HST_CNTL2_BYTE 0x01 +#define HST_CNTL2_BYTE_DATA 0x02 +#define HST_CNTL2_WORD_DATA 0x03 +#define HST_CNTL2_BLOCK 0x05 + + +#define HST_CNTL2_SIZEMASK 0x38 + +static struct pci_driver ali1563_pci_driver; +static unsigned short ali1563_smba; + +static int ali1563_transaction(struct i2c_adapter *a, int size) +{ + u32 data; + int timeout; + int status = -EIO; + + dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) { + dev_err(&a->dev, "ali1563: Trying to reset busy device\n"); + outb_p(data | HST_STS_BAD, SMB_HST_STS); + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) + return -EBUSY; + } + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); + + timeout = ALI1563_MAX_TIMEOUT; + do { + msleep(1); + } while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout); + + dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + if (timeout && !(data & HST_STS_BAD)) + return 0; + + if (!timeout) { + /* Issue 'kill' to host controller */ + outb_p(HST_CNTL2_KILL, SMB_HST_CNTL2); + data = inb_p(SMB_HST_STS); + status = -ETIMEDOUT; + } + + /* device error - no response, ignore the autodetection case */ + if (data & HST_STS_DEVERR) { + if (size != HST_CNTL2_QUICK) + dev_err(&a->dev, "Device error!\n"); + status = -ENXIO; + } + /* bus collision */ + if (data & HST_STS_BUSERR) { + dev_err(&a->dev, "Bus collision!\n"); + /* Issue timeout, hoping it helps */ + outb_p(HST_CNTL1_TIMEOUT, SMB_HST_CNTL1); + } + + if (data & HST_STS_FAIL) { + dev_err(&a->dev, "Cleaning fail after KILL!\n"); + outb_p(0x0, SMB_HST_CNTL2); + } + + return status; +} + +static int ali1563_block_start(struct i2c_adapter *a) +{ + u32 data; + int timeout; + int status = -EIO; + + dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) { + dev_warn(&a->dev, "ali1563: Trying to reset busy device\n"); + outb_p(data | HST_STS_BAD, SMB_HST_STS); + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) + return -EBUSY; + } + + /* Clear byte-ready bit */ + outb_p(data | HST_STS_DONE, SMB_HST_STS); + + /* Start transaction and wait for byte-ready bit to be set */ + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); + + timeout = ALI1563_MAX_TIMEOUT; + do { + msleep(1); + } while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout); + + dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + if (timeout && !(data & HST_STS_BAD)) + return 0; + + if (timeout == 0) + status = -ETIMEDOUT; + + if (data & HST_STS_DEVERR) + status = -ENXIO; + + dev_err(&a->dev, "SMBus Error: %s%s%s%s%s\n", + timeout ? "" : "Timeout ", + data & HST_STS_FAIL ? "Transaction Failed " : "", + data & HST_STS_BUSERR ? "No response or Bus Collision " : "", + data & HST_STS_DEVERR ? "Device Error " : "", + !(data & HST_STS_DONE) ? "Transaction Never Finished " : ""); + return status; +} + +static int ali1563_block(struct i2c_adapter *a, + union i2c_smbus_data *data, u8 rw) +{ + int i, len; + int error = 0; + + /* Do we need this? */ + outb_p(HST_CNTL1_LAST, SMB_HST_CNTL1); + + if (rw == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + else if (len > 32) + len = 32; + outb_p(len, SMB_HST_DAT0); + outb_p(data->block[1], SMB_BLK_DAT); + } else + len = 32; + + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2); + + for (i = 0; i < len; i++) { + if (rw == I2C_SMBUS_WRITE) { + outb_p(data->block[i + 1], SMB_BLK_DAT); + error = ali1563_block_start(a); + if (error) + break; + } else { + error = ali1563_block_start(a); + if (error) + break; + if (i == 0) { + len = inb_p(SMB_HST_DAT0); + if (len < 1) + len = 1; + else if (len > 32) + len = 32; + } + data->block[i+1] = inb_p(SMB_BLK_DAT); + } + } + /* Do we need this? */ + outb_p(HST_CNTL1_LAST, SMB_HST_CNTL1); + return error; +} + +static s32 ali1563_access(struct i2c_adapter *a, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + int timeout; + u32 reg; + + for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) { + reg = inb_p(SMB_HST_STS); + if (!(reg & HST_STS_BUSY)) + break; + } + if (!timeout) + dev_warn(&a->dev, "SMBus not idle. HST_STS = %02x\n", reg); + outb_p(0xff, SMB_HST_STS); + + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + size = HST_CNTL2_QUICK; + break; + case I2C_SMBUS_BYTE: + size = HST_CNTL2_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + size = HST_CNTL2_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + size = HST_CNTL2_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + size = HST_CNTL2_BLOCK; + break; + default: + dev_warn(&a->dev, "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD); + outb_p((inb_p(SMB_HST_CNTL2) & ~HST_CNTL2_SIZEMASK) | + (size << 3), SMB_HST_CNTL2); + + /* Write the command register */ + + switch (size) { + case HST_CNTL2_BYTE: + if (rw == I2C_SMBUS_WRITE) + /* Beware it uses DAT0 register and not CMD! */ + outb_p(cmd, SMB_HST_DAT0); + break; + case HST_CNTL2_BYTE_DATA: + outb_p(cmd, SMB_HST_CMD); + if (rw == I2C_SMBUS_WRITE) + outb_p(data->byte, SMB_HST_DAT0); + break; + case HST_CNTL2_WORD_DATA: + outb_p(cmd, SMB_HST_CMD); + if (rw == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMB_HST_DAT0); + outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1); + } + break; + case HST_CNTL2_BLOCK: + outb_p(cmd, SMB_HST_CMD); + error = ali1563_block(a, data, rw); + goto Done; + } + + error = ali1563_transaction(a, size); + if (error) + goto Done; + + if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK)) + goto Done; + + switch (size) { + case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMB_HST_DAT0); + break; + case HST_CNTL2_BYTE_DATA: + data->byte = inb_p(SMB_HST_DAT0); + break; + case HST_CNTL2_WORD_DATA: + data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8); + break; + } +Done: + return error; +} + +static u32 ali1563_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + + +static int ali1563_setup(struct pci_dev *dev) +{ + u16 ctrl; + + pci_read_config_word(dev, ALI1563_SMBBA, &ctrl); + + /* SMB I/O Base in high 12 bits and must be aligned with the + * size of the I/O space. */ + ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1); + if (!ali1563_smba) { + dev_warn(&dev->dev, "ali1563_smba Uninitialized\n"); + goto Err; + } + + /* Check if device is enabled */ + if (!(ctrl & ALI1563_SMB_HOSTEN)) { + dev_warn(&dev->dev, "Host Controller not enabled\n"); + goto Err; + } + if (!(ctrl & ALI1563_SMB_IOEN)) { + dev_warn(&dev->dev, "I/O space not enabled, trying manually\n"); + pci_write_config_word(dev, ALI1563_SMBBA, + ctrl | ALI1563_SMB_IOEN); + pci_read_config_word(dev, ALI1563_SMBBA, &ctrl); + if (!(ctrl & ALI1563_SMB_IOEN)) { + dev_err(&dev->dev, + "I/O space still not enabled, giving up\n"); + goto Err; + } + } + + if (acpi_check_region(ali1563_smba, ALI1563_SMB_IOSIZE, + ali1563_pci_driver.name)) + goto Err; + + if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE, + ali1563_pci_driver.name)) { + dev_err(&dev->dev, "Could not allocate I/O space at 0x%04x\n", + ali1563_smba); + goto Err; + } + dev_info(&dev->dev, "Found ALi1563 SMBus at 0x%04x\n", ali1563_smba); + + return 0; +Err: + return -ENODEV; +} + +static void ali1563_shutdown(struct pci_dev *dev) +{ + release_region(ali1563_smba, ALI1563_SMB_IOSIZE); +} + +static const struct i2c_algorithm ali1563_algorithm = { + .smbus_xfer = ali1563_access, + .functionality = ali1563_func, +}; + +static struct i2c_adapter ali1563_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &ali1563_algorithm, +}; + +static int ali1563_probe(struct pci_dev *dev, + const struct pci_device_id *id_table) +{ + int error; + + error = ali1563_setup(dev); + if (error) + goto exit; + ali1563_adapter.dev.parent = &dev->dev; + snprintf(ali1563_adapter.name, sizeof(ali1563_adapter.name), + "SMBus ALi 1563 Adapter @ %04x", ali1563_smba); + error = i2c_add_adapter(&ali1563_adapter); + if (error) + goto exit_shutdown; + return 0; + +exit_shutdown: + ali1563_shutdown(dev); +exit: + dev_warn(&dev->dev, "ALi1563 SMBus probe failed (%d)\n", error); + return error; +} + +static void ali1563_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali1563_adapter); + ali1563_shutdown(dev); +} + +static const struct pci_device_id ali1563_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, ali1563_id_table); + +static struct pci_driver ali1563_pci_driver = { + .name = "ali1563_smbus", + .id_table = ali1563_id_table, + .probe = ali1563_probe, + .remove = ali1563_remove, +}; + +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 new file mode 100644 index 000000000..418d11266 --- /dev/null +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1999 Frodo Looijaard and + Philip Edelbrock and + Mark D. Studebaker + +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Target controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static u16 force_addr; +module_param_hw(force_addr, ushort, ioport, 0); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +static struct pci_driver ali15x3_driver; +static unsigned short ali15x3_smba; + +static int ali15x3_setup(struct pci_dev *ALI15X3_dev) +{ + u16 a; + unsigned char temp; + + /* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses + */ + + /* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. + */ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + + /* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + dev_err(&ALI15X3_dev->dev, "ALI15X3_smb region uninitialized " + "- upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (acpi_check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, + ali15x3_driver.name)) + return -EBUSY; + + if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, + ali15x3_driver.name)) { + dev_err(&ALI15X3_dev->dev, + "ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + int ret; + + dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n", + ali15x3_smba); + ret = pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba); + if (ret != PCIBIOS_SUCCESSFUL) + goto error; + 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 */ + dev_err(&ALI15X3_dev->dev, + "force address failed - not supported?\n"); + goto error; + } + } + /* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + dev_info(&ALI15X3_dev->dev, "enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + + /* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + dev_info(&ALI15X3_dev->dev, "enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + + /* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + dev_dbg(&ALI15X3_dev->dev, "ALI15X3 using Interrupt 9 for SMBus.\n"); + */ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + dev_dbg(&ALI15X3_dev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&ALI15X3_dev->dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba); + + return 0; +error: + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); + return -ENODEV; +} + +/* Another internally used function */ +static int ali15x3_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), + inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { + /* + If the host controller is still busy, it may have timed out in the + previous transaction, resulting in a "SMBus Timeout" Dev. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external + device is hung) + 2. Reset the controller and the other SMBus devices with a + T_OUT command. (this clears the host busy bit if an + external device is hung, but it comes back upon a new access + to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. + */ + /* Abort - reset the host controller */ + /* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. + */ + dev_info(&adap->dev, "Resetting entire SMB Bus to " + "clear busy condition (%02x)\n", temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + dev_err(&adap->dev, "SMBus reset failed! (0x%02x) - " + "controller or device on bus is probably hung\n", + temp); + return -EBUSY; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) + result = -ETIMEDOUT; + + if (temp & ALI15X3_STS_TERM) { + result = -EIO; + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + } + + /* + Unfortunately the ALI SMB controller maps "no response" and "bus + collision" into a single bit. No response is the usual case so don't + do a printk. + This means that bus collisions go unreported. + */ + if (temp & ALI15X3_STS_COLL) { + result = -ENXIO; + dev_dbg(&adap->dev, + "Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); + } + + /* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -EIO; + dev_err(&adap->dev, "Error: device error\n"); + } + dev_dbg(&adap->dev, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), + inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + return result; +} + +/* Return negative errno on error. */ +static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + + /* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + /* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + msleep(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + dev_err(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); + } + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + temp = ali15x3_transaction(adap); + if (temp) + return temp; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); + dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); + } + break; + } + return 0; +} + +static u32 ali15x3_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_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ali15x3_access, + .functionality = ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id ali15x3_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, + { 0, } +}; + +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"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + ali15x3_adapter.dev.parent = &dev->dev; + + snprintf(ali15x3_adapter.name, sizeof(ali15x3_adapter.name), + "SMBus ALI15X3 adapter at %04x", ali15x3_smba); + 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) +{ + i2c_del_adapter(&ali15x3_adapter); + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); +} + +static struct pci_driver ali15x3_driver = { + .name = "ali15x3_smbus", + .id_table = ali15x3_ids, + .probe = ali15x3_probe, + .remove = ali15x3_remove, +}; + +module_pci_driver(ali15x3_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_AUTHOR("Philip Edelbrock "); +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-altera.c b/drivers/i2c/busses/i2c-altera.c new file mode 100644 index 000000000..2da73173c --- /dev/null +++ b/drivers/i2c/busses/i2c-altera.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright Intel Corporation (C) 2017. + * + * Based on the i2c-axxia.c driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ALTR_I2C_TFR_CMD 0x00 /* Transfer Command register */ +#define ALTR_I2C_TFR_CMD_STA BIT(9) /* send START before byte */ +#define ALTR_I2C_TFR_CMD_STO BIT(8) /* send STOP after byte */ +#define ALTR_I2C_TFR_CMD_RW_D BIT(0) /* Direction of transfer */ +#define ALTR_I2C_RX_DATA 0x04 /* RX data FIFO register */ +#define ALTR_I2C_CTRL 0x08 /* Control register */ +#define ALTR_I2C_CTRL_RXT_SHFT 4 /* RX FIFO Threshold */ +#define ALTR_I2C_CTRL_TCT_SHFT 2 /* TFER CMD FIFO Threshold */ +#define ALTR_I2C_CTRL_BSPEED BIT(1) /* Bus Speed (1=Fast) */ +#define ALTR_I2C_CTRL_EN BIT(0) /* Enable Core (1=Enable) */ +#define ALTR_I2C_ISER 0x0C /* Interrupt Status Enable register */ +#define ALTR_I2C_ISER_RXOF_EN BIT(4) /* Enable RX OVERFLOW IRQ */ +#define ALTR_I2C_ISER_ARB_EN BIT(3) /* Enable ARB LOST IRQ */ +#define ALTR_I2C_ISER_NACK_EN BIT(2) /* Enable NACK DET IRQ */ +#define ALTR_I2C_ISER_RXRDY_EN BIT(1) /* Enable RX Ready IRQ */ +#define ALTR_I2C_ISER_TXRDY_EN BIT(0) /* Enable TX Ready IRQ */ +#define ALTR_I2C_ISR 0x10 /* Interrupt Status register */ +#define ALTR_I2C_ISR_RXOF BIT(4) /* RX OVERFLOW IRQ */ +#define ALTR_I2C_ISR_ARB BIT(3) /* ARB LOST IRQ */ +#define ALTR_I2C_ISR_NACK BIT(2) /* NACK DET IRQ */ +#define ALTR_I2C_ISR_RXRDY BIT(1) /* RX Ready IRQ */ +#define ALTR_I2C_ISR_TXRDY BIT(0) /* TX Ready IRQ */ +#define ALTR_I2C_STATUS 0x14 /* Status register */ +#define ALTR_I2C_STAT_CORE BIT(0) /* Core Status (0=idle) */ +#define ALTR_I2C_TC_FIFO_LVL 0x18 /* Transfer FIFO LVL register */ +#define ALTR_I2C_RX_FIFO_LVL 0x1C /* Receive FIFO LVL register */ +#define ALTR_I2C_SCL_LOW 0x20 /* SCL low count register */ +#define ALTR_I2C_SCL_HIGH 0x24 /* SCL high count register */ +#define ALTR_I2C_SDA_HOLD 0x28 /* SDA hold count register */ + +#define ALTR_I2C_ALL_IRQ (ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \ + ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \ + ALTR_I2C_ISR_TXRDY) + +#define ALTR_I2C_THRESHOLD 0 /* IRQ Threshold at 1 element */ +#define ALTR_I2C_DFLT_FIFO_SZ 4 +#define ALTR_I2C_TIMEOUT 100000 /* 100ms */ +#define ALTR_I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) + +/** + * 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 + * @msg_err: error code for completed message + * @msg_complete: xfer completion object + * @dev: device reference + * @adapter: core i2c abstraction + * @i2c_clk: clock reference for i2c input clock + * @bus_clk_rate: current i2c bus clock rate + * @buf: ptr to msg buffer for easier use. + * @fifo_size: size of the FIFO passed in. + * @isr_mask: cached copy of local ISR enables. + * @isr_status: cached copy of local ISR status. + * @isr_mutex: mutex for IRQ thread. + */ +struct altr_i2c_dev { + void __iomem *base; + struct i2c_msg *msg; + size_t msg_len; + int msg_err; + struct completion msg_complete; + struct device *dev; + struct i2c_adapter adapter; + struct clk *i2c_clk; + u32 bus_clk_rate; + u8 *buf; + u32 fifo_size; + u32 isr_mask; + u32 isr_status; + struct mutex isr_mutex; +}; + +static void +altr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable) +{ + u32 int_en; + + int_en = readl(idev->base + ALTR_I2C_ISER); + if (enable) + idev->isr_mask = int_en | mask; + else + idev->isr_mask = int_en & ~mask; + + writel(idev->isr_mask, idev->base + ALTR_I2C_ISER); +} + +static void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask) +{ + u32 int_en = readl(idev->base + ALTR_I2C_ISR); + + writel(int_en | mask, idev->base + ALTR_I2C_ISR); +} + +static void altr_i2c_core_disable(struct altr_i2c_dev *idev) +{ + u32 tmp = readl(idev->base + ALTR_I2C_CTRL); + + writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); +} + +static void altr_i2c_core_enable(struct altr_i2c_dev *idev) +{ + u32 tmp = readl(idev->base + ALTR_I2C_CTRL); + + writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); +} + +static void altr_i2c_reset(struct altr_i2c_dev *idev) +{ + altr_i2c_core_disable(idev); + altr_i2c_core_enable(idev); +} + +static inline void altr_i2c_stop(struct altr_i2c_dev *idev) +{ + writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD); +} + +static void altr_i2c_init(struct altr_i2c_dev *idev) +{ + u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; + u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; + u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) | + (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT); + u32 t_high, t_low; + + 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; + t_low = divisor * 1 / 2; + } else { + tmp |= ALTR_I2C_CTRL_BSPEED; + /* Fast mode SCL 33/66 */ + t_high = divisor * 1 / 3; + t_low = divisor * 2 / 3; + } + writel(tmp, idev->base + ALTR_I2C_CTRL); + + dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", + idev->bus_clk_rate, clk_mhz, divisor); + + /* Reset controller */ + altr_i2c_reset(idev); + + /* SCL High Time */ + writel(t_high, idev->base + ALTR_I2C_SCL_HIGH); + /* SCL Low Time */ + writel(t_low, idev->base + ALTR_I2C_SCL_LOW); + /* SDA Hold Time, 300ns */ + writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD); + + /* 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. + */ +static void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data) +{ + /* On the last byte to be transmitted, send STOP */ + if (idev->msg_len == 1) + data |= ALTR_I2C_TFR_CMD_STO; + if (idev->msg_len > 0) + 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. + */ +static void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev) +{ + size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL); + int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len); + + while (bytes_to_transfer-- > 0) { + *idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA); + idev->msg_len--; + altr_i2c_transfer(idev, 0); + } +} + +/* + * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer. + */ +static int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev) +{ + size_t tx_fifo_avail = idev->fifo_size - readl(idev->base + + ALTR_I2C_TC_FIFO_LVL); + int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len); + int ret = idev->msg_len - bytes_to_transfer; + + while (bytes_to_transfer-- > 0) { + altr_i2c_transfer(idev, *idev->buf++); + idev->msg_len--; + } + + return ret; +} + +static irqreturn_t altr_i2c_isr_quick(int irq, void *_dev) +{ + struct altr_i2c_dev *idev = _dev; + irqreturn_t ret = IRQ_HANDLED; + + /* Read IRQ status but only interested in Enabled IRQs. */ + idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask; + if (idev->isr_status) + ret = IRQ_WAKE_THREAD; + + return ret; +} + +static irqreturn_t altr_i2c_isr(int irq, void *_dev) +{ + int ret; + bool read, finish = false; + 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); + goto out; + } + read = (idev->msg->flags & I2C_M_RD) != 0; + + /* handle Lost Arbitration */ + if (unlikely(status & ALTR_I2C_ISR_ARB)) { + altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB); + idev->msg_err = -EAGAIN; + finish = true; + } else if (unlikely(status & ALTR_I2C_ISR_NACK)) { + dev_dbg(idev->dev, "Could not get ACK\n"); + idev->msg_err = -ENXIO; + altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK); + altr_i2c_stop(idev); + finish = true; + } else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) { + /* handle RX FIFO Overflow */ + altr_i2c_empty_rx_fifo(idev); + altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); + altr_i2c_stop(idev); + dev_err(idev->dev, "RX FIFO Overflow\n"); + finish = true; + } else if (read && (status & ALTR_I2C_ISR_RXRDY)) { + /* RX FIFO needs service? */ + altr_i2c_empty_rx_fifo(idev); + altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); + if (!idev->msg_len) + finish = true; + } else if (!read && (status & ALTR_I2C_ISR_TXRDY)) { + /* TX FIFO needs service? */ + altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY); + if (idev->msg_len > 0) + altr_i2c_fill_tx_fifo(idev); + else + finish = true; + } else { + dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status); + altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); + } + + if (finish) { + /* Wait for the Core to finish */ + ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS, + status, + !(status & ALTR_I2C_STAT_CORE), + 1, ALTR_I2C_TIMEOUT); + if (ret) + dev_err(idev->dev, "message timeout\n"); + altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); + altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); + complete(&idev->msg_complete); + dev_dbg(idev->dev, "Message Complete\n"); + } +out: + mutex_unlock(&idev->isr_mutex); + + return IRQ_HANDLED; +} + +static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg) +{ + u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK; + unsigned long time_left; + 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; + idev->msg_err = 0; + reinit_completion(&idev->msg_complete); + altr_i2c_core_enable(idev); + + /* Make sure RX FIFO is empty */ + do { + readl(idev->base + ALTR_I2C_RX_DATA); + } while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL)); + + writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD); + + if ((msg->flags & I2C_M_RD) != 0) { + imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN; + altr_i2c_int_enable(idev, imask, true); + /* write the first byte to start the RX */ + altr_i2c_transfer(idev, 0); + } else { + imask |= ALTR_I2C_ISR_TXRDY; + 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; + if (value) + dev_err(idev->dev, "Core Status not IDLE...\n"); + + if (time_left == 0) { + idev->msg_err = -ETIMEDOUT; + dev_dbg(idev->dev, "Transaction timed out.\n"); + } + + altr_i2c_core_disable(idev); + mutex_unlock(&idev->isr_mutex); + + return idev->msg_err; +} + +static int +altr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct altr_i2c_dev *idev = i2c_get_adapdata(adap); + int i, ret; + + for (i = 0; i < num; i++) { + ret = altr_i2c_xfer_msg(idev, msgs++); + if (ret) + return ret; + } + return num; +} + +static u32 altr_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm altr_i2c_algo = { + .xfer = altr_i2c_xfer, + .functionality = altr_i2c_func, +}; + +static int altr_i2c_probe(struct platform_device *pdev) +{ + struct altr_i2c_dev *idev = NULL; + int irq, ret; + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + 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) + return irq; + + idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(idev->i2c_clk)) { + dev_err(&pdev->dev, "missing clock\n"); + return PTR_ERR(idev->i2c_clk); + } + + idev->dev = &pdev->dev; + init_completion(&idev->msg_complete); + mutex_init(&idev->isr_mutex); + + ret = device_property_read_u32(idev->dev, "fifo-size", + &idev->fifo_size); + 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; + } + + ret = device_property_read_u32(idev->dev, "clock-frequency", + &idev->bus_clk_rate); + if (ret) { + dev_err(&pdev->dev, "Default to 100kHz\n"); + idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */ + } + + 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; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick, + altr_i2c_isr, IRQF_ONESHOT, + pdev->name, idev); + if (ret) { + dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq); + return ret; + } + + ret = clk_prepare_enable(idev->i2c_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + return ret; + } + + mutex_lock(&idev->isr_mutex); + altr_i2c_init(idev); + mutex_unlock(&idev->isr_mutex); + + i2c_set_adapdata(&idev->adapter, idev); + 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; + idev->adapter.dev.of_node = pdev->dev.of_node; + + 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, "Altera SoftIP I2C Probe Complete\n"); + + return 0; +} + +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); +} + +/* Match table for of_platform binding */ +static const struct of_device_id altr_i2c_of_match[] = { + { .compatible = "altr,softip-i2c-v1.0" }, + {}, +}; +MODULE_DEVICE_TABLE(of, altr_i2c_of_match); + +static struct platform_driver altr_i2c_driver = { + .probe = altr_i2c_probe, + .remove = altr_i2c_remove, + .driver = { + .name = "altera-i2c", + .of_match_table = altr_i2c_of_match, + }, +}; + +module_platform_driver(altr_i2c_driver); + +MODULE_DESCRIPTION("Altera Soft IP I2C bus driver"); +MODULE_AUTHOR("Thor Thayer "); +MODULE_LICENSE("GPL v2"); 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 000000000..b0b40fe1d --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-asf-plat.c @@ -0,0 +1,373 @@ +// 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 + * Sanket Goswami + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + + if (len > ASF_BLOCK_MAX_BYTES) + return; + + 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, ®, 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, ®, 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 irq; + + ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev); + if (ret) + return ret; + + 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 000000000..5b41d18b6 --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * AMD MP2 PCIe communication driver + * + * Authors: Shyam Sundar S K + * Elie Morisse + */ + +#include +#include +#include +#include +#include + +#include "i2c-amd-mp2.h" + +#include + +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 amd_mp2_dev *privdata; + struct device *dev; + struct pci_dev *pci_dev; + + dev = driver_find_next_device(&amd_mp2_pci_driver.driver, NULL); + if (!dev) + return NULL; + + pci_dev = to_pci_dev(dev); + privdata = pci_get_drvdata(pci_dev); + + put_device(dev); + + return privdata; +} +EXPORT_SYMBOL_GPL(amd_mp2_find_device); + +MODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver"); +MODULE_AUTHOR("Shyam Sundar S K "); +MODULE_AUTHOR("Elie Morisse "); +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 000000000..9fdd6a5fb --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -0,0 +1,362 @@ +// 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 + * Elie Morisse + */ + +#include +#include +#include +#include +#include +#include + +#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); + amd_mp2_unregister_cb(&i2c_dev->common); + } + + 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 "); +MODULE_AUTHOR("Elie Morisse "); +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 000000000..9b7e9494d --- /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 + * Elie Morisse + */ + +#ifndef I2C_AMD_PCI_MP2_H +#define I2C_AMD_PCI_MP2_H + +#include +#include +#include + +#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.c b/drivers/i2c/busses/i2c-amd756.c new file mode 100644 index 000000000..3621c02f1 --- /dev/null +++ b/drivers/i2c/busses/i2c-amd756.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1999-2002 Merlin Hughes + + Shamelessly ripped from i2c-piix4.c: + + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + +*/ + +/* + 2002-04-08: Added nForce support. (Csaba Halasz) + 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) + 2002-12-28: Rewritten into something that resembles a Linux driver (hch) + 2003-11-29: Added back AMD8111 removed by the previous rewrite. + (Philip Pokorny) +*/ + +/* + Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AMD756 SMBus address offsets */ +#define SMB_ADDR_OFFSET 0xE0 +#define SMB_IOSIZE 16 +#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport) +#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport) +#define SMB_HOST_ADDRESS (0x4 + amd756_ioport) +#define SMB_HOST_DATA (0x6 + amd756_ioport) +#define SMB_HOST_COMMAND (0x8 + amd756_ioport) +#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport) +#define SMB_HAS_DATA (0xA + amd756_ioport) +#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport) +#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport) +#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport) + +/* PCI Address Constants */ + +/* address of I/O space */ +#define SMBBA 0x058 /* mh */ +#define SMBBANFORCE 0x014 + +/* general configuration */ +#define SMBGCFG 0x041 /* mh */ + +/* silicon revision code */ +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* AMD756 constants */ +#define AMD756_QUICK 0x00 +#define AMD756_BYTE 0x01 +#define AMD756_BYTE_DATA 0x02 +#define AMD756_WORD_DATA 0x03 +#define AMD756_PROCESS_CALL 0x04 +#define AMD756_BLOCK_DATA 0x05 + +static struct pci_driver amd756_driver; +static unsigned short amd756_ioport; + +/* + SMBUS event = I/O 28-29 bit 11 + see E0 for the status bits and enabled in E2 + +*/ +#define GS_ABRT_STS (1 << 0) +#define GS_COL_STS (1 << 1) +#define GS_PRERR_STS (1 << 2) +#define GS_HST_STS (1 << 3) +#define GS_HCYC_STS (1 << 4) +#define GS_TO_STS (1 << 5) +#define GS_SMB_STS (1 << 11) + +#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ + GS_HCYC_STS | GS_TO_STS ) + +#define GE_CYC_TYPE_MASK (7) +#define GE_HOST_STC (1 << 3) +#define GE_ABORT (1 << 5) + + +static int amd756_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, " + "DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS), + inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS), + inb_p(SMB_HOST_DATA)); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { + dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp); + do { + msleep(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & (GS_HST_STS | GS_SMB_STS)) && + (timeout++ < MAX_TIMEOUT)); + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp); + goto abort; + } + timeout = 0; + } + + /* start the transaction by setting the start bit */ + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "Completion timeout!\n"); + goto abort; + } + + if (temp & GS_PRERR_STS) { + result = -ENXIO; + dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n"); + } + + if (temp & GS_COL_STS) { + result = -EIO; + dev_warn(&adap->dev, "SMBus collision!\n"); + } + + if (temp & GS_TO_STS) { + result = -ETIMEDOUT; + dev_dbg(&adap->dev, "SMBus protocol timeout!\n"); + } + + if (temp & GS_HCYC_STS) + dev_dbg(&adap->dev, "SMBus protocol success!\n"); + + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + +#ifdef DEBUG + if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { + dev_dbg(&adap->dev, + "Failed reset at end of transaction (%04x)\n", temp); + } +#endif + + dev_dbg(&adap->dev, + "Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); + + return result; + + abort: + dev_warn(&adap->dev, "Sending abort\n"); + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); + msleep(100); + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + return -EIO; +} + +/* Return negative errno on error. */ +static s32 amd756_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + size = AMD756_QUICK; + break; + case I2C_SMBUS_BYTE: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMB_HOST_DATA); + size = AMD756_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->byte, SMB_HOST_DATA); + size = AMD756_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->word, SMB_HOST_DATA); + size = AMD756_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outw_p(len, SMB_HOST_DATA); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], + SMB_HOST_BLOCK_DATA); + } + size = AMD756_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + /* How about enabling interrupts... */ + outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); + + status = amd756_transaction(adap); + if (status) + return status; + + if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) + return 0; + + + switch (size) { + case AMD756_BYTE: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_BYTE_DATA: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_WORD_DATA: + data->word = inw_p(SMB_HOST_DATA); + break; + case AMD756_BLOCK_DATA: + data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f; + if(data->block[0] > 32) + data->block[0] = 32; + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); + break; + } + + return 0; +} + +static u32 amd756_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_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = amd756_access, + .functionality = amd756_func, +}; + +static struct i2c_adapter amd756_smbus = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, +}; + +enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 }; +static const char* chipname[] = { + "AMD756", "AMD766", "AMD768", + "nVidia nForce", "AMD8111", +}; + +static const struct pci_device_id amd756_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B), + .driver_data = AMD756 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413), + .driver_data = AMD766 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443), + .driver_data = AMD768 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), + .driver_data = AMD8111 }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS), + .driver_data = NFORCE }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, amd756_ids); + +static int amd756_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int nforce = (id->driver_data == NFORCE); + int error; + u8 temp; + + if (amd756_ioport) { + dev_err(&pdev->dev, "Only one device supported " + "(you have a strange motherboard, btw)\n"); + return -ENODEV; + } + + if (nforce) { + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport); + amd756_ioport &= 0xfffc; + } else { /* amd */ + if (PCI_FUNC(pdev->devfn) != 3) + return -ENODEV; + + pci_read_config_byte(pdev, SMBGCFG, &temp); + if ((temp & 128) == 0) { + dev_err(&pdev->dev, + "Error: SMBus controller I/O not enabled!\n"); + return -ENODEV; + } + + /* Determine the address of the SMBus areas */ + /* Technically it is a dword but... */ + pci_read_config_word(pdev, SMBBA, &amd756_ioport); + amd756_ioport &= 0xff00; + amd756_ioport += SMB_ADDR_OFFSET; + } + + error = acpi_check_region(amd756_ioport, SMB_IOSIZE, + amd756_driver.name); + if (error) + return -ENODEV; + + if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) { + dev_err(&pdev->dev, "SMB region 0x%x already in use!\n", + amd756_ioport); + return -ENODEV; + } + + pci_read_config_byte(pdev, SMBREV, &temp); + dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); + + /* set up the sysfs linkage to our parent device */ + amd756_smbus.dev.parent = &pdev->dev; + + snprintf(amd756_smbus.name, sizeof(amd756_smbus.name), + "SMBus %s adapter at %04x", chipname[id->driver_data], + amd756_ioport); + + error = i2c_add_adapter(&amd756_smbus); + if (error) + goto out_err; + + return 0; + + out_err: + release_region(amd756_ioport, SMB_IOSIZE); + return error; +} + +static void amd756_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&amd756_smbus); + release_region(amd756_ioport, SMB_IOSIZE); +} + +static struct pci_driver amd756_driver = { + .name = "amd756_smbus", + .id_table = amd756_ids, + .probe = amd756_probe, + .remove = amd756_remove, +}; + +module_pci_driver(amd756_driver); + +MODULE_AUTHOR("Merlin Hughes "); +MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c new file mode 100644 index 000000000..dd9ac4bb6 --- /dev/null +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMBus 2.0 driver for AMD-8111 IO-Hub. + * + * Copyright (c) 2002 Vojtech Pavlik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); + +struct amd_smbus { + struct pci_dev *dev; + struct i2c_adapter adapter; + int base; + int size; +}; + +static struct pci_driver amd8111_driver; + +/* + * AMD PCI control registers definitions. + */ + +#define AMD_PCI_MISC 0x48 + +#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ +#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ +#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ + +/* + * ACPI 2.0 chapter 13 PCI interface definitions. + */ + +#define AMD_EC_DATA 0x00 /* data register */ +#define AMD_EC_SC 0x04 /* status of controller */ +#define AMD_EC_CMD 0x04 /* command register */ +#define AMD_EC_ICR 0x08 /* interrupt control register */ + +#define AMD_EC_SC_SMI 0x04 /* smi event pending */ +#define AMD_EC_SC_SCI 0x02 /* sci event pending */ +#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ +#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ +#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ +#define AMD_EC_SC_OBF 0x01 /* data ready for host */ + +#define AMD_EC_CMD_RD 0x80 /* read EC */ +#define AMD_EC_CMD_WR 0x81 /* write EC */ +#define AMD_EC_CMD_BE 0x82 /* enable burst mode */ +#define AMD_EC_CMD_BD 0x83 /* disable burst mode */ +#define AMD_EC_CMD_QR 0x84 /* query EC */ + +/* + * ACPI 2.0 chapter 13 access of registers of the EC + */ + +static int amd_ec_wait_write(struct amd_smbus *smbus) +{ + int timeout = 500; + + while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout) + udelay(1); + + if (!timeout) { + dev_warn(&smbus->dev->dev, + "Timeout while waiting for IBF to clear\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int amd_ec_wait_read(struct amd_smbus *smbus) +{ + int timeout = 500; + + while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout) + udelay(1); + + if (!timeout) { + dev_warn(&smbus->dev->dev, + "Timeout while waiting for OBF to set\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int amd_ec_read(struct amd_smbus *smbus, unsigned char address, + unsigned char *data) +{ + int status; + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(address, smbus->base + AMD_EC_DATA); + + status = amd_ec_wait_read(smbus); + if (status) + return status; + *data = inb(smbus->base + AMD_EC_DATA); + + return 0; +} + +static int amd_ec_write(struct amd_smbus *smbus, unsigned char address, + unsigned char data) +{ + int status; + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(address, smbus->base + AMD_EC_DATA); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(data, smbus->base + AMD_EC_DATA); + + return 0; +} + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ + +#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ +#define AMD_SMB_STS 0x01 /* status */ +#define AMD_SMB_ADDR 0x02 /* address */ +#define AMD_SMB_CMD 0x03 /* command */ +#define AMD_SMB_DATA 0x04 /* 32 data registers */ +#define AMD_SMB_BCNT 0x24 /* number of data bytes */ +#define AMD_SMB_ALRM_A 0x25 /* alarm address */ +#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ + +#define AMD_SMB_STS_DONE 0x80 +#define AMD_SMB_STS_ALRM 0x40 +#define AMD_SMB_STS_RES 0x20 +#define AMD_SMB_STS_STATUS 0x1f + +#define AMD_SMB_STATUS_OK 0x00 +#define AMD_SMB_STATUS_FAIL 0x07 +#define AMD_SMB_STATUS_DNAK 0x10 +#define AMD_SMB_STATUS_DERR 0x11 +#define AMD_SMB_STATUS_CMD_DENY 0x12 +#define AMD_SMB_STATUS_UNKNOWN 0x13 +#define AMD_SMB_STATUS_ACC_DENY 0x17 +#define AMD_SMB_STATUS_TIMEOUT 0x18 +#define AMD_SMB_STATUS_NOTSUP 0x19 +#define AMD_SMB_STATUS_BUSY 0x1A +#define AMD_SMB_STATUS_PEC 0x1F + +#define AMD_SMB_PRTCL_WRITE 0x00 +#define AMD_SMB_PRTCL_READ 0x01 +#define AMD_SMB_PRTCL_QUICK 0x02 +#define AMD_SMB_PRTCL_BYTE 0x04 +#define AMD_SMB_PRTCL_BYTE_DATA 0x06 +#define AMD_SMB_PRTCL_WORD_DATA 0x08 +#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a +#define AMD_SMB_PRTCL_PROC_CALL 0x0c +#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d +#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a +#define AMD_SMB_PRTCL_PEC 0x80 + + +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) +{ + struct amd_smbus *smbus = adap->algo_data; + unsigned char protocol, len, pec, temp[2]; + int i, status; + + protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ + : AMD_SMB_PRTCL_WRITE; + 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); + 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; + + 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); + 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; + + 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; + 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); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); + if (status) + return status; + + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + + if (~temp[0] & AMD_SMB_STS_DONE) { + udelay(500); + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + } + + if (~temp[0] & AMD_SMB_STS_DONE) { + msleep(1); + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + } + + if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) + return -EIO; + + if (read_write == I2C_SMBUS_WRITE) + 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_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); + 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; + } + data->block[0] = len; + break; + } + + return 0; +} + + +static u32 amd8111_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_BLOCK_DATA | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = amd8111_access, + .functionality = amd8111_func, +}; + + +static const struct pci_device_id amd8111_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, amd8111_ids); + +static int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct amd_smbus *smbus; + int error; + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) + return -ENODEV; + + smbus = devm_kzalloc(&dev->dev, sizeof(struct amd_smbus), GFP_KERNEL); + if (!smbus) + return -ENOMEM; + + smbus->dev = dev; + smbus->base = pci_resource_start(dev, 0); + smbus->size = pci_resource_len(dev, 0); + + error = acpi_check_resource_conflict(&dev->resource[0]); + if (error) + return -ENODEV; + + if (!devm_request_region(&dev->dev, smbus->base, smbus->size, amd8111_driver.name)) + return -EBUSY; + + 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; + 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; + + pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); + error = i2c_add_adapter(&smbus->adapter); + if (error) + return error; + + pci_set_drvdata(dev, smbus); + return 0; +} + +static void amd8111_remove(struct pci_dev *dev) +{ + struct amd_smbus *smbus = pci_get_drvdata(dev); + + i2c_del_adapter(&smbus->adapter); +} + +static struct pci_driver amd8111_driver = { + .name = "amd8111_smbus2", + .id_table = amd8111_ids, + .probe = amd8111_probe, + .remove = amd8111_remove, +}; + +module_pci_driver(amd8111_driver); diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c new file mode 100644 index 000000000..a26b74c71 --- /dev/null +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -0,0 +1,1115 @@ +// 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C Register */ +#define ASPEED_I2C_FUN_CTRL_REG 0x00 +#define ASPEED_I2C_AC_TIMING_REG1 0x04 +#define ASPEED_I2C_AC_TIMING_REG2 0x08 +#define ASPEED_I2C_INTR_CTRL_REG 0x0c +#define ASPEED_I2C_INTR_STS_REG 0x10 +#define ASPEED_I2C_CMD_REG 0x14 +#define ASPEED_I2C_DEV_ADDR_REG 0x18 +#define ASPEED_I2C_BYTE_BUF_REG 0x20 + +/* Global Register Definition */ +/* 0x00 : I2C Interrupt Status Register */ +/* 0x08 : I2C Interrupt Target Assignment */ + +/* Device Register Definition */ +/* 0x00 : I2CD Function Control Register */ +#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15) +#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) +#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) +#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) +#define ASPEED_I2CD_SLAVE_EN BIT(1) +#define ASPEED_I2CD_MASTER_EN BIT(0) + +/* 0x04 : I2CD Clock and AC Timing Control Register #1 */ +#define ASPEED_I2CD_TIME_TBUF_MASK GENMASK(31, 28) +#define ASPEED_I2CD_TIME_THDSTA_MASK GENMASK(27, 24) +#define ASPEED_I2CD_TIME_TACST_MASK GENMASK(23, 20) +#define ASPEED_I2CD_TIME_SCL_HIGH_SHIFT 16 +#define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16) +#define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12 +#define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12) +#define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0) +#define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0) +/* 0x08 : I2CD Clock and AC Timing Control Register #2 */ +#define ASPEED_NO_TIMEOUT_CTRL 0 + +/* 0x0c : I2CD Interrupt Control Register & + * 0x10 : I2CD Interrupt Status Register + * + * These share bit definitions, so use the same values for the enable & + * status bits. + */ +#define 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) +#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6) +#define ASPEED_I2CD_INTR_ABNORMAL BIT(5) +#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4) +#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3) +#define ASPEED_I2CD_INTR_RX_DONE BIT(2) +#define ASPEED_I2CD_INTR_TX_NAK BIT(1) +#define ASPEED_I2CD_INTR_TX_ACK BIT(0) +#define ASPEED_I2CD_INTR_MASTER_ERRORS \ + (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ + ASPEED_I2CD_INTR_SCL_TIMEOUT | \ + ASPEED_I2CD_INTR_ABNORMAL | \ + ASPEED_I2CD_INTR_ARBIT_LOSS) +#define ASPEED_I2CD_INTR_ALL \ + (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ + ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \ + ASPEED_I2CD_INTR_SCL_TIMEOUT | \ + ASPEED_I2CD_INTR_ABNORMAL | \ + ASPEED_I2CD_INTR_NORMAL_STOP | \ + ASPEED_I2CD_INTR_ARBIT_LOSS | \ + ASPEED_I2CD_INTR_RX_DONE | \ + ASPEED_I2CD_INTR_TX_NAK | \ + ASPEED_I2CD_INTR_TX_ACK) + +/* 0x14 : I2CD Command/Status Register */ +#define ASPEED_I2CD_SCL_LINE_STS BIT(18) +#define ASPEED_I2CD_SDA_LINE_STS BIT(17) +#define ASPEED_I2CD_BUS_BUSY_STS BIT(16) +#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) + +/* Command Bit */ +#define ASPEED_I2CD_M_STOP_CMD BIT(5) +#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4) +#define ASPEED_I2CD_M_RX_CMD BIT(3) +#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, + ASPEED_I2C_MASTER_RX_FIRST, + ASPEED_I2C_MASTER_RX, + ASPEED_I2C_MASTER_STOP, +}; + +enum aspeed_i2c_slave_state { + 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 { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct reset_control *rst; + /* Synchronizes I/O mem access to base. */ + spinlock_t lock; + struct completion cmd_complete; + u32 (*get_clk_reg_val)(struct device *dev, + u32 divisor); + unsigned long parent_clk_frequency; + u32 bus_frequency; + /* Transaction state. */ + enum aspeed_i2c_master_state master_state; + struct i2c_msg *msgs; + size_t buf_index; + size_t msgs_index; + size_t msgs_count; + bool send_stop; + 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; +#endif /* CONFIG_I2C_SLAVE */ +}; + +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; + int ret = 0; + u32 command; + + spin_lock_irqsave(&bus->lock, flags); + command = readl(bus->base + ASPEED_I2C_CMD_REG); + + if (command & ASPEED_I2CD_SDA_LINE_STS) { + /* Bus is idle: no recovery needed. */ + if (command & ASPEED_I2CD_SCL_LINE_STS) + goto out; + dev_dbg(bus->dev, "SCL hung (state %x), attempting recovery\n", + command); + + reinit_completion(&bus->cmd_complete); + aspeed_i2c_do_stop(bus); + spin_unlock_irqrestore(&bus->lock, flags); + + time_left = wait_for_completion_timeout( + &bus->cmd_complete, bus->adap.timeout); + + spin_lock_irqsave(&bus->lock, flags); + if (time_left == 0) + goto reset_out; + else if (bus->cmd_err) + goto reset_out; + /* Recovery failed. */ + else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_SCL_LINE_STS)) + goto reset_out; + /* Bus error. */ + } else { + dev_dbg(bus->dev, "SDA hung (state %x), attempting recovery\n", + command); + + reinit_completion(&bus->cmd_complete); + /* Writes 1 to 8 SCL clock cycles until SDA is released. */ + writel(ASPEED_I2CD_BUS_RECOVER_CMD, + bus->base + ASPEED_I2C_CMD_REG); + spin_unlock_irqrestore(&bus->lock, flags); + + time_left = wait_for_completion_timeout( + &bus->cmd_complete, bus->adap.timeout); + + spin_lock_irqsave(&bus->lock, flags); + if (time_left == 0) + goto reset_out; + else if (bus->cmd_err) + goto reset_out; + /* Recovery failed. */ + else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_SDA_LINE_STS)) + goto reset_out; + } + +out: + spin_unlock_irqrestore(&bus->lock, flags); + + return ret; + +reset_out: + spin_unlock_irqrestore(&bus->lock, flags); + + return aspeed_i2c_reset(bus); +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +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; + + /* + * 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; + } + + /* + * 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; + } + + /* + * 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); + + /* Slave was sent something. */ + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { + value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; + /* Handle address frame. */ + if (bus->slave_state == ASPEED_I2C_SLAVE_START) { + if (value & 0x1) + bus->slave_state = + ASPEED_I2C_SLAVE_READ_REQUESTED; + else + bus->slave_state = + ASPEED_I2C_SLAVE_WRITE_REQUESTED; + } + irq_handled |= ASPEED_I2CD_INTR_RX_DONE; + } + + switch (bus->slave_state) { + case ASPEED_I2C_SLAVE_READ_REQUESTED: + 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); + 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_READ_PROCESSED: + 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; + 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: + /* 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, "unknown slave_state: %d\n", + bus->slave_state); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + } + + return irq_handled; +} +#endif /* CONFIG_I2C_SLAVE */ + +/* precondition: bus.lock has been acquired. */ +static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) +{ + u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD; + 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; + + if (msg->flags & I2C_M_RD) { + command |= ASPEED_I2CD_M_RX_CMD; + /* Need to let the hardware know to NACK after RX. */ + if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + } + + writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(command, 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) { + bus->msgs_index++; + aspeed_i2c_do_start(bus); + } else { + aspeed_i2c_do_stop(bus); + } +} + +static int aspeed_i2c_is_irq_error(u32 irq_status) +{ + if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS) + return -EAGAIN; + if (irq_status & (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | + ASPEED_I2CD_INTR_SCL_TIMEOUT)) + return -EBUSY; + if (irq_status & (ASPEED_I2CD_INTR_ABNORMAL)) + return -EPROTO; + + return 0; +} + +static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) +{ + u32 irq_handled = 0, command = 0; + struct i2c_msg *msg; + u8 recv_byte; + int ret; + + if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) { + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; + goto out_complete; + } + + /* + * We encountered an interrupt that reports an error: the hardware + * should clear the command queue effectively taking us back to the + * INACTIVE state. + */ + ret = aspeed_i2c_is_irq_error(irq_status); + if (ret) { + dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", + irq_status); + irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); + 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", + irq_status); + bus->cmd_err = -EIO; + if (bus->master_state != ASPEED_I2C_MASTER_STOP && + bus->master_state != ASPEED_I2C_MASTER_INACTIVE) + aspeed_i2c_do_stop(bus); + goto out_no_complete; + } + msg = &bus->msgs[bus->msgs_index]; + + /* + * START is a special case because we still have to handle a subsequent + * TX or RX immediately after we handle it, so we handle it here and + * 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; + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + goto out_complete; + } + pr_devel("no slave present at %02x\n", msg->addr); + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; + bus->cmd_err = -ENXIO; + aspeed_i2c_do_stop(bus); + goto out_no_complete; + } + irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + if (msg->len == 0) { /* SMBUS_QUICK */ + aspeed_i2c_do_stop(bus); + goto out_no_complete; + } + if (msg->flags & I2C_M_RD) + bus->master_state = ASPEED_I2C_MASTER_RX_FIRST; + else + bus->master_state = ASPEED_I2C_MASTER_TX_FIRST; + } + + switch (bus->master_state) { + case ASPEED_I2C_MASTER_TX: + if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) { + dev_dbg(bus->dev, "slave NACKed TX\n"); + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; + goto error_and_stop; + } else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { + dev_err(bus->dev, "slave failed to ACK TX\n"); + goto error_and_stop; + } + irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + fallthrough; + case ASPEED_I2C_MASTER_TX_FIRST: + if (bus->buf_index < msg->len) { + bus->master_state = ASPEED_I2C_MASTER_TX; + writel(msg->buf[bus->buf_index++], + bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(ASPEED_I2CD_M_TX_CMD, + bus->base + ASPEED_I2C_CMD_REG); + } else { + aspeed_i2c_next_msg_or_stop(bus); + } + goto out_no_complete; + case ASPEED_I2C_MASTER_RX_FIRST: + /* RX may not have completed yet (only address cycle) */ + if (!(irq_status & ASPEED_I2CD_INTR_RX_DONE)) + goto out_no_complete; + fallthrough; + case ASPEED_I2C_MASTER_RX: + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) { + dev_err(bus->dev, "master failed to RX\n"); + goto error_and_stop; + } + irq_handled |= ASPEED_I2CD_INTR_RX_DONE; + + recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; + msg->buf[bus->buf_index++] = recv_byte; + + if (msg->flags & I2C_M_RECV_LEN) { + if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { + bus->cmd_err = -EPROTO; + aspeed_i2c_do_stop(bus); + goto out_no_complete; + } + msg->len = recv_byte + + ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1); + msg->flags &= ~I2C_M_RECV_LEN; + } + + if (bus->buf_index < msg->len) { + bus->master_state = ASPEED_I2C_MASTER_RX; + command = ASPEED_I2CD_M_RX_CMD; + if (bus->buf_index + 1 == msg->len) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + writel(command, bus->base + ASPEED_I2C_CMD_REG); + } else { + aspeed_i2c_next_msg_or_stop(bus); + } + goto out_no_complete; + case ASPEED_I2C_MASTER_STOP: + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) { + dev_err(bus->dev, + "master failed to STOP. irq_status:0x%x\n", + irq_status); + bus->cmd_err = -EIO; + /* Do not STOP as we have already tried. */ + } else { + irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; + } + + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + goto out_complete; + case ASPEED_I2C_MASTER_INACTIVE: + dev_err(bus->dev, + "master received interrupt 0x%08x, but is inactive\n", + irq_status); + bus->cmd_err = -EIO; + /* Do not STOP as we should be inactive. */ + goto out_complete; + default: + WARN(1, "unknown master state\n"); + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + bus->cmd_err = -EINVAL; + goto out_complete; + } +error_and_stop: + bus->cmd_err = -EIO; + aspeed_i2c_do_stop(bus); + goto out_no_complete; +out_complete: + bus->msgs = NULL; + if (bus->cmd_err) + bus->master_xfer_result = bus->cmd_err; + else + bus->master_xfer_result = bus->msgs_index + 1; + complete(&bus->cmd_complete); +out_no_complete: + return irq_handled; +} + +static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) +{ + struct aspeed_i2c_bus *bus = dev_id; + u32 irq_received, irq_remaining, irq_handled; + + spin_lock(&bus->lock); + irq_received = readl(bus->base + ASPEED_I2C_INTR_STS_REG); + /* 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) + /* + * In most cases, interrupt bits will be set one by one, although + * multiple interrupt bits could be set at the same time. It's also + * possible that master interrupt bits could be set along with slave + * interrupt bits. Each case needs to be handled using corresponding + * handlers depending on the current state. + */ + 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) + irq_handled |= aspeed_i2c_slave_irq(bus, irq_remaining); + } else { + irq_handled = aspeed_i2c_slave_irq(bus, irq_remaining); + irq_remaining &= ~irq_handled; + if (irq_remaining) + 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 */ + + irq_remaining &= ~irq_handled; + if (irq_remaining) + dev_err(bus->dev, + "irq handled != irq. expected 0x%08x, but was 0x%08x\n", + irq_received, irq_handled); + + /* Ack 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; +} + +static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; + + /* 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) + return ret; + spin_lock_irqsave(&bus->lock, flags); + } + + bus->cmd_err = 0; + bus->msgs = msgs; + bus->msgs_index = 0; + bus->msgs_count = num; + + reinit_completion(&bus->cmd_complete); + aspeed_i2c_do_start(bus); + spin_unlock_irqrestore(&bus->lock, flags); + + time_left = wait_for_completion_timeout(&bus->cmd_complete, + bus->adap.timeout); + + 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; + } + + return bus->master_xfer_result; +} + +static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +/* precondition: bus.lock has been acquired. */ +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. 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) +{ + struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); + unsigned long flags; + + spin_lock_irqsave(&bus->lock, flags); + if (bus->slave) { + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + + __aspeed_i2c_reg_slave(bus, client->addr); + + bus->slave = client; + spin_unlock_irqrestore(&bus->lock, flags); + + return 0; +} + +static int aspeed_i2c_unreg_slave(struct i2c_client *client) +{ + struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); + u32 func_ctrl_reg_val; + unsigned long flags; + + spin_lock_irqsave(&bus->lock, flags); + if (!bus->slave) { + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + + /* Turn off 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 = NULL; + spin_unlock_irqrestore(&bus->lock, flags); + + return 0; +} +#endif /* CONFIG_I2C_SLAVE */ + +static const struct i2c_algorithm aspeed_i2c_algo = { + .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, +#endif /* CONFIG_I2C_SLAVE */ +}; + +static u32 aspeed_i2c_get_clk_reg_val(struct device *dev, + u32 clk_high_low_mask, + u32 divisor) +{ + u32 base_clk_divisor, clk_high_low_max, clk_high, clk_low, tmp; + + /* + * SCL_high and SCL_low represent a value 1 greater than what is stored + * since a zero divider is meaningless. Thus, the max value each can + * store is every bit set + 1. Since SCL_high and SCL_low are added + * together (see below), the max value of both is the max value of one + * them times two. + */ + clk_high_low_max = (clk_high_low_mask + 1) * 2; + + /* + * The actual clock frequency of SCL is: + * SCL_freq = APB_freq / (base_freq * (SCL_high + SCL_low)) + * = APB_freq / divisor + * where base_freq is a programmable clock divider; its value is + * base_freq = 1 << base_clk_divisor + * SCL_high is the number of base_freq clock cycles that SCL stays high + * and SCL_low is the number of base_freq clock cycles that SCL stays + * low for a period of SCL. + * The actual register has a minimum SCL_high and SCL_low minimum of 1; + * thus, they start counting at zero. So + * SCL_high = clk_high + 1 + * SCL_low = clk_low + 1 + * Thus, + * SCL_freq = APB_freq / + * ((1 << base_clk_divisor) * (clk_high + 1 + clk_low + 1)) + * The documentation recommends clk_high >= clk_high_max / 2 and + * clk_low >= clk_low_max / 2 - 1 when possible; this last constraint + * gives us the following solution: + */ + base_clk_divisor = divisor > clk_high_low_max ? + ilog2((divisor - 1) / clk_high_low_max) + 1 : 0; + + if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) { + base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK; + clk_low = clk_high_low_mask; + clk_high = clk_high_low_mask; + dev_err(dev, + "clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.\n", + divisor, (1 << base_clk_divisor) * clk_high_low_max); + } else { + tmp = (divisor + (1 << base_clk_divisor) - 1) + >> base_clk_divisor; + clk_low = tmp / 2; + clk_high = tmp - clk_low; + + if (clk_high) + clk_high--; + + if (clk_low) + clk_low--; + } + + + return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT) + & ASPEED_I2CD_TIME_SCL_HIGH_MASK) + | ((clk_low << ASPEED_I2CD_TIME_SCL_LOW_SHIFT) + & ASPEED_I2CD_TIME_SCL_LOW_MASK) + | (base_clk_divisor + & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK); +} + +static u32 aspeed_i2c_24xx_get_clk_reg_val(struct device *dev, u32 divisor) +{ + /* + * clk_high and clk_low are each 3 bits wide, so each can hold a max + * value of 8 giving a clk_high_low_max of 16. + */ + return aspeed_i2c_get_clk_reg_val(dev, GENMASK(2, 0), divisor); +} + +static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor) +{ + /* + * clk_high and clk_low are each 4 bits wide, so each can hold a max + * value of 16 giving a clk_high_low_max of 32. + */ + return aspeed_i2c_get_clk_reg_val(dev, GENMASK(3, 0), divisor); +} + +/* precondition: bus.lock has been acquired. */ +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) +{ + u32 divisor, clk_reg_val; + + divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); + clk_reg_val = readl(bus->base + ASPEED_I2C_AC_TIMING_REG1); + clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK | + ASPEED_I2CD_TIME_THDSTA_MASK | + ASPEED_I2CD_TIME_TACST_MASK); + clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor); + writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1); + writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2); + + return 0; +} + +/* precondition: bus.lock has been acquired. */ +static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + struct platform_device *pdev) +{ + u32 fun_ctrl_reg = ASPEED_I2CD_MASTER_EN; + int ret; + + /* Disable everything. */ + writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); + + ret = aspeed_i2c_init_clk(bus); + if (ret < 0) + return ret; + + 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 */ + writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg, + bus->base + ASPEED_I2C_FUN_CTRL_REG); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* If slave has already been registered, re-enable it. */ + if (bus->slave) + __aspeed_i2c_reg_slave(bus, bus->slave->addr); +#endif /* CONFIG_I2C_SLAVE */ + + /* Set interrupt generation of I2C controller */ + writel(ASPEED_I2CD_INTR_ALL, bus->base + ASPEED_I2C_INTR_CTRL_REG); + + return 0; +} + +static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus) +{ + struct platform_device *pdev = to_platform_device(bus->dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&bus->lock, flags); + + /* Disable and ack all interrupts. */ + writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); + writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); + + ret = aspeed_i2c_init(bus, pdev); + + spin_unlock_irqrestore(&bus->lock, flags); + + return ret; +} + +static const struct of_device_id aspeed_i2c_bus_of_table[] = { + { + .compatible = "aspeed,ast2400-i2c-bus", + .data = aspeed_i2c_24xx_get_clk_reg_val, + }, + { + .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); + +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; + int irq, ret; + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + + bus->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(bus->base)) + return PTR_ERR(bus->base); + + parent_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(parent_clk)) + return PTR_ERR(parent_clk); + bus->parent_clk_frequency = clk_get_rate(parent_clk); + /* We just need the clock rate, we don't actually use the clk object. */ + devm_clk_put(&pdev->dev, parent_clk); + + bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL); + if (IS_ERR(bus->rst)) { + dev_err(&pdev->dev, + "missing or invalid reset controller device tree entry\n"); + return PTR_ERR(bus->rst); + } + reset_control_deassert(bus->rst); + + ret = of_property_read_u32(pdev->dev.of_node, + "bus-frequency", &bus->bus_frequency); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not read bus-frequency property\n"); + bus->bus_frequency = I2C_MAX_STANDARD_MODE_FREQ; + } + + match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node); + if (!match) + bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val; + else + bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) + match->data; + + /* Initialize the I2C adapter */ + spin_lock_init(&bus->lock); + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; + bus->adap.retries = 0; + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; + strscpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); + i2c_set_adapdata(&bus->adap, bus); + + bus->dev = &pdev->dev; + + /* Clean up any left over interrupt state. */ + writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); + writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); + /* + * bus.lock does not need to be held because the interrupt handler has + * not been enabled yet. + */ + ret = aspeed_i2c_init(bus, pdev); + if (ret < 0) + return ret; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, + 0, dev_name(&pdev->dev), bus); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&bus->adap); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, bus); + + dev_info(bus->dev, "i2c bus %d registered, irq %d\n", + bus->adap.nr, irq); + + return 0; +} + +static void aspeed_i2c_remove_bus(struct platform_device *pdev) +{ + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&bus->lock, flags); + + /* Disable everything. */ + writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); + writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); + + spin_unlock_irqrestore(&bus->lock, flags); + + reset_control_assert(bus->rst); + + i2c_del_adapter(&bus->adap); +} + +static struct platform_driver aspeed_i2c_bus_driver = { + .probe = aspeed_i2c_probe_bus, + .remove = aspeed_i2c_remove_bus, + .driver = { + .name = "aspeed-i2c-bus", + .of_match_table = aspeed_i2c_bus_of_table, + }, +}; +module_platform_driver(aspeed_i2c_bus_driver); + +MODULE_AUTHOR("Brendan Higgins "); +MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c new file mode 100644 index 000000000..b64adef77 --- /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 + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:at91_i2c"); diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c new file mode 100644 index 000000000..894cedbca --- /dev/null +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -0,0 +1,921 @@ +// 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 + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-at91.h" + +void at91_init_twi_bus_master(struct at91_twi_dev *dev) +{ + struct at91_twi_pdata *pdata = dev->pdata; + u32 filtr = 0; + + /* 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 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; + 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 * t->bus_freq_hz) - offset); + ckdiv = fls(div >> 8); + cdiv = div >> ckdiv; + + if (ckdiv > max_ckdiv) { + dev_warn(dev->dev, "%d exceeds ckdiv max value which is %d.\n", + ckdiv, max_ckdiv); + ckdiv = max_ckdiv; + cdiv = 255; + } + + if (pdata->has_hold_field) { + /* + * hold time = HOLD + 3 x T_peripheral_clock + * Use clk rate in kHz to prevent overflows when computing + * hold. + */ + hold = DIV_ROUND_UP(t->sda_hold_ns + * (clk_get_rate(dev->clk) / 1000), 1000000); + hold -= 3; + if (hold < 0) + hold = 0; + if (hold > AT91_TWI_CWGR_HOLD_MAX) { + dev_warn(dev->dev, + "HOLD field set to its maximum value (%d instead of %d)\n", + AT91_TWI_CWGR_HOLD_MAX, hold); + hold = AT91_TWI_CWGR_HOLD_MAX; + } + } + + 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->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) +{ + struct at91_twi_dma *dma = &dev->dma; + + at91_twi_irq_save(dev); + + if (dma->xfer_in_progress) { + if (dma->direction == DMA_FROM_DEVICE) + dmaengine_terminate_sync(dma->chan_rx); + else + dmaengine_terminate_sync(dma->chan_tx); + dma->xfer_in_progress = false; + } + if (dma->buf_mapped) { + dma_unmap_single(dev->dev, sg_dma_address(&dma->sg[0]), + dev->buf_len, dma->direction); + dma->buf_mapped = false; + } + + at91_twi_irq_restore(dev); +} + +static void at91_twi_write_next_byte(struct at91_twi_dev *dev) +{ + if (!dev->buf_len) + return; + + /* 8bit write works with and without FIFO */ + writeb_relaxed(*dev->buf, dev->base + AT91_TWI_THR); + + /* send stop when last byte has been written */ + 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); + + ++dev->buf; +} + +static void at91_twi_write_data_dma_callback(void *data) +{ + struct at91_twi_dev *dev = (struct at91_twi_dev *)data; + + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), + dev->buf_len, DMA_TO_DEVICE); + + /* + * When this callback is called, THR/TX FIFO is likely not to be empty + * yet. So we have to wait for TXCOMP or NACK bits to be set into the + * Status Register to be sure that the STOP bit has been sent and the + * transfer is completed. The NACK interrupt has already been enabled, + * we just have to enable TXCOMP one. + */ + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); + if (!dev->use_alt_cmd) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); +} + +static void at91_twi_write_data_dma(struct at91_twi_dev *dev) +{ + dma_addr_t dma_addr; + struct dma_async_tx_descriptor *txdesc; + struct at91_twi_dma *dma = &dev->dma; + struct dma_chan *chan_tx = dma->chan_tx; + unsigned int sg_len = 1; + + if (!dev->buf_len) + return; + + dma->direction = DMA_TO_DEVICE; + + at91_twi_irq_save(dev); + dma_addr = dma_map_single(dev->dev, dev->buf, dev->buf_len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev->dev, dma_addr)) { + dev_err(dev->dev, "dma map failed\n"); + return; + } + dma->buf_mapped = true; + at91_twi_irq_restore(dev); + + if (dev->fifo_size) { + size_t part1_len, part2_len; + struct scatterlist *sg; + unsigned fifo_mr; + + sg_len = 0; + + part1_len = dev->buf_len & ~0x3; + if (part1_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part1_len; + sg_dma_address(sg) = dma_addr; + } + + part2_len = dev->buf_len & 0x3; + if (part2_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part2_len; + sg_dma_address(sg) = dma_addr + part1_len; + } + + /* + * DMA controller is triggered when at least 4 data can be + * written into the TX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_TXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } else { + sg_dma_len(&dma->sg[0]) = dev->buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + } + + txdesc = dmaengine_prep_slave_sg(chan_tx, dma->sg, sg_len, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_err(dev->dev, "dma prep slave sg failed\n"); + goto error; + } + + txdesc->callback = at91_twi_write_data_dma_callback; + txdesc->callback_param = dev; + + dma->xfer_in_progress = true; + dmaengine_submit(txdesc); + dma_async_issue_pending(chan_tx); + + return; + +error: + at91_twi_dma_cleanup(dev); +} + +static void at91_twi_read_next_byte(struct at91_twi_dev *dev) +{ + /* + * If we are in this case, it means there is garbage data in RHR, so + * delete them. + */ + if (!dev->buf_len) { + at91_twi_read(dev, AT91_TWI_RHR); + return; + } + + /* 8bit read works with and without FIFO */ + *dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR); + --dev->buf_len; + + /* return if aborting, we only needed to read RHR to clear RXRDY*/ + if (dev->recv_len_abort) + return; + + /* handle I2C_SMBUS_BLOCK_DATA */ + if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) { + /* ensure length byte is a valid value */ + if (*dev->buf <= I2C_SMBUS_BLOCK_MAX && *dev->buf > 0) { + dev->msg->flags &= ~I2C_M_RECV_LEN; + dev->buf_len += *dev->buf; + dev->msg->len = dev->buf_len + 1; + dev_dbg(dev->dev, "received block length %zu\n", + dev->buf_len); + } else { + /* abort and send the stop by reading one more byte */ + dev->recv_len_abort = true; + dev->buf_len = 1; + } + } + + /* send stop if second but last byte has been read */ + if (!dev->use_alt_cmd && dev->buf_len == 1) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); + + dev_dbg(dev->dev, "read 0x%x, to go %zu\n", *dev->buf, dev->buf_len); + + ++dev->buf; +} + +static void at91_twi_read_data_dma_callback(void *data) +{ + struct at91_twi_dev *dev = (struct at91_twi_dev *)data; + unsigned ier = AT91_TWI_TXCOMP; + + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), + dev->buf_len, DMA_FROM_DEVICE); + + if (!dev->use_alt_cmd) { + /* The last two bytes have to be read without using dma */ + dev->buf += dev->buf_len - 2; + dev->buf_len = 2; + ier |= AT91_TWI_RXRDY; + } + at91_twi_write(dev, AT91_TWI_IER, ier); +} + +static void at91_twi_read_data_dma(struct at91_twi_dev *dev) +{ + dma_addr_t dma_addr; + struct dma_async_tx_descriptor *rxdesc; + struct at91_twi_dma *dma = &dev->dma; + struct dma_chan *chan_rx = dma->chan_rx; + size_t buf_len; + + buf_len = (dev->use_alt_cmd) ? dev->buf_len : dev->buf_len - 2; + dma->direction = DMA_FROM_DEVICE; + + /* Keep in mind that we won't use dma to read the last two bytes */ + at91_twi_irq_save(dev); + dma_addr = dma_map_single(dev->dev, dev->buf, buf_len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev->dev, dma_addr)) { + dev_err(dev->dev, "dma map failed\n"); + return; + } + dma->buf_mapped = true; + at91_twi_irq_restore(dev); + + if (dev->fifo_size && IS_ALIGNED(buf_len, 4)) { + unsigned fifo_mr; + + /* + * DMA controller is triggered when at least 4 data can be + * read from the RX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_RXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } + + sg_dma_len(&dma->sg[0]) = buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + + rxdesc = dmaengine_prep_slave_sg(chan_rx, dma->sg, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) { + dev_err(dev->dev, "dma prep slave sg failed\n"); + goto error; + } + + rxdesc->callback = at91_twi_read_data_dma_callback; + rxdesc->callback_param = dev; + + dma->xfer_in_progress = true; + dmaengine_submit(rxdesc); + dma_async_issue_pending(dma->chan_rx); + + return; + +error: + at91_twi_dma_cleanup(dev); +} + +static irqreturn_t atmel_twi_interrupt(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); + + if (!irqstatus) + return IRQ_NONE; + /* + * In reception, the behavior of the twi device (before sama5d2) is + * weird. There is some magic about RXRDY flag! When a data has been + * almost received, the reception of a new one is anticipated if there + * is no stop command to send. That is the reason why ask for sending + * the stop command not on the last data but on the second last one. + * + * Unfortunately, we could still have the RXRDY flag set even if the + * transfer is done and we have read the last data. It might happen + * when the i2c slave device sends too quickly data after receiving the + * ack from the master. The data has been almost received before having + * the order to send stop. In this case, sending the stop command could + * cause a RXRDY interrupt with a TXCOMP one. It is better to manage + * the RXRDY interrupt first in order to not keep garbage data in the + * Receive Holding Register for the next transfer. + */ + if (irqstatus & AT91_TWI_RXRDY) { + /* + * Read all available bytes at once by polling RXRDY usable w/ + * and w/o FIFO. With FIFO enabled we could also read RXFL and + * avoid polling RXRDY. + */ + do { + at91_twi_read_next_byte(dev); + } while (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY); + } + + /* + * When a NACK condition is detected, the I2C controller sets the NACK, + * TXCOMP and TXRDY bits all together in the Status Register (SR). + * + * 1 - Handling NACK errors with CPU write transfer. + * + * In such case, we should not write the next byte into the Transmit + * Holding Register (THR) otherwise the I2C controller would start a new + * transfer and the I2C slave is likely to reply by another NACK. + * + * 2 - Handling NACK errors with DMA write transfer. + * + * By setting the TXRDY bit in the SR, the I2C controller also triggers + * the DMA controller to write the next data into the THR. Then the + * result depends on the hardware version of the I2C controller. + * + * 2a - Without support of the Alternative Command mode. + * + * This is the worst case: the DMA controller is triggered to write the + * next data into the THR, hence starting a new transfer: the I2C slave + * is likely to reply by another NACK. + * Concurrently, this interrupt handler is likely to be called to manage + * the first NACK before the I2C controller detects the second NACK and + * sets once again the NACK bit into the SR. + * When handling the first NACK, this interrupt handler disables the I2C + * controller interruptions, especially the NACK interrupt. + * Hence, the NACK bit is pending into the SR. This is why we should + * read the SR to clear all pending interrupts at the beginning of + * at91_do_twi_transfer() before actually starting a new transfer. + * + * 2b - With support of the Alternative Command mode. + * + * When a NACK condition is detected, the I2C controller also locks the + * THR (and sets the LOCK bit in the SR): even though the DMA controller + * is triggered by the TXRDY bit to write the next data into the THR, + * this data actually won't go on the I2C bus hence a second NACK is not + * generated. + */ + if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) { + at91_disable_twi_interrupts(dev); + complete(&dev->cmd_complete); + } else if (irqstatus & AT91_TWI_TXRDY) { + at91_twi_write_next_byte(dev); + } + + /* catch error flags */ + dev->transfer_status |= status; + + return IRQ_HANDLED; +} + +static int at91_do_twi_transfer(struct at91_twi_dev *dev) +{ + int ret; + unsigned long time_left; + bool has_unre_flag = dev->pdata->has_unre_flag; + bool has_alt_cmd = dev->pdata->has_alt_cmd; + + /* + * WARNING: the TXCOMP bit in the Status Register is NOT a clear on + * read flag but shows the state of the transmission at the time the + * Status Register is read. According to the programmer datasheet, + * TXCOMP is set when both holding register and internal shifter are + * empty and STOP condition has been sent. + * Consequently, we should enable NACK interrupt rather than TXCOMP to + * detect transmission failure. + * Indeed let's take the case of an i2c write command using DMA. + * Whenever the slave doesn't acknowledge a byte, the LOCK, NACK and + * TXCOMP bits are set together into the Status Register. + * LOCK is a clear on write bit, which is set to prevent the DMA + * controller from sending new data on the i2c bus after a NACK + * condition has happened. Once locked, this i2c peripheral stops + * triggering the DMA controller for new data but it is more than + * likely that a new DMA transaction is already in progress, writing + * into the Transmit Holding Register. Since the peripheral is locked, + * these new data won't be sent to the i2c bus but they will remain + * into the Transmit Holding Register, so TXCOMP bit is cleared. + * Then when the interrupt handler is called, the Status Register is + * read: the TXCOMP bit is clear but NACK bit is still set. The driver + * manage the error properly, without waiting for timeout. + * This case can be reproduced easyly when writing into an at24 eeprom. + * + * Besides, the TXCOMP bit is already set before the i2c transaction + * has been started. For read transactions, this bit is cleared when + * writing the START bit into the Control Register. So the + * corresponding interrupt can safely be enabled just after. + * However for write transactions managed by the CPU, we first write + * into THR, so TXCOMP is cleared. Then we can safely enable TXCOMP + * interrupt. If TXCOMP interrupt were enabled before writing into THR, + * the interrupt handler would be called immediately and the i2c command + * would be reported as completed. + * Also when a write transaction is managed by the DMA controller, + * enabling the TXCOMP interrupt in this function may lead to a race + * condition since we don't know whether the TXCOMP interrupt is enabled + * before or after the DMA has started to write into THR. So the TXCOMP + * interrupt is enabled later by at91_twi_write_data_dma_callback(). + * Immediately after in that DMA callback, if the alternative command + * mode is not used, we still need to send the STOP condition manually + * writing the corresponding bit into the Control Register. + */ + + dev_dbg(dev->dev, "transfer: %s %zu bytes.\n", + str_read_write(dev->msg->flags & I2C_M_RD), dev->buf_len); + + reinit_completion(&dev->cmd_complete); + dev->transfer_status = 0; + + /* Clear pending interrupts, such as NACK. */ + at91_twi_read(dev, AT91_TWI_SR); + + if (dev->fifo_size) { + unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + + /* Reset FIFO mode register */ + fifo_mr &= ~(AT91_TWI_FMR_TXRDYM_MASK | + AT91_TWI_FMR_RXRDYM_MASK); + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_ONE_DATA); + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_ONE_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + + /* Flush FIFOs */ + at91_twi_write(dev, AT91_TWI_CR, + AT91_TWI_THRCLR | AT91_TWI_RHRCLR); + } + + if (!dev->buf_len) { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK); + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); + } else if (dev->msg->flags & I2C_M_RD) { + unsigned start_flags = AT91_TWI_START; + + /* if only one byte is to be read, immediately stop transfer */ + if (!dev->use_alt_cmd && dev->buf_len <= 1 && + !(dev->msg->flags & I2C_M_RECV_LEN)) + start_flags |= AT91_TWI_STOP; + at91_twi_write(dev, AT91_TWI_CR, start_flags); + /* + * When using dma without alternative command mode, the last + * byte has to be read manually in order to not send the stop + * command too late and then to receive extra data. + * In practice, there are some issues if you use the dma to + * read n-1 bytes because of latency. + * Reading n-2 bytes with dma and the two last ones manually + * seems to be the best solution. + */ + if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) { + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); + at91_twi_read_data_dma(dev); + } else { + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXCOMP | + AT91_TWI_NACK | + AT91_TWI_RXRDY); + } + } else { + if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) { + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); + at91_twi_write_data_dma(dev); + } else { + at91_twi_write_next_byte(dev); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXCOMP | AT91_TWI_NACK | + (dev->buf_len ? AT91_TWI_TXRDY : 0)); + } + } + + time_left = wait_for_completion_timeout(&dev->cmd_complete, + dev->adapter.timeout); + if (time_left == 0) { + dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR); + at91_init_twi_bus(dev); + ret = -ETIMEDOUT; + goto error; + } + if (dev->transfer_status & AT91_TWI_NACK) { + dev_dbg(dev->dev, "received nack\n"); + ret = -EREMOTEIO; + goto error; + } + if (dev->transfer_status & AT91_TWI_OVRE) { + dev_err(dev->dev, "overrun while reading\n"); + ret = -EIO; + goto error; + } + if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) { + dev_err(dev->dev, "underrun while writing\n"); + ret = -EIO; + goto error; + } + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { + dev_err(dev->dev, "tx locked\n"); + ret = -EIO; + goto error; + } + if (dev->recv_len_abort) { + dev_err(dev->dev, "invalid smbus block length recvd\n"); + ret = -EPROTO; + goto error; + } + + dev_dbg(dev->dev, "transfer complete\n"); + + return 0; + +error: + /* first stop DMA transfer if still in progress */ + at91_twi_dma_cleanup(dev); + /* then flush THR/FIFO and unlock TX if locked */ + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { + dev_dbg(dev->dev, "unlock tx\n"); + 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; +} + +static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) +{ + struct at91_twi_dev *dev = i2c_get_adapdata(adap); + int ret; + 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); + + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + goto out; + + if (num == 2) { + int internal_address = 0; + int i; + + /* 1st msg is put into the internal address, start with 2nd */ + m_start = &msg[1]; + for (i = 0; i < msg->len; ++i) { + const unsigned addr = msg->buf[msg->len - 1 - i]; + + internal_address |= addr << (8 * i); + int_addr_flag += AT91_TWI_IADRSZ_1; + } + at91_twi_write(dev, AT91_TWI_IADR, internal_address); + } + + dev->use_alt_cmd = false; + is_read = (m_start->flags & I2C_M_RD); + if (dev->pdata->has_alt_cmd) { + if (m_start->len > 0 && + m_start->len < AT91_I2C_MAX_ALT_CMD_DATA_SIZE) { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMEN); + at91_twi_write(dev, AT91_TWI_ACR, + AT91_TWI_ACR_DATAL(m_start->len) | + ((is_read) ? AT91_TWI_ACR_DIR : 0)); + dev->use_alt_cmd = true; + } else { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMDIS); + } + } + + at91_twi_write(dev, AT91_TWI_MMR, + (m_start->addr << 16) | + int_addr_flag | + ((!dev->use_alt_cmd && is_read) ? AT91_TWI_MREAD : 0)); + + dev->buf_len = m_start->len; + dev->buf = m_start->buf; + 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_put_autosuspend(dev->dev); + + return ret; +} + +/* + * The hardware can handle at most two messages concatenated by a + * repeated start via it's internal address feature. + */ +static const struct i2c_adapter_quirks at91_twi_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, + .max_comb_1st_msg_len = 3, +}; + +static u32 at91_twi_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL + | I2C_FUNC_SMBUS_READ_BLOCK_DATA; +} + +static const struct i2c_algorithm at91_twi_algorithm = { + .xfer = at91_twi_xfer, + .functionality = at91_twi_func, +}; + +static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) +{ + int ret = 0; + struct dma_slave_config slave_config; + struct at91_twi_dma *dma = &dev->dma; + enum dma_slave_buswidth addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + /* + * The actual width of the access will be chosen in + * dmaengine_prep_slave_sg(): + * for each buffer in the scatter-gather list, if its size is aligned + * to addr_width then addr_width accesses will be performed to transfer + * the buffer. On the other hand, if the buffer size is not aligned to + * addr_width then the buffer is transferred using single byte accesses. + * Please refer to the Atmel eXtended DMA controller driver. + * When FIFOs are used, the TXRDYM threshold can always be set to + * trigger the XDMAC when at least 4 data can be written into the TX + * FIFO, even if single byte accesses are performed. + * However the RXRDYM threshold must be set to fit the access width, + * deduced from buffer length, so the XDMAC is triggered properly to + * read data from the RX FIFO. + */ + if (dev->fifo_size) + addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + memset(&slave_config, 0, sizeof(slave_config)); + slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR; + slave_config.src_addr_width = addr_width; + slave_config.src_maxburst = 1; + slave_config.dst_addr = (dma_addr_t)phy_addr + AT91_TWI_THR; + slave_config.dst_addr_width = addr_width; + slave_config.dst_maxburst = 1; + slave_config.device_fc = false; + + 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_chan(dev->dev, "rx"); + if (IS_ERR(dma->chan_rx)) { + ret = PTR_ERR(dma->chan_rx); + dma->chan_rx = NULL; + goto error; + } + + slave_config.direction = DMA_MEM_TO_DEV; + if (dmaengine_slave_config(dma->chan_tx, &slave_config)) { + dev_err(dev->dev, "failed to configure tx channel\n"); + ret = -EINVAL; + goto error; + } + + slave_config.direction = DMA_DEV_TO_MEM; + if (dmaengine_slave_config(dma->chan_rx, &slave_config)) { + dev_err(dev->dev, "failed to configure rx channel\n"); + ret = -EINVAL; + goto error; + } + + sg_init_table(dma->sg, 2); + dma->buf_mapped = false; + dma->xfer_in_progress = false; + dev->use_dma = true; + + dev_info(dev->dev, "using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); + + return ret; + +error: + if (ret != -EPROBE_DEFER) + dev_info(dev->dev, "can't get DMA channel, continue without DMA support\n"); + if (dma->chan_rx) + dma_release_channel(dma->chan_rx); + if (dma->chan_tx) + dma_release_channel(dma->chan_tx); + return ret; +} + +static int at91_init_twi_recovery_gpio(struct platform_device *pdev, + struct at91_twi_dev *dev) +{ + struct i2c_bus_recovery_info *rinfo = &dev->rinfo; + + rinfo->pinctrl = devm_pinctrl_get(&pdev->dev); + if (!rinfo->pinctrl) { + dev_info(dev->dev, "pinctrl unavailable, bus recovery not supported\n"); + return 0; + } + 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; + + return 0; +} + +static int at91_twi_recover_bus_cmd(struct i2c_adapter *adap) +{ + struct at91_twi_dev *dev = i2c_get_adapdata(adap); + + 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; + + 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; +} + +static int at91_init_twi_recovery_info(struct platform_device *pdev, + struct at91_twi_dev *dev) +{ + struct i2c_bus_recovery_info *rinfo = &dev->rinfo; + bool has_clear_cmd = dev->pdata->has_clear_cmd; + + if (!has_clear_cmd) + return at91_init_twi_recovery_gpio(pdev, dev); + + rinfo->recover_bus = at91_twi_recover_bus_cmd; + dev->adapter.bus_recovery_info = rinfo; + + return 0; +} + +int at91_twi_probe_master(struct platform_device *pdev, + u32 phy_addr, struct at91_twi_dev *dev) +{ + int rc; + + init_completion(&dev->cmd_complete); + + 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; + } + + if (dev->dev->of_node) { + rc = at91_twi_configure_dma(dev, phy_addr); + if (rc == -EPROBE_DEFER) + 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); + } + + dev->enable_dig_filt = of_property_read_bool(pdev->dev.of_node, + "i2c-digital-filter"); + + dev->enable_ana_filt = of_property_read_bool(pdev->dev.of_node, + "i2c-analog-filter"); + at91_calc_twi_clock(dev); + + rc = at91_init_twi_recovery_info(pdev, dev); + if (rc == -EPROBE_DEFER) + return rc; + + dev->adapter.algo = &at91_twi_algorithm; + dev->adapter.quirks = &at91_twi_quirks; + + return 0; +} diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c new file mode 100644 index 000000000..131a67d9d --- /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 + */ + +#include +#include +#include +#include + +#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 000000000..942e9c397 --- /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 + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include + +#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 new file mode 100644 index 000000000..b78b38dda --- /dev/null +++ b/drivers/i2c/busses/i2c-au1550.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface + * Copyright (C) 2004 Embedded Edge, LLC + * + * 2.6 port by Matt Porter + * + * The documentation describes this as an SMBus controller, but it doesn't + * understand any of the SMBus protocol in hardware. It's really an I2C + * controller that could emulate most of the SMBus in software. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PSC_SEL 0x00 +#define PSC_CTRL 0x04 +#define PSC_SMBCFG 0x08 +#define PSC_SMBMSK 0x0C +#define PSC_SMBPCR 0x10 +#define PSC_SMBSTAT 0x14 +#define PSC_SMBEVNT 0x18 +#define PSC_SMBTXRX 0x1C +#define PSC_SMBTMR 0x20 + +struct i2c_au1550_data { + void __iomem *psc_base; + int xfer_timeout; + struct i2c_adapter adap; +}; + +static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v) +{ + __raw_writel(v, a->psc_base + r); + wmb(); +} + +static inline unsigned long RD(struct i2c_au1550_data *a, int r) +{ + return __raw_readl(a->psc_base + r); +} + +static int wait_xfer_done(struct i2c_au1550_data *adap) +{ + int i; + + /* Wait for Tx Buffer Empty */ + for (i = 0; i < adap->xfer_timeout; i++) { + if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE) + return 0; + + udelay(1); + } + + return -ETIMEDOUT; +} + +static int wait_ack(struct i2c_au1550_data *adap) +{ + unsigned long stat; + + if (wait_xfer_done(adap)) + return -ETIMEDOUT; + + stat = RD(adap, PSC_SMBEVNT); + if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0) + return -ETIMEDOUT; + + return 0; +} + +static int wait_controller_done(struct i2c_au1550_data *adap) +{ + int i; + + for (i = 0; i < 2 * adap->xfer_timeout; i++) { + if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) +{ + unsigned long stat; + + /* Reset the FIFOs, clear events. */ + stat = RD(adap, PSC_SMBSTAT); + WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR); + + if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) { + WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC); + while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0) + cpu_relax(); + udelay(50); + } + + /* Write out the i2c chip address and specify operation */ + addr <<= 1; + if (rd) + addr |= 1; + + /* zero-byte xfers stop immediately */ + if (q) + addr |= PSC_SMBTXRX_STP; + + /* 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_controller_done(adap) : 0; +} + +static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out) +{ + int j; + + if (wait_xfer_done(adap)) + return -EIO; + + j = adap->xfer_timeout * 100; + do { + j--; + if (j <= 0) + return -EIO; + + if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0) + j = 0; + else + udelay(1); + } while (j > 0); + + *out = RD(adap, PSC_SMBTXRX); + + return 0; +} + +static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + if (len == 0) + return 0; + + /* A read is performed by stuffing the transmit fifo with + * zero bytes for timing, waiting for bytes to appear in the + * receive fifo, then reading the bytes. + */ + i = 0; + while (i < (len - 1)) { + WR(adap, PSC_SMBTXRX, 0); + if (wait_for_rx_byte(adap, &buf[i])) + return -EIO; + + i++; + } + + /* The last byte has to indicate transfer done. */ + WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP); + if (wait_controller_done(adap)) + return -EIO; + + buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff); + return 0; +} + +static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + unsigned long data; + + if (len == 0) + return 0; + + i = 0; + while (i < (len-1)) { + data = buf[i]; + WR(adap, PSC_SMBTXRX, data); + if (wait_ack(adap)) + return -EIO; + i++; + } + + /* The last byte has to indicate transfer done. */ + data = buf[i]; + data |= PSC_SMBTXRX_STP; + WR(adap, PSC_SMBTXRX, data); + if (wait_controller_done(adap)) + return -EIO; + return 0; +} + +static int +au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct i2c_au1550_data *adap = i2c_adap->algo_data; + struct i2c_msg *p; + int i, err = 0; + + WR(adap, PSC_CTRL, PSC_CTRL_ENABLE); + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = do_address(adap, p->addr, p->flags & I2C_M_RD, + (p->len == 0)); + if (err || !p->len) + continue; + if (p->flags & I2C_M_RD) + err = i2c_read(adap, p->buf, p->len); + else + err = i2c_write(adap, p->buf, p->len); + } + + /* Return the number of messages processed, or the error code. + */ + if (err == 0) + err = num; + + WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND); + + return err; +} + +static u32 au1550_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm au1550_algo = { + .xfer = au1550_xfer, + .functionality = au1550_func, +}; + +static void i2c_au1550_setup(struct i2c_au1550_data *priv) +{ + unsigned long cfg; + + WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); + WR(priv, PSC_SEL, PSC_SEL_PS_SMBUSMODE); + WR(priv, PSC_SMBCFG, 0); + WR(priv, PSC_CTRL, PSC_CTRL_ENABLE); + while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) + cpu_relax(); + + cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE; + WR(priv, PSC_SMBCFG, cfg); + + /* Divide by 8 to get a 6.25 MHz clock. The later protocol + * timings are based on this clock. + */ + cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8); + WR(priv, PSC_SMBCFG, cfg); + WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK); + + /* Set the protocol timer values. See Table 71 in the + * Au1550 Data Book for standard timing values. + */ + WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(20) | \ + PSC_SMBTMR_SET_PU(20) | PSC_SMBTMR_SET_SH(20) | \ + PSC_SMBTMR_SET_SU(20) | PSC_SMBTMR_SET_CL(20) | \ + PSC_SMBTMR_SET_CH(20)); + + cfg |= PSC_SMBCFG_DE_ENABLE; + WR(priv, PSC_SMBCFG, cfg); + while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) + cpu_relax(); + + WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND); +} + +static void i2c_au1550_disable(struct i2c_au1550_data *priv) +{ + WR(priv, PSC_SMBCFG, 0); + WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); +} + +/* + * registering functions to load algorithms at runtime + * Prior to calling us, the 50MHz clock frequency and routing + * must have been set up for the PSC indicated by the adapter. + */ +static int +i2c_au1550_probe(struct platform_device *pdev) +{ + struct i2c_au1550_data *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->psc_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(priv->psc_base)) + return PTR_ERR(priv->psc_base); + + priv->xfer_timeout = 200; + + priv->adap.nr = pdev->id; + priv->adap.algo = &au1550_algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; + strscpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); + + /* Now, set up the PSC for SMBus PIO mode. */ + i2c_au1550_setup(priv); + + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret) { + i2c_au1550_disable(priv); + return ret; + } + + platform_set_drvdata(pdev, priv); + return 0; +} + +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); +} + +static int i2c_au1550_suspend(struct device *dev) +{ + struct i2c_au1550_data *priv = dev_get_drvdata(dev); + + i2c_au1550_disable(priv); + + return 0; +} + +static int i2c_au1550_resume(struct device *dev) +{ + struct i2c_au1550_data *priv = dev_get_drvdata(dev); + + i2c_au1550_setup(priv); + + return 0; +} + +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 = pm_sleep_ptr(&i2c_au1550_pmops), + }, + .probe = i2c_au1550_probe, + .remove = i2c_au1550_remove, +}; + +module_platform_driver(au1xpsc_smbus_driver); + +MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); +MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:au1xpsc_smbus"); diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c new file mode 100644 index 000000000..0555eeb69 --- /dev/null +++ b/drivers/i2c/busses/i2c-axxia.c @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This driver implements I2C master functionality using the LSI API2C + * controller. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCL_WAIT_TIMEOUT_NS 25000000 +#define I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) +#define I2C_STOP_TIMEOUT (msecs_to_jiffies(100)) +#define FIFO_SIZE 8 +#define SEQ_LEN 2 + +#define GLOBAL_CONTROL 0x00 +#define GLOBAL_MST_EN BIT(0) +#define GLOBAL_SLV_EN BIT(1) +#define GLOBAL_IBML_EN BIT(2) +#define INTERRUPT_STATUS 0x04 +#define INTERRUPT_ENABLE 0x08 +#define INT_SLV BIT(1) +#define INT_MST BIT(0) +#define WAIT_TIMER_CONTROL 0x0c +#define WT_EN BIT(15) +#define WT_VALUE(_x) ((_x) & 0x7fff) +#define IBML_TIMEOUT 0x10 +#define IBML_LOW_MEXT 0x14 +#define IBML_LOW_SEXT 0x18 +#define TIMER_CLOCK_DIV 0x1c +#define I2C_BUS_MONITOR 0x20 +#define BM_SDAC BIT(3) +#define BM_SCLC BIT(2) +#define BM_SDAS BIT(1) +#define BM_SCLS BIT(0) +#define SOFT_RESET 0x24 +#define MST_COMMAND 0x28 +#define CMD_BUSY (1<<3) +#define CMD_MANUAL (0x00 | CMD_BUSY) +#define CMD_AUTO (0x01 | CMD_BUSY) +#define CMD_SEQUENCE (0x02 | CMD_BUSY) +#define MST_RX_XFER 0x2c +#define MST_TX_XFER 0x30 +#define MST_ADDR_1 0x34 +#define MST_ADDR_2 0x38 +#define MST_DATA 0x3c +#define MST_TX_FIFO 0x40 +#define MST_RX_FIFO 0x44 +#define MST_INT_ENABLE 0x48 +#define MST_INT_STATUS 0x4c +#define MST_STATUS_RFL (1 << 13) /* RX FIFO serivce */ +#define MST_STATUS_TFL (1 << 12) /* TX FIFO service */ +#define MST_STATUS_SNS (1 << 11) /* Manual mode done */ +#define MST_STATUS_SS (1 << 10) /* Automatic mode done */ +#define MST_STATUS_SCC (1 << 9) /* Stop complete */ +#define MST_STATUS_IP (1 << 8) /* Invalid parameter */ +#define MST_STATUS_TSS (1 << 7) /* Timeout */ +#define MST_STATUS_AL (1 << 6) /* Arbitration lost */ +#define MST_STATUS_ND (1 << 5) /* NAK on data phase */ +#define MST_STATUS_NA (1 << 4) /* NAK on address phase */ +#define MST_STATUS_NAK (MST_STATUS_NA | \ + MST_STATUS_ND) +#define MST_STATUS_ERR (MST_STATUS_NAK | \ + MST_STATUS_AL | \ + 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 +#define SDA_SETUP_TIME 0x8c +#define SDA_HOLD_TIME 0x90 + +/** + * 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) + * @msg_xfrd: number of bytes transferred in tx_fifo + * @msg_xfrd_r: number of bytes transferred in rx_fifo + * @msg_err: error code for completed message + * @msg_complete: xfer completion object + * @dev: device reference + * @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; + struct i2c_msg *msg; + struct i2c_msg *msg_r; + size_t msg_xfrd; + size_t msg_xfrd_r; + int msg_err; + struct completion msg_complete; + struct device *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) +{ + u32 int_en; + + int_en = readl(idev->base + MST_INT_ENABLE); + writel(int_en & ~mask, idev->base + MST_INT_ENABLE); +} + +static void i2c_int_enable(struct axxia_i2c_dev *idev, u32 mask) +{ + u32 int_en; + + int_en = readl(idev->base + MST_INT_ENABLE); + 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) +{ + return div_u64(ns * clk_mhz, 1000); +} + +static int axxia_i2c_init(struct axxia_i2c_dev *idev) +{ + u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; + u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; + u32 t_setup; + u32 t_high, t_low; + u32 tmo_clk; + u32 prescale; + unsigned long timeout; + + dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", + idev->bus_clk_rate, clk_mhz, divisor); + + /* Reset controller */ + writel(0x01, idev->base + SOFT_RESET); + timeout = jiffies + msecs_to_jiffies(100); + while (readl(idev->base + SOFT_RESET) & 1) { + if (time_after(jiffies, timeout)) { + dev_warn(idev->dev, "Soft reset failed\n"); + break; + } + } + + /* Enable Master Mode */ + writel(0x1, idev->base + GLOBAL_CONTROL); + + 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; + t_setup = ns_to_clk(250, clk_mhz); + } else { + /* Fast mode SCL 33/66, tSU:DAT = 100 ns */ + t_high = divisor * 1 / 3; + t_low = divisor * 2 / 3; + t_setup = ns_to_clk(100, clk_mhz); + } + + /* SCL High Time */ + writel(t_high, idev->base + SCL_HIGH_PERIOD); + /* SCL Low Time */ + writel(t_low, idev->base + SCL_LOW_PERIOD); + /* SDA Setup Time */ + writel(t_setup, idev->base + SDA_SETUP_TIME); + /* SDA Hold Time, 300ns */ + writel(ns_to_clk(300, clk_mhz), idev->base + SDA_HOLD_TIME); + /* Filter <50ns spikes */ + writel(ns_to_clk(50, clk_mhz), idev->base + SPIKE_FLTR_LEN); + + /* Configure Time-Out Registers */ + tmo_clk = ns_to_clk(SCL_WAIT_TIMEOUT_NS, clk_mhz); + + /* Find prescaler value that makes tmo_clk fit in 15-bits counter. */ + for (prescale = 0; prescale < 15; ++prescale) { + if (tmo_clk <= 0x7fff) + break; + tmo_clk >>= 1; + } + if (tmo_clk > 0x7fff) + tmo_clk = 0x7fff; + + /* Prescale divider (log2) */ + writel(prescale, idev->base + TIMER_CLOCK_DIV); + /* Timeout in divided clocks */ + writel(WT_EN | WT_VALUE(tmo_clk), idev->base + WAIT_TIMER_CONTROL); + + /* Mask all master interrupt bits */ + i2c_int_disable(idev, ~0); + + /* Interrupt enable */ + writel(0x01, idev->base + INTERRUPT_ENABLE); + + return 0; +} + +static int i2c_m_rd(const struct i2c_msg *msg) +{ + return (msg->flags & I2C_M_RD) != 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. + */ +static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) +{ + struct i2c_msg *msg = idev->msg_r; + size_t rx_fifo_avail = readl(idev->base + MST_RX_FIFO); + int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd_r); + + while (bytes_to_transfer-- > 0) { + int c = readl(idev->base + MST_DATA); + + if (idev->msg_xfrd_r == 0 && i2c_m_recv_len(msg)) { + /* + * Check length byte for SMBus block read + */ + if (c <= 0 || c > I2C_SMBUS_BLOCK_MAX) { + idev->msg_err = -EPROTO; + i2c_int_disable(idev, ~MST_STATUS_TSS); + complete(&idev->msg_complete); + break; + } + msg->len = 1 + c; + writel(msg->len, idev->base + MST_RX_XFER); + } + msg->buf[idev->msg_xfrd_r++] = c; + } + + return 0; +} + +/* + * axxia_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer. + * @return: Number of bytes left to transfer. + */ +static int axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev) +{ + struct i2c_msg *msg = idev->msg; + size_t tx_fifo_avail = FIFO_SIZE - readl(idev->base + MST_TX_FIFO); + int bytes_to_transfer = min(tx_fifo_avail, msg->len - idev->msg_xfrd); + int ret = msg->len - idev->msg_xfrd - bytes_to_transfer; + + while (bytes_to_transfer-- > 0) + writel(msg->buf[idev->msg_xfrd++], idev->base + MST_DATA); + + 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; + + 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); + + if (!idev->msg) { + dev_warn(idev->dev, "unexpected interrupt\n"); + goto out; + } + + /* RX FIFO needs service? */ + if (i2c_m_rd(idev->msg_r) && (status & MST_STATUS_RFL)) + axxia_i2c_empty_rx_fifo(idev); + + /* TX FIFO needs service? */ + if (!i2c_m_rd(idev->msg) && (status & MST_STATUS_TFL)) { + if (axxia_i2c_fill_tx_fifo(idev) == 0) + i2c_int_disable(idev, MST_STATUS_TFL); + } + + if (unlikely(status & MST_STATUS_ERR)) { + /* Transfer error */ + i2c_int_disable(idev, ~0); + if (status & MST_STATUS_AL) + idev->msg_err = -EAGAIN; + else if (status & MST_STATUS_NAK) + idev->msg_err = -ENXIO; + else + idev->msg_err = -EIO; + dev_dbg(idev->dev, "error %#x, addr=%#x rx=%u/%u tx=%u/%u\n", + status, + idev->msg->addr, + readl(idev->base + MST_RX_BYTES_XFRD), + readl(idev->base + MST_RX_XFER), + readl(idev->base + MST_TX_BYTES_XFRD), + readl(idev->base + MST_TX_XFER)); + complete(&idev->msg_complete); + } else if (status & MST_STATUS_SCC) { + /* Stop completed */ + i2c_int_disable(idev, ~MST_STATUS_TSS); + complete(&idev->msg_complete); + } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) { + /* Transfer done */ + 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_TSS) { + /* Transfer timeout */ + idev->msg_err = -ETIMEDOUT; + i2c_int_disable(idev, ~MST_STATUS_TSS); + complete(&idev->msg_complete); + } + +out: + /* Clear interrupt */ + writel(INT_MST, idev->base + INTERRUPT_STATUS); + + return IRQ_HANDLED; +} + +static void axxia_i2c_set_addr(struct axxia_i2c_dev *idev, struct i2c_msg *msg) +{ + u32 addr_1, addr_2; + + 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 { + addr_1 = i2c_8bit_addr_from_msg(msg); + addr_2 = 0; + } + + writel(addr_1, idev->base + MST_ADDR_1); + writel(addr_2, idev->base + MST_ADDR_2); +} + +/* The NAK interrupt will be sent _before_ issuing STOP command + * so the controller might still be busy processing it. No + * interrupt will be sent at the end so we have to poll for it + */ +static int axxia_i2c_handle_seq_nak(struct axxia_i2c_dev *idev) +{ + unsigned long timeout = jiffies + I2C_XFER_TIMEOUT; + + do { + if ((readl(idev->base + MST_COMMAND) & CMD_BUSY) == 0) + return 0; + usleep_range(1, 100); + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[]) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SS | MST_STATUS_RFL; + u32 rlen = i2c_m_recv_len(&msgs[1]) ? I2C_SMBUS_BLOCK_MAX : msgs[1].len; + unsigned long time_left; + + axxia_i2c_set_addr(idev, &msgs[0]); + + writel(msgs[0].len, idev->base + MST_TX_XFER); + writel(rlen, idev->base + MST_RX_XFER); + + idev->msg = &msgs[0]; + 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); + + reinit_completion(&idev->msg_complete); + i2c_int_enable(idev, int_mask); + + time_left = wait_for_completion_timeout(&idev->msg_complete, + I2C_XFER_TIMEOUT); + + if (idev->msg_err == -ENXIO) { + if (axxia_i2c_handle_seq_nak(idev)) + axxia_i2c_init(idev); + } else if (readl(idev->base + MST_COMMAND) & CMD_BUSY) { + dev_warn(idev->dev, "busy after xfer\n"); + } + + if (time_left == 0) { + idev->msg_err = -ETIMEDOUT; + i2c_recover_bus(&idev->adapter); + axxia_i2c_init(idev); + } + + if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO) + axxia_i2c_init(idev); + + return idev->msg_err; +} + +static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg, + bool last) +{ + u32 int_mask = MST_STATUS_ERR; + u32 rx_xfer, tx_xfer; + unsigned long time_left; + unsigned int wt_value; + + idev->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); + + if (i2c_m_rd(msg)) { + /* I2C read transfer */ + rx_xfer = i2c_m_recv_len(msg) ? I2C_SMBUS_BLOCK_MAX : msg->len; + tx_xfer = 0; + } else { + /* I2C write transfer */ + rx_xfer = 0; + tx_xfer = msg->len; + } + + writel(rx_xfer, idev->base + MST_RX_XFER); + writel(tx_xfer, idev->base + MST_TX_XFER); + + if (i2c_m_rd(msg)) + int_mask |= MST_STATUS_RFL; + else if (axxia_i2c_fill_tx_fifo(idev) != 0) + int_mask |= MST_STATUS_TFL; + + wt_value = WT_VALUE(readl(idev->base + WAIT_TIMER_CONTROL)); + /* Disable wait timer temporarly */ + writel(wt_value, idev->base + WAIT_TIMER_CONTROL); + /* Check if timeout error happened */ + if (idev->msg_err) + goto out; + + 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); + + i2c_int_enable(idev, int_mask); + + time_left = wait_for_completion_timeout(&idev->msg_complete, + I2C_XFER_TIMEOUT); + + i2c_int_disable(idev, int_mask); + + if (readl(idev->base + MST_COMMAND) & CMD_BUSY) + dev_warn(idev->dev, "busy after xfer\n"); + + if (time_left == 0) { + idev->msg_err = -ETIMEDOUT; + i2c_recover_bus(&idev->adapter); + axxia_i2c_init(idev); + } + +out: + if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO && + idev->msg_err != -ETIMEDOUT) + axxia_i2c_init(idev); + + return idev->msg_err; +} + +/* 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, + * both targeted at the same client device. + */ +static bool axxia_i2c_sequence_ok(struct i2c_msg msgs[], int num) +{ + return num == SEQ_LEN && !i2c_m_rd(&msgs[0]) && i2c_m_rd(&msgs[1]) && + msgs[0].len > 0 && msgs[0].len <= FIFO_SIZE && + msgs[1].len > 0 && msgs[0].addr == msgs[1].addr; +} + +static int +axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + idev->msg_err = 0; + + if (axxia_i2c_sequence_ok(msgs, num)) { + ret = axxia_i2c_xfer_seq(idev, msgs); + return ret ? : SEQ_LEN; + } + + i2c_int_enable(idev, MST_STATUS_TSS); + + for (i = 0; ret == 0 && i < num; ++i) + ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1)); + + return ret ? : i; +} + +static int axxia_i2c_get_scl(struct i2c_adapter *adap) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + + return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SCLS); +} + +static void axxia_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + u32 tmp; + + /* Preserve SDA Control */ + tmp = readl(idev->base + I2C_BUS_MONITOR) & BM_SDAC; + if (!val) + tmp |= BM_SCLC; + writel(tmp, idev->base + I2C_BUS_MONITOR); +} + +static int axxia_i2c_get_sda(struct i2c_adapter *adap) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + + return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SDAS); +} + +static struct i2c_bus_recovery_info axxia_i2c_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = axxia_i2c_get_scl, + .set_scl = axxia_i2c_set_scl, + .get_sda = axxia_i2c_get_sda, +}; + +static u32 axxia_i2c_func(struct i2c_adapter *adap) +{ + u32 caps = (I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA); + 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 = { + .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 = { + .max_read_len = 255, + .max_write_len = 255, +}; + +static int axxia_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct axxia_i2c_dev *idev = NULL; + void __iomem *base; + int ret = 0; + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + 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)) { + dev_err(&pdev->dev, "missing clock\n"); + return PTR_ERR(idev->i2c_clk); + } + + idev->base = base; + idev->dev = &pdev->dev; + init_completion(&idev->msg_complete); + + of_property_read_u32(np, "clock-frequency", &idev->bus_clk_rate); + if (idev->bus_clk_rate == 0) + idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */ + + ret = clk_prepare_enable(idev->i2c_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + return ret; + } + + ret = axxia_i2c_init(idev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize\n"); + goto error_disable_clk; + } + + 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", idev->irq); + goto error_disable_clk; + } + + i2c_set_adapdata(&idev->adapter, idev); + 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; + idev->adapter.quirks = &axxia_i2c_quirks; + idev->adapter.dev.parent = &pdev->dev; + idev->adapter.dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, idev); + + ret = i2c_add_adapter(&idev->adapter); + if (ret) + goto error_disable_clk; + + return 0; + +error_disable_clk: + clk_disable_unprepare(idev->i2c_clk); + return ret; +} + +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); +} + +/* Match table for of_platform binding */ +static const struct of_device_id axxia_i2c_of_match[] = { + { .compatible = "lsi,api2c", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, axxia_i2c_of_match); + +static struct platform_driver axxia_i2c_driver = { + .probe = axxia_i2c_probe, + .remove = axxia_i2c_remove, + .driver = { + .name = "axxia-i2c", + .of_match_table = axxia_i2c_of_match, + }, +}; + +module_platform_driver(axxia_i2c_driver); + +MODULE_DESCRIPTION("Axxia I2C Bus driver"); +MODULE_AUTHOR("Anders Berg "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 000000000..66bb3bfda --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,1262 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#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 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#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 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#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; + + struct completion done; + int xfer_is_done; + + 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)\ + | BIT(IS_M_RX_THLD_SHIFT)) + +#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) +{ + 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); + } + + return val; +} + +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); + } +} + +static void bcm_iproc_i2c_slave_init(struct bcm_iproc_i2c_dev *iproc_i2c, + bool need_reset) +{ + u32 val; + + 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); + + /* wait approximately 100 usec as per spec */ + usleep_range(100, 200); + + /* 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 + */ + tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, + tmp); + } + } + + /* load data into TX FIFO */ + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); + } + + /* 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); + } + } 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; + 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; + } + + 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 void bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + 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 approximately 100 usec as per spec */ + usleep_range(100, 200); + + /* bring controller out of reset */ + 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 = (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 */ + 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 */ + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff); +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_err(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_err(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + 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_err(iproc_i2c->device, "unknown error code=%d\n", val); + + /* re-initialize i2c for recovery */ + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_init(iproc_i2c); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, + u32 cmd) +{ + 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) { + /* + * The controller may fail to clear START_BUSY after a timeout, + * reset the controller to recover in that case. + */ + if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) & + BIT(M_CMD_START_BUSY_SHIFT))) { + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_init(iproc_i2c); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + } + + /* 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, tmp, val_intr_en; + unsigned int tx_bytes; + struct i2c_msg *msg = &msgs[0]; + + /* check if bus is busy */ + 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; + } + + iproc_i2c->msg = msg; + + /* format and load slave address into the TX FIFO */ + addr = i2c_8bit_addr_from_msg(msg); + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr); + + /* + * For a write transaction, load data into the TX FIFO. Only allow + * loading up to TX FIFO size - 1 bytes of data since the first byte + * has been used up by the slave address + */ + tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1); + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < tx_bytes; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (!process_call && (i == msg->len - 1)) + val |= BIT(M_TX_WR_STATUS_SHIFT); + + 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 */ + if (iproc_i2c->irq) + reinit_completion(&iproc_i2c->done); + + iproc_i2c->xfer_is_done = 0; + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + 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 (!process_call && !(msg->flags & I2C_M_RD) && + msg->len > iproc_i2c->tx_bytes) + 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->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; + + 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; + + /* 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); + + /* enable the RX threshold interrupt */ + val_intr_en |= BIT(IE_M_RX_THLD_SHIFT); + + protocol = process_call ? + M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD; + + val |= (protocol << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + + if (iproc_i2c->irq) + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en); + + 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); + 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 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) +{ + 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 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 = { + .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) +{ + unsigned int bus_speed; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = I2C_MAX_STANDARD_MODE_FREQ; + } + + 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 = 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); + + return 0; +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + int irq, ret; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + iproc_i2c->type = (kernel_ulong_t)of_device_get_match_data(&pdev->dev); + init_completion(&iproc_i2c->done); + + iproc_i2c->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + 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) { + ret = devm_request_irq(iproc_i2c->device, irq, + bcm_iproc_i2c_isr, 0, pdev->name, + iproc_i2c); + if (ret < 0) + return ret; + + 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); + 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; + adap->dev.of_node = pdev->dev.of_node; + + return i2c_add_adapter(adap); +} + +static void bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + 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); +} + +static int bcm_iproc_i2c_suspend(struct device *dev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); + + 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); + + return 0; +} + +static int bcm_iproc_i2c_resume(struct device *dev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); + u32 val; + + /* + * Power domain could have been shut off completely in system deep + * sleep, so re-initialize the block here + */ + bcm_iproc_i2c_init(iproc_i2c); + + /* configure to the desired bus speed */ + 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); + + return 0; +} + +static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { + .suspend_late = &bcm_iproc_i2c_suspend, + .resume_early = &bcm_iproc_i2c_resume +}; + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { + .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); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + .pm = pm_sleep_ptr(&bcm_iproc_i2c_pm_ops), + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui "); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c new file mode 100644 index 000000000..d4f287b31 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hardware register offsets and field defintions */ +#define CS_OFFSET 0x00000020 +#define CS_ACK_SHIFT 3 +#define CS_ACK_MASK 0x00000008 +#define CS_ACK_CMD_GEN_START 0x00000000 +#define CS_ACK_CMD_GEN_RESTART 0x00000001 +#define CS_CMD_SHIFT 1 +#define CS_CMD_CMD_NO_ACTION 0x00000000 +#define CS_CMD_CMD_START_RESTART 0x00000001 +#define CS_CMD_CMD_STOP 0x00000002 +#define CS_EN_SHIFT 0 +#define CS_EN_CMD_ENABLE_BSC 0x00000001 + +#define TIM_OFFSET 0x00000024 +#define TIM_PRESCALE_SHIFT 6 +#define TIM_P_SHIFT 3 +#define TIM_NO_DIV_SHIFT 2 +#define TIM_DIV_SHIFT 0 + +#define DAT_OFFSET 0x00000028 + +#define TOUT_OFFSET 0x0000002c + +#define TXFCR_OFFSET 0x0000003c +#define TXFCR_FIFO_FLUSH_MASK 0x00000080 +#define TXFCR_FIFO_EN_MASK 0x00000040 + +#define IER_OFFSET 0x00000044 +#define IER_READ_COMPLETE_INT_MASK 0x00000010 +#define IER_I2C_INT_EN_MASK 0x00000008 +#define IER_FIFO_INT_EN_MASK 0x00000002 +#define IER_NOACK_EN_MASK 0x00000001 + +#define ISR_OFFSET 0x00000048 +#define ISR_RESERVED_MASK 0xffffff60 +#define ISR_CMDBUSY_MASK 0x00000080 +#define ISR_READ_COMPLETE_MASK 0x00000010 +#define ISR_SES_DONE_MASK 0x00000008 +#define ISR_ERR_MASK 0x00000004 +#define ISR_TXFIFOEMPTY_MASK 0x00000002 +#define ISR_NOACK_MASK 0x00000001 + +#define CLKEN_OFFSET 0x0000004C +#define CLKEN_AUTOSENSE_OFF_MASK 0x00000080 +#define CLKEN_M_SHIFT 4 +#define CLKEN_N_SHIFT 1 +#define CLKEN_CLKEN_MASK 0x00000001 + +#define FIFO_STATUS_OFFSET 0x00000054 +#define FIFO_STATUS_RXFIFO_EMPTY_MASK 0x00000004 +#define FIFO_STATUS_TXFIFO_EMPTY_MASK 0x00000010 + +#define HSTIM_OFFSET 0x00000058 +#define HSTIM_HS_MODE_MASK 0x00008000 +#define HSTIM_HS_HOLD_SHIFT 10 +#define HSTIM_HS_HIGH_PHASE_SHIFT 5 +#define HSTIM_HS_SETUP_SHIFT 0 + +#define PADCTL_OFFSET 0x0000005c +#define PADCTL_PAD_OUT_EN_MASK 0x00000004 + +#define RXFCR_OFFSET 0x00000068 +#define RXFCR_NACK_EN_SHIFT 7 +#define RXFCR_READ_COUNT_SHIFT 0 +#define RXFIFORDOUT_OFFSET 0x0000006c + +/* Locally used constants */ +#define MAX_RX_FIFO_SIZE 64U /* bytes */ +#define MAX_TX_FIFO_SIZE 64U /* bytes */ + +#define STD_EXT_CLK_FREQ 13000000UL +#define HS_EXT_CLK_FREQ 104000000UL + +#define CONTROLLER_CODE 0x08 /* Controller codes are 0000_1xxxb */ + +#define I2C_TIMEOUT 100 /* msecs */ + +/* Operations that can be commanded to the controller */ +enum bcm_kona_cmd_t { + BCM_CMD_NOACTION = 0, + BCM_CMD_START, + BCM_CMD_RESTART, + BCM_CMD_STOP, +}; + +enum bus_speed_index { + BCM_SPD_100K = 0, + BCM_SPD_400K, + BCM_SPD_1MHZ, +}; + +enum hs_bus_speed_index { + BCM_SPD_3P4MHZ = 0, +}; + +/* Internal divider settings for standard mode, fast mode and fast mode plus */ +struct bus_speed_cfg { + uint8_t time_m; /* Number of cycles for setup time */ + uint8_t time_n; /* Number of cycles for hold time */ + uint8_t prescale; /* Prescale divider */ + uint8_t time_p; /* Timing coefficient */ + uint8_t no_div; /* Disable clock divider */ + uint8_t time_div; /* Post-prescale divider */ +}; + +/* Internal divider settings for high-speed mode */ +struct hs_bus_speed_cfg { + uint8_t hs_hold; /* Number of clock cycles SCL stays low until + the end of bit period */ + uint8_t hs_high_phase; /* Number of clock cycles SCL stays high + before it falls */ + uint8_t hs_setup; /* Number of clock cycles SCL stays low + before it rises */ + uint8_t prescale; /* Prescale divider */ + uint8_t time_p; /* Timing coefficient */ + uint8_t no_div; /* Disable clock divider */ + uint8_t time_div; /* Post-prescale divider */ +}; + +static const struct bus_speed_cfg std_cfg_table[] = { + [BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02}, + [BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02}, + [BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03}, +}; + +static const struct hs_bus_speed_cfg hs_cfg_table[] = { + [BCM_SPD_3P4MHZ] = {0x01, 0x08, 0x14, 0x00, 0x06, 0x01, 0x00}, +}; + +struct bcm_kona_i2c_dev { + struct device *device; + + void __iomem *base; + int irq; + struct clk *external_clk; + + struct i2c_adapter adapter; + + struct completion done; + + const struct bus_speed_cfg *std_cfg; + const struct hs_bus_speed_cfg *hs_cfg; +}; + +static void bcm_kona_i2c_send_cmd_to_ctrl(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + dev_dbg(dev->device, "%s, %d\n", __func__, cmd); + + switch (cmd) { + case BCM_CMD_NOACTION: + writel((CS_CMD_CMD_NO_ACTION << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_START: + writel((CS_ACK_CMD_GEN_START << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_RESTART: + writel((CS_ACK_CMD_GEN_RESTART << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_STOP: + writel((CS_CMD_CMD_STOP << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + default: + dev_err(dev->device, "Unknown command %d\n", cmd); + } +} + +static void bcm_kona_i2c_enable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) | CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_disable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +static irqreturn_t bcm_kona_i2c_isr(int irq, void *devid) +{ + struct bcm_kona_i2c_dev *dev = devid; + uint32_t status = readl(dev->base + ISR_OFFSET); + + if ((status & ~ISR_RESERVED_MASK) == 0) + return IRQ_NONE; + + /* Must flush the TX FIFO when NAK detected */ + if (status & ISR_NOACK_MASK) + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + writel(status & ~ISR_RESERVED_MASK, dev->base + ISR_OFFSET); + complete(&dev->done); + + return IRQ_HANDLED; +} + +/* Wait for ISR_CMDBUSY_MASK to go low before writing to CS, DAT, or RCD */ +static int bcm_kona_i2c_wait_if_busy(struct bcm_kona_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT); + + while (readl(dev->base + ISR_OFFSET) & ISR_CMDBUSY_MASK) + if (time_after(jiffies, timeout)) { + dev_err(dev->device, "CMDBUSY timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* Send command to I2C bus */ +static int bcm_kona_send_i2c_cmd(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + int rc; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT); + + /* Make sure the hardware is ready */ + rc = bcm_kona_i2c_wait_if_busy(dev); + if (rc < 0) + return rc; + + /* Unmask the session done interrupt */ + writel(IER_I2C_INT_EN_MASK, dev->base + IER_OFFSET); + + /* Mark as incomplete before sending the command */ + reinit_completion(&dev->done); + + /* Send the command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, cmd); + + /* Wait for transaction to finish or timeout */ + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + if (!time_left) { + dev_err(dev->device, "controller timed out\n"); + rc = -ETIMEDOUT; + } + + /* Clear command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + return rc; +} + +/* Read a single RX FIFO worth of data from the i2c bus */ +static int bcm_kona_i2c_read_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len, + unsigned int last_byte_nak) +{ + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT); + + /* Mark as incomplete before starting the RX FIFO */ + reinit_completion(&dev->done); + + /* Unmask the read complete interrupt */ + writel(IER_READ_COMPLETE_INT_MASK, dev->base + IER_OFFSET); + + /* Start the RX FIFO */ + writel((last_byte_nak << RXFCR_NACK_EN_SHIFT) | + (len << RXFCR_READ_COUNT_SHIFT), + dev->base + RXFCR_OFFSET); + + /* Wait for FIFO read to complete */ + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + if (!time_left) { + dev_err(dev->device, "RX FIFO time out\n"); + return -EREMOTEIO; + } + + /* Read data from FIFO */ + for (; len > 0; len--, buf++) + *buf = readl(dev->base + RXFIFORDOUT_OFFSET); + + return 0; +} + +/* Read any amount of data using the RX FIFO from the i2c bus */ +static int bcm_kona_i2c_read_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_read = MAX_RX_FIFO_SIZE; + unsigned int last_byte_nak = 0; + unsigned int bytes_read = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_read < msg->len) { + if (msg->len - bytes_read <= MAX_RX_FIFO_SIZE) { + last_byte_nak = 1; /* NAK last byte of transfer */ + bytes_to_read = msg->len - bytes_read; + } + + rc = bcm_kona_i2c_read_fifo_single(dev, tmp_buf, bytes_to_read, + last_byte_nak); + if (rc < 0) + return -EREMOTEIO; + + bytes_read += bytes_to_read; + tmp_buf += bytes_to_read; + } + + return 0; +} + +/* Write a single byte of data to the i2c bus */ +static int bcm_kona_i2c_write_byte(struct bcm_kona_i2c_dev *dev, uint8_t data, + unsigned int nak_expected) +{ + int rc; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT); + unsigned int nak_received; + + /* Make sure the hardware is ready */ + rc = bcm_kona_i2c_wait_if_busy(dev); + if (rc < 0) + return rc; + + /* Clear pending session done interrupt */ + writel(ISR_SES_DONE_MASK, dev->base + ISR_OFFSET); + + /* Unmask the session done interrupt */ + writel(IER_I2C_INT_EN_MASK, dev->base + IER_OFFSET); + + /* Mark as incomplete before sending the data */ + reinit_completion(&dev->done); + + /* Send one byte of data */ + writel(data, dev->base + DAT_OFFSET); + + /* Wait for byte to be written */ + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + if (!time_left) { + dev_dbg(dev->device, "controller timed out\n"); + return -ETIMEDOUT; + } + + nak_received = readl(dev->base + CS_OFFSET) & CS_ACK_MASK ? 1 : 0; + + if (nak_received ^ nak_expected) { + dev_dbg(dev->device, "unexpected NAK/ACK\n"); + return -EREMOTEIO; + } + + return 0; +} + +/* Write a single TX FIFO worth of data to the i2c bus */ +static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len) +{ + int k; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT); + unsigned int fifo_status; + + /* Mark as incomplete before sending data to the TX FIFO */ + reinit_completion(&dev->done); + + /* Unmask the fifo empty and nak interrupt */ + writel(IER_FIFO_INT_EN_MASK | IER_NOACK_EN_MASK, + dev->base + IER_OFFSET); + + /* Disable IRQ to load a FIFO worth of data without interruption */ + disable_irq(dev->irq); + + /* Write data into FIFO */ + for (k = 0; k < len; k++) + writel(buf[k], (dev->base + DAT_OFFSET)); + + /* Enable IRQ now that data has been loaded */ + enable_irq(dev->irq); + + /* Wait for FIFO to empty */ + do { + time_left = wait_for_completion_timeout(&dev->done, time_left); + fifo_status = readl(dev->base + FIFO_STATUS_OFFSET); + } while (time_left && !(fifo_status & FIFO_STATUS_TXFIFO_EMPTY_MASK)); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + /* Check if there was a NAK */ + if (readl(dev->base + CS_OFFSET) & CS_ACK_MASK) { + dev_err(dev->device, "unexpected NAK\n"); + return -EREMOTEIO; + } + + /* Check if a timeout occurred */ + if (!time_left) { + dev_err(dev->device, "completion timed out\n"); + return -EREMOTEIO; + } + + return 0; +} + + +/* Write any amount of data using TX FIFO to the i2c bus */ +static int bcm_kona_i2c_write_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_write = MAX_TX_FIFO_SIZE; + unsigned int bytes_written = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_written < msg->len) { + if (msg->len - bytes_written <= MAX_TX_FIFO_SIZE) + bytes_to_write = msg->len - bytes_written; + + rc = bcm_kona_i2c_write_fifo_single(dev, tmp_buf, + bytes_to_write); + if (rc < 0) + return -EREMOTEIO; + + bytes_written += bytes_to_write; + tmp_buf += bytes_to_write; + } + + return 0; +} + +/* Send i2c address */ +static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned char addr; + + if (msg->flags & I2C_M_TEN) { + /* First byte is 11110XX0 where XX is upper 2 bits */ + 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 = i2c_10bit_addr_lo_from_msg(msg); + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + + if (msg->flags & I2C_M_RD) { + /* For read, send restart command */ + if (bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART) < 0) + return -EREMOTEIO; + + /* Then re-send the first byte with the read bit set */ + addr = i2c_10bit_addr_hi_from_msg(msg); + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + } else { + addr = i2c_8bit_addr_from_msg(msg); + + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + + return 0; +} + +static void bcm_kona_i2c_enable_autosense(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_AUTOSENSE_OFF_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_config_timing(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + HSTIM_OFFSET) & ~HSTIM_HS_MODE_MASK, + dev->base + HSTIM_OFFSET); + + writel((dev->std_cfg->prescale << TIM_PRESCALE_SHIFT) | + (dev->std_cfg->time_p << TIM_P_SHIFT) | + (dev->std_cfg->no_div << TIM_NO_DIV_SHIFT) | + (dev->std_cfg->time_div << TIM_DIV_SHIFT), + dev->base + TIM_OFFSET); + + writel((dev->std_cfg->time_m << CLKEN_M_SHIFT) | + (dev->std_cfg->time_n << CLKEN_N_SHIFT) | + CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_config_timing_hs(struct bcm_kona_i2c_dev *dev) +{ + writel((dev->hs_cfg->prescale << TIM_PRESCALE_SHIFT) | + (dev->hs_cfg->time_p << TIM_P_SHIFT) | + (dev->hs_cfg->no_div << TIM_NO_DIV_SHIFT) | + (dev->hs_cfg->time_div << TIM_DIV_SHIFT), + dev->base + TIM_OFFSET); + + writel((dev->hs_cfg->hs_hold << HSTIM_HS_HOLD_SHIFT) | + (dev->hs_cfg->hs_high_phase << HSTIM_HS_HIGH_PHASE_SHIFT) | + (dev->hs_cfg->hs_setup << HSTIM_HS_SETUP_SHIFT), + dev->base + HSTIM_OFFSET); + + writel(readl(dev->base + HSTIM_OFFSET) | HSTIM_HS_MODE_MASK, + dev->base + HSTIM_OFFSET); +} + +static int bcm_kona_i2c_switch_to_hs(struct bcm_kona_i2c_dev *dev) +{ + int rc; + + /* 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; + } + + /* Configure external clock to higher frequency */ + rc = clk_set_rate(dev->external_clk, HS_EXT_CLK_FREQ); + if (rc) { + dev_err(dev->device, "%s: clk_set_rate returned %d\n", + __func__, rc); + return rc; + } + + /* Reconfigure internal dividers */ + bcm_kona_i2c_config_timing_hs(dev); + + /* Send a restart command */ + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART); + if (rc < 0) + dev_err(dev->device, "High speed restart command failed\n"); + + return rc; +} + +static int bcm_kona_i2c_switch_to_std(struct bcm_kona_i2c_dev *dev) +{ + int rc; + + /* Reconfigure internal dividers */ + bcm_kona_i2c_config_timing(dev); + + /* Configure external clock to lower frequency */ + rc = clk_set_rate(dev->external_clk, STD_EXT_CLK_FREQ); + if (rc) { + dev_err(dev->device, "%s: clk_set_rate returned %d\n", + __func__, rc); + } + + return rc; +} + +static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_kona_i2c_dev *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int rc = 0; + int i; + + rc = clk_prepare_enable(dev->external_clk); + if (rc) { + dev_err(dev->device, "%s: peri clock enable failed. err %d\n", + __func__, rc); + return rc; + } + + /* Enable pad output */ + writel(0, dev->base + PADCTL_OFFSET); + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Send start command */ + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_START); + if (rc < 0) { + dev_err(dev->device, "Start command failed rc = %d\n", rc); + goto xfer_disable_pad; + } + + /* Switch to high speed if applicable */ + if (dev->hs_cfg) { + rc = bcm_kona_i2c_switch_to_hs(dev); + if (rc < 0) + goto xfer_send_stop; + } + + /* Loop through all messages */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + /* Send restart for subsequent messages */ + if ((i != 0) && ((pmsg->flags & I2C_M_NOSTART) == 0)) { + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART); + if (rc < 0) { + dev_err(dev->device, + "restart cmd failed rc = %d\n", rc); + goto xfer_send_stop; + } + } + + /* Send target address */ + if (!(pmsg->flags & I2C_M_NOSTART)) { + rc = bcm_kona_i2c_do_addr(dev, pmsg); + if (rc < 0) { + dev_err(dev->device, + "NAK from addr %2.2x msg#%d rc = %d\n", + pmsg->addr, i, rc); + goto xfer_send_stop; + } + } + + /* Perform data transfer */ + if (pmsg->flags & I2C_M_RD) { + rc = bcm_kona_i2c_read_fifo(dev, pmsg); + if (rc < 0) { + dev_err(dev->device, "read failure\n"); + goto xfer_send_stop; + } + } else { + rc = bcm_kona_i2c_write_fifo(dev, pmsg); + if (rc < 0) { + dev_err(dev->device, "write failure"); + goto xfer_send_stop; + } + } + } + + rc = num; + +xfer_send_stop: + /* Send a STOP command */ + bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP); + + /* Return from high speed if applicable */ + if (dev->hs_cfg) { + int hs_rc = bcm_kona_i2c_switch_to_std(dev); + + if (hs_rc) + rc = hs_rc; + } + +xfer_disable_pad: + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); + + /* Stop internal clock */ + bcm_kona_i2c_disable_clock(dev); + + clk_disable_unprepare(dev->external_clk); + + return rc; +} + +static uint32_t bcm_kona_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm bcm_algo = { + .xfer = bcm_kona_i2c_xfer, + .functionality = bcm_kona_i2c_functionality, +}; + +static int bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev) +{ + unsigned int bus_speed; + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", + &bus_speed); + if (ret < 0) { + dev_err(dev->device, "missing clock-frequency property\n"); + return -ENODEV; + } + + switch (bus_speed) { + case I2C_MAX_STANDARD_MODE_FREQ: + dev->std_cfg = &std_cfg_table[BCM_SPD_100K]; + break; + case I2C_MAX_FAST_MODE_FREQ: + dev->std_cfg = &std_cfg_table[BCM_SPD_400K]; + break; + case I2C_MAX_FAST_MODE_PLUS_FREQ: + dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ]; + break; + 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; + default: + pr_err("%d hz bus speed not supported\n", bus_speed); + pr_err("Valid speeds are 100khz, 400khz, 1mhz, and 3.4mhz\n"); + return -EINVAL; + } + + return 0; +} + +static int bcm_kona_i2c_probe(struct platform_device *pdev) +{ + int rc = 0; + struct bcm_kona_i2c_dev *dev; + struct i2c_adapter *adap; + + /* Allocate memory for private data structure */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + /* Map hardware registers */ + dev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + /* Get and enable external clock */ + dev->external_clk = devm_clk_get(dev->device, NULL); + if (IS_ERR(dev->external_clk)) { + dev_err(dev->device, "couldn't get clock\n"); + return -ENODEV; + } + + rc = clk_set_rate(dev->external_clk, STD_EXT_CLK_FREQ); + if (rc) { + dev_err(dev->device, "%s: clk_set_rate returned %d\n", + __func__, rc); + return rc; + } + + rc = clk_prepare_enable(dev->external_clk); + if (rc) { + dev_err(dev->device, "couldn't enable clock\n"); + return rc; + } + + /* Parse bus speed */ + rc = bcm_kona_i2c_assign_bus_speed(dev); + if (rc) + goto probe_disable_clk; + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Configure internal dividers */ + bcm_kona_i2c_config_timing(dev); + + /* Disable timeout */ + writel(0, dev->base + TOUT_OFFSET); + + /* Enable autosense */ + bcm_kona_i2c_enable_autosense(dev); + + /* Enable TX FIFO */ + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + /* Clear all pending interrupts */ + writel(ISR_CMDBUSY_MASK | + ISR_READ_COMPLETE_MASK | + ISR_SES_DONE_MASK | + ISR_ERR_MASK | + ISR_TXFIFOEMPTY_MASK | + ISR_NOACK_MASK, + dev->base + ISR_OFFSET); + + /* Get the interrupt number */ + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + rc = dev->irq; + goto probe_disable_clk; + } + + /* register the ISR handler */ + rc = devm_request_irq(&pdev->dev, dev->irq, bcm_kona_i2c_isr, + IRQF_SHARED, pdev->name, dev); + if (rc) { + dev_err(dev->device, "failed to request irq %i\n", dev->irq); + goto probe_disable_clk; + } + + /* Enable the controller but leave it idle */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); + + /* Disable internal clock */ + bcm_kona_i2c_disable_clock(dev); + + /* Disable external clock */ + clk_disable_unprepare(dev->external_clk); + + /* Add the i2c adapter */ + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + 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; + + rc = i2c_add_adapter(adap); + if (rc) + return rc; + + dev_info(dev->device, "device registered successfully\n"); + + return 0; + +probe_disable_clk: + bcm_kona_i2c_disable_clock(dev); + clk_disable_unprepare(dev->external_clk); + + return rc; +} + +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); +} + +static const struct of_device_id bcm_kona_i2c_of_match[] = { + {.compatible = "brcm,kona-i2c",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_kona_i2c_of_match); + +static struct platform_driver bcm_kona_i2c_driver = { + .driver = { + .name = "bcm-kona-i2c", + .of_match_table = bcm_kona_i2c_of_match, + }, + .probe = bcm_kona_i2c_probe, + .remove = bcm_kona_i2c_remove, +}; +module_platform_driver(bcm_kona_i2c_driver); + +MODULE_AUTHOR("Tim Kryger "); +MODULE_DESCRIPTION("Broadcom Kona I2C Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c new file mode 100644 index 000000000..0d7e2654a --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BCM2835 I2C controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCM2835_I2C_C 0x0 +#define BCM2835_I2C_S 0x4 +#define BCM2835_I2C_DLEN 0x8 +#define BCM2835_I2C_A 0xc +#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) +#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */ +#define BCM2835_I2C_C_ST BIT(7) +#define BCM2835_I2C_C_INTD BIT(8) +#define BCM2835_I2C_C_INTT BIT(9) +#define BCM2835_I2C_C_INTR BIT(10) +#define BCM2835_I2C_C_I2CEN BIT(15) + +#define BCM2835_I2C_S_TA BIT(0) +#define BCM2835_I2C_S_DONE BIT(1) +#define BCM2835_I2C_S_TXW BIT(2) +#define BCM2835_I2C_S_RXR BIT(3) +#define BCM2835_I2C_S_TXD BIT(4) +#define BCM2835_I2C_S_RXD BIT(5) +#define BCM2835_I2C_S_TXE BIT(6) +#define BCM2835_I2C_S_RXF BIT(7) +#define BCM2835_I2C_S_ERR BIT(8) +#define BCM2835_I2C_S_CLKT BIT(9) +#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */ + +#define BCM2835_I2C_FEDL_SHIFT 16 +#define BCM2835_I2C_REDL_SHIFT 0 + +#define BCM2835_I2C_CDIV_MIN 0x0002 +#define BCM2835_I2C_CDIV_MAX 0xFFFE + +struct bcm2835_i2c_dev { + struct device *dev; + void __iomem *regs; + int irq; + 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; + size_t msg_buf_remaining; +}; + +static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, + u32 reg, u32 val) +{ + writel(val, i2c_dev->regs + reg); +} + +static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) +{ + return readl(i2c_dev->regs + reg); +} + +#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 = DIV_ROUND_UP(parent_rate, rate); + + /* + * Per the datasheet, the register is always interpreted as an even + * number, by rounding down. In other words, the LSB is ignored. So, + * if the LSB is set, increment the divider to avoid any issue. + */ + if (divider & 1) + divider++; + if ((divider < BCM2835_I2C_CDIV_MIN) || + (divider > BCM2835_I2C_CDIV_MAX)) + return -EINVAL; + + 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 + * outputting the next data bit. Note that both FEDL and REDL + * can't be greater than CDIV/2. + */ + fedl = max(divider / 16, 1u); + + /* + * Number of core clocks to wait after rising edge before + * sampling the next incoming data bit. + */ + redl = max(divider / 4, 1u); + + 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; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_TXD)) + break; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO, + *i2c_dev->msg_buf); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_RXD)) + break; + *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev, + BCM2835_I2C_FIFO); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +/* + * Repeated Start Condition (Sr) + * The BCM2835 ARM Peripherals datasheet mentions a way to trigger a Sr when it + * 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 + * firmware actually does it using polling and says that it's a workaround for + * a problem in the state machine. + * It turns out that it is possible to use the TXW interrupt to know when the + * transfer is active, provided the FIFO has not been prefilled. + */ + +static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 c = BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN; + struct i2c_msg *msg = i2c_dev->curr_msg; + bool last_msg = (i2c_dev->num_msgs == 1); + + if (!i2c_dev->num_msgs) + return; + + i2c_dev->num_msgs--; + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + + if (msg->flags & I2C_M_RD) + c |= BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR; + else + c |= BCM2835_I2C_C_INTT; + + if (last_msg) + c |= BCM2835_I2C_C_INTD; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); + 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 + * non-idle state and I2C_C_READ, it sets an abort_rx flag and runs through + * the state machine to send a NACK and a STOP. Since we're setting CLEAR + * without I2CEN, that NACK will be hanging around queued up for next time + * we start the engine. + */ + +static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) +{ + struct bcm2835_i2c_dev *i2c_dev = data; + u32 val, err; + + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + + err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); + if (err) { + i2c_dev->msg_err = err; + goto complete; + } + + if (val & BCM2835_I2C_S_DONE) { + if (!i2c_dev->curr_msg) { + dev_err(i2c_dev->dev, "Got unexpected interrupt (from firmware?)\n"); + } else if (i2c_dev->curr_msg->flags & I2C_M_RD) { + bcm2835_drain_rxfifo(i2c_dev); + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + } + + if ((val & BCM2835_I2C_S_RXD) || i2c_dev->msg_buf_remaining) + i2c_dev->msg_err = BCM2835_I2C_S_LEN; + else + i2c_dev->msg_err = 0; + goto complete; + } + + if (val & BCM2835_I2C_S_TXW) { + if (!i2c_dev->msg_buf_remaining) { + i2c_dev->msg_err = val | BCM2835_I2C_S_LEN; + goto complete; + } + + bcm2835_fill_txfifo(i2c_dev); + + if (i2c_dev->num_msgs && !i2c_dev->msg_buf_remaining) { + i2c_dev->curr_msg++; + bcm2835_i2c_start_transfer(i2c_dev); + } + + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_RXR) { + if (!i2c_dev->msg_buf_remaining) { + i2c_dev->msg_err = val | BCM2835_I2C_S_LEN; + goto complete; + } + + bcm2835_drain_rxfifo(i2c_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; + +complete: + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, BCM2835_I2C_S_CLKT | + BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE); + complete(&i2c_dev->completion); + + return IRQ_HANDLED; +} + +static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + unsigned long time_left; + int i; + + for (i = 0; i < (num - 1); i++) + if (msgs[i].flags & I2C_M_RD) { + dev_warn_once(i2c_dev->dev, + "only one read message supported, has to be last\n"); + return -EOPNOTSUPP; + } + + i2c_dev->curr_msg = msgs; + i2c_dev->num_msgs = num; + reinit_completion(&i2c_dev->completion); + + bcm2835_i2c_start_transfer(i2c_dev); + + 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); + return -ETIMEDOUT; + } + + if (!i2c_dev->msg_err) + return num; + + dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err); + + if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) + return -EREMOTEIO; + + return -EIO; +} + +static u32 bcm2835_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm2835_i2c_algo = { + .xfer = bcm2835_i2c_xfer, + .functionality = bcm2835_i2c_func, +}; + +/* + * 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 = { + .flags = I2C_AQ_NO_CLK_STRETCH, +}; + +static int bcm2835_i2c_probe(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev; + 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) + return -ENOMEM; + platform_set_drvdata(pdev, i2c_dev); + i2c_dev->dev = &pdev->dev; + init_completion(&i2c_dev->completion); + + i2c_dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(i2c_dev->regs)) + return PTR_ERR(i2c_dev->regs); + + 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", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; + } + + 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; + } + + 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"); + goto err_disable_unprepare_clk; + } + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + 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 = 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) + 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 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); +} + +static const struct of_device_id bcm2835_i2c_of_match[] = { + { .compatible = "brcm,bcm2711-i2c" }, + { .compatible = "brcm,bcm2835-i2c", .data = &bcm2835_i2c_quirks }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match); + +static struct platform_driver bcm2835_i2c_driver = { + .probe = bcm2835_i2c_probe, + .remove = bcm2835_i2c_remove, + .driver = { + .name = "i2c-bcm2835", + .of_match_table = bcm2835_i2c_of_match, + }, +}; +module_platform_driver(bcm2835_i2c_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("BCM2835 I2C bus adapter"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-bcm2835"); diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c new file mode 100644 index 000000000..5fa30e892 --- /dev/null +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_DATA_REGS 8 + +/* + * PER_I2C/BSC count register mask depends on 1 byte/4 byte data register + * size. Cable modem and DSL SoCs with Peripheral i2c cores use 1 byte per + * data register whereas STB SoCs use 4 byte per data register transfer, + * account for this difference in total count per transaction and mask to + * use. + */ +#define BSC_CNT_REG1_MASK(nb) (nb == 1 ? GENMASK(3, 0) : GENMASK(5, 0)) +#define BSC_CNT_REG1_SHIFT 0 + +/* BSC CTL register field definitions */ +#define BSC_CTL_REG_DTF_MASK 0x00000003 +#define BSC_CTL_REG_SCL_SEL_MASK 0x00000030 +#define BSC_CTL_REG_SCL_SEL_SHIFT 4 +#define BSC_CTL_REG_INT_EN_MASK 0x00000040 +#define BSC_CTL_REG_INT_EN_SHIFT 6 +#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080 + +/* BSC_IIC_ENABLE r/w enable and interrupt field definitions */ +#define BSC_IIC_EN_RESTART_MASK 0x00000040 +#define BSC_IIC_EN_NOSTART_MASK 0x00000020 +#define BSC_IIC_EN_NOSTOP_MASK 0x00000010 +#define BSC_IIC_EN_NOACK_MASK 0x00000004 +#define BSC_IIC_EN_INTRP_MASK 0x00000002 +#define BSC_IIC_EN_ENABLE_MASK 0x00000001 + +/* BSC_CTLHI control register field definitions */ +#define BSC_CTLHI_REG_INPUT_SWITCHING_LEVEL_MASK 0x00000080 +#define BSC_CTLHI_REG_DATAREG_SIZE_MASK 0x00000040 +#define BSC_CTLHI_REG_IGNORE_ACK_MASK 0x00000002 +#define BSC_CTLHI_REG_WAIT_DIS_MASK 0x00000001 + +#define I2C_TIMEOUT 100 /* msecs */ + +/* Condition mask used for non combined transfer */ +#define COND_RESTART BSC_IIC_EN_RESTART_MASK +#define COND_NOSTART BSC_IIC_EN_NOSTART_MASK +#define COND_NOSTOP BSC_IIC_EN_NOSTOP_MASK +#define COND_START_STOP (COND_RESTART | COND_NOSTART | COND_NOSTOP) + +/* BSC data transfer direction */ +#define DTF_WR_MASK 0x00000000 +#define DTF_RD_MASK 0x00000001 +/* BSC data transfer direction combined format */ +#define DTF_RD_WR_MASK 0x00000002 +#define DTF_WR_RD_MASK 0x00000003 + +#define INT_ENABLE true +#define INT_DISABLE false + +/* BSC block register map structure to cache fields to be written */ +struct bsc_regs { + 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 */ + u32 iic_enable; /* xfer enable and status */ + u32 data_out[N_DATA_REGS]; /* rx data buffer */ + u32 ctlhi_reg; /* more control fields */ + u32 scl_param; /* reserved */ +}; + +struct bsc_clk_param { + u32 hz; + u32 scl_mask; + u32 div_mask; +}; + +enum bsc_xfer_cmd { + CMD_WR, + CMD_RD, + CMD_WR_NOACK, + CMD_RD_NOACK, +}; + +static char const *cmd_string[] = { + [CMD_WR] = "WR", + [CMD_RD] = "RD", + [CMD_WR_NOACK] = "WR NOACK", + [CMD_RD_NOACK] = "RD NOACK", +}; + +enum bus_speeds { + SPD_375K, + SPD_390K, + SPD_187K, + SPD_200K, + SPD_93K, + SPD_97K, + SPD_46K, + SPD_50K +}; + +static const struct bsc_clk_param bsc_clk[] = { + [SPD_375K] = { + .hz = 375000, + .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_390K] = { + .hz = 390000, + .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_187K] = { + .hz = 187500, + .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_200K] = { + .hz = 200000, + .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_93K] = { + .hz = 93750, + .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_97K] = { + .hz = 97500, + .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_46K] = { + .hz = 46875, + .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_50K] = { + .hz = 50000, + .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + } +}; + +struct brcmstb_i2c_dev { + struct device *device; + void __iomem *base; + int irq; + struct bsc_regs *bsc_regmap; + struct i2c_adapter adapter; + struct completion done; + u32 clk_freq_hz; + int data_regsz; + bool atomic; +}; + +/* register accessors for both be and le cpu arch */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define __bsc_readl(_reg) ioread32be(_reg) +#define __bsc_writel(_val, _reg) iowrite32be(_val, _reg) +#else +#define __bsc_readl(_reg) ioread32(_reg) +#define __bsc_writel(_val, _reg) iowrite32(_val, _reg) +#endif + +#define bsc_readl(_dev, _reg) \ + __bsc_readl(_dev->base + offsetof(struct bsc_regs, _reg)) + +#define bsc_writel(_dev, _val, _reg) \ + __bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg)) + +static inline int brcmstb_i2c_get_xfersz(struct brcmstb_i2c_dev *dev) +{ + return (N_DATA_REGS * dev->data_regsz); +} + +static inline int brcmstb_i2c_get_data_regsz(struct brcmstb_i2c_dev *dev) +{ + return dev->data_regsz; +} + +static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev, + bool int_en) +{ + + if (int_en) + /* Enable BSC CTL interrupt line */ + dev->bsc_regmap->ctl_reg |= BSC_CTL_REG_INT_EN_MASK; + else + /* Disable BSC CTL interrupt line */ + dev->bsc_regmap->ctl_reg &= ~BSC_CTL_REG_INT_EN_MASK; + + barrier(); + bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg); +} + +static irqreturn_t brcmstb_i2c_isr(int irq, void *devid) +{ + struct brcmstb_i2c_dev *dev = devid; + u32 status_bsc_ctl = bsc_readl(dev, ctl_reg); + u32 status_iic_intrp = bsc_readl(dev, iic_enable); + + dev_dbg(dev->device, "isr CTL_REG %x IIC_EN %x\n", + status_bsc_ctl, status_iic_intrp); + + if (!(status_bsc_ctl & BSC_CTL_REG_INT_EN_MASK)) + return IRQ_NONE; + + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + complete(&dev->done); + + dev_dbg(dev->device, "isr handled"); + return IRQ_HANDLED; +} + +/* Wait for device to be ready */ +static int brcmstb_i2c_wait_if_busy(struct brcmstb_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT); + + while ((bsc_readl(dev, iic_enable) & BSC_IIC_EN_INTRP_MASK)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + return 0; +} + +/* i2c xfer completion function, handles both irq and polling mode */ +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 && !dev->atomic) { + if (!wait_for_completion_timeout(&dev->done, timeout)) + ret = -ETIMEDOUT; + } else { + /* we are in polling mode */ + u32 bsc_intrp; + unsigned long time_left = jiffies + timeout; + + do { + bsc_intrp = bsc_readl(dev, iic_enable) & + BSC_IIC_EN_INTRP_MASK; + if (time_after(jiffies, time_left)) { + ret = -ETIMEDOUT; + break; + } + cpu_relax(); + } while (!bsc_intrp); + } + + if (dev->irq < 0 || ret == -ETIMEDOUT) + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + + return ret; +} + +/* Set xfer START/STOP conditions for subsequent transfer */ +static void brcmstb_set_i2c_start_stop(struct brcmstb_i2c_dev *dev, + u32 cond_flag) +{ + u32 regval = dev->bsc_regmap->iic_enable; + + dev->bsc_regmap->iic_enable = (regval & ~COND_START_STOP) | cond_flag; +} + +/* Send I2C request check completion */ +static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev, + enum bsc_xfer_cmd cmd) +{ + int rc = 0; + struct bsc_regs *pi2creg = dev->bsc_regmap; + + /* Make sure the hardware is ready */ + rc = brcmstb_i2c_wait_if_busy(dev); + if (rc < 0) + return rc; + + /* only if we are in interrupt mode */ + if (dev->irq >= 0 && !dev->atomic) + reinit_completion(&dev->done); + + /* enable BSC CTL interrupt line */ + brcmstb_i2c_enable_disable_irq(dev, INT_ENABLE); + + /* initiate transfer by setting iic_enable */ + pi2creg->iic_enable |= BSC_IIC_EN_ENABLE_MASK; + bsc_writel(dev, pi2creg->iic_enable, iic_enable); + + /* Wait for transaction to finish or timeout */ + rc = brcmstb_i2c_wait_for_completion(dev); + if (rc) { + dev_dbg(dev->device, "intr timeout for cmd %s\n", + cmd_string[cmd]); + goto cmd_out; + } + + 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", + cmd_string[cmd]); + } + +cmd_out: + bsc_writel(dev, 0, cnt_reg); + bsc_writel(dev, 0, iic_enable); + + return rc; +} + +/* 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) +{ + int cnt, byte, i, rc; + enum bsc_xfer_cmd cmd; + u32 ctl_reg; + struct bsc_regs *pi2creg = dev->bsc_regmap; + int no_ack = pmsg->flags & I2C_M_IGNORE_NAK; + int data_regsz = brcmstb_i2c_get_data_regsz(dev); + + /* see if the transaction needs to check NACK conditions */ + if (no_ack) { + cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK + : CMD_WR_NOACK; + pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK; + } else { + cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD : CMD_WR; + pi2creg->ctlhi_reg &= ~BSC_CTLHI_REG_IGNORE_ACK_MASK; + } + bsc_writel(dev, pi2creg->ctlhi_reg, ctlhi_reg); + + /* set data transfer direction */ + ctl_reg = pi2creg->ctl_reg & ~BSC_CTL_REG_DTF_MASK; + if (cmd == CMD_WR || cmd == CMD_WR_NOACK) + pi2creg->ctl_reg = ctl_reg | DTF_WR_MASK; + else + pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK; + + /* set the read/write length */ + bsc_writel(dev, BSC_CNT_REG1_MASK(data_regsz) & + (len << BSC_CNT_REG1_SHIFT), cnt_reg); + + /* Write data into data_in register */ + + if (cmd == CMD_WR || cmd == CMD_WR_NOACK) { + for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) { + u32 word = 0; + + for (byte = 0; byte < data_regsz; byte++) { + word >>= BITS_PER_BYTE; + if ((cnt + byte) < len) + word |= buf[cnt + byte] << + (BITS_PER_BYTE * (data_regsz - 1)); + } + bsc_writel(dev, word, data_in[i]); + } + } + + /* Initiate xfer, the function will return on completion */ + rc = brcmstb_send_i2c_cmd(dev, cmd); + + if (rc != 0) { + dev_dbg(dev->device, "%s failure", cmd_string[cmd]); + return rc; + } + + /* Read data from data_out register */ + if (cmd == CMD_RD || cmd == CMD_RD_NOACK) { + for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) { + u32 data = bsc_readl(dev, data_out[i]); + + for (byte = 0; byte < data_regsz && + (byte + cnt) < len; byte++) { + buf[cnt + byte] = data & 0xff; + data >>= BITS_PER_BYTE; + } + } + } + + return 0; +} + +/* Write a single byte of data to the i2c bus */ +static int brcmstb_i2c_write_data_byte(struct brcmstb_i2c_dev *dev, + u8 *buf, unsigned int nak_expected) +{ + enum bsc_xfer_cmd cmd = nak_expected ? CMD_WR : CMD_WR_NOACK; + + bsc_writel(dev, 1, cnt_reg); + bsc_writel(dev, *buf, data_in); + + return brcmstb_send_i2c_cmd(dev, cmd); +} + +/* Send i2c address */ +static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned char addr; + + if (msg->flags & I2C_M_TEN) { + /* First byte is 11110XX0 where XX is upper 2 bits */ + 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 = 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); + + /* Then re-send the first byte with the read bit set */ + 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); + + bsc_writel(dev, addr, chip_address); + } + + return 0; +} + +static int brcmstb_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct brcmstb_i2c_dev *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int rc = 0; + int i; + int bytes_to_xfer; + u8 *tmp_buf; + int len = 0; + int xfersz = brcmstb_i2c_get_xfersz(dev); + u32 cond, cond_per_msg; + + /* Loop through all messages */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + len = pmsg->len; + tmp_buf = pmsg->buf; + + dev_dbg(dev->device, + "msg# %d/%d flg %x buf %x len %d\n", i, + num - 1, pmsg->flags, + pmsg->buf ? pmsg->buf[0] : '0', pmsg->len); + + if (i < (num - 1) && (msgs[i + 1].flags & I2C_M_NOSTART)) + cond = ~COND_START_STOP; + else + cond = COND_RESTART | COND_NOSTOP; + + brcmstb_set_i2c_start_stop(dev, cond); + + /* Send target address */ + if (!(pmsg->flags & I2C_M_NOSTART)) { + rc = brcmstb_i2c_do_addr(dev, pmsg); + if (rc < 0) { + dev_dbg(dev->device, + "NACK for addr %2.2x msg#%d rc = %d\n", + pmsg->addr, i, rc); + goto out; + } + } + + cond_per_msg = cond; + + /* Perform data transfer */ + while (len) { + bytes_to_xfer = min(len, xfersz); + + if (len <= xfersz) { + if (i == (num - 1)) + cond_per_msg = cond_per_msg & + ~(COND_RESTART | COND_NOSTOP); + else + cond_per_msg = cond; + } else { + cond_per_msg = (cond_per_msg & ~COND_RESTART) | + COND_NOSTOP; + } + + brcmstb_set_i2c_start_stop(dev, cond_per_msg); + + rc = brcmstb_i2c_xfer_bsc_data(dev, tmp_buf, + bytes_to_xfer, pmsg); + if (rc < 0) + goto out; + + len -= bytes_to_xfer; + tmp_buf += bytes_to_xfer; + + cond_per_msg = COND_NOSTART | COND_NOSTOP; + } + } + + rc = num; +out: + return rc; + +} + +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 + | I2C_FUNC_NOSTART | I2C_FUNC_PROTOCOL_MANGLING; +} + +static const struct i2c_algorithm brcmstb_i2c_algo = { + .xfer = brcmstb_i2c_xfer, + .xfer_atomic = brcmstb_i2c_xfer_atomic, + .functionality = brcmstb_i2c_functionality, +}; + +static void brcmstb_i2c_set_bus_speed(struct brcmstb_i2c_dev *dev) +{ + int i = 0, num_speeds = ARRAY_SIZE(bsc_clk); + u32 clk_freq_hz = dev->clk_freq_hz; + + for (i = 0; i < num_speeds; i++) { + if (bsc_clk[i].hz == clk_freq_hz) { + dev->bsc_regmap->ctl_reg &= ~(BSC_CTL_REG_SCL_SEL_MASK + | BSC_CTL_REG_DIV_CLK_MASK); + dev->bsc_regmap->ctl_reg |= (bsc_clk[i].scl_mask | + bsc_clk[i].div_mask); + bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg); + break; + } + } + + /* in case we did not get find a valid speed */ + if (i == num_speeds) { + i = (bsc_readl(dev, ctl_reg) & BSC_CTL_REG_SCL_SEL_MASK) >> + BSC_CTL_REG_SCL_SEL_SHIFT; + dev_warn(dev->device, "leaving current clock-frequency @ %dHz\n", + bsc_clk[i].hz); + } +} + +static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev) +{ + if (brcmstb_i2c_get_data_regsz(dev) == sizeof(u32)) + /* set 4 byte data in/out xfers */ + dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK; + else + dev->bsc_regmap->ctlhi_reg &= ~BSC_CTLHI_REG_DATAREG_SIZE_MASK; + + bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg); + /* set bus speed */ + 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) +{ + struct brcmstb_i2c_dev *dev; + struct i2c_adapter *adap; + const char *int_name; + int rc; + + /* Allocate memory for private data structure */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->bsc_regmap = devm_kzalloc(&pdev->dev, sizeof(*dev->bsc_regmap), GFP_KERNEL); + if (!dev->bsc_regmap) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + /* Map hardware registers */ + 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", + &int_name); + if (rc < 0) + int_name = NULL; + + /* Get the interrupt number */ + 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 */ + 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, + "clock-frequency", &dev->clk_freq_hz)) { + dev_warn(dev->device, "setting clock-frequency@%dHz\n", + bsc_clk[0].hz); + dev->clk_freq_hz = bsc_clk[0].hz; + } + + /* set the data in/out register size for compatible SoCs */ + if (of_device_is_compatible(dev->device->of_node, + "brcm,brcmper-i2c")) + dev->data_regsz = sizeof(u8); + else + dev->data_regsz = sizeof(u32); + + brcmstb_i2c_set_bsc_reg_defaults(dev); + + /* Add the i2c adapter */ + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + 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) + 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; +} + +static void brcmstb_i2c_remove(struct platform_device *pdev) +{ + struct brcmstb_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); +} + +static int brcmstb_i2c_suspend(struct device *dev) +{ + struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c_dev->adapter); + return 0; +} + +static int brcmstb_i2c_resume(struct device *dev) +{ + struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + brcmstb_i2c_set_bsc_reg_defaults(i2c_dev); + i2c_mark_adapter_resumed(&i2c_dev->adapter); + + return 0; +} + +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); + +static struct platform_driver brcmstb_i2c_driver = { + .driver = { + .name = "brcmstb-i2c", + .of_match_table = brcmstb_i2c_of_match, + .pm = pm_sleep_ptr(&brcmstb_i2c_pm), + }, + .probe = brcmstb_i2c_probe, + .remove = brcmstb_i2c_remove, +}; +module_platform_driver(brcmstb_i2c_driver); + +MODULE_AUTHOR("Kamal Dasu "); +MODULE_DESCRIPTION("Broadcom Settop I2C Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c new file mode 100644 index 000000000..da8770182 --- /dev/null +++ b/drivers/i2c/busses/i2c-cadence.c @@ -0,0 +1,1672 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * I2C bus driver for the Cadence I2C controller. + * + * Copyright (C) 2009 - 2014 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets for the I2C device. */ +#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */ +#define CDNS_I2C_SR_OFFSET 0x04 /* Status Register, RO */ +#define CDNS_I2C_ADDR_OFFSET 0x08 /* I2C Address Register, RW */ +#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */ +#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 */ + +/* Control Register Bit mask definitions */ +#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */ +#define CDNS_I2C_CR_ACK_EN BIT(3) +#define CDNS_I2C_CR_NEA BIT(2) +#define CDNS_I2C_CR_MS BIT(1) +/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */ +#define CDNS_I2C_CR_RW BIT(0) +/* 1 = Auto init FIFO to zeroes */ +#define CDNS_I2C_CR_CLR_FIFO BIT(6) +#define CDNS_I2C_CR_DIVA_SHIFT 14 +#define CDNS_I2C_CR_DIVA_MASK (3 << CDNS_I2C_CR_DIVA_SHIFT) +#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 + * Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0] + * bits. A write access to this register always initiates a transfer if the I2C + * is in master mode. + */ +#define CDNS_I2C_ADDR_MASK 0x000003FF /* I2C Address Mask */ + +/* + * I2C Interrupt Registers Bit mask definitions + * All the four interrupt registers (Status/Mask/Enable/Disable) have the same + * bit definitions. + */ +#define CDNS_I2C_IXR_ARB_LOST BIT(9) +#define CDNS_I2C_IXR_RX_UNF BIT(7) +#define CDNS_I2C_IXR_TX_OVF BIT(6) +#define CDNS_I2C_IXR_RX_OVF BIT(5) +#define CDNS_I2C_IXR_SLV_RDY BIT(4) +#define CDNS_I2C_IXR_TO BIT(3) +#define CDNS_I2C_IXR_NACK BIT(2) +#define CDNS_I2C_IXR_DATA BIT(1) +#define CDNS_I2C_IXR_COMP BIT(0) + +#define CDNS_I2C_IXR_ALL_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_SLV_RDY | \ + CDNS_I2C_IXR_TO | \ + CDNS_I2C_IXR_NACK | \ + CDNS_I2C_IXR_DATA | \ + CDNS_I2C_IXR_COMP) + +#define CDNS_I2C_IXR_ERR_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_NACK) + +#define CDNS_I2C_ENABLED_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_NACK | \ + 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_DEFAULT 16 +#define CDNS_I2C_MAX_TRANSFER_SIZE 255 +/* Transfer size in multiples of data interrupt depth */ +#define CDNS_I2C_TRANSFER_SIZE(max) ((max) - 3) + +#define DRIVER_NAME "cdns-i2c" + +#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 + * + * @dev: Pointer to device structure + * @membase: Base address of the I2C device + * @adap: I2C adapter instance + * @p_msg: Message pointer + * @err_status: Error status in Interrupt Status Register + * @xfer_done: Transfer complete status + * @p_send_buf: Pointer to transmit buffer + * @p_recv_buf: Pointer to receive buffer + * @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 + * @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; + void __iomem *membase; + struct i2c_adapter adap; + struct i2c_msg *p_msg; + int err_status; + struct completion xfer_done; + unsigned char *p_send_buf; + unsigned char *p_recv_buf; + unsigned int send_count; + unsigned int recv_count; + unsigned int curr_recv_count; + 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 { + u32 quirks; +}; + +#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \ + 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 + * + * Helper to clear the controller's bus hold bit. + */ +static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id) +{ + u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); + if (reg & CDNS_I2C_CR_HOLD) + cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET); +} + +static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround) +{ + return (hold_wrkaround && + (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_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 in master role. + * + * Return: IRQ_HANDLED always + */ +static irqreturn_t cdns_i2c_master_isr(void *ptr) +{ + unsigned int isr_status, avail_bytes; + unsigned int bytes_to_send; + bool updatetx; + struct cdns_i2c *id = ptr; + /* Signal completion only after everything is updated */ + int done_flag = 0; + irqreturn_t status = IRQ_NONE; + + 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)) { + done_flag = 1; + status = IRQ_HANDLED; + } + + /* + * Check if transfer size register needs to be updated again for a + * large data receive operation. + */ + updatetx = id->recv_count > id->curr_recv_count; + + /* When receiving, handle data interrupt and completion interrupt */ + if (id->p_recv_buf && + ((isr_status & CDNS_I2C_IXR_COMP) || + (isr_status & CDNS_I2C_IXR_DATA))) { + /* Read data if receive data valid is set */ + while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & + CDNS_I2C_SR_RXDV) { + 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); + + } 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, updatetx)) + break; + } + + /* + * The controller sends NACK to the slave 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 (((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 = 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) and signal completion */ + if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) { + if (!id->bus_hold_flag) + cdns_i2c_clear_bus_hold(id); + done_flag = 1; + } + + status = IRQ_HANDLED; + } + + /* When sending, handle transfer complete interrupt */ + if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) { + /* + * If there is more data to be sent, calculate the + * space available in FIFO and fill with that many bytes. + */ + if (id->send_count) { + 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--; + } + } else { + /* + * Signal the completion of transaction and + * clear the hold bus bit if there are no + * further messages to be processed. + */ + done_flag = 1; + } + if (!id->send_count && !id->bus_hold_flag) + cdns_i2c_clear_bus_hold(id); + + status = IRQ_HANDLED; + } + + /* Update the status for errors */ + id->err_status |= isr_status & CDNS_I2C_IXR_ERR_INTR_MASK; + if (id->err_status) + status = IRQ_HANDLED; + + if (done_flag) + complete(&id->xfer_done); + + return status; +} + +/** + * 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 + */ +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; + + /* Put the controller in master receive mode and clear the FIFO */ + 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 + id->p_msg->len; + + id->curr_recv_count = id->recv_count; + + /* + * 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 > id->fifo_depth) + ctrl_reg |= CDNS_I2C_CR_HOLD; + + cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); + + /* Clear the interrupts in interrupt status register */ + isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); + cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); + + /* + * The no. of bytes to receive is checked against the limit of + * max transfer size. Set transfer size register with no of bytes + * receive if it is less than transfer size and transfer size if + * it is more. Enable the interrupts. + */ + if (id->recv_count > id->transfer_size) { + cdns_i2c_writereg(id->transfer_size, + CDNS_I2C_XFER_SIZE_OFFSET); + id->curr_recv_count = id->transfer_size; + } else { + cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_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); +} + +/** + * cdns_i2c_msend - Prepare and start a master send operation + * @id: pointer to the i2c device + */ +static void cdns_i2c_msend(struct cdns_i2c *id) +{ + unsigned int avail_bytes; + unsigned int bytes_to_send; + unsigned int ctrl_reg; + unsigned int isr_status; + + id->p_recv_buf = NULL; + id->p_send_buf = id->p_msg->buf; + id->send_count = id->p_msg->len; + + /* Set the controller in Master transmit mode and clear the FIFO. */ + ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); + ctrl_reg &= ~CDNS_I2C_CR_RW; + ctrl_reg |= CDNS_I2C_CR_CLR_FIFO; + + /* + * 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 > id->fifo_depth) + ctrl_reg |= CDNS_I2C_CR_HOLD; + cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET); + + /* Clear the interrupts in interrupt status register. */ + isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); + cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET); + + /* + * Calculate the space available in FIFO. Check the message length + * against the space available, and fill the FIFO accordingly. + * Enable the interrupts. + */ + 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--; + } + + /* + * Clear the bus hold flag if there is no more data + * and if it is the last message. + */ + if (!id->bus_hold_flag && !id->send_count) + cdns_i2c_clear_bus_hold(id); + /* Set the slave address in address register - triggers operation. */ + cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, + CDNS_I2C_ADDR_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); +} + +/** + * cdns_i2c_master_reset - Reset the interface + * @adap: pointer to the i2c adapter driver instance + * + * This function cleanup the fifos, clear the hold bit and status + * and disable the interrupts. + */ +static void cdns_i2c_master_reset(struct i2c_adapter *adap) +{ + struct cdns_i2c *id = adap->algo_data; + u32 regval; + + /* Disable the interrupts */ + cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET); + /* Clear the hold bit and fifos */ + regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); + regval &= ~CDNS_I2C_CR_HOLD; + regval |= CDNS_I2C_CR_CLR_FIFO; + 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 interrupt status register */ + regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); + cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET); + /* Clear the status register */ + regval = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET); + cdns_i2c_writereg(regval, CDNS_I2C_SR_OFFSET); +} + +static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, + struct i2c_adapter *adap) +{ + unsigned long time_left, msg_timeout; + u32 reg; + + id->p_msg = msg; + id->err_status = 0; + 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); + if (msg->flags & I2C_M_TEN) { + if (reg & CDNS_I2C_CR_NEA) + cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA, + CDNS_I2C_CR_OFFSET); + } else { + if (!(reg & CDNS_I2C_CR_NEA)) + cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA, + CDNS_I2C_CR_OFFSET); + } + + /* Check for the R/W flag on each msg */ + if (msg->flags & I2C_M_RD) + cdns_i2c_mrecv(id); + else + cdns_i2c_msend(id); + + /* 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); + return -ETIMEDOUT; + } + + cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, + CDNS_I2C_IDR_OFFSET); + + /* If it is bus arbitration error, try again */ + 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; +} + +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; + + /* Check if the bus is free */ + 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; + if (id->adap.bus_recovery_info) + i2c_recover_bus(adap); + return ret; + } + + hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT); + /* + * Set the flag to one when multiple messages are to be + * processed with a repeated start. + */ + if (num > 1) { + /* + * This controller does not give completion interrupt after a + * master receive message if HOLD bit is set (repeated start), + * resulting in SW timeout. Hence, if a receive message is + * followed by any other message, an error is returned + * indicating that this sequence is not supported. + */ + for (count = 0; (count < num - 1 && hold_quirk); count++) { + if (msgs[count].flags & I2C_M_RD) { + dev_warn(adap->dev.parent, + "Can't do repeated start after a receive message\n"); + return -EOPNOTSUPP; + } + } + id->bus_hold_flag = 1; + reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); + reg |= CDNS_I2C_CR_HOLD; + cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET); + } else { + id->bus_hold_flag = 0; + } + + /* Process the msg one by one */ + for (count = 0; count < num; count++, msgs++) { + if (count == (num - 1)) + id->bus_hold_flag = 0; + + ret = cdns_i2c_process_msg(id, msgs, adap); + if (ret) + return ret; + + /* Report the other error interrupts to application */ + if (id->err_status || id->err_status_atomic) { + cdns_i2c_master_reset(adap); + + 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 = cdns_i2c_master_common_xfer(adap, msgs, num); + if (!ret) + ret = num; +#if IS_ENABLED(CONFIG_I2C_SLAVE) +out: + /* 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 + * + * Return: 32 bit value, each bit corresponding to a feature + */ +static u32 cdns_i2c_func(struct i2c_adapter *adap) +{ + 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 = { + .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 +}; + +/** + * cdns_i2c_calc_divs - Calculate clock dividers + * @f: I2C clock frequency + * @input_clk: Input clock frequency + * @a: First divider (return value) + * @b: Second divider (return value) + * + * f is used as input and output variable. As input it is used as target I2C + * frequency. On function exit f holds the actually resulting I2C frequency. + * + * Return: 0 on success, negative errno otherwise. + */ +static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk, + unsigned int *a, unsigned int *b) +{ + unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp; + unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0; + unsigned int last_error, current_error; + + /* calculate (divisor_a+1) x (divisor_b+1) */ + temp = input_clk / (22 * fscl); + + /* + * If the calculated value is negative or 0, the fscl input is out of + * range. Return error. + */ + if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX))) + return -EINVAL; + + last_error = -1; + for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) { + div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1)); + + if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX)) + continue; + div_b--; + + actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1)); + + if (actual_fscl > fscl) + continue; + + current_error = fscl - actual_fscl; + + if (last_error > current_error) { + calc_div_a = div_a; + calc_div_b = div_b; + best_fscl = actual_fscl; + last_error = current_error; + } + } + + *a = calc_div_a; + *b = calc_div_b; + *f = best_fscl; + + return 0; +} + +/** + * cdns_i2c_setclk - This function sets the serial clock rate for the I2C device + * @clk_in: I2C clock input frequency in Hz + * @id: Pointer to the I2C device structure + * + * The device must be idle rather than busy transferring data before setting + * these device options. + * The data rate is set by values in the control register. + * The formula for determining the correct register values is + * Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1)) + * See the hardware data sheet for a full explanation of setting the serial + * clock rate. The clock can not be faster than the input clock divide by 22. + * The two most common clock rates are 100KHz and 400KHz. + * + * Return: 0 on success, negative error otherwise + */ +static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id) +{ + unsigned int div_a, div_b; + unsigned int ctrl_reg; + int ret = 0; + unsigned long fscl = id->i2c_clk; + + ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b); + if (ret) + return ret; + + 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; +} + +/** + * cdns_i2c_clk_notifier_cb - Clock rate change callback + * @nb: Pointer to notifier block + * @event: Notification reason + * @data: Pointer to notification data object + * + * This function is called when the cdns_i2c input clock frequency changes. + * The callback checks whether a valid bus frequency can be generated after the + * change. If so, the change is acknowledged, otherwise the change is aborted. + * New dividers are written to the HW in the pre- or post change notification + * depending on the scaling direction. + * + * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK + * to acknowledge the change, NOTIFY_DONE if the notification is + * considered irrelevant. + */ +static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long + event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct cdns_i2c *id = to_cdns_i2c(nb); + + if (pm_runtime_suspended(id->dev)) + return NOTIFY_OK; + + switch (event) { + case PRE_RATE_CHANGE: + { + unsigned long input_clk = ndata->new_rate; + unsigned long fscl = id->i2c_clk; + unsigned int div_a, div_b; + int ret; + + ret = cdns_i2c_calc_divs(&fscl, input_clk, &div_a, &div_b); + if (ret) { + dev_warn(id->adap.dev.parent, + "clock rate change rejected\n"); + return NOTIFY_STOP; + } + + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + cdns_i2c_setclk(ndata->new_rate, id); + + return NOTIFY_OK; + } + case POST_RATE_CHANGE: + id->input_clk = ndata->new_rate; + /* scale down */ + if (ndata->new_rate < ndata->old_rate) + cdns_i2c_setclk(ndata->new_rate, id); + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + cdns_i2c_setclk(ndata->old_rate, id); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int __maybe_unused cdns_i2c_suspend(struct device *dev) +{ + struct cdns_i2c *xi2c = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&xi2c->adap); + + if (!pm_runtime_status_suspended(dev)) + return cdns_i2c_runtime_suspend(dev); + + return 0; +} + +static int __maybe_unused cdns_i2c_resume(struct device *dev) +{ + struct cdns_i2c *xi2c = dev_get_drvdata(dev); + int err; + + 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) +}; + +static const struct cdns_platform_data r1p10_i2c_def = { + .quirks = CDNS_I2C_BROKEN_HOLD_BIT, +}; + +static const struct of_device_id cdns_i2c_of_match[] = { + { .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def }, + { .compatible = "cdns,i2c-r1p14",}, + { /* end of table */ } +}; +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 + * + * This function does all the memory allocation and registration for the i2c + * device. User can modify the address mode to 10 bit address mode using the + * ioctl call with option I2C_TENBIT. + * + * Return: 0 on success, negative error otherwise + */ +static int cdns_i2c_probe(struct platform_device *pdev) +{ + struct resource *r_mem; + struct cdns_i2c *id; + int ret, irq; + const struct of_device_id *match; + + id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL); + if (!id) + return -ENOMEM; + + id->dev = &pdev->dev; + platform_set_drvdata(pdev, id); + + match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node); + if (match && match->data) { + const struct cdns_platform_data *data = match->data; + id->quirks = data->quirks; + } + + 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); + + 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; + id->adap.algo = &cdns_i2c_algo; + id->adap.timeout = CDNS_I2C_TIMEOUT; + id->adap.retries = 3; /* Default retry value. */ + id->adap.algo_data = id; + id->adap.dev.parent = &pdev->dev; + init_completion(&id->xfer_done); + snprintf(id->adap.name, sizeof(id->adap.name), + "Cadence I2C at %08lx", (unsigned long)r_mem->start); + + 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) + return dev_err_probe(&pdev->dev, ret, + "Failed to de-assert reset.\n"); + + 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)) + dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); + id->input_clk = clk_get_rate(id->clk); + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &id->i2c_clk); + 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_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_notifier_unregister; + } + + 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", irq); + goto err_clk_notifier_unregister; + } + cdns_i2c_init(id); + + ret = i2c_add_adapter(&id->adap); + if (ret < 0) + 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, irq); + + return 0; + +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; +} + +/** + * cdns_i2c_remove - Unregister the device after releasing the resources + * @pdev: Handle to the platform device structure + * + * This function frees all the resources allocated to the device. + * + * Return: 0 always + */ +static void cdns_i2c_remove(struct platform_device *pdev) +{ + struct cdns_i2c *id = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + + i2c_del_adapter(&id->adap); + clk_notifier_unregister(id->clk, &id->clk_rate_change_nb); + reset_control_assert(id->reset); +} + +/** + * cdns_i2c_shutdown - Prepare I2C controller for system shutdown + * @pdev: Handle to the platform device structure + * + * Mark the adapter as suspended and reset the controller to ensure a clean + * handoff during system reboot or kexec. + */ +static void cdns_i2c_shutdown(struct platform_device *pdev) +{ + struct cdns_i2c *id = platform_get_drvdata(pdev); + + /* Mark the adapter as suspended to prevent further I2C transfers */ + i2c_mark_adapter_suspended(&id->adap); + + /* Reset the controller state if active - clocks are disabled when suspended */ + if (!pm_runtime_status_suspended(&pdev->dev)) + cdns_i2c_master_reset(&id->adap); +} + +static struct platform_driver cdns_i2c_drv = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = cdns_i2c_of_match, + .pm = &cdns_i2c_dev_pm_ops, + }, + .probe = cdns_i2c_probe, + .remove = cdns_i2c_remove, + .shutdown = cdns_i2c_shutdown, +}; + +module_platform_driver(cdns_i2c_drv); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_DESCRIPTION("Cadence I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c new file mode 100644 index 000000000..8065c7e44 --- /dev/null +++ b/drivers/i2c/busses/i2c-cbus-gpio.c @@ -0,0 +1,282 @@ +/* + * CBUS I2C driver for Nokia Internet Tablets. + * + * Copyright (C) 2004-2010 Nokia Corporation + * + * Based on code written by Juha Yrjölä, David Weinehall, Mikko Ylinen and + * Felipe Balbi. Converted to I2C driver by Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Bit counts are derived from Nokia implementation. These should be checked + * if other CBUS implementations appear. + */ +#define CBUS_ADDR_BITS 3 +#define CBUS_REG_BITS 5 + +struct cbus_host { + spinlock_t lock; /* host lock */ + struct device *dev; + struct gpio_desc *clk; + struct gpio_desc *dat; + struct gpio_desc *sel; +}; + +/** + * cbus_send_bit - sends one bit over the bus + * @host: the host we're using + * @bit: one bit of information to send + */ +static void cbus_send_bit(struct cbus_host *host, unsigned bit) +{ + gpiod_set_value(host->dat, bit ? 1 : 0); + gpiod_set_value(host->clk, 1); + gpiod_set_value(host->clk, 0); +} + +/** + * cbus_send_data - sends @len amount of data over the bus + * @host: the host we're using + * @data: the data to send + * @len: size of the transfer + */ +static void cbus_send_data(struct cbus_host *host, unsigned data, unsigned len) +{ + int i; + + for (i = len; i > 0; i--) + cbus_send_bit(host, data & (1 << (i - 1))); +} + +/** + * cbus_receive_bit - receives one bit from the bus + * @host: the host we're using + */ +static int cbus_receive_bit(struct cbus_host *host) +{ + int ret; + + gpiod_set_value(host->clk, 1); + ret = gpiod_get_value(host->dat); + gpiod_set_value(host->clk, 0); + return ret; +} + +/** + * cbus_receive_word - receives 16-bit word from the bus + * @host: the host we're using + */ +static int cbus_receive_word(struct cbus_host *host) +{ + int ret = 0; + int i; + + for (i = 16; i > 0; i--) { + int bit = cbus_receive_bit(host); + + if (bit < 0) + return bit; + + if (bit) + ret |= 1 << (i - 1); + } + return ret; +} + +/** + * cbus_transfer - transfers data over the bus + * @host: the host we're using + * @rw: read/write flag + * @dev: device address + * @reg: register address + * @data: if @rw == I2C_SBUS_WRITE data to send otherwise 0 + */ +static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, + unsigned reg, unsigned data) +{ + unsigned long flags; + int ret; + + /* We don't want interrupts disturbing our transfer */ + spin_lock_irqsave(&host->lock, flags); + + /* Reset state and start of transfer, SEL stays down during transfer */ + gpiod_set_value(host->sel, 0); + + /* Set the DAT pin to output */ + gpiod_direction_output(host->dat, 1); + + /* Send the device address */ + cbus_send_data(host, dev, CBUS_ADDR_BITS); + + /* Send the rw flag */ + cbus_send_bit(host, rw == I2C_SMBUS_READ); + + /* Send the register address */ + cbus_send_data(host, reg, CBUS_REG_BITS); + + if (rw == I2C_SMBUS_WRITE) { + cbus_send_data(host, data, 16); + ret = 0; + } else { + ret = gpiod_direction_input(host->dat); + if (ret) { + dev_dbg(host->dev, "failed setting direction\n"); + goto out; + } + gpiod_set_value(host->clk, 1); + + ret = cbus_receive_word(host); + if (ret < 0) { + dev_dbg(host->dev, "failed receiving data\n"); + goto out; + } + } + + /* Indicate end of transfer, SEL goes up until next transfer */ + 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); + + return ret; +} + +static int cbus_i2c_smbus_xfer(struct i2c_adapter *adapter, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data *data) +{ + struct cbus_host *chost = i2c_get_adapdata(adapter); + int ret; + + if (size != I2C_SMBUS_WORD_DATA) + return -EINVAL; + + ret = cbus_transfer(chost, read_write == I2C_SMBUS_READ, addr, + command, data->word); + if (ret < 0) + return ret; + + if (read_write == I2C_SMBUS_READ) + data->word = ret; + + return 0; +} + +static u32 cbus_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; +} + +static const struct i2c_algorithm cbus_i2c_algo = { + .smbus_xfer = cbus_i2c_smbus_xfer, + .smbus_xfer_atomic = cbus_i2c_smbus_xfer, + .functionality = cbus_i2c_func, +}; + +static void cbus_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + + i2c_del_adapter(adapter); +} + +static int cbus_i2c_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + struct cbus_host *chost; + + adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), + GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + chost = devm_kzalloc(&pdev->dev, sizeof(*chost), GFP_KERNEL); + if (!chost) + return -ENOMEM; + + 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; + adapter->dev.parent = &pdev->dev; + adapter->dev.of_node = pdev->dev.of_node; + adapter->nr = pdev->id; + adapter->timeout = HZ; + adapter->algo = &cbus_i2c_algo; + strscpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); + + spin_lock_init(&chost->lock); + chost->dev = &pdev->dev; + + i2c_set_adapdata(adapter, chost); + platform_set_drvdata(pdev, adapter); + + return i2c_add_numbered_adapter(adapter); +} + +#if defined(CONFIG_OF) +static const struct of_device_id i2c_cbus_dt_ids[] = { + { .compatible = "i2c-cbus-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids); +#endif + +static struct platform_driver cbus_i2c_driver = { + .probe = cbus_i2c_probe, + .remove = cbus_i2c_remove, + .driver = { + .name = "i2c-cbus-gpio", + .of_match_table = of_match_ptr(i2c_cbus_dt_ids), + }, +}; +module_platform_driver(cbus_i2c_driver); + +MODULE_ALIAS("platform:i2c-cbus-gpio"); +MODULE_DESCRIPTION("CBUS I2C driver"); +MODULE_AUTHOR("Juha Yrjölä"); +MODULE_AUTHOR("David Weinehall"); +MODULE_AUTHOR("Mikko Ylinen"); +MODULE_AUTHOR("Felipe Balbi"); +MODULE_AUTHOR("Aaro Koskinen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.c b/drivers/i2c/busses/i2c-ccgx-ucsi.c new file mode 100644 index 000000000..d97233862 --- /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 +#include +#include +#include + +#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 000000000..739ac7a4b --- /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 000000000..25a74fa51 --- /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 + */ + +#include +#include +#include +#include +#include + +#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 "); +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 new file mode 100644 index 000000000..606ac071c --- /dev/null +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -0,0 +1,559 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Intel CHT Whiskey Cove PMIC I2C controller driver + * Copyright (C) 2017 Hans de Goede + * + * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: + * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHT_WC_I2C_CTRL 0x5e24 +#define CHT_WC_I2C_CTRL_WR BIT(0) +#define CHT_WC_I2C_CTRL_RD BIT(1) +#define CHT_WC_I2C_CLIENT_ADDR 0x5e25 +#define CHT_WC_I2C_REG_OFFSET 0x5e26 +#define CHT_WC_I2C_WRDATA 0x5e27 +#define CHT_WC_I2C_RDDATA 0x5e28 + +#define CHT_WC_EXTCHGRIRQ 0x6e0a +#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ BIT(0) +#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ BIT(1) +#define CHT_WC_EXTCHGRIRQ_READ_IRQ BIT(2) +#define CHT_WC_EXTCHGRIRQ_NACK_IRQ BIT(3) +#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK ((u8)GENMASK(3, 1)) +#define CHT_WC_EXTCHGRIRQ_MSK 0x6e17 + +struct cht_wc_i2c_adap { + struct i2c_adapter adapter; + wait_queue_head_t wait; + struct irq_chip irqchip; + struct mutex adap_lock; + struct mutex irqchip_lock; + struct regmap *regmap; + struct irq_domain *irq_domain; + struct i2c_client *client; + int client_irq; + u8 irq_mask; + u8 old_irq_mask; + int read_data; + bool io_error; + bool done; +}; + +static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data) +{ + struct cht_wc_i2c_adap *adap = data; + int ret, reg; + + mutex_lock(&adap->adap_lock); + + /* Read IRQs */ + ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, ®); + if (ret) { + dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n"); + mutex_unlock(&adap->adap_lock); + return IRQ_NONE; + } + + reg &= ~adap->irq_mask; + + /* Reads must be acked after reading the received data. */ + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data); + if (ret) + adap->io_error = true; + + /* + * Immediately ack IRQs, so that if new IRQs arrives while we're + * handling the previous ones our irq will re-trigger when we're done. + */ + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg); + if (ret) + dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n"); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) { + adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ); + adap->done = true; + } + + mutex_unlock(&adap->adap_lock); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) + wake_up(&adap->wait); + + /* + * Do NOT use handle_nested_irq here, the client irq handler will + * likely want to do i2c transfers and the i2c controller uses this + * 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_safe(adap->client_irq); + + return IRQ_HANDLED; +} + +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; +} + +static int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap); + int ret; + + mutex_lock(&adap->adap_lock); + adap->io_error = false; + adap->done = false; + mutex_unlock(&adap->adap_lock); + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr); + if (ret) + return ret; + + if (read_write == I2C_SMBUS_WRITE) { + ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte); + if (ret) + return ret; + } + + ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL, + (read_write == I2C_SMBUS_WRITE) ? + CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD); + if (ret) + return ret; + + ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30)); + if (ret == 0) { + /* + * The CHT GPIO controller serializes all IRQs, sometimes + * causing significant delays, check status manually. + */ + cht_wc_i2c_adap_thread_handler(0, adap); + if (!adap->done) + return -ETIMEDOUT; + } + + ret = 0; + mutex_lock(&adap->adap_lock); + if (adap->io_error) + ret = -EIO; + else if (read_write == I2C_SMBUS_READ) + data->byte = adap->read_data; + mutex_unlock(&adap->adap_lock); + + return ret; +} + +static const struct i2c_algorithm cht_wc_i2c_adap_algo = { + .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) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + mutex_lock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_sync_unlock(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + int ret; + + if (adap->irq_mask != adap->old_irq_mask) { + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, + adap->irq_mask); + if (ret == 0) + adap->old_irq_mask = adap->irq_mask; + else + dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n"); + } + + mutex_unlock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_enable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static void cht_wc_i2c_irq_disable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static const struct irq_chip cht_wc_i2c_irq_chip = { + .irq_bus_lock = cht_wc_i2c_irq_lock, + .irq_bus_sync_unlock = cht_wc_i2c_irq_sync_unlock, + .irq_disable = cht_wc_i2c_irq_disable, + .irq_enable = cht_wc_i2c_irq_enable, + .name = "cht_wc_ext_chrg_irq_chip", +}; + +/********** GPD Win / Pocket charger IC settings **********/ +static const char * const bq24190_suppliers[] = { + "tcpm-source-psy-i2c-fusb302" }; + +static const struct property_entry bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_suppliers), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +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 */ + .dev_name = "i2c-fusb302", +}; + +static const struct regulator_init_data bq24190_vbus_init_data = { + .constraints = { + /* The name is used in intel_cht_int33fe.c do not change. */ + .name = "cht_wc_usb_typec_vbus", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = &fusb302_consumer, + .num_consumer_supplies = 1, +}; + +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; + int ret, reg, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + init_waitqueue_head(&adap->wait); + mutex_init(&adap->adap_lock); + mutex_init(&adap->irqchip_lock); + adap->irqchip = cht_wc_i2c_irq_chip; + adap->regmap = pmic->regmap; + adap->adapter.owner = THIS_MODULE; + adap->adapter.class = I2C_CLASS_HWMON; + adap->adapter.algo = &cht_wc_i2c_adap_algo; + 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; + + /* Clear and activate i2c-adapter interrupts, disable client IRQ */ + adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK; + + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, ®); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask); + if (ret) + return ret; + + /* Alloc and register client IRQ */ + adap->irq_domain = irq_domain_create_linear(NULL, 1, &irq_domain_simple_ops, NULL); + if (!adap->irq_domain) + return -ENOMEM; + + adap->client_irq = irq_create_mapping(adap->irq_domain, 0); + if (!adap->client_irq) { + ret = -ENOMEM; + goto remove_irq_domain; + } + + irq_set_chip_data(adap->client_irq, adap); + irq_set_chip_and_handler(adap->client_irq, &adap->irqchip, + handle_simple_irq); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + cht_wc_i2c_adap_thread_handler, + IRQF_ONESHOT, "PMIC I2C Adapter", adap); + if (ret) + goto remove_irq_domain; + + i2c_set_adapdata(&adap->adapter, adap); + ret = i2c_add_adapter(&adap->adapter); + if (ret) + goto remove_irq_domain; + + 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; + } + } + + platform_set_drvdata(pdev, adap); + return 0; + +del_adapter: + i2c_del_adapter(&adap->adapter); +remove_irq_domain: + irq_domain_remove(adap->irq_domain); + return ret; +} + +static void cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev) +{ + struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev); + + i2c_unregister_device(adap->client); + i2c_del_adapter(&adap->adapter); + irq_domain_remove(adap->irq_domain); +} + +static const struct platform_device_id cht_wc_i2c_adap_id_table[] = { + { .name = "cht_wcove_ext_chgr" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table); + +static struct platform_driver cht_wc_i2c_adap_driver = { + .probe = cht_wc_i2c_adap_i2c_probe, + .remove = cht_wc_i2c_adap_i2c_remove, + .driver = { + .name = "cht_wcove_ext_chgr", + }, + .id_table = cht_wc_i2c_adap_id_table, +}; +module_platform_driver(cht_wc_i2c_adap_driver); + +MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C controller driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c new file mode 100644 index 000000000..951de6249 --- /dev/null +++ b/drivers/i2c/busses/i2c-cp2615.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c support for Silicon Labs' CP2615 Digital Audio Bridge + * + * (c) 2021, Bence Csókás + */ + +#include +#include +#include +#include +#include +#include + +/** 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_obj(*msg); + 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_obj(*msg); + 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_obj(*msg); + 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_disconnect(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; + + if (!usbdev->serial) + return -EINVAL; + + 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_disconnect, + .id_table = id_table, +}; + +module_usb_driver(cp2615_i2c_driver); + +MODULE_AUTHOR("Bence Csókás "); +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 new file mode 100644 index 000000000..2cb6a233d --- /dev/null +++ b/drivers/i2c/busses/i2c-cpm.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale CPM1/CPM2 I2C interface. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). + * + * moved into proper i2c interface; + * Brad Parker (brad@heeltoe.com) + * + * Parts from dbox2_i2c.c (cvs.tuxbox.org) + * (C) 2000-2001 Felix Domke (tmbinc@gmx.net), Gillem (htoa@gmx.net) + * + * (C) 2007 Montavista Software, Inc. + * Vitaly Bordug + * + * Converted to of_platform_device. Renamed to i2c-cpm.c. + * (C) 2007,2008 Jochen Friedrich + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Try to define this if you have an older CPU (earlier than rev D4) */ +/* However, better use a GPIO based bitbang driver in this case :/ */ +#undef I2C_CHIP_ERRATA + +#define CPM_MAX_READ 513 +#define CPM_MAXBD 4 + +#define I2C_EB (0x10) /* Big endian mode */ +#define I2C_EB_CPM2 (0x30) /* Big endian mode, memory snoop */ + +#define DPRAM_BASE ((u8 __iomem __force *)cpm_muram_addr(0)) + +/* I2C parameter RAM. */ +struct i2c_ram { + ushort rbase; /* Rx Buffer descriptor base address */ + ushort tbase; /* Tx Buffer descriptor base address */ + u_char rfcr; /* Rx function code */ + u_char tfcr; /* Tx function code */ + ushort mrblr; /* Max receive buffer length */ + uint rstate; /* Internal */ + uint rdp; /* Internal */ + ushort rbptr; /* Rx Buffer descriptor pointer */ + ushort rbc; /* Internal */ + uint rxtmp; /* Internal */ + uint tstate; /* Internal */ + uint tdp; /* Internal */ + ushort tbptr; /* Tx Buffer descriptor pointer */ + ushort tbc; /* Internal */ + uint txtmp; /* Internal */ + 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 +#define I2COM_MASTER 0x01 +#define I2CER_TXE 0x10 +#define I2CER_BUSY 0x04 +#define I2CER_TXB 0x02 +#define I2CER_RXB 0x01 +#define I2MOD_EN 0x01 + +/* I2C Registers */ +struct i2c_reg { + u8 i2mod; + u8 res1[3]; + u8 i2add; + u8 res2[3]; + u8 i2brg; + u8 res3[3]; + u8 i2com; + u8 res4[3]; + u8 i2cer; + u8 res5[3]; + u8 i2cmr; +}; + +struct cpm_i2c { + char *base; + struct platform_device *ofdev; + struct i2c_adapter adap; + uint dp_addr; + int version; /* CPM1=1, CPM2=2 */ + int irq; + int cp_command; + int freq; + struct i2c_reg __iomem *i2c_reg; + struct i2c_ram __iomem *i2c_ram; + u16 i2c_addr; + wait_queue_head_t i2c_wait; + cbd_t __iomem *tbase; + cbd_t __iomem *rbase; + u_char *txbuf[CPM_MAXBD]; + u_char *rxbuf[CPM_MAXBD]; + dma_addr_t txdma[CPM_MAXBD]; + dma_addr_t rxdma[CPM_MAXBD]; +}; + +static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id) +{ + struct cpm_i2c *cpm; + struct i2c_reg __iomem *i2c_reg; + struct i2c_adapter *adap = dev_id; + int i; + + cpm = i2c_get_adapdata(dev_id); + i2c_reg = cpm->i2c_reg; + + /* Clear interrupt. */ + i = in_8(&i2c_reg->i2cer); + out_8(&i2c_reg->i2cer, i); + + dev_dbg(&adap->dev, "Interrupt: %x\n", i); + + wake_up(&cpm->i2c_wait); + + return i ? IRQ_HANDLED : IRQ_NONE; +} + +static void cpm_reset_i2c_params(struct cpm_i2c *cpm) +{ + struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; + + /* Set up the I2C parameters in the parameter ram. */ + out_be16(&i2c_ram->tbase, (u8 __iomem *)cpm->tbase - DPRAM_BASE); + out_be16(&i2c_ram->rbase, (u8 __iomem *)cpm->rbase - DPRAM_BASE); + + if (cpm->version == 1) { + out_8(&i2c_ram->tfcr, I2C_EB); + out_8(&i2c_ram->rfcr, I2C_EB); + } else { + out_8(&i2c_ram->tfcr, I2C_EB_CPM2); + out_8(&i2c_ram->rfcr, I2C_EB_CPM2); + } + + out_be16(&i2c_ram->mrblr, CPM_MAX_READ); + + out_be32(&i2c_ram->rstate, 0); + out_be32(&i2c_ram->rdp, 0); + out_be16(&i2c_ram->rbptr, 0); + out_be16(&i2c_ram->rbc, 0); + out_be32(&i2c_ram->rxtmp, 0); + out_be32(&i2c_ram->tstate, 0); + out_be32(&i2c_ram->tdp, 0); + out_be16(&i2c_ram->tbptr, 0); + out_be16(&i2c_ram->tbc, 0); + out_be32(&i2c_ram->txtmp, 0); +} + +static void cpm_i2c_force_close(struct i2c_adapter *adap) +{ + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; + + dev_dbg(&adap->dev, "cpm_i2c_force_close()\n"); + + cpm_command(cpm->cp_command, CPM_CR_CLOSE_RX_BD); + + out_8(&i2c_reg->i2cmr, 0x00); /* Disable all interrupts */ + out_8(&i2c_reg->i2cer, 0xff); +} + +static void cpm_i2c_parse_message(struct i2c_adapter *adap, + struct i2c_msg *pmsg, int num, int tx, int rx) +{ + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + u_char addr; + u_char *tb; + u_char *rb; + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + addr = i2c_8bit_addr_from_msg(pmsg); + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + + /* Align read buffer */ + rb = (u_char *) (((ulong) rb + 1) & ~1); + + tb[0] = addr; /* Device address byte w/rw flag */ + + out_be16(&tbdf->cbd_datlen, pmsg->len + 1); + out_be16(&tbdf->cbd_sc, 0); + + if (!(pmsg->flags & I2C_M_NOSTART)) + setbits16(&tbdf->cbd_sc, BD_I2C_START); + + if (tx + 1 == num) + setbits16(&tbdf->cbd_sc, BD_SC_LAST | BD_SC_WRAP); + + if (pmsg->flags & I2C_M_RD) { + /* + * To read, we need an empty buffer of the proper length. + * All that is used is the first byte for address, the remainder + * is just used for timing (and doesn't really have to exist). + */ + + dev_dbg(&adap->dev, "cpm_i2c_read(abyte=0x%x)\n", addr); + + out_be16(&rbdf->cbd_datlen, 0); + out_be16(&rbdf->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT); + + if (rx + 1 == CPM_MAXBD) + setbits16(&rbdf->cbd_sc, BD_SC_WRAP); + + eieio(); + setbits16(&tbdf->cbd_sc, BD_SC_READY); + } else { + dev_dbg(&adap->dev, "cpm_i2c_write(abyte=0x%x)\n", addr); + + memcpy(tb+1, pmsg->buf, pmsg->len); + + eieio(); + setbits16(&tbdf->cbd_sc, BD_SC_READY | BD_SC_INTRPT); + } +} + +static int cpm_i2c_check_message(struct i2c_adapter *adap, + struct i2c_msg *pmsg, int tx, int rx) +{ + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + u_char *tb; + u_char *rb; + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + + /* Align read buffer */ + rb = (u_char *) (((uint) rb + 1) & ~1); + + eieio(); + if (pmsg->flags & I2C_M_RD) { + dev_dbg(&adap->dev, "tx sc 0x%04x, rx sc 0x%04x\n", + in_be16(&tbdf->cbd_sc), in_be16(&rbdf->cbd_sc)); + + if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) { + dev_dbg(&adap->dev, "I2C read; No ack\n"); + return -ENXIO; + } + if (in_be16(&rbdf->cbd_sc) & BD_SC_EMPTY) { + dev_err(&adap->dev, + "I2C read; complete but rbuf empty\n"); + return -EREMOTEIO; + } + if (in_be16(&rbdf->cbd_sc) & BD_SC_OV) { + dev_err(&adap->dev, "I2C read; Overrun\n"); + return -EREMOTEIO; + } + memcpy(pmsg->buf, rb, pmsg->len); + } else { + dev_dbg(&adap->dev, "tx sc %d 0x%04x\n", tx, + in_be16(&tbdf->cbd_sc)); + + if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) { + dev_dbg(&adap->dev, "I2C write; No ack\n"); + return -ENXIO; + } + if (in_be16(&tbdf->cbd_sc) & BD_SC_UN) { + dev_err(&adap->dev, "I2C write; Underrun\n"); + return -EIO; + } + if (in_be16(&tbdf->cbd_sc) & BD_SC_CL) { + dev_err(&adap->dev, "I2C write; Collision\n"); + return -EIO; + } + } + return 0; +} + +static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; + struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; + struct i2c_msg *pmsg; + int ret; + int tptr; + int rptr; + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + + /* Reset to use first buffer */ + out_be16(&i2c_ram->rbptr, in_be16(&i2c_ram->rbase)); + out_be16(&i2c_ram->tbptr, in_be16(&i2c_ram->tbase)); + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + + tptr = 0; + rptr = 0; + + /* + * If there was a collision in the last i2c transaction, + * Set I2COM_MASTER as it was cleared during collision. + */ + if (in_be16(&tbdf->cbd_sc) & BD_SC_CL) { + out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); + } + + while (tptr < num) { + pmsg = &msgs[tptr]; + dev_dbg(&adap->dev, "R: %d T: %d\n", rptr, tptr); + + cpm_i2c_parse_message(adap, pmsg, num, tptr, rptr); + if (pmsg->flags & I2C_M_RD) + rptr++; + tptr++; + } + /* Start transfer now */ + /* Enable RX/TX/Error interupts */ + out_8(&i2c_reg->i2cmr, I2CER_TXE | I2CER_TXB | I2CER_RXB); + out_8(&i2c_reg->i2cer, 0xff); /* Clear interrupt status */ + /* Chip bug, set enable here */ + setbits8(&i2c_reg->i2mod, I2MOD_EN); /* Enable */ + /* Begin transmission */ + setbits8(&i2c_reg->i2com, I2COM_START); + + tptr = 0; + rptr = 0; + + while (tptr < num) { + /* Check for outstanding messages */ + dev_dbg(&adap->dev, "test ready.\n"); + pmsg = &msgs[tptr]; + if (pmsg->flags & I2C_M_RD) + ret = wait_event_timeout(cpm->i2c_wait, + (in_be16(&tbdf[tptr].cbd_sc) & BD_SC_NAK) || + !(in_be16(&rbdf[rptr].cbd_sc) & BD_SC_EMPTY), + 1 * HZ); + else + ret = wait_event_timeout(cpm->i2c_wait, + !(in_be16(&tbdf[tptr].cbd_sc) & BD_SC_READY), + 1 * HZ); + if (ret == 0) { + ret = -EREMOTEIO; + dev_err(&adap->dev, "I2C transfer: timeout\n"); + goto out_err; + } + if (ret > 0) { + dev_dbg(&adap->dev, "ready.\n"); + ret = cpm_i2c_check_message(adap, pmsg, tptr, rptr); + tptr++; + if (pmsg->flags & I2C_M_RD) + rptr++; + if (ret) + goto out_err; + } + } +#ifdef I2C_CHIP_ERRATA + /* + * Chip errata, clear enable. This is not needed on rev D4 CPUs. + * Disabling I2C too early may cause too short stop condition + */ + udelay(4); + clrbits8(&i2c_reg->i2mod, I2MOD_EN); +#endif + return (num); + +out_err: + cpm_i2c_force_close(adap); +#ifdef I2C_CHIP_ERRATA + /* + * Chip errata, clear enable. This is not needed on rev D4 CPUs. + */ + clrbits8(&i2c_reg->i2mod, I2MOD_EN); +#endif + return ret; +} + +static u32 cpm_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm cpm_i2c_algo = { + .xfer = cpm_i2c_xfer, + .functionality = cpm_i2c_func, +}; + +/* CPM_MAX_READ is also limiting writes according to the code! */ +static const struct i2c_adapter_quirks cpm_i2c_quirks = { + .max_num_msgs = CPM_MAXBD, + .max_read_len = CPM_MAX_READ, + .max_write_len = CPM_MAX_READ, +}; + +static const struct i2c_adapter cpm_ops = { + .owner = THIS_MODULE, + .name = "i2c-cpm", + .algo = &cpm_i2c_algo, + .quirks = &cpm_i2c_quirks, +}; + +static int cpm_i2c_setup(struct cpm_i2c *cpm) +{ + struct platform_device *ofdev = cpm->ofdev; + const u32 *data; + int len, ret, i; + void __iomem *i2c_base; + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + unsigned char brg; + + dev_dbg(&cpm->ofdev->dev, "cpm_i2c_setup()\n"); + + init_waitqueue_head(&cpm->i2c_wait); + + cpm->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + if (!cpm->irq) + return -EINVAL; + + /* Install interrupt handler. */ + ret = request_irq(cpm->irq, cpm_i2c_interrupt, 0, "cpm_i2c", + &cpm->adap); + if (ret) + return ret; + + /* I2C parameter RAM */ + i2c_base = of_iomap(ofdev->dev.of_node, 1); + if (i2c_base == NULL) { + ret = -EINVAL; + goto out_irq; + } + + if (of_device_is_compatible(ofdev->dev.of_node, "fsl,cpm1-i2c")) { + + /* Check for and use a microcode relocation patch. */ + cpm->i2c_ram = i2c_base; + cpm->i2c_addr = in_be16(&cpm->i2c_ram->rpbase); + + /* + * Maybe should use cpm_muram_alloc instead of hardcoding + * this in micropatch.c + */ + if (cpm->i2c_addr) { + cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr); + iounmap(i2c_base); + } + + cpm->version = 1; + + } else if (of_device_is_compatible(ofdev->dev.of_node, "fsl,cpm2-i2c")) { + cpm->i2c_addr = cpm_muram_alloc(sizeof(struct i2c_ram), 64); + cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr); + out_be16(i2c_base, cpm->i2c_addr); + iounmap(i2c_base); + + cpm->version = 2; + + } else { + iounmap(i2c_base); + ret = -EINVAL; + goto out_irq; + } + + /* I2C control/status registers */ + cpm->i2c_reg = of_iomap(ofdev->dev.of_node, 0); + if (cpm->i2c_reg == NULL) { + ret = -EINVAL; + goto out_ram; + } + + data = of_get_property(ofdev->dev.of_node, "fsl,cpm-command", &len); + if (!data || len != 4) { + ret = -EINVAL; + goto out_reg; + } + cpm->cp_command = *data; + + data = of_get_property(ofdev->dev.of_node, "linux,i2c-class", &len); + if (data && len == 4) + cpm->adap.class = *data; + + data = of_get_property(ofdev->dev.of_node, "clock-frequency", &len); + if (data && len == 4) + cpm->freq = *data; + else + cpm->freq = 60000; /* use 60kHz i2c clock by default */ + + /* + * Allocate space for CPM_MAXBD transmit and receive buffer + * descriptors in the DP ram. + */ + cpm->dp_addr = cpm_muram_alloc(sizeof(cbd_t) * 2 * CPM_MAXBD, 8); + if (!cpm->dp_addr) { + ret = -ENOMEM; + goto out_reg; + } + + cpm->tbase = cpm_muram_addr(cpm->dp_addr); + cpm->rbase = cpm_muram_addr(cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD); + + /* Allocate TX and RX buffers */ + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + + for (i = 0; i < CPM_MAXBD; i++) { + cpm->rxbuf[i] = dma_alloc_coherent(&cpm->ofdev->dev, + CPM_MAX_READ + 1, + &cpm->rxdma[i], GFP_KERNEL); + if (!cpm->rxbuf[i]) { + ret = -ENOMEM; + goto out_muram; + } + out_be32(&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] + 1) & ~1)); + + 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; + } + out_be32(&tbdf[i].cbd_bufaddr, cpm->txdma[i]); + } + + /* Initialize Tx/Rx parameters. */ + + cpm_reset_i2c_params(cpm); + + dev_dbg(&cpm->ofdev->dev, "i2c_ram 0x%p, i2c_addr 0x%04x, freq %d\n", + cpm->i2c_ram, cpm->i2c_addr, cpm->freq); + dev_dbg(&cpm->ofdev->dev, "tbase 0x%04x, rbase 0x%04x\n", + (u8 __iomem *)cpm->tbase - DPRAM_BASE, + (u8 __iomem *)cpm->rbase - DPRAM_BASE); + + cpm_command(cpm->cp_command, CPM_CR_INIT_TRX); + + /* + * Select an invalid address. Just make sure we don't use loopback mode + */ + out_8(&cpm->i2c_reg->i2add, 0x7f << 1); + + /* + * PDIV is set to 00 in i2mod, so brgclk/32 is used as input to the + * i2c baud rate generator. This is divided by 2 x (DIV + 3) to get + * the actual i2c bus frequency. + */ + brg = get_brgfreq() / (32 * 2 * cpm->freq) - 3; + out_8(&cpm->i2c_reg->i2brg, brg); + + out_8(&cpm->i2c_reg->i2mod, 0x00); + out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); + + /* Disable interrupts. */ + out_8(&cpm->i2c_reg->i2cmr, 0); + out_8(&cpm->i2c_reg->i2cer, 0xff); + + return 0; + +out_muram: + for (i = 0; i < CPM_MAXBD; i++) { + if (cpm->rxbuf[i]) + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->rxbuf[i], cpm->rxdma[i]); + if (cpm->txbuf[i]) + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->txbuf[i], cpm->txdma[i]); + } + cpm_muram_free(cpm->dp_addr); +out_reg: + iounmap(cpm->i2c_reg); +out_ram: + if ((cpm->version == 1) && (!cpm->i2c_addr)) + iounmap(cpm->i2c_ram); + if (cpm->version == 2) + cpm_muram_free(cpm->i2c_addr); +out_irq: + free_irq(cpm->irq, &cpm->adap); + return ret; +} + +static void cpm_i2c_shutdown(struct cpm_i2c *cpm) +{ + int i; + + /* Shut down I2C. */ + clrbits8(&cpm->i2c_reg->i2mod, I2MOD_EN); + + /* Disable interrupts */ + out_8(&cpm->i2c_reg->i2cmr, 0); + out_8(&cpm->i2c_reg->i2cer, 0xff); + + free_irq(cpm->irq, &cpm->adap); + + /* Free all memory */ + for (i = 0; i < CPM_MAXBD; i++) { + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->rxbuf[i], cpm->rxdma[i]); + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->txbuf[i], cpm->txdma[i]); + } + + cpm_muram_free(cpm->dp_addr); + iounmap(cpm->i2c_reg); + + if ((cpm->version == 1) && (!cpm->i2c_addr)) + iounmap(cpm->i2c_ram); + if (cpm->version == 2) + cpm_muram_free(cpm->i2c_addr); +} + +static int cpm_i2c_probe(struct platform_device *ofdev) +{ + int result, len; + struct cpm_i2c *cpm; + const u32 *data; + + cpm = kzalloc_obj(struct cpm_i2c); + if (!cpm) + return -ENOMEM; + + cpm->ofdev = ofdev; + + platform_set_drvdata(ofdev, cpm); + + cpm->adap = cpm_ops; + i2c_set_adapdata(&cpm->adap, cpm); + cpm->adap.dev.parent = &ofdev->dev; + cpm->adap.dev.of_node = of_node_get(ofdev->dev.of_node); + + result = cpm_i2c_setup(cpm); + if (result) { + dev_err(&ofdev->dev, "Unable to init hardware\n"); + goto out_free; + } + + /* register new adapter to i2c module... */ + + data = of_get_property(ofdev->dev.of_node, "linux,i2c-index", &len); + cpm->adap.nr = (data && len == 4) ? *data : -1; + result = i2c_add_numbered_adapter(&cpm->adap); + + if (result < 0) + goto out_shut; + + dev_dbg(&ofdev->dev, "hw routines for %s registered.\n", + cpm->adap.name); + + return 0; +out_shut: + cpm_i2c_shutdown(cpm); +out_free: + kfree(cpm); + + return result; +} + +static void cpm_i2c_remove(struct platform_device *ofdev) +{ + struct cpm_i2c *cpm = platform_get_drvdata(ofdev); + + i2c_del_adapter(&cpm->adap); + + cpm_i2c_shutdown(cpm); + + kfree(cpm); +} + +static const struct of_device_id cpm_i2c_match[] = { + { + .compatible = "fsl,cpm1-i2c", + }, + { + .compatible = "fsl,cpm2-i2c", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, cpm_i2c_match); + +static struct platform_driver cpm_i2c_driver = { + .probe = cpm_i2c_probe, + .remove = cpm_i2c_remove, + .driver = { + .name = "fsl-i2c-cpm", + .of_match_table = cpm_i2c_match, + }, +}; + +module_platform_driver(cpm_i2c_driver); + +MODULE_AUTHOR("Jochen Friedrich "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for CPM boards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c new file mode 100644 index 000000000..208ce4f9e --- /dev/null +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Expose an I2C passthrough to the ChromeOS EC. +// +// Copyright (C) 2013 Google, Inc. + +#include +#include +#include +#include +#include +#include +#include + +#define I2C_MAX_RETRIES 3 + +/** + * struct ec_i2c_device - Driver data for I2C tunnel + * + * @dev: Device node + * @adap: I2C adapter + * @ec: Pointer to EC device + * @remote_bus: The EC bus number we tunnel to on the other side. + * @request_buf: Buffer for transmitting data; we expect most transfers to fit. + * @response_buf: Buffer for receiving data; we expect most transfers to fit. + */ + +struct ec_i2c_device { + struct device *dev; + struct i2c_adapter adap; + struct cros_ec_device *ec; + + u16 remote_bus; + + u8 request_buf[256]; + u8 response_buf[256]; +}; + +/** + * ec_i2c_count_message - Count bytes needed for ec_i2c_construct_message + * + * @i2c_msgs: The i2c messages to read + * @num: The number of i2c messages. + * + * Returns the number of bytes the messages will take up. + */ +static int ec_i2c_count_message(const struct i2c_msg i2c_msgs[], int num) +{ + int i; + int size; + + size = sizeof(struct ec_params_i2c_passthru); + size += num * sizeof(struct ec_params_i2c_passthru_msg); + for (i = 0; i < num; i++) + if (!(i2c_msgs[i].flags & I2C_M_RD)) + size += i2c_msgs[i].len; + + return size; +} + +/** + * ec_i2c_construct_message - construct a message to go to the EC + * + * This function effectively stuffs the standard i2c_msg format of Linux into + * a format that the EC understands. + * + * @buf: The buffer to fill. We assume that the buffer is big enough. + * @i2c_msgs: The i2c messages to read. + * @num: The number of i2c messages. + * @bus_num: The remote bus number we want to talk to. + * + * Returns 0 or a negative error number. + */ +static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[], + int num, u16 bus_num) +{ + struct ec_params_i2c_passthru *params; + u8 *out_data; + int i; + + out_data = buf + sizeof(struct ec_params_i2c_passthru) + + num * sizeof(struct ec_params_i2c_passthru_msg); + + params = (struct ec_params_i2c_passthru *)buf; + params->port = bus_num; + params->num_msgs = num; + for (i = 0; i < num; i++) { + const struct i2c_msg *i2c_msg = &i2c_msgs[i]; + struct ec_params_i2c_passthru_msg *msg = ¶ms->msg[i]; + + msg->len = i2c_msg->len; + msg->addr_flags = i2c_msg->addr; + + if (i2c_msg->flags & I2C_M_TEN) + return -EINVAL; + + if (i2c_msg->flags & I2C_M_RD) { + msg->addr_flags |= EC_I2C_FLAG_READ; + } else { + memcpy(out_data, i2c_msg->buf, msg->len); + out_data += msg->len; + } + } + + return 0; +} + +/** + * ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response + * + * @i2c_msgs: The i2c messages to fill up. + * @num: The number of i2c messages expected. + * + * Returns the number of response bytes expeced. + */ +static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num) +{ + int size; + int i; + + size = sizeof(struct ec_response_i2c_passthru); + for (i = 0; i < num; i++) + if (i2c_msgs[i].flags & I2C_M_RD) + size += i2c_msgs[i].len; + + return size; +} + +/** + * ec_i2c_parse_response - Parse a response from the EC + * + * We'll take the EC's response and copy it back into msgs. + * + * @buf: The buffer to parse. + * @i2c_msgs: The i2c messages to fill up. + * @num: The number of i2c messages; will be modified to include the actual + * number received. + * + * Returns 0 or a negative error number. + */ +static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[], + int *num) +{ + const struct ec_response_i2c_passthru *resp; + const u8 *in_data; + int i; + + in_data = buf + sizeof(struct ec_response_i2c_passthru); + + resp = (const struct ec_response_i2c_passthru *)buf; + if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT) + return -ETIMEDOUT; + else if (resp->i2c_status & EC_I2C_STATUS_NAK) + return -ENXIO; + else if (resp->i2c_status & EC_I2C_STATUS_ERROR) + return -EIO; + + /* Other side could send us back fewer messages, but not more */ + if (resp->num_msgs > *num) + return -EPROTO; + *num = resp->num_msgs; + + for (i = 0; i < *num; i++) { + struct i2c_msg *i2c_msg = &i2c_msgs[i]; + + if (i2c_msgs[i].flags & I2C_M_RD) { + memcpy(i2c_msg->buf, in_data, i2c_msg->len); + in_data += i2c_msg->len; + } + } + + return 0; +} + +static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], + int num) +{ + struct ec_i2c_device *bus = adap->algo_data; + struct device *dev = bus->dev; + const u16 bus_num = bus->remote_bus; + int request_len; + int response_len; + int alloc_size; + int result; + struct cros_ec_command *msg; + + request_len = ec_i2c_count_message(i2c_msgs, num); + if (request_len < 0) { + dev_warn(dev, "Error constructing message %d\n", request_len); + return request_len; + } + + response_len = ec_i2c_count_response(i2c_msgs, num); + if (response_len < 0) { + /* Unexpected; no errors should come when NULL response */ + dev_warn(dev, "Error preparing response %d\n", response_len); + return response_len; + } + + alloc_size = max(request_len, response_len); + msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num); + if (result) { + dev_err(dev, "Error constructing EC i2c message %d\n", result); + goto exit; + } + + msg->version = 0; + msg->command = EC_CMD_I2C_PASSTHRU; + msg->outsize = request_len; + msg->insize = response_len; + + result = cros_ec_cmd_xfer_status(bus->ec, msg); + if (result < 0) { + dev_err(dev, "Error transferring EC i2c message %d\n", result); + goto exit; + } + + result = ec_i2c_parse_response(msg->data, i2c_msgs, &num); + if (result < 0) + goto exit; + + /* Indicate success by saying how many messages were sent */ + result = num; +exit: + kfree(msg); + return result; +} + +static u32 ec_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm ec_i2c_algorithm = { + .xfer = ec_i2c_xfer, + .functionality = ec_i2c_functionality, +}; + +static int ec_i2c_probe(struct platform_device *pdev) +{ + 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; + } + + bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + + 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; + } + bus->remote_bus = remote_bus; + + bus->ec = ec; + bus->dev = dev; + + bus->adap.owner = THIS_MODULE; + 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 = 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) + return err; + platform_set_drvdata(pdev, bus); + + return err; +} + +static void ec_i2c_remove(struct platform_device *dev) +{ + struct ec_i2c_device *bus = platform_get_drvdata(dev); + + i2c_del_adapter(&bus->adap); +} + +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); + +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), + }, +}; + +module_platform_driver(ec_i2c_tunnel_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EC I2C tunnel driver"); +MODULE_ALIAS("platform:cros-ec-i2c-tunnel"); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c new file mode 100644 index 000000000..e15eef163 --- /dev/null +++ b/drivers/i2c/busses/i2c-davinci.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * + * Updated by Vinod & Sudhakar Feb 2005 + * + * ---------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ----- global defines ----------------------------------------------- */ + +#define DAVINCI_I2C_TIMEOUT (1*HZ) +#define DAVINCI_I2C_MAX_TRIES 2 +#define DAVINCI_I2C_OWN_ADDRESS 0x08 +#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_SCD | \ + DAVINCI_I2C_IMR_ARDY | \ + DAVINCI_I2C_IMR_NACK | \ + DAVINCI_I2C_IMR_AL) + +#define DAVINCI_I2C_OAR_REG 0x00 +#define DAVINCI_I2C_IMR_REG 0x04 +#define DAVINCI_I2C_STR_REG 0x08 +#define DAVINCI_I2C_CLKL_REG 0x0c +#define DAVINCI_I2C_CLKH_REG 0x10 +#define DAVINCI_I2C_CNT_REG 0x14 +#define DAVINCI_I2C_DRR_REG 0x18 +#define DAVINCI_I2C_SAR_REG 0x1c +#define DAVINCI_I2C_DXR_REG 0x20 +#define DAVINCI_I2C_MDR_REG 0x24 +#define DAVINCI_I2C_IVR_REG 0x28 +#define DAVINCI_I2C_EMDR_REG 0x2c +#define DAVINCI_I2C_PSC_REG 0x30 +#define DAVINCI_I2C_FUNC_REG 0x48 +#define DAVINCI_I2C_DIR_REG 0x4c +#define DAVINCI_I2C_DIN_REG 0x50 +#define DAVINCI_I2C_DOUT_REG 0x54 +#define DAVINCI_I2C_DSET_REG 0x58 +#define DAVINCI_I2C_DCLR_REG 0x5c + +#define DAVINCI_I2C_IVR_AAS 0x07 +#define DAVINCI_I2C_IVR_SCD 0x06 +#define DAVINCI_I2C_IVR_XRDY 0x05 +#define DAVINCI_I2C_IVR_RDR 0x04 +#define DAVINCI_I2C_IVR_ARDY 0x03 +#define DAVINCI_I2C_IVR_NACK 0x02 +#define DAVINCI_I2C_IVR_AL 0x01 + +#define DAVINCI_I2C_STR_BB BIT(12) +#define DAVINCI_I2C_STR_RSFULL BIT(11) +#define DAVINCI_I2C_STR_SCD BIT(5) +#define DAVINCI_I2C_STR_ARDY BIT(2) +#define DAVINCI_I2C_STR_NACK BIT(1) +#define DAVINCI_I2C_STR_AL BIT(0) + +#define DAVINCI_I2C_MDR_NACK BIT(15) +#define DAVINCI_I2C_MDR_STT BIT(13) +#define DAVINCI_I2C_MDR_STP BIT(11) +#define DAVINCI_I2C_MDR_MST BIT(10) +#define DAVINCI_I2C_MDR_TRX BIT(9) +#define DAVINCI_I2C_MDR_XA BIT(8) +#define DAVINCI_I2C_MDR_RM BIT(7) +#define DAVINCI_I2C_MDR_IRS BIT(5) + +#define DAVINCI_I2C_IMR_AAS BIT(6) +#define DAVINCI_I2C_IMR_SCD BIT(5) +#define DAVINCI_I2C_IMR_XRDY BIT(4) +#define DAVINCI_I2C_IMR_RRDY BIT(3) +#define DAVINCI_I2C_IMR_ARDY BIT(2) +#define DAVINCI_I2C_IMR_NACK BIT(1) +#define DAVINCI_I2C_IMR_AL BIT(0) + +/* set SDA and SCL as GPIO */ +#define DAVINCI_I2C_FUNC_PFUNC0 BIT(0) + +/* set SCL as output when used as GPIO*/ +#define DAVINCI_I2C_DIR_PDIR0 BIT(0) +/* set SDA as output when used as GPIO*/ +#define DAVINCI_I2C_DIR_PDIR1 BIT(1) + +/* read SCL GPIO level */ +#define DAVINCI_I2C_DIN_PDIN0 BIT(0) +/* read SDA GPIO level */ +#define DAVINCI_I2C_DIN_PDIN1 BIT(1) + +/*set the SCL GPIO high */ +#define DAVINCI_I2C_DSET_PDSET0 BIT(0) +/*set the SDA GPIO high */ +#define DAVINCI_I2C_DSET_PDSET1 BIT(1) + +/* set the SCL GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR0 BIT(0) +/* set the SDA GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR1 BIT(1) + +/* timeout for pm runtime autosuspend */ +#define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */ + +struct davinci_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct clk *clk; + int cmd_err; + u8 *buf; + size_t buf_len; + int irq; + int stop; + u8 terminate; + struct i2c_adapter adapter; +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif + /* 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, + int reg, u16 val) +{ + writew_relaxed(val, i2c_dev->base + reg); +} + +static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) +{ + return readw_relaxed(i2c_dev->base + reg); +} + +static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, + int val) +{ + u16 w; + + w = davinci_i2c_read_reg(i2c_dev, DAVINCI_I2C_MDR_REG); + if (!val) /* put I2C into reset */ + w &= ~DAVINCI_I2C_MDR_IRS; + else /* take I2C out of reset */ + w |= DAVINCI_I2C_MDR_IRS; + + davinci_i2c_write_reg(i2c_dev, DAVINCI_I2C_MDR_REG, w); +} + +static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) +{ + u16 psc; + u32 clk; + u32 d; + u32 clkh; + u32 clkl; + u32 input_clock = clk_get_rate(dev->clk); + + /* NOTE: I2C Clock divider programming info + * As per I2C specs the following formulas provide prescaler + * and low/high divider values + * input clk --> PSC Div -----------> ICCL/H Div --> output clock + * module clk + * + * output clk = module clk / (PSC + 1) [ (ICCL + d) + (ICCH + d) ] + * + * Thus, + * (ICCL + ICCH) = clk = (input clk / ((psc +1) * output clk)) - 2d; + * + * where if PSC == 0, d = 7, + * if PSC == 1, d = 6 + * if PSC > 1 , d = 5 + * + * Note: + * d is always 6 on Keystone I2C controller + */ + + /* + * Both Davinci and current Keystone User Guides recommend a value + * between 7MHz and 12MHz. In reality 7MHz module clock doesn't + * always produce enough margin between SDA and SCL transitions. + * Measurements show that the higher the module clock is, the + * bigger is the margin, providing more reliable communication. + * So we better target for 12MHz. + */ + psc = (input_clock / 12000000) - 1; + if ((input_clock / (psc + 1)) > 12000000) + psc++; /* better to run under spec than over */ + d = (psc >= 2) ? 5 : 7 - psc; + + if (device_is_compatible(dev->dev, "ti,keystone-i2c")) + d = 6; + + 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 > 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 (dev->bus_freq > 100) + clkl = (clk << 1) / 3; + else + clkl = (clk >> 1); + /* + * It's not always possible to have 1 to 2 ratio when d=7, so fall back + * to minimal possible clkh in this case. + * + * Note: + * CLKH is not allowed to be 0, in this case I2C clock is not generated + * at all + */ + if (clk > clkl + d) { + clkh = clk - clkl - d; + clkl -= d; + } else { + clkh = 1; + clkl = clk - (d << 1); + } + + davinci_i2c_write_reg(dev, DAVINCI_I2C_PSC_REG, psc); + davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh); + davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl); + + dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk); +} + +/* + * This function configures I2C and brings I2C out of reset. + * This function is called during I2C init function. This function + * also gets called if I2C encounters any errors. + */ +static int i2c_davinci_init(struct davinci_i2c_dev *dev) +{ + /* put I2C into reset */ + davinci_i2c_reset_ctrl(dev, 0); + + /* compute clock dividers */ + i2c_davinci_calc_clk_dividers(dev); + + /* 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); + + dev_dbg(dev->dev, "PSC = %d\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); + dev_dbg(dev->dev, "CLKL = %d\n", + 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\n", dev->bus_freq); + + + /* Take the I2C module out of reset: */ + davinci_i2c_reset_ctrl(dev, 1); + + /* Enable interrupts */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, I2C_DAVINCI_INTR_ALL); + + return 0; +} + +/* + * This routine does i2c bus recovery by using i2c_generic_scl_recovery + * which is provided by I2C Bus recovery infrastructure. + */ +static void davinci_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + /* Disable interrupts */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, 0); + + /* put I2C into reset */ + davinci_i2c_reset_ctrl(dev, 0); +} + +static void davinci_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + i2c_davinci_init(dev); +} + +static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + if (val) + davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG, + DAVINCI_I2C_DSET_PDSET0); + else + davinci_i2c_write_reg(dev, DAVINCI_I2C_DCLR_REG, + DAVINCI_I2C_DCLR_PDCLR0); +} + +static int davinci_i2c_get_scl(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int val; + + /* read the state of SCL */ + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + return val & DAVINCI_I2C_DIN_PDIN0; +} + +static int davinci_i2c_get_sda(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int val; + + /* read the state of SDA */ + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + return val & DAVINCI_I2C_DIN_PDIN1; +} + +static void davinci_i2c_scl_prepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + davinci_i2c_prepare_recovery(adap); + + /* SCL output, SDA input */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, DAVINCI_I2C_DIR_PDIR0); + + /* change to GPIO mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, + DAVINCI_I2C_FUNC_PFUNC0); +} + +static void davinci_i2c_scl_unprepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + /* change back to I2C mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, 0); + + davinci_i2c_unprepare_recovery(adap); +} + +static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .set_scl = davinci_i2c_set_scl, + .get_scl = davinci_i2c_get_scl, + .get_sda = davinci_i2c_get_sda, + .prepare_recovery = davinci_i2c_scl_prepare_recovery, + .unprepare_recovery = davinci_i2c_scl_unprepare_recovery, +}; + +/* + * Waiting for bus not busy + */ +static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev) +{ + unsigned long timeout = jiffies + dev->adapter.timeout; + + do { + if (!(davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB)) + return 0; + schedule_timeout_uninterruptible(1); + } while (time_before_eq(jiffies, timeout)); + + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + i2c_recover_bus(&dev->adapter); + + /* + * if bus is still "busy" here, it's most probably a HW problem like + * short-circuit + */ + if (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB) + return -EIO; + + return 0; +} + +/* + * 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); + u32 flag; + u16 w; + unsigned long time_left; + + if (msg->addr == DAVINCI_I2C_OWN_ADDRESS) { + dev_warn(dev->dev, "transfer to own address aborted\n"); + return -EADDRNOTAVAIL; + } + + /* set the target address */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_SAR_REG, msg->addr); + + dev->buf = msg->buf; + dev->buf_len = msg->len; + dev->stop = stop; + + davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len); + + reinit_completion(&dev->cmd_complete); + dev->cmd_err = 0; + + /* Take I2C out of reset and configure it as controller */ + flag = DAVINCI_I2C_MDR_IRS | DAVINCI_I2C_MDR_MST; + + if (msg->flags & I2C_M_TEN) + flag |= DAVINCI_I2C_MDR_XA; + if (!(msg->flags & I2C_M_RD)) + flag |= DAVINCI_I2C_MDR_TRX; + if (msg->len == 0) + flag |= DAVINCI_I2C_MDR_RM; + + /* Enable receive or transmit interrupts */ + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); + if (msg->flags & I2C_M_RD) + w |= DAVINCI_I2C_IMR_RRDY; + else + w |= DAVINCI_I2C_IMR_XRDY; + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); + + dev->terminate = 0; + + /* + * Write mode register first as needed for correct behaviour + * on OMAP-L138, but don't set STT yet to avoid a race with XRDY + * occurring before we have loaded DXR + */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + + /* + * First byte should be set here, not after interrupt, + * because transmit-data-ready interrupt can come before + * NACK-interrupt during sending of previous message and + * ICDXR may have wrong data + * It also saves us one interrupt, slightly faster + */ + if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) { + davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++); + dev->buf_len--; + } + + /* Set STT to begin transmit now DXR is loaded */ + flag |= DAVINCI_I2C_MDR_STT; + if (stop && msg->len != 0) + flag |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + + time_left = wait_for_completion_timeout(&dev->cmd_complete, + dev->adapter.timeout); + if (!time_left) { + i2c_recover_bus(adap); + dev->buf_len = 0; + return -ETIMEDOUT; + } + if (dev->buf_len) { + /* This should be 0 if all bytes were transferred + * or dev->cmd_err denotes an error. + */ + dev_err(dev->dev, "abnormal termination buf_len=%zu\n", + dev->buf_len); + dev->terminate = 1; + wmb(); + dev->buf_len = 0; + return -EREMOTEIO; + } + + /* no error */ + if (likely(!dev->cmd_err)) + return msg->len; + + /* We have an error */ + if (dev->cmd_err & DAVINCI_I2C_STR_AL) { + i2c_davinci_init(dev); + return -EIO; + } + + if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return msg->len; + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + return -EREMOTEIO; + } + return -EIO; +} + +/* + * Prepare controller for a transaction and call i2c_davinci_xfer_msg + */ +static int +i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int i; + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "Failed to runtime_get device: %d\n", ret); + return ret; + } + + ret = i2c_davinci_wait_bus_not_busy(dev); + if (ret < 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + goto out; + } + + for (i = 0; i < num; i++) { + ret = i2c_davinci_xfer_msg(adap, &msgs[i], (i == (num - 1))); + dev_dbg(dev->dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num, + ret); + if (ret < 0) + goto out; + } + + ret = num; + +out: + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +static u32 i2c_davinci_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_PROTOCOL_MANGLING; +} + +static void terminate_read(struct davinci_i2c_dev *dev) +{ + u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_NACK; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + + /* Throw away data */ + davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG); + if (!dev->terminate) + dev_err(dev->dev, "RDR IRQ while no data requested\n"); +} +static void terminate_write(struct davinci_i2c_dev *dev) +{ + u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_RM | DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + + if (!dev->terminate) + dev_dbg(dev->dev, "TDR IRQ while no data to send\n"); +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id) +{ + struct davinci_i2c_dev *dev = dev_id; + u32 stat; + int count = 0; + u16 w; + + if (pm_runtime_suspended(dev->dev)) + return IRQ_NONE; + + while ((stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG))) { + dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + if (count++ == 100) { + dev_warn(dev->dev, "Too much work in one IRQ\n"); + break; + } + + switch (stat) { + case DAVINCI_I2C_IVR_AL: + /* Arbitration lost, must retry */ + dev->cmd_err |= DAVINCI_I2C_STR_AL; + dev->buf_len = 0; + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_NACK: + dev->cmd_err |= DAVINCI_I2C_STR_NACK; + dev->buf_len = 0; + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_ARDY: + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY); + if (((dev->buf_len == 0) && (dev->stop != 0)) || + (dev->cmd_err & DAVINCI_I2C_STR_NACK)) { + w = davinci_i2c_read_reg(dev, + DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, + DAVINCI_I2C_MDR_REG, w); + } + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_RDR: + if (dev->buf_len) { + *dev->buf++ = + davinci_i2c_read_reg(dev, + DAVINCI_I2C_DRR_REG); + dev->buf_len--; + if (dev->buf_len) + continue; + + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, + DAVINCI_I2C_IMR_RRDY); + } else { + /* signal can terminate transfer */ + terminate_read(dev); + } + break; + + case DAVINCI_I2C_IVR_XRDY: + if (dev->buf_len) { + davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, + *dev->buf++); + dev->buf_len--; + if (dev->buf_len) + continue; + + w = davinci_i2c_read_reg(dev, + DAVINCI_I2C_IMR_REG); + w &= ~DAVINCI_I2C_IMR_XRDY; + davinci_i2c_write_reg(dev, + DAVINCI_I2C_IMR_REG, + w); + } else { + /* signal can terminate transfer */ + terminate_write(dev); + } + break; + + case DAVINCI_I2C_IVR_SCD: + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_SCD); + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_AAS: + dev_dbg(dev->dev, "Address as target interrupt\n"); + break; + + default: + dev_warn(dev->dev, "Unrecognized irq stat %d\n", stat); + break; + } + } + + return count ? IRQ_HANDLED : IRQ_NONE; +} + +#ifdef CONFIG_CPU_FREQ +static int i2c_davinci_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct davinci_i2c_dev *dev; + + dev = container_of(nb, struct davinci_i2c_dev, freq_transition); + + i2c_lock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER); + if (val == CPUFREQ_PRECHANGE) { + davinci_i2c_reset_ctrl(dev, 0); + } else if (val == CPUFREQ_POSTCHANGE) { + i2c_davinci_calc_clk_dividers(dev); + davinci_i2c_reset_ctrl(dev, 1); + } + i2c_unlock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER); + + return 0; +} + +static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev) +{ + dev->freq_transition.notifier_call = i2c_davinci_cpufreq_transition; + + return cpufreq_register_notifier(&dev->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) +{ + cpufreq_unregister_notifier(&dev->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} +#else +static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev) +{ + return 0; +} + +static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) +{ +} +#endif + +static const struct i2c_algorithm i2c_davinci_algo = { + .xfer = i2c_davinci_xfer, + .functionality = i2c_davinci_func, +}; + +static const struct of_device_id davinci_i2c_of_match[] = { + {.compatible = "ti,davinci-i2c", }, + {.compatible = "ti,keystone-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_i2c_of_match); + +static int davinci_i2c_probe(struct platform_device *pdev) +{ + struct davinci_i2c_dev *dev; + struct i2c_adapter *adap; + int r, irq; + u32 prop; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + 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; + platform_set_drvdata(pdev, dev); + + r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop); + if (r) + prop = I2C_MAX_STANDARD_MODE_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); + + dev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->base)) { + return PTR_ERR(dev->base); + } + + pm_runtime_set_autosuspend_delay(dev->dev, + DAVINCI_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(dev->dev); + + pm_runtime_enable(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); + goto err_pm; + } + + i2c_davinci_init(dev); + + r = devm_request_irq(&pdev->dev, dev->irq, i2c_davinci_isr, 0, + pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_unuse_clocks; + } + + r = i2c_davinci_cpufreq_register(dev); + if (r) { + dev_err(&pdev->dev, "failed to register cpufreq\n"); + goto err_unuse_clocks; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + 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 = dev_of_node(&pdev->dev); + + if (dev->has_pfunc) + adap->bus_recovery_info = &davinci_i2c_scl_recovery_info; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) + goto err_cpufreq; + + pm_runtime_put_autosuspend(dev->dev); + + return 0; + +err_cpufreq: + i2c_davinci_cpufreq_deregister(dev); +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 void davinci_i2c_remove(struct platform_device *pdev) +{ + struct davinci_i2c_dev *dev = platform_get_drvdata(pdev); + int ret; + + i2c_davinci_cpufreq_deregister(dev); + + i2c_del_adapter(&dev->adapter); + + ret = pm_runtime_get_sync(&pdev->dev); + 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); +} + +static int davinci_i2c_suspend(struct device *dev) +{ + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + /* put I2C into reset */ + davinci_i2c_reset_ctrl(i2c_dev, 0); + + return 0; +} + +static int davinci_i2c_resume(struct device *dev) +{ + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + /* take I2C out of reset */ + davinci_i2c_reset_ctrl(i2c_dev, 1); + + return 0; +} + +static const struct dev_pm_ops davinci_i2c_pm = { + .suspend = davinci_i2c_suspend, + .resume = davinci_i2c_resume, + NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +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 = pm_sleep_ptr(&davinci_i2c_pm), + .of_match_table = davinci_i2c_of_match, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init davinci_i2c_init_driver(void) +{ + return platform_driver_register(&davinci_i2c_driver); +} +subsys_initcall(davinci_i2c_init_driver); + +static void __exit davinci_i2c_exit_driver(void) +{ + platform_driver_unregister(&davinci_i2c_driver); +} +module_exit(davinci_i2c_exit_driver); + +MODULE_AUTHOR("Texas Instruments India"); +MODULE_DESCRIPTION("TI DaVinci I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-amdisp.c b/drivers/i2c/busses/i2c-designware-amdisp.c new file mode 100644 index 000000000..9f0ec0fae --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-amdisp.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on Synopsys DesignWare I2C adapter driver. + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include + +#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); +} + +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); + + dev_pm_genpd_resume(&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; + } + dev_pm_genpd_suspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; + +error_release_rpm: + amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_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); + + 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; + + i2c_dw_prepare_clk(i_dev, true); + i2c_dw_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 "); +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_AUTHOR("Bin Du "); +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 000000000..186f9b9f4 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#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_obj(*req); + 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 new file mode 100644 index 000000000..45774aa47 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel BayTrail PMIC I2C bus semaphore implementation + * Copyright (c) 2014, Intel Corporation. + */ +#include +#include +#include +#include + +#include + +#include "i2c-designware-core.h" + +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) + return -ENODEV; + + handle = ACPI_HANDLE(dev->dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if (!shared_host) + return -ENODEV; + + if (!iosf_mbi_available()) + return -EPROBE_DEFER; + + dev_info(dev->dev, "I2C bus managed by PUNIT\n"); + dev->acquire_lock = iosf_mbi_block_punit_i2c_access; + dev->release_lock = iosf_mbi_unblock_punit_i2c_access; + dev->shared_with_punit = true; + + return 0; +} diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c new file mode 100644 index 000000000..a1eca6cd4 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Synopsys DesignWare I2C adapter driver. + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + */ + +#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW_COMMON" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "i2c-designware-core.h" + +#define DW_IC_DEFAULT_BUS_CAPACITANCE_pF 100 +#define DW_IC_ABORT_TIMEOUT_US 10 +#define DW_IC_BUSY_POLL_TIMEOUT_US (1 * USEC_PER_MSEC) + +static const char *const abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", + [ABRT_SLAVE_FLUSH_TXFIFO] = + "read command so flush old data in the TX FIFO", + [ABRT_SLAVE_ARBLOST] = + "slave lost the bus while transmitting data to a remote master", + [ABRT_SLAVE_RD_INTX] = + "incorrect slave-transmitter mode configuration", +}; + +static int dw_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct dw_i2c_dev *dev = context; + + *val = readl(dev->base + reg); + + return 0; +} + +static int dw_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct dw_i2c_dev *dev = context; + + 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 + DW_IC_REG_STEP_BYTES) << DW_IC_REG_WORD_SHIFT); + + 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 >> DW_IC_REG_WORD_SHIFT, dev->base + reg + DW_IC_REG_STEP_BYTES); + + return 0; +} + +/** + * i2c_dw_init_regmap() - Initialize registers map + * @dev: device private data + * + * 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. + */ +static 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 = 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)) { + map_cfg.reg_read = dw_reg_read_swab; + map_cfg.reg_write = dw_reg_write_swab; + } else if (reg == lower_16_bits(DW_IC_COMP_TYPE_VALUE)) { + 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; +} + +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; + + /* + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. + */ + 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 + +#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); + + if (device_is_compatible(dev->dev, "mscc,ocelot-i2c")) { + dev->ext = devm_platform_ioremap_resource(pdev, 1); + if (!IS_ERR(dev->ext)) + dev->set_sda_hold_time = mscc_twi_set_sda_hold_time; + } +} + +#else /* CONFIG_OF */ + +static inline void i2c_dw_of_configure(struct device *device) { } + +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI + +#include + +/* + * 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_configure_mode(struct dw_i2c_dev *dev, int mode) +{ + switch (mode) { + case DW_IC_MASTER: + regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2); + regmap_write(dev->map, DW_IC_RX_TL, 0); + regmap_write(dev->map, DW_IC_CON, dev->master_cfg); + break; + case DW_IC_SLAVE: + dev->status = 0; + regmap_write(dev->map, DW_IC_TX_TL, 0); + regmap_write(dev->map, DW_IC_RX_TL, 0); + regmap_write(dev->map, DW_IC_CON, dev->slave_cfg); + regmap_write(dev->map, DW_IC_SAR, dev->slave->addr); + regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK); + __i2c_dw_enable(dev); + break; + default: + WARN(1, "Invalid mode %d\n", mode); + return; + } +} + +static void i2c_dw_write_timings(struct dw_i2c_dev *dev) +{ + /* Write standard speed timing parameters */ + 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 */ + 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 */ + regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt); + regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); +} + +/** + * i2c_dw_set_mode() - Select the controller mode of operation - master or slave + * @dev: device private data + * @mode: I2C mode of operation + * + * Configures the controller to operate in @mode. This function needs to be + * called when ever a mode swap is required. + * + * Setting the slave mode does not have an effect before a slave device is + * registered. So before the slave device is registered, the controller is kept + * in master mode regardless of @mode. + * + * The controller must be disabled before this function is called. + */ +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode) +{ + if (mode == DW_IC_SLAVE && !dev->slave) + mode = DW_IC_MASTER; + if (dev->mode == mode) + return; + + i2c_dw_configure_mode(dev, mode); + dev->mode = mode; +} + +/** + * i2c_dw_init() - Initialize the DesignWare I2C hardware + * @dev: device private data + * + * This functions configures and enables the DesigWare I2C hardware. + * + * Return: 0 on success, or negative errno otherwise. + */ +int i2c_dw_init(struct dw_i2c_dev *dev) +{ + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + /* 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); + + i2c_dw_write_timings(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); + + i2c_dw_configure_mode(dev, dev->mode); + + i2c_dw_release_lock(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_dw_init); + +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 + t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ; +} + +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 = DW_IC_DEFAULT_BUS_CAPACITANCE_pF; + + dev->clk_freq_optimized = device_property_read_bool(device, "snps,clk-freq-optimized"); + + /* Mobileye controllers do not hold the clock on empty FIFO */ + if (device_is_compatible(device, "mobileye,eyeq6lplus-i2c")) + dev->emptyfifo_hold_master = false; + else + dev->emptyfifo_hold_master = true; + + 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: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) - 1 + offset; +} + +static int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev) +{ + unsigned int reg; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + /* Configure SDA Hold Time if required */ + ret = regmap_read(dev->map, DW_IC_COMP_VERSION, ®); + 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 */ + 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 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 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; + + dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n", + dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK, + dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT); + } else if (dev->set_sda_hold_time) { + dev->set_sda_hold_time(dev); + } else if (dev->sda_hold_time) { + dev_warn(dev->dev, + "Hardware too old to adjust SDA hold time.\n"); + dev->sda_hold_time = 0; + } + +err_release_lock: + i2c_dw_release_lock(dev); + + 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 we are in target mode and there is activity, we should also + * trigger an abort to clear the internal state machines. + */ + if (dev->mode == DW_IC_SLAVE && (ic_stats & DW_IC_STATUS_SLAVE_ACTIVITY)) + abort_needed = true; + + 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), + DW_IC_ABORT_TIMEOUT_US, + 10 * DW_IC_ABORT_TIMEOUT_US); + if (ret) + dev_err(dev->dev, "timeout while trying to abort current transfer\n"); + } + + do { + __i2c_dw_disable_nowait(dev); + /* + * The enable status register may be unimplemented, but + * in that case this test reads zero and exits the loop. + */ + regmap_read(dev->map, DW_IC_ENABLE_STATUS, &status); + if (!(status & 1)) + return; + + /* + * Wait 10 times the signaling period of the highest I2C + * transfer supported by the driver (for 400kHz this is + * 25us) as described in the DesignWare I2C databook. + */ + usleep_range(25, 250); + } while (timeout--); + + dev_warn(dev->dev, "timeout in disabling adapter\n"); +} + +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 (!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) +{ + int ret; + + if (prepare) { + /* Optional interface clock */ + ret = clk_prepare_enable(dev->pclk); + if (ret) + return ret; + + 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); + +int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) +{ + int ret; + + if (!dev->acquire_lock) + return 0; + + ret = dev->acquire_lock(); + if (!ret) + return 0; + + dev_err(dev->dev, "couldn't acquire bus ownership\n"); + + return ret; +} + +void i2c_dw_release_lock(struct dw_i2c_dev *dev) +{ + if (dev->release_lock) + dev->release_lock(); +} + +/* + * Waiting for bus not busy + */ +int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) +{ + unsigned int status; + int ret; + + ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status, + !(status & DW_IC_STATUS_ACTIVITY), + DW_IC_BUSY_POLL_TIMEOUT_US, + 20 * DW_IC_BUSY_POLL_TIMEOUT_US); + if (ret) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + + i2c_recover_bus(&dev->adapter); + + regmap_read(dev->map, DW_IC_STATUS, &status); + if (!(status & DW_IC_STATUS_ACTIVITY)) + ret = 0; + } + + return ret; +} + +int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & DW_IC_TX_ARB_LOST) + return -EAGAIN; + if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ + + return -EIO; +} + +static 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, ¶m); + i2c_dw_release_lock(dev); + if (ret) + return ret; + + tx_fifo_depth = FIELD_GET(DW_IC_FIFO_TX_FIELD, param) + 1; + rx_fifo_depth = FIELD_GET(DW_IC_FIFO_RX_FIELD, param) + 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 >= DW_IC_FIFO_MIN_DEPTH) { + 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) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + return dev->functionality; +} + +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 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); + +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + + if (dev->mode == DW_IC_SLAVE) + return i2c_dw_isr_slave(dev); + + return i2c_dw_isr_master(dev); +} + +static const struct i2c_algorithm i2c_dw_algo = { + .xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_dw_reg_slave, + .unreg_slave = i2c_dw_unreg_slave, +#endif +}; + +static const struct i2c_adapter_quirks i2c_dw_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +int i2c_dw_probe(struct dw_i2c_dev *dev) +{ + struct i2c_adapter *adap = &dev->adapter; + unsigned long irq_flags; + int ret; + + device_set_node(&dev->adapter.dev, dev_fwnode(dev->dev)); + + ret = i2c_dw_init_regmap(dev); + if (ret) + return ret; + + ret = i2c_dw_set_sda_hold(dev); + if (ret) + return ret; + + ret = i2c_dw_set_fifo_size(dev); + if (ret) + return ret; + + ret = i2c_dw_probe_master(dev); + if (ret) + return ret; + + ret = i2c_dw_init(dev); + if (ret) + return ret; + + if (!adap->name[0]) + strscpy(adap->name, "Synopsys DesignWare I2C adapter"); + + adap->retries = 3; + adap->algo = &i2c_dw_algo; + adap->quirks = &i2c_dw_quirks; + adap->dev.parent = dev->dev; + i2c_set_adapdata(adap, dev); + + /* + * REVISIT: The mode check may not be necessary. + * For now keeping the flags as they were originally. + */ + if (dev->mode == DW_IC_SLAVE) + irq_flags = IRQF_SHARED; + else if (dev->flags & ACCESS_NO_IRQ_SUSPEND) + irq_flags = IRQF_NO_SUSPEND; + else + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + + /* + * The first writing to TX FIFO buffer causes transmission start. + * If IC_EMPTYFIFO_HOLD_MASTER_EN is not set, when TX FIFO gets + * empty, I2C controller finishes the transaction. If writing to + * FIFO is interrupted, FIFO can get empty and the transaction will + * be finished prematurely. FIFO buffer is filled in IRQ handler, + * but in PREEMPT_RT kernel IRQ handler by default is executed + * in thread that can be preempted with another higher priority + * thread or an interrupt. So, IRQF_NO_THREAD flag is required in + * order to prevent any preemption when filling the FIFO. + */ + if (!dev->emptyfifo_hold_master) + irq_flags |= IRQF_NO_THREAD; + + 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 ret; + } + + /* + * Increment PM usage count during adapter registration in order to + * avoid possible spurious runtime suspend when adapter device is + * registered to the device core and immediate resume in case bus has + * registered I2C slaves that do I2C transfers in their probe. + */ + PM_RUNTIME_ACQUIRE(dev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + return i2c_add_numbered_adapter(adap); +} +EXPORT_SYMBOL_GPL(i2c_dw_probe); + +static int i2c_dw_prepare(struct device *device) +{ + /* + * 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); +} + +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); + + i2c_dw_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) +}; + +void i2c_dw_shutdown(struct dw_i2c_dev *dev) +{ + unsigned int con; + + /* + * We only need to handle shutdown for target mode to ensure + * we NACK any incoming controller requests. Controller mode cleanup + * is handled after each transfer in i2c_dw_xfer(). + */ + if (dev->mode != DW_IC_SLAVE) + return; + + /* + * To quickly NACK the controller during shutdown, we set the target + * disable bit while the controller is still enabled. + */ + regmap_read(dev->map, DW_IC_CON, &con); + con |= DW_IC_CON_SLAVE_DISABLE; + regmap_write(dev->map, DW_IC_CON, con); + + i2c_dw_disable(dev); +} +EXPORT_SYMBOL_GPL(i2c_dw_shutdown); + +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 new file mode 100644 index 000000000..2c929a6e8 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Synopsys DesignWare I2C adapter driver. + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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) + +/* + * Register access parameters + */ +#define DW_IC_REG_STEP_BYTES 2 +#define DW_IC_REG_WORD_SHIFT 16 + +/* + * FIFO depth configuration + */ +#define DW_IC_FIFO_TX_FIELD GENMASK(23, 16) +#define DW_IC_FIFO_RX_FIELD GENMASK(15, 8) +#define DW_IC_FIFO_MIN_DEPTH 2 + +#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ +#define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ + +#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_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) + +/* + * Sofware status flags + */ +#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 + +/* + * 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. + */ +#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 + * @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 + * @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 + * @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 + * @rx_outstanding: current master-rx elements in tx fifo + * @timings: bus clock frequency, SDA hold and other timings + * @sda_hold_time: SDA hold value + * @ss_hcnt: standard speed HCNT value + * @ss_lcnt: standard speed LCNT value + * @fs_hcnt: fast speed HCNT value + * @fs_lcnt: fast speed LCNT value + * @fp_hcnt: fast plus HCNT value + * @fp_lcnt: fast plus LCNT value + * @hs_hcnt: high speed HCNT value + * @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 + * @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 + * @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. + * @emptyfifo_hold_master: true if the controller acting as master holds + * the clock when the Tx FIFO is empty instead of emitting a stop. + * + * 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. + * Leave them to be %0 if not used. + */ +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; + u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + u32 tx_buf_len; + u8 *tx_buf; + int msg_read_idx; + u32 rx_buf_len; + u8 *rx_buf; + int msg_err; + unsigned int status; + unsigned int abort_source; + unsigned int sw_mask; + int irq; + u32 flags; + struct i2c_adapter adapter; + u32 functionality; + u32 master_cfg; + u32 slave_cfg; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + int rx_outstanding; + struct i2c_timings timings; + u32 sda_hold_time; + u16 ss_hcnt; + u16 ss_lcnt; + u16 fs_hcnt; + u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; + int (*acquire_lock)(void); + void (*release_lock)(void); + int semaphore_idx; + bool shared_with_punit; + 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; + bool emptyfifo_hold_master; +}; + +#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_AMD_NAVI_GPU BIT(10) +#define MODEL_WANGXUN_SP BIT(11) +#define MODEL_MASK GENMASK(11, 8) + +/* + * 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 + +#define TXGBE_TX_FIFO_DEPTH 4 +#define TXGBE_RX_FIFO_DEPTH 1 + +struct i2c_dw_semaphore_callbacks { + int (*probe)(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); +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); +u32 i2c_dw_func(struct i2c_adapter *adap); +irqreturn_t i2c_dw_isr_master(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) +{ + dev->status |= STATUS_ACTIVE; + regmap_write(dev->map, DW_IC_ENABLE, 1); +} + +static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev) +{ + 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); + +int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev); +irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev); +int i2c_dw_reg_slave(struct i2c_client *client); +int i2c_dw_unreg_slave(struct i2c_client *client); +#else +static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { } +static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; } +#endif + +static inline void i2c_dw_configure(struct dw_i2c_dev *dev) +{ + i2c_dw_configure_slave(dev); + i2c_dw_configure_master(dev); +} + +int i2c_dw_probe(struct dw_i2c_dev *dev); +int i2c_dw_init(struct dw_i2c_dev *dev); +void i2c_dw_shutdown(struct dw_i2c_dev *dev); +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode); + +#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) +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 new file mode 100644 index 000000000..a1bcc3797 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -0,0 +1,1053 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + */ + +#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) +{ + 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; + + 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 */ + scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + ic_clk = i2c_dw_clk_rate(dev); + dev->ss_hcnt = + i2c_dw_scl_hcnt(dev, + DW_IC_SS_SCL_HCNT, + ic_clk, + 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, + 0); /* No offset */ + dev->ss_lcnt = + i2c_dw_scl_lcnt(dev, + DW_IC_SS_SCL_LCNT, + ic_clk, + 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", + dev->ss_hcnt, dev->ss_lcnt); + + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers are + * the same. + */ + if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) { + /* + * 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; + } 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 + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + ic_clk = i2c_dw_clk_rate(dev); + dev->fs_hcnt = + i2c_dw_scl_hcnt(dev, + DW_IC_FS_SCL_HCNT, + ic_clk, + 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, + 0); /* No offset */ + dev->fs_lcnt = + i2c_dw_scl_lcnt(dev, + DW_IC_FS_SCL_LCNT, + ic_clk, + 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", + fp_str, dev->fs_hcnt, dev->fs_lcnt); + + /* Check is high speed possible and fall back to fast mode if not */ + if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) == + DW_IC_CON_SPEED_HIGH) { + 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) { + 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); + } + + dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz)); + return 0; +} + +static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con = 0, ic_tar = 0; + unsigned int dummy; + + /* Disable the adapter */ + __i2c_dw_disable(dev); + + i2c_dw_set_mode(dev, DW_IC_MASTER); + + /* If the slave address is ten bit address, enable 10BITADDR */ + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { + 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. + * We set it always as I2C_DYNAMIC_TAR_UPDATE can't be + * detected from registers. + */ + ic_tar = DW_IC_TAR_10BITADDR_MASTER; + } + + 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. + */ + regmap_write(dev->map, DW_IC_TAR, + msgs[dev->msg_write_idx].addr | ic_tar); + + /* Enforce disabled interrupts (due to HW issues) */ + __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 */ + regmap_read(dev->map, DW_IC_ENABLE_STATUS, &dummy); + + /* Clear and enable interrupts */ + 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 dw_i2c_dev *dev, struct i2c_msg *msgs, int num_msgs) +{ + int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx; + int cmd = 0, status; + u8 *tx_buf; + unsigned int val; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + /* + * 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 + * 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. + */ +static void +i2c_dw_xfer_msg(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + 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; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + u32 flags = msgs[dev->msg_write_idx].flags; + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + + /* + * If both IC_EMPTYFIFO_HOLD_MASTER_EN and + * IC_RESTART_EN are set, we must manually + * set restart bit between messages. + */ + if ((dev->master_cfg & DW_IC_CON_RESTART_EN) && + (dev->msg_write_idx > 0)) + need_restart = true; + } + + 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; + + /* + * If IC_EMPTYFIFO_HOLD_MASTER_EN is set we must + * manually set the stop bit. However, it cannot be + * detected from the registers so we set it always + * when writing/reading the last byte. + */ + + /* + * i2c-core always sets the buffer length of + * I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will + * be adjusted when receiving the first byte. + * Thus we can't stop the transaction here. + */ + if (dev->msg_write_idx == dev->msgs_num - 1 && + buf_len == 1 && !(flags & I2C_M_RECV_LEN)) + cmd |= BIT(9); + + if (need_restart) { + cmd |= BIT(10); + need_restart = false; + } + + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + + /* Avoid rx buffer overrun */ + if (dev->rx_outstanding >= dev->rx_fifo_depth) + break; + + regmap_write(dev->map, DW_IC_DATA_CMD, + cmd | 0x100); + rx_limit--; + dev->rx_outstanding++; + } else { + regmap_write(dev->map, DW_IC_DATA_CMD, + cmd | *buf++); + } + tx_limit--; buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + /* + * Because we don't know the buffer length in the + * 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 (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; + } else + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + + /* + * If i2c_msg index search is completed, we don't need TX_EMPTY + * interrupt any more. + */ + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + __i2c_dw_write_intr_mask(dev, intr_mask); +} + +static u8 +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 + * after receiving the first byte. + */ + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; + dev->tx_buf_len = len - min(len, dev->rx_outstanding); + 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; +} + +static void +i2c_dw_read(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + unsigned int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 flags = msgs[dev->msg_read_idx].flags; + unsigned int tmp; + u32 len; + u8 *buf; + + if (!(flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + regmap_read(dev->map, DW_IC_RXFLR, &rx_valid); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + 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) { + /* + * 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++ = tmp; + dev->rx_outstanding--; + } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +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); + + /* Abort if we detect a STOP in the middle of a read or a write */ + if ((stat & DW_IC_INTR_STOP_DET) && + (dev->status & (STATUS_READ_IN_PROGRESS | STATUS_WRITE_IN_PROGRESS))) { + dev_err(dev->dev, "spurious STOP detected\n"); + dev->rx_outstanding = 0; + dev->msg_err = -EIO; + } + + /* + * 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. + */ +irqreturn_t i2c_dw_isr_master(struct dw_i2c_dev *dev) +{ + 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, start the transfer of the @msgs + * and wait for completion, either a STOP or a error. + * Return: 0 or a negative error code. + */ +static int +__i2c_dw_xfer_one_part(struct dw_i2c_dev *dev, struct i2c_msg *msgs, size_t num) +{ + int ret; + + reinit_completion(&dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = 0; + dev->abort_source = 0; + dev->rx_outstanding = 0; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) + return ret; + + /* Start the transfers */ + i2c_dw_xfer_init(dev); + + /* Wait for tx to complete */ + ret = i2c_dw_wait_transfer(dev); + if (ret) { + dev_err(dev->dev, "controller timed out\n"); + /* i2c_dw_init() implicitly disables the adapter */ + i2c_recover_bus(&dev->adapter); + i2c_dw_init(dev); + return ret; + } + + /* + * 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)) { + /* + * If the controller is still active after the timeout, attempt a + * bus recovery to clear any potentially locked state. + */ + dev_err(dev->dev, "controller active after xfer, recovering\n"); + i2c_recover_bus(&dev->adapter); + i2c_dw_init(dev); + } else { + /* + * 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 + * additional interrupts are a hardware bug or this driver doesn't + * handle them correctly yet. + */ + __i2c_dw_disable_nowait(dev); + } + + if (dev->msg_err) + return dev->msg_err; + + /* No error */ + if (likely(!dev->cmd_err && !dev->status)) + return 0; + + /* We have an error */ + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) + return i2c_dw_handle_tx_abort(dev); + + if (dev->status) + dev_err(dev->dev, + "transfer terminated early - interrupt latency too high?\n"); + + return -EIO; +} + +/* + * Verify that the message at index @idx can be processed as part + * of a single transaction. The @msgs array contains the messages + * of the transaction. The message is checked against its predecessor + * to ensure that it respects the limitation of the controller. + * Return: true if the message can be processed, false otherwise. + */ +static bool +i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t idx) +{ + /* + * The first message of a transaction is valid, + * no constraints from a previous message. + */ + if (!idx) + return true; + + /* + * We cannot change the target address during a transaction, so make + * sure the address is identical to the one of the previous message. + */ + if (msgs[idx - 1].addr != msgs[idx].addr) { + dev_err(dev->dev, "invalid target address\n"); + return false; + } + + /* + * Make sure we don't need explicit RESTART between two messages + * in the same direction for controllers that cannot emit them. + */ + if (!dev->emptyfifo_hold_master && + (msgs[idx - 1].flags & I2C_M_RD) == (msgs[idx].flags & I2C_M_RD)) { + dev_err(dev->dev, "cannot emit RESTART\n"); + return false; + } + + return true; +} + +static int +i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *msgs_part; + size_t cnt; + int ret; + + dev_dbg(dev->dev, "msgs: %d\n", num); + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + /* + * If the I2C_M_STOP is present in some the messages, + * we do one transaction for each part up to the STOP. + */ + for (msgs_part = msgs; msgs_part < msgs + num; msgs_part += cnt) { + /* + * Count the messages in a transaction, up to a STOP or + * the end of the msgs. The last if below guarantees that + * we check all messages and that msg_parts and cnt are + * in-bounds of msgs and num. + */ + for (cnt = 1; ; cnt++) { + if (!i2c_dw_msg_is_valid(dev, msgs_part, cnt - 1)) { + ret = -EINVAL; + break; + } + + if ((msgs_part[cnt - 1].flags & I2C_M_STOP) || + (msgs_part + cnt == msgs + num)) + break; + } + if (ret < 0) + break; + + /* transfer one part up to a STOP */ + ret = __i2c_dw_xfer_one_part(dev, msgs_part, cnt); + if (ret < 0) + break; + } + + i2c_dw_set_mode(dev, DW_IC_SLAVE); + + i2c_dw_release_lock(dev); + + if (ret < 0) + return ret; + return num; +} + +int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) + return amd_i2c_dw_xfer_quirk(dev, msgs, num); + + return i2c_dw_xfer_common(dev, msgs, num); +} + +void i2c_dw_configure_master(struct dw_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; + + /* amd_i2c_dw_xfer_quirk() does not implement protocol mangling */ + if ((dev->flags & MODEL_MASK) != MODEL_AMD_NAVI_GPU) + dev->functionality |= I2C_FUNC_PROTOCOL_MANGLING; + + 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 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; + } +} +EXPORT_SYMBOL_GPL(i2c_dw_configure_master); + +static void i2c_dw_prepare_recovery(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + i2c_dw_disable(dev); + reset_control_assert(dev->rst); + i2c_dw_prepare_clk(dev, false); +} + +static void i2c_dw_unprepare_recovery(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + i2c_dw_prepare_clk(dev, true); + reset_control_deassert(dev->rst); + i2c_dw_init(dev); +} + +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; + + 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); + if (IS_ERR(gpio)) + 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", + rinfo->sda_gpiod ? ",sda" : ""); + + return 0; +} + +int i2c_dw_probe_master(struct dw_i2c_dev *dev) +{ + unsigned int ic_con; + int ret; + + init_completion(&dev->cmd_complete); + + ret = i2c_dw_set_timings_master(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; + + return i2c_dw_init_recovery_info(dev); +} + +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 new file mode 100644 index 000000000..468287922 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * Copyright (C) 2011, 2015, 2016 Intel Corporation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-designware-core.h" +#include "i2c-ccgx-ucsi.h" + +#define DRIVER_NAME "i2c-designware-pci" + +enum dw_pci_ctl_id_t { + medfield, + merrifield, + 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 { + u16 ss_hcnt; + u16 fs_hcnt; + u16 ss_lcnt; + u16 fs_lcnt; + u32 sda_hold_time; +}; + +struct dw_pci_controller { + u32 bus_num; + 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); +}; + +/* Merrifield HCNT/LCNT/SDA hold time */ +static struct dw_scl_sda_cfg mrfld_config = { + .ss_hcnt = 0x2f8, + .fs_hcnt = 0x87, + .ss_lcnt = 0x37b, + .fs_lcnt = 0x10a, +}; + +/* BayTrail HCNT/LCNT/SDA hold time */ +static struct dw_scl_sda_cfg byt_config = { + .ss_hcnt = 0x200, + .fs_hcnt = 0x55, + .ss_lcnt = 0x200, + .fs_lcnt = 0x99, + .sda_hold_time = 0x6, +}; + +/* Haswell HCNT/LCNT/SDA hold time */ +static struct dw_scl_sda_cfg hsw_config = { + .ss_hcnt = 0x01b0, + .fs_hcnt = 0x48, + .ss_lcnt = 0x01fb, + .fs_lcnt = 0xa0, + .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: + dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; + fallthrough; + case 0x0818: + case 0x0819: + c->bus_num = pdev->device - 0x817 + 3; + return 0; + case 0x082C: + case 0x082D: + case 0x082E: + c->bus_num = pdev->device - 0x82C + 0; + return 0; + } + return -ENODEV; +} + +static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) +{ + /* + * 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. + */ + switch (PCI_SLOT(pdev->devfn)) { + case 8: + c->bus_num = PCI_FUNC(pdev->devfn) + 0 + 1; + return 0; + case 9: + c->bus_num = PCI_FUNC(pdev->devfn) + 4 + 1; + return 0; + } + 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, + .setup = mfld_setup, + .get_clk_rate_khz = mfld_get_clk_rate_khz, + }, + [merrifield] = { + .bus_num = -1, + .scl_sda_cfg = &mrfld_config, + .setup = mrfld_setup, + }, + [baytrail] = { + .bus_num = -1, + .scl_sda_cfg = &byt_config, + }, + [haswell] = { + .bus_num = -1, + .scl_sda_cfg = &hsw_config, + }, + [cherrytrail] = { + .bus_num = -1, + .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, + }, +}; + +static const struct property_entry dgpu_properties[] = { + /* USB-C doesn't power the system */ + PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), + {} +}; + +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)) + 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) + 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) + return dev_err_probe(device, r, "I/O memory remapping failed\n"); + + dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + 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 = 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; + } + + r = i2c_dw_fw_parse_and_configure(dev); + if (r) + return r; + + i2c_dw_configure(dev); + + 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_time; + } + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = 0; + adap->nr = controller->bus_num; + + r = i2c_dw_probe(dev); + if (r) + return r; + + 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; +} + +static void i2c_dw_pci_remove(struct pci_dev *pdev) +{ + struct dw_i2c_dev *dev = pci_get_drvdata(pdev); + struct device *device = &pdev->dev; + + i2c_dw_disable(dev); + + pm_runtime_forbid(device); + pm_runtime_get_noresume(device); + + i2c_del_adapter(&dev->adapter); +} + +static const struct pci_device_id i2c_designware_pci_ids[] = { + /* Medfield */ + { PCI_VDEVICE(INTEL, 0x0817), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0818), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0819), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082C), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082D), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082E), .driver_data = medfield }, + /* Merrifield */ + { PCI_VDEVICE(INTEL, 0x1195), .driver_data = merrifield }, + { PCI_VDEVICE(INTEL, 0x1196), .driver_data = merrifield }, + /* Baytrail */ + { PCI_VDEVICE(INTEL, 0x0F41), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F42), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F43), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F44), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F45), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F46), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F47), .driver_data = baytrail }, + /* Haswell */ + { PCI_VDEVICE(INTEL, 0x9c61), .driver_data = haswell }, + { PCI_VDEVICE(INTEL, 0x9c62), .driver_data = haswell }, + /* Braswell / Cherrytrail */ + { PCI_VDEVICE(INTEL, 0x22C1), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C2), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C3), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C4), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C5), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C6), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C7), .driver_data = cherrytrail }, + /* Elkhart Lake (PSE I2C) */ + { PCI_VDEVICE(INTEL, 0x4bb9), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bba), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbb), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbc), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbd), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbe), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbf), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bc0), .driver_data = elkhartlake }, + /* AMD NAVI */ + { PCI_VDEVICE(ATI, 0x7314), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73a4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73e4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73c4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7444), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7464), .driver_data = navi_amd }, + { } +}; +MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids); + +static void i2c_dw_pci_shutdown(struct pci_dev *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = pci_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + +static struct pci_driver dw_i2c_driver = { + .name = DRIVER_NAME, + .probe = i2c_dw_pci_probe, + .remove = i2c_dw_pci_remove, + .shutdown = i2c_dw_pci_shutdown, + .driver = { + .pm = pm_ptr(&i2c_dw_dev_pm_ops), + }, + .id_table = i2c_designware_pci_ids, +}; +module_pci_driver(dw_i2c_driver); + +MODULE_AUTHOR("Baruch Siach "); +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 new file mode 100644 index 000000000..447af5523 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Synopsys DesignWare I2C adapter driver. + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-designware-core.h" + +static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) +{ + return clk_get_rate(dev->clk) / HZ_PER_KHZ; +} + +static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev) +{ + dev->map = dev_get_regmap(dev->dev->parent, NULL); + if (!dev->map) + return -ENODEV; + + return 0; +} + +static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev) +{ + pm_runtime_disable(dev->dev); + + if (dev->shared_with_punit) + pm_runtime_put_noidle(dev->dev); +} + +static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev) +{ + struct platform_device *pdev = to_platform_device(dev->dev); + int ret; + + if (device_is_compatible(dev->dev, "intel,xe-i2c")) + return dw_i2c_get_parent_regmap(dev); + + switch (dev->flags & MODEL_MASK) { + case MODEL_WANGXUN_SP: + ret = dw_i2c_get_parent_regmap(dev); + break; + default: + dev->base = devm_platform_ioremap_resource(pdev, 0); + ret = PTR_ERR_OR_ZERO(dev->base); + break; + } + + return ret; +} + +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 */ +}; + +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 int i2c_dw_probe_lock_support(struct dw_i2c_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; + } + + return 0; +} + +static int dw_i2c_plat_probe(struct platform_device *pdev) +{ + u32 flags = (uintptr_t)device_get_match_data(&pdev->dev); + struct device *device = &pdev->dev; + struct i2c_adapter *adap; + struct dw_i2c_dev *dev; + 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(device, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + if (device_property_present(device, "wx,i2c-snps-model")) + flags = MODEL_WANGXUN_SP | ACCESS_POLLING; + + dev->dev = device; + dev->irq = irq; + dev->flags = flags; + platform_set_drvdata(pdev, dev); + + ret = dw_i2c_plat_request_regs(dev); + if (ret) + return ret; + + dev->rst = devm_reset_control_get_optional_exclusive_deasserted(device, NULL); + if (IS_ERR(dev->rst)) + return dev_err_probe(device, PTR_ERR(dev->rst), "failed to acquire reset\n"); + + ret = i2c_dw_fw_parse_and_configure(dev); + if (ret) + return ret; + + ret = i2c_dw_probe_lock_support(dev); + if (ret) + return dev_err_probe(device, ret, "failed to probe lock support\n"); + + i2c_dw_configure(dev); + + /* Optional interface clock */ + dev->pclk = devm_clk_get_optional(device, "pclk"); + if (IS_ERR(dev->pclk)) + return dev_err_probe(device, PTR_ERR(dev->pclk), "failed to acquire pclk\n"); + + dev->clk = devm_clk_get_optional(device, NULL); + if (IS_ERR(dev->clk)) + return dev_err_probe(device, PTR_ERR(dev->clk), "failed to acquire clock\n"); + + ret = i2c_dw_prepare_clk(dev, true); + if (ret) + return ret; + + if (dev->clk) { + struct i2c_timings *t = &dev->timings; + u64 clk_khz; + + dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); + + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = + DIV_S64_ROUND_CLOSEST(clk_khz * t->sda_hold_ns, MICRO); + } + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ? + I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED; + adap->nr = -1; + + 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(device)); + + 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(device); + + pm_runtime_enable(device); + + ret = i2c_dw_probe(dev); + if (ret) { + dw_i2c_plat_pm_cleanup(dev); + i2c_dw_prepare_clk(dev, false); + } + + return ret; +} + +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(device); + + i2c_del_adapter(&dev->adapter); + + i2c_dw_disable(dev); + + pm_runtime_dont_use_autosuspend(device); + pm_runtime_put_noidle(device); + dw_i2c_plat_pm_cleanup(dev); + + i2c_dw_prepare_clk(dev, false); +} + +static const struct of_device_id dw_i2c_of_match[] = { + { .compatible = "mobileye,eyeq6lplus-i2c" }, + { .compatible = "mscc,ocelot-i2c" }, + { .compatible = "snps,designware-i2c" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_i2c_of_match); + +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 }, + { "GOOG5000", 0 }, + { "HISI02A1", 0 }, + { "HISI02A2", 0 }, + { "HISI02A3", 0 }, + { "HJMC3001", ACCESS_INTR_MASK }, + { "HYGO0010", ACCESS_INTR_MASK }, + { "INT33C2", 0 }, + { "INT33C3", 0 }, + { "INT3432", 0 }, + { "INT3433", 0 }, + { "INTC10EF", 0 }, + { "LECA0003", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); + +static const struct platform_device_id dw_i2c_platform_ids[] = { + { "i2c_designware" }, + {} +}; +MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids); + +static void dw_i2c_plat_shutdown(struct platform_device *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = platform_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + +static struct platform_driver dw_i2c_driver = { + .probe = dw_i2c_plat_probe, + .remove = dw_i2c_plat_remove, + .shutdown = dw_i2c_plat_shutdown, + .driver = { + .name = "i2c_designware", + .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) +{ + return platform_driver_register(&dw_i2c_driver); +} +subsys_initcall(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + platform_driver_unregister(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach "); +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 new file mode 100644 index 000000000..0abcc7757 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare I2C adapter driver (slave only). + * + * Based on the Synopsys DesignWare I2C adapter driver (master). + * + * Copyright (C) 2016 Synopsys Inc. + */ + +#define DEFAULT_SYMBOL_NAMESPACE "I2C_DW" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "i2c-designware-core.h" + +int i2c_dw_reg_slave(struct i2c_client *slave) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + int ret; + + if (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE)) + return -EOPNOTSUPP; + if (dev->slave) + return -EBUSY; + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + pm_runtime_get_sync(dev->dev); + __i2c_dw_disable_nowait(dev); + dev->slave = slave; + i2c_dw_set_mode(dev, DW_IC_SLAVE); + + i2c_dw_release_lock(dev); + + return 0; +} + +int i2c_dw_unreg_slave(struct i2c_client *slave) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + regmap_write(dev->map, DW_IC_INTR_MASK, 0); + i2c_dw_disable(dev); + synchronize_irq(dev->irq); + dev->slave = NULL; + i2c_dw_set_mode(dev, DW_IC_MASTER); + pm_runtime_put_sync_suspend(dev->dev); + + return 0; +} + +static u32 i2c_dw_read_clear_intrbits_slave(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. + */ + 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 + * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & DW_IC_INTR_TX_ABRT) + regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy); + 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_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) + 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; +} + +/* + * Interrupt service routine. This gets called whenever an I2C slave interrupt + * occurs. + */ +irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) +{ + unsigned int raw_stat, stat, enabled, tmp; + u8 val = 0, slave_activity; + + 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 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) { + 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) { + 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 { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, + &val); + } + regmap_write(dev->map, DW_IC_DATA_CMD, val); + } + } + + if (stat & DW_IC_INTR_STOP_DET) + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); + + return IRQ_HANDLED; +} + +void i2c_dw_configure_slave(struct dw_i2c_dev *dev) +{ + if (dev->flags & ACCESS_POLLING) + return; + + 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; +} +EXPORT_SYMBOL_GPL(i2c_dw_configure_slave); + +MODULE_AUTHOR("Luis Oliveira "); +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 new file mode 100644 index 000000000..38d7f31ae --- /dev/null +++ b/drivers/i2c/busses/i2c-digicolor.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C bus driver for Conexant Digicolor SoCs + * + * Author: Baruch Siach + * + * Copyright (C) 2015 Paradox Innovation Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MS 100 + +#define II_CONTROL 0x0 +#define II_CONTROL_LOCAL_RESET BIT(0) + +#define II_CLOCKTIME 0x1 + +#define II_COMMAND 0x2 +#define II_CMD_START 1 +#define II_CMD_RESTART 2 +#define II_CMD_SEND_ACK 3 +#define II_CMD_GET_ACK 6 +#define II_CMD_GET_NOACK 7 +#define II_CMD_STOP 10 +#define II_COMMAND_GO BIT(7) +#define II_COMMAND_COMPLETION_STATUS(r) (((r) >> 5) & 3) +#define II_CMD_STATUS_NORMAL 0 +#define II_CMD_STATUS_ACK_GOOD 1 +#define II_CMD_STATUS_ACK_BAD 2 +#define II_CMD_STATUS_ABORT 3 + +#define II_DATA 0x3 +#define II_INTFLAG_CLEAR 0x8 +#define II_INTENABLE 0xa + +struct dc_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *regs; + struct clk *clk; + unsigned int frequency; + + struct i2c_msg *msg; + unsigned int msgbuf_ptr; + int last; + spinlock_t lock; + struct completion done; + int state; + int error; +}; + +enum { + STATE_IDLE, + STATE_START, + STATE_ADDR, + STATE_WRITE, + STATE_READ, + STATE_STOP, +}; + +static void dc_i2c_cmd(struct dc_i2c *i2c, u8 cmd) +{ + writeb_relaxed(cmd | II_COMMAND_GO, i2c->regs + II_COMMAND); +} + +static u8 dc_i2c_addr_cmd(struct i2c_msg *msg) +{ + u8 addr = (msg->addr & 0x7f) << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + return addr; +} + +static void dc_i2c_data(struct dc_i2c *i2c, u8 data) +{ + writeb_relaxed(data, i2c->regs + II_DATA); +} + +static void dc_i2c_write_byte(struct dc_i2c *i2c, u8 byte) +{ + dc_i2c_data(i2c, byte); + dc_i2c_cmd(i2c, II_CMD_SEND_ACK); +} + +static void dc_i2c_write_buf(struct dc_i2c *i2c) +{ + dc_i2c_write_byte(i2c, i2c->msg->buf[i2c->msgbuf_ptr++]); +} + +static void dc_i2c_next_read(struct dc_i2c *i2c) +{ + bool last = (i2c->msgbuf_ptr + 1 == i2c->msg->len); + + dc_i2c_cmd(i2c, last ? II_CMD_GET_NOACK : II_CMD_GET_ACK); +} + +static void dc_i2c_stop(struct dc_i2c *i2c) +{ + i2c->state = STATE_STOP; + if (i2c->last) + dc_i2c_cmd(i2c, II_CMD_STOP); + else + complete(&i2c->done); +} + +static u8 dc_i2c_read_byte(struct dc_i2c *i2c) +{ + return readb_relaxed(i2c->regs + II_DATA); +} + +static void dc_i2c_read_buf(struct dc_i2c *i2c) +{ + i2c->msg->buf[i2c->msgbuf_ptr++] = dc_i2c_read_byte(i2c); + dc_i2c_next_read(i2c); +} + +static void dc_i2c_set_irq(struct dc_i2c *i2c, int enable) +{ + if (enable) + writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR); + writeb_relaxed(!!enable, i2c->regs + II_INTENABLE); +} + +static int dc_i2c_cmd_status(struct dc_i2c *i2c) +{ + u8 cmd = readb_relaxed(i2c->regs + II_COMMAND); + + return II_COMMAND_COMPLETION_STATUS(cmd); +} + +static void dc_i2c_start_msg(struct dc_i2c *i2c, int first) +{ + struct i2c_msg *msg = i2c->msg; + + if (!(msg->flags & I2C_M_NOSTART)) { + i2c->state = STATE_START; + dc_i2c_cmd(i2c, first ? II_CMD_START : II_CMD_RESTART); + } else if (msg->flags & I2C_M_RD) { + i2c->state = STATE_READ; + dc_i2c_next_read(i2c); + } else { + i2c->state = STATE_WRITE; + dc_i2c_write_buf(i2c); + } +} + +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); + u8 addr_cmd; + + writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR); + + spin_lock(&i2c->lock); + + if (cmd_status == II_CMD_STATUS_ACK_BAD + || cmd_status == II_CMD_STATUS_ABORT) { + i2c->error = -EIO; + complete(&i2c->done); + goto out; + } + + switch (i2c->state) { + case STATE_START: + addr_cmd = dc_i2c_addr_cmd(i2c->msg); + dc_i2c_write_byte(i2c, addr_cmd); + i2c->state = STATE_ADDR; + break; + case STATE_ADDR: + if (i2c->msg->flags & I2C_M_RD) { + dc_i2c_next_read(i2c); + i2c->state = STATE_READ; + break; + } + i2c->state = STATE_WRITE; + fallthrough; + case STATE_WRITE: + if (i2c->msgbuf_ptr < i2c->msg->len) + dc_i2c_write_buf(i2c); + else + dc_i2c_stop(i2c); + break; + case STATE_READ: + if (i2c->msgbuf_ptr < i2c->msg->len) + dc_i2c_read_buf(i2c); + else + dc_i2c_stop(i2c); + break; + case STATE_STOP: + i2c->state = STATE_IDLE; + complete(&i2c->done); + break; + } + +out: + 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 time_left = msecs_to_jiffies(TIMEOUT_MS); + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + i2c->msg = msg; + i2c->msgbuf_ptr = 0; + i2c->last = last; + i2c->error = 0; + + reinit_completion(&i2c->done); + dc_i2c_set_irq(i2c, 1); + dc_i2c_start_msg(i2c, first); + spin_unlock_irqrestore(&i2c->lock, flags); + + time_left = wait_for_completion_timeout(&i2c->done, time_left); + dc_i2c_set_irq(i2c, 0); + if (time_left == 0) { + i2c->state = STATE_IDLE; + return -ETIMEDOUT; + } + + if (i2c->error) + return i2c->error; + + return 0; +} + +static int dc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct dc_i2c *i2c = adap->algo_data; + int i, ret; + + for (i = 0; i < num; i++) { + ret = dc_i2c_xfer_msg(i2c, &msgs[i], i == 0, i == num - 1); + if (ret) + return ret; + } + + return num; +} + +static int dc_i2c_init_hw(struct dc_i2c *i2c) +{ + unsigned long clk_rate = clk_get_rate(i2c->clk); + unsigned int clocktime; + + writeb_relaxed(II_CONTROL_LOCAL_RESET, i2c->regs + II_CONTROL); + udelay(100); + writeb_relaxed(0, i2c->regs + II_CONTROL); + udelay(100); + + clocktime = DIV_ROUND_UP(clk_rate, 64 * i2c->frequency); + if (clocktime < 1 || clocktime > 0xff) { + dev_err(i2c->dev, "can't set bus speed of %u Hz\n", + i2c->frequency); + return -EINVAL; + } + writeb_relaxed(clocktime - 1, i2c->regs + II_CLOCKTIME); + + return 0; +} + +static u32 dc_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm dc_i2c_algorithm = { + .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; + int ret = 0, irq; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct dc_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &i2c->frequency)) + i2c->frequency = I2C_MAX_STANDARD_MODE_FREQ; + + i2c->dev = &pdev->dev; + platform_set_drvdata(pdev, i2c); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->done); + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) + return PTR_ERR(i2c->clk); + + 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) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, dc_i2c_irq, 0, + dev_name(&pdev->dev), i2c); + if (ret < 0) + return ret; + + strscpy(i2c->adap.name, "Conexant Digicolor I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &dc_i2c_algorithm; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + + ret = dc_i2c_init_hw(i2c); + if (ret) + return ret; + + ret = clk_prepare_enable(i2c->clk); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + clk_disable_unprepare(i2c->clk); + return ret; + } + + return 0; +} + +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); +} + +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, + .driver = { + .name = "digicolor-i2c", + .of_match_table = dc_i2c_match, + }, +}; +module_platform_driver(dc_i2c_driver); + +MODULE_AUTHOR("Baruch Siach "); +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 new file mode 100644 index 000000000..d502f6e47 --- /dev/null +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Diolan u2c-12 USB-I2C adapter + * + * Copyright (c) 2010-2011 Ericsson AB + * + * Derived from: + * i2c-tiny-usb.c + * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "i2c-diolan-u2c" + +#define USB_VENDOR_ID_DIOLAN 0x0abf +#define USB_DEVICE_ID_DIOLAN_U2C 0x3370 + + +/* commands via USB, must match command ids in the firmware */ +#define CMD_I2C_READ 0x01 +#define CMD_I2C_WRITE 0x02 +#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */ +#define CMD_I2C_RELEASE_SDA 0x04 +#define CMD_I2C_RELEASE_SCL 0x05 +#define CMD_I2C_DROP_SDA 0x06 +#define CMD_I2C_DROP_SCL 0x07 +#define CMD_I2C_READ_SDA 0x08 +#define CMD_I2C_READ_SCL 0x09 +#define CMD_GET_FW_VERSION 0x0a +#define CMD_GET_SERIAL 0x0b +#define CMD_I2C_START 0x0c +#define CMD_I2C_STOP 0x0d +#define CMD_I2C_REPEATED_START 0x0e +#define CMD_I2C_PUT_BYTE 0x0f +#define CMD_I2C_GET_BYTE 0x10 +#define CMD_I2C_PUT_ACK 0x11 +#define CMD_I2C_GET_ACK 0x12 +#define CMD_I2C_PUT_BYTE_ACK 0x13 +#define CMD_I2C_GET_BYTE_ACK 0x14 +#define CMD_I2C_SET_SPEED 0x1b +#define CMD_I2C_GET_SPEED 0x1c +#define CMD_I2C_SET_CLK_SYNC 0x24 +#define CMD_I2C_GET_CLK_SYNC 0x25 +#define CMD_I2C_SET_CLK_SYNC_TO 0x26 +#define CMD_I2C_GET_CLK_SYNC_TO 0x27 + +#define RESP_OK 0x00 +#define RESP_FAILED 0x01 +#define RESP_BAD_MEMADDR 0x04 +#define RESP_DATA_ERR 0x05 +#define RESP_NOT_IMPLEMENTED 0x06 +#define RESP_NACK 0x07 +#define RESP_TIMEOUT 0x09 + +#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */ +#define U2C_I2C_SPEED_STD 1 /* 100 kHz */ +#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(s) (1000000 / (2 * (s - 1) + 10)) + +#define DIOLAN_USB_TIMEOUT 100 /* in ms */ +#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */ + +#define DIOLAN_OUTBUF_LEN 128 +#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4) +#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */ + +/* Structure to hold all of our device specific stuff */ +struct i2c_diolan_u2c { + u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */ + u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */ + int ep_in, ep_out; /* Endpoints */ + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface;/* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ + int olen; /* Output buffer length */ + int ocount; /* Number of enqueued messages */ +}; + +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"); + +/* usb layer */ + +/* Send command to device, and get response. */ +static int diolan_usb_transfer(struct i2c_diolan_u2c *dev) +{ + int ret = 0; + int actual; + int i; + + if (!dev->olen || !dev->ocount) + return -EINVAL; + + ret = usb_bulk_msg(dev->usb_dev, + usb_sndbulkpipe(dev->usb_dev, dev->ep_out), + dev->obuffer, dev->olen, &actual, + DIOLAN_USB_TIMEOUT); + if (!ret) { + for (i = 0; i < dev->ocount; i++) { + int tmpret; + + tmpret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, + dev->ep_in), + dev->ibuffer, + sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + /* + * Stop command processing if a previous command + * returned an error. + * Note that we still need to retrieve all messages. + */ + if (ret < 0) + continue; + ret = tmpret; + if (ret == 0 && actual > 0) { + switch (dev->ibuffer[actual - 1]) { + case RESP_NACK: + /* + * Return ENXIO if NACK was received as + * response to the address phase, + * EIO otherwise + */ + ret = i == 1 ? -ENXIO : -EIO; + break; + case RESP_TIMEOUT: + ret = -ETIMEDOUT; + break; + case RESP_OK: + /* strip off return code */ + ret = actual - 1; + break; + default: + ret = -EIO; + break; + } + } + } + } + dev->olen = 0; + dev->ocount = 0; + return ret; +} + +static int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush) +{ + if (flush || dev->olen >= DIOLAN_FLUSH_LEN) + return diolan_usb_transfer(dev); + return 0; +} + +/* Send command (no data) */ +static int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with one byte of data */ +static int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data, + bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = data; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with two bytes of data */ +static int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1, + u8 d2, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = d1; + dev->obuffer[dev->olen++] = d2; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* + * Flush input queue. + * If we don't do this at startup and the controller has queued up + * messages which were not retrieved, it will stop responding + * at some point. + */ +static void diolan_flush_input(struct i2c_diolan_u2c *dev) +{ + int i; + + for (i = 0; i < 10; i++) { + int actual = 0; + int ret; + + ret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, dev->ep_in), + dev->ibuffer, sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + if (ret < 0 || actual == 0) + break; + } + if (i == 10) + dev_err(&dev->interface->dev, "Failed to flush input buffer\n"); +} + +static int diolan_i2c_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_START, false); +} + +static int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false); +} + +static int diolan_i2c_stop(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_STOP, true); +} + +static int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack, + u8 *byte) +{ + int ret; + + ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true); + if (ret > 0) + *byte = dev->ibuffer[0]; + else if (ret == 0) + ret = -EIO; + + return ret; +} + +static int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false); +} + +static int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true); +} + +/* Enable or disable clock synchronization (stretching) */ +static int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true); +} + +/* Set clock synchronization timeout in ms */ +static int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms) +{ + int to_val = ms * 10; + + return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO, + to_val & 0xff, (to_val >> 8) & 0xff, true); +} + +static void diolan_fw_version(struct i2c_diolan_u2c *dev) +{ + int ret; + + ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true); + if (ret >= 2) + dev_info(&dev->interface->dev, + "Diolan U2C firmware version %u.%u\n", + (unsigned int)dev->ibuffer[0], + (unsigned int)dev->ibuffer[1]); +} + +static void diolan_get_serial(struct i2c_diolan_u2c *dev) +{ + int ret; + u32 serial; + + ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true); + if (ret >= 4) { + serial = le32_to_cpu(*(u32 *)dev->ibuffer); + dev_info(&dev->interface->dev, + "Diolan U2C serial number %u\n", serial); + } +} + +static int diolan_init(struct i2c_diolan_u2c *dev) +{ + int speed, ret; + + if (frequency >= 2 * I2C_MAX_STANDARD_MODE_FREQ) { + speed = U2C_I2C_SPEED_FAST; + frequency = I2C_MAX_FAST_MODE_FREQ; + } else if (frequency >= I2C_MAX_STANDARD_MODE_FREQ || frequency == 0) { + speed = U2C_I2C_SPEED_STD; + frequency = I2C_MAX_STANDARD_MODE_FREQ; + } else { + speed = U2C_I2C_SPEED(frequency); + if (speed > U2C_I2C_SPEED_2KHZ) + speed = U2C_I2C_SPEED_2KHZ; + frequency = U2C_I2C_FREQ(speed); + } + + dev_info(&dev->interface->dev, + "Diolan U2C at USB bus %03d address %03d speed %d Hz\n", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency); + + diolan_flush_input(dev); + diolan_fw_version(dev); + diolan_get_serial(dev); + + /* Set I2C speed */ + ret = diolan_set_speed(dev, speed); + if (ret < 0) + return ret; + + /* Configure I2C clock synchronization */ + ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST); + if (ret < 0) + return ret; + + if (speed != U2C_I2C_SPEED_FAST) + ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT); + + return ret; +} + +/* i2c layer */ + +static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int i, j; + int ret, sret; + + ret = diolan_i2c_start(dev); + if (ret < 0) + return ret; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + if (i) { + ret = diolan_i2c_repeated_start(dev); + if (ret < 0) + goto abort; + } + ret = diolan_i2c_put_byte_ack(dev, + i2c_8bit_addr_from_msg(pmsg)); + if (ret < 0) + goto abort; + if (pmsg->flags & I2C_M_RD) { + for (j = 0; j < pmsg->len; j++) { + u8 byte; + bool ack = j < pmsg->len - 1; + + /* + * Don't send NACK if this is the first byte + * of a SMBUS_BLOCK message. + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) + ack = true; + + ret = diolan_i2c_get_byte_ack(dev, ack, &byte); + if (ret < 0) + goto abort; + /* + * Adjust count if first received byte is length + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) { + if (byte == 0 + || byte > I2C_SMBUS_BLOCK_MAX) { + ret = -EPROTO; + goto abort; + } + pmsg->len += byte; + } + pmsg->buf[j] = byte; + } + } else { + for (j = 0; j < pmsg->len; j++) { + ret = diolan_i2c_put_byte_ack(dev, + pmsg->buf[j]); + if (ret < 0) + goto abort; + } + } + } + ret = num; +abort: + sret = diolan_i2c_stop(dev); + if (sret < 0 && ret >= 0) + ret = sret; + return ret; +} + +/* + * Return list of supported functionality. + */ +static u32 diolan_usb_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm diolan_usb_algorithm = { + .xfer = diolan_usb_xfer, + .functionality = diolan_usb_func, +}; + +/* device layer */ + +static const struct usb_device_id diolan_u2c_table[] = { + { USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, diolan_u2c_table); + +static int diolan_u2c_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *hostif = interface->cur_altsetting; + struct i2c_diolan_u2c *dev; + int ret; + + if (hostif->desc.bInterfaceNumber != 0 + || hostif->desc.bNumEndpoints < 2) + return -ENODEV; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc_obj(*dev); + if (dev == NULL) { + ret = -ENOMEM; + goto error; + } + dev->ep_out = hostif->endpoint[0].desc.bEndpointAddress; + dev->ep_in = hostif->endpoint[1].desc.bEndpointAddress; + + dev->usb_dev = interface_to_usbdev(interface); + dev->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* setup i2c adapter description */ + dev->adapter.owner = THIS_MODULE; + dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.algo = &diolan_usb_algorithm; + i2c_set_adapdata(&dev->adapter, dev); + snprintf(dev->adapter.name, sizeof(dev->adapter.name), + DRIVER_NAME " at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + dev->adapter.dev.parent = &dev->interface->dev; + + /* initialize diolan i2c interface */ + ret = diolan_init(dev); + if (ret < 0) { + dev_err(&interface->dev, "failed to initialize adapter\n"); + goto error_free; + } + + /* and finally attach to i2c layer */ + ret = i2c_add_adapter(&dev->adapter); + if (ret < 0) + goto error_free; + + dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); + + return 0; + +error_free: + usb_set_intfdata(interface, NULL); + kfree(dev); +error: + return ret; +} + +static void diolan_u2c_disconnect(struct usb_interface *interface) +{ + struct i2c_diolan_u2c *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + kfree(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver diolan_u2c_driver = { + .name = DRIVER_NAME, + .probe = diolan_u2c_probe, + .disconnect = diolan_u2c_disconnect, + .id_table = diolan_u2c_table, +}; + +module_usb_driver(diolan_u2c_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION(DRIVER_NAME " driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000..bde2ef098 --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Diolan DLN-2 USB-I2C adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + * i2c-diolan-u2c.c + * Copyright (c) 2010-2011 Ericsson AB + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DLN2_I2C_MODULE_ID 0x03 +#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) + +/* I2C commands */ +#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) +#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) +#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) +#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) +#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) +#define DLN2_I2C_READ DLN2_I2C_CMD(0x07) +#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) +#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) +#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) +#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) +#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) +#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) +#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) + +#define DLN2_I2C_MAX_XFER_SIZE 256 +#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) + +struct dln2_i2c { + struct platform_device *pdev; + struct i2c_adapter adapter; + u8 port; + /* + * Buffer to hold the packet for read or write transfers. One is enough + * since we can't have multiple transfers in parallel on the i2c bus. + */ + void *buf; +}; + +static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) +{ + u16 cmd; + struct { + u8 port; + } tx; + + tx.port = dln2->port; + + if (enable) + cmd = DLN2_I2C_ENABLE; + else + cmd = DLN2_I2C_DISABLE; + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, + u8 *data, u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *tx = dln2->buf; + unsigned len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); + + tx->port = dln2->port; + tx->addr = addr; + tx->mem_addr_len = 0; + tx->mem_addr = 0; + tx->buf_len = cpu_to_le16(data_len); + memcpy(tx->buf, data, data_len); + + len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; + ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); + if (ret < 0) + return ret; + + return data_len; +} + +static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, + u16 data_len) +{ + int ret; + struct { + u8 port; + u8 addr; + u8 mem_addr_len; + __le32 mem_addr; + __le16 buf_len; + } __packed tx; + struct { + __le16 buf_len; + u8 buf[DLN2_I2C_MAX_XFER_SIZE]; + } __packed *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + + BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); + + tx.port = dln2->port; + tx.addr = addr; + tx.mem_addr_len = 0; + tx.mem_addr = 0; + tx.buf_len = cpu_to_le16(data_len); + + ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->buf_len) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->buf_len) != data_len) + return -EPROTO; + + memcpy(data, rx->buf, data_len); + + return data_len; +} + +static int dln2_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int i; + + for (i = 0; i < num; i++) { + int ret; + + pmsg = &msgs[i]; + + if (pmsg->flags & I2C_M_RD) { + ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret < 0) + return ret; + + pmsg->len = ret; + } else { + ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, + pmsg->len); + if (ret != pmsg->len) + return -EPROTO; + } + } + + return num; +} + +static u32 dln2_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm dln2_i2c_usb_algorithm = { + .xfer = dln2_i2c_xfer, + .functionality = dln2_i2c_func, +}; + +static const struct i2c_adapter_quirks dln2_i2c_quirks = { + .max_read_len = DLN2_I2C_MAX_XFER_SIZE, + .max_write_len = DLN2_I2C_MAX_XFER_SIZE, +}; + +static int dln2_i2c_probe(struct platform_device *pdev) +{ + int ret; + struct dln2_i2c *dln2; + struct device *dev = &pdev->dev; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + + dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); + if (!dln2) + return -ENOMEM; + + dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); + if (!dln2->buf) + return -ENOMEM; + + dln2->pdev = pdev; + dln2->port = pdata->port; + + /* setup i2c adapter description */ + dln2->adapter.owner = THIS_MODULE; + dln2->adapter.class = I2C_CLASS_HWMON; + dln2->adapter.algo = &dln2_i2c_usb_algorithm; + dln2->adapter.quirks = &dln2_i2c_quirks; + dln2->adapter.dev.parent = dev; + ACPI_COMPANION_SET(&dln2->adapter.dev, ACPI_COMPANION(&pdev->dev)); + dln2->adapter.dev.of_node = dev->of_node; + i2c_set_adapdata(&dln2->adapter, dln2); + snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", + "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); + + platform_set_drvdata(pdev, dln2); + + /* initialize the i2c interface */ + ret = dln2_i2c_enable(dln2, true); + 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); + if (ret < 0) + goto out_disable; + + return 0; + +out_disable: + dln2_i2c_enable(dln2, false); + + return ret; +} + +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); +} + +static struct platform_driver dln2_i2c_driver = { + .driver.name = "dln2-i2c", + .probe = dln2_i2c_probe, + .remove = dln2_i2c_remove, +}; + +module_platform_driver(dln2_i2c_driver); + +MODULE_AUTHOR("Laurentiu Palcu "); +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-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c new file mode 100644 index 000000000..8c67ab4f2 --- /dev/null +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCH_EVENT_SET 0 /* I2C Interrupt Event Set Status */ +#define PCH_EVENT_NONE 1 /* I2C Interrupt Event Clear Status */ +#define PCH_MAX_CLK 100000 /* Maximum Clock speed in MHz */ +#define PCH_BUFFER_MODE_ENABLE 0x0002 /* flag for Buffer mode enable */ +#define PCH_EEPROM_SW_RST_MODE_ENABLE 0x0008 /* EEPROM SW RST enable flag */ + +#define PCH_I2CSADR 0x00 /* I2C slave address register */ +#define PCH_I2CCTL 0x04 /* I2C control register */ +#define PCH_I2CSR 0x08 /* I2C status register */ +#define PCH_I2CDR 0x0C /* I2C data register */ +#define PCH_I2CMON 0x10 /* I2C bus monitor register */ +#define PCH_I2CBC 0x14 /* I2C bus transfer rate setup counter */ +#define PCH_I2CMOD 0x18 /* I2C mode register */ +#define PCH_I2CBUFSLV 0x1C /* I2C buffer mode slave address register */ +#define PCH_I2CBUFSUB 0x20 /* I2C buffer mode subaddress register */ +#define PCH_I2CBUFFOR 0x24 /* I2C buffer mode format register */ +#define PCH_I2CBUFCTL 0x28 /* I2C buffer mode control register */ +#define PCH_I2CBUFMSK 0x2C /* I2C buffer mode interrupt mask register */ +#define PCH_I2CBUFSTA 0x30 /* I2C buffer mode status register */ +#define PCH_I2CBUFLEV 0x34 /* I2C buffer mode level register */ +#define PCH_I2CESRFOR 0x38 /* EEPROM software reset mode format register */ +#define PCH_I2CESRCTL 0x3C /* EEPROM software reset mode ctrl register */ +#define PCH_I2CESRMSK 0x40 /* EEPROM software reset mode */ +#define PCH_I2CESRSTA 0x44 /* EEPROM software reset mode status register */ +#define PCH_I2CTMR 0x48 /* I2C timer register */ +#define PCH_I2CSRST 0xFC /* I2C reset register */ +#define PCH_I2CNF 0xF8 /* I2C noise filter register */ + +#define BUS_IDLE_TIMEOUT 20 +#define PCH_I2CCTL_I2CMEN 0x0080 +#define PCH_START 0x0020 +#define PCH_RESTART 0x0004 +#define PCH_ESR_START 0x0001 +#define PCH_BUFF_START 0x1 +#define PCH_REPSTART 0x0004 +#define PCH_ACK 0x0008 +#define PCH_GETACK 0x0001 +#define CLR_REG 0x0 +#define I2CMCF_BIT 0x0080 +#define I2CMIF_BIT 0x0002 +#define I2CMAL_BIT 0x0010 +#define I2CBMFI_BIT 0x0001 +#define I2CBMAL_BIT 0x0002 +#define I2CBMNA_BIT 0x0004 +#define I2CBMTO_BIT 0x0008 +#define I2CBMIS_BIT 0x0010 +#define I2CESRFI_BIT 0X0001 +#define I2CESRTO_BIT 0x0002 +#define I2CESRFIIE_BIT 0x1 +#define I2CESRTOIE_BIT 0x2 +#define I2CBMDZ_BIT 0x0040 +#define I2CBMAG_BIT 0x0020 +#define I2CMBB_BIT 0x0020 +#define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \ + I2CBMTO_BIT | I2CBMIS_BIT) +#define FAST_MODE_CLK 400 +#define FAST_MODE_EN 0x0001 +#define SUB_ADDR_LEN_MAX 4 +#define BUF_LEN_MAX 32 +#define PCH_BUFFER_MODE 0x1 +#define EEPROM_SW_RST_MODE 0x0002 +#define NORMAL_INTR_ENBL 0x0300 +#define EEPROM_RST_INTR_ENBL (I2CESRFIIE_BIT | I2CESRTOIE_BIT) +#define EEPROM_RST_INTR_DISBL 0x0 +#define BUFFER_MODE_INTR_ENBL 0x001F +#define BUFFER_MODE_INTR_DISBL 0x0 +#define NORMAL_MODE 0x0 +#define BUFFER_MODE 0x1 +#define EEPROM_SR_MODE 0x2 +#define I2C_TX_MODE 0x0010 +#define PCH_BUF_TX 0xFFF7 +#define PCH_BUF_RD 0x0008 +#define I2C_ERROR_MASK (I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \ + I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT) +#define I2CMAL_EVENT 0x0001 +#define I2CMCF_EVENT 0x0002 +#define I2CBMFI_EVENT 0x0004 +#define I2CBMAL_EVENT 0x0008 +#define I2CBMNA_EVENT 0x0010 +#define I2CBMTO_EVENT 0x0020 +#define I2CBMIS_EVENT 0x0040 +#define I2CESRFI_EVENT 0x0080 +#define I2CESRTO_EVENT 0x0100 +#define PCI_DEVICE_ID_PCH_I2C 0x8817 + +#define pch_dbg(adap, fmt, arg...) \ + dev_dbg(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) + +#define pch_err(adap, fmt, arg...) \ + dev_err(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) + +#define pch_pci_err(pdev, fmt, arg...) \ + dev_err(&pdev->dev, "%s :" fmt, __func__, ##arg) + +#define pch_pci_dbg(pdev, fmt, arg...) \ + dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg) + +/* +Set the number of I2C instance max +Intel EG20T PCH : 1ch +LAPIS Semiconductor ML7213 IOH : 2ch +LAPIS Semiconductor ML7831 IOH : 1ch +*/ +#define PCH_I2C_MAX_DEV 2 + +/** + * struct i2c_algo_pch_data - for I2C driver functionalities + * @pch_adapter: stores the reference to i2c_adapter structure + * @p_adapter_info: stores the reference to adapter_info structure + * @pch_base_address: specifies the remapped base address + * @pch_buff_mode_en: specifies if buffer mode is enabled + * @pch_event_flag: specifies occurrence of interrupt events + * @pch_i2c_xfer_in_progress: specifies whether the transfer is completed + */ +struct i2c_algo_pch_data { + struct i2c_adapter pch_adapter; + struct adapter_info *p_adapter_info; + void __iomem *pch_base_address; + int pch_buff_mode_en; + u32 pch_event_flag; + bool pch_i2c_xfer_in_progress; +}; + +/** + * struct adapter_info - This structure holds the adapter information for the + * 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. + * @ch_num: specifies the number of i2c instance + * + * pch_data has as many elements as maximum I2C channels + */ +struct adapter_info { + struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV]; + bool pch_i2c_suspended; + int ch_num; +}; + + +static int pch_i2c_speed = 100; /* I2C bus speed in Kbps */ +static int pch_clk = 50000; /* specifies I2C clock speed in KHz */ +static wait_queue_head_t pch_event; +static DEFINE_MUTEX(pch_mutex); + +/* Definition for ML7213 by LAPIS Semiconductor */ +#define PCI_DEVICE_ID_ML7213_I2C 0x802D +#define PCI_DEVICE_ID_ML7223_I2C 0x8010 +#define PCI_DEVICE_ID_ML7831_I2C 0x8817 + +static const struct pci_device_id pch_pcidev_id[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), .driver_data = 2 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), .driver_data = 1 }, + { } +}; +MODULE_DEVICE_TABLE(pci, pch_pcidev_id); + +static irqreturn_t pch_i2c_handler(int irq, void *pData); + +static inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask) +{ + u32 val; + val = ioread32(addr + offset); + val |= bitmask; + iowrite32(val, addr + offset); +} + +static inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask) +{ + u32 val; + val = ioread32(addr + offset); + val &= (~bitmask); + iowrite32(val, addr + offset); +} + +/** + * pch_i2c_init() - hardware initialization of I2C module + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_init(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + u32 pch_i2cbc; + u32 pch_i2ctmr; + u32 reg_value; + + /* reset I2C controller */ + iowrite32(0x01, p + PCH_I2CSRST); + msleep(20); + iowrite32(0x0, p + PCH_I2CSRST); + + /* Initialize I2C registers */ + iowrite32(0x21, p + PCH_I2CNF); + + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN); + + if (pch_i2c_speed != 400) + pch_i2c_speed = 100; + + reg_value = PCH_I2CCTL_I2CMEN; + if (pch_i2c_speed == FAST_MODE_CLK) { + reg_value |= FAST_MODE_EN; + pch_dbg(adap, "Fast mode enabled\n"); + } + + if (pch_clk > PCH_MAX_CLK) + pch_clk = 62500; + + pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); + /* Set transfer speed in I2CBC */ + iowrite32(pch_i2cbc, p + PCH_I2CBC); + + pch_i2ctmr = (pch_clk) / 8; + iowrite32(pch_i2ctmr, p + PCH_I2CTMR); + + reg_value |= NORMAL_INTR_ENBL; /* Enable interrupts in normal mode */ + iowrite32(reg_value, p + PCH_I2CCTL); + + pch_dbg(adap, + "I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n", + ioread32(p + PCH_I2CCTL), pch_i2cbc, pch_i2ctmr); + + init_waitqueue_head(&pch_event); +} + +/** + * pch_i2c_wait_for_bus_idle() - check the status of bus. + * @adap: Pointer to struct i2c_algo_pch_data. + * @timeout: waiting time counter (ms). + */ +static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, + s32 timeout) +{ + void __iomem *p = adap->pch_base_address; + int schedule = 0; + unsigned long end = jiffies + msecs_to_jiffies(timeout); + + while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) { + if (time_after(jiffies, end)) { + pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + pch_err(adap, "%s: Timeout Error.return%d\n", + __func__, -ETIME); + pch_i2c_init(adap); + + return -ETIME; + } + + if (!schedule) + /* Retry after some usecs */ + udelay(5); + else + /* Wait a bit more without consuming CPU */ + usleep_range(20, 1000); + + schedule = 1; + } + + return 0; +} + +/** + * pch_i2c_start() - Generate I2C start condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + * + * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1. + */ +static void pch_i2c_start(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); +} + +/** + * pch_i2c_stop() - generate stop condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_stop(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + /* clear the start bit */ + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); +} + +static int pch_i2c_wait_for_check_xfer(struct i2c_algo_pch_data *adap) +{ + long ret; + void __iomem *p = adap->pch_base_address; + + ret = wait_event_timeout(pch_event, + (adap->pch_event_flag != 0), msecs_to_jiffies(1000)); + if (!ret) { + pch_err(adap, "%s:wait-event timeout\n", __func__); + adap->pch_event_flag = 0; + pch_i2c_stop(adap); + pch_i2c_init(adap); + return -ETIMEDOUT; + } + + if (adap->pch_event_flag & I2C_ERROR_MASK) { + pch_err(adap, "Lost Arbitration\n"); + adap->pch_event_flag = 0; + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; + } + + adap->pch_event_flag = 0; + + if (ioread32(p + PCH_I2CSR) & PCH_GETACK) { + pch_dbg(adap, "Receive NACK for slave address setting\n"); + return -ENXIO; + } + + return 0; +} + +/** + * pch_i2c_repstart() - generate repeated start condition in normal mode + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_repstart(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_REPSTART); +} + +/** + * 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. + * @first: specifies whether first message or not. + * 1 for first message otherwise 0. + */ +static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, u32 last, u32 first) +{ + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + u8 *buf; + u32 length; + s32 wrcount; + s32 rtn; + void __iomem *p = adap->pch_base_address; + + length = msgs->len; + buf = msgs->buf; + + /* enable master tx */ + pch_setbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); + + pch_dbg(adap, "I2CCTL = %x msgs->len = %d\n", ioread32(p + PCH_I2CCTL), + length); + + if (first) { + if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) + return -ETIME; + } + + if (msgs->flags & I2C_M_TEN) { + iowrite32(i2c_10bit_addr_hi_from_msg(msgs), p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + 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); + if (first) + pch_i2c_start(adap); + } + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + for (wrcount = 0; wrcount < length; ++wrcount) { + /* write buffer value to I2C data register */ + iowrite32(buf[wrcount], p + PCH_I2CDR); + pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMCF_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); + } + + /* check if this is the last message */ + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + + pch_dbg(adap, "return=%d\n", wrcount); + + return wrcount; +} + +/** + * pch_i2c_sendack() - send ACK + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_sendack(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); +} + +/** + * pch_i2c_sendnack() - send NACK + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); +} + +/** + * pch_i2c_restart() - Generate I2C restart condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + * + * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA. + */ +static void pch_i2c_restart(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART); +} + +/** + * pch_i2c_readbytes() - read data from I2C bus in normal mode. + * @i2c_adap: Pointer to the struct i2c_adapter. + * @msgs: Pointer to i2c_msg structure. + * @last: specifies whether last message or not. + * @first: specifies whether first message or not. + */ +static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + u32 last, u32 first) +{ + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + + u8 *buf; + u32 count; + u32 length; + void __iomem *p = adap->pch_base_address; + s32 rtn; + + length = msgs->len; + buf = msgs->buf; + + /* enable master reception */ + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); + + if (first) { + if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) + return -ETIME; + } + + if (msgs->flags & I2C_M_TEN) { + iowrite32(i2c_10bit_addr_hi_from_msg(msgs) & ~I2C_M_RD, p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + iowrite32(i2c_10bit_addr_lo_from_msg(msgs), p + PCH_I2CDR); + + pch_i2c_restart(adap); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + 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); + } + + /* check if it is the first message */ + if (first) + pch_i2c_start(adap); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + if (length == 0) { + pch_i2c_stop(adap); + ioread32(p + PCH_I2CDR); /* Dummy read needs */ + + count = length; + } else { + int read_index; + int loop; + pch_i2c_sendack(adap); + + /* Dummy read */ + for (loop = 1, read_index = 0; loop < length; loop++) { + buf[read_index] = ioread32(p + PCH_I2CDR); + + if (loop != 1) + read_index++; + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + } /* end for */ + + pch_i2c_sendnack(adap); + + buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */ + + if (length != 1) + read_index++; + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + + buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */ + count = read_index; + } + + return count; +} + +/** + * pch_i2c_cb() - Interrupt handler Call back function + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_cb(struct i2c_algo_pch_data *adap) +{ + u32 sts; + void __iomem *p = adap->pch_base_address; + + sts = ioread32(p + PCH_I2CSR); + sts &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT); + if (sts & I2CMAL_BIT) + adap->pch_event_flag |= I2CMAL_EVENT; + + if (sts & I2CMCF_BIT) + adap->pch_event_flag |= I2CMCF_EVENT; + + /* clear the applicable bits */ + pch_clrbit(adap->pch_base_address, PCH_I2CSR, sts); + + pch_dbg(adap, "PCH_I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + + wake_up(&pch_event); +} + +/** + * pch_i2c_handler() - interrupt handler for the PCH I2C controller + * @irq: irq number. + * @pData: cookie passed back to the handler function. + */ +static irqreturn_t pch_i2c_handler(int irq, void *pData) +{ + u32 reg_val; + int flag; + int i; + struct adapter_info *adap_info = pData; + void __iomem *p; + u32 mode; + + for (i = 0, flag = 0; i < adap_info->ch_num; i++) { + p = adap_info->pch_data[i].pch_base_address; + mode = ioread32(p + PCH_I2CMOD); + mode &= BUFFER_MODE | EEPROM_SR_MODE; + if (mode != NORMAL_MODE) { + pch_err(adap_info->pch_data, + "I2C-%d mode(%d) is not supported\n", mode, i); + continue; + } + reg_val = ioread32(p + PCH_I2CSR); + if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) { + pch_i2c_cb(&adap_info->pch_data[i]); + flag = 1; + } + } + + return flag ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * pch_i2c_xfer() - Reading adnd writing data through I2C bus + * @i2c_adap: Pointer to the struct i2c_adapter. + * @msgs: Pointer to i2c_msg structure. + * @num: number of messages. + */ +static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, s32 num) +{ + struct i2c_msg *pmsg; + u32 i = 0; + u32 status; + s32 ret; + + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + + ret = mutex_lock_interruptible(&pch_mutex); + if (ret) + return ret; + + if (adap->p_adapter_info->pch_i2c_suspended) { + mutex_unlock(&pch_mutex); + return -EBUSY; + } + + pch_dbg(adap, "adap->p_adapter_info->pch_i2c_suspended is %d\n", + adap->p_adapter_info->pch_i2c_suspended); + /* transfer not completed */ + adap->pch_i2c_xfer_in_progress = true; + + for (i = 0; i < num && ret >= 0; i++) { + pmsg = &msgs[i]; + pmsg->flags |= adap->pch_buff_mode_en; + status = pmsg->flags; + pch_dbg(adap, + "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); + + if ((status & (I2C_M_RD)) != false) { + ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } else { + ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } + } + + adap->pch_i2c_xfer_in_progress = false; /* transfer completed */ + + mutex_unlock(&pch_mutex); + + return (ret < 0) ? ret : num; +} + +/** + * pch_i2c_func() - return the functionality of the I2C driver + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static u32 pch_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm pch_algorithm = { + .xfer = pch_i2c_xfer, + .functionality = pch_i2c_func +}; + +/** + * pch_i2c_disbl_int() - Disable PCH I2C interrupts + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, NORMAL_INTR_ENBL); + + iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK); + + iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK); +} + +static int pch_i2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *base_addr; + int ret; + int i, j; + struct adapter_info *adap_info; + struct i2c_adapter *pch_adap; + + pch_pci_dbg(pdev, "Entered.\n"); + + adap_info = kzalloc_obj(struct adapter_info); + if (adap_info == NULL) + return -ENOMEM; + + ret = pci_enable_device(pdev); + if (ret) { + pch_pci_err(pdev, "pci_enable_device FAILED\n"); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + pch_pci_err(pdev, "pci_request_regions FAILED\n"); + goto err_pci_req; + } + + base_addr = pci_iomap(pdev, 1, 0); + + if (base_addr == NULL) { + pch_pci_err(pdev, "pci_iomap FAILED\n"); + ret = -ENOMEM; + goto err_pci_iomap; + } + + /* Set the number of I2C channel instance */ + adap_info->ch_num = id->driver_data; + + for (i = 0; i < adap_info->ch_num; i++) { + pch_adap = &adap_info->pch_data[i].pch_adapter; + adap_info->pch_i2c_suspended = false; + + adap_info->pch_data[i].p_adapter_info = adap_info; + + pch_adap->owner = THIS_MODULE; + pch_adap->class = I2C_CLASS_HWMON; + strscpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name)); + pch_adap->algo = &pch_algorithm; + pch_adap->algo_data = &adap_info->pch_data[i]; + + /* base_addr + offset; */ + adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i; + + pch_adap->dev.of_node = pdev->dev.of_node; + pch_adap->dev.parent = &pdev->dev; + } + + ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, + KBUILD_MODNAME, adap_info); + if (ret) { + pch_pci_err(pdev, "request_irq FAILED\n"); + goto err_request_irq; + } + + for (i = 0; i < adap_info->ch_num; i++) { + pch_adap = &adap_info->pch_data[i].pch_adapter; + + pch_i2c_init(&adap_info->pch_data[i]); + + pch_adap->nr = i; + ret = i2c_add_numbered_adapter(pch_adap); + if (ret) { + pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); + goto err_add_adapter; + } + } + + pci_set_drvdata(pdev, adap_info); + pch_pci_dbg(pdev, "returns %d.\n", ret); + return 0; + +err_add_adapter: + for (j = 0; j < i; j++) + i2c_del_adapter(&adap_info->pch_data[j].pch_adapter); + free_irq(pdev->irq, adap_info); +err_request_irq: + pci_iounmap(pdev, base_addr); +err_pci_iomap: + pci_release_regions(pdev); +err_pci_req: + pci_disable_device(pdev); +err_pci_enable: + kfree(adap_info); + return ret; +} + +static void pch_i2c_remove(struct pci_dev *pdev) +{ + int i; + struct adapter_info *adap_info = pci_get_drvdata(pdev); + + free_irq(pdev->irq, adap_info); + + for (i = 0; i < adap_info->ch_num; i++) { + pch_i2c_disbl_int(&adap_info->pch_data[i]); + i2c_del_adapter(&adap_info->pch_data[i].pch_adapter); + } + + if (adap_info->pch_data[0].pch_base_address) + pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address); + + for (i = 0; i < adap_info->ch_num; i++) + adap_info->pch_data[i].pch_base_address = NULL; + + pci_release_regions(pdev); + + pci_disable_device(pdev); + kfree(adap_info); +} + +static int __maybe_unused pch_i2c_suspend(struct device *dev) +{ + 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; + + adap_info->pch_i2c_suspended = true; + + for (i = 0; i < adap_info->ch_num; i++) { + while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) { + /* Wait until all channel transfers are completed */ + msleep(20); + } + } + + /* Disable the i2c interrupts */ + for (i = 0; i < adap_info->ch_num; i++) + pch_i2c_disbl_int(&adap_info->pch_data[i]); + + pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x " + "invoked function pch_i2c_disbl_int successfully\n", + ioread32(p + PCH_I2CSR), ioread32(p + PCH_I2CBUFSTA), + ioread32(p + PCH_I2CESRSTA)); + + return 0; +} + +static int __maybe_unused pch_i2c_resume(struct device *dev) +{ + int i; + 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]); + + adap_info->pch_i2c_suspended = false; + + return 0; +} + +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, + .driver.pm = &pch_i2c_pm_ops, +}; + +module_pci_driver(pch_pcidriver); + +MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomoya MORINAGA. "); +module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); +module_param(pch_clk, int, (S_IRUSR | S_IWUSR)); diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c new file mode 100644 index 000000000..cfe8665ca --- /dev/null +++ b/drivers/i2c/busses/i2c-elektor.c @@ -0,0 +1,321 @@ +// 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 + + */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* Partially rewriten by Oleg I. Vdovikin for mmapped support of + for Alpha Processor Inc. UP-2000(+) boards */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../algos/i2c-algo-pcf.h" + +#define DEFAULT_BASE 0x330 + +static int base; +static u8 __iomem *base_iomem; + +static int irq; +static int clock = 0x1c; +static int own = 0x55; +static int mmapped; + +/* vdovikin: removed static struct i2c_pcf_isa gpi; code - + this module in real supports only one device, due to missing arguments + in some functions, called from the algo-pcf module. Sometimes it's + need to be rewriten - but for now just remove this for simpler reading */ + +static wait_queue_head_t pcf_wait; +static int pcf_pending; +static DEFINE_SPINLOCK(lock); + +static struct i2c_adapter pcf_isa_ops; + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; + + /* enable irq if any specified for serial operation */ + if (ctl && irq && (val & I2C_PCF_ESO)) { + val |= I2C_PCF_ENI; + } + + pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val); + iowrite8(val, address); +#ifdef __alpha__ + /* API UP2000 needs some hardware fudging to make the write stick */ + iowrite8(val, address); +#endif +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; + int val = ioread8(address); + + pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val); + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (clock); +} + +static void pcf_isa_waitforpin(void *data) +{ + DEFINE_WAIT(wait); + int timeout = 2; + unsigned long flags; + + if (irq > 0) { + spin_lock_irqsave(&lock, flags); + if (pcf_pending == 0) { + spin_unlock_irqrestore(&lock, flags); + prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); + if (schedule_timeout(timeout*HZ)) { + spin_lock_irqsave(&lock, flags); + if (pcf_pending == 1) { + pcf_pending = 0; + } + spin_unlock_irqrestore(&lock, flags); + } + finish_wait(&pcf_wait, &wait); + } else { + pcf_pending = 0; + spin_unlock_irqrestore(&lock, flags); + } + } else { + udelay(100); + } +} + + +static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) { + spin_lock(&lock); + pcf_pending = 1; + spin_unlock(&lock); + wake_up_interruptible(&pcf_wait); + return IRQ_HANDLED; +} + + +static int pcf_isa_init(void) +{ + if (!mmapped) { + if (!request_region(base, 2, pcf_isa_ops.name)) { + printk(KERN_ERR "%s: requested I/O region (%#x:2) is " + "in use\n", pcf_isa_ops.name, base); + return -ENODEV; + } + base_iomem = ioport_map(base, 2); + if (!base_iomem) { + printk(KERN_ERR "%s: remap of I/O region %#x failed\n", + pcf_isa_ops.name, base); + release_region(base, 2); + return -ENODEV; + } + } else { + if (!request_mem_region(base, 2, pcf_isa_ops.name)) { + printk(KERN_ERR "%s: requested memory region (%#x:2) " + "is in use\n", pcf_isa_ops.name, base); + return -ENODEV; + } + base_iomem = ioremap(base, 2); + if (base_iomem == NULL) { + printk(KERN_ERR "%s: remap of memory region %#x " + "failed\n", pcf_isa_ops.name, base); + release_mem_region(base, 2); + return -ENODEV; + } + } + pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base, + base_iomem); + + if (irq > 0) { + if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name, + NULL) < 0) { + printk(KERN_ERR "%s: Request irq%d failed\n", + pcf_isa_ops.name, irq); + irq = 0; + } else + enable_irq(irq); + } + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + .setpcf = pcf_isa_setbyte, + .getpcf = pcf_isa_getbyte, + .getown = pcf_isa_getown, + .getclock = pcf_isa_getclock, + .waitforpin = pcf_isa_waitforpin, +}; + +static struct i2c_adapter pcf_isa_ops = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo_data = &pcf_isa_data, + .name = "i2c-elektor", +}; + +static int elektor_match(struct device *dev, unsigned int id) +{ +#ifdef __alpha__ + /* check to see we have memory mapped PCF8584 connected to the + Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ + if (base == 0) { + struct pci_dev *cy693_dev; + + cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ, + PCI_DEVICE_ID_CONTAQ_82C693, NULL); + if (cy693_dev) { + unsigned char config; + /* yeap, we've found cypress, let's check config */ + if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { + + dev_dbg(dev, "found cy82c693, config " + "register 0x47 = 0x%02x\n", config); + + /* UP2000 board has this register set to 0xe1, + but the most significant bit as seems can be + reset during the proper initialisation + sequence if guys from API decides to do that + (so, we can even enable Tsunami Pchip + window for the upper 1 Gb) */ + + /* so just check for ROMCS at 0xe0000, + ROMCS enabled for writes + and external XD Bus buffer in use. */ + if ((config & 0x7f) == 0x61) { + /* seems to be UP2000 like board */ + base = 0xe0000; + mmapped = 1; + /* UP2000 drives ISA with + 8.25 MHz (PCI/4) clock + (this can be read from cypress) */ + clock = I2C_PCF_CLK | I2C_PCF_TRNS90; + dev_info(dev, "found API UP2000 like " + "board, will probe PCF8584 " + "later\n"); + } + } + pci_dev_put(cy693_dev); + } + } +#endif + + /* sanity checks for mmapped I/O */ + if (mmapped && base < 0xc8000) { + dev_err(dev, "incorrect base address (%#x) specified " + "for mmapped I/O\n", base); + return 0; + } + + if (base == 0) { + base = DEFAULT_BASE; + } + return 1; +} + +static int elektor_probe(struct device *dev, unsigned int id) +{ + init_waitqueue_head(&pcf_wait); + if (pcf_isa_init()) + return -ENODEV; + pcf_isa_ops.dev.parent = dev; + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + goto fail; + + dev_info(dev, "found device at %#x\n", base); + + return 0; + + fail: + if (irq > 0) { + disable_irq(irq); + free_irq(irq, NULL); + } + + if (!mmapped) { + ioport_unmap(base_iomem); + release_region(base, 2); + } else { + iounmap(base_iomem); + release_mem_region(base, 2); + } + return -ENODEV; +} + +static void elektor_remove(struct device *dev, unsigned int id) +{ + i2c_del_adapter(&pcf_isa_ops); + + if (irq > 0) { + disable_irq(irq); + free_irq(irq, NULL); + } + + if (!mmapped) { + ioport_unmap(base_iomem); + release_region(base, 2); + } else { + iounmap(base_iomem); + release_mem_region(base, 2); + } +} + +static struct isa_driver i2c_elektor_driver = { + .match = elektor_match, + .probe = elektor_probe, + .remove = elektor_remove, + .driver = { + .owner = THIS_MODULE, + .name = "i2c-elektor", + }, +}; + +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); +MODULE_LICENSE("GPL"); + +module_param_hw(base, int, ioport_or_iomem, 0); +module_param_hw(irq, int, irq, 0); +module_param(clock, int, 0); +module_param(own, int, 0); +module_param_hw(mmapped, int, other, 0); +module_isa_driver(i2c_elektor_driver, 1); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c new file mode 100644 index 000000000..ece019b3d --- /dev/null +++ b/drivers/i2c/busses/i2c-emev2.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C driver for the Renesas EMEV2 SoC + * + * Copyright (C) 2015 Wolfram Sang + * Copyright 2013 Codethink Ltd. + * Copyright 2010-2015 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C Registers */ +#define I2C_OFS_IICACT0 0x00 /* start */ +#define I2C_OFS_IIC0 0x04 /* shift */ +#define I2C_OFS_IICC0 0x08 /* control */ +#define I2C_OFS_SVA0 0x0c /* slave address */ +#define I2C_OFS_IICCL0 0x10 /* clock select */ +#define I2C_OFS_IICX0 0x14 /* extension */ +#define I2C_OFS_IICS0 0x18 /* status */ +#define I2C_OFS_IICSE0 0x1c /* status For emulation */ +#define I2C_OFS_IICF0 0x20 /* IIC flag */ + +/* I2C IICACT0 Masks */ +#define I2C_BIT_IICE0 0x0001 + +/* I2C IICC0 Masks */ +#define I2C_BIT_LREL0 0x0040 +#define I2C_BIT_WREL0 0x0020 +#define I2C_BIT_SPIE0 0x0010 +#define I2C_BIT_WTIM0 0x0008 +#define I2C_BIT_ACKE0 0x0004 +#define I2C_BIT_STT0 0x0002 +#define I2C_BIT_SPT0 0x0001 + +/* I2C IICCL0 Masks */ +#define I2C_BIT_SMC0 0x0008 +#define I2C_BIT_DFC0 0x0004 + +/* I2C IICSE0 Masks */ +#define I2C_BIT_MSTS0 0x0080 +#define I2C_BIT_ALD0 0x0040 +#define I2C_BIT_EXC0 0x0020 +#define I2C_BIT_COI0 0x0010 +#define I2C_BIT_TRC0 0x0008 +#define I2C_BIT_ACKD0 0x0004 +#define I2C_BIT_STD0 0x0002 +#define I2C_BIT_SPD0 0x0001 + +/* I2C IICF0 Masks */ +#define I2C_BIT_STCF 0x0080 +#define I2C_BIT_IICBSY 0x0040 +#define I2C_BIT_STCEN 0x0002 +#define I2C_BIT_IICRSV 0x0001 + +struct em_i2c_device { + void __iomem *base; + struct i2c_adapter adap; + struct completion msg_done; + struct i2c_client *slave; + int irq; +}; + +static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg) +{ + writeb((readb(priv->base + reg) & ~clear) | set, priv->base + reg); +} + +static int em_i2c_wait_for_event(struct em_i2c_device *priv) +{ + unsigned long time_left; + int status; + + reinit_completion(&priv->msg_done); + + time_left = wait_for_completion_timeout(&priv->msg_done, priv->adap.timeout); + + if (!time_left) + return -ETIMEDOUT; + + status = readb(priv->base + I2C_OFS_IICSE0); + return status & I2C_BIT_ALD0 ? -EAGAIN : status; +} + +static void em_i2c_stop(struct em_i2c_device *priv) +{ + /* Send Stop condition */ + em_clear_set_bit(priv, 0, I2C_BIT_SPT0 | I2C_BIT_SPIE0, I2C_OFS_IICC0); + + /* Wait for stop condition */ + em_i2c_wait_for_event(priv); +} + +static void em_i2c_reset(struct i2c_adapter *adap) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int retr; + + /* If I2C active */ + if (readb(priv->base + I2C_OFS_IICACT0) & I2C_BIT_IICE0) { + /* Disable I2C operation */ + writeb(0, priv->base + I2C_OFS_IICACT0); + + retr = 1000; + while (readb(priv->base + I2C_OFS_IICACT0) == 1 && retr) + retr--; + WARN_ON(retr == 0); + } + + /* Transfer mode set */ + writeb(I2C_BIT_DFC0, priv->base + I2C_OFS_IICCL0); + + /* Can Issue start without detecting a stop, Reservation disabled. */ + writeb(I2C_BIT_STCEN | I2C_BIT_IICRSV, priv->base + I2C_OFS_IICF0); + + /* I2C enable, 9 bit interrupt mode */ + writeb(I2C_BIT_WTIM0, priv->base + I2C_OFS_IICC0); + + /* Enable I2C operation */ + writeb(I2C_BIT_IICE0, priv->base + I2C_OFS_IICACT0); + + retr = 1000; + while (readb(priv->base + I2C_OFS_IICACT0) == 0 && retr) + retr--; + WARN_ON(retr == 0); +} + +static int __em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int stop) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int count, status, read = !!(msg->flags & I2C_M_RD); + + /* Send start condition */ + em_clear_set_bit(priv, 0, I2C_BIT_ACKE0 | I2C_BIT_WTIM0, I2C_OFS_IICC0); + em_clear_set_bit(priv, 0, I2C_BIT_STT0, I2C_OFS_IICC0); + + /* Send slave address and R/W type */ + writeb(i2c_8bit_addr_from_msg(msg), priv->base + I2C_OFS_IIC0); + + /* Wait for transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + + /* Received NACK (result of setting slave address and R/W) */ + if (!(status & I2C_BIT_ACKD0)) { + em_i2c_stop(priv); + goto out; + } + + /* Extra setup for read transactions */ + if (read) { + /* 8 bit interrupt mode */ + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0, I2C_OFS_IICC0); + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0, I2C_OFS_IICC0); + + /* Wait for transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + } + + /* Send / receive data */ + for (count = 0; count < msg->len; count++) { + if (read) { /* Read transaction */ + msg->buf[count] = readb(priv->base + I2C_OFS_IIC0); + em_clear_set_bit(priv, 0, I2C_BIT_WREL0, I2C_OFS_IICC0); + + } else { /* Write transaction */ + /* Received NACK */ + if (!(status & I2C_BIT_ACKD0)) { + em_i2c_stop(priv); + goto out; + } + + /* Write data */ + writeb(msg->buf[count], priv->base + I2C_OFS_IIC0); + } + + /* Wait for R/W transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + } + + if (stop) + em_i2c_stop(priv); + + return count; + +out_reset: + em_i2c_reset(adap); +out: + return status < 0 ? status : -ENXIO; +} + +static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int ret, i; + + if (readb(priv->base + I2C_OFS_IICF0) & I2C_BIT_IICBSY) + return -EAGAIN; + + for (i = 0; i < num; i++) { + ret = __em_i2c_xfer(adap, &msgs[i], (i == (num - 1))); + if (ret < 0) + return ret; + } + + /* I2C transfer completed */ + return num; +} + +static bool em_i2c_slave_irq(struct em_i2c_device *priv) +{ + u8 status, value; + enum i2c_slave_event event; + int ret; + + if (!priv->slave) + return false; + + status = readb(priv->base + I2C_OFS_IICSE0); + + /* Extension code, do not participate */ + if (status & I2C_BIT_EXC0) { + em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0); + return true; + } + + /* Stop detected, we don't know if it's for slave or master */ + if (status & I2C_BIT_SPD0) { + /* Notify slave device */ + i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value); + /* Pretend we did not handle the interrupt */ + return false; + } + + /* Only handle interrupts addressed to us */ + if (!(status & I2C_BIT_COI0)) + return false; + + /* Enable stop interrupts */ + em_clear_set_bit(priv, 0, I2C_BIT_SPIE0, I2C_OFS_IICC0); + + /* Transmission or Reception */ + if (status & I2C_BIT_TRC0) { + if (status & I2C_BIT_ACKD0) { + /* 9 bit interrupt mode */ + em_clear_set_bit(priv, 0, I2C_BIT_WTIM0, I2C_OFS_IICC0); + + /* Send data */ + event = status & I2C_BIT_STD0 ? + I2C_SLAVE_READ_REQUESTED : + I2C_SLAVE_READ_PROCESSED; + i2c_slave_event(priv->slave, event, &value); + writeb(value, priv->base + I2C_OFS_IIC0); + } else { + /* NACK, stop transmitting */ + em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0); + } + } else { + /* 8 bit interrupt mode */ + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0, + I2C_OFS_IICC0); + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0, + I2C_OFS_IICC0); + + if (status & I2C_BIT_STD0) { + i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, + &value); + } else { + /* Recv data */ + value = readb(priv->base + I2C_OFS_IIC0); + ret = i2c_slave_event(priv->slave, + I2C_SLAVE_WRITE_RECEIVED, &value); + if (ret < 0) + em_clear_set_bit(priv, I2C_BIT_ACKE0, 0, + I2C_OFS_IICC0); + } + } + + return true; +} + +static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id) +{ + struct em_i2c_device *priv = dev_id; + + if (em_i2c_slave_irq(priv)) + return IRQ_HANDLED; + + complete(&priv->msg_done); + + return IRQ_HANDLED; +} + +static u32 em_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE; +} + +static int em_i2c_reg_slave(struct i2c_client *slave) +{ + struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter); + + if (priv->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + priv->slave = slave; + + /* Set slave address */ + writeb(slave->addr << 1, priv->base + I2C_OFS_SVA0); + + return 0; +} + +static int em_i2c_unreg_slave(struct i2c_client *slave) +{ + struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter); + + WARN_ON(!priv->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 = { + .xfer = em_i2c_xfer, + .functionality = em_i2c_func, + .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 clk *sclk; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + strscpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name)); + + 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; + priv->adap.dev.parent = &pdev->dev; + priv->adap.algo = &em_i2c_algo; + priv->adap.owner = THIS_MODULE; + priv->adap.dev.of_node = pdev->dev.of_node; + + init_completion(&priv->msg_done); + + platform_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adap, priv); + + em_i2c_reset(&priv->adap); + + 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) + return ret; + + ret = i2c_add_adapter(&priv->adap); + if (ret) + return ret; + + dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, + priv->irq); + + return 0; +} + +static void em_i2c_remove(struct platform_device *dev) +{ + struct em_i2c_device *priv = platform_get_drvdata(dev); + + i2c_del_adapter(&priv->adap); +} + +static const struct of_device_id em_i2c_ids[] = { + { .compatible = "renesas,iic-emev2", }, + { } +}; + +static struct platform_driver em_i2c_driver = { + .probe = em_i2c_probe, + .remove = em_i2c_remove, + .driver = { + .name = "em-i2c", + .of_match_table = em_i2c_ids, + } +}; +module_platform_driver(em_i2c_driver); + +MODULE_DESCRIPTION("EMEV2 I2C bus driver"); +MODULE_AUTHOR("Ian Molton"); +MODULE_AUTHOR("Wolfram Sang "); +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 new file mode 100644 index 000000000..9c1c5f3c0 --- /dev/null +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c-exynos5.c - Samsung Exynos5 I2C Controller Driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * HSI2C controller from Samsung supports 2 modes of operation + * 1. Auto mode: Where in master automatically controls the whole transaction + * 2. Manual mode: Software controls the transaction by issuing commands + * START, READ, WRITE, STOP, RESTART in I2C_MANUAL_CMD register. + * + * Operation mode can be selected by setting AUTO_MODE bit in I2C_CONF register + * + * Special bits are available for both modes of operation to set commands + * and for checking transfer status + */ + +/* Register Map */ +#define HSI2C_CTL 0x00 +#define HSI2C_FIFO_CTL 0x04 +#define HSI2C_TRAILIG_CTL 0x08 +#define HSI2C_CLK_CTL 0x0C +#define HSI2C_CLK_SLOT 0x10 +#define HSI2C_INT_ENABLE 0x20 +#define HSI2C_INT_STATUS 0x24 +#define HSI2C_ERR_STATUS 0x2C +#define HSI2C_FIFO_STATUS 0x30 +#define HSI2C_TX_DATA 0x34 +#define HSI2C_RX_DATA 0x38 +#define HSI2C_CONF 0x40 +#define HSI2C_AUTO_CONF 0x44 +#define HSI2C_TIMEOUT 0x48 +#define HSI2C_MANUAL_CMD 0x4C +#define HSI2C_TRANS_STATUS 0x50 +#define HSI2C_TIMING_HS1 0x54 +#define HSI2C_TIMING_HS2 0x58 +#define HSI2C_TIMING_HS3 0x5C +#define HSI2C_TIMING_FS1 0x60 +#define HSI2C_TIMING_FS2 0x64 +#define HSI2C_TIMING_FS3 0x68 +#define HSI2C_TIMING_SLA 0x6C +#define HSI2C_ADDR 0x70 + +/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) +#define HSI2C_TXCHON (1u << 7) +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_RXFIFO_TRIGGER_LEVEL(x) ((x) << 4) +#define HSI2C_TXFIFO_TRIGGER_LEVEL(x) ((x) << 16) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xf) + +/* I2C_INT_EN Register bits */ +#define HSI2C_INT_TX_ALMOSTEMPTY_EN (1u << 0) +#define HSI2C_INT_RX_ALMOSTFULL_EN (1u << 1) +#define HSI2C_INT_TRAILING_EN (1u << 6) + +/* I2C_INT_STAT Register bits */ +#define HSI2C_INT_TX_ALMOSTEMPTY (1u << 0) +#define HSI2C_INT_RX_ALMOSTFULL (1u << 1) +#define HSI2C_INT_TX_UNDERRUN (1u << 2) +#define HSI2C_INT_TX_OVERRUN (1u << 3) +#define HSI2C_INT_RX_UNDERRUN (1u << 4) +#define HSI2C_INT_RX_OVERRUN (1u << 5) +#define HSI2C_INT_TRAILING (1u << 6) +#define HSI2C_INT_I2C (1u << 9) + +#define HSI2C_INT_TRANS_DONE (1u << 7) +#define HSI2C_INT_TRANS_ABORT (1u << 8) +#define HSI2C_INT_NO_DEV_ACK (1u << 9) +#define HSI2C_INT_NO_DEV (1u << 10) +#define HSI2C_INT_TIMEOUT (1u << 11) +#define HSI2C_INT_I2C_TRANS (HSI2C_INT_TRANS_DONE | \ + HSI2C_INT_TRANS_ABORT | \ + HSI2C_INT_NO_DEV_ACK | \ + HSI2C_INT_NO_DEV | \ + HSI2C_INT_TIMEOUT) + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_RX_FIFO_EMPTY (1u << 24) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_RX_FIFO_LVL(x) ((x >> 16) & 0x7f) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_TX_FIFO_LVL(x) ((x >> 0) & 0x7f) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) +#define HSI2C_TIMEOUT_MASK 0xff + +/* I2C_MANUAL_CMD register bits */ +#define HSI2C_CMD_READ_DATA (1u << 4) +#define HSI2C_CMD_SEND_STOP (1u << 2) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) + +/* I2C_TRANS_STATUS register bits for Exynos5 variant */ +#define HSI2C_TIMEOUT_AUTO (1u << 4) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_DONE (1u << 0) + +/* I2C_TRANS_STATUS register bits for Exynos7 variant */ +#define HSI2C_MASTER_ST_MASK 0xf +#define HSI2C_MASTER_ST_IDLE 0x0 +#define HSI2C_MASTER_ST_START 0x1 +#define HSI2C_MASTER_ST_RESTART 0x2 +#define HSI2C_MASTER_ST_STOP 0x3 +#define HSI2C_MASTER_ST_MASTER_ID 0x4 +#define HSI2C_MASTER_ST_ADDR0 0x5 +#define HSI2C_MASTER_ST_ADDR1 0x6 +#define HSI2C_MASTER_ST_ADDR2 0x7 +#define HSI2C_MASTER_ST_ADDR_SR 0x8 +#define HSI2C_MASTER_ST_READ 0x9 +#define HSI2C_MASTER_ST_WRITE 0xa +#define HSI2C_MASTER_ST_NO_ACK 0xb +#define HSI2C_MASTER_ST_LOSE 0xc +#define HSI2C_MASTER_ST_WAIT 0xd +#define HSI2C_MASTER_ST_WAIT_CMD 0xe + +/* I2C_ADDR register bits */ +#define HSI2C_SLV_ADDR_SLV(x) ((x & 0x3ff) << 0) +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) +#define HSI2C_MASTER_ID(x) ((x & 0xff) << 24) +#define MASTER_ID(x) ((x & 0x7) + 0x08) + +#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; + + struct i2c_msg *msg; + struct completion msg_complete; + unsigned int msg_ptr; + + unsigned int irq; + + void __iomem *regs; + struct clk *clk; /* operating clock */ + struct clk *pclk; /* bus clock */ + struct device *dev; + int state; + + spinlock_t lock; /* IRQ synchronization */ + + /* + * Since the TRANS_DONE bit is cleared on read, and we may read it + * either during an IRQ or after a transaction, keep track of its + * state here. + */ + int trans_done; + + /* + * Called from atomic context, don't use interrupts. + */ + unsigned int atomic; + + /* Controller operating frequency */ + unsigned int op_clock; + + /* Version of HS-I2C Hardware */ + const struct exynos_hsi2c_variant *variant; +}; + +/** + * struct exynos_hsi2c_variant - platform specific HSI2C driver data + * @fifo_depth: the fifo depth supported by the HSI2C module + * @hw: the hardware variant of Exynos I2C controller + * + * Specifies platform specific configuration of HSI2C module. + * Note: A structure for driver specific platform data is used for future + * expansion of its usage. + */ +struct exynos_hsi2c_variant { + unsigned int fifo_depth; + enum i2c_type_exynos hw; +}; + +static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = { + .fifo_depth = 64, + .hw = I2C_TYPE_EXYNOS5, +}; + +static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = { + .fifo_depth = 16, + .hw = I2C_TYPE_EXYNOS5, +}; + +static const struct exynos_hsi2c_variant exynos7_hsi2c_data = { + .fifo_depth = 16, + .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", + .data = &exynos5250_hsi2c_data + }, { + .compatible = "samsung,exynos5250-hsi2c", + .data = &exynos5250_hsi2c_data + }, { + .compatible = "samsung,exynos5260-hsi2c", + .data = &exynos5260_hsi2c_data + }, { + .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); + +static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c) +{ + writel(readl(i2c->regs + HSI2C_INT_STATUS), + i2c->regs + HSI2C_INT_STATUS); +} + +/* + * 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. + */ +static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings) +{ + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + unsigned int t_sr_release; + 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 >= 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 + * + * In case of HSI2C controllers in Exynos7 series + * FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + FLT_CYCLE + * + * 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; + 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; + + 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; + } + + /* + * 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; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = div << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + dev_dbg(i2c->dev, "tSTART_SU: %X, tSTART_HD: %X, tSTOP_SU: %X\n", + t_start_su, t_start_hd, t_stop_su); + dev_dbg(i2c->dev, "tDATA_SU: %X, tSCL_L: %X, tSCL_H: %X\n", + t_data_su, t_scl_l, t_scl_h); + dev_dbg(i2c->dev, "nClkDiv: %X, tSR_RELEASE: %X\n", + div, t_sr_release); + dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd); + + if (hs_timings) { + writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1); + writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2); + writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3); + } else { + writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_FS1); + writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_FS2); + writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3); + } + writel(i2c_timing_sla, i2c->regs + HSI2C_TIMING_SLA); + + return 0; +} + +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 < I2C_MAX_FAST_MODE_PLUS_FREQ) + return ret; + + return exynos5_i2c_set_timing(i2c, true); +} + +/* + * exynos5_i2c_init: configures the controller for I2C functionality + * Programs I2C controller for Master mode operation + */ +static void exynos5_i2c_init(struct exynos5_i2c *i2c) +{ + u32 i2c_conf = readl(i2c->regs + HSI2C_CONF); + u32 i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT); + + /* Clear to disable Timeout */ + i2c_timeout &= ~HSI2C_TIMEOUT_EN; + writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT); + + writel((HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + i2c->regs + HSI2C_CTL); + writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL); + + 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; + } + + writel(i2c_conf | HSI2C_AUTO_MODE, i2c->regs + HSI2C_CONF); +} + +static void exynos5_i2c_reset(struct exynos5_i2c *i2c) +{ + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = readl(i2c->regs + HSI2C_CTL); + i2c_ctl |= HSI2C_SW_RST; + writel(i2c_ctl, i2c->regs + HSI2C_CTL); + + i2c_ctl = readl(i2c->regs + HSI2C_CTL); + i2c_ctl &= ~HSI2C_SW_RST; + writel(i2c_ctl, i2c->regs + HSI2C_CTL); + + /* We don't expect calculations to fail during the run */ + exynos5_hsi2c_clock_setup(i2c); + /* Initialize the configure registers */ + exynos5_i2c_init(i2c); +} + +/* + * exynos5_i2c_irq: top level IRQ servicing routine + * + * INT_STATUS registers gives the interrupt details. Further, + * FIFO_STATUS or TRANS_STATUS registers are to be check for detailed + * state of the bus. + */ +static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) +{ + struct exynos5_i2c *i2c = dev_id; + u32 fifo_level, int_status, fifo_status, trans_status; + unsigned char byte; + int len = 0; + + i2c->state = -EINVAL; + + spin_lock(&i2c->lock); + + int_status = readl(i2c->regs + HSI2C_INT_STATUS); + writel(int_status, i2c->regs + HSI2C_INT_STATUS); + + /* handle interrupt related to the transfer status */ + 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; + } else if (int_status & HSI2C_INT_TRANS_ABORT) { + dev_dbg(i2c->dev, "Deal with arbitration lose\n"); + i2c->state = -EAGAIN; + goto stop; + } else if (int_status & HSI2C_INT_NO_DEV_ACK) { + dev_dbg(i2c->dev, "No ACK from device\n"); + i2c->state = -ENXIO; + goto stop; + } else if (int_status & HSI2C_INT_NO_DEV) { + dev_dbg(i2c->dev, "No device\n"); + i2c->state = -ENXIO; + goto stop; + } else if (int_status & HSI2C_INT_TIMEOUT) { + dev_dbg(i2c->dev, "Accessing device timed out\n"); + i2c->state = -ETIMEDOUT; + goto stop; + } + + 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"); + i2c->state = -ENXIO; + goto stop; + } else if (trans_status & HSI2C_NO_DEV) { + dev_dbg(i2c->dev, "No device\n"); + i2c->state = -ENXIO; + goto stop; + } else if (trans_status & HSI2C_TRANS_ABORT) { + dev_dbg(i2c->dev, "Deal with arbitration lose\n"); + i2c->state = -EAGAIN; + goto stop; + } else if (trans_status & HSI2C_TIMEOUT_AUTO) { + dev_dbg(i2c->dev, "Accessing device timed out\n"); + i2c->state = -ETIMEDOUT; + goto stop; + } else if (trans_status & HSI2C_TRANS_DONE) { + i2c->trans_done = 1; + i2c->state = 0; + } + + break; + } + + if ((i2c->msg->flags & I2C_M_RD) && (int_status & + (HSI2C_INT_TRAILING | HSI2C_INT_RX_ALMOSTFULL))) { + fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS); + fifo_level = HSI2C_RX_FIFO_LVL(fifo_status); + len = min(fifo_level, i2c->msg->len - i2c->msg_ptr); + + while (len > 0) { + byte = (unsigned char) + readl(i2c->regs + HSI2C_RX_DATA); + i2c->msg->buf[i2c->msg_ptr++] = byte; + len--; + } + i2c->state = 0; + } else if (int_status & HSI2C_INT_TX_ALMOSTEMPTY) { + fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS); + fifo_level = HSI2C_TX_FIFO_LVL(fifo_status); + + len = i2c->variant->fifo_depth - fifo_level; + if (len > (i2c->msg->len - i2c->msg_ptr)) { + u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE); + + int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN; + writel(int_en, i2c->regs + HSI2C_INT_ENABLE); + len = i2c->msg->len - i2c->msg_ptr; + } + + while (len > 0) { + byte = i2c->msg->buf[i2c->msg_ptr++]; + writel(byte, i2c->regs + HSI2C_TX_DATA); + len--; + } + i2c->state = 0; + } + + stop: + if ((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) || + (i2c->state < 0)) { + writel(0, i2c->regs + HSI2C_INT_ENABLE); + exynos5_i2c_clr_pend_irq(i2c); + complete(&i2c->msg_complete); + } + + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +/* + * exynos5_i2c_wait_bus_idle + * + * Wait for the bus to go idle, indicated by the MASTER_BUSY bit being + * cleared. + * + * Returns -EBUSY if the bus cannot be bought to idle + */ +static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c) +{ + unsigned long stop_time; + u32 trans_status; + + /* wait for 100 milli seconds for the bus to be idle */ + stop_time = jiffies + msecs_to_jiffies(100) + 1; + do { + trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); + if (!(trans_status & HSI2C_MASTER_BUSY)) + return 0; + + usleep_range(50, 200); + } while (time_before(jiffies, stop_time)); + + return -EBUSY; +} + +static void exynos5_i2c_bus_recover(struct exynos5_i2c *i2c) +{ + u32 val; + + val = readl(i2c->regs + HSI2C_CTL) | HSI2C_RXCHON; + writel(val, i2c->regs + HSI2C_CTL); + val = readl(i2c->regs + HSI2C_CONF) & ~HSI2C_AUTO_MODE; + writel(val, i2c->regs + HSI2C_CONF); + + /* + * Specification says master should send nine clock pulses. It can be + * emulated by sending manual read command (nine pulses for read eight + * bits + one pulse for NACK). + */ + writel(HSI2C_CMD_READ_DATA, i2c->regs + HSI2C_MANUAL_CMD); + exynos5_i2c_wait_bus_idle(i2c); + writel(HSI2C_CMD_SEND_STOP, i2c->regs + HSI2C_MANUAL_CMD); + exynos5_i2c_wait_bus_idle(i2c); + + val = readl(i2c->regs + HSI2C_CTL) & ~HSI2C_RXCHON; + writel(val, i2c->regs + HSI2C_CTL); + val = readl(i2c->regs + HSI2C_CONF) | HSI2C_AUTO_MODE; + writel(val, i2c->regs + HSI2C_CONF); +} + +static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c) +{ + unsigned long timeout; + + if (i2c->variant->hw == I2C_TYPE_EXYNOS5) + return; + + /* + * 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 (;;) { + u32 st = readl(i2c->regs + HSI2C_TRANS_STATUS); + + if ((st & HSI2C_MASTER_ST_MASK) != HSI2C_MASTER_ST_LOSE) + return; + + if (time_is_before_jiffies(timeout)) + return; + + exynos5_i2c_bus_recover(i2c); + } +} + +/* + * exynos5_i2c_message_start: Configures the bus and starts the xfer + * i2c: struct exynos5_i2c pointer for the current bus + * stop: Enables stop after transfer if set. Set for last transfer of + * in the list of messages. + * + * Configures the bus for read/write function + * Sets chip address to talk to, message length to be sent. + * Enables appropriate interrupts and sends start xfer command. + */ +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_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); + fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN; + + if (i2c->msg->flags & I2C_M_RD) { + i2c_ctl |= HSI2C_RXCHON; + + i2c_auto_conf |= HSI2C_READ_WRITE; + + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ? + (i2c->variant->fifo_depth * 3 / 4) : i2c->msg->len; + fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl); + + int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN | + HSI2C_INT_TRAILING_EN); + } else { + i2c_ctl |= HSI2C_TXCHON; + + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ? + (i2c->variant->fifo_depth * 1 / 4) : i2c->msg->len; + fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl); + + int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN; + } + + 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); + + exynos5_i2c_bus_check(i2c); + + /* + * Enable interrupts before starting the transfer so that we don't + * miss any INT_I2C interrupts. + */ + spin_lock_irqsave(&i2c->lock, flags); + writel(int_en, i2c->regs + HSI2C_INT_ENABLE); + + if (stop == 1) + i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS; + i2c_auto_conf |= i2c->msg->len; + i2c_auto_conf |= HSI2C_MASTER_RUN; + writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF); + 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 time_left; + int ret; + + i2c->msg = msgs; + i2c->msg_ptr = 0; + i2c->trans_done = 0; + + reinit_completion(&i2c->msg_complete); + + exynos5_i2c_message_start(i2c, stop); + + 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 transferred (stop == 1) + * Then check if the bus can be brought back to idle. + */ + if (ret == 0 && stop) + ret = exynos5_i2c_wait_bus_idle(i2c); + + if (ret < 0) { + exynos5_i2c_reset(i2c); + if (ret == -ETIMEDOUT) + dev_warn(i2c->dev, "%s timeout\n", + (msgs->flags & I2C_M_RD) ? "rx" : "tx"); + } + + /* Return the state as in interrupt routine */ + return ret; +} + +static int exynos5_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct exynos5_i2c *i2c = adap->algo_data; + int i, ret; + + ret = clk_enable(i2c->pclk); + if (ret) + return ret; + + ret = clk_enable(i2c->clk); + if (ret) + goto err_pclk; + + for (i = 0; i < num; ++i) { + ret = exynos5_i2c_xfer_msg(i2c, msgs + i, i + 1 == num); + if (ret) + break; + } + + 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 = { + .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; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock)) + i2c->op_clock = I2C_MAX_STANDARD_MODE_FREQ; + + 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; + + i2c->dev = &pdev->dev; + i2c->clk = devm_clk_get(&pdev->dev, "hsi2c"); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return -ENOENT; + } + + 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; + + 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; + } + + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + /* Clear pending interrupts from u-boot or misc causes */ + exynos5_i2c_clr_pend_irq(i2c); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->msg_complete); + + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_clk; + + ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq, + 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; + } + + i2c->variant = of_device_get_match_data(&pdev->dev); + + ret = exynos5_hsi2c_clock_setup(i2c); + if (ret) + goto err_clk; + + exynos5_i2c_reset(i2c); + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) + goto err_clk; + + 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 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); + clk_unprepare(i2c->pclk); +} + +static int exynos5_i2c_suspend_noirq(struct device *dev) +{ + struct exynos5_i2c *i2c = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c->adap); + clk_unprepare(i2c->clk); + clk_unprepare(i2c->pclk); + + return 0; +} + +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->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(i2c->clk); + if (ret) + goto err_pclk; + + ret = exynos5_hsi2c_clock_setup(i2c); + if (ret) + goto err_clk; + + exynos5_i2c_init(i2c); + clk_disable(i2c->clk); + 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; +} + +static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, + exynos5_i2c_resume_noirq) +}; + +static struct platform_driver exynos5_i2c_driver = { + .probe = exynos5_i2c_probe, + .remove = exynos5_i2c_remove, + .driver = { + .name = "exynos5-hsi2c", + .pm = pm_sleep_ptr(&exynos5_i2c_dev_pm_ops), + .of_match_table = exynos5_i2c_match, + }, +}; + +module_platform_driver(exynos5_i2c_driver); + +MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver"); +MODULE_AUTHOR("Naveen Krishna Chatradhi "); +MODULE_AUTHOR("Taekgyun Ko "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c new file mode 100644 index 000000000..b2dc5ae1d --- /dev/null +++ b/drivers/i2c/busses/i2c-fsi.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * FSI-attached I2C controller algorithm + * + * Copyright 2018 IBM 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FSI_ENGID_I2C 0x7 + +#define I2C_DEFAULT_CLK_DIV 6 + +/* i2c registers */ +#define I2C_FSI_FIFO 0x00 +#define I2C_FSI_CMD 0x04 +#define I2C_FSI_MODE 0x08 +#define I2C_FSI_WATER_MARK 0x0C +#define I2C_FSI_INT_MASK 0x10 +#define I2C_FSI_INT_COND 0x14 +#define I2C_FSI_OR_INT_MASK 0x14 +#define I2C_FSI_INTS 0x18 +#define I2C_FSI_AND_INT_MASK 0x18 +#define I2C_FSI_STAT 0x1C +#define I2C_FSI_RESET_I2C 0x1C +#define I2C_FSI_ESTAT 0x20 +#define I2C_FSI_RESET_ERR 0x20 +#define I2C_FSI_RESID_LEN 0x24 +#define I2C_FSI_SET_SCL 0x24 +#define I2C_FSI_PORT_BUSY 0x28 +#define I2C_FSI_RESET_SCL 0x2C +#define I2C_FSI_SET_SDA 0x30 +#define I2C_FSI_RESET_SDA 0x34 + +/* cmd register */ +#define I2C_CMD_WITH_START BIT(31) +#define I2C_CMD_WITH_ADDR BIT(30) +#define I2C_CMD_RD_CONT BIT(29) +#define I2C_CMD_WITH_STOP BIT(28) +#define I2C_CMD_FORCELAUNCH BIT(27) +#define I2C_CMD_ADDR GENMASK(23, 17) +#define I2C_CMD_READ BIT(16) +#define I2C_CMD_LEN GENMASK(15, 0) + +/* mode register */ +#define I2C_MODE_CLKDIV GENMASK(31, 16) +#define I2C_MODE_PORT GENMASK(15, 10) +#define I2C_MODE_ENHANCED BIT(3) +#define I2C_MODE_DIAG BIT(2) +#define I2C_MODE_PACE_ALLOW BIT(1) +#define I2C_MODE_WRAP BIT(0) + +/* watermark register */ +#define I2C_WATERMARK_HI GENMASK(15, 12) +#define I2C_WATERMARK_LO GENMASK(7, 4) + +#define I2C_FIFO_HI_LVL 4 +#define I2C_FIFO_LO_LVL 4 + +/* interrupt register */ +#define I2C_INT_INV_CMD BIT(15) +#define I2C_INT_PARITY BIT(14) +#define I2C_INT_BE_OVERRUN BIT(13) +#define I2C_INT_BE_ACCESS BIT(12) +#define I2C_INT_LOST_ARB BIT(11) +#define I2C_INT_NACK BIT(10) +#define I2C_INT_DAT_REQ BIT(9) +#define I2C_INT_CMD_COMP BIT(8) +#define I2C_INT_STOP_ERR BIT(7) +#define I2C_INT_BUSY BIT(6) +#define I2C_INT_IDLE BIT(5) + +/* status register */ +#define I2C_STAT_INV_CMD BIT(31) +#define I2C_STAT_PARITY BIT(30) +#define I2C_STAT_BE_OVERRUN BIT(29) +#define I2C_STAT_BE_ACCESS BIT(28) +#define I2C_STAT_LOST_ARB BIT(27) +#define I2C_STAT_NACK BIT(26) +#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(22, 16) +#define I2C_STAT_ANY_INT BIT(15) +#define I2C_STAT_SCL_IN BIT(11) +#define I2C_STAT_SDA_IN BIT(10) +#define I2C_STAT_PORT_BUSY BIT(9) +#define I2C_STAT_SELF_BUSY BIT(8) +#define I2C_STAT_FIFO_COUNT GENMASK(7, 0) + +#define I2C_STAT_ERR (I2C_STAT_INV_CMD | \ + I2C_STAT_PARITY | \ + I2C_STAT_BE_OVERRUN | \ + I2C_STAT_BE_ACCESS | \ + I2C_STAT_LOST_ARB | \ + I2C_STAT_NACK | \ + I2C_STAT_STOP_ERR) +#define I2C_STAT_ANY_RESP (I2C_STAT_ERR | \ + I2C_STAT_DAT_REQ | \ + I2C_STAT_CMD_COMP) + +/* extended status register */ +#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24) +#define I2C_ESTAT_SCL_IN_SY BIT(15) +#define I2C_ESTAT_SDA_IN_SY BIT(14) +#define I2C_ESTAT_S_SCL BIT(13) +#define I2C_ESTAT_S_SDA BIT(12) +#define I2C_ESTAT_M_SCL BIT(11) +#define I2C_ESTAT_M_SDA BIT(10) +#define I2C_ESTAT_HI_WATER BIT(9) +#define I2C_ESTAT_LO_WATER BIT(8) +#define I2C_ESTAT_PORT_BUSY BIT(7) +#define I2C_ESTAT_SELF_BUSY BIT(6) +#define I2C_ESTAT_VERSION GENMASK(4, 0) + +/* port busy register */ +#define I2C_PORT_BUSY_RESET BIT(31) + +/* wait for command complete or data request */ +#define I2C_CMD_SLEEP_MAX_US 500 +#define I2C_CMD_SLEEP_MIN_US 50 + +/* wait after reset; choose time from legacy driver */ +#define I2C_RESET_SLEEP_MAX_US 2000 +#define I2C_RESET_SLEEP_MIN_US 1000 + +/* choose timeout length from legacy driver; it's well tested */ +#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100) + +struct fsi_i2c_ctrl { + struct fsi_device *fsi; + u8 fifo_size; + struct list_head ports; + struct mutex lock; +}; + +struct fsi_i2c_port { + struct list_head list; + struct i2c_adapter adapter; + struct fsi_i2c_ctrl *ctrl; + u16 port; + u16 xfrd; +}; + +static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg, + u32 *data) +{ + int rc; + __be32 data_be; + + rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be)); + if (rc) + return rc; + + *data = be32_to_cpu(data_be); + + return 0; +} + +static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg, + u32 *data) +{ + __be32 data_be = cpu_to_be32p(data); + + return fsi_device_write(fsi, reg, &data_be, sizeof(data_be)); +} + +static int fsi_i2c_dev_init(struct fsi_i2c_ctrl *i2c) +{ + int rc; + u32 mode = I2C_MODE_ENHANCED, extended_status, watermark; + u32 interrupt = 0; + + /* since we use polling, disable interrupts */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt); + if (rc) + return rc; + + mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV); + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status); + if (rc) + return rc; + + i2c->fifo_size = FIELD_GET(I2C_ESTAT_FIFO_SZ, extended_status); + watermark = FIELD_PREP(I2C_WATERMARK_HI, + i2c->fifo_size - I2C_FIFO_HI_LVL); + watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL); + + return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark); +} + +static int fsi_i2c_set_port(struct fsi_i2c_port *port) +{ + int rc; + struct fsi_device *fsi = port->ctrl->fsi; + u32 mode, dummy = 0; + + rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + if (FIELD_GET(I2C_MODE_PORT, mode) == port->port) + return 0; + + mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port); + rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + /* reset engine when port is changed */ + return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy); +} + +static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg, + bool stop) +{ + struct fsi_i2c_ctrl *i2c = port->ctrl; + u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR; + + port->xfrd = 0; + + if (msg->flags & I2C_M_RD) + cmd |= I2C_CMD_READ; + + if (stop || msg->flags & I2C_M_STOP) + cmd |= I2C_CMD_WITH_STOP; + + cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr); + cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len); + + return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd); +} + +static int fsi_i2c_get_op_bytes(int op_bytes) +{ + /* fsi is limited to max 4 byte aligned ops */ + if (op_bytes > 4) + return 4; + else if (op_bytes == 3) + return 2; + return op_bytes; +} + +static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg, + u8 fifo_count) +{ + int write; + int rc; + struct fsi_i2c_ctrl *i2c = port->ctrl; + int bytes_to_write = i2c->fifo_size - fifo_count; + int bytes_remaining = msg->len - port->xfrd; + + bytes_to_write = min(bytes_to_write, bytes_remaining); + + while (bytes_to_write) { + write = fsi_i2c_get_op_bytes(bytes_to_write); + + rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO, + &msg->buf[port->xfrd], write); + if (rc) + return rc; + + port->xfrd += write; + bytes_to_write -= write; + } + + return 0; +} + +static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg, + u8 fifo_count) +{ + int read; + int rc; + struct fsi_i2c_ctrl *i2c = port->ctrl; + int bytes_to_read; + int xfr_remaining = msg->len - port->xfrd; + u32 dummy; + + bytes_to_read = min_t(int, fifo_count, xfr_remaining); + + while (bytes_to_read) { + read = fsi_i2c_get_op_bytes(bytes_to_read); + + if (xfr_remaining) { + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, + &msg->buf[port->xfrd], read); + if (rc) + return rc; + + port->xfrd += read; + xfr_remaining -= read; + } else { + /* no more buffer but data in fifo, need to clear it */ + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy, + read); + if (rc) + return rc; + } + + bytes_to_read -= read; + } + + return 0; +} + +static int fsi_i2c_get_scl(struct i2c_adapter *adap) +{ + u32 stat = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_ctrl *i2c = port->ctrl; + + fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + + return !!(stat & I2C_STAT_SCL_IN); +} + +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_ctrl *i2c = port->ctrl; + + if (val) + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy); + else + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy); +} + +static int fsi_i2c_get_sda(struct i2c_adapter *adap) +{ + u32 stat = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_ctrl *i2c = port->ctrl; + + fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + + return !!(stat & I2C_STAT_SDA_IN); +} + +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_ctrl *i2c = port->ctrl; + + if (val) + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy); + else + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy); +} + +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_ctrl *i2c = port->ctrl; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return; + + mode |= I2C_MODE_DIAG; + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); +} + +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_ctrl *i2c = port->ctrl; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return; + + mode &= ~I2C_MODE_DIAG; + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); +} + +static int fsi_i2c_reset_bus(struct fsi_i2c_ctrl *i2c, + struct fsi_i2c_port *port) +{ + int rc; + u32 stat, dummy = 0; + + /* force bus reset, ignore errors */ + i2c_recover_bus(&port->adapter); + + /* reset errors */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy); + if (rc) + return rc; + + /* wait for command complete */ + usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US); + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + if (rc) + return rc; + + if (stat & I2C_STAT_CMD_COMP) + return 0; + + /* failed to get command complete; reset engine again */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy); + if (rc) + return rc; + + /* re-init engine again */ + return fsi_i2c_dev_init(i2c); +} + +static int fsi_i2c_reset_engine(struct fsi_i2c_ctrl *i2c, u16 port) +{ + int rc; + u32 mode, dummy = 0; + + /* reset engine */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy); + if (rc) + return rc; + + /* re-init engine */ + rc = fsi_i2c_dev_init(i2c); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + /* set port; default after reset is 0 */ + if (port) { + mode &= ~I2C_MODE_PORT; + mode |= FIELD_PREP(I2C_MODE_PORT, port); + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + } + + /* reset busy register; hw workaround */ + dummy = I2C_PORT_BUSY_RESET; + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy); + if (rc) + return rc; + + return 0; +} + +static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status) +{ + int rc; + unsigned long start; + u32 cmd = I2C_CMD_WITH_STOP; + u32 stat; + struct fsi_i2c_ctrl *i2c = port->ctrl; + struct fsi_device *fsi = i2c->fsi; + + rc = fsi_i2c_reset_engine(i2c, port->port); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat); + if (rc) + return rc; + + /* if sda is low, peform full bus reset */ + if (!(stat & I2C_STAT_SDA_IN)) { + rc = fsi_i2c_reset_bus(i2c, port); + if (rc) + return rc; + } + + /* skip final stop command for these errors */ + if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR)) + return 0; + + /* write stop command */ + rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd); + if (rc) + return rc; + + /* wait until we see command complete in the controller */ + start = jiffies; + + do { + rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status); + if (rc) + return rc; + + if (status & I2C_STAT_CMD_COMP) + return 0; + + usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US); + } while (time_after(start + I2C_ABORT_TIMEOUT, jiffies)); + + return -ETIMEDOUT; +} + +static int fsi_i2c_handle_status(struct fsi_i2c_port *port, + struct i2c_msg *msg, u32 status) +{ + int rc; + u8 fifo_count; + + if (status & I2C_STAT_ERR) { + rc = fsi_i2c_abort(port, status); + if (rc) + return rc; + + if (status & I2C_STAT_INV_CMD) + return -EINVAL; + + if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN | + I2C_STAT_BE_ACCESS)) + return -EPROTO; + + if (status & I2C_STAT_NACK) + return -ENXIO; + + if (status & I2C_STAT_LOST_ARB) + return -EAGAIN; + + if (status & I2C_STAT_STOP_ERR) + return -EBADMSG; + + return -EIO; + } + + if (status & I2C_STAT_DAT_REQ) { + fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status); + + if (msg->flags & I2C_M_RD) + return fsi_i2c_read_fifo(port, msg, fifo_count); + + return fsi_i2c_write_fifo(port, msg, fifo_count); + } + + if (status & I2C_STAT_CMD_COMP) { + if (port->xfrd < msg->len) + return -ENODATA; + + return msg->len; + } + + return 0; +} + +static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg, + unsigned long timeout) +{ + u32 status = 0; + int rc; + unsigned long start = jiffies; + + do { + rc = fsi_i2c_read_reg(port->ctrl->fsi, I2C_FSI_STAT, + &status); + if (rc) + return rc; + + if (status & I2C_STAT_ANY_RESP) { + rc = fsi_i2c_handle_status(port, msg, status); + if (rc < 0) + return rc; + + /* cmd complete and all data xfrd */ + if (rc == msg->len) + return 0; + + /* need to xfr more data, but maybe don't need wait */ + continue; + } + + usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US); + } while (time_after(start + timeout, jiffies)); + + return -ETIMEDOUT; +} + +static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int i, rc; + unsigned long start_time; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_ctrl *ctrl = port->ctrl; + struct i2c_msg *msg; + + mutex_lock(&ctrl->lock); + + rc = fsi_i2c_set_port(port); + if (rc) + goto unlock; + + for (i = 0; i < num; i++) { + msg = msgs + i; + start_time = jiffies; + + rc = fsi_i2c_start(port, msg, i == num - 1); + if (rc) + goto unlock; + + rc = fsi_i2c_wait(port, msg, + adap->timeout - (jiffies - start_time)); + if (rc) + goto unlock; + } + +unlock: + mutex_unlock(&ctrl->lock); + return rc ? : num; +} + +static u32 fsi_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | + I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = fsi_i2c_get_scl, + .set_scl = fsi_i2c_set_scl, + .get_sda = fsi_i2c_get_sda, + .set_sda = fsi_i2c_set_sda, + .prepare_recovery = fsi_i2c_prepare_recovery, + .unprepare_recovery = fsi_i2c_unprepare_recovery, +}; + +static const struct i2c_algorithm fsi_i2c_algorithm = { + .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 fsi_device *fsi_dev) +{ + struct device *dev = &fsi_dev->dev; + struct fsi_i2c_ctrl *i2c; + struct fsi_i2c_port *port; + struct device_node *np; + u32 port_no, ports, stat; + int rc; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + mutex_init(&i2c->lock); + i2c->fsi = to_fsi_dev(dev); + INIT_LIST_HEAD(&i2c->ports); + + rc = fsi_i2c_dev_init(i2c); + if (rc) + return rc; + + 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_obj(*port); + if (!port) { + of_node_put(np); + break; + } + + port->ctrl = i2c; + port->port = port_no; + + port->adapter.owner = THIS_MODULE; + port->adapter.dev.of_node = np; + port->adapter.dev.parent = dev; + port->adapter.algo = &fsi_i2c_algorithm; + port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info; + port->adapter.algo_data = port; + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "i2c_bus-%u", port_no); + + rc = i2c_add_adapter(&port->adapter); + if (rc < 0) { + dev_err(dev, "Failed to register adapter: %d\n", rc); + of_node_put(np); + kfree(port); + continue; + } + + list_add(&port->list, &i2c->ports); + } + + fsi_set_drvdata(fsi_dev, i2c); + + return 0; +} + +static void fsi_i2c_remove(struct fsi_device *fsi_dev) +{ + struct fsi_i2c_ctrl *i2c = fsi_get_drvdata(fsi_dev); + struct fsi_i2c_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &i2c->ports, list) { + list_del(&port->list); + i2c_del_adapter(&port->adapter); + kfree(port); + } +} + +static const struct fsi_device_id fsi_i2c_ids[] = { + { FSI_ENGID_I2C, FSI_VERSION_ANY }, + { } +}; + +static struct fsi_driver fsi_i2c_driver = { + .id_table = fsi_i2c_ids, + .probe = fsi_i2c_probe, + .remove = fsi_i2c_remove, + .drv = { + .name = "i2c-fsi", + }, +}; + +module_fsi_driver(fsi_i2c_driver); + +MODULE_AUTHOR("Eddie James "); +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 new file mode 100644 index 000000000..b7521b7ec --- /dev/null +++ b/drivers/i2c/busses/i2c-gpio.c @@ -0,0 +1,703 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Bitbanging I2C bus driver using the GPIO API + * + * Copyright (C) 2007 Atmel Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(i2c_gpio_scl_list); +static DEFINE_MUTEX(i2c_gpio_scl_list_lock); +static struct lock_class_key i2c_gpio_scl_lock_key; + +struct i2c_gpio_scl_data { + struct fwnode_reference_args args; + struct gpio_desc *gpio; + struct rt_mutex lock; + struct list_head list; + refcount_t ref; +}; + +struct i2c_gpio_private_data { + struct gpio_desc *sda; + struct i2c_gpio_scl_data *scl; + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_gpio_platform_data pdata; +#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR + /* these must be protected by bus lock */ + struct completion scl_irq_completion; + u64 scl_irq_data; +#endif +}; + +static inline struct i2c_gpio_private_data *adap_to_priv(struct i2c_adapter *adap) +{ + return container_of(adap, struct i2c_gpio_private_data, adap); +} + +/* + * Toggle SDA by changing the output value of the pin. This is only + * valid for pins configured as open drain (i.e. setting the value + * high effectively turns off the output driver.) + */ +static void i2c_gpio_setsda_val(void *data, int state) +{ + struct i2c_gpio_private_data *priv = data; + + gpiod_set_value_cansleep(priv->sda, state); +} + +/* + * Toggle SCL by changing the output value of the pin. This is used + * for pins that are configured as open drain and for output-only + * pins. The latter case will break the i2c protocol, but it will + * often work in practice. + */ +static void i2c_gpio_setscl_val(void *data, int state) +{ + struct i2c_gpio_private_data *priv = data; + + gpiod_set_value_cansleep(priv->scl->gpio, state); +} + +static int i2c_gpio_getsda(void *data) +{ + struct i2c_gpio_private_data *priv = data; + + return gpiod_get_value_cansleep(priv->sda); +} + +static int i2c_gpio_getscl(void *data) +{ + struct i2c_gpio_private_data *priv = data; + + return gpiod_get_value_cansleep(priv->scl->gpio); +} + +static void i2c_gpio_lock_bus(struct i2c_adapter *adap, unsigned int flags) +{ + /* Take care about adapter lock. See i2c_adapter_lock_bus() and others. */ + rt_mutex_lock_nested(&adap->bus_lock, i2c_adapter_depth(adap)); + rt_mutex_lock(&adap_to_priv(adap)->scl->lock); +} + +static int i2c_gpio_trylock_bus(struct i2c_adapter *adap, unsigned int flags) +{ + if (!rt_mutex_trylock(&adap->bus_lock)) + return 0; + + if (!rt_mutex_trylock(&adap_to_priv(adap)->scl->lock)) { + rt_mutex_unlock(&adap->bus_lock); + return 0; + } + + return 1; +} + +static void i2c_gpio_unlock_bus(struct i2c_adapter *adap, unsigned int flags) +{ + rt_mutex_unlock(&adap_to_priv(adap)->scl->lock); + rt_mutex_unlock(&adap->bus_lock); +} + +static const struct i2c_lock_operations i2c_gpio_lock_ops = { + .lock_bus = i2c_gpio_lock_bus, + .trylock_bus = i2c_gpio_trylock_bus, + .unlock_bus = i2c_gpio_unlock_bus, +}; + +#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR + +#define setsda(bd, val) ((bd)->setsda((bd)->data, val)) +#define setscl(bd, val) ((bd)->setscl((bd)->data, val)) +#define getsda(bd) ((bd)->getsda((bd)->data)) +#define getscl(bd) ((bd)->getscl((bd)->data)) + +#define WIRE_ATTRIBUTE(wire) \ +static int fops_##wire##_get(void *data, u64 *val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + *val = get##wire(&priv->bit_data); \ + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + return 0; \ +} \ +static int fops_##wire##_set(void *data, u64 val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + set##wire(&priv->bit_data, val); \ + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + return 0; \ +} \ +DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n") + +WIRE_ATTRIBUTE(scl); +WIRE_ATTRIBUTE(sda); + +static void i2c_gpio_incomplete_transfer(struct i2c_gpio_private_data *priv, + u32 pattern, u8 pattern_size) +{ + struct i2c_algo_bit_data *bit_data = &priv->bit_data; + int i; + + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + + /* START condition */ + setsda(bit_data, 0); + udelay(bit_data->udelay); + + /* Send pattern, request ACK, don't send STOP */ + for (i = pattern_size - 1; i >= 0; i--) { + setscl(bit_data, 0); + udelay(bit_data->udelay / 2); + setsda(bit_data, (pattern >> i) & 1); + udelay((bit_data->udelay + 1) / 2); + setscl(bit_data, 1); + udelay(bit_data->udelay); + } + + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); +} + +static int fops_incomplete_addr_phase_set(void *data, u64 addr) +{ + struct i2c_gpio_private_data *priv = data; + u32 pattern; + + if (addr > 0x7f) + return -EINVAL; + + /* ADDR (7 bit) + RD (1 bit) + Client ACK, keep SDA hi (1 bit) */ + pattern = (addr << 2) | 3; + + i2c_gpio_incomplete_transfer(priv, pattern, 9); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_addr_phase, NULL, fops_incomplete_addr_phase_set, "%llu\n"); + +static int fops_incomplete_write_byte_set(void *data, u64 addr) +{ + struct i2c_gpio_private_data *priv = data; + u32 pattern; + + if (addr > 0x7f) + return -EINVAL; + + /* ADDR (7 bit) + WR (1 bit) + Client ACK (1 bit) */ + pattern = (addr << 2) | 1; + /* 0x00 (8 bit) + Client ACK, keep SDA hi (1 bit) */ + pattern = (pattern << 9) | 1; + + i2c_gpio_incomplete_transfer(priv, pattern, 18); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); + +static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv, + irqreturn_t handler(int, void*)) +{ + int ret, irq = gpiod_to_irq(priv->scl->gpio); + + if (irq < 0) + return irq; + + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + + ret = gpiod_direction_input(priv->scl->gpio); + 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->gpio, 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; + /* + * 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. + */ + 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"); + +static irqreturn_t inject_panic_irq(int irq, void *dev_id) +{ + struct i2c_gpio_private_data *priv = dev_id; + + 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_init(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); + + 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) {} +#endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/ + +/* 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; + + device_property_read_u32(dev, "i2c-gpio,delay-us", &pdata->udelay); + + if (!device_property_read_u32(dev, "i2c-gpio,timeout-ms", ®)) + pdata->timeout = msecs_to_jiffies(reg); + + pdata->sda_is_open_drain = + device_property_read_bool(dev, "i2c-gpio,sda-open-drain"); + pdata->scl_is_open_drain = + device_property_read_bool(dev, "i2c-gpio,scl-open-drain"); + pdata->scl_is_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, + const char *con_id, + unsigned int index, + enum gpiod_flags gflags) +{ + struct gpio_desc *retdesc; + int ret; + + /* + * Don't use resource-managed functions. SCL may be shared across adapters and has + * its own lifetime management. SDA uses the same path for consistency. + */ + retdesc = gpiod_get(dev, con_id, gflags); + if (!IS_ERR(retdesc)) { + dev_dbg(dev, "got GPIO from name %s\n", con_id); + return retdesc; + } + + retdesc = gpiod_get_index(dev, NULL, index, gflags); + if (!IS_ERR(retdesc)) { + dev_dbg(dev, "got GPIO from index %u\n", index); + return retdesc; + } + + ret = PTR_ERR(retdesc); + + /* FIXME: hack in the old code, is this really necessary? */ + if (ret == -EINVAL) + retdesc = ERR_PTR(-EPROBE_DEFER); + + /* This happens if the GPIO driver is not yet probed, let's defer */ + if (ret == -ENOENT) + retdesc = ERR_PTR(-EPROBE_DEFER); + + if (PTR_ERR(retdesc) != -EPROBE_DEFER) + dev_err(dev, "error trying to get descriptor: %d\n", ret); + + return retdesc; +} + +static struct i2c_gpio_scl_data *i2c_gpio_create_scl(struct device *dev) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_reference_args args; + struct i2c_gpio_scl_data *scl; + bool sharable = false; + int ret; + + /* + * SCL gpios can be shared if they are defined in the devicetree and have at least one + * cell in addition to the phandle. This is usually the pin. To support complex designs + * with arbitrary of_xlate() mappings handle the node cells opaque. + */ + if (fwnode) { + ret = fwnode_property_get_reference_args(fwnode, "scl-gpios", + "#gpio-cells", 0, 0, &args); + if (ret) + /* try the ancient way */ + ret = fwnode_property_get_reference_args(fwnode, "gpios", + "#gpio-cells", 0, 1, &args); + + if (!ret) { + if (args.nargs >= 1) + sharable = true; + else + fwnode_handle_put(args.fwnode); + } + } + + scl = kzalloc(sizeof(*scl), GFP_KERNEL); + if (!scl) { + if (sharable) + fwnode_handle_put(args.fwnode); + return ERR_PTR(-ENOMEM); + } + + if (sharable) { + scl->args.fwnode = args.fwnode; + scl->args.nargs = args.nargs; + memcpy(scl->args.args, args.args, sizeof(args.args[0]) * args.nargs); + } + + rt_mutex_init(&scl->lock); + lockdep_set_class(&scl->lock, &i2c_gpio_scl_lock_key); + refcount_set(&scl->ref, 1); + + return scl; +} + +static void i2c_gpio_free_scl(struct i2c_gpio_scl_data *scl) +{ + if (scl->args.fwnode) + fwnode_handle_put(scl->args.fwnode); + kfree(scl); +} + +static bool i2c_gpio_scl_matches(struct i2c_gpio_scl_data *a, struct i2c_gpio_scl_data *b) +{ + if (!a->args.fwnode || !b->args.fwnode || + a->args.fwnode != b->args.fwnode || a->args.nargs != b->args.nargs) + return false; + + return memcmp(a->args.args, b->args.args, sizeof(a->args.args[0]) * a->args.nargs) == 0; +} + +/* + * Look up an existing or create a new shared SCL structure. Optimistic setup sequence always + * creates and tries to add a new entry to the list. This uses minimum locking and afterwards + * requests the GPIO without a lock held. Concurrent probes for the same SCL pin see the entry + * and do not race into a second gpiod_get(). Until everything is setup they terminate with + * -EPROBE_DEFER. + */ +static struct i2c_gpio_scl_data *i2c_gpio_lookup_scl(struct device *dev, enum gpiod_flags gflags) +{ + struct i2c_gpio_scl_data *scl, *new_scl; + struct gpio_desc *gpio; + + new_scl = i2c_gpio_create_scl(dev); + if (IS_ERR(new_scl)) + return new_scl; + + scoped_guard(mutex, &i2c_gpio_scl_list_lock) { + list_for_each_entry(scl, &i2c_gpio_scl_list, list) { + if (!i2c_gpio_scl_matches(scl, new_scl)) + continue; + + i2c_gpio_free_scl(new_scl); + if (!scl->gpio) + return ERR_PTR(-EPROBE_DEFER); + + refcount_inc(&scl->ref); + if (scl->args.fwnode) + dev_dbg(dev, "reusing shared SCL (%pfwP)\n", scl->args.fwnode); + + return scl; + } + list_add(&new_scl->list, &i2c_gpio_scl_list); + } + + gpio = i2c_gpio_get_desc(dev, "scl", 1, gflags); + if (IS_ERR(gpio)) { + scoped_guard(mutex, &i2c_gpio_scl_list_lock) + list_del(&new_scl->list); + i2c_gpio_free_scl(new_scl); + + return ERR_CAST(gpio); + } + + scoped_guard(mutex, &i2c_gpio_scl_list_lock) + new_scl->gpio = gpio; + + if (new_scl->args.fwnode) + dev_dbg(dev, "registered shared SCL (%pfwP)\n", new_scl->args.fwnode); + + return new_scl; +} + +static void i2c_gpio_cleanup_scl(struct i2c_gpio_scl_data *scl) +{ + if (!refcount_dec_and_mutex_lock(&scl->ref, &i2c_gpio_scl_list_lock)) + return; + + list_del(&scl->list); + mutex_unlock(&i2c_gpio_scl_list_lock); + gpiod_put(scl->gpio); + i2c_gpio_free_scl(scl); +} + +static int i2c_gpio_probe(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv; + struct i2c_gpio_platform_data *pdata; + struct i2c_algo_bit_data *bit_data; + struct i2c_adapter *adap; + struct device *dev = &pdev->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); + enum gpiod_flags gflags; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + adap = &priv->adap; + bit_data = &priv->bit_data; + pdata = &priv->pdata; + + if (fwnode) { + i2c_gpio_get_properties(dev, pdata); + } else { + /* + * If all platform data settings are zero it is OK + * to not provide any platform data from the board. + */ + if (dev_get_platdata(dev)) + memcpy(pdata, dev_get_platdata(dev), sizeof(*pdata)); + } + + /* + * First get the GPIO pins; if it fails, we'll defer the probe. + * 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 || pdata->sda_has_no_pullup) + gflags = GPIOD_OUT_HIGH; + else + gflags = GPIOD_OUT_HIGH_OPEN_DRAIN; + priv->sda = i2c_gpio_get_desc(dev, "sda", 0, gflags); + if (IS_ERR(priv->sda)) + return PTR_ERR(priv->sda); + + if (pdata->scl_is_open_drain || pdata->scl_has_no_pullup) + gflags = GPIOD_OUT_HIGH; + else + gflags = GPIOD_OUT_HIGH_OPEN_DRAIN; + priv->scl = i2c_gpio_lookup_scl(dev, gflags); + if (IS_ERR(priv->scl)) { + ret = PTR_ERR(priv->scl); + goto err_cleanup_sda; + } + + if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl->gpio)) + dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); + else + bit_data->can_do_atomic = true; + + bit_data->skip_bit_test = !!priv->scl->args.fwnode; + 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; + if (!pdata->sda_is_output_only) + bit_data->getsda = i2c_gpio_getsda; + + if (pdata->udelay) + bit_data->udelay = pdata->udelay; + else if (pdata->scl_is_output_only) + bit_data->udelay = 50; /* 10 kHz */ + else + bit_data->udelay = 5; /* 100 kHz */ + + if (pdata->timeout) + bit_data->timeout = pdata->timeout; + else + bit_data->timeout = HZ / 10; /* 100 ms */ + + bit_data->data = priv; + + adap->owner = THIS_MODULE; + if (fwnode) + strscpy(adap->name, dev_name(dev), sizeof(adap->name)); + else + snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); + + /* Always use shared SCL aware locking */ + adap->lock_ops = &i2c_gpio_lock_ops; + adap->algo_data = bit_data; + adap->class = I2C_CLASS_HWMON; + adap->dev.parent = dev; + device_set_node(&adap->dev, fwnode); + + adap->nr = pdev->id; + ret = i2c_bit_add_numbered_bus(adap); + if (ret) + goto err_cleanup_scl; + + platform_set_drvdata(pdev, priv); + + /* + * FIXME: using global GPIO numbers is not helpful. If/when we + * get accessors to get the actual name of the GPIO line, + * from the descriptor, then provide that instead. + */ + dev_info(dev, "using lines %u (SDA) and %u (SCL%s)\n", + desc_to_gpio(priv->sda), desc_to_gpio(priv->scl->gpio), + pdata->scl_is_output_only + ? ", no clock stretching" : ""); + + i2c_gpio_fault_injector_init(pdev); + + return 0; + +err_cleanup_scl: + i2c_gpio_cleanup_scl(priv->scl); +err_cleanup_sda: + gpiod_put(priv->sda); + + return ret; +} + +static void i2c_gpio_remove(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv; + struct i2c_adapter *adap; + + priv = platform_get_drvdata(pdev); + adap = &priv->adap; + + i2c_del_adapter(adap); + i2c_gpio_cleanup_scl(priv->scl); + gpiod_put(priv->sda); +} + +static const struct of_device_id i2c_gpio_dt_ids[] = { + { .compatible = "i2c-gpio", }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids); + +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 = i2c_gpio_dt_ids, + .acpi_match_table = i2c_gpio_acpi_match, + }, + .probe = i2c_gpio_probe, + .remove = i2c_gpio_remove, +}; + +static int __init i2c_gpio_init(void) +{ + int ret; + + ret = platform_driver_register(&i2c_gpio_driver); + if (ret) + printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); + + return ret; +} +subsys_initcall(i2c_gpio_init); + +static void __exit i2c_gpio_exit(void) +{ + platform_driver_unregister(&i2c_gpio_driver); +} +module_exit(i2c_gpio_exit); + +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); +MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver"); +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 000000000..6a5cef7e4 --- /dev/null +++ b/drivers/i2c/busses/i2c-gxp.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ + +#include +#include +#include +#include +#include +#include +#include + +#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 rc; + + 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 "); +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 new file mode 100644 index 000000000..a53b4bb7c --- /dev/null +++ b/drivers/i2c/busses/i2c-highlander.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas Solutions Highlander FPGA I2C/SMBus support. + * + * Supported devices: R0P7780LC0011RL, R0P7785LC0011RL + * + * Copyright (C) 2008 Paul Mundt + * Copyright (C) 2008 Renesas Solutions Corp. + * Copyright (C) 2008 Atom Create Engineering Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMCR 0x00 +#define SMCR_START (1 << 0) +#define SMCR_IRIC (1 << 1) +#define SMCR_BBSY (1 << 2) +#define SMCR_ACKE (1 << 3) +#define SMCR_RST (1 << 4) +#define SMCR_IEIC (1 << 6) + +#define SMSMADR 0x02 + +#define SMMR 0x04 +#define SMMR_MODE0 (1 << 0) +#define SMMR_MODE1 (1 << 1) +#define SMMR_CAP (1 << 3) +#define SMMR_TMMD (1 << 4) +#define SMMR_SP (1 << 7) + +#define SMSADR 0x06 +#define SMTRDR 0x46 + +struct highlander_i2c_dev { + struct device *dev; + void __iomem *base; + struct i2c_adapter adapter; + struct completion cmd_complete; + unsigned long last_read_time; + int irq; + u8 *buf; + size_t buf_len; +}; + +static bool iic_force_poll, iic_force_normal; +static int iic_timeout = 1000, iic_read_delay; + +static inline void highlander_i2c_irq_enable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_irq_disable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) & ~SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_start(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_START, dev->base + SMCR); +} + +static inline void highlander_i2c_done(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IRIC, dev->base + SMCR); +} + +static void highlander_i2c_setup(struct highlander_i2c_dev *dev) +{ + u16 smmr; + + smmr = ioread16(dev->base + SMMR); + smmr |= SMMR_TMMD; + + if (iic_force_normal) + smmr &= ~SMMR_SP; + else + smmr |= SMMR_SP; + + iowrite16(smmr, dev->base + SMMR); +} + +static void smbus_write_data(u8 *src, u16 *dst, int len) +{ + for (; len > 1; len -= 2) { + *dst++ = be16_to_cpup((__be16 *)src); + src += 2; + } + + if (len) + *dst = *src << 8; +} + +static void smbus_read_data(u16 *src, u8 *dst, int len) +{ + for (; len > 1; len -= 2) { + *(__be16 *)dst = cpu_to_be16p(src++); + dst += 2; + } + + if (len) + *dst = *src >> 8; +} + +static void highlander_i2c_command(struct highlander_i2c_dev *dev, + u8 command, int len) +{ + unsigned int i; + u16 cmd = (command << 8) | command; + + for (i = 0; i < len; i += 2) { + if (len - i == 1) + cmd = command << 8; + iowrite16(cmd, dev->base + SMSADR + i); + dev_dbg(dev->dev, "command data[%x] 0x%04x\n", i/2, cmd); + } +} + +static int highlander_i2c_wait_for_bbsy(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + while (ioread16(dev->base + SMCR) & SMCR_BBSY) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + + msleep(1); + } + + return 0; +} + +static int highlander_i2c_reset(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_RST, dev->base + SMCR); + return highlander_i2c_wait_for_bbsy(dev); +} + +static int highlander_i2c_wait_for_ack(struct highlander_i2c_dev *dev) +{ + u16 tmp = ioread16(dev->base + SMCR); + + if ((tmp & (SMCR_IRIC | SMCR_ACKE)) == SMCR_ACKE) { + dev_warn(dev->dev, "ack abnormality\n"); + return highlander_i2c_reset(dev); + } + + return 0; +} + +static irqreturn_t highlander_i2c_irq(int irq, void *dev_id) +{ + struct highlander_i2c_dev *dev = dev_id; + + highlander_i2c_done(dev); + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static void highlander_i2c_poll(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + u16 smcr; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + for (;;) { + smcr = ioread16(dev->base + SMCR); + + /* + * Don't bother checking ACKE here, this and the reset + * are handled in highlander_i2c_wait_xfer_done() when + * waiting for the ACK. + */ + + if (smcr & SMCR_IRIC) + return; + if (time_after(jiffies, timeout)) + break; + + cpu_relax(); + cond_resched(); + } + + dev_err(dev->dev, "polling timed out\n"); +} + +static inline int highlander_i2c_wait_xfer_done(struct highlander_i2c_dev *dev) +{ + if (dev->irq) + wait_for_completion_timeout(&dev->cmd_complete, + msecs_to_jiffies(iic_timeout)); + else + /* busy looping, the IRQ of champions */ + highlander_i2c_poll(dev); + + return highlander_i2c_wait_for_ack(dev); +} + +static int highlander_i2c_read(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + if (highlander_i2c_wait_xfer_done(dev)) { + dev_err(dev->dev, "Arbitration loss\n"); + return -EAGAIN; + } + + /* + * The R0P7780LC0011RL FPGA needs a significant delay between + * data read cycles, otherwise the transceiver gets confused and + * garbage is returned when the read is subsequently aborted. + * + * It is not sufficient to wait for BBSY. + * + * While this generally only applies to the older SH7780-based + * Highlanders, the same issue can be observed on SH7785 ones, + * albeit less frequently. SH7780-based Highlanders may need + * this to be as high as 1000 ms. + */ + if (iic_read_delay && time_before(jiffies, dev->last_read_time + + msecs_to_jiffies(iic_read_delay))) + msleep(jiffies_to_msecs((dev->last_read_time + + msecs_to_jiffies(iic_read_delay)) - jiffies)); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + data[i] = ioread16(dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "read data[%x] 0x%04x\n", i, data[i]); + } + + smbus_read_data(data, dev->buf, dev->buf_len); + + dev->last_read_time = jiffies; + + return 0; +} + +static int highlander_i2c_write(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + smbus_write_data(dev->buf, data, dev->buf_len); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + iowrite16(data[i], dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "write data[%x] 0x%04x\n", i, data[i]); + } + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + return highlander_i2c_wait_xfer_done(dev); +} + +static int highlander_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 highlander_i2c_dev *dev = i2c_get_adapdata(adap); + u16 tmp; + + init_completion(&dev->cmd_complete); + + dev_dbg(dev->dev, "addr %04x, command %02x, read_write %d, size %d\n", + addr, command, read_write, size); + + /* + * Set up the buffer and transfer size + */ + switch (size) { + case I2C_SMBUS_BYTE_DATA: + dev->buf = &data->byte; + dev->buf_len = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + dev->buf = &data->block[1]; + dev->buf_len = data->block[0]; + break; + default: + dev_err(dev->dev, "unsupported command %d\n", size); + return -EINVAL; + } + + /* + * Encode the mode setting + */ + tmp = ioread16(dev->base + SMMR); + tmp &= ~(SMMR_MODE0 | SMMR_MODE1); + + switch (dev->buf_len) { + case 1: + /* default */ + break; + case 8: + tmp |= SMMR_MODE0; + break; + case 16: + tmp |= SMMR_MODE1; + break; + case 32: + tmp |= (SMMR_MODE0 | SMMR_MODE1); + break; + default: + dev_err(dev->dev, "unsupported xfer size %zu\n", dev->buf_len); + return -EINVAL; + } + + iowrite16(tmp, dev->base + SMMR); + + /* Ensure we're in a sane state */ + highlander_i2c_done(dev); + + /* Set target address */ + iowrite16((addr << 1) | read_write, dev->base + SMSMADR); + + highlander_i2c_command(dev, command, dev->buf_len); + + if (read_write == I2C_SMBUS_READ) + return highlander_i2c_read(dev); + else + return highlander_i2c_write(dev); +} + +static u32 highlander_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm highlander_i2c_algo = { + .smbus_xfer = highlander_i2c_smbus_xfer, + .functionality = highlander_i2c_func, +}; + +static int highlander_i2c_probe(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "no mem resource\n"); + return -ENODEV; + } + + dev = kzalloc_obj(struct highlander_i2c_dev); + if (unlikely(!dev)) + return -ENOMEM; + + dev->base = ioremap(res->start, resource_size(res)); + if (unlikely(!dev->base)) { + ret = -ENXIO; + goto err; + } + + dev->dev = &pdev->dev; + platform_set_drvdata(pdev, dev); + + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0 || iic_force_poll) + dev->irq = 0; + + if (dev->irq) { + ret = request_irq(dev->irq, highlander_i2c_irq, 0, + pdev->name, dev); + if (unlikely(ret)) + goto err_unmap; + + highlander_i2c_irq_enable(dev); + } else { + dev_notice(&pdev->dev, "no IRQ, using polling mode\n"); + highlander_i2c_irq_disable(dev); + } + + dev->last_read_time = jiffies; /* initial read jiffies */ + + highlander_i2c_setup(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strscpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name)); + adap->algo = &highlander_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + + /* + * Reset the adapter + */ + ret = highlander_i2c_reset(dev); + if (unlikely(ret)) { + dev_err(&pdev->dev, "controller didn't come up\n"); + goto err_free_irq; + } + + ret = i2c_add_numbered_adapter(adap); + if (unlikely(ret)) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + if (dev->irq) + free_irq(dev->irq, dev); +err_unmap: + iounmap(dev->base); +err: + kfree(dev); + + return ret; +} + +static void highlander_i2c_remove(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + + if (dev->irq) + free_irq(dev->irq, dev); + + iounmap(dev->base); + kfree(dev); +} + +static struct platform_driver highlander_i2c_driver = { + .driver = { + .name = "i2c-highlander", + }, + + .probe = highlander_i2c_probe, + .remove = highlander_i2c_remove, +}; + +module_platform_driver(highlander_i2c_driver); + +MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("Renesas Highlander FPGA I2C/SMBus adapter"); +MODULE_LICENSE("GPL v2"); + +module_param(iic_force_poll, bool, 0); +module_param(iic_force_normal, bool, 0); +module_param(iic_timeout, int, 0); +module_param(iic_read_delay, int, 0); + +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); +MODULE_PARM_DESC(iic_force_normal, + "Force normal mode (100 kHz), default is fast mode (400 kHz)"); +MODULE_PARM_DESC(iic_timeout, "Set timeout value in msecs (default 1000 ms)"); +MODULE_PARM_DESC(iic_read_delay, + "Delay between data read cycles (default 0 ms)"); diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c new file mode 100644 index 000000000..ba5c9579a --- /dev/null +++ b/drivers/i2c/busses/i2c-hisi.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HiSilicon I2C Controller Driver for Kunpeng SoC + * + * Copyright (c) 2021 HiSilicon Technologies Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ret; + + 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 "); +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 new file mode 100644 index 000000000..95ab910b8 --- /dev/null +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 HiSilicon Limited. + * + * Now only support 7 bit address. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Map */ +#define HIX5I2C_CTRL 0x00 +#define HIX5I2C_COM 0x04 +#define HIX5I2C_ICR 0x08 +#define HIX5I2C_SR 0x0c +#define HIX5I2C_SCL_H 0x10 +#define HIX5I2C_SCL_L 0x14 +#define HIX5I2C_TXR 0x18 +#define HIX5I2C_RXR 0x1c + +/* I2C_CTRL_REG */ +#define I2C_ENABLE BIT(8) +#define I2C_UNMASK_TOTAL BIT(7) +#define I2C_UNMASK_START BIT(6) +#define I2C_UNMASK_END BIT(5) +#define I2C_UNMASK_SEND BIT(4) +#define I2C_UNMASK_RECEIVE BIT(3) +#define I2C_UNMASK_ACK BIT(2) +#define I2C_UNMASK_ARBITRATE BIT(1) +#define I2C_UNMASK_OVER BIT(0) +#define I2C_UNMASK_ALL (I2C_UNMASK_ACK | I2C_UNMASK_OVER) + +/* I2C_COM_REG */ +#define I2C_NO_ACK BIT(4) +#define I2C_START BIT(3) +#define I2C_READ BIT(2) +#define I2C_WRITE BIT(1) +#define I2C_STOP BIT(0) + +/* I2C_ICR_REG */ +#define I2C_CLEAR_START BIT(6) +#define I2C_CLEAR_END BIT(5) +#define I2C_CLEAR_SEND BIT(4) +#define I2C_CLEAR_RECEIVE BIT(3) +#define I2C_CLEAR_ACK BIT(2) +#define I2C_CLEAR_ARBITRATE BIT(1) +#define I2C_CLEAR_OVER BIT(0) +#define I2C_CLEAR_ALL (I2C_CLEAR_START | I2C_CLEAR_END | \ + I2C_CLEAR_SEND | I2C_CLEAR_RECEIVE | \ + I2C_CLEAR_ACK | I2C_CLEAR_ARBITRATE | \ + I2C_CLEAR_OVER) + +/* I2C_SR_REG */ +#define I2C_BUSY BIT(7) +#define I2C_START_INTR BIT(6) +#define I2C_END_INTR BIT(5) +#define I2C_SEND_INTR BIT(4) +#define I2C_RECEIVE_INTR BIT(3) +#define I2C_ACK_INTR BIT(2) +#define I2C_ARBITRATE_INTR BIT(1) +#define I2C_OVER_INTR BIT(0) + +enum hix5hd2_i2c_state { + HIX5I2C_STAT_RW_ERR = -1, + HIX5I2C_STAT_INIT, + HIX5I2C_STAT_RW, + HIX5I2C_STAT_SND_STOP, + HIX5I2C_STAT_RW_SUCCESS, +}; + +struct hix5hd2_i2c_priv { + struct i2c_adapter adap; + struct i2c_msg *msg; + struct completion msg_complete; + unsigned int msg_idx; + unsigned int msg_len; + int stop; + void __iomem *regs; + struct clk *clk; + struct device *dev; + spinlock_t lock; /* IRQ synchronization */ + int err; + unsigned int freq; + enum hix5hd2_i2c_state state; +}; + +static u32 hix5hd2_i2c_clr_pend_irq(struct hix5hd2_i2c_priv *priv) +{ + u32 val = readl_relaxed(priv->regs + HIX5I2C_SR); + + writel_relaxed(val, priv->regs + HIX5I2C_ICR); + + return val; +} + +static void hix5hd2_i2c_clr_all_irq(struct hix5hd2_i2c_priv *priv) +{ + writel_relaxed(I2C_CLEAR_ALL, priv->regs + HIX5I2C_ICR); +} + +static void hix5hd2_i2c_disable_irq(struct hix5hd2_i2c_priv *priv) +{ + writel_relaxed(0, priv->regs + HIX5I2C_CTRL); +} + +static void hix5hd2_i2c_enable_irq(struct hix5hd2_i2c_priv *priv) +{ + writel_relaxed(I2C_ENABLE | I2C_UNMASK_TOTAL | I2C_UNMASK_ALL, + priv->regs + HIX5I2C_CTRL); +} + +static void hix5hd2_i2c_drv_setrate(struct hix5hd2_i2c_priv *priv) +{ + u32 rate, val; + u32 scl, sysclock; + + /* close all i2c interrupt */ + val = readl_relaxed(priv->regs + HIX5I2C_CTRL); + writel_relaxed(val & (~I2C_UNMASK_TOTAL), priv->regs + HIX5I2C_CTRL); + + rate = priv->freq; + sysclock = clk_get_rate(priv->clk); + scl = (sysclock / (rate * 2)) / 2 - 1; + writel_relaxed(scl, priv->regs + HIX5I2C_SCL_H); + writel_relaxed(scl, priv->regs + HIX5I2C_SCL_L); + + /* restore original interrupt*/ + writel_relaxed(val, priv->regs + HIX5I2C_CTRL); + + dev_dbg(priv->dev, "%s: sysclock=%d, rate=%d, scl=%d\n", + __func__, sysclock, rate, scl); +} + +static void hix5hd2_i2c_init(struct hix5hd2_i2c_priv *priv) +{ + hix5hd2_i2c_disable_irq(priv); + hix5hd2_i2c_drv_setrate(priv); + hix5hd2_i2c_clr_all_irq(priv); + hix5hd2_i2c_enable_irq(priv); +} + +static void hix5hd2_i2c_reset(struct hix5hd2_i2c_priv *priv) +{ + clk_disable_unprepare(priv->clk); + msleep(20); + clk_prepare_enable(priv->clk); + hix5hd2_i2c_init(priv); +} + +static int hix5hd2_i2c_wait_bus_idle(struct hix5hd2_i2c_priv *priv) +{ + unsigned long stop_time; + u32 int_status; + + /* wait for 100 milli seconds for the bus to be idle */ + stop_time = jiffies + msecs_to_jiffies(100); + do { + int_status = hix5hd2_i2c_clr_pend_irq(priv); + if (!(int_status & I2C_BUSY)) + return 0; + + usleep_range(50, 200); + } while (time_before(jiffies, stop_time)); + + return -EBUSY; +} + +static void hix5hd2_rw_over(struct hix5hd2_i2c_priv *priv) +{ + if (priv->state == HIX5I2C_STAT_SND_STOP) + dev_dbg(priv->dev, "%s: rw and send stop over\n", __func__); + else + dev_dbg(priv->dev, "%s: have not data to send\n", __func__); + + priv->state = HIX5I2C_STAT_RW_SUCCESS; + priv->err = 0; +} + +static void hix5hd2_rw_handle_stop(struct hix5hd2_i2c_priv *priv) +{ + if (priv->stop) { + priv->state = HIX5I2C_STAT_SND_STOP; + writel_relaxed(I2C_STOP, priv->regs + HIX5I2C_COM); + } else { + hix5hd2_rw_over(priv); + } +} + +static void hix5hd2_read_handle(struct hix5hd2_i2c_priv *priv) +{ + if (priv->msg_len == 1) { + /* 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 controller receive data will send ACK */ + writel_relaxed(I2C_READ, priv->regs + HIX5I2C_COM); + } else { + hix5hd2_rw_handle_stop(priv); + } +} + +static void hix5hd2_write_handle(struct hix5hd2_i2c_priv *priv) +{ + u8 data; + + if (priv->msg_len > 0) { + data = priv->msg->buf[priv->msg_idx++]; + writel_relaxed(data, priv->regs + HIX5I2C_TXR); + writel_relaxed(I2C_WRITE, priv->regs + HIX5I2C_COM); + } else { + hix5hd2_rw_handle_stop(priv); + } +} + +static int hix5hd2_rw_preprocess(struct hix5hd2_i2c_priv *priv) +{ + u8 data; + + if (priv->state == HIX5I2C_STAT_INIT) { + priv->state = HIX5I2C_STAT_RW; + } else if (priv->state == HIX5I2C_STAT_RW) { + if (priv->msg->flags & I2C_M_RD) { + data = readl_relaxed(priv->regs + HIX5I2C_RXR); + priv->msg->buf[priv->msg_idx++] = data; + } + priv->msg_len--; + } else { + dev_dbg(priv->dev, "%s: error: priv->state = %d, msg_len = %d\n", + __func__, priv->state, priv->msg_len); + return -EAGAIN; + } + return 0; +} + +static irqreturn_t hix5hd2_i2c_irq(int irqno, void *dev_id) +{ + struct hix5hd2_i2c_priv *priv = dev_id; + u32 int_status; + int ret; + + spin_lock(&priv->lock); + + int_status = hix5hd2_i2c_clr_pend_irq(priv); + + /* handle error */ + if (int_status & I2C_ARBITRATE_INTR) { + /* bus error */ + dev_dbg(priv->dev, "ARB bus loss\n"); + priv->err = -EAGAIN; + priv->state = HIX5I2C_STAT_RW_ERR; + goto stop; + } else if (int_status & I2C_ACK_INTR) { + /* ack error */ + dev_dbg(priv->dev, "No ACK from device\n"); + priv->err = -ENXIO; + priv->state = HIX5I2C_STAT_RW_ERR; + goto stop; + } + + if (int_status & I2C_OVER_INTR) { + if (priv->msg_len > 0) { + ret = hix5hd2_rw_preprocess(priv); + if (ret) { + priv->err = ret; + priv->state = HIX5I2C_STAT_RW_ERR; + goto stop; + } + if (priv->msg->flags & I2C_M_RD) + hix5hd2_read_handle(priv); + else + hix5hd2_write_handle(priv); + } else { + hix5hd2_rw_over(priv); + } + } + +stop: + if ((priv->state == HIX5I2C_STAT_RW_SUCCESS && + priv->msg->len == priv->msg_idx) || + (priv->state == HIX5I2C_STAT_RW_ERR)) { + hix5hd2_i2c_disable_irq(priv); + hix5hd2_i2c_clr_pend_irq(priv); + complete(&priv->msg_complete); + } + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static void hix5hd2_i2c_message_start(struct hix5hd2_i2c_priv *priv, int stop) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + hix5hd2_i2c_clr_all_irq(priv); + hix5hd2_i2c_enable_irq(priv); + + writel_relaxed(i2c_8bit_addr_from_msg(priv->msg), + priv->regs + HIX5I2C_TXR); + + writel_relaxed(I2C_WRITE | I2C_START, priv->regs + HIX5I2C_COM); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int hix5hd2_i2c_xfer_msg(struct hix5hd2_i2c_priv *priv, + struct i2c_msg *msgs, int stop) +{ + unsigned long time_left; + int ret; + + priv->msg = msgs; + priv->msg_idx = 0; + priv->msg_len = priv->msg->len; + priv->stop = stop; + priv->err = 0; + priv->state = HIX5I2C_STAT_INIT; + + reinit_completion(&priv->msg_complete); + hix5hd2_i2c_message_start(priv, stop); + + 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", + msgs->flags & I2C_M_RD ? "rx" : "tx", + priv->adap.timeout); + } + ret = priv->state; + + /* + * 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) + ret = hix5hd2_i2c_wait_bus_idle(priv); + + if (ret < 0) + hix5hd2_i2c_reset(priv); + + return priv->err; +} + +static int hix5hd2_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct hix5hd2_i2c_priv *priv = i2c_get_adapdata(adap); + int i, ret, stop; + + pm_runtime_get_sync(priv->dev); + + for (i = 0; i < num; i++, msgs++) { + 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; + } + + ret = num; + +out: + pm_runtime_put_autosuspend(priv->dev); + return ret; +} + +static u32 hix5hd2_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm hix5hd2_i2c_algorithm = { + .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; + unsigned int freq; + int irq, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (of_property_read_u32(np, "clock-frequency", &freq)) { + /* use 100k as default value */ + priv->freq = I2C_MAX_STANDARD_MODE_FREQ; + } else { + if (freq > I2C_MAX_FAST_MODE_FREQ) { + priv->freq = I2C_MAX_FAST_MODE_FREQ; + dev_warn(priv->dev, "use max freq %d instead\n", + I2C_MAX_FAST_MODE_FREQ); + } else { + priv->freq = freq; + } + } + + 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) + return irq; + + priv->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "cannot enable clock\n"); + return PTR_ERR(priv->clk); + } + + 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; + priv->adap.retries = 3; + priv->adap.dev.of_node = np; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; + i2c_set_adapdata(&priv->adap, priv); + platform_set_drvdata(pdev, priv); + spin_lock_init(&priv->lock); + init_completion(&priv->msg_complete); + + hix5hd2_i2c_init(priv); + + ret = devm_request_irq(&pdev->dev, irq, hix5hd2_i2c_irq, + IRQF_NO_SUSPEND, dev_name(&pdev->dev), priv); + if (ret != 0) { + dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", irq); + return ret; + } + + pm_runtime_set_autosuspend_delay(priv->dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(priv->dev); + pm_runtime_set_active(priv->dev); + pm_runtime_enable(priv->dev); + + ret = i2c_add_adapter(&priv->adap); + if (ret < 0) + goto err_runtime; + + return ret; + +err_runtime: + pm_runtime_disable(priv->dev); + pm_runtime_set_suspended(priv->dev); + + return ret; +} + +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); +} + +static int hix5hd2_i2c_runtime_suspend(struct device *dev) +{ + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int hix5hd2_i2c_runtime_resume(struct device *dev) +{ + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); + + clk_prepare_enable(priv->clk); + hix5hd2_i2c_init(priv); + + return 0; +} + +static const struct dev_pm_ops hix5hd2_i2c_pm_ops = { + RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend, + hix5hd2_i2c_runtime_resume, + NULL) +}; + +static const struct of_device_id hix5hd2_i2c_match[] = { + { .compatible = "hisilicon,hix5hd2-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_i2c_match); + +static struct platform_driver hix5hd2_i2c_driver = { + .probe = hix5hd2_i2c_probe, + .remove = hix5hd2_i2c_remove, + .driver = { + .name = "hix5hd2-i2c", + .pm = pm_ptr(&hix5hd2_i2c_pm_ops), + .of_match_table = hix5hd2_i2c_match, + }, +}; + +module_platform_driver(hix5hd2_i2c_driver); + +MODULE_DESCRIPTION("Hix5hd2 I2C Bus driver"); +MODULE_AUTHOR("Wei Yan "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hix5hd2-i2c"); diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c new file mode 100644 index 000000000..c60b73e93 --- /dev/null +++ b/drivers/i2c/busses/i2c-hydra.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c Support for the Apple `Hydra' Mac I/O + + Copyright (c) 1999-2004 Geert Uytterhoeven + + Based on i2c Support for Via Technologies 82C586B South Bridge + Copyright (c) 1998, 1999 Kyösti Mälkki + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */ +#define HYDRA_CPD_PD1 0x00000002 +#define HYDRA_CPD_PD2 0x00000004 +#define HYDRA_CPD_PD3 0x00000008 + +#define HYDRA_SCLK HYDRA_CPD_PD0 +#define HYDRA_SDAT HYDRA_CPD_PD1 +#define HYDRA_SCLK_OE 0x00000010 +#define HYDRA_SDAT_OE 0x00000020 + +static inline void pdregw(void *data, u32 val) +{ + struct Hydra *hydra = (struct Hydra *)data; + writel(val, &hydra->CachePD); +} + +static inline u32 pdregr(void *data) +{ + struct Hydra *hydra = (struct Hydra *)data; + return readl(&hydra->CachePD); +} + +static void hydra_bit_setscl(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SCLK_OE; + else { + val &= ~HYDRA_SCLK; + val |= HYDRA_SCLK_OE; + } + pdregw(data, val); +} + +static void hydra_bit_setsda(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SDAT_OE; + else { + val &= ~HYDRA_SDAT; + val |= HYDRA_SDAT_OE; + } + pdregw(data, val); +} + +static int hydra_bit_getscl(void *data) +{ + return (pdregr(data) & HYDRA_SCLK) != 0; +} + +static int hydra_bit_getsda(void *data) +{ + return (pdregr(data) & HYDRA_SDAT) != 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data hydra_bit_data = { + .setsda = hydra_bit_setsda, + .setscl = hydra_bit_setscl, + .getsda = hydra_bit_getsda, + .getscl = hydra_bit_getscl, + .udelay = 5, + .timeout = HZ +}; + +static struct i2c_adapter hydra_adap = { + .owner = THIS_MODULE, + .name = "Hydra i2c", + .algo_data = &hydra_bit_data, +}; + +static const struct pci_device_id hydra_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_HYDRA) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, hydra_ids); + +static int hydra_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + unsigned long base = pci_resource_start(dev, 0); + int res; + + if (!request_mem_region(base+offsetof(struct Hydra, CachePD), 4, + hydra_adap.name)) + return -EBUSY; + + hydra_bit_data.data = pci_ioremap_bar(dev, 0); + if (hydra_bit_data.data == NULL) { + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return -ENODEV; + } + + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + hydra_adap.dev.parent = &dev->dev; + res = i2c_bit_add_bus(&hydra_adap); + if (res < 0) { + iounmap(hydra_bit_data.data); + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return res; + } + return 0; +} + +static void hydra_remove(struct pci_dev *dev) +{ + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + i2c_del_adapter(&hydra_adap); + iounmap(hydra_bit_data.data); + release_mem_region(pci_resource_start(dev, 0)+ + offsetof(struct Hydra, CachePD), 4); +} + + +static struct pci_driver hydra_driver = { + .name = "hydra_smbus", + .id_table = hydra_ids, + .probe = hydra_probe, + .remove = hydra_remove, +}; + +module_pci_driver(hydra_driver); + +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c new file mode 100644 index 000000000..b29c99ed3 --- /dev/null +++ b/drivers/i2c/busses/i2c-i801.c @@ -0,0 +1,1769 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1998 - 2002 Frodo Looijaard , + Philip Edelbrock , and Mark D. Studebaker + + Copyright (C) 2007 - 2014 Jean Delvare + Copyright (C) 2010 Intel Corporation, + David Woodhouse + +*/ + +/* + * Supports the following Intel I/O Controller Hubs (ICH): + * + * I/O Block I2C + * region SMBus Block proc. block + * Chip name PCI ID size PEC buffer call read + * --------------------------------------------------------------------------- + * 82801AA (ICH) 0x2413 16 no no no no + * 82801AB (ICH0) 0x2423 16 no no no no + * 82801BA (ICH2) 0x2443 16 no no no no + * 82801CA (ICH3) 0x2483 32 soft no no no + * 82801DB (ICH4) 0x24c3 32 hard yes no no + * 82801E (ICH5) 0x24d3 32 hard yes yes yes + * 6300ESB 0x25a4 32 hard yes yes yes + * 82801F (ICH6) 0x266a 32 hard yes yes yes + * 6310ESB/6320ESB 0x269b 32 hard yes yes yes + * 82801G (ICH7) 0x27da 32 hard yes yes yes + * 82801H (ICH8) 0x283e 32 hard yes yes yes + * 82801I (ICH9) 0x2930 32 hard yes yes yes + * EP80579 (Tolapai) 0x5032 32 hard yes yes yes + * ICH10 0x3a30 32 hard yes yes yes + * ICH10 0x3a60 32 hard yes yes yes + * 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes + * 6 Series (PCH) 0x1c22 32 hard yes yes yes + * Patsburg (PCH) 0x1d22 32 hard yes yes yes + * Patsburg (PCH) IDF 0x1d70 32 hard yes yes yes + * Patsburg (PCH) IDF 0x1d71 32 hard yes yes yes + * Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes + * DH89xxCC (PCH) 0x2330 32 hard yes yes yes + * Panther Point (PCH) 0x1e22 32 hard yes yes yes + * Lynx Point (PCH) 0x8c22 32 hard yes yes yes + * Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes + * Avoton (SOC) 0x1f3c 32 hard yes yes yes + * Wellsburg (PCH) 0x8d22 32 hard yes yes yes + * Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes + * Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes + * Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes + * Coleto Creek (PCH) 0x23b0 32 hard yes yes yes + * Wildcat Point (PCH) 0x8ca2 32 hard yes yes yes + * Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes + * BayTrail (SOC) 0x0f12 32 hard yes yes yes + * Braswell (SOC) 0x2292 32 hard yes yes yes + * 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 + * Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes + * Gemini Lake (SOC) 0x31d4 32 hard yes yes yes + * Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes + * 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 + * Nova Lake-S (PCH) 0x6e23 32 hard yes yes yes + * + * Features supported by this driver: + * Software PEC no + * Hardware PEC yes + * Block buffer yes + * Block process call transaction yes + * I2C block read transaction yes (doesn't use the block buffer) + * Target mode no + * SMBus Host Notify yes + * Interrupt processing yes + * + * See the file Documentation/i2c/busses/i2c-i801.rst for details. + */ + +#define DRV_NAME "i801_smbus" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_I2C_I801_MUX +#include +#include +#endif + +/* I801 SMBus address offsets */ +#define SMBHSTSTS(p) (0 + (p)->smba) +#define SMBHSTCNT(p) (2 + (p)->smba) +#define SMBHSTCMD(p) (3 + (p)->smba) +#define SMBHSTADD(p) (4 + (p)->smba) +#define SMBHSTDAT0(p) (5 + (p)->smba) +#define SMBHSTDAT1(p) (6 + (p)->smba) +#define SMBBLKDAT(p) (7 + (p)->smba) +#define SMBPEC(p) (8 + (p)->smba) /* ICH3 and later */ +#define SMBAUXSTS(p) (12 + (p)->smba) /* ICH4 and later */ +#define SMBAUXCTL(p) (13 + (p)->smba) /* ICH4 and later */ +#define SMBSLVSTS(p) (16 + (p)->smba) /* ICH3 and later */ +#define SMBSLVCMD(p) (17 + (p)->smba) /* ICH3 and later */ +#define SMBNTFDADD(p) (20 + (p)->smba) /* ICH3 and later */ + +/* PCI Address Constants */ +#define SMBBAR_MMIO 0 +#define SMBBAR 4 +#define SMBHSTCFG 0x040 +#define TCOBASE 0x050 +#define TCOCTL 0x054 + +#define SBREG_SMBCTRL 0xc6000c +#define SBREG_SMBCTRL_DNV 0xcf000c + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN BIT(0) +#define SMBHSTCFG_SMB_SMI_EN BIT(1) +#define SMBHSTCFG_I2C_EN BIT(2) +#define SMBHSTCFG_SPD_WD BIT(4) + +/* TCO configuration bits for TCOCTL */ +#define TCOCTL_EN BIT(8) + +/* Auxiliary status register bits, ICH4+ only */ +#define SMBAUXSTS_CRCE BIT(0) +#define SMBAUXSTS_STCO BIT(1) + +/* Auxiliary control register bits, ICH4+ only */ +#define SMBAUXCTL_CRC BIT(0) +#define SMBAUXCTL_E32B BIT(1) + +/* 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 +#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) +#define SMBHSTCNT_KILL BIT(1) +#define SMBHSTCNT_LAST_BYTE BIT(5) +#define SMBHSTCNT_START BIT(6) +#define SMBHSTCNT_PEC_EN BIT(7) /* ICH3 and later */ + +/* I801 Hosts Status register bits */ +#define SMBHSTSTS_BYTE_DONE BIT(7) +#define SMBHSTSTS_INUSE_STS BIT(6) +#define SMBHSTSTS_SMBALERT_STS BIT(5) +#define SMBHSTSTS_FAILED BIT(4) +#define SMBHSTSTS_BUS_ERR BIT(3) +#define SMBHSTSTS_DEV_ERR BIT(2) +#define SMBHSTSTS_INTR BIT(1) +#define SMBHSTSTS_HOST_BUSY BIT(0) + +/* Host Notify Status register bits */ +#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 | \ + SMBHSTSTS_DEV_ERR) + +#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 */ +#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 */ +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c +#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 +#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_NOVA_LAKE_S_SMBUS 0x6e23 +#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 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0 0x8d7d +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1 0x8d7e +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 +#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 gpios[2]; /* Relative to gpio_chip->base */ + int n_gpios; +}; + +struct i801_priv { + struct i2c_adapter adapter; + 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 */ + struct completion done; + u8 status; + + /* Command state used by isr for byte-by-byte block transactions */ + u8 cmd; + bool is_read; + int count; + int len; + u8 *data; + +#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. Needs extra protection by acpi_lock. + */ + bool acpi_reserved; + struct mutex acpi_lock; +}; + +#define FEATURE_SMBUS_PEC BIT(0) +#define FEATURE_BLOCK_BUFFER BIT(1) +#define FEATURE_BLOCK_PROC BIT(2) +#define FEATURE_I2C_BLOCK_READ BIT(3) +#define FEATURE_IRQ BIT(4) +#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_SPT BIT(16) +#define FEATURE_TCO_CNL BIT(17) + +static const char *i801_feature_names[] = { + "SMBus PEC", + "Block buffer", + "Block process call", + "I2C block read", + "Interrupt", + "SMBus Host Notify", +}; + +static unsigned int disable_features; +module_param(disable_features, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n" + "\t\t 0x01 disable SMBus PEC\n" + "\t\t 0x02 disable the block buffer\n" + "\t\t 0x08 disable the I2C block read functionality\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, result; + + status = ioread8(SMBHSTSTS(priv)); + if (status & SMBHSTSTS_HOST_BUSY) { + pci_err(priv->pci_dev, "SMBus is busy, can't use it!\n"); + return -EBUSY; + } + + status &= STATUS_FLAGS; + if (status) { + pci_dbg(priv->pci_dev, "Clearing status flags (%02x)\n", status); + iowrite8(status, SMBHSTSTS(priv)); + } + + /* + * Clear CRC status if needed. + * During normal operation, i801_check_post() takes care + * of it after every operation. We do it here only in case + * the hardware was already in this state when the driver + * started. + */ + result = i801_check_and_clear_pec_error(priv); + if (result) + pci_dbg(priv->pci_dev, "Clearing aux status flag CRCE\n"); + + return 0; +} + +static int i801_check_post(struct i801_priv *priv, int status) +{ + int result = 0; + + /* + * If the SMBus is still busy, we give up + */ + if (unlikely(status < 0)) { + /* try to stop the current command */ + iowrite8(SMBHSTCNT_KILL, SMBHSTCNT(priv)); + status = i801_wait_intr(priv); + iowrite8(0, SMBHSTCNT(priv)); + + /* Check if it worked */ + if (status < 0 || !(status & SMBHSTSTS_FAILED)) + pci_dbg(priv->pci_dev, "Failed terminating the transaction\n"); + return -ETIMEDOUT; + } + + if (status & SMBHSTSTS_FAILED) { + result = -EIO; + pci_err(priv->pci_dev, "Transaction failed\n"); + } + if (status & SMBHSTSTS_DEV_ERR) { + /* + * This may be a PEC error, check and clear it. + * + * AUXSTS is handled differently from HSTSTS. + * For HSTSTS, i801_isr() or i801_wait_intr() + * has already cleared the error bits in hardware, + * and we are passed a copy of the original value + * in "status". + * For AUXSTS, the hardware register is left + * for us to handle here. + * This is asymmetric, slightly iffy, but safe, + * since all this code is serialized and the CRCE + * bit is harmless as long as it's cleared before + * the next operation. + */ + result = i801_check_and_clear_pec_error(priv); + if (result) { + pci_dbg(priv->pci_dev, "PEC error\n"); + } else { + result = -ENXIO; + pci_dbg(priv->pci_dev, "No response\n"); + } + } + if (status & SMBHSTSTS_BUS_ERR) { + result = -EAGAIN; + pci_dbg(priv->pci_dev, "Lost arbitration\n"); + } + + return result; +} + +static int i801_transaction(struct i801_priv *priv, int xact) +{ + unsigned long result; + const struct i2c_adapter *adap = &priv->adapter; + + if (priv->features & FEATURE_IRQ) { + reinit_completion(&priv->done); + iowrite8(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, + SMBHSTCNT(priv)); + result = wait_for_completion_timeout(&priv->done, adap->timeout); + return result ? priv->status : -ETIMEDOUT; + } + + iowrite8(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); + + 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 command) +{ + int len, status, xact; + + 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)); + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + iowrite8(len, SMBHSTDAT0(priv)); + ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */ + iowrite8_rep(SMBBLKDAT(priv), data->block + 1, len); + } + + status = i801_transaction(priv, xact); + if (status) + goto out; + + 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; + ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */ + ioread8_rep(SMBBLKDAT(priv), data->block + 1, len); + } +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) { + /* + * 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; + + priv->data[-1] = priv->len; + } + + /* Read next byte */ + if (priv->count < priv->len) + priv->data[priv->count++] = ioread8(SMBBLKDAT(priv)); + else + 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) + iowrite8(priv->cmd | SMBHSTCNT_LAST_BYTE, + SMBHSTCNT(priv)); + } else if (priv->count < priv->len - 1) { + /* Write next byte, except for IRQ after last byte */ + iowrite8(priv->data[++priv->count], SMBBLKDAT(priv)); + } +} + +static irqreturn_t i801_host_notify_isr(struct i801_priv *priv) +{ + unsigned short addr; + + addr = ioread8(SMBNTFDADD(priv)) >> 1; + + /* + * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba) + * always returns 0. Our current implementation doesn't provide + * data, so we just ignore it. + */ + i2c_handle_smbus_host_notify(&priv->adapter, addr); + + /* clear Host Notify bit and return */ + iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); + return IRQ_HANDLED; +} + +/* + * There are three kinds of interrupts: + * + * 1) i801 signals transaction completion with one of these interrupts: + * INTR - Success + * 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 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. + * + * 3) Host Notify interrupts + */ +static irqreturn_t i801_isr(int irq, void *dev_id) +{ + struct i801_priv *priv = dev_id; + u16 pcists; + u8 status; + + /* Confirm this is our interrupt */ + 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 = ioread8(SMBSLVSTS(priv)); + if (status & SMBSLVSTS_HST_NTFY_STS) + return i801_host_notify_isr(priv); + } + + status = ioread8(SMBHSTSTS(priv)); + if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE) + i801_isr_byte_done(priv); + + /* + * 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 &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS; + iowrite8(status, SMBHSTSTS(priv)); + + status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; + if (status) { + priv->status = status & STATUS_ERROR_FLAGS; + complete(&priv->done); + } + + return IRQ_HANDLED; +} + +/* + * For "byte-by-byte" block transactions: + * I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1 + * I2C read uses cmd=I801_I2C_BLOCK_DATA + */ +static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, + union i2c_smbus_data *data, + char read_write, int command) +{ + int i, len; + int smbcmd; + int status; + unsigned long result; + const struct i2c_adapter *adap = &priv->adapter; + + if (command == I2C_SMBUS_BLOCK_PROC_CALL) + return -EOPNOTSUPP; + + len = data->block[0]; + + if (read_write == I2C_SMBUS_WRITE) { + iowrite8(len, SMBHSTDAT0(priv)); + iowrite8(data->block[1], SMBBLKDAT(priv)); + } + + if (command == I2C_SMBUS_I2C_BLOCK_DATA && + read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + + if (priv->features & FEATURE_IRQ) { + priv->is_read = (read_write == I2C_SMBUS_READ); + if (len == 1 && priv->is_read) + smbcmd |= SMBHSTCNT_LAST_BYTE; + priv->cmd = smbcmd | SMBHSTCNT_INTREN; + priv->len = len; + priv->count = 0; + priv->data = &data->block[1]; + + 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; + } + + 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) + 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 (ioread8(SMBHSTSTS(priv)) & + SMBHSTSTS_HOST_BUSY) + iowrite8(SMBHSTSTS_BYTE_DONE, + SMBHSTSTS(priv)); + iowrite8(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + return -EPROTO; + } + data->block[0] = len; + } + + 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) + iowrite8(data->block[i+1], SMBBLKDAT(priv)); + + /* signals SMBBLKDAT ready */ + iowrite8(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); + } + + return i801_wait_intr(priv); +} + +static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write) +{ + iowrite8((addr << 1) | (read_write & 0x01), SMBHSTADD(priv)); +} + +/* 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 xact, ret; + + 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) { + 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; + } + + 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; + } + + 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 + i801_set_hstadd(priv, addr, read_write); + iowrite8(hstcmd, SMBHSTCMD(priv)); + + 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; +} + +/* Return negative errno on error. */ +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, ret; + struct i801_priv *priv = i2c_get_adapdata(adap); + + mutex_lock(&priv->acpi_lock); + if (priv->acpi_reserved) { + mutex_unlock(&priv->acpi_lock); + 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; + + if (hwpec) /* enable/disable hardware PEC */ + iowrite8(ioread8(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); + else + iowrite8(ioread8(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), + SMBAUXCTL(priv)); + + 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_simple_transaction(priv, data, addr, command, read_write, size); + + 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)); + /* + * 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)); + +out: + pm_runtime_put_autosuspend(&priv->pci_dev->dev); + mutex_unlock(&priv->acpi_lock); + return ret; +} + + +static u32 i801_func(struct i2c_adapter *adapter) +{ + struct i801_priv *priv = i2c_get_adapdata(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) ? + I2C_FUNC_SMBUS_HOST_NOTIFY : 0); +} + +static void i801_enable_host_notify(struct i2c_adapter *adapter) +{ + struct i801_priv *priv = i2c_get_adapdata(adapter); + + if (!(priv->features & FEATURE_HOST_NOTIFY)) + return; + + /* + * 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 */ + iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); +} + +static void i801_disable_host_notify(struct i801_priv *priv) +{ + if (!(priv->features & FEATURE_HOST_NOTIFY)) + return; + + iowrite8(priv->original_slvcmd, SMBSLVCMD(priv)); +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = i801_access, + .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_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) }, + { PCI_DEVICE_DATA(INTEL, NOVA_LAKE_S_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 __ro_after_init; + +/* Scan the system ROM for the signature "FJKEYINF" */ +static __init const void __iomem *bios_signature(const void __iomem *bios) +{ + ssize_t offset; + const unsigned char signature[] = "FJKEYINF"; + + for (offset = 0; offset < 0x10000; offset += 0x10) { + if (check_signature(bios + offset, signature, + sizeof(signature)-1)) + return bios + offset; + } + return NULL; +} + +static void __init input_apanel_init(void) +{ + void __iomem *bios; + const void __iomem *p; + + bios = ioremap(0xF0000, 0x10000); /* Can't fail */ + p = bios_signature(bios); + if (p) { + /* just use the first address */ + apanel_addr = readb(p + 8 + 3) >> 1; + } + iounmap(bios); +} + +struct dmi_onboard_device_info { + const char *name; + u8 type; + unsigned short i2c_addr; + const char *i2c_type; +}; + +static const struct dmi_onboard_device_info dmi_devices[] = { + { "Syleus", DMI_DEV_TYPE_OTHER, 0x73, "fscsyl" }, + { "Hermes", DMI_DEV_TYPE_OTHER, 0x73, "fscher" }, + { "Hades", DMI_DEV_TYPE_OTHER, 0x73, "fschds" }, +}; + +static void dmi_check_onboard_device(u8 type, const char *name, + struct i2c_adapter *adap) +{ + int i; + struct i2c_board_info info; + + for (i = 0; i < ARRAY_SIZE(dmi_devices); i++) { + /* & ~0x80, ignore enabled/disabled bit */ + if ((type & ~0x80) != dmi_devices[i].type) + continue; + if (strcasecmp(name, dmi_devices[i].name)) + continue; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = dmi_devices[i].i2c_addr; + strscpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE); + i2c_new_client_device(adap, &info); + break; + } +} + +/* We use our own function to check for onboard devices instead of + dmi_find_device() as some buggy BIOS's have the devices we are interested + in marked as disabled */ +static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) +{ + int i, count; + + if (dm->type != DMI_ENTRY_ONBOARD_DEVICE) + return; + + count = (dm->length - sizeof(struct dmi_header)) / 2; + for (i = 0; i < count; i++) { + const u8 *d = (char *)(dm + 1) + (i * 2); + const char *name = ((char *) dm) + dm->length; + u8 type = d[0]; + u8 s = d[1]; + + if (!s) + continue; + s--; + while (s > 0 && name[0]) { + name += strlen(name) + 1; + s--; + } + if (name[0] == 0) /* Bogus string reference */ + continue; + + dmi_check_onboard_device(type, name, adap); + } +} + +/* Register optional targets */ +static void i801_probe_optional_targets(struct i801_priv *priv) +{ + /* Only register targets on main SMBus channel */ + if (priv->features & FEATURE_IDF) + return; + + if (apanel_addr) { + struct i2c_board_info info = { + .addr = apanel_addr, + .type = "fujitsu_apanel", + }; + + 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_targets(struct i801_priv *priv) {} +#endif /* CONFIG_X86 && 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, + .gpios = { 52, 53 }, + .n_gpios = 2, +}; + +static struct i801_mux_config i801_mux_config_asus_z8_d18 = { + .gpio_chip = "gpio_ich", + .values = { 0x02, 0x03, 0x01 }, + .n_values = 3, + .gpios = { 52, 53 }, + .n_gpios = 2, +}; + +static const struct dmi_system_id mux_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8NA-D6(C)"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8P(N)E-D12(X)"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8NH-D12"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8PH-D12/IFB"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8NR-D12"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8P(N)H-D12"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8PG-D18"), + }, + .driver_data = &i801_mux_config_asus_z8_d18, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8PE-D18"), + }, + .driver_data = &i801_mux_config_asus_z8_d18, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "Z8PS-D12"), + }, + .driver_data = &i801_mux_config_asus_z8_d12, + }, + { } +}; + +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 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; + struct gpiod_lookup_table *lookup; + const struct dmi_system_id *id; + int i; + + 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.idle = I2C_MUX_GPIO_NO_IDLE; + + /* 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_NONE, &gpio_data, + sizeof(struct i2c_mux_gpio_platform_data)); + if (IS_ERR(priv->mux_pdev)) { + gpiod_remove_lookup_table(lookup); + devm_kfree(dev, lookup); + dev_err(dev, "Failed to register i2c-mux-gpio device\n"); + } else { + priv->lookup = lookup; + } +} + +static void i801_del_mux(struct i801_priv *priv) +{ + 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 struct platform_device * +i801_add_tco_spt(struct pci_dev *pci_dev, struct resource *tco_res) +{ + static const struct itco_wdt_platform_data pldata = { + .name = "Intel PCH", + .version = 4, + }; + struct resource *res; + int ret; + + /* + * We must access the NO_REBOOT bit over the Primary to Sideband + * (P2SB) bridge. + */ + + res = &tco_res[1]; + ret = p2sb_bar(pci_dev->bus, 0, res); + if (ret) + return ERR_PTR(ret); + + 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)); +} + +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, + }; + + 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[2], *res; + u32 tco_base, tco_ctl; + + /* 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); + pci_read_config_dword(pci_dev, TCOCTL, &tco_ctl); + if (!(tco_ctl & TCOCTL_EN)) + return; + + memset(tco_res, 0, sizeof(tco_res)); + /* + * Always populate the main iTCO IO resource here. The second entry + * for NO_REBOOT MMIO is filled by the SPT specific function. + */ + res = &tco_res[0]; + res->start = tco_base & ~1; + res->end = res->start + 32 - 1; + res->flags = IORESOURCE_IO; + + if (priv->features & FEATURE_TCO_CNL) + priv->tco_pdev = i801_add_tco_cnl(pci_dev, tco_res); + else + priv->tco_pdev = i801_add_tco_spt(pci_dev, tco_res); + + 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 >= pci_resource_start(priv->pci_dev, SMBBAR) && + address <= pci_resource_end(priv->pci_dev, SMBBAR); +} + +static acpi_status +i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits, + u64 *value, void *handler_context, void *region_context) +{ + struct i801_priv *priv = handler_context; + struct pci_dev *pdev = priv->pci_dev; + acpi_status status; + + /* + * Once BIOS AML code touches the OpRegion we warn and inhibit any + * further access from the driver itself. This device is now owned + * by the system firmware. + */ + mutex_lock(&priv->acpi_lock); + + if (!priv->acpi_reserved && i801_acpi_is_smbus_ioport(priv, address)) { + priv->acpi_reserved = true; + + 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 + * suspending automatically from now on. + */ + pm_runtime_get_sync(&pdev->dev); + } + + if ((function & ACPI_IO_MASK) == ACPI_READ) + status = acpi_os_read_port(address, (u32 *)value, bits); + else + status = acpi_os_write_port(address, (u32)*value, bits); + + mutex_unlock(&priv->acpi_lock); + + return status; +} + +static int i801_acpi_probe(struct i801_priv *priv) +{ + acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev); + acpi_status status; + + 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) +{ + acpi_handle ah = ACPI_HANDLE(&priv->pci_dev->dev); + + 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) +{ + int err, i, bar = SMBBAR; + struct i801_priv *priv; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + priv->adapter.class = I2C_CLASS_HWMON; + priv->adapter.algo = &smbus_algorithm; + priv->adapter.dev.parent = &dev->dev; + acpi_use_parent_companion(&priv->adapter.dev); + priv->adapter.retries = 3; + mutex_init(&priv->acpi_lock); + + priv->pci_dev = dev; + 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)) + pci_notice(dev, "%s disabled by user\n", i801_feature_names[i]); + } + priv->features &= ~disable_features; + + /* 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) { + pci_err(dev, "Failed to enable SMBus PCI device (%d)\n", err); + return err; + } + + /* Determine the address of the SMBus area */ + 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; + + 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 PTR_ERR(priv->smba); + } + + 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 (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 (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)) + iowrite8(ioread8(SMBAUXCTL(priv)) & + ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + + /* Default timeout in interrupt mode: 200 ms */ + priv->adapter.timeout = HZ / 5; + + if (dev->irq == IRQ_NOTCONNECTED) + priv->features &= ~FEATURE_IRQ; + + if (priv->features & FEATURE_IRQ) { + u16 pcists; + + /* Complain if an interrupt is already pending */ + 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_completion(&priv->done); + + err = devm_request_irq(&dev->dev, dev->irq, i801_isr, + IRQF_SHARED, DRV_NAME, priv); + if (err) { + pci_err(dev, "Failed to allocate irq %d: %d\n", dev->irq, err); + priv->features &= ~FEATURE_IRQ; + } + } + 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 %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); + + /* 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); + pm_runtime_allow(&dev->dev); + + return 0; +} + +static void i801_remove(struct pci_dev *dev) +{ + struct i801_priv *priv = pci_get_drvdata(dev); + + i801_disable_host_notify(priv); + i801_del_mux(priv); + i2c_del_adapter(&priv->adapter); + i801_acpi_remove(priv); + + 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) + */ +} + +static void i801_shutdown(struct pci_dev *dev) +{ + struct i801_priv *priv = pci_get_drvdata(dev); + + i801_disable_host_notify(priv); + /* Restore config registers to avoid hard hang on some systems */ + i801_restore_regs(priv); +} + +static int i801_suspend(struct device *dev) +{ + struct i801_priv *priv = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&priv->adapter); + i801_restore_regs(priv); + + return 0; +} + +static int i801_resume(struct device *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; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume); + +static struct pci_driver i801_driver = { + .name = DRV_NAME, + .id_table = i801_ids, + .probe = i801_probe, + .remove = i801_remove, + .shutdown = i801_shutdown, + .driver = { + .pm = pm_sleep_ptr(&i801_pm_ops), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +static int __init i2c_i801_init(struct pci_driver *drv) +{ + if (dmi_name_in_vendors("FUJITSU")) + input_apanel_init(); + return pci_register_driver(drv); +} + +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("I801 SMBus driver"); +MODULE_LICENSE("GPL"); + +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 new file mode 100644 index 000000000..7c70e8bda --- /dev/null +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/i2c/busses/i2c-ibm_iic.c + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003, 2004 Zultys Technologies. + * Eugene Surovegin or + * + * Copyright (c) 2008 PIKA Technologies + * Sean MacLennan + * + * Based on original work by + * Ian DaSilva + * Armin Kuster + * Matt Porter + * + * Copyright 2000-2003 MontaVista Software Inc. + * + * Original driver version was highly leveraged from i2c-elektor.c + * + * Copyright 1995-97 Simon G. Vogl + * 1998-99 Hans Berglund + * + * With some changes from Kyösti Mälkki + * and even Frodo Looijaard + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-ibm_iic.h" + +#define DRIVER_VERSION "2.2" + +MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static bool iic_force_poll; +module_param(iic_force_poll, bool, 0); +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); + +static bool iic_force_fast; +module_param(iic_force_fast, bool, 0); +MODULE_PARM_DESC(iic_force_fast, "Force fast mode (400 kHz)"); + +#define DBG_LEVEL 0 + +#ifdef DBG +#undef DBG +#endif + +#ifdef DBG2 +#undef DBG2 +#endif + +#if DBG_LEVEL > 0 +# define DBG(f,x...) printk(KERN_DEBUG "ibm-iic" f, ##x) +#else +# define DBG(f,x...) ((void)0) +#endif +#if DBG_LEVEL > 1 +# define DBG2(f,x...) DBG(f, ##x) +#else +# define DBG2(f,x...) ((void)0) +#endif +#if DBG_LEVEL > 2 +static void dump_iic_regs(const char* header, struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + printk(KERN_DEBUG "ibm-iic%d: %s\n", dev->idx, header); + printk(KERN_DEBUG + " cntl = 0x%02x, mdcntl = 0x%02x\n" + " sts = 0x%02x, extsts = 0x%02x\n" + " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" + " xtcntlss = 0x%02x, directcntl = 0x%02x\n", + in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), + in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), + in_8(&iic->xtcntlss), in_8(&iic->directcntl)); +} +# define DUMP_REGS(h,dev) dump_iic_regs((h),(dev)) +#else +# define DUMP_REGS(h,dev) ((void)0) +#endif + +/* Bus timings (in ns) for bit-banging */ +static struct ibm_iic_timings { + unsigned int hd_sta; + unsigned int su_sto; + unsigned int low; + unsigned int high; + unsigned int buf; +} timings [] = { +/* Standard mode (100 KHz) */ +{ + .hd_sta = 4000, + .su_sto = 4000, + .low = 4700, + .high = 4000, + .buf = 4700, +}, +/* Fast mode (400 KHz) */ +{ + .hd_sta = 600, + .su_sto = 600, + .low = 1300, + .high = 600, + .buf = 1300, +}}; + +/* Enable/disable interrupt generation */ +static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable) +{ + out_8(&dev->vaddr->intmsk, enable ? INTRMSK_EIMTC : 0); +} + +/* + * Initialize IIC interface. + */ +static void iic_dev_init(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + + DBG("%d: init\n", dev->idx); + + /* Clear remote target address */ + out_8(&iic->lmadr, 0); + out_8(&iic->hmadr, 0); + + /* Clear local target address */ + out_8(&iic->lsadr, 0); + out_8(&iic->hsadr, 0); + + /* Clear status & extended status */ + out_8(&iic->sts, STS_SCMP | STS_IRQA); + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | EXTSTS_LA + | EXTSTS_ICT | EXTSTS_XFRA); + + /* Set clock divider */ + out_8(&iic->clkdiv, dev->clckdiv); + + /* Clear transfer count */ + out_8(&iic->xfrcnt, 0); + + /* Clear extended control and status */ + out_8(&iic->xtcntlss, XTCNTLSS_SRC | XTCNTLSS_SRS | XTCNTLSS_SWC + | XTCNTLSS_SWS); + + /* Clear control register */ + out_8(&iic->cntl, 0); + + /* Enable interrupts if possible */ + iic_interrupt_mode(dev, dev->irq >= 0); + + /* Set mode control */ + out_8(&iic->mdcntl, MDCNTL_FMDB | MDCNTL_EINT | MDCNTL_EUBS + | (dev->fast_mode ? MDCNTL_FSM : 0)); + + DUMP_REGS("iic_init", dev); +} + +/* + * Reset IIC interface + */ +static void iic_dev_reset(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + int i; + u8 dc; + + DBG("%d: soft reset\n", dev->idx); + DUMP_REGS("reset", dev); + + /* Place chip in the reset state */ + out_8(&iic->xtcntlss, XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&iic->directcntl); + if (!DIRCTNL_FREE(dc)){ + DBG("%d: trying to regain bus control\n", dev->idx); + + /* Try to set bus free state */ + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i){ + dc = in_8(&iic->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + udelay(10); + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + + /* be nice */ + cond_resched(); + } + } + + /* Remove reset */ + out_8(&iic->xtcntlss, 0); + + /* Reinitialize interface */ + iic_dev_init(dev); +} + +/* + * Do 0-length transaction using bit-banging through IIC_DIRECTCNTL register. + */ + +/* Wait for SCL and/or SDA to be high */ +static int iic_dc_wait(volatile struct iic_regs __iomem *iic, u8 mask) +{ + unsigned long x = jiffies + HZ / 28 + 2; + while ((in_8(&iic->directcntl) & mask) != mask){ + if (unlikely(time_after(jiffies, x))) + return -1; + cond_resched(); + } + return 0; +} + +static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + const struct ibm_iic_timings *t = &timings[dev->fast_mode ? 1 : 0]; + u8 mask, v, sda; + int i, res; + + /* Only 7-bit addresses are supported */ + if (unlikely(p->flags & I2C_M_TEN)){ + DBG("%d: smbus_quick - 10 bit addresses are not supported\n", + dev->idx); + return -EINVAL; + } + + DBG("%d: smbus_quick(0x%02x)\n", dev->idx, p->addr); + + /* Reset IIC interface */ + out_8(&iic->xtcntlss, XTCNTLSS_SRST); + + /* Wait for bus to become free */ + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSDA | DIRCNTL_MSC))) + goto err; + ndelay(t->buf); + + /* START */ + out_8(&iic->directcntl, DIRCNTL_SCC); + sda = 0; + ndelay(t->hd_sta); + + /* Send address */ + v = i2c_8bit_addr_from_msg(p); + for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){ + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + sda = (v & mask) ? DIRCNTL_SDAC : 0; + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + + out_8(&iic->directcntl, DIRCNTL_SCC | sda); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + ndelay(t->high); + } + + /* ACK */ + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + out_8(&iic->directcntl, DIRCNTL_SDAC); + ndelay(t->low / 2); + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + res = (in_8(&iic->directcntl) & DIRCNTL_MSDA) ? -EREMOTEIO : 1; + ndelay(t->high); + + /* STOP */ + out_8(&iic->directcntl, 0); + ndelay(t->low); + out_8(&iic->directcntl, DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + ndelay(t->su_sto); + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + + ndelay(t->buf); + + DBG("%d: smbus_quick -> %s\n", dev->idx, res ? "NACK" : "ACK"); +out: + /* Remove reset */ + out_8(&iic->xtcntlss, 0); + + /* Reinitialize interface */ + iic_dev_init(dev); + + return res; +err: + DBG("%d: smbus_quick - bus is stuck\n", dev->idx); + res = -EREMOTEIO; + goto out; +} + +/* + * IIC interrupt handler + */ +static irqreturn_t iic_handler(int irq, void *dev_id) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)dev_id; + volatile struct iic_regs __iomem *iic = dev->vaddr; + + DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n", + dev->idx, in_8(&iic->sts), in_8(&iic->extsts)); + + /* Acknowledge IRQ and wakeup iic_wait_for_tc */ + out_8(&iic->sts, STS_IRQA | STS_SCMP); + wake_up_interruptible(&dev->wq); + + return IRQ_HANDLED; +} + +/* + * 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) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + + if (unlikely(in_8(&iic->sts) & STS_ERR)){ + DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx, + in_8(&iic->extsts)); + + /* Clear errors and possible pending IRQs */ + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | + EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA); + + /* Flush controller data buffer */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + + /* Is bus free? + * If error happened during combined xfer + * IIC interface is usually stuck in some strange + * state, the only way out - soft reset. + */ + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: bus is stuck, resetting\n", dev->idx); + iic_dev_reset(dev); + } + return -EREMOTEIO; + } + else + return in_8(&iic->xfrcnt) & XFRCNT_MTC_MASK; +} + +/* + * Try to abort active transfer. + */ +static void iic_abort_xfer(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + unsigned long x; + + DBG("%d: iic_abort_xfer\n", dev->idx); + + out_8(&iic->cntl, CNTL_HMT); + + /* + * Wait for the abort command to complete. + * It's not worth to be optimized, just poll (timeout >= 1 tick) + */ + x = jiffies + 2; + while ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + if (time_after(jiffies, x)){ + DBG("%d: abort timeout, resetting...\n", dev->idx); + iic_dev_reset(dev); + return; + } + schedule(); + } + + /* Just to clear errors */ + iic_xfer_result(dev); +} + +/* + * 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) + */ +static int iic_wait_for_tc(struct ibm_iic_private* dev){ + + volatile struct iic_regs __iomem *iic = dev->vaddr; + int ret = 0; + + if (dev->irq >= 0){ + /* Interrupt mode */ + ret = wait_event_interruptible_timeout(dev->wq, + !(in_8(&iic->sts) & STS_PT), dev->adap.timeout); + + if (unlikely(ret < 0)) + DBG("%d: wait interrupted\n", dev->idx); + else if (unlikely(in_8(&iic->sts) & STS_PT)){ + DBG("%d: wait timeout\n", dev->idx); + ret = -ETIMEDOUT; + } + } + else { + /* Polling mode */ + unsigned long x = jiffies + dev->adap.timeout; + + while (in_8(&iic->sts) & STS_PT){ + if (unlikely(time_after(jiffies, x))){ + DBG("%d: poll timeout\n", dev->idx); + ret = -ETIMEDOUT; + break; + } + + if (signal_pending(current)){ + DBG("%d: poll interrupted\n", dev->idx); + ret = -ERESTARTSYS; + break; + } + schedule(); + } + } + + if (unlikely(ret < 0)) + iic_abort_xfer(dev); + else + ret = iic_xfer_result(dev); + + DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret); + + return ret; +} + +static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, + int combined_xfer) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + char* buf = pm->buf; + int i, j, loops, ret = 0; + int len = pm->len; + + u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT; + if (pm->flags & I2C_M_RD) + cntl |= CNTL_RW; + + loops = (len + 3) / 4; + for (i = 0; i < loops; ++i, len -= 4){ + int count = len > 4 ? 4 : len; + u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT); + + if (!(cntl & CNTL_RW)) + for (j = 0; j < count; ++j) + out_8((void __iomem *)&iic->mdbuf, *buf++); + + if (i < loops - 1) + cmd |= CNTL_CHT; + else if (combined_xfer) + cmd |= CNTL_RPST; + + DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd); + + /* Start transfer */ + out_8(&iic->cntl, cmd); + + /* Wait for completion */ + ret = iic_wait_for_tc(dev); + + if (unlikely(ret < 0)) + break; + else if (unlikely(ret != count)){ + DBG("%d: xfer_bytes, requested %d, transferred %d\n", + dev->idx, count, ret); + + /* If it's not a last part of xfer, abort it */ + if (combined_xfer || (i < loops - 1)) + iic_abort_xfer(dev); + + ret = -EREMOTEIO; + break; + } + + if (cntl & CNTL_RW) + for (j = 0; j < count; ++j) + *buf++ = in_8((void __iomem *)&iic->mdbuf); + } + + return ret > 0 ? 0 : ret; +} + +/* 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; + + DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, + msg->addr, msg->flags & I2C_M_TEN ? 10 : 7); + + if (msg->flags & I2C_M_TEN) { + out_8(&iic->cntl, CNTL_AMD); + 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, i2c_8bit_addr_from_msg(msg) & ~I2C_M_RD); + } +} + +static inline int iic_invalid_address(const struct i2c_msg* p) +{ + return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f)); +} + +static inline int iic_address_neq(const struct i2c_msg* p1, + const struct i2c_msg* p2) +{ + return (p1->addr != p2->addr) + || ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN)); +} + +/* + * 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) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)(i2c_get_adapdata(adap)); + volatile struct iic_regs __iomem *iic = dev->vaddr; + int i, ret = 0; + + DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num); + + /* Check the sanity of the passed messages. + * Uhh, generic i2c layer is more suitable place for such code... + */ + if (unlikely(iic_invalid_address(&msgs[0]))){ + DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, + msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7); + return -EINVAL; + } + for (i = 0; i < num; ++i){ + if (unlikely(msgs[i].len <= 0)){ + if (num == 1 && !msgs[0].len){ + /* Special case for I2C_SMBUS_QUICK emulation. + * IBM IIC doesn't support 0-length transactions + * so we have to emulate them using bit-banging. + */ + return iic_smbus_quick(dev, &msgs[0]); + } + DBG("%d: invalid len %d in msg[%d]\n", dev->idx, + msgs[i].len, i); + return -EINVAL; + } + if (unlikely(iic_address_neq(&msgs[0], &msgs[i]))){ + DBG("%d: invalid addr in msg[%d]\n", dev->idx, i); + return -EINVAL; + } + } + + /* Check bus state */ + if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){ + DBG("%d: iic_xfer, bus is not free\n", dev->idx); + + /* Usually it means something serious has happened. + * We *cannot* have unfinished previous transfer + * so it doesn't make any sense to try to stop it. + * Probably we were not able to recover from the + * previous error. + * The only *reasonable* thing I can think of here + * is soft reset. --ebs + */ + iic_dev_reset(dev); + + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: iic_xfer, bus is still not free\n", dev->idx); + return -EREMOTEIO; + } + } + else { + /* Flush controller data buffer (just in case) */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + } + + /* Load target address */ + iic_address(dev, &msgs[0]); + + /* Do real transfer */ + for (i = 0; i < num && !ret; ++i) + ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1); + + return ret < 0 ? ret : num; +} + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm iic_algo = { + .xfer = iic_xfer, + .functionality = iic_func +}; + +/* + * Calculates IICx_CLCKDIV value for a specific OPB clock frequency + */ +static inline u8 iic_clckdiv(unsigned int opb) +{ + /* Compatibility kludge, should go away after all cards + * are fixed to fill correct value for opbfreq. + * Previous driver version used hardcoded divider value 4, + * it corresponds to OPB frequency from the range (40, 50] MHz + */ + if (!opb){ + printk(KERN_WARNING "ibm-iic: using compatibility value for OPB freq," + " fix your board specific setup\n"); + opb = 50000000; + } + + /* Convert to MHz */ + opb /= 1000000; + + if (opb < 20 || opb > 150){ + printk(KERN_WARNING "ibm-iic: invalid OPB clock frequency %u MHz\n", + opb); + opb = opb < 20 ? 20 : 150; + } + return (u8)((opb + 9) / 10 - 1); +} + +static int iic_request_irq(struct platform_device *ofdev, + struct ibm_iic_private *dev) +{ + struct device_node *np = ofdev->dev.of_node; + int irq; + + if (iic_force_poll) + return 0; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&ofdev->dev, "irq_of_parse_and_map failed\n"); + return 0; + } + + /* Disable interrupts until we finish initialization, assumes + * level-sensitive IRQ setup... + */ + iic_interrupt_mode(dev, 0); + if (request_irq(irq, iic_handler, 0, "IBM IIC", dev)) { + dev_err(&ofdev->dev, "request_irq %d failed\n", irq); + /* Fallback to the polling mode */ + return 0; + } + + return irq; +} + +/* + * Register single IIC interface + */ +static int iic_probe(struct platform_device *ofdev) +{ + struct device_node *np = ofdev->dev.of_node; + struct ibm_iic_private *dev; + struct i2c_adapter *adap; + const u32 *freq; + int ret; + + dev = kzalloc_obj(*dev); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(ofdev, dev); + + dev->vaddr = of_iomap(np, 0); + if (dev->vaddr == NULL) { + dev_err(&ofdev->dev, "failed to iomap device\n"); + ret = -ENXIO; + goto error_cleanup; + } + + init_waitqueue_head(&dev->wq); + + dev->irq = iic_request_irq(ofdev, dev); + if (!dev->irq) + dev_warn(&ofdev->dev, "using polling mode\n"); + + /* Board specific settings */ + if (iic_force_fast || of_get_property(np, "fast-mode", NULL)) + dev->fast_mode = 1; + + freq = of_get_property(np, "clock-frequency", NULL); + if (freq == NULL) { + freq = of_get_property(np->parent, "clock-frequency", NULL); + if (freq == NULL) { + dev_err(&ofdev->dev, "Unable to get bus frequency\n"); + ret = -EINVAL; + goto error_cleanup; + } + } + + dev->clckdiv = iic_clckdiv(*freq); + dev_dbg(&ofdev->dev, "clckdiv = %d\n", dev->clckdiv); + + /* Initialize IIC interface */ + iic_dev_init(dev); + + /* Register it with i2c layer */ + adap = &dev->adap; + adap->dev.parent = &ofdev->dev; + adap->dev.of_node = of_node_get(np); + strscpy(adap->name, "IBM IIC", sizeof(adap->name)); + i2c_set_adapdata(adap, dev); + adap->class = I2C_CLASS_HWMON; + adap->algo = &iic_algo; + adap->timeout = HZ; + + ret = i2c_add_adapter(adap); + if (ret < 0) + goto error_cleanup; + + dev_info(&ofdev->dev, "using %s mode\n", + dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); + + return 0; + +error_cleanup: + if (dev->irq) { + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + + if (dev->vaddr) + iounmap(dev->vaddr); + + kfree(dev); + return ret; +} + +/* + * Cleanup initialized IIC interface + */ +static void iic_remove(struct platform_device *ofdev) +{ + struct ibm_iic_private *dev = platform_get_drvdata(ofdev); + + i2c_del_adapter(&dev->adap); + + if (dev->irq) { + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + + iounmap(dev->vaddr); + kfree(dev); +} + +static const struct of_device_id ibm_iic_match[] = { + { .compatible = "ibm,iic", }, + {} +}; +MODULE_DEVICE_TABLE(of, ibm_iic_match); + +static struct platform_driver ibm_iic_driver = { + .driver = { + .name = "ibm-iic", + .of_match_table = ibm_iic_match, + }, + .probe = iic_probe, + .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 new file mode 100644 index 000000000..f0288889c --- /dev/null +++ b/drivers/i2c/busses/i2c-ibm_iic.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * drivers/i2c/busses/i2c-ibm_iic.h + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003 Zultys Technologies. + * Eugene Surovegin or + * + * Based on original work by + * Ian DaSilva + * Armin Kuster + * Matt Porter + * + * Copyright 2000-2003 MontaVista Software Inc. + */ +#ifndef __I2C_IBM_IIC_H_ +#define __I2C_IBM_IIC_H_ + +#include + +struct iic_regs { + u16 mdbuf; + u16 sbbuf; + u8 lmadr; + u8 hmadr; + u8 cntl; + u8 mdcntl; + u8 sts; + u8 extsts; + u8 lsadr; + u8 hsadr; + u8 clkdiv; + u8 intmsk; + u8 xfrcnt; + u8 xtcntlss; + u8 directcntl; +}; + +struct ibm_iic_private { + struct i2c_adapter adap; + volatile struct iic_regs __iomem *vaddr; + wait_queue_head_t wq; + int idx; + int irq; + int fast_mode; + u8 clckdiv; +}; + +/* IICx_CNTL register */ +#define CNTL_HMT 0x80 +#define CNTL_AMD 0x40 +#define CNTL_TCT_MASK 0x30 +#define CNTL_TCT_SHIFT 4 +#define CNTL_RPST 0x08 +#define CNTL_CHT 0x04 +#define CNTL_RW 0x02 +#define CNTL_PT 0x01 + +/* IICx_MDCNTL register */ +#define MDCNTL_FSDB 0x80 +#define MDCNTL_FMDB 0x40 +#define MDCNTL_EGC 0x20 +#define MDCNTL_FSM 0x10 +#define MDCNTL_ESM 0x08 +#define MDCNTL_EINT 0x04 +#define MDCNTL_EUBS 0x02 +#define MDCNTL_HSCL 0x01 + +/* IICx_STS register */ +#define STS_SSS 0x80 +#define STS_SLPR 0x40 +#define STS_MDBS 0x20 +#define STS_MDBF 0x10 +#define STS_SCMP 0x08 +#define STS_ERR 0x04 +#define STS_IRQA 0x02 +#define STS_PT 0x01 + +/* IICx_EXTSTS register */ +#define EXTSTS_IRQP 0x80 +#define EXTSTS_BCS_MASK 0x70 +#define EXTSTS_BCS_FREE 0x40 +#define EXTSTS_IRQD 0x08 +#define EXTSTS_LA 0x04 +#define EXTSTS_ICT 0x02 +#define EXTSTS_XFRA 0x01 + +/* IICx_INTRMSK register */ +#define INTRMSK_EIRC 0x80 +#define INTRMSK_EIRS 0x40 +#define INTRMSK_EIWC 0x20 +#define INTRMSK_EIWS 0x10 +#define INTRMSK_EIHE 0x08 +#define INTRMSK_EIIC 0x04 +#define INTRMSK_EITA 0x02 +#define INTRMSK_EIMTC 0x01 + +/* IICx_XFRCNT register */ +#define XFRCNT_MTC_MASK 0x07 + +/* IICx_XTCNTLSS register */ +#define XTCNTLSS_SRC 0x80 +#define XTCNTLSS_SRS 0x40 +#define XTCNTLSS_SWC 0x20 +#define XTCNTLSS_SWS 0x10 +#define XTCNTLSS_SRST 0x01 + +/* IICx_DIRECTCNTL register */ +#define DIRCNTL_SDAC 0x08 +#define DIRCNTL_SCC 0x04 +#define DIRCNTL_MSDA 0x02 +#define DIRCNTL_MSC 0x01 + +/* Check if we really control the I2C bus and bus is free */ +#define DIRCTNL_FREE(v) (((v) & 0x0f) == 0x0f) + +#endif /* __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 000000000..55496e48c --- /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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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, + <c2990_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[] = { + { .id = ZORRO_ID(VMC, 15, 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 "); +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 new file mode 100644 index 000000000..88192c25c --- /dev/null +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -0,0 +1,1503 @@ +// 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. + * + * There are three ways that this I2C controller can be driven: + * + * - Raw control of the SDA and SCK signals. + * + * This corresponds to MODE_RAW, which takes control of the signals + * directly for a certain number of clock cycles (the INT_TIMING + * interrupt can be used for timing). + * + * - Atomic commands. A low level I2C symbol (such as generate + * start/stop/ack/nack bit, generate byte, receive byte, and receive + * ACK) is given to the hardware, with detection of completion by bits + * in the LINESTAT register. + * + * This mode of operation is used by MODE_ATOMIC, which uses an I2C + * state machine in the interrupt handler to compose/react to I2C + * transactions using atomic mode commands, and also by MODE_SEQUENCE, + * which emits a simple fixed sequence of atomic mode commands. + * + * Due to software control, the use of atomic commands usually results + * in suboptimal use of the bus, with gaps between the I2C symbols while + * the driver decides what to do next. + * + * - Automatic mode. A bus address, and whether to read/write is + * specified, and the hardware takes care of the I2C state machine, + * using a FIFO to send/receive bytes of data to an I2C slave. The + * driver just has to keep the FIFO drained or filled in response to the + * appropriate FIFO interrupts. + * + * This corresponds to MODE_AUTOMATIC, which manages the FIFOs and deals + * with control of repeated start bits between I2C messages. + * + * Use of automatic mode and the FIFO can make much more efficient use + * of the bus compared to individual atomic commands, with potentially + * no wasted time between I2C symbols or I2C messages. + * + * In most cases MODE_AUTOMATIC is used, however if any of the messages in + * a transaction are zero byte writes (e.g. used by i2cdetect for probing + * the bus), MODE_ATOMIC must be used since automatic mode is normally + * started by the writing of data into the FIFO. + * + * The other modes are used in specific circumstances where MODE_ATOMIC and + * MODE_AUTOMATIC aren't appropriate. MODE_RAW is used to implement a bus + * recovery routine. MODE_SEQUENCE is used to reset the bus and make sure + * it is in a sane state. + * + * Notice that the driver implements a timer-based timeout mechanism. + * The reason for this mechanism is to reduce the number of interrupts + * received in automatic mode. + * + * The driver would get a slave event and transaction done interrupts for + * each atomic mode command that gets completed. However, these events are + * not needed in automatic mode, becase those atomic mode commands are + * managed automatically by the hardware. + * + * In practice, normal I2C transactions will be complete well before you + * get the timer interrupt, as the timer is re-scheduled during FIFO + * maintenance and disabled after the transaction is complete. + * + * In this way normal automatic mode operation isn't impacted by + * unnecessary interrupts, but the exceptional abort condition can still be + * detected (with a slight delay). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ + +#define SCB_STATUS_REG 0x00 +#define SCB_OVERRIDE_REG 0x04 +#define SCB_READ_ADDR_REG 0x08 +#define SCB_READ_COUNT_REG 0x0c +#define SCB_WRITE_ADDR_REG 0x10 +#define SCB_READ_DATA_REG 0x14 +#define SCB_WRITE_DATA_REG 0x18 +#define SCB_FIFO_STATUS_REG 0x1c +#define SCB_CONTROL_SOFT_RESET 0x1f +#define SCB_CLK_SET_REG 0x3c +#define SCB_INT_STATUS_REG 0x40 +#define SCB_INT_CLEAR_REG 0x44 +#define SCB_INT_MASK_REG 0x48 +#define SCB_CONTROL_REG 0x4c +#define SCB_TIME_TPL_REG 0x50 +#define SCB_TIME_TPH_REG 0x54 +#define SCB_TIME_TP2S_REG 0x58 +#define SCB_TIME_TBI_REG 0x60 +#define SCB_TIME_TSL_REG 0x64 +#define SCB_TIME_TDL_REG 0x68 +#define SCB_TIME_TSDL_REG 0x6c +#define SCB_TIME_TSDH_REG 0x70 +#define SCB_READ_XADDR_REG 0x74 +#define SCB_WRITE_XADDR_REG 0x78 +#define SCB_WRITE_COUNT_REG 0x7c +#define SCB_CORE_REV_REG 0x80 +#define SCB_TIME_TCKH_REG 0x84 +#define SCB_TIME_TCKL_REG 0x88 +#define SCB_FIFO_FLUSH_REG 0x8c +#define SCB_READ_FIFO_REG 0x94 +#define SCB_CLEAR_REG 0x98 + +/* SCB_CONTROL_REG bits */ + +#define SCB_CONTROL_CLK_ENABLE 0x1e0 +#define SCB_CONTROL_TRANSACTION_HALT 0x200 + +#define FIFO_READ_FULL BIT(0) +#define FIFO_READ_EMPTY BIT(1) +#define FIFO_WRITE_FULL BIT(2) +#define FIFO_WRITE_EMPTY BIT(3) + +/* SCB_CLK_SET_REG bits */ +#define SCB_FILT_DISABLE BIT(31) +#define SCB_FILT_BYPASS BIT(30) +#define SCB_FILT_INC_MASK 0x7f +#define SCB_FILT_INC_SHIFT 16 +#define SCB_INC_MASK 0x7f +#define SCB_INC_SHIFT 8 + +/* SCB_INT_*_REG bits */ + +#define INT_BUS_INACTIVE BIT(0) +#define INT_UNEXPECTED_START BIT(1) +#define INT_SCLK_LOW_TIMEOUT BIT(2) +#define INT_SDAT_LOW_TIMEOUT BIT(3) +#define INT_WRITE_ACK_ERR BIT(4) +#define INT_ADDR_ACK_ERR BIT(5) +#define INT_FIFO_FULL BIT(9) +#define INT_FIFO_FILLING BIT(10) +#define INT_FIFO_EMPTY BIT(11) +#define INT_FIFO_EMPTYING BIT(12) +#define INT_TRANSACTION_DONE BIT(15) +#define INT_SLAVE_EVENT BIT(16) +#define INT_MASTER_HALTED BIT(17) +#define INT_TIMING BIT(18) +#define INT_STOP_DETECTED BIT(19) + +#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING) + +/* Level interrupts need clearing after handling instead of before */ +#define INT_LEVEL 0x01e00 + +/* Don't allow any interrupts while the clock may be off */ +#define INT_ENABLE_MASK_INACTIVE 0x00000 + +/* Interrupt masks for the different driver modes */ + +#define INT_ENABLE_MASK_RAW INT_TIMING + +#define INT_ENABLE_MASK_ATOMIC (INT_TRANSACTION_DONE | \ + INT_SLAVE_EVENT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR) + +#define INT_ENABLE_MASK_AUTOMATIC (INT_SCLK_LOW_TIMEOUT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR | \ + INT_FIFO_FULL | \ + INT_FIFO_FILLING | \ + INT_FIFO_EMPTY | \ + INT_MASTER_HALTED | \ + INT_STOP_DETECTED) + +#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR) + +/* SCB_STATUS_REG fields */ + +#define LINESTAT_SCLK_LINE_STATUS BIT(0) +#define LINESTAT_SCLK_EN BIT(1) +#define LINESTAT_SDAT_LINE_STATUS BIT(2) +#define LINESTAT_SDAT_EN BIT(3) +#define LINESTAT_DET_START_STATUS BIT(4) +#define LINESTAT_DET_STOP_STATUS BIT(5) +#define LINESTAT_DET_ACK_STATUS BIT(6) +#define LINESTAT_DET_NACK_STATUS BIT(7) +#define LINESTAT_BUS_IDLE BIT(8) +#define LINESTAT_T_DONE_STATUS BIT(9) +#define LINESTAT_SCLK_OUT_STATUS BIT(10) +#define LINESTAT_SDAT_OUT_STATUS BIT(11) +#define LINESTAT_GEN_LINE_MASK_STATUS BIT(12) +#define LINESTAT_START_BIT_DET BIT(13) +#define LINESTAT_STOP_BIT_DET BIT(14) +#define LINESTAT_ACK_DET BIT(15) +#define LINESTAT_NACK_DET BIT(16) +#define LINESTAT_INPUT_HELD_V BIT(17) +#define LINESTAT_ABORT_DET BIT(18) +#define LINESTAT_ACK_OR_NACK_DET (LINESTAT_ACK_DET | LINESTAT_NACK_DET) +#define LINESTAT_INPUT_DATA 0xff000000 +#define LINESTAT_INPUT_DATA_SHIFT 24 + +#define LINESTAT_CLEAR_SHIFT 13 +#define LINESTAT_LATCHED (0x3f << LINESTAT_CLEAR_SHIFT) + +/* SCB_OVERRIDE_REG fields */ + +#define OVERRIDE_SCLK_OVR BIT(0) +#define OVERRIDE_SCLKEN_OVR BIT(1) +#define OVERRIDE_SDAT_OVR BIT(2) +#define OVERRIDE_SDATEN_OVR BIT(3) +#define OVERRIDE_MASTER BIT(9) +#define OVERRIDE_LINE_OVR_EN BIT(10) +#define OVERRIDE_DIRECT BIT(11) +#define OVERRIDE_CMD_SHIFT 4 +#define OVERRIDE_CMD_MASK 0x1f +#define OVERRIDE_DATA_SHIFT 24 + +#define OVERRIDE_SCLK_DOWN (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SCLKEN_OVR) +#define OVERRIDE_SCLK_UP (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SCLKEN_OVR | \ + OVERRIDE_SCLK_OVR) +#define OVERRIDE_SDAT_DOWN (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SDATEN_OVR) +#define OVERRIDE_SDAT_UP (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SDATEN_OVR | \ + OVERRIDE_SDAT_OVR) + +/* OVERRIDE_CMD values */ + +#define CMD_PAUSE 0x00 +#define CMD_GEN_DATA 0x01 +#define CMD_GEN_START 0x02 +#define CMD_GEN_STOP 0x03 +#define CMD_GEN_ACK 0x04 +#define CMD_GEN_NACK 0x05 +#define CMD_RET_DATA 0x08 +#define CMD_RET_ACK 0x09 + +/* Fixed timing values */ + +#define TIMEOUT_TBI 0x0 +#define TIMEOUT_TSL 0xffff +#define TIMEOUT_TDL 0x0 + +/* Transaction timeout */ + +#define IMG_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +/* + * 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). + */ +#define SCB_OPT_INC 64 + +/* Setup the clock enable filtering for 25 ns */ +#define SCB_FILT_GLITCH 25 + +/* + * Bits to return from interrupt handler functions for different modes. + * This delays completion until we've finished with the registers, so that the + * function waiting for completion can safely disable the clock to save power. + */ +#define ISR_COMPLETE_M BIT(31) +#define ISR_FATAL_M BIT(30) +#define ISR_WAITSTOP BIT(29) +#define ISR_STATUS_M 0x0000ffff /* contains +ve errno */ +#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err))) +#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M) + +#define IMG_I2C_PM_TIMEOUT 1000 /* ms */ + +enum img_i2c_mode { + MODE_INACTIVE, + MODE_RAW, + MODE_ATOMIC, + MODE_AUTOMATIC, + MODE_SEQUENCE, + MODE_FATAL, + MODE_WAITSTOP, + MODE_SUSPEND, +}; + +/* Timing parameters for i2c modes (in ns) */ +struct img_i2c_timings { + const char *name; + unsigned int max_bitrate; + unsigned int tckh, tckl, tsdh, tsdl; + unsigned int tp2s, tpl, tph; +}; + +/* The timings array must be ordered from slower to faster */ +static struct img_i2c_timings timings[] = { + /* Standard mode */ + { + .name = "standard", + .max_bitrate = I2C_MAX_STANDARD_MODE_FREQ, + .tckh = 4000, + .tckl = 4700, + .tsdh = 4700, + .tsdl = 8700, + .tp2s = 4700, + .tpl = 4700, + .tph = 4000, + }, + /* Fast mode */ + { + .name = "fast", + .max_bitrate = I2C_MAX_FAST_MODE_FREQ, + .tckh = 600, + .tckl = 1300, + .tsdh = 600, + .tsdl = 1200, + .tp2s = 1300, + .tpl = 600, + .tph = 600, + }, +}; + +/* Reset dance */ +static u8 img_i2c_reset_seq[] = { CMD_GEN_START, + CMD_GEN_DATA, 0xff, + CMD_RET_ACK, + CMD_GEN_START, + CMD_GEN_STOP, + 0 }; +/* Just issue a stop (after an abort condition) */ +static u8 img_i2c_stop_seq[] = { CMD_GEN_STOP, + 0 }; + +/* We're interested in different interrupts depending on the mode */ +static unsigned int img_i2c_int_enable_by_mode[] = { + [MODE_INACTIVE] = INT_ENABLE_MASK_INACTIVE, + [MODE_RAW] = INT_ENABLE_MASK_RAW, + [MODE_ATOMIC] = INT_ENABLE_MASK_ATOMIC, + [MODE_AUTOMATIC] = INT_ENABLE_MASK_AUTOMATIC, + [MODE_SEQUENCE] = INT_ENABLE_MASK_ATOMIC, + [MODE_FATAL] = 0, + [MODE_WAITSTOP] = INT_ENABLE_MASK_WAITSTOP, + [MODE_SUSPEND] = 0, +}; + +/* Atomic command names */ +static const char * const img_i2c_atomic_cmd_names[] = { + [CMD_PAUSE] = "PAUSE", + [CMD_GEN_DATA] = "GEN_DATA", + [CMD_GEN_START] = "GEN_START", + [CMD_GEN_STOP] = "GEN_STOP", + [CMD_GEN_ACK] = "GEN_ACK", + [CMD_GEN_NACK] = "GEN_NACK", + [CMD_RET_DATA] = "RET_DATA", + [CMD_RET_ACK] = "RET_ACK", +}; + +struct img_i2c { + struct i2c_adapter adap; + + void __iomem *base; + + /* + * The scb core clock is used to get the input frequency, and to disable + * it after every set of transactions to save some power. + */ + struct clk *scb_clk, *sys_clk; + unsigned int bitrate; + bool need_wr_rd_fence; + + /* state */ + struct completion msg_complete; + spinlock_t lock; /* lock before doing anything with the state */ + struct i2c_msg msg; + + /* After the last transaction, wait for a stop bit */ + bool last_msg; + int msg_status; + + enum img_i2c_mode mode; + u32 int_enable; /* depends on mode */ + u32 line_status; /* line status over command */ + + /* + * To avoid slave event interrupts in automatic mode, use a timer to + * poll the abort condition if we don't get an interrupt for too long. + */ + struct timer_list check_timer; + bool t_halt; + + /* atomic mode state */ + bool at_t_done; + bool at_slave_event; + int at_cur_cmd; + u8 at_cur_data; + + /* Sequence: either reset or stop. See img_i2c_sequence. */ + u8 *seq; + + /* raw mode */ + unsigned int raw_timeout; +}; + +static int img_i2c_runtime_suspend(struct device *dev); +static int img_i2c_runtime_resume(struct device *dev); + +static void img_i2c_writel(struct img_i2c *i2c, u32 offset, u32 value) +{ + writel(value, i2c->base + offset); +} + +static u32 img_i2c_readl(struct img_i2c *i2c, u32 offset) +{ + return readl(i2c->base + offset); +} + +/* + * The code to read from the master read fifo, and write to the master + * write fifo, checks a bit in an SCB register before every byte to + * ensure that the fifo is not full (write fifo) or empty (read fifo). + * Due to clock domain crossing inside the SCB block the updated value + * of this bit is only visible after 2 cycles. + * + * The scb_wr_rd_fence() function does 2 dummy writes (to the read-only + * revision register), and it's called after reading from or writing to the + * fifos to ensure that subsequent reads of the fifo status bits do not read + * stale values. + */ +static void img_i2c_wr_rd_fence(struct img_i2c *i2c) +{ + if (i2c->need_wr_rd_fence) { + img_i2c_writel(i2c, SCB_CORE_REV_REG, 0); + img_i2c_writel(i2c, SCB_CORE_REV_REG, 0); + } +} + +static void img_i2c_switch_mode(struct img_i2c *i2c, enum img_i2c_mode mode) +{ + i2c->mode = mode; + i2c->int_enable = img_i2c_int_enable_by_mode[mode]; + i2c->line_status = 0; +} + +static void img_i2c_raw_op(struct img_i2c *i2c) +{ + i2c->raw_timeout = 0; + img_i2c_writel(i2c, SCB_OVERRIDE_REG, + OVERRIDE_SCLKEN_OVR | + OVERRIDE_SDATEN_OVR | + OVERRIDE_MASTER | + OVERRIDE_LINE_OVR_EN | + OVERRIDE_DIRECT | + ((i2c->at_cur_cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) | + (i2c->at_cur_data << OVERRIDE_DATA_SHIFT)); +} + +static const char *img_i2c_atomic_op_name(unsigned int cmd) +{ + if (unlikely(cmd >= ARRAY_SIZE(img_i2c_atomic_cmd_names))) + return "UNKNOWN"; + return img_i2c_atomic_cmd_names[cmd]; +} + +/* Send a single atomic mode command to the hardware */ +static void img_i2c_atomic_op(struct img_i2c *i2c, int cmd, u8 data) +{ + i2c->at_cur_cmd = cmd; + i2c->at_cur_data = data; + + /* work around lack of data setup time when generating data */ + if (cmd == CMD_GEN_DATA && i2c->mode == MODE_ATOMIC) { + u32 line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + + if (line_status & LINESTAT_SDAT_LINE_STATUS && !(data & 0x80)) { + /* hold the data line down for a moment */ + img_i2c_switch_mode(i2c, MODE_RAW); + img_i2c_raw_op(i2c); + return; + } + } + + dev_dbg(i2c->adap.dev.parent, + "atomic cmd=%s (%d) data=%#x\n", + img_i2c_atomic_op_name(cmd), cmd, data); + i2c->at_t_done = (cmd == CMD_RET_DATA || cmd == CMD_RET_ACK); + i2c->at_slave_event = false; + i2c->line_status = 0; + + img_i2c_writel(i2c, SCB_OVERRIDE_REG, + ((cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) | + OVERRIDE_MASTER | + OVERRIDE_DIRECT | + (data << OVERRIDE_DATA_SHIFT)); +} + +/* Start a transaction in atomic mode */ +static void img_i2c_atomic_start(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_ATOMIC); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + img_i2c_atomic_op(i2c, CMD_GEN_START, 0x00); +} + +static void img_i2c_soft_reset(struct img_i2c *i2c) +{ + i2c->t_halt = false; + img_i2c_writel(i2c, SCB_CONTROL_REG, 0); + img_i2c_writel(i2c, SCB_CONTROL_REG, + SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET); +} + +/* + * Enable or release transaction halt for control of repeated starts. + * In version 3.3 of the IP when transaction halt is set, an interrupt + * will be generated after each byte of a transfer instead of after + * every transfer but before the stop bit. + * Due to this behaviour we have to be careful that every time we + * release the transaction halt we have to re-enable it straight away + * so that we only process a single byte, not doing so will result in + * all remaining bytes been processed and a stop bit being issued, + * which will prevent us having a repeated start. + */ +static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt) +{ + u32 val; + + if (i2c->t_halt == t_halt) + return; + i2c->t_halt = t_halt; + val = img_i2c_readl(i2c, SCB_CONTROL_REG); + if (t_halt) + val |= SCB_CONTROL_TRANSACTION_HALT; + else + val &= ~SCB_CONTROL_TRANSACTION_HALT; + img_i2c_writel(i2c, SCB_CONTROL_REG, val); +} + +/* Drain data from the FIFO into the buffer (automatic mode) */ +static void img_i2c_read_fifo(struct img_i2c *i2c) +{ + while (i2c->msg.len) { + u32 fifo_status; + u8 data; + + img_i2c_wr_rd_fence(i2c); + fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); + if (fifo_status & FIFO_READ_EMPTY) + break; + + data = img_i2c_readl(i2c, SCB_READ_DATA_REG); + *i2c->msg.buf = data; + + img_i2c_writel(i2c, SCB_READ_FIFO_REG, 0xff); + i2c->msg.len--; + i2c->msg.buf++; + } +} + +/* Fill the FIFO with data from the buffer (automatic mode) */ +static void img_i2c_write_fifo(struct img_i2c *i2c) +{ + while (i2c->msg.len) { + u32 fifo_status; + + img_i2c_wr_rd_fence(i2c); + fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); + if (fifo_status & FIFO_WRITE_FULL) + break; + + img_i2c_writel(i2c, SCB_WRITE_DATA_REG, *i2c->msg.buf); + i2c->msg.len--; + i2c->msg.buf++; + } + + /* Disable fifo emptying interrupt if nothing more to write */ + if (!i2c->msg.len) + i2c->int_enable &= ~INT_FIFO_EMPTYING; +} + +/* Start a read transaction in automatic mode */ +static void img_i2c_read(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_AUTOMATIC); + if (!i2c->last_msg) + i2c->int_enable |= INT_SLAVE_EVENT; + + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr); + img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len); + + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); +} + +/* Start a write transaction in automatic mode */ +static void img_i2c_write(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_AUTOMATIC); + if (!i2c->last_msg) + i2c->int_enable |= INT_SLAVE_EVENT; + + img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr); + img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len); + + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); + img_i2c_write_fifo(i2c); + + /* img_i2c_write_fifo() may modify int_enable */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); +} + +/* + * Indicate that the transaction is complete. This is called from the + * ISR to wake up the waiting thread, after which the ISR must not + * access any more SCB registers. + */ +static void img_i2c_complete_transaction(struct img_i2c *i2c, int status) +{ + img_i2c_switch_mode(i2c, MODE_INACTIVE); + if (status) { + i2c->msg_status = status; + img_i2c_transaction_halt(i2c, false); + } + complete(&i2c->msg_complete); +} + +static unsigned int img_i2c_raw_atomic_delay_handler(struct img_i2c *i2c, + u32 int_status, u32 line_status) +{ + /* Stay in raw mode for this, so we don't just loop infinitely */ + img_i2c_atomic_op(i2c, i2c->at_cur_cmd, i2c->at_cur_data); + img_i2c_switch_mode(i2c, MODE_ATOMIC); + return 0; +} + +static unsigned int img_i2c_raw(struct img_i2c *i2c, u32 int_status, + u32 line_status) +{ + if (int_status & INT_TIMING) { + if (i2c->raw_timeout == 0) + return img_i2c_raw_atomic_delay_handler(i2c, + int_status, line_status); + --i2c->raw_timeout; + } + return 0; +} + +static unsigned int img_i2c_sequence(struct img_i2c *i2c, u32 int_status) +{ + static const unsigned int continue_bits[] = { + [CMD_GEN_START] = LINESTAT_START_BIT_DET, + [CMD_GEN_DATA] = LINESTAT_INPUT_HELD_V, + [CMD_RET_ACK] = LINESTAT_ACK_DET | LINESTAT_NACK_DET, + [CMD_RET_DATA] = LINESTAT_INPUT_HELD_V, + [CMD_GEN_STOP] = LINESTAT_STOP_BIT_DET, + }; + int next_cmd = -1; + u8 next_data = 0x00; + + if (int_status & INT_SLAVE_EVENT) + i2c->at_slave_event = true; + if (int_status & INT_TRANSACTION_DONE) + i2c->at_t_done = true; + + if (!i2c->at_slave_event || !i2c->at_t_done) + return 0; + + /* wait if no continue bits are set */ + if (i2c->at_cur_cmd >= 0 && + i2c->at_cur_cmd < ARRAY_SIZE(continue_bits)) { + unsigned int cont_bits = continue_bits[i2c->at_cur_cmd]; + + if (cont_bits) { + cont_bits |= LINESTAT_ABORT_DET; + if (!(i2c->line_status & cont_bits)) + return 0; + } + } + + /* follow the sequence of commands in i2c->seq */ + next_cmd = *i2c->seq; + /* stop on a nil */ + if (!next_cmd) { + img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0); + return ISR_COMPLETE(0); + } + /* when generating data, the next byte is the data */ + if (next_cmd == CMD_GEN_DATA) { + ++i2c->seq; + next_data = *i2c->seq; + } + ++i2c->seq; + img_i2c_atomic_op(i2c, next_cmd, next_data); + + return 0; +} + +static void img_i2c_reset_start(struct img_i2c *i2c) +{ + /* Initiate the magic dance */ + img_i2c_switch_mode(i2c, MODE_SEQUENCE); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + i2c->seq = img_i2c_reset_seq; + i2c->at_slave_event = true; + i2c->at_t_done = true; + i2c->at_cur_cmd = -1; + + /* img_i2c_reset_seq isn't empty so the following won't fail */ + img_i2c_sequence(i2c, 0); +} + +static void img_i2c_stop_start(struct img_i2c *i2c) +{ + /* Initiate a stop bit sequence */ + img_i2c_switch_mode(i2c, MODE_SEQUENCE); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + i2c->seq = img_i2c_stop_seq; + i2c->at_slave_event = true; + i2c->at_t_done = true; + i2c->at_cur_cmd = -1; + + /* img_i2c_stop_seq isn't empty so the following won't fail */ + img_i2c_sequence(i2c, 0); +} + +static unsigned int img_i2c_atomic(struct img_i2c *i2c, + u32 int_status, + u32 line_status) +{ + int next_cmd = -1; + u8 next_data = 0x00; + + if (int_status & INT_SLAVE_EVENT) + i2c->at_slave_event = true; + if (int_status & INT_TRANSACTION_DONE) + i2c->at_t_done = true; + + if (!i2c->at_slave_event || !i2c->at_t_done) + goto next_atomic_cmd; + if (i2c->line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, "abort condition detected\n"); + next_cmd = CMD_GEN_STOP; + i2c->msg_status = -EIO; + goto next_atomic_cmd; + } + + /* i2c->at_cur_cmd may have completed */ + switch (i2c->at_cur_cmd) { + case CMD_GEN_START: + next_cmd = CMD_GEN_DATA; + next_data = i2c_8bit_addr_from_msg(&i2c->msg); + break; + case CMD_GEN_DATA: + if (i2c->line_status & LINESTAT_INPUT_HELD_V) + next_cmd = CMD_RET_ACK; + break; + case CMD_RET_ACK: + if (i2c->line_status & LINESTAT_ACK_DET || + (i2c->line_status & LINESTAT_NACK_DET && + i2c->msg.flags & I2C_M_IGNORE_NAK)) { + if (i2c->msg.len == 0) { + next_cmd = CMD_GEN_STOP; + } else if (i2c->msg.flags & I2C_M_RD) { + next_cmd = CMD_RET_DATA; + } else { + next_cmd = CMD_GEN_DATA; + next_data = *i2c->msg.buf; + --i2c->msg.len; + ++i2c->msg.buf; + } + } else if (i2c->line_status & LINESTAT_NACK_DET) { + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + } + break; + case CMD_RET_DATA: + if (i2c->line_status & LINESTAT_INPUT_HELD_V) { + *i2c->msg.buf = (i2c->line_status & + LINESTAT_INPUT_DATA) + >> LINESTAT_INPUT_DATA_SHIFT; + --i2c->msg.len; + ++i2c->msg.buf; + if (i2c->msg.len) + next_cmd = CMD_GEN_ACK; + else + next_cmd = CMD_GEN_NACK; + } + break; + case CMD_GEN_ACK: + if (i2c->line_status & LINESTAT_ACK_DET) { + next_cmd = CMD_RET_DATA; + } else { + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + } + break; + case CMD_GEN_NACK: + next_cmd = CMD_GEN_STOP; + break; + case CMD_GEN_STOP: + img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0); + return ISR_COMPLETE(0); + default: + dev_err(i2c->adap.dev.parent, "bad atomic command %d\n", + i2c->at_cur_cmd); + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + break; + } + +next_atomic_cmd: + if (next_cmd != -1) { + /* don't actually stop unless we're the last transaction */ + if (next_cmd == CMD_GEN_STOP && !i2c->msg_status && + !i2c->last_msg) + return ISR_COMPLETE(0); + img_i2c_atomic_op(i2c, next_cmd, next_data); + } + return 0; +} + +/* + * Timer function to check if something has gone wrong in automatic mode (so we + * don't have to handle so many interrupts just to catch an exception). + */ +static void img_i2c_check_timer(struct timer_list *t) +{ + struct img_i2c *i2c = timer_container_of(i2c, t, check_timer); + unsigned long flags; + unsigned int line_status; + + spin_lock_irqsave(&i2c->lock, flags); + line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + + /* check for an abort condition */ + if (line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, + "abort condition detected by check timer\n"); + /* enable slave event interrupt mask to trigger irq */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, + i2c->int_enable | INT_SLAVE_EVENT); + } + + spin_unlock_irqrestore(&i2c->lock, flags); +} + +static unsigned int img_i2c_auto(struct img_i2c *i2c, + unsigned int int_status, + unsigned int line_status) +{ + if (int_status & (INT_WRITE_ACK_ERR | INT_ADDR_ACK_ERR)) + return ISR_COMPLETE(EIO); + + if (line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, "abort condition detected\n"); + /* empty the read fifo */ + if ((i2c->msg.flags & I2C_M_RD) && + (int_status & INT_FIFO_FULL_FILLING)) + img_i2c_read_fifo(i2c); + /* use atomic mode and try to force a stop bit */ + i2c->msg_status = -EIO; + img_i2c_stop_start(i2c); + return 0; + } + + /* Enable transaction halt on start bit */ + if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) { + img_i2c_transaction_halt(i2c, !i2c->last_msg); + /* we're no longer interested in the slave event */ + i2c->int_enable &= ~INT_SLAVE_EVENT; + } + + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); + + if (int_status & INT_STOP_DETECTED) { + /* Drain remaining data in FIFO and complete transaction */ + if (i2c->msg.flags & I2C_M_RD) + img_i2c_read_fifo(i2c); + return ISR_COMPLETE(0); + } + + if (i2c->msg.flags & I2C_M_RD) { + if (int_status & (INT_FIFO_FULL_FILLING | INT_MASTER_HALTED)) { + img_i2c_read_fifo(i2c); + if (i2c->msg.len == 0) + return ISR_WAITSTOP; + } + } else { + if (int_status & (INT_FIFO_EMPTY | INT_MASTER_HALTED)) { + if ((int_status & INT_FIFO_EMPTY) && + i2c->msg.len == 0) + return ISR_WAITSTOP; + img_i2c_write_fifo(i2c); + } + } + if (int_status & INT_MASTER_HALTED) { + /* + * Release and then enable transaction halt, to + * allow only a single byte to proceed. + */ + img_i2c_transaction_halt(i2c, false); + img_i2c_transaction_halt(i2c, !i2c->last_msg); + } + + return 0; +} + +static irqreturn_t img_i2c_isr(int irq, void *dev_id) +{ + struct img_i2c *i2c = dev_id; + u32 int_status, line_status; + /* We handle transaction completion AFTER accessing registers */ + unsigned int hret; + + /* Read interrupt status register. */ + int_status = img_i2c_readl(i2c, SCB_INT_STATUS_REG); + /* Clear detected interrupts. */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status); + + /* + * Read line status and clear it until it actually is clear. We have + * to be careful not to lose any line status bits that get latched. + */ + line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + if (line_status & LINESTAT_LATCHED) { + img_i2c_writel(i2c, SCB_CLEAR_REG, + (line_status & LINESTAT_LATCHED) + >> LINESTAT_CLEAR_SHIFT); + img_i2c_wr_rd_fence(i2c); + } + + spin_lock(&i2c->lock); + + /* Keep track of line status bits received */ + i2c->line_status &= ~LINESTAT_INPUT_DATA; + i2c->line_status |= line_status; + + /* + * Certain interrupts indicate that sclk low timeout is not + * a problem. If any of these are set, just continue. + */ + if ((int_status & INT_SCLK_LOW_TIMEOUT) && + !(int_status & (INT_SLAVE_EVENT | + INT_FIFO_EMPTY | + INT_FIFO_FULL))) { + dev_crit(i2c->adap.dev.parent, + "fatal: clock low timeout occurred %s addr 0x%02x\n", + (i2c->msg.flags & I2C_M_RD) ? "reading" : "writing", + i2c->msg.addr); + hret = ISR_FATAL(EIO); + goto out; + } + + if (i2c->mode == MODE_ATOMIC) + hret = img_i2c_atomic(i2c, int_status, line_status); + else if (i2c->mode == MODE_AUTOMATIC) + hret = img_i2c_auto(i2c, int_status, line_status); + else if (i2c->mode == MODE_SEQUENCE) + hret = img_i2c_sequence(i2c, int_status); + else if (i2c->mode == MODE_WAITSTOP && (int_status & INT_SLAVE_EVENT) && + (line_status & LINESTAT_STOP_BIT_DET)) + hret = ISR_COMPLETE(0); + else if (i2c->mode == MODE_RAW) + hret = img_i2c_raw(i2c, int_status, line_status); + else + hret = 0; + + /* Clear detected level interrupts. */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status & INT_LEVEL); + +out: + if (hret & ISR_WAITSTOP) { + /* + * Only wait for stop on last message. + * Also we may already have detected the stop bit. + */ + if (!i2c->last_msg || i2c->line_status & LINESTAT_STOP_BIT_DET) + hret = ISR_COMPLETE(0); + else + img_i2c_switch_mode(i2c, MODE_WAITSTOP); + } + + /* now we've finished using regs, handle transaction completion */ + if (hret & ISR_COMPLETE_M) { + int status = -(hret & ISR_STATUS_M); + + img_i2c_complete_transaction(i2c, status); + if (hret & ISR_FATAL_M) + img_i2c_switch_mode(i2c, MODE_FATAL); + } + + /* Enable interrupts (int_enable may be altered by changing mode) */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +/* Force a bus reset sequence and wait for it to complete */ +static int img_i2c_reset_bus(struct img_i2c *i2c) +{ + unsigned long flags; + unsigned long time_left; + + spin_lock_irqsave(&i2c->lock, flags); + reinit_completion(&i2c->msg_complete); + img_i2c_reset_start(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); + + time_left = wait_for_completion_timeout(&i2c->msg_complete, + IMG_I2C_TIMEOUT); + if (time_left == 0) + return -ETIMEDOUT; + return 0; +} + +static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct img_i2c *i2c = i2c_get_adapdata(adap); + bool atomic = false; + int i, ret; + unsigned long time_left; + + if (i2c->mode == MODE_SUSPEND) { + WARN(1, "refusing to service transaction in suspended state\n"); + return -EIO; + } + + if (i2c->mode == MODE_FATAL) + return -EIO; + + for (i = 0; i < num; i++) { + /* + * 0 byte reads are not possible because the slave could try + * and pull the data line low, preventing a stop bit. + */ + if (!msgs[i].len && msgs[i].flags & I2C_M_RD) + return -EIO; + /* + * 0 byte writes are possible and used for probing, but we + * cannot do them in automatic mode, so use atomic mode + * instead. + * + * Also, the I2C_M_IGNORE_NAK mode can only be implemented + * in atomic mode. + */ + if (!msgs[i].len || + (msgs[i].flags & I2C_M_IGNORE_NAK)) + atomic = true; + } + + ret = pm_runtime_resume_and_get(adap->dev.parent); + if (ret < 0) + return ret; + + for (i = 0; i < num; i++) { + struct i2c_msg *msg = &msgs[i]; + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + + /* + * Make a copy of the message struct. We mustn't modify the + * original or we'll confuse drivers and i2c-dev. + */ + i2c->msg = *msg; + i2c->msg_status = 0; + + /* + * After the last message we must have waited for a stop bit. + * Not waiting can cause problems when the clock is disabled + * before the stop bit is sent, and the linux I2C interface + * requires separate transfers not to joined with repeated + * start. + */ + i2c->last_msg = (i == num - 1); + reinit_completion(&i2c->msg_complete); + + /* + * Clear line status and all interrupts before starting a + * transfer, as we may have unserviced interrupts from + * previous transfers that might be handled in the context + * of the new transfer. + */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); + img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + + if (atomic) { + img_i2c_atomic_start(i2c); + } else { + /* + * Enable transaction halt if not the last message in + * the queue so that we can control repeated starts. + */ + img_i2c_transaction_halt(i2c, !i2c->last_msg); + + if (msg->flags & I2C_M_RD) + img_i2c_read(i2c); + else + img_i2c_write(i2c); + + /* + * Release and then enable transaction halt, to + * allow only a single byte to proceed. + * This doesn't have an effect on the initial transfer + * but will allow the following transfers to start + * processing if the previous transfer was marked as + * complete while the i2c block was halted. + */ + img_i2c_transaction_halt(i2c, false); + img_i2c_transaction_halt(i2c, !i2c->last_msg); + } + spin_unlock_irqrestore(&i2c->lock, flags); + + time_left = wait_for_completion_timeout(&i2c->msg_complete, + IMG_I2C_TIMEOUT); + timer_delete_sync(&i2c->check_timer); + + if (time_left == 0) + i2c->msg_status = -ETIMEDOUT; + + if (i2c->msg_status) + break; + } + + pm_runtime_put_autosuspend(adap->dev.parent); + + return i2c->msg_status ? i2c->msg_status : num; +} + +static u32 img_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm img_i2c_algo = { + .xfer = img_i2c_xfer, + .functionality = img_i2c_func, +}; + +static int img_i2c_init(struct img_i2c *i2c) +{ + unsigned int clk_khz, bitrate_khz, clk_period, tckh, tckl, tsdh; + unsigned int i, data, prescale, inc, int_bitrate, filt; + struct img_i2c_timings timing; + u32 rev; + int ret; + + ret = pm_runtime_resume_and_get(i2c->adap.dev.parent); + if (ret < 0) + return ret; + + rev = img_i2c_readl(i2c, SCB_CORE_REV_REG); + if ((rev & 0x00ffffff) < 0x00020200) { + dev_info(i2c->adap.dev.parent, + "Unknown hardware revision (%d.%d.%d.%d)\n", + (rev >> 24) & 0xff, (rev >> 16) & 0xff, + (rev >> 8) & 0xff, rev & 0xff); + pm_runtime_put_autosuspend(i2c->adap.dev.parent); + return -EINVAL; + } + + /* Fencing enabled by default. */ + i2c->need_wr_rd_fence = true; + + /* Determine what mode we're in from the bitrate */ + timing = timings[0]; + for (i = 0; i < ARRAY_SIZE(timings); i++) { + if (i2c->bitrate <= timings[i].max_bitrate) { + timing = timings[i]; + break; + } + } + if (i2c->bitrate > timings[ARRAY_SIZE(timings) - 1].max_bitrate) { + dev_warn(i2c->adap.dev.parent, + "requested bitrate (%u) is higher than the max bitrate supported (%u)\n", + i2c->bitrate, + timings[ARRAY_SIZE(timings) - 1].max_bitrate); + timing = timings[ARRAY_SIZE(timings) - 1]; + i2c->bitrate = timing.max_bitrate; + } + + bitrate_khz = i2c->bitrate / 1000; + clk_khz = clk_get_rate(i2c->scb_clk) / 1000; + + /* Find the prescale that would give us that inc (approx delay = 0) */ + prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz); + prescale = clamp_t(unsigned int, prescale, 1, 8); + clk_khz /= prescale; + + /* Setup the clock increment value */ + inc = (256 * 16 * bitrate_khz) / clk_khz; + + /* + * The clock generation logic allows to filter glitches on the bus. + * This filter is able to remove bus glitches shorter than 50ns. + * If the clock enable rate is greater than 20 MHz, no filtering + * is required, so we need to disable it. + * If it's between the 20-40 MHz range, there's no need to divide + * the clock to get a filter. + */ + if (clk_khz < 20000) { + filt = SCB_FILT_DISABLE; + } else if (clk_khz < 40000) { + filt = SCB_FILT_BYPASS; + } else { + /* Calculate filter clock */ + filt = (64000 / ((clk_khz / 1000) * SCB_FILT_GLITCH)); + + /* Scale up if needed */ + if (64000 % ((clk_khz / 1000) * SCB_FILT_GLITCH)) + inc++; + + if (filt > SCB_FILT_INC_MASK) + filt = SCB_FILT_INC_MASK; + + filt = (filt & SCB_FILT_INC_MASK) << SCB_FILT_INC_SHIFT; + } + data = filt | ((inc & SCB_INC_MASK) << SCB_INC_SHIFT) | (prescale - 1); + img_i2c_writel(i2c, SCB_CLK_SET_REG, data); + + /* Obtain the clock period of the fx16 clock in ns */ + clk_period = (256 * 1000000) / (clk_khz * inc); + + /* Calculate the bitrate in terms of internal clock pulses */ + int_bitrate = 1000000 / (bitrate_khz * clk_period); + if ((1000000 % (bitrate_khz * clk_period)) >= + ((bitrate_khz * clk_period) / 2)) + int_bitrate++; + + /* + * Setup clock duty cycle, start with 50% and adjust TCKH and TCKL + * values from there if they don't meet minimum timing requirements + */ + tckh = int_bitrate / 2; + tckl = int_bitrate - tckh; + + /* Adjust TCKH and TCKL values */ + data = DIV_ROUND_UP(timing.tckl, clk_period); + + if (tckl < data) { + tckl = data; + tckh = int_bitrate - tckl; + } + + if (tckh > 0) + --tckh; + + if (tckl > 0) + --tckl; + + img_i2c_writel(i2c, SCB_TIME_TCKH_REG, tckh); + img_i2c_writel(i2c, SCB_TIME_TCKL_REG, tckl); + + /* Setup TSDH value */ + tsdh = DIV_ROUND_UP(timing.tsdh, clk_period); + + if (tsdh > 1) + data = tsdh - 1; + else + data = 0x01; + img_i2c_writel(i2c, SCB_TIME_TSDH_REG, data); + + /* This value is used later */ + tsdh = data; + + /* Setup TPL value */ + data = timing.tpl / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TPL_REG, data); + + /* Setup TPH value */ + data = timing.tph / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TPH_REG, data); + + /* Setup TSDL value to TPL + TSDH + 2 */ + img_i2c_writel(i2c, SCB_TIME_TSDL_REG, data + tsdh + 2); + + /* Setup TP2S value */ + data = timing.tp2s / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TP2S_REG, data); + + img_i2c_writel(i2c, SCB_TIME_TBI_REG, TIMEOUT_TBI); + img_i2c_writel(i2c, SCB_TIME_TSL_REG, TIMEOUT_TSL); + img_i2c_writel(i2c, SCB_TIME_TDL_REG, TIMEOUT_TDL); + + /* Take module out of soft reset and enable clocks */ + img_i2c_soft_reset(i2c); + + /* Disable all interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, 0); + + /* Clear all interrupts */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); + + /* Clear the scb_line_status events */ + img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + + /* Enable interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + + /* Perform a synchronous sequence to reset the bus */ + ret = img_i2c_reset_bus(i2c); + + pm_runtime_put_autosuspend(i2c->adap.dev.parent); + + return ret; +} + +static int img_i2c_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct img_i2c *i2c; + int irq, ret; + u32 val; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct img_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + 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) + return irq; + + i2c->sys_clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(i2c->sys_clk)) { + dev_err(&pdev->dev, "can't get system clock\n"); + return PTR_ERR(i2c->sys_clk); + } + + i2c->scb_clk = devm_clk_get(&pdev->dev, "scb"); + if (IS_ERR(i2c->scb_clk)) { + dev_err(&pdev->dev, "can't get core clock\n"); + return PTR_ERR(i2c->scb_clk); + } + + ret = devm_request_irq(&pdev->dev, irq, img_i2c_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "can't request irq %d\n", irq); + return ret; + } + + /* Set up the exception check timer */ + timer_setup(&i2c->check_timer, img_i2c_check_timer, 0); + + i2c->bitrate = timings[0].max_bitrate; + if (!of_property_read_u32(node, "clock-frequency", &val)) + i2c->bitrate = val; + + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = node; + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &img_i2c_algo; + i2c->adap.retries = 5; + i2c->adap.nr = pdev->id; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "IMG SCB I2C"); + + img_i2c_switch_mode(i2c, MODE_INACTIVE); + spin_lock_init(&i2c->lock); + init_completion(&i2c->msg_complete); + + platform_set_drvdata(pdev, i2c); + + pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2c_runtime_resume(&pdev->dev); + if (ret) + return ret; + } + + ret = img_i2c_init(i2c); + if (ret) + goto rpm_disable; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) + goto rpm_disable; + + return 0; + +rpm_disable: + if (!pm_runtime_enabled(&pdev->dev)) + img_i2c_runtime_suspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + return ret; +} + +static void img_i2c_remove(struct platform_device *dev) +{ + struct img_i2c *i2c = platform_get_drvdata(dev); + + i2c_del_adapter(&i2c->adap); + pm_runtime_disable(&dev->dev); + if (!pm_runtime_status_suspended(&dev->dev)) + img_i2c_runtime_suspend(&dev->dev); +} + +static int img_i2c_runtime_suspend(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + + clk_disable_unprepare(i2c->scb_clk); + clk_disable_unprepare(i2c->sys_clk); + + return 0; +} + +static int img_i2c_runtime_resume(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2c->sys_clk); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(i2c->scb_clk); + if (ret) { + dev_err(dev, "Unable to enable scb clock\n"); + clk_disable_unprepare(i2c->sys_clk); + return ret; + } + + return 0; +} + +static int img_i2c_suspend(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + img_i2c_switch_mode(i2c, MODE_SUSPEND); + + return 0; +} + +static int img_i2c_resume(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + img_i2c_init(i2c); + + return 0; +} + +static const struct dev_pm_ops img_i2c_pm = { + 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[] = { + { .compatible = "img,scb-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(of, img_scb_i2c_match); + +static struct platform_driver img_scb_i2c_driver = { + .driver = { + .name = "img-i2c-scb", + .of_match_table = img_scb_i2c_match, + .pm = pm_ptr(&img_i2c_pm), + }, + .probe = img_i2c_probe, + .remove = img_i2c_remove, +}; +module_platform_driver(img_scb_i2c_driver); + +MODULE_AUTHOR("James Hogan "); +MODULE_DESCRIPTION("IMG host I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c new file mode 100644 index 000000000..1cfd7a4c8 --- /dev/null +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -0,0 +1,1774 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * This is i.MX low power i2c controller driver. + * + * Copyright 2016 Freescale Semiconductor, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "imx-lpi2c" + +#define LPI2C_VERID 0x00 /* i2c version ID */ +#define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */ +#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 */ +#define LPI2C_MCFGR3 0x2C /* i2c master configuration */ +#define LPI2C_MCCR0 0x48 /* i2c master clk configuration */ +#define LPI2C_MCCR1 0x50 /* i2c master clk configuration */ +#define LPI2C_MFCR 0x58 /* i2c master FIFO control */ +#define LPI2C_MFSR 0x5C /* i2c master FIFO status */ +#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 +#define GEN_STOP 0X02 +#define RECV_DISCARD 0X03 +#define GEN_START 0X04 +#define START_NACK 0X05 +#define START_HIGH 0X06 +#define START_HIGH_NACK 0X07 + +#define MCR_MEN BIT(0) +#define MCR_RST BIT(1) +#define MCR_DOZEN BIT(2) +#define MCR_DBGEN BIT(3) +#define MCR_RTF BIT(8) +#define MCR_RRF BIT(9) +#define MSR_TDF BIT(0) +#define MSR_RDF BIT(1) +#define MSR_SDF BIT(9) +#define MSR_NDF BIT(10) +#define MSR_ALF BIT(11) +#define MSR_MBF BIT(24) +#define MSR_BBF BIT(25) +#define MIER_TDIE BIT(0) +#define MIER_RDIE BIT(1) +#define MIER_SDIE BIT(9) +#define MIER_NDIE BIT(10) +#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 MSR_RDF_ASSERTED(x) FIELD_GET(MSR_RDF, (x)) + +#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 I2C_PM_TIMEOUT 10 /* ms */ +#define I2C_PM_LONG_TIMEOUT_MS 1000 /* Avoid dead lock caused by big clock prepare lock */ +#define I2C_DMA_THRESHOLD 8 /* bytes */ + +/* Bit 0 indicates the presence of the target feature */ +#define VERID_FEATURE_TARGET_PRESENT BIT(0) + +enum lpi2c_imx_mode { + STANDARD, /* 100+Kbps */ + FAST, /* 400+Kbps */ + FAST_PLUS, /* 1.0+Mbps */ + HS, /* 3.4+Mbps */ + ULTRA_FAST, /* 5.0+Mbps */ +}; + +enum lpi2c_imx_pincfg { + TWO_PIN_OD, + TWO_PIN_OO, + TWO_PIN_PP, + FOUR_PIN_PP, +}; + +struct imx_lpi2c_hwdata { + bool need_request_free_irq; /* Needed by irqsteer */ + bool need_prepare_unprepare_clk; /* Needed by LPCG */ +}; + +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; + 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; + unsigned int bitrate; + 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; + bool target_supported; + int irq; + const struct imx_lpi2c_hwdata *hwdata; +}; + +static const struct imx_lpi2c_hwdata imx7ulp_lpi2c_hwdata = { +}; + +static const struct imx_lpi2c_hwdata imx8qxp_lpi2c_hwdata = { + .need_request_free_irq = true, + .need_prepare_unprepare_clk = true, +}; + +static const struct imx_lpi2c_hwdata imx8qm_lpi2c_hwdata = { + .need_request_free_irq = true, + .need_prepare_unprepare_clk = true, +}; + +#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, bool atomic) +{ + unsigned int temp; + int err; + + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, + temp & (MSR_ALF | MSR_BBF | MSR_MBF)); + + /* check for arbitration lost, clear if set */ + if (temp & MSR_ALF) { + writel(temp, lpi2c_imx->base + LPI2C_MSR); + return -EAGAIN; + } + + /* 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 < I2C_MAX_FAST_MODE_FREQ) + mode = STANDARD; + else if (bitrate < I2C_MAX_FAST_MODE_PLUS_FREQ) + mode = FAST; + else if (bitrate < I2C_MAX_HIGH_SPEED_MODE_FREQ) + mode = FAST_PLUS; + else if (bitrate < I2C_MAX_ULTRA_FAST_MODE_FREQ) + mode = HS; + else + mode = ULTRA_FAST; + + lpi2c_imx->mode = mode; +} + +static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs, bool atomic) +{ + unsigned int temp; + + temp = readl(lpi2c_imx->base + LPI2C_MCR); + temp |= MCR_RRF | MCR_RTF; + writel(temp, lpi2c_imx->base + LPI2C_MCR); + writel(0x7f00, lpi2c_imx->base + LPI2C_MSR); + + temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8); + writel(temp, lpi2c_imx->base + LPI2C_MTDR); + + return lpi2c_imx_bus_busy(lpi2c_imx, atomic); +} + +static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) +{ + unsigned int temp; + int err; + + writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR); + + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & MSR_SDF); + + 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, 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 = lpi2c_imx->rate_per; + + if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST) + filt = 0; + else + filt = 2; + + for (prescale = 0; prescale <= 7; prescale++) { + clk_cycle = clk_rate / ((1 << prescale) * lpi2c_imx->bitrate) + - 3 - (filt >> 1); + clkhi = DIV_ROUND_UP(clk_cycle, I2C_CLK_RATIO + 1); + clklo = clk_cycle - clkhi; + if (clklo < 64) + break; + } + + if (prescale > 7) + return -EINVAL; + + /* set MCFGR1: PINCFG, PRESCALE, IGNACK */ + if (lpi2c_imx->mode == ULTRA_FAST) + pincfg = TWO_PIN_OO; + else + pincfg = TWO_PIN_OD; + temp = prescale | pincfg << 24; + + if (lpi2c_imx->mode == ULTRA_FAST) + temp |= MCFGR1_IGNACK; + + writel(temp, lpi2c_imx->base + LPI2C_MCFGR1); + + /* set MCFGR2: FILTSDA, FILTSCL */ + temp = (filt << 16) | (filt << 24); + writel(temp, lpi2c_imx->base + LPI2C_MCFGR2); + + /* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */ + sethold = clkhi; + datavd = clkhi >> 1; + temp = datavd << 24 | sethold << 16 | clkhi << 8 | clklo; + + if (lpi2c_imx->mode == HS) + writel(temp, lpi2c_imx->base + LPI2C_MCCR1); + else + writel(temp, lpi2c_imx->base + LPI2C_MCCR0); + + return 0; +} + +static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) +{ + unsigned int temp; + int ret; + + ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent); + if (ret < 0) + return ret; + + temp = MCR_RST; + writel(temp, lpi2c_imx->base + LPI2C_MCR); + writel(0, lpi2c_imx->base + LPI2C_MCR); + + ret = lpi2c_imx_config(lpi2c_imx); + if (ret) + goto rpm_put; + + temp = readl(lpi2c_imx->base + LPI2C_MCR); + temp |= MCR_MEN; + writel(temp, lpi2c_imx->base + LPI2C_MCR); + + return 0; + +rpm_put: + pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent); + + return ret; +} + +static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx) +{ + u32 temp; + + temp = readl(lpi2c_imx->base + LPI2C_MCR); + temp &= ~MCR_MEN; + writel(temp, lpi2c_imx->base + LPI2C_MCR); + + pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent); + + return 0; +} + +static int lpi2c_imx_pio_msg_complete(struct lpi2c_imx_struct *lpi2c_imx) +{ + unsigned long time_left; + + time_left = wait_for_completion_timeout(&lpi2c_imx->complete, HZ); + + return time_left ? 0 : -ETIMEDOUT; +} + +static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) +{ + unsigned int temp; + int err; + + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, + (temp & MSR_NDF) || !lpi2c_imx_txfifo_cnt(lpi2c_imx)); + + if (temp & MSR_NDF) { + dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n"); + return -EIO; + } + + 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; +} + +static void lpi2c_imx_set_tx_watermark(struct lpi2c_imx_struct *lpi2c_imx) +{ + writel(lpi2c_imx->txfifosize >> 1, lpi2c_imx->base + LPI2C_MFCR); +} + +static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx) +{ + unsigned int temp, remaining; + + remaining = lpi2c_imx->msglen - lpi2c_imx->delivered; + + if (remaining > (lpi2c_imx->rxfifosize >> 1)) + temp = lpi2c_imx->rxfifosize >> 1; + else + temp = 0; + + writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR); +} + +static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) +{ + unsigned int data, txcnt; + + txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff; + + while (txcnt < lpi2c_imx->txfifosize) { + if (lpi2c_imx->delivered == lpi2c_imx->msglen) + break; + + data = lpi2c_imx->tx_buf[lpi2c_imx->delivered++]; + writel(data, lpi2c_imx->base + LPI2C_MTDR); + txcnt++; + } + + 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 bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) +{ + unsigned int remaining; + unsigned int temp, data; + + do { + data = readl(lpi2c_imx->base + LPI2C_MRDR); + if (data & MRDR_RXEMPTY) + break; + + lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff; + } while (1); + + remaining = lpi2c_imx->msglen - lpi2c_imx->delivered; + + if (!remaining) { + if (!atomic) + complete(&lpi2c_imx->complete); + return true; + } + + /* not finished, still waiting for rx data */ + lpi2c_imx_set_rx_watermark(lpi2c_imx); + + /* multiple receive commands */ + if (!(lpi2c_imx->delivered & 0xff)) { + temp = (remaining > CHUNK_DATA ? CHUNK_DATA : remaining) - 1; + temp |= (RECV_DATA << 8); + writel(temp, lpi2c_imx->base + LPI2C_MTDR); + } + + if (!atomic) + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); + + return false; +} + +static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + lpi2c_imx->tx_buf = msgs->buf; + lpi2c_imx_set_tx_watermark(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 unsigned int lpi2c_SMBus_block_read_length_byte(struct lpi2c_imx_struct *lpi2c_imx) +{ + unsigned int data; + + data = readl(lpi2c_imx->base + LPI2C_MRDR); + lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff; + + return data; +} + +static int lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + unsigned int temp, val, block_len; + int ret; + + lpi2c_imx->rx_buf = msgs->buf; + lpi2c_imx->block_data = msgs->flags & I2C_M_RECV_LEN; + + lpi2c_imx_set_rx_watermark(lpi2c_imx); + + if (!lpi2c_imx->block_data) { + temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1; + temp |= (RECV_DATA << 8); + writel(temp, lpi2c_imx->base + LPI2C_MTDR); + } else { + /* + * The LPI2C controller automatically sends a NACK after the last byte of a + * receive command, unless the next command in MTDR is also a receive command. + * If MTDR is empty when a receive completes, a NACK is sent by default. + * + * To comply with the SMBus block read spec, we start with a 2-byte read: + * The first byte in RXFIFO is the block length. Once this byte arrives, the + * controller immediately updates MTDR with the next read command, ensuring + * continuous ACK instead of NACK. + * + * The second byte is the first block data byte. Therefore, the subsequent + * read command should request (block_len - 1) bytes, since one data byte + * has already been read. + */ + + writel((RECV_DATA << 8) | 0x01, lpi2c_imx->base + LPI2C_MTDR); + + ret = readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val, + MSR_RDF_ASSERTED(val), 1, 1000); + if (ret) { + dev_err(&lpi2c_imx->adapter.dev, "SMBus read count failed %d\n", ret); + return ret; + } + + /* Read block length byte and confirm this SMBus transfer meets protocol */ + block_len = lpi2c_SMBus_block_read_length_byte(lpi2c_imx); + if (block_len == 0 || block_len > I2C_SMBUS_BLOCK_MAX) { + dev_err(&lpi2c_imx->adapter.dev, "Invalid SMBus block read length\n"); + return -EPROTO; + } + + /* + * When block_len shows more bytes need to be read, update second read command to + * keep MTDR non-empty and ensuring continuous ACKs. Only update command register + * here. All block bytes will be read out at IRQ handler or lpi2c_imx_read_atomic() + * function. + */ + if (block_len > 1) + writel((RECV_DATA << 8) | (block_len - 2), lpi2c_imx->base + LPI2C_MTDR); + + lpi2c_imx->msglen += block_len; + msgs->len += block_len; + } + + return 0; +} + +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; + + /* DMA is not suitable for SMBus block read */ + if (msg->flags & I2C_M_RECV_LEN) + return false; + + /* + * A system-wide suspend or resume transition is in progress. LPI2C should use PIO to + * transfer data to avoid issue caused by no ready DMA HW resource. + */ + if (pm_suspend_in_progress()) + 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) +{ + int ret; + + reinit_completion(&lpi2c_imx->complete); + + if (msg->flags & I2C_M_RD) { + ret = lpi2c_imx_read_init(lpi2c_imx, msg); + if (ret) + return ret; + 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) +{ + int ret; + + if (msg->flags & I2C_M_RD) { + ret = lpi2c_imx_read_init(lpi2c_imx, msg); + if (ret) + return ret; + 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; + + 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 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; + int i, result; + + result = lpi2c_imx_master_enable(lpi2c_imx); + if (result) + return result; + + for (i = 0; i < num; i++) { + result = lpi2c_imx_start(lpi2c_imx, &msgs[i], atomic); + if (result) + goto disable; + + /* quick smbus */ + 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; + + 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]); + } + } + + if (result) + goto stop; + + if (!(msgs[i].flags & I2C_M_RD)) { + result = lpi2c_imx_txfifo_empty(lpi2c_imx, atomic); + if (result) + goto stop; + } + } + +stop: + lpi2c_imx_stop(lpi2c_imx, atomic); + + temp = readl(lpi2c_imx->base + LPI2C_MSR); + if ((temp & MSR_NDF) && !result) + result = -EIO; + +disable: + lpi2c_imx_master_disable(lpi2c_imx); + + dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__, + (result < 0) ? "error" : "success msg", + (result < 0) ? result : num); + + return (result < 0) ? result : num; +} + +static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + 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); + 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; + + /* Reject target-mode registration on controllers that don't support it. */ + if (!lpi2c_imx->target_supported) + return -EOPNOTSUPP; + + 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 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; + void *group; + int ret; + + /* + * Open a devres group so that all resources allocated within + * this function can be released together if DMA init fails but + * probe continues in PIO mode. + */ + group = devres_open_group(dev, NULL, GFP_KERNEL); + if (!group) + return -ENOMEM; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto release_group; + } + + dma->phy_addr = phy_addr; + + /* Prepare for TX DMA: */ + dma->chan_tx = devm_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 release_group; + } + + /* Prepare for RX DMA: */ + dma->chan_rx = devm_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 release_group; + } + + /* + * DMA init succeeded. Remove the group marker but keep all resources + * bound to the device, they will be freed at device removal. + */ + devres_remove_group(dev, group); + + lpi2c_imx->can_use_dma = true; + lpi2c_imx->dma = dma; + return 0; + +release_group: + /* + * DMA init failed. Release ALL resources allocated inside this + * group (dma memory, TX channel if already acquired, etc.) so + * that a successful PIO-mode probe does not hold unused resources + * for the entire device lifetime. + */ + devres_release_group(dev, group); + return ret; +} + +static u32 lpi2c_imx_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA; +} + +static const struct i2c_algorithm lpi2c_imx_algo = { + .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", .data = &imx7ulp_lpi2c_hwdata,}, + { .compatible = "fsl,imx8qxp-lpi2c", .data = &imx8qxp_lpi2c_hwdata,}, + { .compatible = "fsl,imx8qm-lpi2c", .data = &imx8qm_lpi2c_hwdata,}, + { } +}; +MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match); + +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 ret; + + lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL); + if (!lpi2c_imx) + return -ENOMEM; + + lpi2c_imx->hwdata = of_device_get_match_data(&pdev->dev); + if (!lpi2c_imx->hwdata) + return -ENODEV; + + lpi2c_imx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(lpi2c_imx->base)) + return PTR_ERR(lpi2c_imx->base); + + lpi2c_imx->irq = platform_get_irq(pdev, 0); + if (lpi2c_imx->irq < 0) + return lpi2c_imx->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; + strscpy(lpi2c_imx->adapter.name, pdev->name, + sizeof(lpi2c_imx->adapter.name)); + phy_addr = (dma_addr_t)res->start; + + 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 = I2C_MAX_STANDARD_MODE_FREQ; + + i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx); + platform_set_drvdata(pdev, lpi2c_imx); + + 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) { + ret = dev_err_probe(&pdev->dev, ret, + "can't lock I2C peripheral clock rate\n"); + goto clk_disable; + } + + lpi2c_imx->rate_per = clk_get_rate(lpi2c_imx->clks[0].clk); + if (!lpi2c_imx->rate_per) { + ret = dev_err_probe(&pdev->dev, -EINVAL, + "can't get I2C peripheral clock rate\n"); + goto clk_disable; + } + + if (lpi2c_imx->hwdata->need_prepare_unprepare_clk) + pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS); + else + pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + /* + * Reset all internal controller registers to avoid effects of any + * state left over from a previous stage (e.g. the bootloader). + * + * The Master block (MCR) is present on every controller, so reset it + * unconditionally. VERID shows whether the target feature is supported. + * Do not touch the Target block (SCR) on a master-only controller to + * avoid an asynchronous SError. + */ + writel(MCR_RST, lpi2c_imx->base + LPI2C_MCR); + writel(0, lpi2c_imx->base + LPI2C_MCR); + + lpi2c_imx->target_supported = !!(readl(lpi2c_imx->base + LPI2C_VERID) & + VERID_FEATURE_TARGET_PRESENT); + if (lpi2c_imx->target_supported) { + writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR); + writel(0, lpi2c_imx->base + LPI2C_SCR); + } + + ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND, + pdev->name, lpi2c_imx); + if (ret) + goto rpm_disable; + + temp = readl(lpi2c_imx->base + LPI2C_PARAM); + 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_put_autosuspend(&pdev->dev); + + dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n"); + + return 0; + +rpm_disable: + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); +clk_disable: + clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks); + + return ret; +} + +static void lpi2c_imx_remove(struct platform_device *pdev) +{ + struct lpi2c_imx_struct *lpi2c_imx = platform_get_drvdata(pdev); + + i2c_del_adapter(&lpi2c_imx->adapter); + + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); +} + +static int __maybe_unused lpi2c_runtime_suspend(struct device *dev) +{ + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk; + bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq; + + if (need_request_free_irq) + devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx); + + if (need_prepare_unprepare_clk) + clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks); + else + clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks); + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int __maybe_unused lpi2c_runtime_resume(struct device *dev) +{ + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk; + bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq; + int ret; + + pinctrl_pm_select_default_state(dev); + if (need_prepare_unprepare_clk) { + ret = clk_bulk_prepare_enable(lpi2c_imx->num_clks, lpi2c_imx->clks); + if (ret) { + dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret); + return ret; + } + } else { + ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks); + if (ret) { + dev_err(dev, "failed to enable clock %d\n", ret); + return ret; + } + } + + if (need_request_free_irq) { + ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND, + dev_name(dev), lpi2c_imx); + if (ret) { + dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq); + return ret; + } + } + + return 0; +} + +static int __maybe_unused lpi2c_suspend_noirq(struct device *dev) +{ + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&lpi2c_imx->adapter); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return ret; + } + + return 0; +} + +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); + + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + + 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(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) +}; + +static struct platform_driver lpi2c_imx_driver = { + .probe = lpi2c_imx_probe, + .remove = lpi2c_imx_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = lpi2c_imx_of_match, + .pm = &lpi2c_pm_ops, + }, +}; + +module_platform_driver(lpi2c_imx_driver); + +MODULE_AUTHOR("Gao Pan "); +MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c new file mode 100644 index 000000000..19ec056b0 --- /dev/null +++ b/drivers/i2c/busses/i2c-imx.c @@ -0,0 +1,2065 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2002 Motorola GSG-China + * + * Author: + * Darius Augulis, Teltonika Inc. + * + * Desc.: + * Implementation of I2C Adapter/Algorithm Driver + * for I2C Bus integrated in Freescale i.MX/MXC processors + * + * Derived from Motorola GSG China I2C example driver + * + * Copyright (C) 2005 Torsten Koschorrek + * + * Copyright 2013 Freescale Semiconductor, Inc. + * Copyright 2020, 2024 NXP + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This will be the driver name the kernel reports */ +#define DRIVER_NAME "imx-i2c" + +#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. + * As the hardware request, it must bigger than 4 bytes.\ + * I have set '16' here, maybe it's not the best but I think it's + * the appropriate. + */ +#define DMA_THRESHOLD 16 +#define DMA_TIMEOUT 1000 + +/* IMX I2C registers: + * the I2C register offset is different between SoCs, + * 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 + * reg_off = ( reg_base_addr << reg_shift) + */ +#define IMX_I2C_IADR 0x00 /* i2c slave address */ +#define IMX_I2C_IFDR 0x01 /* i2c frequency divider */ +#define IMX_I2C_I2CR 0x02 /* i2c control */ +#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 +#define I2SR_IIF 0x02 +#define I2SR_SRW 0x04 +#define I2SR_IAL 0x10 +#define I2SR_IBB 0x20 +#define I2SR_IAAS 0x40 +#define I2SR_ICF 0x80 +#define I2CR_DMAEN 0x02 +#define I2CR_RSTA 0x04 +#define I2CR_TXAK 0x08 +#define I2CR_MTX 0x10 +#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: + * - write zero to clear(w0c) INT flag on i.MX, + * - but write one to clear(w1c) INT flag on Vybrid. + * 2) I2CR: I2C module enable operation also differ between SoCs: + * - set I2CR_IEN bit enable the module on i.MX, + * - but clear I2CR_IEN bit enable the module on Vybrid. + */ +#define I2SR_CLR_OPCODE_W0C 0x0 +#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF) +#define I2CR_IEN_OPCODE_0 0x0 +#define I2CR_IEN_OPCODE_1 I2CR_IEN + +#define I2C_PM_TIMEOUT 10 /* ms */ + +/* + * sorted list of clock divider, register value pairs + * taken from table 26-5, p.26-9, Freescale i.MX + * Integrated Portable System Processor Reference Manual + * Document Number: MC9328MXLRM, Rev. 5.1, 06/2007 + * + * Duplicated divider values removed from list + */ +struct imx_i2c_clk_pair { + u16 div; + u16 val; +}; + +static struct imx_i2c_clk_pair imx_i2c_clk_div[] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +/* Vybrid VF610 clock divider, register value pairs */ +static struct imx_i2c_clk_pair vf610_i2c_clk_div[] = { + { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 }, + { 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 }, + { 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D }, + { 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 }, + { 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 }, + { 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 }, + { 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 }, + { 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 }, + { 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 }, + { 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B }, + { 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 }, + { 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 }, + { 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B }, + { 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A }, + { 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E }, +}; + +/* 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 int regshift; + struct imx_i2c_clk_pair *clk_div; + 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 { + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct dma_chan *chan_using; + struct completion cmd_complete; + dma_addr_t dma_buf; + unsigned int dma_len; + enum dma_transfer_direction dma_transfer_dir; + 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; + struct notifier_block clk_change_nb; + void __iomem *base; + wait_queue_head_t queue; + unsigned long i2csr; + unsigned int disable_delay; + int stopped; + unsigned int ifdr; /* IMX_I2C_IFDR */ + unsigned int cur_clk; + unsigned int bitrate; + const struct imx_i2c_hwdata *hwdata; + struct i2c_bus_recovery_info rinfo; + + 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 = { + .devtype = IMX1_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, + +}; + +static const struct imx_i2c_hwdata imx21_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, + +}; + +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, + .clk_div = vf610_i2c_clk_div, + .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[] = { + { + .name = "imx1-i2c", + .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata, + }, { + .name = "imx21-i2c", + .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata, + }, { + /* sentinel */ + } +}; +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) +{ + writeb(val, i2c_imx->base + (reg << i2c_imx->hwdata->regshift)); +} + +static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, + unsigned int reg) +{ + 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 , so we write ~i2sr_clr_opcode with just + * 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 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.parent; + int ret; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + 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; + } + + dma_sconfig.dst_addr = phy_addr + + (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.dst_maxburst = 1; + dma_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); + if (ret < 0) { + dev_err(dev, "can't configure tx channel (%d)\n", ret); + goto fail_tx; + } + + 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; + } + + dma_sconfig.src_addr = phy_addr + + (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 1; + dma_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); + if (ret < 0) { + dev_err(dev, "can't configure rx channel (%d)\n", ret); + goto fail_rx; + } + + i2c_imx->dma = dma; + init_completion(&dma->cmd_complete); + 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 0; + +fail_rx: + dma_release_channel(dma->chan_rx); +fail_tx: + dma_release_channel(dma->chan_tx); +fail_al: + devm_kfree(dev, dma); + + return ret; +} + +static void i2c_imx_dma_callback(void *arg) +{ + struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg; + struct imx_i2c_dma *dma = i2c_imx->dma; + + dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf, + dma->dma_len, dma->dma_data_dir); + complete(&dma->cmd_complete); +} + +static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs) +{ + struct imx_i2c_dma *dma = i2c_imx->dma; + struct dma_async_tx_descriptor *txdesc; + struct device *dev = &i2c_imx->adapter.dev; + struct device *chan_dev = dma->chan_using->device->dev; + + dma->dma_buf = dma_map_single(chan_dev, msgs->buf, + dma->dma_len, dma->dma_data_dir); + if (dma_mapping_error(chan_dev, dma->dma_buf)) { + dev_err(dev, "DMA mapping failed\n"); + goto err_map; + } + + txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, + dma->dma_len, dma->dma_transfer_dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_err(dev, "Not able to get desc for DMA xfer\n"); + goto err_desc; + } + + reinit_completion(&dma->cmd_complete); + txdesc->callback = i2c_imx_dma_callback; + txdesc->callback_param = i2c_imx; + if (dma_submit_error(dmaengine_submit(txdesc))) { + dev_err(dev, "DMA submit failed\n"); + goto err_submit; + } + + dma_async_issue_pending(dma->chan_using); + return 0; + +err_submit: + dmaengine_terminate_sync(dma->chan_using); +err_desc: + dma_unmap_single(chan_dev, dma->dma_buf, + dma->dma_len, dma->dma_data_dir); +err_map: + return -EINVAL; +} + +static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) +{ + struct imx_i2c_dma *dma = i2c_imx->dma; + + dma->dma_buf = 0; + dma->dma_len = 0; + + dma_release_channel(dma->chan_tx); + dma->chan_tx = NULL; + + dma_release_channel(dma->chan_rx); + dma->chan_rx = NULL; + + dma->chan_using = NULL; +} + +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; + + while (1) { + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + + /* check for arbitration lost */ + if (multi_master && (temp & I2SR_IAL)) { + i2c_imx_clear_irq(i2c_imx, I2SR_IAL); + return -EAGAIN; + } + + if (for_busy && (!multi_master || (temp & I2SR_IBB))) { + i2c_imx->stopped = 0; + break; + } + if (!for_busy && !(temp & I2SR_IBB)) { + i2c_imx->stopped = 1; + break; + } + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> I2C bus is busy\n", __func__); + return -ETIMEDOUT; + } + if (atomic) + udelay(100); + else + schedule(); + } + + return 0; +} + +static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic) +{ + 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; +} + +static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) +{ + if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) { + dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__); + return -ENXIO; /* No ACK */ + } + + dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__); + return 0; +} + +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 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 = 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) + i = i2c_imx->hwdata->ndivs - 1; + else + for (i = 0; i2c_clk_div[i].div < div; i++) + ; + + /* Store divider value */ + i2c_imx->ifdr = i2c_clk_div[i].val; + + /* + * There dummy delay is calculated. + * It should be about one I2C clock period long. + * This delay is used in I2C bus disable function + * to fix chip hardware bug. + */ + 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", + i2c_clk_rate, div); + 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(nb, + struct imx_i2c_struct, + clk_change_nb); + int ret = 0; + + if (action & POST_RATE_CHANGE) + ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate); + + return notifier_from_errno(ret); +} + +static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic) +{ + unsigned int temp = 0; + int result; + + 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 */ + 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, 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, bool atomic) +{ + unsigned int temp = 0; + + if (!i2c_imx->stopped) { + /* Stop I2C transaction */ + 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; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + } + if (is_imx1_i2c(i2c_imx)) { + /* + * This delay caused by an i.MXL hardware bug. + * If no (or too short) delay, no "STOP" bit will be generated. + */ + udelay(i2c_imx->disable_delay); + } + + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0, atomic); + + /* Disable I2C controller */ + temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); +} + +/* + * 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) +{ + 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; + + /* 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; + } + + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event = I2C_SLAVE_STOP; + } + + 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); + + hrtimer_cancel(&i2c_imx->slave_timer); + 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; + + 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 enum imx_i2c_state i2c_imx_isr_read_continue(struct imx_i2c_struct *i2c_imx) +{ + enum imx_i2c_state next_state = IMX_I2C_STATE_READ_CONTINUE; + 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); + + return IMX_I2C_STATE_DONE; + } + /* + * 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); + next_state = IMX_I2C_STATE_DONE; + } 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); + return next_state; +} + +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); + unsigned int temp; + + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + * So NACK it (TXAK) to not hold the bus. + */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + if (len == 0) { + i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = 0; + i2c_imx->msg->len = 2; + return; + } + + i2c_imx->isr_result = -EPROTO; + i2c_imx->state = IMX_I2C_STATE_FAILED; + wake_up(&i2c_imx->queue); + return; + } + 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->state = i2c_imx_isr_read_continue(i2c_imx); + if (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); + if (i2c_imx->state == IMX_I2C_STATE_READ_BLOCK_DATA_LEN) + 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 i2c_imx_master_isr(i2c_imx, status); +} + +static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs) +{ + int result; + unsigned long time_left; + unsigned int temp = 0; + unsigned long orig_jiffies = jiffies; + 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; + dma->dma_len = msgs->len - 1; + result = i2c_imx_dma_xfer(i2c_imx, msgs); + if (result) + return result; + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* + * Write slave address. + * The first byte must be transmitted by the CPU. + */ + imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR); + time_left = wait_for_completion_timeout( + &i2c_imx->dma->cmd_complete, + msecs_to_jiffies(DMA_TIMEOUT)); + if (time_left == 0) { + dmaengine_terminate_sync(dma->chan_using); + return -ETIMEDOUT; + } + + /* Waiting for transfer complete. */ + while (1) { + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if (temp & I2SR_ICF) + break; + if (time_after(jiffies, orig_jiffies + + msecs_to_jiffies(DMA_TIMEOUT))) { + dev_dbg(dev, "<%s> Timeout\n", __func__); + return -ETIMEDOUT; + } + schedule(); + } + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp &= ~I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* 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, 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) +{ + int result; + unsigned long time_left; + unsigned int temp; + unsigned long orig_jiffies = jiffies; + 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; + dma->dma_data_dir = DMA_FROM_DEVICE; + /* The last two data bytes must be transferred by the CPU. */ + dma->dma_len = msgs->len - 2; + result = i2c_imx_dma_xfer(i2c_imx, msgs); + if (result) + return result; + + time_left = wait_for_completion_timeout( + &i2c_imx->dma->cmd_complete, + msecs_to_jiffies(DMA_TIMEOUT)); + if (time_left == 0) { + dmaengine_terminate_sync(dma->chan_using); + return -ETIMEDOUT; + } + + /* waiting for transfer complete. */ + while (1) { + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if (temp & I2SR_ICF) + break; + if (time_after(jiffies, orig_jiffies + + msecs_to_jiffies(DMA_TIMEOUT))) { + dev_dbg(dev, "<%s> Timeout\n", __func__); + return -ETIMEDOUT; + } + schedule(); + } + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp &= ~I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* read n-1 byte data */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + 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, false); + if (result) + return result; + + if (is_lastmsg) { + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + 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); + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0, false); + } 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); + } + msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + + return 0; +} + +static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs) +{ + int i, result; + + 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, true); + if (result) + return result; + result = i2c_imx_acked(i2c_imx); + if (result) + return result; + dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__); + + /* write data */ + for (i = 0; i < msgs->len; i++) { + dev_dbg(&i2c_imx->adapter.dev, + "<%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, true); + if (result) + return result; + result = i2c_imx_acked(i2c_imx); + if (result) + return result; + } + return 0; +} + +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 block_err = 0; + + result = i2c_imx_prepare_read(i2c_imx, msgs, false); + if (result) + return result; + + dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); + + /* read data */ + for (i = 0; i < msgs->len; i++) { + u8 len = 0; + + result = i2c_imx_trx_complete(i2c_imx, true); + if (result) + return result; + /* + * First byte is the length of remaining packet + * in the SMBus block data read. Add it to + * msgs->len. + */ + if ((!i) && block_data) { + len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + */ + if (len > I2C_SMBUS_BLOCK_MAX) + block_err = -EPROTO; + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + msgs->buf[0] = 0; + msgs->len = 2; + continue; + } + dev_dbg(&i2c_imx->adapter.dev, + "<%s> read length: 0x%X\n", + __func__, len); + msgs->len += len; + } + if (i == (msgs->len - 1)) { + if (is_lastmsg) { + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + 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); + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0, true); + } 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 (i == (msgs->len - 2)) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> set TXAK\n", __func__); + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + } + if ((!i) && block_data) + msgs->buf[0] = len; + else + msgs->buf[i] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + dev_dbg(&i2c_imx->adapter.dev, + "<%s> read byte: B%d=0x%X\n", + __func__, i, msgs->buf[i]); + } + return block_err; +} + +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; + int ret = 0; + + 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->is_lastmsg) { + if (!i2c_imx->stopped) + ret = i2c_imx_bus_busy(i2c_imx, 0, false); + /* + * Only read the last byte of the last message after the bus is + * not busy. Else the controller generates another clock which + * might confuse devices. + */ + if (!ret) + i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = imx_i2c_read_reg(i2c_imx, + IMX_I2C_I2DR); + } + + return ret; +} + +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); + int use_dma = 0; + + /* Start I2C transfer */ + result = i2c_imx_start(i2c_imx, atomic); + if (result) { + /* + * 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, atomic); + } + } + + if (result) + goto fail0; + + /* read/write data */ + for (i = 0; i < num; i++) { + if (i == num - 1) + is_lastmsg = true; + + if (i) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> repeated start\n", __func__); + 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, atomic); + if (result) + goto fail0; + } + dev_dbg(&i2c_imx->adapter.dev, + "<%s> transfer message: %d\n", __func__, i); + /* write/read data */ +#ifdef CONFIG_I2C_DEBUG_BUS + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + dev_dbg(&i2c_imx->adapter.dev, + "<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", + __func__, + (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0), + (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0), + (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0)); + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + dev_dbg(&i2c_imx->adapter.dev, + "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", + __func__, + (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0), + (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0), + (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0), + (temp & I2SR_RXAK ? 1 : 0)); +#endif + + 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]); + } + if (result) + goto fail0; + } + +fail0: + /* Stop I2C transfer */ + i2c_imx_stop(i2c_imx, atomic); + + 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 int i2c_imx_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + 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; + + result = i2c_imx_xfer_common(adapter, msgs, num, false); + + pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent); + + return result; +} + +static int i2c_imx_xfer_atomic(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); + int result; + + result = clk_enable(i2c_imx->clk); + if (result) + return result; + + result = i2c_imx_xfer_common(adapter, msgs, num, true); + + clk_disable(i2c_imx->clk); + + return result; +} + +/* + * We switch SCL and SDA to their GPIO function and do some bitbanging + * for bus recovery. These alternative pinmux settings can be + * described in the device tree by a separate pinctrl state "gpio". If + * this is missing this is not a big problem, the only implication is + * that we can't do bus recovery. + */ +static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx, + struct platform_device *pdev) +{ + struct i2c_bus_recovery_info *bri = &i2c_imx->rinfo; + + bri->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(bri->pinctrl)) + return PTR_ERR(bri->pinctrl); + + i2c_imx->adapter.bus_recovery_info = bri; + + return 0; +} + +static u32 i2c_imx_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL + | I2C_FUNC_SMBUS_READ_BLOCK_DATA; +} + +static const struct i2c_algorithm i2c_imx_algo = { + .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) +{ + 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; + const struct imx_i2c_hwdata *match; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_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; + + 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 */ + 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_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); + + /* Set up adapter data */ + i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); + + /* Set up platform driver data */ + platform_set_drvdata(pdev, i2c_imx); + + pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); + 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 = 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); + 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; + } + + i2c_imx_reset_regs(i2c_imx); + + /* Init optional bus recovery function */ + ret = i2c_imx_init_recovery_info(i2c_imx, pdev); + /* Give it another chance if pinctrl used is not ready yet */ + 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_put_autosuspend(&pdev->dev); + + dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq); + dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); + dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", + i2c_imx->adapter.name); + dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); + + 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); + return ret; +} + +static void i2c_imx_remove(struct platform_device *pdev) +{ + struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); + int irq, ret; + + ret = pm_runtime_get_sync(&pdev->dev); + + hrtimer_cancel(&i2c_imx->slave_timer); + + /* remove adapter */ + dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); + i2c_del_adapter(&i2c_imx->adapter); + + if (i2c_imx->dma) + i2c_imx_dma_free(i2c_imx); + + 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); + 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); +} + +static int i2c_imx_runtime_suspend(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; + + clk_disable(i2c_imx->clk); + + return 0; +} + +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); + pinctrl_pm_select_sleep_state(dev); + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&i2c_imx->adapter); + + /* + * Cancel the slave timer before powering down to prevent + * i2c_imx_slave_timeout() from accessing hardware registers + * while the clock is disabled. + */ + hrtimer_cancel(&i2c_imx->slave_timer); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&i2c_imx->adapter); + if (i2c_imx->slave) { + hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY); + hrtimer_restart(&i2c_imx->slave_timer); + } + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_resume_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + i2c_mark_adapter_resumed(&i2c_imx->adapter); + + return 0; +} + +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 = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq, + i2c_imx_resume_noirq) + SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) + RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) +}; + +static struct platform_driver i2c_imx_driver = { + .probe = i2c_imx_probe, + .remove = i2c_imx_remove, + .driver = { + .name = DRIVER_NAME, + .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, +}; + +static int __init i2c_adap_imx_init(void) +{ + return platform_driver_register(&i2c_imx_driver); +} +subsys_initcall(i2c_adap_imx_init); + +static void __exit i2c_adap_imx_exit(void) +{ + platform_driver_unregister(&i2c_imx_driver); +} +module_exit(i2c_adap_imx_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Darius Augulis"); +MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c new file mode 100644 index 000000000..4c67c5d18 --- /dev/null +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* ------------------------------------------------------------------------- */ +/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd + * + * + * With acknowledgements to i2c-algo-ibm_ocp.c by + * Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com + * + * And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund: + * + * Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund + * + * And which acknowledged Kyösti Mälkki , + * Frodo Looijaard , Martin Bailey + * + * Major cleanup by Deepak Saxena , 01/2005: + * + * - Use driver model to pass per-chip info instead of hardcoding and #ifdefs + * - Use ioremap/__raw_readl/__raw_writel instead of direct dereference + * - Make it work with IXP46x chips + * - Cleanup function names, coding style, etc + * + * - writing to local target address causes latchup on iop331. + * fix: driver refuses to address self. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-iop3xx.h" + +/* global unit counter */ +static int i2c_id; + +static inline unsigned char +iic_cook_addr(struct i2c_msg *msg) +{ + unsigned char addr; + + addr = i2c_8bit_addr_from_msg(msg); + + return addr; +} + +static void +iop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + /* Follows devman 9.3 */ + __raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET); + __raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET); + __raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET); +} + +static void +iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE; + + /* + * Every time unit enable is asserted, GPOD needs to be cleared + * 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 (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 | + IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY; + + cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | + IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE; + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); +} + +static void +iop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + + cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE | + IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN); + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); +} + +/* + * NB: the handler has to clear the source of the interrupt! + * Then it passes the SR flags of interest to BH via adap data + */ +static irqreturn_t +iop3xx_i2c_irq_handler(int this_irq, void *dev_id) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; + u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET); + + if ((sr &= iop3xx_adap->SR_enabled)) { + __raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET); + iop3xx_adap->SR_received |= sr; + wake_up_interruptible(&iop3xx_adap->waitq); + } + return IRQ_HANDLED; +} + +/* check all error conditions, clear them , report most important */ +static int +iop3xx_i2c_error(u32 sr) +{ + int rc = 0; + + if ((sr & IOP3XX_ISR_BERRD)) { + if (!rc) + rc = -I2C_ERR_BERR; + } + if ((sr & IOP3XX_ISR_ALD)) { + if (!rc) + rc = -I2C_ERR_ALD; + } + return rc; +} + +static inline u32 +iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + unsigned long flags; + u32 sr; + + spin_lock_irqsave(&iop3xx_adap->lock, flags); + sr = iop3xx_adap->SR_received; + iop3xx_adap->SR_received = 0; + spin_unlock_irqrestore(&iop3xx_adap->lock, flags); + + return sr; +} + +/* + * sleep until interrupted, then recover and analyse the SR + * saved by handler + */ +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, + compare_func compare) +{ + unsigned sr = 0; + int interrupted; + int done; + int rc = 0; + + do { + interrupted = wait_event_interruptible_timeout ( + iop3xx_adap->waitq, + (done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)), + 1 * HZ + ); + if ((rc = iop3xx_i2c_error(sr)) < 0) { + *status = sr; + return rc; + } else if (!interrupted) { + *status = sr; + return -ETIMEDOUT; + } + } while (!done); + + *status = sr; + + return 0; +} + +/* + * Concrete compare_funcs + */ +static int +all_bits_clear(unsigned test, unsigned mask) +{ + return (test & mask) == 0; +} + +static int +any_bits_set(unsigned test, unsigned mask) +{ + return (test & mask) != 0; +} + +static int +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, + status, any_bits_set); +} + +static int +iop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_i2c_wait_event( + iop3xx_adap, + IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, + status, any_bits_set); +} + +static int +iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_i2c_wait_event( + iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear); +} + +static int +iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, + struct i2c_msg *msg) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + int status; + int rc; + + /* avoid writing to local target address (hangs on 80331), + * forbidden in Intel developer manual + */ + if (msg->addr == MYSAR) { + return -EBUSY; + } + + __raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET); + + cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); + cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE; + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); + + return rc; +} + +static int +iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, + int stop) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + int status; + int rc = 0; + + __raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET); + cr &= ~IOP3XX_ICR_MSTART; + if (stop) { + cr |= IOP3XX_ICR_MSTOP; + } else { + cr &= ~IOP3XX_ICR_MSTOP; + } + cr |= IOP3XX_ICR_TBYTE; + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); + + return rc; +} + +static int +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); + int status; + int rc = 0; + + cr &= ~IOP3XX_ICR_MSTART; + + if (stop) { + cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK; + } else { + cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); + } + cr |= IOP3XX_ICR_TBYTE; + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + + rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status); + + *byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET); + + return rc; +} + +static int +iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for (ii = 0; rc == 0 && ii != count; ++ii) + rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1); + return rc; +} + +static int +iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for (ii = 0; rc == 0 && ii != count; ++ii) + rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1); + + return rc; +} + +/* + * Description: This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * FROM THE SAME ADDRESS + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ +static int +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; + + rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg); + if (rc < 0) { + return rc; + } + + if ((pmsg->flags&I2C_M_RD)) { + return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len); + } else { + return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len); + } +} + +static int +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; + int ret = 0; + int status; + + iop3xx_i2c_wait_idle(iop3xx_adap, &status); + iop3xx_i2c_reset(iop3xx_adap); + iop3xx_i2c_enable(iop3xx_adap); + + for (im = 0; ret == 0 && im != num; im++) { + ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]); + } + + iop3xx_i2c_transaction_cleanup(iop3xx_adap); + + if (ret) + return ret; + + return im; +} + +static u32 +iop3xx_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm iop3xx_i2c_algo = { + .xfer = iop3xx_i2c_xfer, + .functionality = iop3xx_i2c_func, +}; + +static void +iop3xx_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *padapter = platform_get_drvdata(pdev); + struct i2c_algo_iop3xx_data *adapter_data = + (struct i2c_algo_iop3xx_data *)padapter->algo_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET); + + /* + * Disable the actual HW unit + */ + cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | + IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE); + __raw_writel(cr, adapter_data->ioaddr + CR_OFFSET); + + iounmap(adapter_data->ioaddr); + release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); + kfree(adapter_data); + kfree(padapter); +} + +static int +iop3xx_i2c_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, irq; + struct i2c_adapter *new_adapter; + struct i2c_algo_iop3xx_data *adapter_data; + + new_adapter = kzalloc_obj(struct i2c_adapter); + if (!new_adapter) { + ret = -ENOMEM; + goto out; + } + + adapter_data = kzalloc_obj(struct i2c_algo_iop3xx_data); + if (!adapter_data) { + ret = -ENOMEM; + 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; + goto free_both; + } + + if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) { + ret = -EBUSY; + goto free_both; + } + + /* set the adapter enumeration # */ + adapter_data->id = i2c_id++; + + adapter_data->ioaddr = ioremap(res->start, IOP3XX_I2C_IO_SIZE); + if (!adapter_data->ioaddr) { + ret = -ENOMEM; + goto release_region; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto unmap; + } + ret = request_irq(irq, iop3xx_i2c_irq_handler, 0, + pdev->name, adapter_data); + + if (ret) + goto unmap; + + memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); + new_adapter->owner = THIS_MODULE; + 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; + + /* + * Default values...should these come in from board code? + */ + new_adapter->timeout = HZ; + new_adapter->algo = &iop3xx_i2c_algo; + + init_waitqueue_head(&adapter_data->waitq); + spin_lock_init(&adapter_data->lock); + + iop3xx_i2c_reset(adapter_data); + iop3xx_i2c_enable(adapter_data); + + platform_set_drvdata(pdev, new_adapter); + new_adapter->algo_data = adapter_data; + + i2c_add_numbered_adapter(new_adapter); + + return 0; + +unmap: + iounmap(adapter_data->ioaddr); + +release_region: + release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); + +free_both: + kfree(adapter_data); + +free_adapter: + kfree(new_adapter); + +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, + }, +}; + +module_platform_driver(iop3xx_i2c_driver); + +MODULE_AUTHOR("D-TACQ Solutions Ltd "); +MODULE_DESCRIPTION("IOP3xx iic algorithm and driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:IOP3xx-I2C"); diff --git a/drivers/i2c/busses/i2c-iop3xx.h b/drivers/i2c/busses/i2c-iop3xx.h new file mode 100644 index 000000000..143548335 --- /dev/null +++ b/drivers/i2c/busses/i2c-iop3xx.h @@ -0,0 +1,99 @@ +/* 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 + * + + */ +/* ------------------------------------------------------------------------- */ + + +#ifndef I2C_IOP3XX_H +#define I2C_IOP3XX_H 1 + +/* + * iop321 hardware bit definitions + */ +#define IOP3XX_ICR_FAST_MODE 0x8000 /* 1=400kBps, 0=100kBps */ +#define IOP3XX_ICR_UNIT_RESET 0x4000 /* 1=RESET */ +#define IOP3XX_ICR_SAD_IE 0x2000 /* 1=Slave Detect Interrupt Enable */ +#define IOP3XX_ICR_ALD_IE 0x1000 /* 1=Arb Loss Detect Interrupt Enable */ +#define IOP3XX_ICR_SSD_IE 0x0800 /* 1=Slave STOP Detect Interrupt Enable */ +#define IOP3XX_ICR_BERR_IE 0x0400 /* 1=Bus Error Interrupt Enable */ +#define IOP3XX_ICR_RXFULL_IE 0x0200 /* 1=Receive Full Interrupt Enable */ +#define IOP3XX_ICR_TXEMPTY_IE 0x0100 /* 1=Transmit Empty Interrupt Enable */ +#define IOP3XX_ICR_GCD 0x0080 /* 1=General Call Disable */ +/* + * IOP3XX_ICR_GCD: 1 disables response as slave. "This bit must be set + * when sending a master mode general call message from the I2C unit" + */ +#define IOP3XX_ICR_UE 0x0040 /* 1=Unit Enable */ +/* + * "NOTE: To avoid I2C bus integrity problems, + * the user needs to ensure that the GPIO Output Data Register - + * GPOD bits associated with an I2C port are cleared prior to setting + * the enable bit for that I2C serial port. + * The user prepares to enable I2C port 0 and + * I2C port 1 by clearing GPOD bits 7:6 and GPOD bits 5:4, respectively. + */ +#define IOP3XX_ICR_SCLEN 0x0020 /* 1=SCL enable for master mode */ +#define IOP3XX_ICR_MABORT 0x0010 /* 1=Send a STOP with no data + * NB TBYTE must be clear */ +#define IOP3XX_ICR_TBYTE 0x0008 /* 1=Send/Receive a byte. i2c clears */ +#define IOP3XX_ICR_NACK 0x0004 /* 1=reply with NACK */ +#define IOP3XX_ICR_MSTOP 0x0002 /* 1=send a STOP after next data byte */ +#define IOP3XX_ICR_MSTART 0x0001 /* 1=initiate a START */ + + +#define IOP3XX_ISR_BERRD 0x0400 /* 1=BUS ERROR Detected */ +#define IOP3XX_ISR_SAD 0x0200 /* 1=Slave ADdress Detected */ +#define IOP3XX_ISR_GCAD 0x0100 /* 1=General Call Address Detected */ +#define IOP3XX_ISR_RXFULL 0x0080 /* 1=Receive Full */ +#define IOP3XX_ISR_TXEMPTY 0x0040 /* 1=Transmit Empty */ +#define IOP3XX_ISR_ALD 0x0020 /* 1=Arbitration Loss Detected */ +#define IOP3XX_ISR_SSD 0x0010 /* 1=Slave STOP Detected */ +#define IOP3XX_ISR_BBUSY 0x0008 /* 1=Bus BUSY */ +#define IOP3XX_ISR_UNITBUSY 0x0004 /* 1=Unit Busy */ +#define IOP3XX_ISR_NACK 0x0002 /* 1=Unit Rx or Tx a NACK */ +#define IOP3XX_ISR_RXREAD 0x0001 /* 1=READ 0=WRITE (R/W bit of slave addr */ + +#define IOP3XX_ISR_CLEARBITS 0x07f0 + +#define IOP3XX_ISAR_SAMASK 0x007f + +#define IOP3XX_IDBR_MASK 0x00ff + +#define IOP3XX_IBMR_SCL 0x0002 +#define IOP3XX_IBMR_SDA 0x0001 + +#define IOP3XX_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */ +#define IOP3XX_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */ + +#define MYSAR 0 /* default slave address */ + +#define I2C_ERR 321 +#define I2C_ERR_BERR (I2C_ERR+0) +#define I2C_ERR_ALD (I2C_ERR+1) + + +#define CR_OFFSET 0 +#define SR_OFFSET 0x4 +#define SAR_OFFSET 0x8 +#define DBR_OFFSET 0xc +#define CCR_OFFSET 0x10 +#define BMR_OFFSET 0x14 + +#define IOP3XX_I2C_IO_SIZE 0x18 + +struct i2c_algo_iop3xx_data { + void __iomem *ioaddr; + wait_queue_head_t waitq; + 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 new file mode 100644 index 000000000..a2ac992f9 --- /dev/null +++ b/drivers/i2c/busses/i2c-isch.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linux kernel driver for Intel SCH chipset SMBus + * - Based on i2c-piix4.c + * Copyright (c) 1998 - 2002 Frodo Looijaard and + * Philip Edelbrock + * - Intel SCH support + * Copyright (c) 2007 - 2008 Jacob Jun Pan + */ + +/* Supports: Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SCH SMBus address offsets */ +#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 +#define SCH_BYTE 0x01 +#define SCH_BYTE_DATA 0x02 +#define SCH_WORD_DATA 0x03 +#define SCH_BLOCK_DATA 0x05 + +struct sch_i2c { + struct i2c_adapter adapter; + void __iomem *smba; +}; + +static int backbone_speed = 33000; /* backbone speed in kHz */ +module_param(backbone_speed, int, 0600); +MODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)"); + +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(struct i2c_adapter *adap) +{ + struct sch_i2c *priv = container_of(adap, struct sch_i2c, adapter); + int temp; + int rc; + + 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 = sch_io_rd8(priv, SMBHSTSTS) & 0x0f; + if (temp) { + /* Can not be busy since we checked it in sch_access */ + 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(&adap->dev, "SMBus is not ready: (%02x)\n", temp); + return -EAGAIN; + } + } + + /* 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 (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) { + rc = -EIO; + dev_err(&adap->dev, "Error: no response!\n"); + } else if (temp & 0x01) { + 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(&adap->dev, + "Failed reset at end of transaction (%02x), Bus error!\n", temp); + } + } else { + rc = -ENXIO; + dev_dbg(&adap->dev, "No such address.\n"); + } + 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; +} + +/** + * 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 = sch_io_rd8(priv, SMBHSTSTS) & 0x0f; + if (temp & 0x08) { + dev_dbg(&adap->dev, "SMBus busy (%02x)\n", temp); + return -EAGAIN; + } + temp = sch_io_rd16(priv, SMBHSTCLK); + if (!temp) { + /* + * We can't determine if we have 33 or 25 MHz clock for + * SMBus, so expect 33 MHz and calculate a bus clock of + * 100 kHz. If we actually run at 25 MHz the bus will be + * run ~75 kHz instead which should do no harm. + */ + dev_notice(&adap->dev, "Clock divider uninitialized. Setting defaults\n"); + sch_io_wr16(priv, SMBHSTCLK, backbone_speed / (4 * 100)); + } + + dev_dbg(&adap->dev, "access size: %d %s\n", size, str_read_write(read_write)); + switch (size) { + case I2C_SMBUS_QUICK: + sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write); + size = SCH_QUICK; + break; + case I2C_SMBUS_BYTE: + sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write); + if (read_write == I2C_SMBUS_WRITE) + sch_io_wr8(priv, SMBHSTCMD, command); + size = SCH_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write); + sch_io_wr8(priv, SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + sch_io_wr8(priv, SMBHSTDAT0, data->byte); + size = SCH_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write); + sch_io_wr8(priv, SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) { + 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: + 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; + sch_io_wr8(priv, SMBHSTDAT0, len); + for (i = 1; i <= len; i++) + sch_io_wr8(priv, SMBBLKDAT + i - 1, data->block[i]); + } + size = SCH_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + 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(adap); + if (rc) /* Error in transaction */ + return rc; + + if ((read_write == I2C_SMBUS_WRITE) || (size == SCH_QUICK)) + return 0; + + switch (size) { + case SCH_BYTE: + case SCH_BYTE_DATA: + data->byte = sch_io_rd8(priv, SMBHSTDAT0); + break; + case SCH_WORD_DATA: + data->word = (sch_io_rd8(priv, SMBHSTDAT0) << 0) + + (sch_io_rd8(priv, SMBHSTDAT1) << 8); + break; + case SCH_BLOCK_DATA: + 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] = sch_io_rd8(priv, SMBBLKDAT + i - 1); + break; + } + return 0; +} + +static u32 sch_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_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sch_access, + .functionality = sch_func, +}; + +static int smbus_sch_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sch_i2c *priv; + struct resource *res; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EBUSY; + + 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); + + /* 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; + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus SCH adapter at %04x", (unsigned short)res->start); + + return devm_i2c_add_adapter(dev, &priv->adapter); +} + +static struct platform_driver smbus_sch_driver = { + .driver = { + .name = "isch_smbus", + }, + .probe = smbus_sch_probe, +}; + +module_platform_driver(smbus_sch_driver); + +MODULE_AUTHOR("Jacob Pan "); +MODULE_DESCRIPTION("Intel SCH SMBus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:isch_smbus"); diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 000000000..7aaefb214 --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,994 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * GPL LICENSE SUMMARY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * 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. + */ + +/* + * Supports the SMBus Message Transport (SMT) in the Intel Atom Processor + * S12xx Product Family. + * + * Features supported by this driver: + * Hardware PEC yes + * Block buffer yes + * Block process call transaction yes + * Slave mode no + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#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 */ +#define ISMT_DESC_BLK 0X04 /* Perform Block Transaction */ +#define ISMT_DESC_FAIR 0x08 /* Set fairness flag upon successful arbit. */ +#define ISMT_DESC_PEC 0x10 /* Packet Error Code */ +#define ISMT_DESC_I2C 0x20 /* I2C Enable */ +#define ISMT_DESC_INT 0x40 /* Interrupt */ +#define ISMT_DESC_SOE 0x80 /* Stop On Error */ + +/* Hardware Descriptor Constants - Status Field */ +#define ISMT_DESC_SCS 0x01 /* Success */ +#define ISMT_DESC_DLTO 0x04 /* Data Low Time Out */ +#define ISMT_DESC_NAK 0x08 /* NAK Received */ +#define ISMT_DESC_CRC 0x10 /* CRC Error */ +#define ISMT_DESC_CLTO 0x20 /* Clock Low Time Out */ +#define ISMT_DESC_COL 0x40 /* Collisions */ +#define ISMT_DESC_LPR 0x80 /* Large Packet Received */ + +/* Macros */ +#define ISMT_DESC_ADDR_RW(addr, rw) (((addr) << 1) | (rw)) + +/* iSMT General Register address offsets (SMBBAR + ) */ +#define ISMT_GR_GCTRL 0x000 /* General Control */ +#define ISMT_GR_SMTICL 0x008 /* SMT Interrupt Cause Location */ +#define ISMT_GR_ERRINTMSK 0x010 /* Error Interrupt Mask */ +#define ISMT_GR_ERRAERMSK 0x014 /* Error AER Mask */ +#define ISMT_GR_ERRSTS 0x018 /* Error Status */ +#define ISMT_GR_ERRINFO 0x01c /* Error Information */ + +/* iSMT Master Registers */ +#define ISMT_MSTR_MDBA 0x100 /* Master Descriptor Base Address */ +#define ISMT_MSTR_MCTRL 0x108 /* Master Control */ +#define ISMT_MSTR_MSTS 0x10c /* Master Status */ +#define ISMT_MSTR_MDS 0x110 /* Master Descriptor Size */ +#define ISMT_MSTR_RPOLICY 0x114 /* Retry Policy */ + +/* iSMT Miscellaneous Registers */ +#define ISMT_SPGT 0x300 /* SMBus PHY Global Timing */ + +/* General Control Register (GCTRL) bit definitions */ +#define ISMT_GCTRL_TRST 0x04 /* Target Reset */ +#define ISMT_GCTRL_KILL 0x08 /* Kill */ +#define ISMT_GCTRL_SRST 0x40 /* Soft Reset */ + +/* Master Control Register (MCTRL) bit definitions */ +#define ISMT_MCTRL_SS 0x01 /* Start/Stop */ +#define ISMT_MCTRL_MEIE 0x10 /* Master Error Interrupt Enable */ +#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr (FMHP) */ + +/* Master Status Register (MSTS) bit definitions */ +#define ISMT_MSTS_HMTP 0xff0000 /* HW Master Tail Pointer (HMTP) */ +#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status (MIS) */ +#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status (MEIS) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#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 (0x2U << 30) /* 400 kHz */ +#define ISMT_SPGT_SPD_1M (0x3U << 30) /* 1 MHz */ + + +/* MSI Control Register (MSICTL) bit definitions */ +#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */ + +/* iSMT Hardware Descriptor */ +struct ismt_desc { + u8 tgtaddr_rw; /* target address & r/w bit */ + u8 wr_len_cmd; /* write length in bytes or a command */ + u8 rd_len; /* read length */ + u8 control; /* control bits */ + u8 status; /* status bits */ + u8 retry; /* collision retry and retry count */ + u8 rxbytes; /* received bytes */ + u8 txbytes; /* transmitted bytes */ + u32 dptr_low; /* lower 32 bit of the data pointer */ + u32 dptr_high; /* upper 32 bit of the data pointer */ +} __packed; + +struct ismt_priv { + struct i2c_adapter adapter; + void __iomem *smba; /* PCI BAR */ + struct pci_dev *pci_dev; + struct ismt_desc *hw; /* descriptor virt base addr */ + dma_addr_t io_rng_dma; /* descriptor HW base addr */ + 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; +}; + +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, } +}; + +MODULE_DEVICE_TABLE(pci, ismt_ids); + +/* Bus speed control bits for slow debuggers - refer to the docs for usage */ +static unsigned int bus_speed; +module_param(bus_speed, uint, S_IRUGO); +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) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw); + dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd); + dev_dbg(dev, "\trd_len= 0x%02X\n", desc->rd_len); + dev_dbg(dev, "\tcontrol= 0x%02X\n", desc->control); + dev_dbg(dev, "\tstatus= 0x%02X\n", desc->status); + dev_dbg(dev, "\tretry= 0x%02X\n", desc->retry); + dev_dbg(dev, "\trxbytes= 0x%02X\n", desc->rxbytes); + dev_dbg(dev, "\ttxbytes= 0x%02X\n", desc->txbytes); + dev_dbg(dev, "\tdptr_low= 0x%08X\n", desc->dptr_low); + dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high); +} +/** + * ismt_desc_dump() - dump the contents of a descriptor for debug purposes + * @priv: iSMT private data + */ +static void ismt_desc_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + struct ismt_desc *desc = &priv->hw[priv->head]; + + dev_dbg(dev, "Dump of the descriptor struct: 0x%X\n", priv->head); + __ismt_desc_dump(dev, desc); +} + +/** + * ismt_gen_reg_dump() - dump the iSMT General Registers + * @priv: iSMT private data + */ +static void ismt_gen_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT General Registers\n"); + dev_dbg(dev, " GCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_GCTRL, + readl(priv->smba + ISMT_GR_GCTRL)); + dev_dbg(dev, " SMTICL... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_GR_SMTICL, + (long long unsigned int)readq(priv->smba + ISMT_GR_SMTICL)); + dev_dbg(dev, " ERRINTMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINTMSK, + readl(priv->smba + ISMT_GR_ERRINTMSK)); + dev_dbg(dev, " ERRAERMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRAERMSK, + readl(priv->smba + ISMT_GR_ERRAERMSK)); + dev_dbg(dev, " ERRSTS... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRSTS, + readl(priv->smba + ISMT_GR_ERRSTS)); + dev_dbg(dev, " ERRINFO.. : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINFO, + readl(priv->smba + ISMT_GR_ERRINFO)); +} + +/** + * ismt_mstr_reg_dump() - dump the iSMT Master Registers + * @priv: iSMT private data + */ +static void ismt_mstr_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT Master Registers\n"); + dev_dbg(dev, " MDBA..... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_MSTR_MDBA, + (long long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA)); + dev_dbg(dev, " MCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MCTRL, + readl(priv->smba + ISMT_MSTR_MCTRL)); + dev_dbg(dev, " MSTS..... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MSTS, + readl(priv->smba + ISMT_MSTR_MSTS)); + dev_dbg(dev, " MDS...... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MDS, + readl(priv->smba + ISMT_MSTR_MDS)); + dev_dbg(dev, " RPOLICY.. : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_RPOLICY, + readl(priv->smba + ISMT_MSTR_RPOLICY)); + dev_dbg(dev, " SPGT..... : (0x%p)=0x%X\n", + priv->smba + ISMT_SPGT, + readl(priv->smba + ISMT_SPGT)); +} + +/** + * ismt_submit_desc() - add a descriptor to the ring + * @priv: iSMT private data + */ +static void ismt_submit_desc(struct ismt_priv *priv) +{ + uint fmhp; + uint val; + + ismt_desc_dump(priv); + ismt_gen_reg_dump(priv); + ismt_mstr_reg_dump(priv); + + /* Set the FMHP (Firmware Master Head Pointer)*/ + fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16; + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel((val & ~ISMT_MCTRL_FMHP) | fmhp, + priv->smba + ISMT_MSTR_MCTRL); + + /* Set the start bit */ + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel(val | ISMT_MCTRL_SS, + priv->smba + ISMT_MSTR_MCTRL); +} + +/** + * ismt_process_desc() - handle the completion of the descriptor + * @desc: the iSMT hardware descriptor + * @data: data buffer from the upper layer + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write: flag to indicate if this is a read or write + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + u8 *dma_buffer = PTR_ALIGN(&priv->buffer[0], 16); + + dev_dbg(&priv->pci_dev->dev, "Processing completed descriptor\n"); + __ismt_desc_dump(&priv->pci_dev->dev, desc); + ismt_gen_reg_dump(priv); + ismt_mstr_reg_dump(priv); + + if (desc->status & ISMT_DESC_SCS) { + if (read_write == I2C_SMBUS_WRITE && + size != I2C_SMBUS_PROC_CALL && + size != I2C_SMBUS_BLOCK_PROC_CALL) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = dma_buffer[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + 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; + + memcpy(data->block, dma_buffer, desc->rxbytes); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(&data->block[1], dma_buffer, desc->rxbytes); + data->block[0] = desc->rxbytes; + break; + } + return 0; + } + + if (likely(desc->status & ISMT_DESC_NAK)) + return -ENXIO; + + if (desc->status & ISMT_DESC_CRC) + return -EBADMSG; + + if (desc->status & ISMT_DESC_COL) + return -EAGAIN; + + if (desc->status & ISMT_DESC_LPR) + return -EPROTO; + + if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO)) + return -ETIMEDOUT; + + return -EIO; +} + +/** + * 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 + * @flags: command options + * @read_write: read from or write to device + * @command: the i2c/SMBus command to issue + * @size: SMBus transaction type + * @data: read/write data buffer + */ +static int ismt_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret; + unsigned long time_left; + dma_addr_t dma_addr = 0; /* address of the data buffer */ + u8 dma_size = 0; + enum dma_data_direction dma_direction = 0; + struct ismt_desc *desc; + struct ismt_priv *priv = i2c_get_adapdata(adap); + struct device *dev = &priv->pci_dev->dev; + u8 *dma_buffer = PTR_ALIGN(&priv->buffer[0], 16); + + desc = &priv->hw[priv->head]; + + /* Initialize the DMA buffer */ + memset(priv->buffer, 0, sizeof(priv->buffer)); + + /* Initialize the descriptor */ + 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; + else + desc->control = ISMT_DESC_FAIR; + + if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK) + && (size != I2C_SMBUS_I2C_BLOCK_DATA)) + desc->control |= ISMT_DESC_PEC; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(dev, "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Send Byte + * The command field contains the write data + */ + dev_dbg(dev, "I2C_SMBUS_BYTE: WRITE\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + } else { + /* Receive Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE: READ\n"); + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = 1; + } + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Write Byte + * Command plus 1 data byte + */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: WRITE\n"); + desc->wr_len_cmd = 2; + dma_size = 2; + dma_direction = DMA_TO_DEVICE; + dma_buffer[0] = command; + dma_buffer[1] = data->byte; + } else { + /* Read Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: READ\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + desc->rd_len = 1; + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Write Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: WRITE\n"); + desc->wr_len_cmd = 3; + dma_size = 3; + dma_direction = DMA_TO_DEVICE; + dma_buffer[0] = command; + dma_buffer[1] = data->word & 0xff; + dma_buffer[2] = data->word >> 8; + } else { + /* Read Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: READ\n"); + desc->wr_len_cmd = command; + desc->control |= ISMT_DESC_CWRL; + desc->rd_len = 2; + dma_size = 2; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n"); + desc->wr_len_cmd = 3; + desc->rd_len = 2; + dma_size = 3; + dma_direction = DMA_BIDIRECTIONAL; + dma_buffer[0] = command; + dma_buffer[1] = data->word & 0xff; + dma_buffer[2] = data->word >> 8; + break; + + case I2C_SMBUS_BLOCK_DATA: + 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; + desc->control |= ISMT_DESC_BLK; + dma_buffer[0] = command; + memcpy(&dma_buffer[1], &data->block[1], dma_size - 1); + } else { + /* Block Read */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: READ\n"); + dma_size = I2C_SMBUS_BLOCK_MAX; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = dma_size; + desc->wr_len_cmd = command; + desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL); + } + 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) + data->block[0] = 1; + + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + + if (read_write == I2C_SMBUS_WRITE) { + /* i2c Block Write */ + dev_dbg(dev, "I2C_SMBUS_I2C_BLOCK_DATA: WRITE\n"); + dma_size = data->block[0] + 1; + dma_direction = DMA_TO_DEVICE; + desc->wr_len_cmd = dma_size; + desc->control |= ISMT_DESC_I2C; + dma_buffer[0] = command; + memcpy(&dma_buffer[1], &data->block[1], dma_size - 1); + } else { + /* i2c Block Read */ + dev_dbg(dev, "I2C_SMBUS_I2C_BLOCK_DATA: READ\n"); + dma_size = data->block[0]; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = dma_size; + desc->wr_len_cmd = command; + desc->control |= (ISMT_DESC_I2C | ISMT_DESC_CWRL); + /* + * Per the "Table 15-15. I2C Commands", + * in the External Design Specification (EDS), + * (Document Number: 508084, Revision: 2.0), + * the _rw bit must be 0 + */ + desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, 0); + } + break; + + default: + dev_err(dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + /* map the data buffer */ + if (dma_size != 0) { + dev_dbg(dev, " dev=%p\n", dev); + dev_dbg(dev, " data=%p\n", data); + dev_dbg(dev, " dma_buffer=%p\n", dma_buffer); + dev_dbg(dev, " dma_size=%d\n", dma_size); + dev_dbg(dev, " dma_direction=%d\n", dma_direction); + + dma_addr = dma_map_single(dev, + dma_buffer, + dma_size, + dma_direction); + + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "Error in mapping dma buffer %p\n", + dma_buffer); + return -EIO; + } + + dev_dbg(dev, " dma_addr = %pad\n", &dma_addr); + + desc->dptr_low = lower_32_bits(dma_addr); + desc->dptr_high = upper_32_bits(dma_addr); + } + + reinit_completion(&priv->cmp); + + /* Add the descriptor */ + ismt_submit_desc(priv); + + /* Now we wait for interrupt completion, 1s */ + time_left = wait_for_completion_timeout(&priv->cmp, HZ*1); + + /* unmap the data buffer */ + if (dma_size != 0) + dma_unmap_single(dev, dma_addr, dma_size, dma_direction); + + if (unlikely(!time_left)) { + ismt_kill_transaction(priv); + ret = -ETIMEDOUT; + goto out; + } + + /* do any post processing of the descriptor here */ + ret = ismt_process_desc(desc, data, priv, size, read_write); + +out: + /* Update the ring pointer */ + priv->head++; + priv->head %= ISMT_DESC_ENTRIES; + + return ret; +} + +/** + * ismt_func() - report which i2c commands are supported by this adapter + * @adap: the i2c host adapter + */ +static u32 ismt_func(struct i2c_adapter *adap) +{ + 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_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ismt_access, + .functionality = ismt_func, +}; + +/** + * ismt_handle_isr() - interrupt handler bottom half + * @priv: iSMT private data + */ +static irqreturn_t ismt_handle_isr(struct ismt_priv *priv) +{ + complete(&priv->cmp); + + return IRQ_HANDLED; +} + + +/** + * ismt_do_interrupt() - IRQ interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_interrupt(int vec, void *data) +{ + u32 val; + struct ismt_priv *priv = data; + + /* + * check to see it's our interrupt, return IRQ_NONE if not ours + * since we are sharing interrupt + */ + val = readl(priv->smba + ISMT_MSTR_MSTS); + + if (!(val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS))) + return IRQ_NONE; + else + writel(val | ISMT_MSTS_MIS | ISMT_MSTS_MEIS, + priv->smba + ISMT_MSTR_MSTS); + + return ismt_handle_isr(priv); +} + +/** + * ismt_do_msi_interrupt() - MSI interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_msi_interrupt(int vec, void *data) +{ + return ismt_handle_isr(data); +} + +/** + * ismt_hw_init() - initialize the iSMT hardware + * @priv: iSMT private data + */ +static void ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* 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); + + /* initialize the Master Status Register (MSTS) */ + writel(0, priv->smba + ISMT_MSTR_MSTS); + + /* initialize the Master Descriptor Size (MDS) */ + val = readl(priv->smba + ISMT_MSTR_MDS); + writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1), + priv->smba + ISMT_MSTR_MDS); + + /* + * Set the SMBus speed (could use this for slow HW debuggers) + */ + + val = readl(priv->smba + ISMT_SPGT); + + switch (bus_speed) { + case 0: + break; + + case 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + bus_speed = 100; + break; + case ISMT_SPGT_SPD_400K: + bus_speed = 400; + break; + case ISMT_SPGT_SPD_1M: + bus_speed = 1000; + break; + } + dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int ismt_dev_init(struct ismt_priv *priv) +{ + /* allocate memory for the descriptor */ + priv->hw = dmam_alloc_coherent(&priv->pci_dev->dev, + (ISMT_DESC_ENTRIES + * sizeof(struct ismt_desc)), + &priv->io_rng_dma, + GFP_KERNEL); + if (!priv->hw) + return -ENOMEM; + + 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; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int ismt_int_init(struct ismt_priv *priv) +{ + int err; + + /* Try using MSI interrupts */ + err = pci_enable_msi(priv->pci_dev); + if (err) + goto intx; + + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_msi_interrupt, + 0, + "ismt-msi", + priv); + if (err) { + pci_disable_msi(priv->pci_dev); + goto intx; + } + + return 0; + + /* Try using legacy interrupts */ +intx: + dev_warn(&priv->pci_dev->dev, + "Unable to use MSI interrupts, falling back to legacy\n"); + + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_interrupt, + IRQF_SHARED, + "ismt-intx", + priv); + if (err) { + dev_err(&priv->pci_dev->dev, "no usable interrupts\n"); + return err; + } + + return 0; +} + +static struct pci_driver ismt_driver; + +/** + * ismt_probe() - probe for iSMT devices + * @pdev: PCI-Express device + * @id: PCI-Express device ID + */ +static int +ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct ismt_priv *priv; + unsigned long start, len; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(pdev, priv); + + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + priv->adapter.class = I2C_CLASS_HWMON; + priv->adapter.algo = &smbus_algorithm; + priv->adapter.dev.parent = &pdev->dev; + ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); + priv->adapter.retries = ISMT_MAX_RETRIES; + + priv->pci_dev = pdev; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + return err; + } + + /* enable bus mastering */ + pci_set_master(pdev); + + /* Determine the address of the SMBus area */ + start = pci_resource_start(pdev, SMBBAR); + len = pci_resource_len(pdev, SMBBAR); + if (!start || !len) { + dev_err(&pdev->dev, + "SMBus base address uninitialized, upgrade BIOS\n"); + return -ENODEV; + } + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus iSMT adapter at %lx", start); + + dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start); + dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len); + + err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]); + if (err) { + dev_err(&pdev->dev, "ACPI resource conflict!\n"); + return err; + } + + 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", + start, start + len); + return err; + } + + priv->smba = pcim_iomap(pdev, SMBBAR, len); + if (!priv->smba) { + dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n"); + 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); + if (err) + return err; + + ismt_hw_init(priv); + + err = ismt_int_init(priv); + if (err) + return err; + + err = i2c_add_adapter(&priv->adapter); + if (err) + return -ENODEV; + return 0; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void ismt_remove(struct pci_dev *pdev) +{ + struct ismt_priv *priv = pci_get_drvdata(pdev); + + i2c_del_adapter(&priv->adapter); +} + +static struct pci_driver ismt_driver = { + .name = "ismt_smbus", + .id_table = ismt_ids, + .probe = ismt_probe, + .remove = ismt_remove, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown "); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c new file mode 100644 index 000000000..695be3b21 --- /dev/null +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -0,0 +1,864 @@ +// 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 + * Copyright (C) 2019 周琰杰 (Zhou Yanjie) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JZ4780_I2C_CTRL 0x00 +#define JZ4780_I2C_TAR 0x04 +#define JZ4780_I2C_SAR 0x08 +#define JZ4780_I2C_DC 0x10 +#define JZ4780_I2C_SHCNT 0x14 +#define JZ4780_I2C_SLCNT 0x18 +#define JZ4780_I2C_FHCNT 0x1C +#define JZ4780_I2C_FLCNT 0x20 +#define JZ4780_I2C_INTST 0x2C +#define JZ4780_I2C_INTM 0x30 +#define JZ4780_I2C_RXTL 0x38 +#define JZ4780_I2C_TXTL 0x3C +#define JZ4780_I2C_CINTR 0x40 +#define JZ4780_I2C_CRXUF 0x44 +#define JZ4780_I2C_CRXOF 0x48 +#define JZ4780_I2C_CTXOF 0x4C +#define JZ4780_I2C_CRXREQ 0x50 +#define JZ4780_I2C_CTXABRT 0x54 +#define JZ4780_I2C_CRXDONE 0x58 +#define JZ4780_I2C_CACT 0x5C +#define JZ4780_I2C_CSTP 0x60 +#define JZ4780_I2C_CSTT 0x64 +#define JZ4780_I2C_CGC 0x68 +#define JZ4780_I2C_ENB 0x6C +#define JZ4780_I2C_STA 0x70 +#define JZ4780_I2C_TXABRT 0x80 +#define JZ4780_I2C_DMACR 0x88 +#define JZ4780_I2C_DMATDLR 0x8C +#define JZ4780_I2C_DMARDLR 0x90 +#define JZ4780_I2C_SDASU 0x94 +#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) +#define JZ4780_I2C_CTRL_REST BIT(5) +#define JZ4780_I2C_CTRL_MATP BIT(4) +#define JZ4780_I2C_CTRL_SATP BIT(3) +#define JZ4780_I2C_CTRL_SPDF BIT(2) +#define JZ4780_I2C_CTRL_SPDS BIT(1) +#define JZ4780_I2C_CTRL_MD BIT(0) + +#define JZ4780_I2C_STA_SLVACT BIT(6) +#define JZ4780_I2C_STA_MSTACT BIT(5) +#define JZ4780_I2C_STA_RFF BIT(4) +#define JZ4780_I2C_STA_RFNE BIT(3) +#define JZ4780_I2C_STA_TFE BIT(2) +#define JZ4780_I2C_STA_TFNF BIT(1) +#define JZ4780_I2C_STA_ACT BIT(0) + +#define X1000_I2C_DC_STOP BIT(9) + +#define JZ4780_I2C_INTST_IGC BIT(11) +#define JZ4780_I2C_INTST_ISTT BIT(10) +#define JZ4780_I2C_INTST_ISTP BIT(9) +#define JZ4780_I2C_INTST_IACT BIT(8) +#define JZ4780_I2C_INTST_RXDN BIT(7) +#define JZ4780_I2C_INTST_TXABT BIT(6) +#define JZ4780_I2C_INTST_RDREQ BIT(5) +#define JZ4780_I2C_INTST_TXEMP BIT(4) +#define JZ4780_I2C_INTST_TXOF BIT(3) +#define JZ4780_I2C_INTST_RXFL BIT(2) +#define JZ4780_I2C_INTST_RXOF BIT(1) +#define JZ4780_I2C_INTST_RXUF BIT(0) + +#define JZ4780_I2C_INTM_MIGC BIT(11) +#define JZ4780_I2C_INTM_MISTT BIT(10) +#define JZ4780_I2C_INTM_MISTP BIT(9) +#define JZ4780_I2C_INTM_MIACT BIT(8) +#define JZ4780_I2C_INTM_MRXDN BIT(7) +#define JZ4780_I2C_INTM_MTXABT BIT(6) +#define JZ4780_I2C_INTM_MRDREQ BIT(5) +#define JZ4780_I2C_INTM_MTXEMP BIT(4) +#define JZ4780_I2C_INTM_MTXOF BIT(3) +#define JZ4780_I2C_INTM_MRXFL BIT(2) +#define JZ4780_I2C_INTM_MRXOF BIT(1) +#define JZ4780_I2C_INTM_MRXUF BIT(0) + +#define JZ4780_I2C_DC_READ BIT(8) + +#define JZ4780_I2C_SDAHD_HDENB BIT(8) + +#define JZ4780_I2C_ENB_I2C BIT(0) + +#define JZ4780_I2CSHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8)) +#define JZ4780_I2CSLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1)) +#define JZ4780_I2CFHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8)) +#define JZ4780_I2CFLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1)) + +#define JZ4780_I2C_FIFO_LEN 16 + +#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; + unsigned long clk_rate_khz; + 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; + + /* beginning of lock scope */ + unsigned char *rbuf; + int rd_total_len; + int rd_data_xfered; + int rd_cmd_xfered; + + unsigned char *wbuf; + int wt_len; + + int is_write; + int stop_hold; + int speed; + + int data_buf[BUFSIZE]; + int cmd_buf[BUFSIZE]; + int cmd; + + /* end of lock scope */ + struct completion trans_waitq; +}; + +static inline unsigned short jz4780_i2c_readw(struct jz4780_i2c *i2c, + unsigned long offset) +{ + return readw(i2c->iomem + offset); +} + +static inline void jz4780_i2c_writew(struct jz4780_i2c *i2c, + unsigned long offset, unsigned short val) +{ + writew(val, i2c->iomem + offset); +} + +static int jz4780_i2c_disable(struct jz4780_i2c *i2c) +{ + unsigned short regval; + unsigned long loops = 5; + + jz4780_i2c_writew(i2c, JZ4780_I2C_ENB, 0); + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_ENSTA); + if (!(regval & JZ4780_I2C_ENB_I2C)) + return 0; + + usleep_range(5000, 15000); + } while (--loops); + + dev_err(&i2c->adap.dev, "disable failed: ENSTA=0x%04x\n", regval); + return -ETIMEDOUT; +} + +static int jz4780_i2c_enable(struct jz4780_i2c *i2c) +{ + unsigned short regval; + unsigned long loops = 5; + + jz4780_i2c_writew(i2c, JZ4780_I2C_ENB, 1); + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_ENSTA); + if (regval & JZ4780_I2C_ENB_I2C) + return 0; + + usleep_range(5000, 15000); + } while (--loops); + + dev_err(&i2c->adap.dev, "enable failed: ENSTA=0x%04x\n", regval); + return -ETIMEDOUT; +} + +static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address) +{ + unsigned short regval; + unsigned long loops = 5; + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + if ((regval & JZ4780_I2C_STA_TFE) && + !(regval & JZ4780_I2C_STA_MSTACT)) + break; + + usleep_range(5000, 15000); + } while (--loops); + + if (loops) { + jz4780_i2c_writew(i2c, JZ4780_I2C_TAR, address); + return 0; + } + + dev_err(&i2c->adap.dev, + "set device to address 0x%02x failed, STA=0x%04x\n", + address, regval); + + return -ENXIO; +} + +static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c) +{ + int dev_clk_khz = i2c->clk_rate_khz; + int cnt_high = 0; /* HIGH period count of the SCL clock */ + int cnt_low = 0; /* LOW period count of the SCL clock */ + int cnt_period = 0; /* period count of the SCL clock */ + int setup_time = 0; + int hold_time = 0; + unsigned short tmp = 0; + int i2c_clk = i2c->speed; + + if (jz4780_i2c_disable(i2c)) + dev_dbg(&i2c->adap.dev, "i2c not disabled\n"); + + /* + * 1 JZ4780_I2C cycle equals to cnt_period PCLK(i2c_clk) + * standard mode, min LOW and HIGH period are 4700 ns and 4000 ns + * fast mode, min LOW and HIGH period are 1300 ns and 600 ns + */ + cnt_period = dev_clk_khz / i2c_clk; + + if (i2c_clk <= 100) + cnt_high = (cnt_period * 4000) / (4700 + 4000); + else + cnt_high = (cnt_period * 600) / (1300 + 600); + + cnt_low = cnt_period - cnt_high; + + /* + * NOTE: JZ4780_I2C_CTRL_REST can't set when i2c enabled, because + * normal read are 2 messages, we cannot disable i2c controller + * between these two messages, this means that we must always set + * JZ4780_I2C_CTRL_REST when init JZ4780_I2C_CTRL + * + */ + if (i2c_clk <= 100) { + tmp = JZ4780_I2C_CTRL_SPDS | JZ4780_I2C_CTRL_REST + | JZ4780_I2C_CTRL_SLVDIS | JZ4780_I2C_CTRL_MD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + jz4780_i2c_writew(i2c, JZ4780_I2C_SHCNT, + JZ4780_I2CSHCNT_ADJUST(cnt_high)); + jz4780_i2c_writew(i2c, JZ4780_I2C_SLCNT, + JZ4780_I2CSLCNT_ADJUST(cnt_low)); + } else { + tmp = JZ4780_I2C_CTRL_SPDF | JZ4780_I2C_CTRL_REST + | JZ4780_I2C_CTRL_SLVDIS | JZ4780_I2C_CTRL_MD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + jz4780_i2c_writew(i2c, JZ4780_I2C_FHCNT, + JZ4780_I2CFHCNT_ADJUST(cnt_high)); + jz4780_i2c_writew(i2c, JZ4780_I2C_FLCNT, + JZ4780_I2CFLCNT_ADJUST(cnt_low)); + } + + /* + * a i2c device must internally provide a hold time at least 300ns + * tHD:DAT + * Standard Mode: min=300ns, max=3450ns + * Fast Mode: min=0ns, max=900ns + * tSU:DAT + * Standard Mode: min=250ns, max=infinite + * Fast Mode: min=100(250ns is recommended), max=infinite + * + * 1i2c_clk = 10^6 / dev_clk_khz + * on FPGA, dev_clk_khz = 12000, so 1i2c_clk = 1000/12 = 83ns + * on Pisces(1008M), dev_clk_khz=126000, so 1i2c_clk = 1000 / 126 = 8ns + * + * The actual hold time is (SDAHD + 1) * (i2c_clk period). + * + * Length of setup time calculated using (SDASU - 1) * (ic_clk_period) + * + */ + if (i2c_clk <= 100) { /* standard mode */ + setup_time = 300; + hold_time = 400; + } else { + setup_time = 450; + hold_time = 450; + } + + hold_time = ((hold_time * dev_clk_khz) / 1000000) - 1; + setup_time = ((setup_time * dev_clk_khz) / 1000000) + 1; + + if (setup_time > 255) + setup_time = 255; + + if (setup_time <= 0) + setup_time = 1; + + jz4780_i2c_writew(i2c, JZ4780_I2C_SDASU, setup_time); + + if (hold_time > 255) + hold_time = 255; + + if (hold_time >= 0) { + /*i2c hold time enable */ + 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 */ + 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; +} + +static int jz4780_i2c_cleanup(struct jz4780_i2c *i2c) +{ + int ret; + unsigned long flags; + unsigned short tmp; + + spin_lock_irqsave(&i2c->lock, flags); + + /* can send stop now if need */ + 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); + + /* then clear all interrupts */ + jz4780_i2c_readw(i2c, JZ4780_I2C_CTXABRT); + jz4780_i2c_readw(i2c, JZ4780_I2C_CINTR); + + /* then disable the controller */ + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_ENB_I2C; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + udelay(10); + tmp |= JZ4780_I2C_ENB_I2C; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = jz4780_i2c_disable(i2c); + if (ret) + dev_err(&i2c->adap.dev, + "unable to disable device during cleanup!\n"); + + if (unlikely(jz4780_i2c_readw(i2c, JZ4780_I2C_INTM) + & jz4780_i2c_readw(i2c, JZ4780_I2C_INTST))) + dev_err(&i2c->adap.dev, + "device has interrupts after a complete cleanup!\n"); + + return ret; +} + +static int jz4780_i2c_prepare(struct jz4780_i2c *i2c) +{ + jz4780_i2c_set_speed(i2c); + return jz4780_i2c_enable(i2c); +} + +static void jz4780_i2c_send_rcmd(struct jz4780_i2c *i2c, + int cmd_count, + int cmd_left) +{ + int 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); +} + +static void jz4780_i2c_trans_done(struct jz4780_i2c *i2c) +{ + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0); + complete(&i2c->trans_waitq); +} + +static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id) +{ + unsigned short tmp; + unsigned short intst; + unsigned short intmsk; + struct jz4780_i2c *i2c = dev_id; + + spin_lock(&i2c->lock); + intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM); + intst = jz4780_i2c_readw(i2c, JZ4780_I2C_INTST); + + intst &= intmsk; + + if (intst & JZ4780_I2C_INTST_TXABT) { + jz4780_i2c_trans_done(i2c); + goto done; + } + + if (intst & JZ4780_I2C_INTST_RXOF) { + dev_dbg(&i2c->adap.dev, "received fifo overflow!\n"); + jz4780_i2c_trans_done(i2c); + goto done; + } + + /* + * When reading, always drain RX FIFO before we send more Read + * Commands to avoid fifo overrun + */ + if (i2c->is_write == 0) { + int rd_left; + + while ((jz4780_i2c_readw(i2c, JZ4780_I2C_STA) + & JZ4780_I2C_STA_RFNE)) { + *(i2c->rbuf++) = jz4780_i2c_readw(i2c, JZ4780_I2C_DC) + & 0xff; + i2c->rd_data_xfered++; + if (i2c->rd_data_xfered == i2c->rd_total_len) { + jz4780_i2c_trans_done(i2c); + goto done; + } + } + + rd_left = i2c->rd_total_len - i2c->rd_data_xfered; + + 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 = (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, + i2c->cdata->fifosize + - i2c->cdata->tx_level - 1); + + if (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); + + } + + 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); + + 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; + unsigned short i2c_sta; + + i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + + while ((i2c_sta & JZ4780_I2C_STA_TFNF) && + (i2c->wt_len > 0)) { + i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + data = *i2c->wbuf; + data &= ~JZ4780_I2C_DC_READ; + 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) && (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_trans_done(i2c); + goto done; + } + } + } + +done: + spin_unlock(&i2c->lock); + return IRQ_HANDLED; +} + +static void jz4780_i2c_txabrt(struct jz4780_i2c *i2c, int src) +{ + 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, + unsigned char *buf, int len, int cnt, + int idx) +{ + int ret = 0; + unsigned long time_left; + int wait_time = JZ4780_I2C_TIMEOUT * (len + 5); + unsigned short tmp; + unsigned long flags; + + memset(buf, 0, len); + + spin_lock_irqsave(&i2c->lock, flags); + + i2c->stop_hold = 0; + i2c->is_write = 0; + i2c->rbuf = buf; + i2c->rd_total_len = len; + i2c->rd_data_xfered = 0; + i2c->rd_cmd_xfered = 0; + + if (len <= i2c->cdata->fifosize) + jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, len - 1); + else + jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, i2c->cdata->rx_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); + + 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); + + time_left = wait_for_completion_timeout(&i2c->trans_waitq, + msecs_to_jiffies(wait_time)); + + 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]); + dev_dbg(&i2c->adap.dev, "receive data count:%d %d\n", + i2c->cmd, i2c->data_buf[i2c->cmd]); + ret = -EIO; + } + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_TXABRT); + if (tmp) { + jz4780_i2c_txabrt(i2c, tmp); + ret = -EIO; + } + + return ret; +} + +static inline int jz4780_i2c_xfer_write(struct jz4780_i2c *i2c, + unsigned char *buf, int len, + int cnt, int idx) +{ + int ret = 0; + int wait_time = JZ4780_I2C_TIMEOUT * (len + 5); + unsigned long time_left; + unsigned short tmp; + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + + if (idx < (cnt - 1)) + i2c->stop_hold = 1; + else + i2c->stop_hold = 0; + + i2c->is_write = 1; + i2c->wbuf = buf; + i2c->wt_len = len; + + 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); + + 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); + + 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; + + 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) || + !(i2c_sta & JZ4780_I2C_STA_TFE); + if (!write_in_process) + break; + udelay(10); + } + } + + if (!time_left) { + dev_err(&i2c->adap.dev, "write wait timeout\n"); + ret = -EIO; + } + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_TXABRT); + if (tmp) { + jz4780_i2c_txabrt(i2c, tmp); + ret = -EIO; + } + + return ret; +} + +static int jz4780_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int count) +{ + int i = -EIO; + int ret = 0; + struct jz4780_i2c *i2c = adap->algo_data; + + ret = jz4780_i2c_prepare(i2c); + if (ret) { + dev_err(&i2c->adap.dev, "I2C prepare failed\n"); + goto out; + } + + if (msg->addr != jz4780_i2c_readw(i2c, JZ4780_I2C_TAR)) { + ret = jz4780_i2c_set_target(i2c, msg->addr); + if (ret) + goto out; + } + for (i = 0; i < count; i++, msg++) { + if (msg->flags & I2C_M_RD) + ret = jz4780_i2c_xfer_read(i2c, msg->buf, msg->len, + count, i); + else + ret = jz4780_i2c_xfer_write(i2c, msg->buf, msg->len, + count, i); + + if (ret) + goto out; + } + + ret = i; + +out: + jz4780_i2c_cleanup(i2c); + return ret; +} + +static u32 jz4780_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm jz4780_i2c_algorithm = { + .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,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); + +static int jz4780_i2c_probe(struct platform_device *pdev) +{ + int ret = 0; + unsigned int clk_freq = 0; + unsigned short tmp; + 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; + i2c->adap.retries = 5; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + sprintf(i2c->adap.name, "%s", pdev->name); + + init_completion(&i2c->trans_waitq); + spin_lock_init(&i2c->lock); + + 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_enabled(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) + return PTR_ERR(i2c->clk); + + i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000; + + 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"); + return ret; + } + + i2c->speed = clk_freq / 1000; + if (i2c->speed == 0) { + ret = -EINVAL; + dev_err(&pdev->dev, "clock-frequency minimum is 1000\n"); + return ret; + } + jz4780_i2c_set_speed(i2c); + + dev_info(&pdev->dev, "Bus frequency is %d KHz\n", i2c->speed); + + 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); + + 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) + return ret; + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) + return ret; + + return 0; +} + +static void jz4780_i2c_remove(struct platform_device *pdev) +{ + struct jz4780_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); +} + +static struct platform_driver jz4780_i2c_driver = { + .probe = jz4780_i2c_probe, + .remove = jz4780_i2c_remove, + .driver = { + .name = "jz4780-i2c", + .of_match_table = jz4780_i2c_of_matches, + }, +}; + +module_platform_driver(jz4780_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ztyan"); +MODULE_DESCRIPTION("i2c driver for JZ4780 SoCs"); diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c new file mode 100644 index 000000000..4e1d67541 --- /dev/null +++ b/drivers/i2c/busses/i2c-k1.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024-2025 Troy Mitchell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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_ILCR 0x10 /* Load Count Register */ +#define SPACEMIT_IWCR 0x14 /* Wait Count Register */ +#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 */ + +#define SPACEMIT_LCR_LV_STANDARD_MASK GENMASK(8, 0) +#define SPACEMIT_LCR_LV_FAST_MASK GENMASK(17, 9) + +/* SPACEMIT_IWCR register fields */ +#define SPACEMIT_WCR_COUNT GENMASK(4, 0) +#define SPACEMIT_WCR_HS_COUNT1 GENMASK(9, 5) +#define SPACEMIT_WCR_HS_COUNT2 GENMASK(14, 10) + +/* Required by I2C IP for correct SCL timing */ +#define SPACEMIT_IWCR_INIT_VALUE (FIELD_PREP(SPACEMIT_WCR_COUNT, 10) | \ + FIELD_PREP(SPACEMIT_WCR_HS_COUNT1, 1) | \ + FIELD_PREP(SPACEMIT_WCR_HS_COUNT2, 5)) + +/* i2c bus recover timeout: us */ +#define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000 + +#define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD) + +#define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9 + +#define SPACEMIT_WAIT_TIMEOUT 1000 /* ms */ +#define SPACEMIT_POLL_TIMEOUT 1000 /* us */ +#define SPACEMIT_POLL_INTERVAL 30 /* us */ + +enum spacemit_i2c_state { + SPACEMIT_STATE_IDLE, + SPACEMIT_STATE_START, + SPACEMIT_STATE_READ, + SPACEMIT_STATE_WRITE, +}; + +enum spacemit_i2c_mode { + SPACEMIT_MODE_STANDARD, + SPACEMIT_MODE_FAST +}; + +/* i2c-spacemit driver's main struct */ +struct spacemit_i2c_dev { + struct device *dev; + struct i2c_adapter adapt; + + struct clk_hw scl_clk_hw; + struct clk *scl_clk; + enum spacemit_i2c_mode mode; + + /* 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; + bool use_pio; + struct completion complete; + u32 status; +}; + +static void spacemit_i2c_scl_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +/* + * Calculate the ILCR divider value (lv) from the target SCL rate. + * + * Hardware timing formulas: + * - standard mode: SCL = FCLK / (2 * SLV + 8) + * - fast mode: SCL = FCLK / (2 * FLV + 10) + */ +static u32 spacemit_i2c_calc_lv(struct spacemit_i2c_dev *i2c, + unsigned long parent_rate, + unsigned long target_rate) +{ + u32 offset, denom; + + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; + denom = DIV_ROUND_CLOSEST(parent_rate, target_rate); + + return (denom <= offset) ? 0 : DIV_ROUND_CLOSEST(denom - offset, 2); +} + +static int spacemit_i2c_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lv, lcr, mask; + + lv = spacemit_i2c_calc_lv(i2c, parent_rate, rate); + + mask = (i2c->mode == SPACEMIT_MODE_STANDARD) ? + SPACEMIT_LCR_LV_STANDARD_MASK : SPACEMIT_LCR_LV_FAST_MASK; + + lcr = readl(i2c->base + SPACEMIT_ILCR); + lcr &= ~mask; + lcr |= field_prep(mask, lv); + writel(lcr, i2c->base + SPACEMIT_ILCR); + + return 0; +} + +static int spacemit_i2c_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lv, offset; + + lv = spacemit_i2c_calc_lv(i2c, req->best_parent_rate, req->rate); + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; + req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + offset); + + return 0; +} + +static unsigned long spacemit_i2c_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); + u32 lcr, lv = 0; + + lcr = readl(i2c->base + SPACEMIT_ILCR); + + if (i2c->mode == SPACEMIT_MODE_STANDARD) { + lv = FIELD_GET(SPACEMIT_LCR_LV_STANDARD_MASK, lcr); + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 8); + } + + lv = FIELD_GET(SPACEMIT_LCR_LV_FAST_MASK, lcr); + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 10); +} + +static const struct clk_ops spacemit_i2c_clk_ops = { + .set_rate = spacemit_i2c_clk_set_rate, + .determine_rate = spacemit_i2c_clk_determine_rate, + .recalc_rate = spacemit_i2c_clk_recalc_rate, +}; + +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 int spacemit_i2c_register_scl_clk(struct spacemit_i2c_dev *i2c) +{ + struct clk_init_data init = {}; + char name[64]; + int ret; + + ret = snprintf(name, sizeof(name), "%s_scl_clk", dev_name(i2c->dev)); + if (ret >= ARRAY_SIZE(name)) + dev_warn(i2c->dev, "scl clock name truncated"); + + init.name = name; + init.ops = &spacemit_i2c_clk_ops; + init.parent_data = (struct clk_parent_data[]) { + { .fw_name = "func" }, + }; + init.num_parents = 1; + + i2c->scl_clk_hw.init = &init; + + return devm_clk_hw_register(i2c->dev, &i2c->scl_clk_hw); +} + +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 inline void spacemit_i2c_delay(struct spacemit_i2c_dev *i2c, unsigned int us) +{ + if (i2c->use_pio) + udelay(us); + else + fsleep(us); +} + +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); + + spacemit_i2c_delay(i2c, 10); + + 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; + + if (i2c->use_pio) + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); + else + 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); + spacemit_i2c_delay(i2c, 90); + } +} + +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 = 0; + + if (!i2c->use_pio) { + /* + * Enable interrupt bits for all xfer mode: + * bus error, arbitration loss detected. + */ + 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(). + * We don't want a TX empty interrupt until we start + * a transfer in i2c_start(). + */ + val |= SPACEMIT_CR_DRFIE; + + /* + * Enable master stop interrupt bit. + * For transaction complete signal, we use master stop + * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. + */ + val |= SPACEMIT_CR_MSDIE; + } + + if (i2c->mode == SPACEMIT_MODE_FAST) + 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; + + 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); + + /* + * Initialize IWCR to the value specified by the I2C IP designer. + * The SCL frequency formulas (SCL = FCLK / (2*SLV+8) for standard + * mode, SCL = FCLK / (2*FLV+10) for fast mode) are only valid when + * IWCR contains this specific value. + */ + writel(SPACEMIT_IWCR_INIT_VALUE, i2c->base + SPACEMIT_IWCR); +} + +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; + + /* Enable the TX empty interrupt */ + if (!i2c->use_pio) + val |= SPACEMIT_CR_DTEIE; + + writel(val, i2c->base + SPACEMIT_ICR); +} + +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 inline void spacemit_i2c_complete(struct spacemit_i2c_dev *i2c) +{ + /* SPACEMIT_STATE_IDLE avoids triggering the next byte */ + i2c->state = SPACEMIT_STATE_IDLE; + + if (i2c->use_pio) + return; + + complete(&i2c->complete); +} + +static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) +{ + /* If there's no space in the IDBR, we're done */ + if (!(i2c->status & SPACEMIT_SR_ITE)) + return; + + /* 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_i2c_complete(i2c); +} + +static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) +{ + /* If there's nothing in the IDBR, we're done */ + if (!(i2c->status & SPACEMIT_SR_IRF)) + return; + + if (i2c->unprocessed) { + *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR); + i2c->unprocessed--; + return; + } + + /* 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_i2c_complete(i2c); +} + +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); + + spacemit_i2c_complete(i2c); +} + +static void spacemit_i2c_handle_state(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + if (i2c->status & SPACEMIT_SR_ERR) + goto err_out; + + 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 = readl(i2c->base + SPACEMIT_ICR); + val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | + SPACEMIT_CR_STOP | SPACEMIT_CR_START); + val |= SPACEMIT_CR_TB; + if (!i2c->use_pio) + val |= 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); +} + +/* + * In PIO mode, this function is used as a replacement for + * wait_for_completion_timeout(), whose return value indicates + * the remaining time. + * + * We do not have a meaningful remaining-time value here, so + * return a non-zero value on success to indicate "not timed out". + * Returning 1 ensures callers treating the return value as + * time_left will not incorrectly report a timeout. + */ +static int spacemit_i2c_wait_pio_xfer(struct spacemit_i2c_dev *i2c) +{ + u32 mask, msec = jiffies_to_msecs(i2c->adapt.timeout); + ktime_t timeout = ktime_add_ms(ktime_get(), msec); + int ret; + + mask = SPACEMIT_SR_IRF | SPACEMIT_SR_ITE; + + do { + i2c->status = readl(i2c->base + SPACEMIT_ISR); + + spacemit_i2c_clear_int_status(i2c, i2c->status); + + if (i2c->status & mask) + spacemit_i2c_handle_state(i2c); + else + udelay(SPACEMIT_POLL_INTERVAL); + } while (i2c->unprocessed && ktime_compare(ktime_get(), timeout) < 0); + + if (i2c->unprocessed) + return 0; + + if (i2c->read) + return 1; + + /* + * If this is the last byte to write of the current message, + * we have to wait here. Otherwise, control will proceed directly + * to start(), which would overwrite the current data. + */ + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + i2c->status, i2c->status & SPACEMIT_SR_ITE, + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); + if (ret) + return 0; + + /* + * For writes: in interrupt mode, an ITE (write-empty) interrupt is triggered + * after the last byte, and the MSD-related handling takes place there. + * In PIO mode, however, we need to explicitly call err_check() to emulate this + * step, otherwise the next transfer will fail. + */ + if (i2c->msg_idx == i2c->msg_num - 1) { + mask = SPACEMIT_SR_MSD | SPACEMIT_SR_ERR; + /* + * In some cases, MSD may not arrive immediately; + * wait here to handle that. + */ + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + i2c->status, i2c->status & mask, + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); + if (ret) + return 0; + + spacemit_i2c_err_check(i2c); + } + + return 1; +} + +static int spacemit_i2c_wait_xfer_complete(struct spacemit_i2c_dev *i2c) +{ + if (i2c->use_pio) + return spacemit_i2c_wait_pio_xfer(i2c); + + return wait_for_completion_timeout(&i2c->complete, + i2c->adapt.timeout); +} + +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 = spacemit_i2c_wait_xfer_complete(i2c); + + 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 irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) +{ + struct spacemit_i2c_dev *i2c = devid; + u32 status; + + status = readl(i2c->base + SPACEMIT_ISR); + if (!status) + return IRQ_NONE; + + i2c->status = status; + + spacemit_i2c_clear_int_status(i2c, status); + + spacemit_i2c_handle_state(i2c); + + return IRQ_HANDLED; +} + +static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) +{ + unsigned long timeout; + int idx = 0, cnt = 0; + + if (i2c->use_pio) { + i2c->adapt.timeout = msecs_to_jiffies(SPACEMIT_WAIT_TIMEOUT); + return; + } + + 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 inline int +spacemit_i2c_xfer_common(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num, bool use_pio) +{ + struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt); + int ret; + + i2c->use_pio = use_pio; + + 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 int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +{ + return spacemit_i2c_xfer_common(adapt, msgs, num, false); +} + +static int spacemit_i2c_pio_xfer_atomic(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +{ + return spacemit_i2c_xfer_common(adapt, msgs, num, true); +} + +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, + .xfer_atomic = spacemit_i2c_pio_xfer_atomic, + .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; + struct reset_control *rst; + int ret; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); + + /* For now, this driver doesn't support high-speed. */ + if (i2c->clock_freq > I2C_MAX_STANDARD_MODE_FREQ && + i2c->clock_freq <= I2C_MAX_FAST_MODE_FREQ) { + i2c->mode = SPACEMIT_MODE_FAST; + } else if (i2c->clock_freq && i2c->clock_freq <= I2C_MAX_STANDARD_MODE_FREQ) { + i2c->mode = SPACEMIT_MODE_STANDARD; + } else { + dev_info(dev, "clock-frequency not set or out of range, using fast mode\n"); + i2c->mode = SPACEMIT_MODE_FAST; + i2c->clock_freq = I2C_MAX_FAST_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 i2c->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"); + + ret = spacemit_i2c_register_scl_clk(i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to register scl clock\n"); + + i2c->scl_clk = devm_clk_hw_get_clk(dev, &i2c->scl_clk_hw, "scl"); + if (IS_ERR(i2c->scl_clk)) + return dev_err_probe(dev, PTR_ERR(i2c->scl_clk), + "failed to get scl clock\n"); + + clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock"); + + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), + "failed to acquire deasserted reset\n"); + + ret = clk_set_rate(i2c->scl_clk, i2c->clock_freq); + if (ret) + return dev_err_probe(dev, ret, "failed to set rate for SCL clock"); + + ret = clk_prepare_enable(i2c->scl_clk); + if (ret) + return dev_err_probe(dev, ret, "failed to prepare and enable clock"); + + ret = devm_add_action_or_reset(dev, spacemit_i2c_scl_clk_disable_unprepare, + i2c->scl_clk); + if (ret) + return ret; + + 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); + + ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, + IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); + if (ret) + return ret; + + 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 000000000..9420c8b34 --- /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 +#include +#include +#include +#include + +#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 "); +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 new file mode 100644 index 000000000..9b4c7cba6 --- /dev/null +++ b/drivers/i2c/busses/i2c-kempld.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C bus driver for Kontron COM modules + * + * Copyright (c) 2010-2013 Kontron Europe GmbH + * Author: Michael Brunner + * + * The driver is based on the i2c-ocores driver by Peter Korsgaard. + */ + +#include +#include +#include +#include +#include + +#define KEMPLD_I2C_PRELOW 0x0b +#define KEMPLD_I2C_PREHIGH 0x0c +#define KEMPLD_I2C_DATA 0x0e + +#define KEMPLD_I2C_CTRL 0x0d +#define I2C_CTRL_IEN 0x40 +#define I2C_CTRL_EN 0x80 + +#define KEMPLD_I2C_STAT 0x0f +#define I2C_STAT_IF 0x01 +#define I2C_STAT_TIP 0x02 +#define I2C_STAT_ARBLOST 0x20 +#define I2C_STAT_BUSY 0x40 +#define I2C_STAT_NACK 0x80 + +#define KEMPLD_I2C_CMD 0x0f +#define I2C_CMD_START 0x91 +#define I2C_CMD_STOP 0x41 +#define I2C_CMD_READ 0x21 +#define I2C_CMD_WRITE 0x11 +#define I2C_CMD_READ_ACK 0x21 +#define I2C_CMD_READ_NACK 0x29 +#define I2C_CMD_IACK 0x01 + +#define KEMPLD_I2C_FREQ_MAX 2700 /* 2.7 mHz */ +#define KEMPLD_I2C_FREQ_STD 100 /* 100 kHz */ + +enum { + STATE_DONE = 0, + STATE_INIT, + STATE_ADDR, + STATE_ADDR10, + STATE_START, + STATE_WRITE, + STATE_READ, + STATE_ERROR, +}; + +struct kempld_i2c_data { + struct device *dev; + struct kempld_device_data *pld; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; + bool was_active; +}; + +static unsigned int bus_frequency = KEMPLD_I2C_FREQ_STD; +module_param(bus_frequency, uint, 0); +MODULE_PARM_DESC(bus_frequency, "Set I2C bus frequency in kHz (default=" + __MODULE_STRING(KEMPLD_I2C_FREQ_STD)")"); + +static int i2c_bus = -1; +module_param(i2c_bus, int, 0); +MODULE_PARM_DESC(i2c_bus, "Set I2C bus number (default=-1 for dynamic assignment)"); + +static bool i2c_gpio_mux; +module_param(i2c_gpio_mux, bool, 0); +MODULE_PARM_DESC(i2c_gpio_mux, "Enable I2C port on GPIO out (default=false)"); + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static int kempld_i2c_process(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u8 stat = kempld_read8(pld, KEMPLD_I2C_STAT); + struct i2c_msg *msg = i2c->msg; + u8 addr; + + /* Ready? */ + if (stat & I2C_STAT_TIP) + return -EBUSY; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* Stop has been sent */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + if (i2c->state == STATE_ERROR) + return -EIO; + return 0; + } + + /* Error? */ + if (stat & I2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -EAGAIN; + } + + if (i2c->state == STATE_INIT) { + if (stat & I2C_STAT_BUSY) + return -EBUSY; + + i2c->state = STATE_ADDR; + } + + if (i2c->state == STATE_ADDR) { + /* 10 bit address? */ + if (i2c->msg->flags & I2C_M_TEN) { + addr = i2c_10bit_addr_hi_from_msg(msg); + i2c->state = STATE_ADDR10; + } else { + addr = i2c_8bit_addr_from_msg(i2c->msg); + i2c->state = STATE_START; + } + + kempld_write8(pld, KEMPLD_I2C_DATA, addr); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START); + + return 0; + } + + /* Second part of 10 bit addressing */ + if (i2c->state == STATE_ADDR10) { + 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); + + return 0; + } + + if (i2c->state == STATE_START || i2c->state == STATE_WRITE) { + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & I2C_STAT_NACK) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -ENXIO; + } + } else { + msg->buf[i2c->pos++] = kempld_read8(pld, KEMPLD_I2C_DATA); + } + + if (i2c->pos >= msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { + if (!(msg->flags & I2C_M_NOSTART)) { + i2c->state = STATE_ADDR; + return 0; + } else { + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } + } else { + i2c->state = STATE_DONE; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return 0; + } + } + + if (i2c->state == STATE_READ) { + kempld_write8(pld, KEMPLD_I2C_CMD, i2c->pos == (msg->len - 1) ? + I2C_CMD_READ_NACK : I2C_CMD_READ_ACK); + } else { + kempld_write8(pld, KEMPLD_I2C_DATA, msg->buf[i2c->pos++]); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); + } + + return 0; +} + +static int kempld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct kempld_i2c_data *i2c = i2c_get_adapdata(adap); + struct kempld_device_data *pld = i2c->pld; + unsigned long timeout = jiffies + HZ; + int ret; + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_INIT; + + /* Handle the transfer */ + while (time_before(jiffies, timeout)) { + kempld_get_mutex(pld); + ret = kempld_i2c_process(i2c); + kempld_release_mutex(pld); + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) + return (i2c->state == STATE_DONE) ? num : ret; + + if (ret == 0) + timeout = jiffies + HZ; + + usleep_range(5, 15); + } + + i2c->state = STATE_ERROR; + + return -ETIMEDOUT; +} + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static void kempld_i2c_device_init(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u16 prescale_corr; + long prescale; + u8 ctrl; + u8 stat; + u8 cfg; + + /* Make sure the device is disabled */ + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~(I2C_CTRL_EN | I2C_CTRL_IEN); + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + if (bus_frequency > KEMPLD_I2C_FREQ_MAX) + bus_frequency = KEMPLD_I2C_FREQ_MAX; + + if (pld->info.spec_major == 1) + prescale = pld->pld_clock / (bus_frequency * 5) - 1000; + else + prescale = pld->pld_clock / (bus_frequency * 4) - 3000; + + if (prescale < 0) + prescale = 0; + + /* Round to the best matching value */ + prescale_corr = prescale / 1000; + if (prescale % 1000 >= 500) + prescale_corr++; + + kempld_write8(pld, KEMPLD_I2C_PRELOW, prescale_corr & 0xff); + kempld_write8(pld, KEMPLD_I2C_PREHIGH, prescale_corr >> 8); + + /* Activate I2C bus output on GPIO pins */ + cfg = kempld_read8(pld, KEMPLD_CFG); + if (i2c_gpio_mux) + cfg |= KEMPLD_CFG_GPIO_I2C_MUX; + else + cfg &= ~KEMPLD_CFG_GPIO_I2C_MUX; + kempld_write8(pld, KEMPLD_CFG, cfg); + + /* Enable the device */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + ctrl |= I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + stat = kempld_read8(pld, KEMPLD_I2C_STAT); + if (stat & I2C_STAT_BUSY) + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); +} + +static u32 kempld_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm kempld_i2c_algorithm = { + .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_DEPRECATED, + .algo = &kempld_i2c_algorithm, +}; + +static int kempld_i2c_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); + struct kempld_i2c_data *i2c; + int ret; + u8 ctrl; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->pld = pld; + 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); + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + + if (ctrl & I2C_CTRL_EN) + i2c->was_active = true; + + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + /* Add I2C adapter to I2C tree */ + if (i2c_bus >= -1) + i2c->adap.nr = i2c_bus; + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret) + return ret; + + dev_info(i2c->dev, "I2C bus initialized at %dkHz\n", + bus_frequency); + + return 0; +} + +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; + u8 ctrl; + + kempld_get_mutex(pld); + /* + * Disable I2C logic if it was not activated before the + * driver loaded + */ + if (!i2c->was_active) { + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + } + kempld_release_mutex(pld); + + i2c_del_adapter(&i2c->adap); +} + +static int kempld_i2c_suspend(struct device *dev) +{ + struct kempld_i2c_data *i2c = dev_get_drvdata(dev); + struct kempld_device_data *pld = i2c->pld; + u8 ctrl; + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_i2c_resume(struct device *dev) +{ + struct kempld_i2c_data *i2c = dev_get_drvdata(dev); + struct kempld_device_data *pld = i2c->pld; + + kempld_get_mutex(pld); + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + return 0; +} + +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, +}; + +module_platform_driver(kempld_i2c_driver); + +MODULE_DESCRIPTION("KEM PLD I2C Driver"); +MODULE_AUTHOR("Michael Brunner "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_i2c"); diff --git a/drivers/i2c/busses/i2c-ljca.c b/drivers/i2c/busses/i2c-ljca.c new file mode 100644 index 000000000..93274f0c2 --- /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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +MODULE_AUTHOR("Zhifeng Wang "); +MODULE_AUTHOR("Lixu Zhang "); +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 new file mode 100644 index 000000000..ccd13c4fb --- /dev/null +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2011 NXP Semiconductors + * + * Code portions referenced from the i2x-pxa and i2c-pnx drivers + * + * Make SMBus byte and word transactions work on LPC178x/7x + * Copyright (c) 2012 + * Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com + * Anton Protopopov, Emcraft Systems, antonp@emcraft.com + * + * Copyright (C) 2015 Joachim Eastwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LPC24xx register offsets and bits */ +#define LPC24XX_I2CONSET 0x00 +#define LPC24XX_I2STAT 0x04 +#define LPC24XX_I2DAT 0x08 +#define LPC24XX_I2ADDR 0x0c +#define LPC24XX_I2SCLH 0x10 +#define LPC24XX_I2SCLL 0x14 +#define LPC24XX_I2CONCLR 0x18 + +#define LPC24XX_AA BIT(2) +#define LPC24XX_SI BIT(3) +#define LPC24XX_STO BIT(4) +#define LPC24XX_STA BIT(5) +#define LPC24XX_I2EN BIT(6) + +#define LPC24XX_STO_AA (LPC24XX_STO | LPC24XX_AA) +#define LPC24XX_CLEAR_ALL (LPC24XX_AA | LPC24XX_SI | LPC24XX_STO | \ + LPC24XX_STA | LPC24XX_I2EN) + +/* I2C SCL clock has different duty cycle depending on mode */ +#define I2C_STD_MODE_DUTY 46 +#define I2C_FAST_MODE_DUTY 36 +#define I2C_FAST_MODE_PLUS_DUTY 38 + +/* + * 26 possible I2C status codes, but codes applicable only + * to controller mode are listed here and used in this driver + */ +enum { + M_BUS_ERROR = 0x00, + M_START = 0x08, + M_REPSTART = 0x10, + MX_ADDR_W_ACK = 0x18, + MX_ADDR_W_NACK = 0x20, + MX_DATA_W_ACK = 0x28, + MX_DATA_W_NACK = 0x30, + M_DATA_ARB_LOST = 0x38, + MR_ADDR_R_ACK = 0x40, + MR_ADDR_R_NACK = 0x48, + MR_DATA_R_ACK = 0x50, + MR_DATA_R_NACK = 0x58, + M_I2C_IDLE = 0xf8, +}; + +struct lpc2k_i2c { + void __iomem *base; + struct clk *clk; + int irq; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int msg_idx; + int msg_status; + int is_last; +}; + +static void i2c_lpc2k_reset(struct lpc2k_i2c *i2c) +{ + /* Will force clear all statuses */ + writel(LPC24XX_CLEAR_ALL, i2c->base + LPC24XX_I2CONCLR); + writel(0, i2c->base + LPC24XX_I2ADDR); + writel(LPC24XX_I2EN, i2c->base + LPC24XX_I2CONSET); +} + +static int i2c_lpc2k_clear_arb(struct lpc2k_i2c *i2c) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + /* + * If the transfer needs to abort for some reason, we'll try to + * force a stop condition to clear any pending bus conditions + */ + writel(LPC24XX_STO, i2c->base + LPC24XX_I2CONSET); + + /* Wait for status change */ + while (readl(i2c->base + LPC24XX_I2STAT) != M_I2C_IDLE) { + if (time_after(jiffies, timeout)) { + /* Bus was not idle, try to reset adapter */ + i2c_lpc2k_reset(i2c); + return -EBUSY; + } + + cpu_relax(); + } + + return 0; +} + +static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c) +{ + unsigned char data; + u32 status; + + /* + * I2C in the LPC2xxx series is basically a state machine. + * Just run through the steps based on the current status. + */ + status = readl(i2c->base + LPC24XX_I2STAT); + + switch (status) { + case M_START: + case M_REPSTART: + /* Start bit was just sent out, send out addr and dir */ + data = i2c_8bit_addr_from_msg(i2c->msg); + + writel(data, i2c->base + LPC24XX_I2DAT); + writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR); + break; + + case MX_ADDR_W_ACK: + case MX_DATA_W_ACK: + /* + * Address or data was sent out with an ACK. If there is more + * data to send, send it now + */ + if (i2c->msg_idx < i2c->msg->len) { + writel(i2c->msg->buf[i2c->msg_idx], + i2c->base + LPC24XX_I2DAT); + } else if (i2c->is_last) { + /* Last message, send stop */ + writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET); + writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR); + i2c->msg_status = 0; + disable_irq_nosync(i2c->irq); + } else { + i2c->msg_status = 0; + disable_irq_nosync(i2c->irq); + } + + i2c->msg_idx++; + break; + + case MR_ADDR_R_ACK: + /* Receive first byte from target */ + if (i2c->msg->len == 1) { + /* Last byte, return NACK */ + writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR); + } else { + /* Not last byte, return ACK */ + writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET); + } + + writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR); + break; + + case MR_DATA_R_NACK: + /* + * The I2C shows NACK status on reads, so we need to accept + * the NACK as an ACK here. This should be ok, as the real + * BACK would of been caught on the address write. + */ + case MR_DATA_R_ACK: + /* Data was received */ + if (i2c->msg_idx < i2c->msg->len) { + i2c->msg->buf[i2c->msg_idx] = + readl(i2c->base + LPC24XX_I2DAT); + } + + /* If transfer is done, send STOP */ + if (i2c->msg_idx >= i2c->msg->len - 1 && i2c->is_last) { + writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET); + writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR); + i2c->msg_status = 0; + } + + /* Message is done */ + if (i2c->msg_idx >= i2c->msg->len - 1) { + i2c->msg_status = 0; + disable_irq_nosync(i2c->irq); + } + + /* + * 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) { + /* One byte left to receive - NACK */ + writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR); + } else { + /* More than one byte left to receive - ACK */ + writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET); + } + + writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR); + i2c->msg_idx++; + break; + + case MX_ADDR_W_NACK: + case MX_DATA_W_NACK: + case MR_ADDR_R_NACK: + /* NACK processing is done */ + writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET); + i2c->msg_status = -ENXIO; + disable_irq_nosync(i2c->irq); + break; + + case M_DATA_ARB_LOST: + /* Arbitration lost */ + i2c->msg_status = -EAGAIN; + + /* Release the I2C bus */ + writel(LPC24XX_STA | LPC24XX_STO, i2c->base + LPC24XX_I2CONCLR); + disable_irq_nosync(i2c->irq); + break; + + default: + /* Unexpected statuses */ + i2c->msg_status = -EIO; + disable_irq_nosync(i2c->irq); + break; + } + + /* Exit on failure or all bytes transferred */ + if (i2c->msg_status != -EBUSY) + wake_up(&i2c->wait); + + /* + * If `msg_status` is zero, then `lpc2k_process_msg()` + * is responsible for clearing the SI flag. + */ + if (i2c->msg_status != 0) + writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR); +} + +static int lpc2k_process_msg(struct lpc2k_i2c *i2c, int msgidx) +{ + /* A new transfer is kicked off by initiating a start condition */ + if (!msgidx) { + writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET); + } else { + /* + * A multi-message I2C transfer continues where the + * previous I2C transfer left off and uses the + * current condition of the I2C adapter. + */ + if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) { + WARN_ON(i2c->msg->len == 0); + + if (!(i2c->msg->flags & I2C_M_RD)) { + /* Start transmit of data */ + writel(i2c->msg->buf[0], + i2c->base + LPC24XX_I2DAT); + i2c->msg_idx++; + } + } else { + /* Start or repeated start */ + writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET); + } + + writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR); + } + + enable_irq(i2c->irq); + + /* Wait for transfer completion */ + if (wait_event_timeout(i2c->wait, i2c->msg_status != -EBUSY, + msecs_to_jiffies(1000)) == 0) { + disable_irq_nosync(i2c->irq); + + return -ETIMEDOUT; + } + + return i2c->msg_status; +} + +static int i2c_lpc2k_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int msg_num) +{ + struct lpc2k_i2c *i2c = i2c_get_adapdata(adap); + int ret, i; + u32 stat; + + /* Check for bus idle condition */ + stat = readl(i2c->base + LPC24XX_I2STAT); + if (stat != M_I2C_IDLE) { + /* Something is holding the bus, try to clear it */ + return i2c_lpc2k_clear_arb(i2c); + } + + /* Process a single message at a time */ + for (i = 0; i < msg_num; i++) { + /* Save message pointer and current message data index */ + i2c->msg = &msgs[i]; + i2c->msg_idx = 0; + i2c->msg_status = -EBUSY; + i2c->is_last = (i == (msg_num - 1)); + + ret = lpc2k_process_msg(i2c, i); + if (ret) + return ret; + } + + return msg_num; +} + +static irqreturn_t i2c_lpc2k_handler(int irq, void *dev_id) +{ + struct lpc2k_i2c *i2c = dev_id; + + if (readl(i2c->base + LPC24XX_I2CONSET) & LPC24XX_SI) { + i2c_lpc2k_pump_msg(i2c); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static u32 i2c_lpc2k_functionality(struct i2c_adapter *adap) +{ + /* Only emulated SMBus for now */ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm i2c_lpc2k_algorithm = { + .xfer = i2c_lpc2k_xfer, + .functionality = i2c_lpc2k_functionality, +}; + +static int i2c_lpc2k_probe(struct platform_device *pdev) +{ + struct lpc2k_i2c *i2c; + u32 bus_clk_rate; + u32 scl_high; + u32 clkrate; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + 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) + return i2c->irq; + + init_waitqueue_head(&i2c->wait); + + 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); + } + + 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"); + return ret; + } + + disable_irq_nosync(i2c->irq); + + /* Place controller is a known state */ + i2c_lpc2k_reset(i2c); + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret) + 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"); + return -EINVAL; + } + + /* Setup I2C dividers to generate clock with proper duty cycle */ + clkrate = clkrate / bus_clk_rate; + if (bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) + scl_high = (clkrate * I2C_STD_MODE_DUTY) / 100; + 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; + + writel(scl_high, i2c->base + LPC24XX_I2SCLH); + writel(clkrate - scl_high, i2c->base + LPC24XX_I2SCLL); + + platform_set_drvdata(pdev, i2c); + + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.owner = THIS_MODULE; + 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) + return ret; + + dev_info(&pdev->dev, "LPC2K I2C adapter\n"); + + return 0; +} + +static void i2c_lpc2k_remove(struct platform_device *dev) +{ + struct lpc2k_i2c *i2c = platform_get_drvdata(dev); + + i2c_del_adapter(&i2c->adap); +} + +static int i2c_lpc2k_suspend(struct device *dev) +{ + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); + + clk_disable(i2c->clk); + + return 0; +} + +static int i2c_lpc2k_resume(struct device *dev) +{ + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(i2c->clk); + if (ret) { + dev_err(dev, "failed to enable clock.\n"); + return ret; + } + i2c_lpc2k_reset(i2c); + + return 0; +} + +static const struct dev_pm_ops i2c_lpc2k_dev_pm_ops = { + .suspend_noirq = i2c_lpc2k_suspend, + .resume_noirq = i2c_lpc2k_resume, +}; + +static const struct of_device_id lpc2k_i2c_match[] = { + { .compatible = "nxp,lpc1788-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lpc2k_i2c_match); + +static struct platform_driver i2c_lpc2k_driver = { + .probe = i2c_lpc2k_probe, + .remove = i2c_lpc2k_remove, + .driver = { + .name = "lpc2k-i2c", + .pm = pm_sleep_ptr(&i2c_lpc2k_dev_pm_ops), + .of_match_table = lpc2k_i2c_match, + }, +}; +module_platform_driver(i2c_lpc2k_driver); + +MODULE_AUTHOR("Kevin Wells "); +MODULE_DESCRIPTION("I2C driver for LPC2xxx devices"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lpc2k-i2c"); diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c new file mode 100644 index 000000000..9df73557e --- /dev/null +++ b/drivers/i2c/busses/i2c-ls2x-v2.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K0300 I2C controller driver + * + * Copyright (C) 2025-2026 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Loongson-2 fast I2C offset registers */ +#define LOONGSON2_I2C_CR1 0x00 /* I2C control 1 register */ +#define LOONGSON2_I2C_CR2 0x04 /* I2C control 2 register */ +#define LOONGSON2_I2C_OAR 0x08 /* I2C slave address register */ +#define LOONGSON2_I2C_DR 0x10 /* I2C data register */ +#define LOONGSON2_I2C_SR1 0x14 /* I2C status 1 register */ +#define LOONGSON2_I2C_SR2 0x18 /* I2C status 2 register */ +#define LOONGSON2_I2C_CCR 0x1c /* I2C clock control register */ +#define LOONGSON2_I2C_TRISE 0x20 /* I2C trise register */ +#define LOONGSON2_I2C_FLTR 0x24 + +/* Bitfields of I2C control 1 register */ +#define LOONGSON2_I2C_CR1_PE BIT(0) /* Peripheral enable */ +#define LOONGSON2_I2C_CR1_START BIT(8) /* Start generation */ +#define LOONGSON2_I2C_CR1_STOP BIT(9) /* Stop generation */ +#define LOONGSON2_I2C_CR1_ACK BIT(10) /* Acknowledge enable */ +#define LOONGSON2_I2C_CR1_POS BIT(11) /* Acknowledge/PEC Position (for data reception) */ + +#define LOONGSON2_I2C_CR1_OP_MASK (LOONGSON2_I2C_CR1_START | LOONGSON2_I2C_CR1_STOP) + +/* Bitfields of I2C control 2 register */ +#define LOONGSON2_I2C_CR2_FREQ GENMASK(5, 0) /* APB Clock Frequency in MHz */ +#define LOONGSON2_I2C_CR2_ITERREN BIT(8) /* Fault-Class Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITEVTEN BIT(9) /* Event-Based Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITBUFEN BIT(10) /* Cache-Class Interrupt Enable */ + +#define LOONGSON2_I2C_CR2_INT_MASK \ + (LOONGSON2_I2C_CR2_ITBUFEN | LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN) + +/* Bitfields of I2C status 1 register */ +#define LOONGSON2_I2C_SR1_SB BIT(0) /* Start bit (Master mode) */ +#define LOONGSON2_I2C_SR1_ADDR BIT(1) /* Address sent (master mode) */ +#define LOONGSON2_I2C_SR1_BTF BIT(2) /* Byte transfer finished */ +#define LOONGSON2_I2C_SR1_RXNE BIT(6) /* Data register not empty (receivers) */ +#define LOONGSON2_I2C_SR1_TXE BIT(7) /* Data register empty (transmitters) */ +#define LOONGSON2_I2C_SR1_BERR BIT(8) /* Bus error */ +#define LOONGSON2_I2C_SR1_ARLO BIT(9) /* Arbitration lost (master mode) */ +#define LOONGSON2_I2C_SR1_AF BIT(10) /* Acknowledge failure */ + +#define LOONGSON2_I2C_SR1_ITEVTEN_MASK \ + (LOONGSON2_I2C_SR1_BTF | LOONGSON2_I2C_SR1_ADDR | LOONGSON2_I2C_SR1_SB) +#define LOONGSON2_I2C_SR1_ITBUFEN_MASK (LOONGSON2_I2C_SR1_TXE | LOONGSON2_I2C_SR1_RXNE) +#define LOONGSON2_I2C_SR1_ITERREN_MASK \ + (LOONGSON2_I2C_SR1_AF | LOONGSON2_I2C_SR1_ARLO | LOONGSON2_I2C_SR1_BERR) + +/* Bitfields of I2C status 2 register */ +#define LOONGSON2_I2C_SR2_MSL BIT(0) /* Master/slave */ +#define LOONGSON2_I2C_SR2_BUSY BIT(1) /* Bus busy */ +#define LOONGSON2_I2C_SR2_TRA BIT(2) /* Transmitter/receiver */ +#define LOONGSON2_I2C_SR2_GENCALL BIT(4) /* General call address (Slave mode) */ + +/* Bitfields of I2C clock control register */ +#define LOONGSON2_I2C_CCR_CCR GENMASK(11, 0) +#define LOONGSON2_I2C_CCR_DUTY BIT(14) +#define LOONGSON2_I2C_CCR_FS BIT(15) + +/* Bitfields of I2C trise register */ +#define LOONGSON2_I2C_TRISE_SCL GENMASK(5, 0) + +#define LOONGSON2_I2C_FREE_SLEEP_US 10 +#define LOONGSON2_I2C_FREE_TIMEOUT_US (2 * USEC_PER_MSEC) + +/** + * struct loongson2_i2c_msg - client specific data + * @buf: data buffer + * @count: number of bytes to be transferred + * @result: result of the transfer + * @addr: 8-bit slave addr, including r/w bit + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct loongson2_i2c_msg { + u8 *buf; + u32 count; + int result; + u8 addr; + bool stop; +}; + +/** + * struct loongson2_i2c_priv - private data of the controller + * @adapter: I2C adapter for this controller + * @complete: completion of I2C message + * @clk: hw i2c clock + * @regmap: regmap of the I2C device + * @parent_rate_MHz: I2C clock parent rate + * @msg: I2C transfer information + */ +struct loongson2_i2c_priv { + struct i2c_adapter adapter; + struct completion complete; + struct clk *clk; + struct regmap *regmap; + unsigned long parent_rate_MHz; + struct loongson2_i2c_msg msg; +}; + +static void loongson2_i2c_disable_irq(struct loongson2_i2c_priv *priv) +{ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_INT_MASK, 0); +} + +static void loongson2_i2c_read_msg(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + u32 rbuf; + + regmap_read(priv->regmap, LOONGSON2_I2C_DR, &rbuf); + *msg->buf++ = rbuf; + msg->count--; +} + +static void loongson2_i2c_write_msg(struct loongson2_i2c_priv *priv, u8 byte) +{ + regmap_write(priv->regmap, LOONGSON2_I2C_DR, byte); +} + +static void loongson2_i2c_terminate_xfer(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + loongson2_i2c_disable_irq(priv); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_write(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + if (msg->count) { + loongson2_i2c_write_msg(priv, *msg->buf++); + if (!--msg->count) + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITBUFEN, 0); + } else { + loongson2_i2c_terminate_xfer(priv); + } +} + +static void loongson2_i2c_handle_rx_addr(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 0: + loongson2_i2c_terminate_xfer(priv); + break; + case 1: + /* Enable NACK and reset POS (Acknowledge position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, + LOONGSON2_I2C_CR1_ACK | LOONGSON2_I2C_CR1_POS, 0); + /* Set STOP or RepSTART */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + break; + case 2: + /* Enable NACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + /* Set POS (NACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, + LOONGSON2_I2C_CR1_POS); + break; + + default: + /* Enable ACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, + LOONGSON2_I2C_CR1_ACK); + /* Reset POS (ACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, 0); + break; + } +} + +static void loongson2_i2c_isr_error(u32 status, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct loongson2_i2c_msg *msg = &priv->msg; + + /* Arbitration lost */ + if (status & LOONGSON2_I2C_SR1_ARLO) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_ARLO, 0); + msg->result = -EAGAIN; + goto out; + } + + /* + * Acknowledge failure: + * In master transmitter mode a Stop must be generated by software. + */ + if (status & LOONGSON2_I2C_SR1_AF) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_STOP, + LOONGSON2_I2C_CR1_STOP); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_AF, 0); + msg->result = -EIO; + goto out; + } + + /* Bus error */ + if (status & LOONGSON2_I2C_SR1_BERR) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_BERR, 0); + msg->result = -EIO; + goto out; + } + +out: + loongson2_i2c_disable_irq(priv); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_read(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 1: + loongson2_i2c_disable_irq(priv); + loongson2_i2c_read_msg(priv); + complete(&priv->complete); + break; + case 2: + case 3: + /* + * For 2-byte/3-byte reception and for N-byte reception with N > 3, we have to + * wait for byte transferred finished event before reading data. + * Just disable buffer interrupt in order to avoid another system preemption due + * to RX not empty event. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, 0); + break; + default: + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + loongson2_i2c_read_msg(priv); + break; + } +} + +static void loongson2_i2c_handle_rx_done(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 2: + /* + * The STOP/START bit has to be set before reading the last two bytes. + * After that, we could read the last two bytes. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + + for (unsigned int i = msg->count; i > 0; i--) + loongson2_i2c_read_msg(priv); + + loongson2_i2c_disable_irq(priv); + + complete(&priv->complete); + break; + case 3: + /* + * In order to generate the NACK after the last received data byte, enable NACK + * before reading N-2 data. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + loongson2_i2c_read_msg(priv); + break; + default: + loongson2_i2c_read_msg(priv); + break; + } +} + +static irqreturn_t loongson2_i2c_isr_event(int irq, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct device *dev = regmap_get_device(priv->regmap); + struct loongson2_i2c_msg *msg = &priv->msg; + u32 status, ien, event, cr2, possible_status; + + regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status); + if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) { + loongson2_i2c_isr_error(status, data); + return IRQ_HANDLED; + } + + regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2); + ien = cr2 & LOONGSON2_I2C_CR2_INT_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + possible_status = LOONGSON2_I2C_SR1_ITEVTEN_MASK; + if (ien & LOONGSON2_I2C_CR2_ITBUFEN) + possible_status |= LOONGSON2_I2C_SR1_ITBUFEN_MASK; + + event = status & possible_status; + if (!event) { + dev_dbg(dev, "spurious evt IRQ (status=0x%08x, ien=0x%08x)\n", status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & LOONGSON2_I2C_SR1_SB) + loongson2_i2c_write_msg(priv, msg->addr); + + /* I2C Address sent */ + if (event & LOONGSON2_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_addr(priv); + /* Clear ADDR flag */ + regmap_read(priv->regmap, LOONGSON2_I2C_SR2, &status); + /* Enable buffer interrupts for RX/TX not empty events */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, + LOONGSON2_I2C_CR2_ITBUFEN); + } + + /* TX empty */ + if ((event & LOONGSON2_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + loongson2_i2c_handle_write(priv); + + /* RX not empty */ + if ((event & LOONGSON2_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + loongson2_i2c_handle_read(priv); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception: a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & LOONGSON2_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_done(priv); + else + loongson2_i2c_handle_write(priv); + } + + return IRQ_HANDLED; +} + +static int loongson2_i2c_xfer_msg(struct loongson2_i2c_priv *priv, struct i2c_msg *msg, + bool is_stop) +{ + struct loongson2_i2c_msg *l_msg = &priv->msg; + unsigned long timeout; + + l_msg->addr = i2c_8bit_addr_from_msg(msg); + l_msg->buf = msg->buf; + l_msg->count = msg->len; + l_msg->stop = is_stop; + l_msg->result = 0; + + reinit_completion(&priv->complete); + + /* Enable events and errors interrupts */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN); + + timeout = wait_for_completion_timeout(&priv->complete, priv->adapter.timeout); + if (!timeout) + return -ETIMEDOUT; + + return l_msg->result; +} + +static int loongson2_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct loongson2_i2c_priv *priv = i2c_get_adapdata(i2c_adap); + struct device *dev = regmap_get_device(priv->regmap); + unsigned int status; + int ret; + + /* Wait I2C bus free */ + ret = regmap_read_poll_timeout(priv->regmap, LOONGSON2_I2C_SR2, status, + !(status & LOONGSON2_I2C_SR2_BUSY), + LOONGSON2_I2C_FREE_SLEEP_US, + LOONGSON2_I2C_FREE_TIMEOUT_US); + if (ret) { + dev_dbg(dev, "The I2C bus is busy now.\n"); + return ret; + } + + /* Start generation */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_START, + LOONGSON2_I2C_CR1_START); + + for (unsigned int i = 0; i < num; i++) { + ret = loongson2_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret < 0) + return ret; + } + + return num; +} + +static u32 loongson2_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm loongson2_i2c_algo = { + .xfer = loongson2_i2c_xfer, + .functionality = loongson2_i2c_func, +}; + +static int loongson2_i2c_adjust_bus_speed(struct loongson2_i2c_priv *priv) +{ + struct device *dev = regmap_get_device(priv->regmap); + struct i2c_timings i2c_t; + u32 val, freq_MHz, ccr; + + i2c_parse_fw_timings(dev, &i2c_t, true); + priv->parent_rate_MHz = clk_get_rate(priv->clk); + + if (i2c_t.bus_freq_hz == I2C_MAX_STANDARD_MODE_FREQ) { + /* Select Standard mode */ + ccr = 0; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 2); + } else if (i2c_t.bus_freq_hz == I2C_MAX_FAST_MODE_FREQ) { + /* Select Fast mode */ + ccr = LOONGSON2_I2C_CCR_FS; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 3); + } else { + return dev_err_probe(dev, -EINVAL, "Unsupported speed (%uHz)\n", i2c_t.bus_freq_hz); + } + + FIELD_MODIFY(LOONGSON2_I2C_CCR_CCR, &ccr, val); + regmap_write(priv->regmap, LOONGSON2_I2C_CCR, ccr); + + freq_MHz = DIV_ROUND_UP(priv->parent_rate_MHz, HZ_PER_MHZ); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_FREQ, + FIELD_GET(LOONGSON2_I2C_CR2_FREQ, freq_MHz)); + + regmap_update_bits(priv->regmap, LOONGSON2_I2C_TRISE, LOONGSON2_I2C_TRISE_SCL, + LOONGSON2_I2C_TRISE_SCL); + + /* Enable I2C */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_PE, + LOONGSON2_I2C_CR1_PE); + + return 0; +} + +static const struct regmap_config loongson2_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_I2C_TRISE, +}; + +static int loongson2_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_i2c_priv *priv; + struct i2c_adapter *adap; + void __iomem *base; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &loongson2_i2c_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), "Failed to init regmap.\n"); + + 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"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adap = &priv->adapter; + adap->retries = 5; + adap->nr = pdev->id; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->algo = &loongson2_i2c_algo; + adap->timeout = 2 * HZ; + device_set_node(&adap->dev, dev_fwnode(dev)); + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name); + init_completion(&priv->complete); + platform_set_drvdata(pdev, priv); + + ret = loongson2_i2c_adjust_bus_speed(priv); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, loongson2_i2c_isr_event, IRQF_SHARED, pdev->name, priv); + if (ret) + return ret; + + return devm_i2c_add_adapter(dev, adap); +} + +static const struct of_device_id loongson2_i2c_id_table[] = { + { .compatible = "loongson,ls2k0300-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson2_i2c_id_table); + +static struct platform_driver loongson2_i2c_driver = { + .driver = { + .name = "loongson2-i2c-v2", + .of_match_table = loongson2_i2c_id_table, + }, + .probe = loongson2_i2c_probe, +}; +module_platform_driver(loongson2_i2c_driver); + +MODULE_DESCRIPTION("Loongson-2K0300 I2C bus driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c new file mode 100644 index 000000000..af7bf61b0 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 ret; + + 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 000000000..bc0f1a0c8 --- /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 + * Kumaravel Thiagarajan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); +MODULE_AUTHOR("Kumaravel Thiagarajan "); +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 new file mode 100644 index 000000000..0d9032953 --- /dev/null +++ b/drivers/i2c/busses/i2c-meson.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C bus driver for Amlogic Meson SoCs + * + * Copyright (C) 2014 Beniamino Galvani + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Meson I2C register map */ +#define REG_CTRL 0x00 +#define REG_SLAVE_ADDR 0x04 +#define REG_TOK_LIST0 0x08 +#define REG_TOK_LIST1 0x0c +#define REG_TOK_WDATA0 0x10 +#define REG_TOK_WDATA1 0x14 +#define REG_TOK_RDATA0 0x18 +#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, 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, + TOKEN_START, + TOKEN_SLAVE_ADDR_WRITE, + TOKEN_SLAVE_ADDR_READ, + TOKEN_DATA, + TOKEN_DATA_LAST, + TOKEN_STOP, +}; + +enum { + STATE_IDLE, + STATE_READ, + STATE_WRITE, +}; + +/** + * struct meson_i2c - Meson I2C device private data + * + * @adap: I2C adapter instance + * @dev: Pointer to device structure + * @regs: Base address of the device memory mapped registers + * @clk: Pointer to clock structure + * @msg: Pointer to the current I2C message + * @state: Current state in the driver state machine + * @last: Flag set for the last message in the transfer + * @count: Number of bytes to be sent/received in current transfer + * @pos: Current position in the send/receive buffer + * @error: Flag set when an error is received + * @lock: To avoid race conditions between irq handler and xfer code + * @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 controller's platform data + */ +struct meson_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *regs; + struct clk *clk; + + struct i2c_msg *msg; + int state; + bool last; + int count; + int pos; + int error; + + spinlock_t lock; + struct completion done; + u32 tokens[2]; + int num_tokens; + + 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) +{ + u32 data; + + data = readl(i2c->regs + reg); + data &= ~mask; + data |= val & mask; + writel(data, i2c->regs + reg); +} + +static void meson_i2c_reset_tokens(struct meson_i2c *i2c) +{ + i2c->tokens[0] = 0; + i2c->tokens[1] = 0; + i2c->num_tokens = 0; +} + +static void meson_i2c_add_token(struct meson_i2c *i2c, int token) +{ + if (i2c->num_tokens < 8) + i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4); + else + i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4); + + i2c->num_tokens++; +} + +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); + div -= FILTER_DELAY; + div = DIV_ROUND_UP(div, 4); + + /* clock divider has 12 bits */ + if (div > GENMASK(11, 0)) { + dev_err(i2c->dev, "requested bus frequency too low\n"); + div = GENMASK(11, 0); + } + + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK, + FIELD_PREP(REG_CTRL_CLKDIV_MASK, div & GENMASK(9, 0))); + + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK, + 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); +} + +static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len) +{ + u32 rdata0, rdata1; + int i; + + rdata0 = readl(i2c->regs + REG_TOK_RDATA0); + rdata1 = readl(i2c->regs + REG_TOK_RDATA1); + + dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__, + rdata0, rdata1, len); + + for (i = 0; i < min(4, len); i++) + *buf++ = (rdata0 >> i * 8) & 0xff; + + for (i = 4; i < min(8, len); i++) + *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; +} + +static void meson_i2c_put_data(struct meson_i2c *i2c, char *buf, int len) +{ + u32 wdata0 = 0, wdata1 = 0; + int i; + + for (i = 0; i < min(4, len); i++) + wdata0 |= *buf++ << (i * 8); + + for (i = 4; i < min(8, len); i++) + wdata1 |= *buf++ << ((i - 4) * 8); + + writel(wdata0, i2c->regs + REG_TOK_WDATA0); + writel(wdata1, i2c->regs + REG_TOK_WDATA1); + + dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__, + wdata0, wdata1, len); +} + +static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) +{ + bool write = !(i2c->msg->flags & I2C_M_RD); + int i; + + i2c->count = min(i2c->msg->len - i2c->pos, 8); + + for (i = 0; i < i2c->count - 1; i++) + meson_i2c_add_token(i2c, TOKEN_DATA); + + if (i2c->count) { + if (write || i2c->pos + i2c->count < i2c->msg->len) + meson_i2c_add_token(i2c, TOKEN_DATA); + else + meson_i2c_add_token(i2c, TOKEN_DATA_LAST); + } + + if (write) + meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count); + + if (i2c->last && i2c->pos + i2c->count >= i2c->msg->len) + meson_i2c_add_token(i2c, TOKEN_STOP); + + writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0); + 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; + unsigned int ctrl; + + spin_lock(&i2c->lock); + + meson_i2c_reset_tokens(i2c); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); + ctrl = readl(i2c->regs + REG_CTRL); + + dev_dbg(i2c->dev, "irq: state %d, pos %d, count %d, ctrl %08x\n", + i2c->state, i2c->pos, i2c->count, ctrl); + + if (i2c->state == STATE_IDLE) { + spin_unlock(&i2c->lock); + return IRQ_NONE; + } + + meson_i2c_transfer_complete(i2c, ctrl); + + if (i2c->state == STATE_IDLE) { + complete(&i2c->done); + goto out; + } + + /* Restart the processing */ + meson_i2c_prepare_xfer(i2c); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START); +out: + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg) +{ + int token; + + token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : + TOKEN_SLAVE_ADDR_WRITE; + + + 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, bool atomic) +{ + unsigned long time_left, flags; + int ret = 0; + u32 ctrl; + + i2c->msg = msg; + i2c->last = last; + i2c->pos = 0; + i2c->count = 0; + i2c->error = 0; + + meson_i2c_reset_tokens(i2c); + + flags = (msg->flags & I2C_M_IGNORE_NAK) ? REG_CTRL_ACK_IGNORE : 0; + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_ACK_IGNORE, flags); + + if (!(msg->flags & I2C_M_NOSTART)) + meson_i2c_do_start(i2c, msg); + + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + meson_i2c_prepare_xfer(i2c); + + if (!atomic) + reinit_completion(&i2c->done); + + /* Start the transfer */ + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START); + + 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 + * handlers triggered by a transfer terminated after the + * timeout period + */ + 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 (ret) + i2c->state = STATE_IDLE; + + if (i2c->error) + ret = i2c->error; + + spin_unlock_irqrestore(&i2c->lock, flags); + + return ret; +} + +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; + + for (i = 0; i < num; i++) { + ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1, atomic); + if (ret) + break; + } + + 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 = { + .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 i2c_timings timings; + int irq, ret = 0; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct meson_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c_parse_fw_timings(&pdev->dev, &timings, true); + + i2c->dev = &pdev->dev; + platform_set_drvdata(pdev, i2c); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->done); + + i2c->data = (const struct meson_i2c_data *) + of_device_get_match_data(&pdev->dev); + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "can't get device clock\n"); + return PTR_ERR(i2c->clk); + } + + 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) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, meson_i2c_irq, 0, NULL, i2c); + if (ret < 0) { + dev_err(&pdev->dev, "can't request IRQ\n"); + return ret; + } + + ret = clk_prepare_enable(i2c->clk); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare clock\n"); + return ret; + } + + strscpy(i2c->adap.name, "Meson I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &meson_i2c_algorithm; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + + /* + * A transfer is triggered when START bit changes from 0 to 1. + * Ensure that the bit is set to 0 after probe + */ + 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_disable_unprepare(i2c->clk); + return ret; + } + + return 0; +} + +static void meson_i2c_remove(struct platform_device *pdev) +{ + struct meson_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + clk_disable_unprepare(i2c->clk); +} + +static const struct meson_i2c_data i2c_meson6_data = { + .set_clk_div = meson6_i2c_set_clk_div, +}; + +static const struct meson_i2c_data i2c_gxbb_data = { + .set_clk_div = meson_gxbb_axg_i2c_set_clk_div, +}; + +static const struct meson_i2c_data i2c_axg_data = { + .set_clk_div = meson_gxbb_axg_i2c_set_clk_div, +}; + +static const struct of_device_id meson_i2c_match[] = { + { .compatible = "amlogic,meson6-i2c", .data = &i2c_meson6_data }, + { .compatible = "amlogic,meson-gxbb-i2c", .data = &i2c_gxbb_data }, + { .compatible = "amlogic,meson-axg-i2c", .data = &i2c_axg_data }, + {}, +}; + +MODULE_DEVICE_TABLE(of, meson_i2c_match); + +static struct platform_driver meson_i2c_driver = { + .probe = meson_i2c_probe, + .remove = meson_i2c_remove, + .driver = { + .name = "meson-i2c", + .of_match_table = meson_i2c_match, + }, +}; + +module_platform_driver(meson_i2c_driver); + +MODULE_DESCRIPTION("Amlogic Meson I2C Bus driver"); +MODULE_AUTHOR("Beniamino Galvani "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-microchip-corei2c.c b/drivers/i2c/busses/i2c-microchip-corei2c.c new file mode 100644 index 000000000..5a429f173 --- /dev/null +++ b/drivers/i2c/busses/i2c-microchip-corei2c.c @@ -0,0 +1,648 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip CoreI2C I2C controller driver + * + * Copyright (c) 2018-2022 Microchip Corporation. All rights reserved. + * + * Author: Daire McNamara + * Author: Conor Dooley + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = I2C_MAX_STANDARD_MODE_FREQ; + } + + if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) + 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 ret; + + 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 "); +MODULE_AUTHOR("Conor Dooley "); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c new file mode 100644 index 000000000..24ccc4546 --- /dev/null +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -0,0 +1,2449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mellanox BlueField I2C bus driver + * + * Copyright (C) 2020 Mellanox Technologies, Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 * HZ_PER_MHZ) +/* 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 + +/* 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_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)) { + int ret = PTR_ERR(tmp_res->io); + + devm_kfree(dev, tmp_res); + return ret; + } + + 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, HZ_PER_GHZ); + /* + * 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 ret; + + 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 "); +MODULE_AUTHOR("Asmaa Mnebhi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c new file mode 100644 index 000000000..07d3cadbf --- /dev/null +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Mellanox i2c driver + * + * Copyright (C) 2016-2020 Mellanox Technologies + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* General defines */ +#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000 +#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld" +#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD) +#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 200 /* usec */ + +/* LPC I2C registers */ +#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0 +#define MLXCPLD_LPCI2C_CTRL_REG 0x1 +#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4 +#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5 +#define MLXCPLD_LPCI2C_CMD_REG 0x6 +#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7 +#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8 +#define MLXCPLD_LPCI2C_STATUS_REG 0x9 +#define MLXCPLD_LPCI2C_DATA_REG 0xa + +/* LPC I2C masks and parameters */ +#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1 +#define MLXCPLD_LPCI2C_TRANS_END 0x1 +#define MLXCPLD_LPCI2C_STATUS_NACK 0x10 +#define MLXCPLD_LPCI2C_NO_IND 0 +#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; + u8 data_len; + u8 msg_num; + struct i2c_msg *msg; +}; + +struct mlxcpld_i2c_priv { + struct i2c_adapter adap; + u32 base_addr; + struct mutex lock; + 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) +{ + int i; + + for (i = 0; i < len - len % 4; i += 4) + outl(*(u32 *)(data + i), addr + i); + for (; i < len; ++i) + outb(*(data + i), addr + i); +} + +static void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr) +{ + int i; + + for (i = 0; i < len - len % 4; i += 4) + *(u32 *)(data + i) = inl(addr + i); + for (; i < len; ++i) + *(data + i) = inb(addr + i); +} + +static void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs, + u8 *data, u8 datalen) +{ + u32 addr = priv->base_addr + offs; + + switch (datalen) { + case 1: + *(data) = inb(addr); + break; + case 2: + *((u16 *)data) = inw(addr); + break; + case 3: + *((u16 *)data) = inw(addr); + *(data + 2) = inb(addr + 2); + break; + case 4: + *((u32 *)data) = inl(addr); + break; + default: + mlxcpld_i2c_lpc_read_buf(data, datalen, addr); + break; + } +} + +static void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs, + u8 *data, u8 datalen) +{ + u32 addr = priv->base_addr + offs; + + switch (datalen) { + case 1: + outb(*(data), addr); + break; + case 2: + outw(*((u16 *)data), addr); + break; + case 3: + outw(*((u16 *)data), addr); + outb(*(data + 2), addr + 2); + break; + case 4: + outl(*((u32 *)data), addr); + break; + default: + mlxcpld_i2c_lpc_write_buf(data, datalen, addr); + break; + } +} + +/* + * Check validity of received i2c messages parameters. + * Returns 0 if OK, other - in case of invalid parameters. + */ +static int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv, + struct i2c_msg *msgs, int num) +{ + int i; + + if (!num) { + dev_err(priv->dev, "Incorrect 0 num of messages\n"); + return -EINVAL; + } + + if (unlikely(msgs[0].addr > 0x7f)) { + dev_err(priv->dev, "Invalid address 0x%03x\n", + msgs[0].addr); + return -EINVAL; + } + + for (i = 0; i < num; ++i) { + if (unlikely(!msgs[i].buf)) { + dev_err(priv->dev, "Invalid buf in msg[%d]\n", + i); + return -EINVAL; + } + if (unlikely(msgs[0].addr != msgs[i].addr)) { + dev_err(priv->dev, "Invalid addr in msg[%d]\n", + i); + return -EINVAL; + } + } + + return 0; +} + +/* + * Check if transfer is completed and status of operation. + * Returns 0 - transfer completed (both ACK or NACK), + * negative - transfer isn't finished. + */ +static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status) +{ + u8 val; + + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); + + if (val & MLXCPLD_LPCI2C_TRANS_END) { + if (val & MLXCPLD_LPCI2C_STATUS_NACK) + /* + * 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; + else + *status = MLXCPLD_LPCI2C_ACK_IND; + return 0; + } + *status = MLXCPLD_LPCI2C_NO_IND; + + return -EIO; +} + +static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv, + struct i2c_msg *msgs, int num, + u8 comm_len) +{ + priv->xfer.msg = msgs; + priv->xfer.msg_num = num; + + /* + * All upper layers currently are never use transfer with more than + * 2 messages. Actually, it's also not so relevant in Mellanox systems + * because of HW limitation. Max size of transfer is not more than 32 + * or 68 bytes in the current x86 LPCI2C bridge. + */ + priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD; + + if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) { + priv->xfer.addr_width = msgs[0].len; + priv->xfer.data_len = comm_len - priv->xfer.addr_width; + } else { + priv->xfer.addr_width = 0; + priv->xfer.data_len = comm_len; + } +} + +/* Reset CPLD LPCI2C block */ +static void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv) +{ + u8 val; + + mutex_lock(&priv->lock); + + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); + val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK; + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); + + mutex_unlock(&priv->lock); +} + +/* Make sure the CPLD is ready to start transmitting. */ +static int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv) +{ + u8 val; + + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); + + if (val & MLXCPLD_LPCI2C_TRANS_END) + return 0; + + return -EIO; +} + +static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv) +{ + int timeout = 0; + + do { + if (!mlxcpld_i2c_check_busy(priv)) + break; + 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) + return -ETIMEDOUT; + + return 0; +} + +/* + * 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). + */ +static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) +{ + int status, i, timeout = 0; + u8 datalen, val; + + do { + usleep_range(priv->polling_time / 2, priv->polling_time); + if (!mlxcpld_i2c_check_status(priv, &status)) + break; + timeout += priv->polling_time; + } while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO); + + switch (status) { + case MLXCPLD_LPCI2C_NO_IND: + return -ETIMEDOUT; + + case MLXCPLD_LPCI2C_ACK_IND: + if (priv->xfer.cmd != I2C_M_RD) + return (priv->xfer.addr_width + priv->xfer.data_len); + + if (priv->xfer.msg_num == 1) + i = 0; + else + i = 1; + + if (!priv->xfer.msg[i].buf) + return -EINVAL; + + /* + * Actual read data len will be always the same as + * requested len. 0xff (line pull-up) will be returned + * 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. + */ + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, + 1); + 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)) { + dev_err(priv->dev, "Incorrect smbus block read message len\n"); + return -EPROTO; + } + } else { + datalen = priv->xfer.data_len; + } + + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG, + priv->xfer.msg[i].buf, datalen); + + return datalen; + + case MLXCPLD_LPCI2C_NACK_IND: + return -ENXIO; + + default: + return -EINVAL; + } +} + +static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv) +{ + int i, len = 0; + u8 cmd, val; + + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, + &priv->xfer.data_len, 1); + + val = priv->xfer.addr_width; + /* Notify HW about SMBus block read transaction */ + if (priv->smbus_block && priv->xfer.msg_num >= 2 && + priv->xfer.msg[1].len == 1 && + (priv->xfer.msg[1].flags & I2C_M_RECV_LEN) && + (priv->xfer.msg[1].flags & I2C_M_RD)) + val |= MLXCPLD_I2C_SMBUS_BLK_BIT; + + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, 1); + + for (i = 0; i < priv->xfer.msg_num; i++) { + if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) { + /* Don't write to CPLD buffer in read transaction */ + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG + + len, priv->xfer.msg[i].buf, + priv->xfer.msg[i].len); + len += priv->xfer.msg[i].len; + } + } + + /* + * 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; + mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1); +} + +/* + * Generic lpc-i2c transfer. + * Returns the number of processed messages or error (<0). + */ +static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); + u8 comm_len = 0; + int i, err; + + err = mlxcpld_i2c_check_msg_params(priv, msgs, num); + if (err) { + dev_err(priv->dev, "Incorrect message\n"); + return err; + } + + for (i = 0; i < num; ++i) + comm_len += msgs[i].len; + + /* Check bus state */ + if (mlxcpld_i2c_wait_for_free(priv)) { + dev_err(priv->dev, "LPCI2C bridge is busy\n"); + + /* + * Usually it means something serious has happened. + * We can not have unfinished previous transfer + * so it doesn't make any sense to try to stop it. + * Probably we were not able to recover from the + * previous error. + * The only reasonable thing - is soft reset. + */ + mlxcpld_i2c_reset(priv); + if (mlxcpld_i2c_check_busy(priv)) { + dev_err(priv->dev, "LPCI2C bridge is busy after reset\n"); + return -EIO; + } + } + + mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len); + + mutex_lock(&priv->lock); + + /* Do real transfer. Can't fail */ + mlxcpld_i2c_xfer_msg(priv); + + /* Wait for transaction complete */ + err = mlxcpld_i2c_wait_for_tc(priv); + + mutex_unlock(&priv->lock); + + return err < 0 ? err : num; +} + +static u32 mlxcpld_i2c_func(struct i2c_adapter *adap) +{ + struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); + + if (priv->smbus_block) + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA; + else + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm mlxcpld_i2c_algo = { + .xfer = mlxcpld_i2c_xfer, + .functionality = mlxcpld_i2c_func +}; + +static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, + .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, + .max_comb_1st_msg_len = 4, +}; + +static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_read_len = MLXCPLD_I2C_DATA_REG_SZ * 2 - MLXCPLD_I2C_MAX_ADDR_LEN, + .max_write_len = MLXCPLD_I2C_DATA_REG_SZ * 2, + .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, + .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, ®val); + 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; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + 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); + /* Read capability register */ + mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CPBLTY_REG, &val, 1); + /* 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; + if (pdev->id >= -1) + mlxcpld_i2c_adapter.nr = pdev->id; + priv->adap = mlxcpld_i2c_adapter; + priv->adap.dev.parent = &pdev->dev; + i2c_set_adapdata(&priv->adap, priv); + + err = i2c_add_numbered_adapter(&priv->adap); + if (err) + 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 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); +} + +static struct platform_driver mlxcpld_i2c_driver = { + .probe = mlxcpld_i2c_probe, + .remove = mlxcpld_i2c_remove, + .driver = { + .name = MLXCPLD_I2C_DEVICE_NAME, + }, +}; + +module_platform_driver(mlxcpld_i2c_driver); + +MODULE_AUTHOR("Michael Shych "); +MODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:i2c-mlxcpld"); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c new file mode 100644 index 000000000..a21fa45bd --- /dev/null +++ b/drivers/i2c/busses/i2c-mpc.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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). + * + * Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk + * Copyright (C) 2021 Allied Telesis Labs + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MPC_I2C_CLOCK_LEGACY 0 +#define MPC_I2C_CLOCK_PRESERVE (~0U) + +#define MPC_I2C_FDR 0x04 +#define MPC_I2C_CR 0x08 +#define MPC_I2C_SR 0x0c +#define MPC_I2C_DR 0x10 +#define MPC_I2C_DFSRR 0x14 + +#define CCR_MEN 0x80 +#define CCR_MIEN 0x40 +#define CCR_MSTA 0x20 +#define CCR_MTX 0x10 +#define CCR_TXAK 0x08 +#define CCR_RSTA 0x04 +#define CCR_RSVD 0x02 + +#define CSR_MCF 0x80 +#define CSR_MAAS 0x40 +#define CSR_MBB 0x20 +#define CSR_MAL 0x10 +#define CSR_SRW 0x04 +#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 waitq; + spinlock_t lock; + struct i2c_adapter adap; + int irq; + u32 real_clk; + u8 fdr, dfsrr; + 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 { + u16 divider; + u16 fdr; /* including dfsrr */ +}; + +struct mpc_i2c_data { + void (*setup)(struct device_node *node, struct mpc_i2c *i2c, u32 clock); +}; + +static inline void writeccr(struct mpc_i2c *i2c, u32 x) +{ + writeb(x, i2c->base + MPC_I2C_CR); +} + +/* 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, each with a START then ending with STOP, so it's all OK. + */ +static void mpc_i2c_fixup(struct mpc_i2c *i2c) +{ + int k; + unsigned long flags; + + for (k = 9; k; k--) { + writeccr(i2c, 0); + 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); + 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_mpc_wait_sr(struct mpc_i2c *i2c, int mask) +{ + void __iomem *addr = i2c->base + MPC_I2C_SR; + u8 val; + + return readb_poll_timeout(addr, val, val & mask, 0, 100); +} + +/* + * 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; + + 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; + } + + val = readb(i2c->base + MPC_I2C_SR); + + 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); + } +} + +#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x) +static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = { + {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23}, + {28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02}, + {36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28}, + {52, 0x63}, {56, 0x29}, {60, 0x41}, {64, 0x2a}, + {68, 0x07}, {72, 0x2b}, {80, 0x2c}, {88, 0x09}, + {96, 0x2d}, {104, 0x0a}, {112, 0x2e}, {120, 0x81}, + {128, 0x2f}, {136, 0x47}, {144, 0x0c}, {160, 0x30}, + {176, 0x49}, {192, 0x31}, {208, 0x4a}, {224, 0x32}, + {240, 0x0f}, {256, 0x33}, {272, 0x87}, {288, 0x10}, + {320, 0x34}, {352, 0x89}, {384, 0x35}, {416, 0x8a}, + {448, 0x36}, {480, 0x13}, {512, 0x37}, {576, 0x14}, + {640, 0x38}, {768, 0x39}, {896, 0x3a}, {960, 0x17}, + {1024, 0x3b}, {1152, 0x18}, {1280, 0x3c}, {1536, 0x3d}, + {1792, 0x3e}, {1920, 0x1b}, {2048, 0x3f}, {2304, 0x1c}, + {2560, 0x1d}, {3072, 0x1e}, {3584, 0x7e}, {3840, 0x1f}, + {4096, 0x7f}, {4608, 0x5c}, {5120, 0x5d}, {6144, 0x5e}, + {7168, 0xbe}, {7680, 0x5f}, {8192, 0xbf}, {9216, 0x9c}, + {10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f} +}; + +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; + int i; + + if (clock == MPC_I2C_CLOCK_LEGACY) { + /* see below - default fdr = 0x3f -> div = 2048 */ + *real_clk = mpc5xxx_fwnode_get_bus_frequency(fwnode) / 2048; + return -EINVAL; + } + + /* Determine divider value */ + divider = mpc5xxx_fwnode_get_bus_frequency(fwnode) / clock; + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. + */ + for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_52xx); i++) { + div = &mpc_i2c_dividers_52xx[i]; + /* Old MPC5200 rev A CPUs do not support the high bits */ + if (div->fdr & 0xc0 && pvr == 0x80822011) + continue; + if (div->divider >= divider) + break; + } + + *real_clk = mpc5xxx_fwnode_get_bus_frequency(fwnode) / div->divider; + return (int)div->fdr; +} + +static void mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ + int ret, fdr; + + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using fdr %d\n", + readb(i2c->base + MPC_I2C_FDR)); + return; + } + + ret = mpc_i2c_get_fdr_52xx(node, clock, &i2c->real_clk); + fdr = (ret >= 0) ? ret : 0x3f; /* backward compatibility */ + + writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR); + + if (ret >= 0) + dev_info(i2c->dev, "clock %u Hz (fdr=%d)\n", i2c->real_clk, + fdr); +} +#else /* !(CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x) */ +static void mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ +} +#endif /* CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x */ + +#ifdef CONFIG_PPC_MPC512x +static void mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ + void __iomem *ctrl; + u32 idx; + + /* Enable I2C interrupts for mpc5121 */ + 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 */ + of_property_read_reg(node, 0, &addr, NULL); + idx = (addr & 0xff) / 0x20; + setbits32(ctrl, 1 << (24 + idx * 2)); + iounmap(ctrl); + } + } + + /* The clock setup for the 52xx works also fine for the 512x */ + mpc_i2c_setup_52xx(node, i2c, clock); +} +#else /* CONFIG_PPC_MPC512x */ +static void mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ +} +#endif /* CONFIG_PPC_MPC512x */ + +#ifdef CONFIG_FSL_SOC +static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = { + {160, 0x0120}, {192, 0x0121}, {224, 0x0122}, {256, 0x0123}, + {288, 0x0100}, {320, 0x0101}, {352, 0x0601}, {384, 0x0102}, + {416, 0x0602}, {448, 0x0126}, {480, 0x0103}, {512, 0x0127}, + {544, 0x0b03}, {576, 0x0104}, {608, 0x1603}, {640, 0x0105}, + {672, 0x2003}, {704, 0x0b05}, {736, 0x2b03}, {768, 0x0106}, + {800, 0x3603}, {832, 0x0b06}, {896, 0x012a}, {960, 0x0107}, + {1024, 0x012b}, {1088, 0x1607}, {1152, 0x0108}, {1216, 0x2b07}, + {1280, 0x0109}, {1408, 0x1609}, {1536, 0x010a}, {1664, 0x160a}, + {1792, 0x012e}, {1920, 0x010b}, {2048, 0x012f}, {2176, 0x2b0b}, + {2304, 0x010c}, {2560, 0x010d}, {2816, 0x2b0d}, {3072, 0x010e}, + {3328, 0x2b0e}, {3584, 0x0132}, {3840, 0x010f}, {4096, 0x0133}, + {4608, 0x0110}, {5120, 0x0111}, {6144, 0x0112}, {7168, 0x0136}, + {7680, 0x0113}, {8192, 0x0137}, {9216, 0x0114}, {10240, 0x0115}, + {12288, 0x0116}, {14336, 0x013a}, {15360, 0x0117}, {16384, 0x013b}, + {18432, 0x0118}, {20480, 0x0119}, {24576, 0x011a}, {28672, 0x013e}, + {30720, 0x011b}, {32768, 0x013f}, {36864, 0x011c}, {40960, 0x011d}, + {49152, 0x011e}, {61440, 0x011f} +}; + +static u32 mpc_i2c_get_sec_cfg_8xxx(void) +{ + u32 __iomem *reg; + u32 val = 0; + + 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) { + /* + * Map and check POR Device Status Register 2 + * (PORDEVSR2) at 0xE0014. Note than while MPC8533 + * and MPC8544 indicate SEC frequency ratio + * configuration as bit 26 in PORDEVSR2, other MPC8xxx + * parts may store it differently or may not have it + * at all. + */ + reg = ioremap(get_immrbase() + *prop + 0x14, 0x4); + if (!reg) + printk(KERN_ERR + "Error: couldn't map PORDEVSR2\n"); + else + val = in_be32(reg) & 0x00000020; /* sec-cfg */ + iounmap(reg); + } + } + + return val; +} + +static u32 mpc_i2c_get_prescaler_8xxx(void) +{ + /* + * According to the AN2919 all MPC824x have prescaler 1, while MPC83xx + * may have prescaler 1, 2, or 3, depending on the power-on + * configuration. + */ + u32 prescaler = 1; + + /* mpc85xx */ + if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2) + || pvr_version_is(PVR_VER_E500MC) + || pvr_version_is(PVR_VER_E5500) + || pvr_version_is(PVR_VER_E6500)) { + unsigned int svr = mfspr(SPRN_SVR); + + if ((SVR_SOC_VER(svr) == SVR_8540) + || (SVR_SOC_VER(svr) == SVR_8541) + || (SVR_SOC_VER(svr) == SVR_8560) + || (SVR_SOC_VER(svr) == SVR_8555) + || (SVR_SOC_VER(svr) == SVR_8610)) + /* the above 85xx SoCs have prescaler 1 */ + prescaler = 1; + else if ((SVR_SOC_VER(svr) == SVR_8533) + || (SVR_SOC_VER(svr) == SVR_8544)) + /* the above 85xx SoCs have prescaler 3 or 2 */ + prescaler = mpc_i2c_get_sec_cfg_8xxx() ? 3 : 2; + else + /* all the other 85xx have prescaler 2 */ + prescaler = 2; + } + + return prescaler; +} + +static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, + u32 *real_clk) +{ + const struct mpc_i2c_divider *div = NULL; + u32 prescaler = mpc_i2c_get_prescaler_8xxx(); + u32 divider; + int i; + + if (clock == MPC_I2C_CLOCK_LEGACY) { + /* see below - default fdr = 0x1031 -> div = 16 * 3072 */ + *real_clk = fsl_get_sys_freq() / prescaler / (16 * 3072); + return -EINVAL; + } + + divider = fsl_get_sys_freq() / clock / prescaler; + + pr_debug("I2C: src_clock=%d clock=%d divider=%d\n", + fsl_get_sys_freq(), clock, divider); + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. + */ + for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_8xxx); i++) { + div = &mpc_i2c_dividers_8xxx[i]; + if (div->divider >= divider) + break; + } + + *real_clk = fsl_get_sys_freq() / prescaler / div->divider; + return (int)div->fdr; +} + +static void mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ + int ret, fdr; + + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using dfsrr %d, fdr %d\n", + readb(i2c->base + MPC_I2C_DFSRR), + readb(i2c->base + MPC_I2C_FDR)); + return; + } + + ret = mpc_i2c_get_fdr_8xxx(node, clock, &i2c->real_clk); + fdr = (ret >= 0) ? ret : 0x1031; /* backward compatibility */ + + writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR); + writeb((fdr >> 8) & 0xff, i2c->base + MPC_I2C_DFSRR); + + if (ret >= 0) + dev_info(i2c->dev, "clock %d Hz (dfsrr=%d fdr=%d)\n", + i2c->real_clk, fdr >> 8, fdr & 0xff); +} + +#else /* !CONFIG_FSL_SOC */ +static void mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock) +{ +} +#endif /* CONFIG_FSL_SOC */ + +static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc) +{ + i2c->rc = rc; + i2c->block = 0; + i2c->cntl_bits = CCR_MEN; + writeccr(i2c, i2c->cntl_bits); + wake_up(&i2c->waitq); +} + +static void mpc_i2c_do_action(struct mpc_i2c *i2c) +{ + struct i2c_msg *msg = NULL; + int dir = 0; + int recv_len = 0; + u8 byte; + + dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]); + + i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK); + + 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; + } + + 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; + + writeccr(i2c, i2c->cntl_bits); + } + + byte = readb(i2c->base + MPC_I2C_DR); + + 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); + } + } + + dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte); + msg->buf[i2c->byte_posn++] = byte; + break; + + 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; + + case MPC_I2C_ACTION_STOP: + mpc_i2c_finish(i2c, 0); + break; + + default: + WARN(1, "Unexpected action %d\n", i2c->action); + break; + } + + if (msg && msg->len == i2c->byte_posn) { + i2c->curr_msg++; + i2c->byte_posn = 0; + + if (i2c->curr_msg == i2c->num_msgs) { + i2c->action = MPC_I2C_ACTION_STOP; + /* + * We don't get another interrupt on read so + * finish the transfer now + */ + if (dir) + mpc_i2c_finish(i2c, 0); + } else { + i2c->action = MPC_I2C_ACTION_RESTART; + } + } +} + +static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status) +{ + spin_lock(&i2c->lock); + + if (!(status & CSR_MCF)) { + dev_dbg(i2c->dev, "unfinished\n"); + mpc_i2c_finish(i2c, -EIO); + goto out; + } + + if (status & CSR_MAL) { + dev_dbg(i2c->dev, "arbitration lost\n"); + mpc_i2c_finish(i2c, -EAGAIN); + goto out; + } + + 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; + + 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; + } + 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) { + if (time_after(jiffies, orig_jiffies + HZ)) { + u8 status = readb(i2c->base + MPC_I2C_SR); + + dev_dbg(i2c->dev, "timeout\n"); + if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { + writeb(status & ~CSR_MAL, + i2c->base + MPC_I2C_SR); + i2c_recover_bus(&i2c->adap); + } + return -EIO; + } + cond_resched(); + } + + 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) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL + | 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 = { + .xfer = mpc_xfer, + .functionality = mpc_functionality, +}; + +static struct i2c_adapter mpc_ops = { + .owner = THIS_MODULE, + .algo = &mpc_algo, +}; + +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 mpc_i2c_data *data; + struct mpc_i2c *i2c; + struct clk *clk; + int result; + u32 clock; + + 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->waitq); + spin_lock_init(&i2c->lock); + + i2c->base = devm_platform_ioremap_resource(op, 0); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + 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_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 { + result = of_property_read_u32(op->dev.of_node, + "clock-frequency", &clock); + if (result) + clock = MPC_I2C_CLOCK_LEGACY; + } + + data = device_get_match_data(&op->dev); + if (data) { + data->setup(op->dev.of_node, i2c, clock); + } else { + /* Backwards compatibility */ + if (of_property_read_bool(op->dev.of_node, "dfsrr")) + mpc_i2c_setup_8xxx(op->dev.of_node, i2c, clock); + } + + /* 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 = 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); + + if (of_property_read_bool(op->dev.of_node, "fsl,i2c-erratum-a004447")) + i2c->has_errata_A004447 = true; + + i2c->adap = mpc_ops; + scnprintf(i2c->adap.name, sizeof(i2c->adap.name), + "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_numbered_adapter(&i2c->adap); + if (result) + return result; + + return 0; +}; + +static void fsl_i2c_remove(struct platform_device *op) +{ + struct mpc_i2c *i2c = platform_get_drvdata(op); + + i2c_del_adapter(&i2c->adap); +}; + +static int __maybe_unused mpc_i2c_suspend(struct device *dev) +{ + struct mpc_i2c *i2c = dev_get_drvdata(dev); + + i2c->fdr = readb(i2c->base + MPC_I2C_FDR); + i2c->dfsrr = readb(i2c->base + MPC_I2C_DFSRR); + + return 0; +} + +static int __maybe_unused mpc_i2c_resume(struct device *dev) +{ + struct mpc_i2c *i2c = dev_get_drvdata(dev); + + writeb(i2c->fdr, i2c->base + MPC_I2C_FDR); + writeb(i2c->dfsrr, i2c->base + MPC_I2C_DFSRR); + + return 0; +} +static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume); + +static const struct mpc_i2c_data mpc_i2c_data_512x = { + .setup = mpc_i2c_setup_512x, +}; + +static const struct mpc_i2c_data mpc_i2c_data_52xx = { + .setup = mpc_i2c_setup_52xx, +}; + +static const struct mpc_i2c_data mpc_i2c_data_8313 = { + .setup = mpc_i2c_setup_8xxx, +}; + +static const struct mpc_i2c_data mpc_i2c_data_8543 = { + .setup = mpc_i2c_setup_8xxx, +}; + +static const struct mpc_i2c_data mpc_i2c_data_8544 = { + .setup = mpc_i2c_setup_8xxx, +}; + +static const struct of_device_id mpc_i2c_of_match[] = { + {.compatible = "mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200b-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5121-i2c", .data = &mpc_i2c_data_512x, }, + {.compatible = "fsl,mpc8313-i2c", .data = &mpc_i2c_data_8313, }, + {.compatible = "fsl,mpc8543-i2c", .data = &mpc_i2c_data_8543, }, + {.compatible = "fsl,mpc8544-i2c", .data = &mpc_i2c_data_8544, }, + /* Backward compatibility */ + {.compatible = "fsl-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc_i2c_of_match); + +/* Structure for a device driver */ +static struct platform_driver mpc_i2c_driver = { + .probe = fsl_i2c_probe, + .remove = fsl_i2c_remove, + .driver = { + .name = "mpc-i2c", + .of_match_table = mpc_i2c_of_match, + .pm = &mpc_i2c_pm_ops, + }, +}; + +module_platform_driver(mpc_i2c_driver); + +MODULE_AUTHOR("Adrian Cox "); +MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and " + "MPC824x/83xx/85xx/86xx/512x/52xx processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c new file mode 100644 index 000000000..307925fb7 --- /dev/null +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -0,0 +1,1573 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Xudong Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +#define I2C_TRANSAC_START (1 << 0) +#define I2C_RS_MUL_CNFG (1 << 15) +#define I2C_RS_MUL_TRIG (1 << 14) +#define I2C_DCM_DISABLE 0x0000 +#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 +#define I2C_FS_START_CON 0x1800 +#define I2C_TIME_CLR_VALUE 0x0000 +#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_HANDSHAKE_RST 0x0004 + +#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) +#define I2C_CONTROL_CLK_EXT_EN (0x1 << 3) +#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, + OFFSET_EN = 0x08, + OFFSET_RST = 0x0c, + OFFSET_CON = 0x18, + OFFSET_TX_MEM_ADDR = 0x1c, + OFFSET_RX_MEM_ADDR = 0x20, + OFFSET_TX_LEN = 0x24, + OFFSET_RX_LEN = 0x28, + OFFSET_TX_4G_MODE = 0x54, + OFFSET_RX_4G_MODE = 0x58, +}; + +enum i2c_trans_st_rs { + I2C_TRANS_STOP = 0, + I2C_TRANS_REPEATED_START, +}; + +enum mtk_trans_op { + I2C_MASTER_WR = 1, + I2C_MASTER_RD, + I2C_MASTER_WRRD, +}; + +enum I2C_REGS_OFFSET { + 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 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_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 */ + + u16 irq_stat; /* interrupt status */ + unsigned int clk_src_div; + unsigned int speed_hz; /* The speed in transfer */ + 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, + .max_write_len = 255, + .max_read_len = 255, + .max_comb_1st_msg_len = 255, + .max_comb_2nd_msg_len = 31, +}; + +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, + .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, + .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, + .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, + .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, + .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[] = { + { .compatible = "mediatek,mt2712-i2c", .data = &mt2712_compat }, + { .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 u16 mtk_i2c_readw(struct mtk_i2c *i2c, enum I2C_REGS_OFFSET reg) +{ + return readw(i2c->base + i2c->dev_comp->regs[reg]); +} + +static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val, + enum I2C_REGS_OFFSET reg) +{ + writew(val, i2c->base + i2c->dev_comp->regs[reg]); +} + +static void mtk_i2c_init_hw(struct mtk_i2c *i2c) +{ + u16 control_reg; + 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) + mtk_i2c_writew(i2c, I2C_IO_CONFIG_PUSH_PULL, OFFSET_IO_CONFIG); + else + mtk_i2c_writew(i2c, I2C_IO_CONFIG_OPEN_DRAIN, OFFSET_IO_CONFIG); + + if (i2c->dev_comp->dcm) + mtk_i2c_writew(i2c, I2C_DCM_DISABLE, OFFSET_DCM_EN); + + 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); + + 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) + 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; + 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 = (HZ_PER_GHZ * (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, max_sta_cnt = 0x3f; + unsigned int clk_ns, sample_ns; + + 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); + + sample_ns = div_u64(1ULL * HZ_PER_GHZ * (sample_cnt + 1), clk_src); + if (i2c->dev_comp->ltiming_adjust) + clk_ns = HZ_PER_GHZ / 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; +} + +/* + * Calculate i2c port speed + * + * Hardware design: + * i2c_bus_freq = parent_clk / (clock_div * 2 * sample_cnt * step_cnt) + * clock_div: fixed in hardware, but may be various in different SoCs + * + * The calculation want to pick the highest bus frequency that is still + * less than or equal to i2c->speed_hz. The calculation try to get + * sample_cnt and step_cn + */ +static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, + unsigned int target_speed, + unsigned int *timing_step_cnt, + unsigned int *timing_sample_cnt) +{ + unsigned int step_cnt; + unsigned int sample_cnt; + unsigned int max_step_cnt; + unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV; + unsigned int base_step_cnt; + unsigned int opt_div; + unsigned int best_mul; + unsigned int cnt_mul; + int ret = -EINVAL; + int clk_div_restri = 0; + + 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); + best_mul = MAX_SAMPLE_CNT_DIV * max_step_cnt; + + /* Search for the best pair (sample_cnt, step_cnt) with + * 0 < sample_cnt < MAX_SAMPLE_CNT_DIV + * 0 < step_cnt < max_step_cnt + * sample_cnt * step_cnt >= opt_div + * optimizing for sample_cnt * step_cnt being minimal + */ + for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_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 + 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 - clk_div_restri))) > + target_speed) { + /* In this case, hardware can't support such + * low i2c_bus_freq + */ + dev_dbg(i2c->dev, "Unsupported speed (%uhz)\n", target_speed); + return -EINVAL; + } + + *timing_step_cnt = step_cnt - 1; + *timing_sample_cnt = sample_cnt - 1; + + return 0; +} + +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; + + target_speed = i2c->speed_hz; + parent_clk /= i2c->clk_src_div; + + 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 = (l_sample_cnt << 8) | l_step_cnt; + + /* Disable the high speed transaction */ + i2c->high_speed_reg = I2C_TIME_CLR_VALUE; + + if (i2c->dev_comp->ltiming_adjust) + i2c->ltiming_reg = + (l_sample_cnt << 6) | l_step_cnt; + } + + break; + } +} + +static void i2c_dump_register(struct mtk_i2c *i2c) +{ + 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, + int num, int left_num) +{ + u16 addr_reg; + 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; + dma_addr_t wpaddr = 0; + int ret; + + i2c->irq_stat = 0; + + if (i2c->auto_restart) + restart_flag = I2C_RS_TRANSFER; + + reinit_completion(&i2c->msg_complete); + + 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 > 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; + + mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL); + + addr_reg = i2c_8bit_addr_from_msg(msgs); + mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR); + + /* Clear interrupt status */ + 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 */ + 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) { + mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN); + mtk_i2c_writew(i2c, (msgs + 1)->len, + OFFSET_TRANSFER_LEN_AUX); + } else { + mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8, + OFFSET_TRANSFER_LEN); + } + mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN); + } else { + 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 | dma_sync, i2c->pdmabase + OFFSET_CON); + + dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 1); + if (!dma_rd_buf) + return -ENOMEM; + + rpaddr = dma_map_single(i2c->dev, dma_rd_buf, + msgs->len, DMA_FROM_DEVICE); + if (dma_mapping_error(i2c->dev, rpaddr)) { + i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, false); + + return -ENOMEM; + } + + 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); + } + + writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR); + 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 | dma_sync, i2c->pdmabase + OFFSET_CON); + + dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1); + if (!dma_wr_buf) + return -ENOMEM; + + wpaddr = dma_map_single(i2c->dev, dma_wr_buf, + msgs->len, DMA_TO_DEVICE); + if (dma_mapping_error(i2c->dev, wpaddr)) { + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false); + + return -ENOMEM; + } + + 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); + } + + writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR); + writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN); + } else { + writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG); + writel(I2C_DMA_CLR_FLAG | dma_sync, i2c->pdmabase + OFFSET_CON); + + dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1); + if (!dma_wr_buf) + return -ENOMEM; + + wpaddr = dma_map_single(i2c->dev, dma_wr_buf, + msgs->len, DMA_TO_DEVICE); + if (dma_mapping_error(i2c->dev, wpaddr)) { + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false); + + return -ENOMEM; + } + + 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); + + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false); + + return -ENOMEM; + } + + rpaddr = dma_map_single(i2c->dev, dma_rd_buf, + (msgs + 1)->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(i2c->dev, rpaddr)) { + dma_unmap_single(i2c->dev, wpaddr, + msgs->len, DMA_TO_DEVICE); + + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, false); + i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), false); + + return -ENOMEM; + } + + 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 = upper_32_bits(rpaddr); + writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE); + } + + writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR); + writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR); + writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN); + writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN); + } + + writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN); + + if (!i2c->auto_restart) { + start_reg = I2C_TRANSAC_START; + } else { + start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG; + if (left_num >= 1) + start_reg |= I2C_RS_MUL_CNFG; + } + mtk_i2c_writew(i2c, start_reg, OFFSET_START); + + ret = wait_for_completion_timeout(&i2c->msg_complete, + i2c->adap.timeout); + + /* Clear interrupt 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, + msgs->len, DMA_TO_DEVICE); + + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true); + } else if (i2c->op == I2C_MASTER_RD) { + dma_unmap_single(i2c->dev, rpaddr, + msgs->len, DMA_FROM_DEVICE); + + i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, true); + } else { + dma_unmap_single(i2c->dev, wpaddr, msgs->len, + DMA_TO_DEVICE); + dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len, + DMA_FROM_DEVICE); + + i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true); + i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), true); + } + + 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; + } + + 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); + return -ENXIO; + } + + return 0; +} + +static int mtk_i2c_transfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + int ret; + int left_num = num; + bool write_then_read_en = false; + struct mtk_i2c *i2c = i2c_get_adapdata(adap); + + if (i2c->adap.bus_regulator) { + ret = regulator_enable(i2c->adap.bus_regulator); + if (ret) + return ret; + } + + ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) + goto err_regulator; + + i2c->auto_restart = i2c->dev_comp->auto_restart; + + /* checking if we can skip restart and optimize using WRRD mode */ + if (num == 2) { + 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 > I2C_MAX_FAST_MODE_PLUS_FREQ) + /* ignore the first restart irq after the master code, + * otherwise the first transfer will be discarded. + */ + i2c->ignore_restart_irq = true; + else + i2c->ignore_restart_irq = false; + + while (left_num--) { + if (!msgs->buf) { + dev_dbg(i2c->dev, "data buffer is NULL.\n"); + ret = -EINVAL; + goto err_exit; + } + + if (msgs->flags & I2C_M_RD) + i2c->op = I2C_MASTER_RD; + else + i2c->op = I2C_MASTER_WR; + + if (write_then_read_en) { + /* combined two messages into one transaction */ + i2c->op = I2C_MASTER_WRRD; + left_num--; + } + + /* always use DMA mode. */ + ret = mtk_i2c_do_transfer(i2c, msgs, num, left_num); + if (ret < 0) + goto err_exit; + + if (i2c->op == I2C_MASTER_WRRD) + msgs += 2; + else + msgs++; + } + /* the return value is number of executed messages */ + ret = num; + +err_exit: + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); +err_regulator: + if (i2c->adap.bus_regulator) + regulator_disable(i2c->adap.bus_regulator); + + return ret; +} + +static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) +{ + struct mtk_i2c *i2c = dev_id; + u16 restart_flag = i2c->auto_restart ? I2C_RS_TRANSFER : 0; + u16 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 + * first is the ack error interrupt, then the complete interrupt + * i2c->irq_stat need keep the two interrupt value. + */ + i2c->irq_stat |= intr_stat; + + if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) { + i2c->ignore_restart_irq = false; + i2c->irq_stat = 0; + 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); + } + + return IRQ_HANDLED; +} + +static u32 mtk_i2c_functionality(struct i2c_adapter *adap) +{ + 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 = { + .xfer = mtk_i2c_transfer, + .functionality = mtk_i2c_functionality, +}; + +static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) +{ + int ret; + + ret = of_property_read_u32(np, "clock-frequency", &i2c->speed_hz); + if (ret < 0) + i2c->speed_hz = I2C_MAX_STANDARD_MODE_FREQ; + + ret = of_property_read_u32(np, "clock-div", &i2c->clk_src_div); + if (ret < 0) + return ret; + + if (i2c->clk_src_div == 0) + return -EINVAL; + + i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic"); + 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; +} + +static int mtk_i2c_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mtk_i2c *i2c; + int i, irq, speed_clk; + + 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->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) + return irq; + + init_completion(&i2c->msg_complete); + + i2c->dev_comp = of_device_get_match_data(&pdev->dev); + i2c->adap.dev.of_node = pdev->dev.of_node; + i2c->dev = &pdev->dev; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &mtk_i2c_algorithm; + 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->have_pmic && !i2c->dev_comp->pmic_i2c) + return -EINVAL; + + /* 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->clocks[I2C_MT65XX_CLK_MAIN].clk); + } + + 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->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); + } + + if (i2c->have_pmic) { + if (!i2c->clocks[I2C_MT65XX_CLK_PMIC].clk) { + dev_err(&pdev->dev, "cannot get pmic clock\n"); + return -ENODEV; + } + speed_clk = I2C_MT65XX_CLK_PMIC; + } else { + speed_clk = I2C_MT65XX_CLK_MAIN; + } + + strscpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); + + mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); + + 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 = 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); + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + + ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, + 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); + goto err_bulk_unprepare; + } + + i2c_set_adapdata(&i2c->adap, i2c); + ret = i2c_add_adapter(&i2c->adap); + if (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 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; +} + +static int mtk_i2c_resume_noirq(struct device *dev) +{ + int ret; + struct mtk_i2c *i2c = dev_get_drvdata(dev); + + ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) { + dev_err(dev, "clock enable failed!\n"); + return ret; + } + + mtk_i2c_init_hw(i2c); + + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + + i2c_mark_adapter_resumed(&i2c->adap); + + return 0; +} + +static const struct dev_pm_ops mtk_i2c_pm = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_i2c_suspend_noirq, + mtk_i2c_resume_noirq) +}; + +static struct platform_driver mtk_i2c_driver = { + .probe = mtk_i2c_probe, + .remove = mtk_i2c_remove, + .driver = { + .name = I2C_DRV_NAME, + .pm = pm_sleep_ptr(&mtk_i2c_pm), + .of_match_table = mtk_i2c_of_match, + }, +}; + +module_platform_driver(mtk_i2c_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek I2C Bus Driver"); +MODULE_AUTHOR("Xudong Chen "); diff --git a/drivers/i2c/busses/i2c-mt7621.c b/drivers/i2c/busses/i2c-mt7621.c new file mode 100644 index 000000000..0a288c998 --- /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 + * Copyright (C) 2016 Michael Lee + * Copyright (C) 2018 Jan Breuer + * + * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus. + * (C) 2014 Sittisak + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 new file mode 100644 index 000000000..1acba628e --- /dev/null +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -0,0 +1,1112 @@ +/* + * Driver for the i2c controller on the Marvell line of host bridges + * (e.g, gt642[46]0, mv643[46]0, mv644[46]0, and Orion SoC family). + * + * Author: Mark A. Greer + * + * 2005 (c) MontaVista, Software, Inc. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MV64XXX_I2C_BAUD_DIV_N(val) (val & 0x7) +#define MV64XXX_I2C_BAUD_DIV_M(val) ((val & 0xf) << 3) + +#define MV64XXX_I2C_REG_CONTROL_ACK BIT(2) +#define MV64XXX_I2C_REG_CONTROL_IFLG BIT(3) +#define MV64XXX_I2C_REG_CONTROL_STOP BIT(4) +#define MV64XXX_I2C_REG_CONTROL_START BIT(5) +#define MV64XXX_I2C_REG_CONTROL_TWSIEN BIT(6) +#define MV64XXX_I2C_REG_CONTROL_INTEN BIT(7) + +/* Ctlr status values */ +#define MV64XXX_I2C_STATUS_BUS_ERR 0x00 +#define MV64XXX_I2C_STATUS_MAST_START 0x08 +#define MV64XXX_I2C_STATUS_MAST_REPEAT_START 0x10 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK 0x18 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK 0x20 +#define MV64XXX_I2C_STATUS_MAST_WR_ACK 0x28 +#define MV64XXX_I2C_STATUS_MAST_WR_NO_ACK 0x30 +#define MV64XXX_I2C_STATUS_MAST_LOST_ARB 0x38 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK 0x40 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK 0x48 +#define MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK 0x50 +#define MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK 0x58 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK 0xd0 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK 0xd8 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK 0xe0 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK 0xe8 +#define MV64XXX_I2C_STATUS_NO_STATUS 0xf8 + +/* Register defines (I2C bridge) */ +#define MV64XXX_I2C_REG_TX_DATA_LO 0xc0 +#define MV64XXX_I2C_REG_TX_DATA_HI 0xc4 +#define MV64XXX_I2C_REG_RX_DATA_LO 0xc8 +#define MV64XXX_I2C_REG_RX_DATA_HI 0xcc +#define MV64XXX_I2C_REG_BRIDGE_CONTROL 0xd0 +#define MV64XXX_I2C_REG_BRIDGE_STATUS 0xd4 +#define MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE 0xd8 +#define MV64XXX_I2C_REG_BRIDGE_INTR_MASK 0xdC +#define MV64XXX_I2C_REG_BRIDGE_TIMING 0xe0 + +/* Bridge Control values */ +#define MV64XXX_I2C_BRIDGE_CONTROL_WR BIT(0) +#define MV64XXX_I2C_BRIDGE_CONTROL_RD BIT(1) +#define MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT 2 +#define MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT BIT(12) +#define MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT 13 +#define MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT 16 +#define MV64XXX_I2C_BRIDGE_CONTROL_ENABLE BIT(19) +#define MV64XXX_I2C_BRIDGE_CONTROL_REPEATED_START BIT(20) + +/* Bridge Status values */ +#define MV64XXX_I2C_BRIDGE_STATUS_ERROR BIT(0) + +/* Driver states */ +enum { + MV64XXX_I2C_STATE_INVALID, + MV64XXX_I2C_STATE_IDLE, + MV64XXX_I2C_STATE_WAITING_FOR_START_COND, + 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_TARGET_ACK, + MV64XXX_I2C_STATE_WAITING_FOR_TARGET_DATA, +}; + +/* Driver actions */ +enum { + MV64XXX_I2C_ACTION_INVALID, + MV64XXX_I2C_ACTION_CONTINUE, + MV64XXX_I2C_ACTION_SEND_RESTART, + MV64XXX_I2C_ACTION_SEND_ADDR_1, + MV64XXX_I2C_ACTION_SEND_ADDR_2, + MV64XXX_I2C_ACTION_SEND_DATA, + MV64XXX_I2C_ACTION_RCV_DATA, + MV64XXX_I2C_ACTION_RCV_DATA_STOP, + MV64XXX_I2C_ACTION_SEND_STOP, +}; + +struct mv64xxx_i2c_regs { + u8 addr; + u8 ext_addr; + u8 data; + u8 control; + u8 status; + u8 clock; + u8 soft_reset; +}; + +struct mv64xxx_i2c_data { + struct i2c_msg *msgs; + int num_msgs; + int irq; + u32 state; + u32 action; + u32 aborting; + u32 cntl_bits; + void __iomem *reg_base; + struct mv64xxx_i2c_regs reg_offsets; + u32 addr1; + u32 addr2; + u32 bytes_left; + u32 byte_posn; + u32 send_stop; + u32 block; + int rc; + u32 freq_m; + u32 freq_n; + struct clk *clk; + struct clk *reg_clk; + wait_queue_head_t waitq; + spinlock_t lock; + struct i2c_msg *msg; + struct i2c_adapter adapter; + bool offload_enabled; +/* 5us delay in order to avoid repeated start timing violation */ + bool errata_delay; + struct reset_control *rstc; + 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 = { + .addr = 0x00, + .ext_addr = 0x10, + .data = 0x04, + .control = 0x08, + .status = 0x0c, + .clock = 0x0c, + .soft_reset = 0x1c, +}; + +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_sun4i = { + .addr = 0x00, + .ext_addr = 0x04, + .data = 0x08, + .control = 0x0c, + .status = 0x10, + .clock = 0x14, + .soft_reset = 0x18, +}; + +static void +mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | + MV64XXX_I2C_REG_CONTROL_TWSIEN; + + if (!drv_data->atomic) + drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_INTEN; + + if (msg->flags & I2C_M_TEN) { + drv_data->addr1 = i2c_10bit_addr_hi_from_msg(msg); + drv_data->addr2 = i2c_10bit_addr_lo_from_msg(msg); + } else { + drv_data->addr1 = i2c_8bit_addr_from_msg(msg); + drv_data->addr2 = 0; + } +} + +/* + ***************************************************************************** + * + * Finite State Machine & Interrupt Routines + * + ***************************************************************************** + */ + +/* Reset hardware and initialize FSM */ +static void +mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) +{ + if (drv_data->offload_enabled) { + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_TIMING); + writel(0, drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); + writel(0, drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_MASK); + } + + writel(0, drv_data->reg_base + drv_data->reg_offsets.soft_reset); + writel(MV64XXX_I2C_BAUD_DIV_M(drv_data->freq_m) | MV64XXX_I2C_BAUD_DIV_N(drv_data->freq_n), + drv_data->reg_base + drv_data->reg_offsets.clock); + writel(0, drv_data->reg_base + drv_data->reg_offsets.addr); + 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; +} + +static void +mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) +{ + /* + * If state is idle, then this is likely the remnants of an old + * operation that driver has given up on or the user has killed. + * If so, issue the stop condition and go to idle. + */ + if (drv_data->state == MV64XXX_I2C_STATE_IDLE) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + return; + } + + /* The status from the ctlr [mostly] tells us what to do next */ + switch (status) { + /* Start condition interrupt */ + case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */ + case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */ + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; + break; + + /* Performing a write */ + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + 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) + || (drv_data->aborting + && (drv_data->byte_posn != 0))) { + if (drv_data->send_stop || drv_data->aborting) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + } else { + drv_data->action = + MV64XXX_I2C_ACTION_SEND_RESTART; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_RESTART; + } + } else { + drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_TARGET_ACK; + drv_data->bytes_left--; + } + break; + + /* Performing a read */ + case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + 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; + } + 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; + else { + drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA; + drv_data->bytes_left--; + } + 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; + break; + + case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */ + drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + break; + + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */ + case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */ + case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */ + /* Doesn't seem to be a device at other end */ + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + drv_data->rc = -ENXIO; + break; + + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, " + "status: 0x%x, addr: 0x%x, flags: 0x%x\n", + drv_data->state, status, drv_data->msg->addr, + drv_data->msg->flags); + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + mv64xxx_i2c_hw_init(drv_data); + i2c_recover_bus(&drv_data->adapter); + drv_data->rc = -EAGAIN; + } +} + +static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data) +{ + drv_data->msg = drv_data->msgs; + drv_data->byte_posn = 0; + drv_data->bytes_left = drv_data->msg->len; + drv_data->aborting = 0; + drv_data->rc = 0; + + mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs); + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START, + drv_data->reg_base + drv_data->reg_offsets.control); +} + +static void +mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) +{ + switch(drv_data->action) { + case MV64XXX_I2C_ACTION_SEND_RESTART: + /* We should only get here if we have further messages */ + BUG_ON(drv_data->num_msgs == 0); + + drv_data->msgs++; + drv_data->num_msgs--; + mv64xxx_i2c_send_start(drv_data); + + if (drv_data->errata_delay) + udelay(5); + + /* + * We're never at the start of the message here, and by this + * time it's already too late to do any protocol mangling. + * Thankfully, do not advertise support for that feature. + */ + drv_data->send_stop = drv_data->num_msgs == 1; + break; + + case MV64XXX_I2C_ACTION_CONTINUE: + writel(drv_data->cntl_bits, + drv_data->reg_base + drv_data->reg_offsets.control); + break; + + case MV64XXX_I2C_ACTION_SEND_ADDR_1: + writel(drv_data->addr1, + drv_data->reg_base + drv_data->reg_offsets.data); + writel(drv_data->cntl_bits, + drv_data->reg_base + drv_data->reg_offsets.control); + break; + + case MV64XXX_I2C_ACTION_SEND_ADDR_2: + writel(drv_data->addr2, + drv_data->reg_base + drv_data->reg_offsets.data); + writel(drv_data->cntl_bits, + drv_data->reg_base + drv_data->reg_offsets.control); + break; + + case MV64XXX_I2C_ACTION_SEND_DATA: + writel(drv_data->msg->buf[drv_data->byte_posn++], + drv_data->reg_base + drv_data->reg_offsets.data); + writel(drv_data->cntl_bits, + drv_data->reg_base + drv_data->reg_offsets.control); + break; + + case MV64XXX_I2C_ACTION_RCV_DATA: + drv_data->msg->buf[drv_data->byte_posn++] = + readl(drv_data->reg_base + drv_data->reg_offsets.data); + writel(drv_data->cntl_bits, + drv_data->reg_base + drv_data->reg_offsets.control); + break; + + 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); + 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; + if (drv_data->errata_delay) + udelay(5); + + wake_up(&drv_data->waitq); + break; + + case MV64XXX_I2C_ACTION_INVALID: + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_do_action: Invalid action: %d\n", + drv_data->action); + drv_data->rc = -EIO; + fallthrough; + case MV64XXX_I2C_ACTION_SEND_STOP: + 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; + wake_up(&drv_data->waitq); + break; + } +} + +static void +mv64xxx_i2c_read_offload_rx_data(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + u32 buf[2]; + + buf[0] = readl(drv_data->reg_base + MV64XXX_I2C_REG_RX_DATA_LO); + buf[1] = readl(drv_data->reg_base + MV64XXX_I2C_REG_RX_DATA_HI); + + memcpy(msg->buf, buf, msg->len); +} + +static int +mv64xxx_i2c_intr_offload(struct mv64xxx_i2c_data *drv_data) +{ + u32 cause, status; + + cause = readl(drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); + if (!cause) + return IRQ_NONE; + + status = readl(drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_STATUS); + + if (status & MV64XXX_I2C_BRIDGE_STATUS_ERROR) { + drv_data->rc = -EIO; + goto out; + } + + drv_data->rc = 0; + + /* + * Transaction is a one message read transaction, read data + * for this message. + */ + if (drv_data->num_msgs == 1 && drv_data->msgs[0].flags & I2C_M_RD) { + mv64xxx_i2c_read_offload_rx_data(drv_data, drv_data->msgs); + drv_data->msgs++; + drv_data->num_msgs--; + } + /* + * Transaction is a two messages write/read transaction, read + * data for the second (read) message. + */ + else if (drv_data->num_msgs == 2 && + !(drv_data->msgs[0].flags & I2C_M_RD) && + drv_data->msgs[1].flags & I2C_M_RD) { + mv64xxx_i2c_read_offload_rx_data(drv_data, drv_data->msgs + 1); + drv_data->msgs += 2; + drv_data->num_msgs -= 2; + } + +out: + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); + writel(0, drv_data->reg_base + + MV64XXX_I2C_REG_BRIDGE_INTR_CAUSE); + drv_data->block = 0; + + wake_up(&drv_data->waitq); + + return IRQ_HANDLED; +} + +static irqreturn_t +mv64xxx_i2c_intr(int irq, void *dev_id) +{ + struct mv64xxx_i2c_data *drv_data = dev_id; + u32 status; + irqreturn_t rc = IRQ_NONE; + + 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); + + if (drv_data->irq_clear_inverted) + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_IFLG, + drv_data->reg_base + drv_data->reg_offsets.control); + + rc = IRQ_HANDLED; + } + spin_unlock(&drv_data->lock); + + return rc; +} + +/* + ***************************************************************************** + * + * I2C Msg Execution Routines + * + ***************************************************************************** + */ +static void +mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) +{ + long time_left; + unsigned long flags; + char abort = 0; + + time_left = wait_event_timeout(drv_data->waitq, + !drv_data->block, drv_data->adapter.timeout); + + spin_lock_irqsave(&drv_data->lock, flags); + if (!time_left) { /* Timed out */ + drv_data->rc = -ETIMEDOUT; + abort = 1; + } else if (time_left < 0) { /* Interrupted/Error */ + drv_data->rc = time_left; /* errno value */ + abort = 1; + } + + if (abort && drv_data->block) { + drv_data->aborting = 1; + spin_unlock_irqrestore(&drv_data->lock, flags); + + time_left = wait_event_timeout(drv_data->waitq, + !drv_data->block, drv_data->adapter.timeout); + + if ((time_left <= 0) && drv_data->block) { + drv_data->state = MV64XXX_I2C_STATE_IDLE; + dev_err(&drv_data->adapter.dev, + "mv64xxx: I2C bus locked, block: %d, " + "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) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND; + + drv_data->send_stop = is_last; + drv_data->block = 1; + mv64xxx_i2c_send_start(drv_data); + spin_unlock_irqrestore(&drv_data->lock, flags); + + if (!drv_data->atomic) + mv64xxx_i2c_wait_for_completion(drv_data); + else + mv64xxx_i2c_wait_polling(drv_data); + + return drv_data->rc; +} + +static void +mv64xxx_i2c_prepare_tx(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msg = drv_data->msgs; + u32 buf[2]; + + memcpy(buf, msg->buf, msg->len); + + writel(buf[0], drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO); + writel(buf[1], drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI); +} + +static int +mv64xxx_i2c_offload_xfer(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msgs = drv_data->msgs; + int num = drv_data->num_msgs; + unsigned long ctrl_reg; + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + /* Build transaction */ + ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE | + (msgs[0].addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT); + + if (msgs[0].flags & I2C_M_TEN) + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT; + + /* Single write message transaction */ + if (num == 1 && !(msgs[0].flags & I2C_M_RD)) { + size_t len = msgs[0].len - 1; + + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR | + (len << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT); + mv64xxx_i2c_prepare_tx(drv_data); + } + /* Single read message transaction */ + else if (num == 1 && msgs[0].flags & I2C_M_RD) { + size_t len = msgs[0].len - 1; + + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD | + (len << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT); + } + /* + * Transaction with one write and one read message. This is + * guaranteed by the mv64xx_i2c_can_offload() checks. + */ + else if (num == 2) { + size_t lentx = msgs[0].len - 1; + size_t lenrx = msgs[1].len - 1; + + ctrl_reg |= + MV64XXX_I2C_BRIDGE_CONTROL_RD | + MV64XXX_I2C_BRIDGE_CONTROL_WR | + (lentx << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT) | + (lenrx << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT) | + MV64XXX_I2C_BRIDGE_CONTROL_REPEATED_START; + mv64xxx_i2c_prepare_tx(drv_data); + } + + /* Execute transaction */ + drv_data->block = 1; + writel(ctrl_reg, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL); + spin_unlock_irqrestore(&drv_data->lock, flags); + + mv64xxx_i2c_wait_for_completion(drv_data); + + return drv_data->rc; +} + +static bool +mv64xxx_i2c_valid_offload_sz(struct i2c_msg *msg) +{ + return msg->len <= 8 && msg->len >= 1; +} + +static bool +mv64xxx_i2c_can_offload(struct mv64xxx_i2c_data *drv_data) +{ + struct i2c_msg *msgs = drv_data->msgs; + int num = drv_data->num_msgs; + + if (!drv_data->offload_enabled) + return false; + + /* + * We can offload a transaction consisting of a single + * message, as long as the message has a length between 1 and + * 8 bytes. + */ + if (num == 1 && mv64xxx_i2c_valid_offload_sz(msgs)) + return true; + + /* + * We can offload a transaction consisting of two messages, if + * the first is a write and a second is a read, and both have + * a length between 1 and 8 bytes. + */ + if (num == 2 && + mv64xxx_i2c_valid_offload_sz(msgs) && + mv64xxx_i2c_valid_offload_sz(msgs + 1) && + !(msgs[0].flags & I2C_M_RD) && + msgs[1].flags & I2C_M_RD) + return true; + + return false; +} + +/* + ***************************************************************************** + * + * I2C Core Support Routines (Interface to higher level I2C code) + * + ***************************************************************************** + */ +static u32 +mv64xxx_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static int +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) && !drv_data->atomic) + rc = mv64xxx_i2c_offload_xfer(drv_data); + else + rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); + + if (rc < 0) + ret = rc; + + 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 = { + .xfer = mv64xxx_i2c_xfer, + .xfer_atomic = mv64xxx_i2c_xfer_atomic, + .functionality = mv64xxx_i2c_functionality, +}; + +/* + ***************************************************************************** + * + * Driver Interface & Early Init Routines + * + ***************************************************************************** + */ +static const struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible = "allwinner,sun4i-a10-i2c", .data = &mv64xxx_i2c_regs_sun4i}, + { .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i}, + { .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx}, + { .compatible = "marvell,mv78230-i2c", .data = &mv64xxx_i2c_regs_mv64xxx}, + { .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx}, + {} +}; +MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table); + +#ifdef CONFIG_OF +static int +mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data, + const int tclk, const int n, const int m) +{ + if (drv_data->clk_n_base_0) + return tclk / (10 * (m + 1) * (1 << n)); + else + return tclk / (10 * (m + 1) * (2 << n)); +} + +static bool +mv64xxx_find_baud_factors(struct mv64xxx_i2c_data *drv_data, + const u32 req_freq, const u32 tclk) +{ + int freq, delta, best_delta = INT_MAX; + int m, n; + + for (n = 0; n <= 7; n++) + for (m = 0; m <= 15; m++) { + freq = mv64xxx_calc_freq(drv_data, tclk, n, m); + delta = req_freq - freq; + if (delta >= 0 && delta < best_delta) { + drv_data->freq_m = m; + drv_data->freq_n = n; + best_delta = delta; + } + if (best_delta == 0) + return true; + } + if (best_delta == INT_MAX) + return false; + return true; +} + +static int +mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, + struct device *dev) +{ + const struct mv64xxx_i2c_regs *data; + struct device_node *np = dev->of_node; + u32 bus_freq, tclk; + int rc = 0; + + /* CLK is mandatory when using DT to describe the i2c bus. We + * need to know tclk in order to calculate bus clock + * factors. + */ + 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 = 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")) + drv_data->clk_n_base_0 = true; + + if (!mv64xxx_find_baud_factors(drv_data, bus_freq, tclk)) { + rc = -EINVAL; + goto out; + } + + drv_data->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(drv_data->rstc)) { + rc = PTR_ERR(drv_data->rstc); + goto out; + } + + /* 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; + + data = device_get_match_data(dev); + if (!data) + return -ENODEV; + + memcpy(&drv_data->reg_offsets, data, sizeof(drv_data->reg_offsets)); + + /* + * For controllers embedded in new SoCs activate the + * Transaction Generator support and the errata fix. + */ + 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 <= 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 <= I2C_MAX_STANDARD_MODE_FREQ) + drv_data->errata_delay = true; + } + + if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c")) + drv_data->irq_clear_inverted = true; + +out: + return rc; +} +#else /* CONFIG_OF */ +static int +mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, + struct device *dev) +{ + return -ENODEV; +} +#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); + int rc; + + if ((!pdata && !pd->dev.of_node)) + return -ENODEV; + + drv_data = devm_kzalloc(&pd->dev, sizeof(struct mv64xxx_i2c_data), + GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + drv_data->reg_base = devm_platform_ioremap_resource(pd, 0); + if (IS_ERR(drv_data->reg_base)) + return PTR_ERR(drv_data->reg_base); + + strscpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", + sizeof(drv_data->adapter.name)); + + init_waitqueue_head(&drv_data->waitq); + spin_lock_init(&drv_data->lock); + + /* Not all platforms have clocks */ + drv_data->clk = devm_clk_get(&pd->dev, NULL); + 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)) { + 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; + drv_data->freq_n = pdata->freq_n; + drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout); + drv_data->offload_enabled = false; + memcpy(&drv_data->reg_offsets, &mv64xxx_i2c_regs_mv64xxx, sizeof(drv_data->reg_offsets)); + } else if (pd->dev.of_node) { + rc = mv64xxx_of_config(drv_data, &pd->dev); + if (rc) + 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; + drv_data->adapter.class = I2C_CLASS_DEPRECATED; + drv_data->adapter.nr = pd->id; + drv_data->adapter.dev.of_node = pd->dev.of_node; + platform_set_drvdata(pd, drv_data); + i2c_set_adapdata(&drv_data->adapter, 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); + if (rc) { + dev_err(&drv_data->adapter.dev, + "mv64xxx: Can't register intr handler irq%d: %d\n", + drv_data->irq, rc); + 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); + goto exit_free_irq; + } + + return 0; + +exit_free_irq: + free_irq(drv_data->irq, drv_data); +exit_disable_pm: + pm_runtime_disable(&pd->dev); + if (!pm_runtime_status_suspended(&pd->dev)) + mv64xxx_i2c_runtime_suspend(&pd->dev); + + return rc; +} + +static void +mv64xxx_i2c_remove(struct platform_device *pd) +{ + struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pd); + + i2c_del_adapter(&drv_data->adapter); + free_irq(drv_data->irq, drv_data); + pm_runtime_disable(&pd->dev); + if (!pm_runtime_status_suspended(&pd->dev)) + mv64xxx_i2c_runtime_suspend(&pd->dev); +} + +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) +}; + +static struct platform_driver mv64xxx_i2c_driver = { + .probe = mv64xxx_i2c_probe, + .remove = mv64xxx_i2c_remove, + .driver = { + .name = MV64XXX_I2C_CTLR_NAME, + .pm = &mv64xxx_i2c_pm_ops, + .of_match_table = mv64xxx_i2c_of_match_table, + }, +}; + +module_platform_driver(mv64xxx_i2c_driver); + +MODULE_AUTHOR("Mark A. Greer "); +MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c new file mode 100644 index 000000000..eee4fdcd9 --- /dev/null +++ b/drivers/i2c/busses/i2c-mxs.c @@ -0,0 +1,908 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale MXS I2C bus driver + * + * Copyright (C) 2012-2013 Marek Vasut + * Copyright (C) 2011-2012 Wolfram Sang, Pengutronix e.K. + * + * based on a (non-working) driver which was: + * + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs-i2c" + +#define MXS_I2C_CTRL0 (0x00) +#define MXS_I2C_CTRL0_SET (0x04) +#define MXS_I2C_CTRL0_CLR (0x08) + +#define MXS_I2C_CTRL0_SFTRST 0x80000000 +#define MXS_I2C_CTRL0_RUN 0x20000000 +#define MXS_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000 +#define MXS_I2C_CTRL0_PIO_MODE 0x01000000 +#define MXS_I2C_CTRL0_RETAIN_CLOCK 0x00200000 +#define MXS_I2C_CTRL0_POST_SEND_STOP 0x00100000 +#define MXS_I2C_CTRL0_PRE_SEND_START 0x00080000 +#define MXS_I2C_CTRL0_MASTER_MODE 0x00020000 +#define MXS_I2C_CTRL0_DIRECTION 0x00010000 +#define MXS_I2C_CTRL0_XFER_COUNT(v) ((v) & 0x0000FFFF) + +#define MXS_I2C_TIMING0 (0x10) +#define MXS_I2C_TIMING1 (0x20) +#define MXS_I2C_TIMING2 (0x30) + +#define MXS_I2C_CTRL1 (0x40) +#define MXS_I2C_CTRL1_SET (0x44) +#define MXS_I2C_CTRL1_CLR (0x48) + +#define MXS_I2C_CTRL1_CLR_GOT_A_NAK 0x10000000 +#define MXS_I2C_CTRL1_BUS_FREE_IRQ 0x80 +#define MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ 0x40 +#define MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ 0x20 +#define MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ 0x10 +#define MXS_I2C_CTRL1_EARLY_TERM_IRQ 0x08 +#define MXS_I2C_CTRL1_MASTER_LOSS_IRQ 0x04 +#define MXS_I2C_CTRL1_SLAVE_STOP_IRQ 0x02 +#define MXS_I2C_CTRL1_SLAVE_IRQ 0x01 + +#define MXS_I2C_STAT (0x50) +#define MXS_I2C_STAT_GOT_A_NAK 0x10000000 +#define MXS_I2C_STAT_BUS_BUSY 0x00000800 +#define MXS_I2C_STAT_CLK_GEN_BUSY 0x00000400 + +#define MXS_I2C_DATA(i2c) ((i2c->dev_type == MXS_I2C_V1) ? 0x60 : 0xa0) + +#define MXS_I2C_DEBUG0_CLR(i2c) ((i2c->dev_type == MXS_I2C_V1) ? 0x78 : 0xb8) + +#define MXS_I2C_DEBUG0_DMAREQ 0x80000000 + +#define MXS_I2C_IRQ_MASK (MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | \ + MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ | \ + MXS_I2C_CTRL1_EARLY_TERM_IRQ | \ + MXS_I2C_CTRL1_MASTER_LOSS_IRQ | \ + MXS_I2C_CTRL1_SLAVE_STOP_IRQ | \ + MXS_I2C_CTRL1_SLAVE_IRQ) + + +#define MXS_CMD_I2C_SELECT (MXS_I2C_CTRL0_RETAIN_CLOCK | \ + MXS_I2C_CTRL0_PRE_SEND_START | \ + MXS_I2C_CTRL0_MASTER_MODE | \ + MXS_I2C_CTRL0_DIRECTION | \ + MXS_I2C_CTRL0_XFER_COUNT(1)) + +#define MXS_CMD_I2C_WRITE (MXS_I2C_CTRL0_PRE_SEND_START | \ + MXS_I2C_CTRL0_MASTER_MODE | \ + MXS_I2C_CTRL0_DIRECTION) + +#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \ + MXS_I2C_CTRL0_MASTER_MODE) + +enum mxs_i2c_devtype { + MXS_I2C_UNKNOWN = 0, + MXS_I2C_V1, + MXS_I2C_V2, +}; + +/** + * struct mxs_i2c_dev - per device, private MXS-I2C data + * + * @dev: driver model device node + * @dev_type: distinguish i.MX23/i.MX28 features + * @regs: IO registers pointer + * @cmd_complete: completion object for transaction wait + * @cmd_err: error code for last transaction + * @adapter: i2c subsystem adapter node + * @timing0: I2C TIMING0 register value + * @timing1: I2C TIMING1 register value + * @timing2: I2C TIMING2 register value + * @dmach: DMA channel + * @pio_data: PIO data for DMA + * @addr_data: address data for DMA + * @sg_io: scatterlist for I/O + * @dma_read: flag indicating DMA read + */ +struct mxs_i2c_dev { + struct device *dev; + enum mxs_i2c_devtype dev_type; + void __iomem *regs; + struct completion cmd_complete; + int cmd_err; + struct i2c_adapter adapter; + + uint32_t timing0; + uint32_t timing1; + uint32_t timing2; + + /* DMA support components */ + struct dma_chan *dmach; + uint32_t pio_data[2]; + uint32_t addr_data; + struct scatterlist sg_io[2]; + bool dma_read; +}; + +static int mxs_i2c_reset(struct mxs_i2c_dev *i2c) +{ + int ret = stmp_reset_block(i2c->regs); + if (ret) + return ret; + + /* + * Configure timing for the I2C block. The I2C TIMING2 register has to + * be programmed with this particular magic number. The rest is derived + * from the XTAL speed and requested I2C speed. + * + * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. + */ + writel(i2c->timing0, i2c->regs + MXS_I2C_TIMING0); + writel(i2c->timing1, i2c->regs + MXS_I2C_TIMING1); + writel(i2c->timing2, i2c->regs + MXS_I2C_TIMING2); + + writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); + + return 0; +} + +static void mxs_i2c_dma_finish(struct mxs_i2c_dev *i2c) +{ + if (i2c->dma_read) { + dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE); + dma_unmap_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE); + } else { + dma_unmap_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE); + } +} + +static void mxs_i2c_dma_irq_callback(void *param) +{ + struct mxs_i2c_dev *i2c = param; + + complete(&i2c->cmd_complete); + mxs_i2c_dma_finish(i2c); +} + +static int mxs_i2c_dma_setup_xfer(struct i2c_adapter *adap, + struct i2c_msg *msg, u8 *buf, uint32_t flags) +{ + struct dma_async_tx_descriptor *desc; + struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap); + + i2c->addr_data = i2c_8bit_addr_from_msg(msg); + + if (msg->flags & I2C_M_RD) { + i2c->dma_read = true; + + /* + * SELECT command. + */ + + /* Queue the PIO register write transfer. */ + i2c->pio_data[0] = MXS_CMD_I2C_SELECT; + desc = dmaengine_prep_slave_sg(i2c->dmach, + (struct scatterlist *)&i2c->pio_data[0], + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(i2c->dev, + "Failed to get PIO reg. write descriptor.\n"); + goto select_init_pio_fail; + } + + /* Queue the DMA data transfer. */ + sg_init_one(&i2c->sg_io[0], &i2c->addr_data, 1); + 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 | + MXS_DMA_CTRL_WAIT4END); + if (!desc) { + dev_err(i2c->dev, + "Failed to get DMA data write descriptor.\n"); + goto select_init_dma_fail; + } + + /* + * READ command. + */ + + /* Queue the PIO register write transfer. */ + i2c->pio_data[1] = flags | MXS_CMD_I2C_READ | + MXS_I2C_CTRL0_XFER_COUNT(msg->len); + desc = dmaengine_prep_slave_sg(i2c->dmach, + (struct scatterlist *)&i2c->pio_data[1], + 1, DMA_TRANS_NONE, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(i2c->dev, + "Failed to get PIO reg. write descriptor.\n"); + goto select_init_dma_fail; + } + + /* Queue the DMA data transfer. */ + 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 | + MXS_DMA_CTRL_WAIT4END); + if (!desc) { + dev_err(i2c->dev, + "Failed to get DMA data write descriptor.\n"); + goto read_init_dma_fail; + } + } else { + i2c->dma_read = false; + + /* + * WRITE command. + */ + + /* Queue the PIO register write transfer. */ + i2c->pio_data[0] = flags | MXS_CMD_I2C_WRITE | + MXS_I2C_CTRL0_XFER_COUNT(msg->len + 1); + desc = dmaengine_prep_slave_sg(i2c->dmach, + (struct scatterlist *)&i2c->pio_data[0], + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(i2c->dev, + "Failed to get PIO reg. write descriptor.\n"); + goto write_init_pio_fail; + } + + /* 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], 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 | + MXS_DMA_CTRL_WAIT4END); + if (!desc) { + dev_err(i2c->dev, + "Failed to get DMA data write descriptor.\n"); + goto write_init_dma_fail; + } + } + + /* + * The last descriptor must have this callback, + * to finish the DMA transaction. + */ + desc->callback = mxs_i2c_dma_irq_callback; + desc->callback_param = i2c; + + /* Start the transfer. */ + dmaengine_submit(desc); + dma_async_issue_pending(i2c->dmach); + return 0; + +/* Read failpath. */ +read_init_dma_fail: + dma_unmap_sg(i2c->dev, &i2c->sg_io[1], 1, DMA_FROM_DEVICE); +select_init_dma_fail: + dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE); +select_init_pio_fail: + 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_sync(i2c->dmach); + return -EINVAL; +} + +static int mxs_i2c_pio_wait_xfer_end(struct mxs_i2c_dev *i2c) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while (readl(i2c->regs + MXS_I2C_CTRL0) & MXS_I2C_CTRL0_RUN) { + if (readl(i2c->regs + MXS_I2C_CTRL1) & + MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ) + return -ENXIO; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cond_resched(); + } + + return 0; +} + +static int mxs_i2c_pio_check_error_state(struct mxs_i2c_dev *i2c) +{ + u32 state; + + state = readl(i2c->regs + MXS_I2C_CTRL1_CLR) & MXS_I2C_IRQ_MASK; + + if (state & MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ) + i2c->cmd_err = -ENXIO; + else if (state & (MXS_I2C_CTRL1_EARLY_TERM_IRQ | + MXS_I2C_CTRL1_MASTER_LOSS_IRQ | + MXS_I2C_CTRL1_SLAVE_STOP_IRQ | + MXS_I2C_CTRL1_SLAVE_IRQ)) + i2c->cmd_err = -EIO; + + return i2c->cmd_err; +} + +static void mxs_i2c_pio_trigger_cmd(struct mxs_i2c_dev *i2c, u32 cmd) +{ + u32 reg; + + writel(cmd, i2c->regs + MXS_I2C_CTRL0); + + /* readback makes sure the write is latched into hardware */ + reg = readl(i2c->regs + MXS_I2C_CTRL0); + reg |= MXS_I2C_CTRL0_RUN; + writel(reg, i2c->regs + MXS_I2C_CTRL0); +} + +/* + * Start WRITE transaction on the I2C bus. By studying i.MX23 datasheet, + * CTRL0::PIO_MODE bit description clarifies the order in which the registers + * must be written during PIO mode operation. First, the CTRL0 register has + * to be programmed with all the necessary bits but the RUN bit. Then the + * payload has to be written into the DATA register. Finally, the transmission + * is executed by setting the RUN bit in CTRL0. + */ +static void mxs_i2c_pio_trigger_write_cmd(struct mxs_i2c_dev *i2c, u32 cmd, + u32 data) +{ + writel(cmd, i2c->regs + MXS_I2C_CTRL0); + + if (i2c->dev_type == MXS_I2C_V1) + writel(MXS_I2C_CTRL0_PIO_MODE, i2c->regs + MXS_I2C_CTRL0_SET); + + writel(data, i2c->regs + MXS_I2C_DATA(i2c)); + writel(MXS_I2C_CTRL0_RUN, i2c->regs + MXS_I2C_CTRL0_SET); +} + +static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap, + struct i2c_msg *msg, uint32_t flags) +{ + struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap); + uint32_t addr_data = i2c_8bit_addr_from_msg(msg); + uint32_t data = 0; + int i, ret, xlen = 0, xmit = 0; + uint32_t start; + + /* Mute IRQs coming from this block. */ + writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_CLR); + + /* + * MX23 idea: + * - Enable CTRL0::PIO_MODE (1 << 24) + * - Enable CTRL1::ACK_MODE (1 << 27) + * + * WARNING! The MX23 is broken in some way, even if it claims + * to support PIO, when we try to transfer any amount of data + * that is not aligned to 4 bytes, the DMA engine will have + * bits in DEBUG1::DMA_BYTES_ENABLES still set even after the + * transfer. This in turn will mess up the next transfer as + * the block it emit one byte write onto the bus terminated + * with a NAK+STOP. A possible workaround is to reset the IP + * block after every PIO transmission, which might just work. + * + * NOTE: The CTRL0::PIO_MODE description is important, since + * it outlines how the PIO mode is really supposed to work. + */ + if (msg->flags & I2C_M_RD) { + /* + * PIO READ transfer: + * + * This transfer MUST be limited to 4 bytes maximum. It is not + * possible to transfer more than four bytes via PIO, since we + * can not in any way make sure we can read the data from the + * DATA register fast enough. Besides, the RX FIFO is only four + * bytes deep, thus we can only really read up to four bytes at + * time. Finally, there is no bit indicating us that new data + * arrived at the FIFO and can thus be fetched from the DATA + * register. + */ + BUG_ON(msg->len > 4); + + /* SELECT command. */ + mxs_i2c_pio_trigger_write_cmd(i2c, MXS_CMD_I2C_SELECT, + addr_data); + + ret = mxs_i2c_pio_wait_xfer_end(i2c); + if (ret) { + dev_dbg(i2c->dev, + "PIO: Failed to send SELECT command!\n"); + goto cleanup; + } + + /* READ command. */ + mxs_i2c_pio_trigger_cmd(i2c, + MXS_CMD_I2C_READ | flags | + MXS_I2C_CTRL0_XFER_COUNT(msg->len)); + + ret = mxs_i2c_pio_wait_xfer_end(i2c); + if (ret) { + dev_dbg(i2c->dev, + "PIO: Failed to send READ command!\n"); + goto cleanup; + } + + data = readl(i2c->regs + MXS_I2C_DATA(i2c)); + for (i = 0; i < msg->len; i++) { + msg->buf[i] = data & 0xff; + data >>= 8; + } + } else { + /* + * PIO WRITE transfer: + * + * The code below implements clock stretching to circumvent + * the possibility of kernel not being able to supply data + * fast enough. It is possible to transfer arbitrary amount + * of data using PIO write. + */ + + /* + * The LSB of data buffer is the first byte blasted across + * the bus. Higher order bytes follow. Thus the following + * filling schematic. + */ + + data = addr_data << 24; + + /* Start the transfer with START condition. */ + start = MXS_I2C_CTRL0_PRE_SEND_START; + + /* If the transfer is long, use clock stretching. */ + if (msg->len > 3) + start |= MXS_I2C_CTRL0_RETAIN_CLOCK; + + for (i = 0; i < msg->len; i++) { + data >>= 8; + data |= (msg->buf[i] << 24); + + xmit = 0; + + /* This is the last transfer of the message. */ + if (i + 1 == msg->len) { + /* Add optional STOP flag. */ + start |= flags; + /* Remove RETAIN_CLOCK bit. */ + start &= ~MXS_I2C_CTRL0_RETAIN_CLOCK; + xmit = 1; + } + + /* Four bytes are ready in the "data" variable. */ + if ((i & 3) == 2) + xmit = 1; + + /* Nothing interesting happened, continue stuffing. */ + if (!xmit) + continue; + + /* + * Compute the size of the transfer and shift the + * data accordingly. + * + * i = (4k + 0) .... xlen = 2 + * i = (4k + 1) .... xlen = 3 + * i = (4k + 2) .... xlen = 4 + * i = (4k + 3) .... xlen = 1 + */ + + if ((i % 4) == 3) + xlen = 1; + else + xlen = (i % 4) + 2; + + data >>= (4 - xlen) * 8; + + dev_dbg(i2c->dev, + "PIO: len=%i pos=%i total=%i [W%s%s%s]\n", + xlen, i, msg->len, + start & MXS_I2C_CTRL0_PRE_SEND_START ? "S" : "", + start & MXS_I2C_CTRL0_POST_SEND_STOP ? "E" : "", + start & MXS_I2C_CTRL0_RETAIN_CLOCK ? "C" : ""); + + writel(MXS_I2C_DEBUG0_DMAREQ, + i2c->regs + MXS_I2C_DEBUG0_CLR(i2c)); + + mxs_i2c_pio_trigger_write_cmd(i2c, + start | MXS_I2C_CTRL0_MASTER_MODE | + MXS_I2C_CTRL0_DIRECTION | + MXS_I2C_CTRL0_XFER_COUNT(xlen), data); + + /* The START condition is sent only once. */ + start &= ~MXS_I2C_CTRL0_PRE_SEND_START; + + /* Wait for the end of the transfer. */ + ret = mxs_i2c_pio_wait_xfer_end(i2c); + if (ret) { + dev_dbg(i2c->dev, + "PIO: Failed to finish WRITE cmd!\n"); + break; + } + + /* Check NAK here. */ + ret = readl(i2c->regs + MXS_I2C_STAT) & + MXS_I2C_STAT_GOT_A_NAK; + if (ret) { + ret = -ENXIO; + goto cleanup; + } + } + } + + /* make sure we capture any occurred error into cmd_err */ + ret = mxs_i2c_pio_check_error_state(i2c); + +cleanup: + /* Clear any dangling IRQs and re-enable interrupts. */ + writel(MXS_I2C_IRQ_MASK, i2c->regs + MXS_I2C_CTRL1_CLR); + writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); + + /* Clear the PIO_MODE on i.MX23 */ + if (i2c->dev_type == MXS_I2C_V1) + writel(MXS_I2C_CTRL0_PIO_MODE, i2c->regs + MXS_I2C_CTRL0_CLR); + + return ret; +} + +/* + * Low level master read/write transaction. + */ +static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, + int stop) +{ + struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap); + int ret; + int flags; + u8 *dma_buf; + int use_pio = 0; + unsigned long time_left; + + flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0; + + dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + /* + * The MX28 I2C IP block can only do PIO READ for transfer of to up + * 4 bytes of length. The write transfer is not limited as it can use + * clock stretching to avoid FIFO underruns. + */ + if ((msg->flags & I2C_M_RD) && (msg->len <= 4)) + use_pio = 1; + if (!(msg->flags & I2C_M_RD) && (msg->len < 7)) + use_pio = 1; + + i2c->cmd_err = 0; + if (use_pio) { + ret = mxs_i2c_pio_setup_xfer(adap, msg, flags); + /* No need to reset the block if NAK was received. */ + 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, 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; + + ret = i2c->cmd_err; + } + + if (ret == -ENXIO) { + /* + * If the transfer fails with a NAK from the slave the + * controller halts until it gets told to return to idle state. + */ + writel(MXS_I2C_CTRL1_CLR_GOT_A_NAK, + i2c->regs + MXS_I2C_CTRL1_SET); + } + + /* + * WARNING! + * The i.MX23 is strange. After each and every operation, it's I2C IP + * block must be reset, otherwise the IP block will misbehave. This can + * be observed on the bus by the block sending out one single byte onto + * the bus. In case such an error happens, bit 27 will be set in the + * DEBUG0 register. This bit is not documented in the i.MX23 datasheet + * and is marked as "TBD" instead. To reset this bit to a correct state, + * reset the whole block. Since the block reset does not take long, do + * reset the block after every transfer to play safe. + */ + if (i2c->dev_type == MXS_I2C_V1) + mxs_i2c_reset(i2c); + + dev_dbg(i2c->dev, "Done with err=%d\n", ret); + + return ret; + +timeout: + dev_dbg(i2c->dev, "Timeout!\n"); + mxs_i2c_dma_finish(i2c); + ret = mxs_i2c_reset(i2c); + if (ret) + return ret; + + return -ETIMEDOUT; +} + +static int mxs_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + int i; + int err; + + for (i = 0; i < num; i++) { + err = mxs_i2c_xfer_msg(adap, &msgs[i], i == (num - 1)); + if (err) + return err; + } + + return num; +} + +static u32 mxs_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id) +{ + struct mxs_i2c_dev *i2c = dev_id; + u32 stat = readl(i2c->regs + MXS_I2C_CTRL1) & MXS_I2C_IRQ_MASK; + + if (!stat) + return IRQ_NONE; + + if (stat & MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ) + i2c->cmd_err = -ENXIO; + else if (stat & (MXS_I2C_CTRL1_EARLY_TERM_IRQ | + MXS_I2C_CTRL1_MASTER_LOSS_IRQ | + MXS_I2C_CTRL1_SLAVE_STOP_IRQ | MXS_I2C_CTRL1_SLAVE_IRQ)) + /* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */ + i2c->cmd_err = -EIO; + + writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR); + + return IRQ_HANDLED; +} + +static const struct i2c_algorithm mxs_i2c_algo = { + .xfer = mxs_i2c_xfer, + .functionality = mxs_i2c_func, +}; + +static const struct i2c_adapter_quirks mxs_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed) +{ + /* The I2C block clock runs at 24MHz */ + const uint32_t clk = 24000000; + uint32_t divider; + uint16_t high_count, low_count, rcv_count, xmit_count; + uint32_t bus_free, leadin; + struct device *dev = i2c->dev; + + divider = DIV_ROUND_UP(clk, speed); + + if (divider < 25) { + /* + * limit the divider, so that min(low_count, high_count) + * is >= 1 + */ + divider = 25; + dev_warn(dev, + "Speed too high (%u.%03u kHz), using %u.%03u kHz\n", + speed / 1000, speed % 1000, + clk / divider / 1000, clk / divider % 1000); + } else if (divider > 1897) { + /* + * limit the divider, so that max(low_count, high_count) + * cannot exceed 1023 + */ + divider = 1897; + dev_warn(dev, + "Speed too low (%u.%03u kHz), using %u.%03u kHz\n", + speed / 1000, speed % 1000, + clk / divider / 1000, clk / divider % 1000); + } + + /* + * The I2C spec specifies the following timing data: + * standard mode fast mode Bitfield name + * tLOW (SCL LOW period) 4700 ns 1300 ns + * tHIGH (SCL HIGH period) 4000 ns 600 ns + * tSU;DAT (data setup time) 250 ns 100 ns + * tHD;STA (START hold time) 4000 ns 600 ns + * tBUF (bus free time) 4700 ns 1300 ns + * + * The hardware (of the i.MX28 at least) seems to add 2 additional + * clock cycles to the low_count and 7 cycles to the high_count. + * This is compensated for by subtracting the respective constants + * from the values written to the timing registers. + */ + 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)); + leadin = DIV_ROUND_UP(600 * (clk / 1000000), 1000); + bus_free = DIV_ROUND_UP(1300 * (clk / 1000000), 1000); + } else { + /* normal mode */ + low_count = DIV_ROUND_CLOSEST(divider * 47, (47 + 40)); + high_count = DIV_ROUND_CLOSEST(divider * 40, (47 + 40)); + leadin = DIV_ROUND_UP(4700 * (clk / 1000000), 1000); + bus_free = DIV_ROUND_UP(4700 * (clk / 1000000), 1000); + } + rcv_count = high_count * 3 / 8; + xmit_count = low_count * 3 / 8; + + dev_dbg(dev, + "speed=%u(actual %u) divider=%u low=%u high=%u xmit=%u rcv=%u leadin=%u bus_free=%u\n", + speed, clk / divider, divider, low_count, high_count, + xmit_count, rcv_count, leadin, bus_free); + + low_count -= 2; + high_count -= 7; + i2c->timing0 = (high_count << 16) | rcv_count; + i2c->timing1 = (low_count << 16) | xmit_count; + i2c->timing2 = (bus_free << 16 | leadin); +} + +static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) +{ + uint32_t speed; + struct device *dev = i2c->dev; + struct device_node *node = dev->of_node; + int ret; + + ret = of_property_read_u32(node, "clock-frequency", &speed); + if (ret) { + dev_warn(dev, "No I2C speed selected, using 100kHz\n"); + speed = I2C_MAX_STANDARD_MODE_FREQ; + } + + mxs_i2c_derive_timing(i2c, speed); + + return 0; +} + +static const struct of_device_id mxs_i2c_dt_ids[] = { + { .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) +{ + struct device *dev = &pdev->dev; + struct mxs_i2c_dev *i2c; + struct i2c_adapter *adap; + int err, irq; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->dev_type = (uintptr_t)of_device_get_match_data(&pdev->dev); + + 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) + return irq; + + err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c); + if (err) + return err; + + i2c->dev = dev; + + init_completion(&i2c->cmd_complete); + + if (dev->of_node) { + err = mxs_i2c_get_ofdata(i2c); + if (err) + return err; + } + + /* Setup the DMA */ + i2c->dmach = devm_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); + + /* Do reset to enforce correct startup after pinmuxing */ + err = mxs_i2c_reset(i2c); + if (err) + return err; + + adap = &i2c->adapter; + strscpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &mxs_i2c_algo; + adap->quirks = &mxs_i2c_quirks; + adap->dev.parent = dev; + adap->nr = pdev->id; + adap->dev.of_node = pdev->dev.of_node; + i2c_set_adapdata(adap, i2c); + err = i2c_add_numbered_adapter(adap); + if (err) { + writel(MXS_I2C_CTRL0_SFTRST, + i2c->regs + MXS_I2C_CTRL0_SET); + return err; + } + + return 0; +} + +static void mxs_i2c_remove(struct platform_device *pdev) +{ + struct mxs_i2c_dev *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adapter); + + writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); +} + +static struct platform_driver mxs_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = mxs_i2c_dt_ids, + }, + .probe = mxs_i2c_probe, + .remove = mxs_i2c_remove, +}; + +static int __init mxs_i2c_init(void) +{ + return platform_driver_register(&mxs_i2c_driver); +} +subsys_initcall(mxs_i2c_init); + +static void __exit mxs_i2c_exit(void) +{ + platform_driver_unregister(&mxs_i2c_driver); +} +module_exit(mxs_i2c_exit); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("MXS I2C Bus Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c new file mode 100644 index 000000000..1413ab6f9 --- /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 +#include +#include +#include +#include +#include + +/* + * 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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nct6694-i2c"); diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c new file mode 100644 index 000000000..7064fab81 --- /dev/null +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + SMBus driver for nVidia nForce2 MCP + + Added nForce3 Pro 150 Thomas Leibold , + Ported to 2.5 Patrick Dreker , + Copyright (c) 2003 Hans-Frieder Vogt , + Based on + SMBus 2.0 driver for AMD-8111 IO-Hub + Copyright (c) 2002 Vojtech Pavlik + +*/ + +/* + SUPPORTED DEVICES PCI ID + nForce2 MCP 0064 + nForce2 Ultra 400 MCP 0084 + nForce3 Pro150 MCP 00D4 + nForce3 250Gb MCP 00E4 + nForce4 MCP 0052 + nForce4 MCP-04 0034 + nForce MCP51 0264 + nForce MCP55 0368 + nForce MCP61 03EB + nForce MCP65 0446 + nForce MCP67 0542 + nForce MCP73 07D8 + nForce MCP78S 0752 + nForce MCP79 0AA2 + + This driver supports the 2 SMBuses that are included in the MCP of the + nForce2/3/4/5xx chipsets. +*/ + +/* Note: we assume there can only be one nForce2, with two SMBus interfaces */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_DESCRIPTION("nForce2/3/4/5xx SMBus driver"); + + +struct nforce2_smbus { + struct i2c_adapter adapter; + int base; + int size; + int blockops; + int can_abort; +}; + + +/* + * nVidia nForce2 SMBus control register definitions + * (Newer incarnations use standard BARs 4 and 5 instead) + */ +#define NFORCE_PCI_SMB1 0x50 +#define NFORCE_PCI_SMB2 0x54 + + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ +#define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */ +#define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */ +#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */ +#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */ +#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */ +#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data + bytes */ +#define NVIDIA_SMB_STATUS_ABRT (smbus->base + 0x3c) /* register used to + check the status of + the abort command */ +#define NVIDIA_SMB_CTRL (smbus->base + 0x3e) /* control register */ + +#define NVIDIA_SMB_STATUS_ABRT_STS 0x01 /* Bit to notify that + abort succeeded */ +#define NVIDIA_SMB_CTRL_ABORT 0x20 +#define NVIDIA_SMB_STS_DONE 0x80 +#define NVIDIA_SMB_STS_ALRM 0x40 +#define NVIDIA_SMB_STS_RES 0x20 +#define NVIDIA_SMB_STS_STATUS 0x1f + +#define NVIDIA_SMB_PRTCL_WRITE 0x00 +#define NVIDIA_SMB_PRTCL_READ 0x01 +#define NVIDIA_SMB_PRTCL_QUICK 0x02 +#define NVIDIA_SMB_PRTCL_BYTE 0x04 +#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06 +#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08 +#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a +#define NVIDIA_SMB_PRTCL_PEC 0x80 + +/* Misc definitions */ +#define MAX_TIMEOUT 100 + +/* We disable the second SMBus channel on these boards */ +static const struct dmi_system_id nforce2_dmi_blacklist2[] = { + { + .ident = "DFI Lanparty NF4 Expert", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "DFI Corp,LTD"), + DMI_MATCH(DMI_BOARD_NAME, "LP UT NF4 Expert"), + }, + }, + { } +}; + +static struct pci_driver nforce2_driver; + +static void nforce2_abort(struct i2c_adapter *adap) +{ + struct nforce2_smbus *smbus = adap->algo_data; + int timeout = 0; + unsigned char temp; + + dev_dbg(&adap->dev, "Aborting current transaction\n"); + + outb_p(NVIDIA_SMB_CTRL_ABORT, NVIDIA_SMB_CTRL); + do { + msleep(1); + temp = inb_p(NVIDIA_SMB_STATUS_ABRT); + } while (!(temp & NVIDIA_SMB_STATUS_ABRT_STS) && + (timeout++ < MAX_TIMEOUT)); + if (!(temp & NVIDIA_SMB_STATUS_ABRT_STS)) + dev_err(&adap->dev, "Can't reset the smbus\n"); + outb_p(NVIDIA_SMB_STATUS_ABRT_STS, NVIDIA_SMB_STATUS_ABRT); +} + +static int nforce2_check_status(struct i2c_adapter *adap) +{ + struct nforce2_smbus *smbus = adap->algo_data; + int timeout = 0; + unsigned char temp; + + do { + msleep(1); + temp = inb_p(NVIDIA_SMB_STS); + } while ((!temp) && (timeout++ < MAX_TIMEOUT)); + + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + if (smbus->can_abort) + nforce2_abort(adap); + return -ETIMEDOUT; + } + if (!(temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) { + dev_dbg(&adap->dev, "Transaction failed (0x%02x)!\n", temp); + return -EIO; + } + return 0; +} + +/* Return negative errno on error */ +static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct nforce2_smbus *smbus = adap->algo_data; + unsigned char protocol, pec; + u8 len; + int i, status; + + protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : + NVIDIA_SMB_PRTCL_WRITE; + pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0; + + switch (size) { + case I2C_SMBUS_QUICK: + protocol |= NVIDIA_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, NVIDIA_SMB_CMD); + protocol |= NVIDIA_SMB_PRTCL_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, NVIDIA_SMB_DATA); + protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA; + break; + + case I2C_SMBUS_WORD_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word, NVIDIA_SMB_DATA); + outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1); + } + protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec; + break; + + case I2C_SMBUS_BLOCK_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + dev_err(&adap->dev, + "Transaction failed (requested block size: %d)\n", + len); + return -EINVAL; + } + outb_p(len, NVIDIA_SMB_BCNT); + for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++) + outb_p(data->block[i + 1], + NVIDIA_SMB_DATA + i); + } + protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec; + break; + + default: + dev_err(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR); + outb_p(protocol, NVIDIA_SMB_PRTCL); + + status = nforce2_check_status(adap); + if (status) + return status; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = inb_p(NVIDIA_SMB_DATA); + break; + + case I2C_SMBUS_WORD_DATA: + data->word = inb_p(NVIDIA_SMB_DATA) | + (inb_p(NVIDIA_SMB_DATA + 1) << 8); + break; + + case I2C_SMBUS_BLOCK_DATA: + len = inb_p(NVIDIA_SMB_BCNT); + if ((len <= 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + dev_err(&adap->dev, + "Transaction failed (received block size: 0x%02x)\n", + len); + return -EPROTO; + } + for (i = 0; i < len; i++) + data->block[i + 1] = inb_p(NVIDIA_SMB_DATA + i); + data->block[0] = len; + break; + } + + return 0; +} + + +static u32 nforce2_func(struct i2c_adapter *adapter) +{ + /* other functionality might be possible, but is not tested */ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PEC | + (((struct nforce2_smbus *)adapter->algo_data)->blockops ? + I2C_FUNC_SMBUS_BLOCK_DATA : 0); +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = nforce2_access, + .functionality = nforce2_func, +}; + + +static const struct pci_device_id nforce2_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, nforce2_ids); + + +static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg, + struct nforce2_smbus *smbus, const char *name) +{ + int error; + + smbus->base = pci_resource_start(dev, bar); + if (smbus->base) { + smbus->size = pci_resource_len(dev, bar); + } else { + /* Older incarnations of the device used non-standard BARs */ + u16 iobase; + + 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; + } + + smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK; + smbus->size = 64; + } + + error = acpi_check_region(smbus->base, smbus->size, + nforce2_driver.name); + if (error) + return error; + + if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { + dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", + smbus->base, smbus->base+smbus->size-1, name); + return -EBUSY; + } + smbus->adapter.owner = THIS_MODULE; + smbus->adapter.class = I2C_CLASS_HWMON; + smbus->adapter.algo = &smbus_algorithm; + smbus->adapter.algo_data = smbus; + smbus->adapter.dev.parent = &dev->dev; + snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), + "SMBus nForce2 adapter at %04x", smbus->base); + + error = i2c_add_adapter(&smbus->adapter); + if (error) { + release_region(smbus->base, smbus->size); + return error; + } + dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", + smbus->base); + return 0; +} + + +static int nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct nforce2_smbus *smbuses; + int res1, res2; + + /* we support 2 SMBus adapters */ + smbuses = kzalloc_objs(struct nforce2_smbus, 2); + if (!smbuses) + return -ENOMEM; + pci_set_drvdata(dev, smbuses); + + switch (dev->device) { + case PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS: + case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS: + case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS: + smbuses[0].blockops = 1; + smbuses[1].blockops = 1; + smbuses[0].can_abort = 1; + smbuses[1].can_abort = 1; + } + + /* SMBus adapter 1 */ + res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); + if (res1 < 0) + smbuses[0].base = 0; /* to have a check value */ + + /* SMBus adapter 2 */ + if (dmi_check_system(nforce2_dmi_blacklist2)) { + dev_err(&dev->dev, "Disabling SMB2 for safety reasons.\n"); + res2 = -EPERM; + smbuses[1].base = 0; + } else { + res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], + "SMB2"); + if (res2 < 0) + smbuses[1].base = 0; /* to have a check value */ + } + + if ((res1 < 0) && (res2 < 0)) { + /* we did not find even one of the SMBuses, so we give up */ + kfree(smbuses); + return -ENODEV; + } + + return 0; +} + + +static void nforce2_remove(struct pci_dev *dev) +{ + struct nforce2_smbus *smbuses = pci_get_drvdata(dev); + + if (smbuses[0].base) { + i2c_del_adapter(&smbuses[0].adapter); + release_region(smbuses[0].base, smbuses[0].size); + } + if (smbuses[1].base) { + i2c_del_adapter(&smbuses[1].adapter); + release_region(smbuses[1].base, smbuses[1].size); + } + kfree(smbuses); +} + +static struct pci_driver nforce2_driver = { + .name = "nForce2_smbus", + .id_table = nforce2_ids, + .probe = nforce2_probe, + .remove = nforce2_remove, +}; + +module_pci_driver(nforce2_driver); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c new file mode 100644 index 000000000..c3878670d --- /dev/null +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -0,0 +1,1238 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2009 ST-Ericsson SA + * Copyright (C) 2009 STMicroelectronics + * + * 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 + * Author: Sachin Verma + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "nmk-i2c" + +/* I2C Controller register offsets */ +#define I2C_CR (0x000) +#define I2C_SCR (0x004) +#define I2C_HSMCR (0x008) +#define I2C_MCR (0x00C) +#define I2C_TFR (0x010) +#define I2C_SR (0x014) +#define I2C_RFR (0x018) +#define I2C_TFTR (0x01C) +#define I2C_RFTR (0x020) +#define I2C_DMAR (0x024) +#define I2C_BRCR (0x028) +#define I2C_IMSCR (0x02C) +#define I2C_RISR (0x030) +#define I2C_MISR (0x034) +#define I2C_ICR (0x038) + +/* Control registers */ +#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 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 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 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 + +/* maximum threshold value */ +#define MAX_I2C_FIFO_THRESHOLD 15 + +enum i2c_freq_mode { + I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ + I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ + I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ + 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 + * @fifodepth: variant FIFO depth + */ +struct i2c_vendor_data { + bool has_mtdws; + u32 fifodepth; +}; + +enum i2c_status { + I2C_NOP, + I2C_ON_GOING, + I2C_OK, + I2C_ABORT +}; + +/* operation */ +enum i2c_operation { + I2C_NO_OPERATION = 0xff, + I2C_WRITE = 0x00, + 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 + * @count: no. bytes to be transferred + * @buffer: client data buffer + * @xfer_bytes: bytes transferred till now + * @operation: current I2C operation + */ +struct i2c_nmk_client { + unsigned short slave_adr; + unsigned long count; + unsigned char *buffer; + unsigned long xfer_bytes; + enum i2c_operation operation; +}; + +/** + * struct nmk_i2c_dev - private data structure of the controller. + * @vendor: vendor data for this variant. + * @adev: parent amba device. + * @adap: corresponding I2C adapter. + * @irq: interrupt line for the controller. + * @virtbase: virtual io memory area. + * @clk: hardware i2c block clock. + * @cli: holder of client specific data. + * @clk_freq: clock frequency for the operation mode + * @tft: Tx FIFO Threshold in bytes + * @rft: Rx FIFO Threshold in bytes + * @timeout_usecs: Slave response timeout + * @sm: speed mode + * @stop: stop condition. + * @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; + struct amba_device *adev; + struct i2c_adapter adap; + int irq; + void __iomem *virtbase; + struct clk *clk; + struct i2c_nmk_client cli; + u32 clk_freq; + unsigned char tft; + unsigned char rft; + u32 timeout_usecs; + enum i2c_freq_mode sm; + int stop; + struct wait_queue_head xfer_wq; + bool xfer_done; + int result; + bool has_32b_bus; +}; + +/* controller's abort causes */ +static const char *abort_causes[] = { + "no ack received after address transmission", + "no ack received during data phase", + "ack received after xmission of master code", + "master lost arbitration", + "slave restarts", + "slave reset", + "overflow, maxsize is 2047 bytes", +}; + +static inline void i2c_set_bit(void __iomem *reg, u32 mask) +{ + writel(readl(reg) | mask, reg); +} + +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 + * @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 *priv) +{ +#define LOOP_ATTEMPTS 10 + ktime_t timeout; + int i; + + /* + * flush the transmit and receive FIFO. The flushing + * operation takes several cycles before to be completed. + * On the completion, the I2C internal logic clears these + * 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), priv->virtbase + I2C_CR); + + for (i = 0; i < LOOP_ATTEMPTS; i++) { + timeout = ktime_add_us(ktime_get(), priv->timeout_usecs); + + while (ktime_after(timeout, ktime_get())) { + if ((readl(priv->virtbase + I2C_CR) & + (I2C_CR_FTX | I2C_CR_FRX)) == 0) + return 0; + } + } + + dev_err(&priv->adev->dev, + "flushing operation timed out giving up after %d attempts", + LOOP_ATTEMPTS); + + return -ETIMEDOUT; +} + +/** + * disable_all_interrupts() - Disable all interrupts of this I2c Bus + * @priv: private data of I2C Driver + */ +static void disable_all_interrupts(struct nmk_i2c_dev *priv) +{ + writel(0, priv->virtbase + I2C_IMSCR); +} + +/** + * clear_all_interrupts() - Clear all interrupts of I2C Controller + * @priv: private data of I2C Driver + */ +static void clear_all_interrupts(struct nmk_i2c_dev *priv) +{ + writel(I2C_CLEAR_ALL_INTS, priv->virtbase + I2C_ICR); +} + +/** + * init_hw() - initialize the I2C hardware + * @priv: private data of I2C Driver + */ +static int init_hw(struct nmk_i2c_dev *priv) +{ + int stat; + + stat = flush_i2c_fifo(priv); + if (stat) + goto exit; + + /* disable the controller */ + i2c_clr_bit(priv->virtbase + I2C_CR, I2C_CR_PE); + + disable_all_interrupts(priv); + + clear_all_interrupts(priv); + + priv->cli.operation = I2C_NO_OPERATION; + +exit: + return stat; +} + +/* enable peripheral, master mode operation */ +#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 + * @priv: private data of controller + * @flags: message flags + */ +static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *priv, u16 flags) +{ + u32 mcr = 0; + unsigned short slave_adr_3msb_bits; + + mcr |= FIELD_PREP(I2C_MCR_A7, priv->cli.slave_adr); + + if (unlikely(flags & I2C_M_TEN)) { + /* 10-bit address transaction */ + 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 = FIELD_GET(ADR_3MSB_BITS, + priv->cli.slave_adr); + + mcr |= FIELD_PREP(I2C_MCR_EA10, slave_adr_3msb_bits); + } else { + /* 7-bit address transaction */ + mcr |= FIELD_PREP(I2C_MCR_AM, 1); + } + + /* start byte procedure not applied */ + mcr |= FIELD_PREP(I2C_MCR_SB, 0); + + /* check the operation, master read/write? */ + if (priv->cli.operation == I2C_WRITE) + mcr |= FIELD_PREP(I2C_MCR_OP, I2C_WRITE); + else + mcr |= FIELD_PREP(I2C_MCR_OP, I2C_READ); + + /* stop or repeated start? */ + if (priv->stop) + mcr |= FIELD_PREP(I2C_MCR_STOP, 1); + else + mcr &= ~FIELD_PREP(I2C_MCR_STOP, 1); + + mcr |= FIELD_PREP(I2C_MCR_LENGTH, priv->cli.count); + + return mcr; +} + +/** + * setup_i2c_controller() - setup the controller + * @priv: private data of controller + */ +static void setup_i2c_controller(struct nmk_i2c_dev *priv) +{ + u32 brcr; + u32 i2c_clk, div; + u32 ns; + u16 slsu; + + 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(priv->clk); + + /* + * set the slsu: + * + * slsu defines the data setup time after SCL clock + * stretching in terms of i2c clk cycles + 1 (zero means + * "wait one cycle"), the needed setup time for the three + * modes are 250ns, 100ns, 10ns respectively. + * + * As the time for one cycle T in nanoseconds is + * T = (1/f) * HZ_PER_GHZ => + * slsu = cycles / (HZ_PER_GHZ / f) + 1 + */ + ns = DIV_ROUND_UP(HZ_PER_GHZ, i2c_clk); + switch (priv->sm) { + case I2C_FREQ_MODE_FAST: + case I2C_FREQ_MODE_FAST_PLUS: + slsu = DIV_ROUND_UP(100, ns); /* Fast */ + break; + case I2C_FREQ_MODE_HIGH_SPEED: + slsu = DIV_ROUND_UP(10, ns); /* High */ + break; + case I2C_FREQ_MODE_STANDARD: + default: + slsu = DIV_ROUND_UP(250, ns); /* Standard */ + break; + } + slsu += 1; + + 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. + */ + 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. + * + * 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). + */ + 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(brcr, priv->virtbase + I2C_BRCR); + + /* set the speed mode */ + writel(FIELD_PREP(I2C_CR_SM, priv->sm), priv->virtbase + I2C_CR); + + /* set the Tx and Rx FIFO threshold */ + 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 + * @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 *priv, u16 flags) +{ + u32 mcr, irq_mask; + int status = 0; + bool xfer_done; + + mcr = load_i2c_mcr_reg(priv, flags); + writel(mcr, priv->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(priv->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + priv->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(priv->virtbase + I2C_CR, I2C_CR_PE); + + 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 (priv->stop || !priv->vendor->has_mtdws) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask &= I2C_CLEAR_ALL_INTS; + + writel(readl(priv->virtbase + I2C_IMSCR) | irq_mask, + priv->virtbase + I2C_IMSCR); + + xfer_done = nmk_i2c_wait_xfer_done(priv); + + if (!xfer_done) + status = -ETIMEDOUT; + + return status; +} + +static void fill_tx_fifo(struct nmk_i2c_dev *priv, int no_bytes) +{ + int count; + + for (count = (no_bytes - 2); + (count > 0) && + (priv->cli.count != 0); + count--) { + /* write to the Tx FIFO */ + 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. + * @priv: private data of I2C Driver + * @flags: message flags + * + * This function writes data to I2C client + */ +static int write_i2c(struct nmk_i2c_dev *priv, u16 flags) +{ + u32 mcr, irq_mask; + u32 status = 0; + bool xfer_done; + + mcr = load_i2c_mcr_reg(priv, flags); + + writel(mcr, priv->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(priv->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + priv->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(priv->virtbase + I2C_CR, I2C_CR_PE); + + 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(priv, MAX_I2C_FIFO_THRESHOLD); + + if (priv->cli.count != 0) + irq_mask |= I2C_IT_TXFNE; + + /* + * check if we want to transfer a single or multiple bytes, if so + * set the MTDWS bit (Master Transaction Done Without Stop) + * to start repeated start operation + */ + if (priv->stop || !priv->vendor->has_mtdws) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask &= I2C_CLEAR_ALL_INTS; + + writel(readl(priv->virtbase + I2C_IMSCR) | irq_mask, + priv->virtbase + I2C_IMSCR); + + xfer_done = nmk_i2c_wait_xfer_done(priv); + + if (!xfer_done) { + /* Controller timed out */ + dev_err(&priv->adev->dev, "write to slave 0x%x timed out\n", + priv->cli.slave_adr); + status = -ETIMEDOUT; + } + + return status; +} + +/** + * nmk_i2c_xfer_one() - transmit a single I2C message + * @priv: device with a message encoded into it + * @flags: message flags + */ +static int nmk_i2c_xfer_one(struct nmk_i2c_dev *priv, u16 flags) +{ + int status; + + if (flags & I2C_M_RD) { + /* read operation */ + priv->cli.operation = I2C_READ; + status = read_i2c(priv, flags); + } else { + /* write operation */ + priv->cli.operation = I2C_WRITE; + status = write_i2c(priv, flags); + } + + if (status || priv->result) { + u32 i2c_sr; + u32 cause; + + 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]); + } + + init_hw(priv); + + status = status ? status : priv->result; + } + + return status; +} + +/** + * nmk_i2c_xfer() - I2C transfer function used by kernel framework + * @i2c_adap: Adapter pointer to the controller + * @msgs: Pointer to data to be written. + * @num_msgs: Number of messages to be executed + * + * This is the function called by the generic kernel i2c_transfer() + * or i2c_smbus...() API calls. Note that this code is protected by the + * semaphore set in the kernel i2c_transfer() function. + * + * NOTE: + * READ TRANSFER : We impose a restriction of the first message to be the + * index message for any read transaction. + * - a no index is coded as '0', + * - 2byte big endian index is coded as '3' + * !!! msg[0].buf holds the actual index. + * This is compatible with generic messages of smbus emulator + * that send a one byte index. + * eg. a I2C transation to read 2 bytes from index 0 + * idx = 0; + * msg[0].addr = client->addr; + * msg[0].flags = 0x0; + * msg[0].len = 1; + * msg[0].buf = &idx; + * + * msg[1].addr = client->addr; + * msg[1].flags = I2C_M_RD; + * msg[1].len = 2; + * msg[1].buf = rd_buff + * i2c_transfer(adap, msg, 2); + * + * WRITE TRANSFER : The I2C standard interface interprets all data as payload. + * If you want to emulate an SMBUS write transaction put the + * index as first byte(or first and second) in the payload. + * eg. a I2C transation to write 2 bytes from index 1 + * wr_buff[0] = 0x1; + * wr_buff[1] = 0x23; + * wr_buff[2] = 0x46; + * msg[0].flags = 0x0; + * msg[0].len = 3; + * msg[0].buf = wr_buff; + * i2c_transfer(adap, msg, 1); + * + * To read or write a block of data (multiple bytes) using SMBUS emulation + * please use the i2c_smbus_read_i2c_block_data() + * or i2c_smbus_write_i2c_block_data() API + */ +static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num_msgs) +{ + int status = 0; + int i; + struct nmk_i2c_dev *priv = i2c_get_adapdata(i2c_adap); + int j; + + 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(priv); + + for (i = 0; i < num_msgs; i++) { + 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(priv, msgs[i].flags); + if (status != 0) + break; + } + if (status == 0) + break; + } + + pm_runtime_put_sync(&priv->adev->dev); + + /* return the no. messages processed */ + if (status) + return status; + else + return num_msgs; +} + +/** + * disable_interrupts() - disable the interrupts + * @priv: private data of controller + * @irq: interrupt number + */ +static int disable_interrupts(struct nmk_i2c_dev *priv, u32 irq) +{ + irq &= I2C_CLEAR_ALL_INTS; + writel(readl(priv->virtbase + I2C_IMSCR) & ~irq, + priv->virtbase + I2C_IMSCR); + return 0; +} + +/** + * i2c_irq_handler() - interrupt routine + * @irq: interrupt number + * @arg: data passed to the handler + * + * This is the interrupt handler for the i2c driver. Currently + * it handles the major interrupts like Rx & Tx FIFO management + * interrupts, master transaction interrupts, arbitration and + * bus error interrupts. The rest of the interrupts are treated as + * unhandled. + */ +static irqreturn_t i2c_irq_handler(int irq, void *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(priv->virtbase + I2C_TFTR); + rft = readl(priv->virtbase + I2C_RFTR); + + /* read interrupt status register */ + misr = readl(priv->virtbase + I2C_MISR); + + src = __ffs(misr); + switch (BIT(src)) { + + /* Transmit FIFO nearly empty interrupt */ + case I2C_IT_TXFNE: + { + if (priv->cli.operation == I2C_READ) { + /* + * in read operation why do we care for writing? + * so disable the Transmit FIFO interrupt + */ + disable_interrupts(priv, I2C_IT_TXFNE); + } else { + fill_tx_fifo(priv, (MAX_I2C_FIFO_THRESHOLD - tft)); + /* + * if done, close the transfer by disabling the + * corresponding TXFNE interrupt + */ + if (priv->cli.count == 0) + disable_interrupts(priv, I2C_IT_TXFNE); + } + } + break; + + /* + * Rx FIFO nearly full interrupt. + * This is set when the numer of entries in Rx FIFO is + * greater or equal than the threshold value programmed + * in RFT + */ + case I2C_IT_RXFNF: + for (count = rft; count > 0; count--) { + /* Read the Rx FIFO */ + *priv->cli.buffer = nmk_i2c_readb(priv, I2C_RFR); + priv->cli.buffer++; + } + 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--) { + *priv->cli.buffer = nmk_i2c_readb(priv, I2C_RFR); + priv->cli.buffer++; + } + 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 (priv->cli.operation == I2C_READ) { + while (!(readl(priv->virtbase + I2C_RISR) + & I2C_IT_RXFE)) { + if (priv->cli.count == 0) + break; + *priv->cli.buffer = + nmk_i2c_readb(priv, I2C_RFR); + priv->cli.buffer++; + priv->cli.count--; + priv->cli.xfer_bytes++; + } + } + + disable_all_interrupts(priv); + clear_all_interrupts(priv); + + if (priv->cli.count) { + priv->result = -EIO; + dev_err(dev, "%lu bytes still remain to be xfered\n", + priv->cli.count); + init_hw(priv); + } + priv->xfer_done = true; + wake_up(&priv->xfer_wq); + + + break; + + /* Master Arbitration lost interrupt */ + case I2C_IT_MAL: + 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); + + + break; + + /* + * Bus Error interrupt. + * This happens when an unexpected start/stop condition occurs + * during the transaction. + */ + case I2C_IT_BERR: + { + u32 sr; + + sr = readl(priv->virtbase + I2C_SR); + priv->result = -EIO; + if (FIELD_GET(I2C_SR_STATUS, sr) == I2C_ABORT) + init_hw(priv); + + i2c_set_bit(priv->virtbase + I2C_ICR, I2C_IT_BERR); + priv->xfer_done = true; + wake_up(&priv->xfer_wq); + + } + break; + + /* + * Tx FIFO overrun interrupt. + * This is set when a write operation in Tx FIFO is performed and + * the Tx FIFO is full. + */ + case I2C_IT_TXFOVR: + priv->result = -EIO; + init_hw(priv); + + dev_err(dev, "Tx Fifo Over run\n"); + priv->xfer_done = true; + wake_up(&priv->xfer_wq); + + + break; + + /* unhandled interrupts by this driver - TODO*/ + case I2C_IT_TXFE: + case I2C_IT_TXFF: + case I2C_IT_RXFE: + case I2C_IT_RFSR: + case I2C_IT_RFSE: + case I2C_IT_WTSR: + case I2C_IT_STD: + dev_err(dev, "unhandled Interrupt\n"); + break; + default: + dev_err(dev, "spurious Interrupt..\n"); + break; + } + + return IRQ_HANDLED; +} + +static int nmk_i2c_suspend_late(struct device *dev) +{ + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + pinctrl_pm_select_sleep_state(dev); + return 0; +} + +static int nmk_i2c_resume_early(struct device *dev) +{ + return pm_runtime_force_resume(dev); +} + +static int nmk_i2c_runtime_suspend(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct nmk_i2c_dev *priv = amba_get_drvdata(adev); + + clk_disable_unprepare(priv->clk); + pinctrl_pm_select_idle_state(dev); + return 0; +} + +static int nmk_i2c_runtime_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct nmk_i2c_dev *priv = amba_get_drvdata(adev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "can't prepare_enable clock\n"); + return ret; + } + + pinctrl_pm_select_default_state(dev); + + ret = init_hw(priv); + if (ret) { + clk_disable_unprepare(priv->clk); + pinctrl_pm_select_idle_state(dev); + } + + return ret; +} + +static const struct dev_pm_ops nmk_i2c_pm = { + 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) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm nmk_i2c_algo = { + .xfer = nmk_i2c_xfer, + .functionality = nmk_i2c_functionality +}; + +static void nmk_i2c_of_probe(struct device_node *np, + 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", &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 */ + + /* Slave response timeout */ + if (!of_property_read_u32(np, "i2c-transfer-timeout-us", &timeout_usecs)) + priv->timeout_usecs = timeout_usecs; + else + 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 <= I2C_MAX_FAST_MODE_FREQ) + speed_mode = I2C_EYEQ5_SPEED_FAST; + else if (priv->clk_freq <= I2C_MAX_FAST_MODE_PLUS_FREQ) + 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) +{ + 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; + + /* + * 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 (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; + } + + 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; + } + + amba_set_drvdata(adev, priv); + + priv->virtbase = devm_ioremap(dev, adev->res.start, + resource_size(&adev->res)); + if (!priv->virtbase) + return -ENOMEM; + + priv->irq = adev->irq[0]; + ret = devm_request_irq(dev, priv->irq, i2c_irq_handler, 0, + DRIVER_NAME, priv); + 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), + "could enable i2c clock\n"); + + init_hw(priv); + + adap = &priv->adap; + adap->dev.of_node = np; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + adap->algo = &nmk_i2c_algo; + adap->timeout = usecs_to_jiffies(priv->timeout_usecs); + snprintf(adap->name, sizeof(adap->name), + "Nomadik I2C at %pR", &adev->res); + + i2c_set_adapdata(adap, priv); + + dev_info(dev, + "initialize %s on virtual base %p\n", + adap->name, priv->virtbase); + + ret = i2c_add_adapter(adap); + if (ret) + return ret; + + pm_runtime_put(dev); + + return 0; +} + +static void nmk_i2c_remove(struct amba_device *adev) +{ + struct nmk_i2c_dev *priv = amba_get_drvdata(adev); + + i2c_del_adapter(&priv->adap); + flush_i2c_fifo(priv); + disable_all_interrupts(priv); + clear_all_interrupts(priv); + /* disable the controller */ + i2c_clr_bit(priv->virtbase + I2C_CR, I2C_CR_PE); +} + +static struct i2c_vendor_data vendor_stn8815 = { + .has_mtdws = false, + .fifodepth = 16, /* Guessed from TFTR/RFTR = 7 */ +}; + +static struct i2c_vendor_data vendor_db8500 = { + .has_mtdws = true, + .fifodepth = 32, /* Guessed from TFTR/RFTR = 15 */ +}; + +static const struct amba_id nmk_i2c_ids[] = { + { + .id = 0x00180024, + .mask = 0x00ffffff, + .data = &vendor_stn8815, + }, + { + .id = 0x00380024, + .mask = 0x00ffffff, + .data = &vendor_db8500, + }, + {}, +}; + +MODULE_DEVICE_TABLE(amba, nmk_i2c_ids); + +static struct amba_driver nmk_i2c_driver = { + .drv = { + .name = DRIVER_NAME, + .pm = pm_ptr(&nmk_i2c_pm), + }, + .id_table = nmk_i2c_ids, + .probe = nmk_i2c_probe, + .remove = nmk_i2c_remove, +}; + +static int __init nmk_i2c_init(void) +{ + return amba_driver_register(&nmk_i2c_driver); +} + +static void __exit nmk_i2c_exit(void) +{ + amba_driver_unregister(&nmk_i2c_driver); +} + +subsys_initcall(nmk_i2c_init); +module_exit(nmk_i2c_exit); + +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 000000000..f667a873b --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, NULL); + 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 "); +MODULE_AUTHOR("Tali Perry "); +MODULE_AUTHOR("Tyrone Ting "); +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 new file mode 100644 index 000000000..14c059b03 --- /dev/null +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvidia GPU I2C controller Driver + * + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. + * Author: Ajay Gupta + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "i2c-ccgx-ucsi.h" + +/* I2C definitions */ +#define I2C_MST_CNTL 0x00 +#define I2C_MST_CNTL_GEN_START BIT(0) +#define I2C_MST_CNTL_GEN_STOP BIT(1) +#define I2C_MST_CNTL_CMD_READ (1 << 2) +#define I2C_MST_CNTL_CMD_WRITE (2 << 2) +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 +#define I2C_MST_CNTL_GEN_NACK BIT(28) +#define I2C_MST_CNTL_STATUS GENMASK(30, 29) +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) +#define I2C_MST_CNTL_STATUS_TIMEOUT (2 << 29) +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) +#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) + +#define I2C_MST_ADDR 0x04 + +#define I2C_MST_I2C0_TIMING 0x08 +#define I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ 0x10e +#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT 16 +#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX 255 +#define I2C_MST_I2C0_TIMING_TIMEOUT_CHECK BIT(24) + +#define I2C_MST_DATA 0x0c + +#define I2C_MST_HYBRID_PADCTL 0x20 +#define I2C_MST_HYBRID_PADCTL_MODE_I2C BIT(0) +#define I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV BIT(14) +#define I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV BIT(15) + +struct gpu_i2c_dev { + struct device *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) +{ + u32 val; + + /* enable I2C */ + val = readl(i2cd->regs + I2C_MST_HYBRID_PADCTL); + val |= I2C_MST_HYBRID_PADCTL_MODE_I2C | + I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | + I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV; + writel(val, i2cd->regs + I2C_MST_HYBRID_PADCTL); + + /* enable 100KHZ mode */ + val = I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ; + val |= (I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX + << I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT); + val |= I2C_MST_I2C0_TIMING_TIMEOUT_CHECK; + writel(val, i2cd->regs + I2C_MST_I2C0_TIMING); +} + +static int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd) +{ + u32 val; + int ret; + + 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; + } + + val = readl(i2cd->regs + I2C_MST_CNTL); + switch (val & I2C_MST_CNTL_STATUS) { + case I2C_MST_CNTL_STATUS_OKAY: + return 0; + case I2C_MST_CNTL_STATUS_NO_ACK: + return -ENXIO; + case I2C_MST_CNTL_STATUS_TIMEOUT: + return -ETIMEDOUT; + default: + return 0; + } +} + +static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len) +{ + int status; + u32 val; + + val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_READ | + (len << I2C_MST_CNTL_BURST_SIZE_SHIFT) | + I2C_MST_CNTL_CYCLE_TRIGGER | I2C_MST_CNTL_GEN_NACK; + writel(val, i2cd->regs + I2C_MST_CNTL); + + status = gpu_i2c_check_status(i2cd); + if (status < 0) + return status; + + val = readl(i2cd->regs + I2C_MST_DATA); + switch (len) { + case 1: + data[0] = val; + break; + case 2: + put_unaligned_be16(val, data); + break; + case 3: + put_unaligned_be24(val, data); + break; + case 4: + put_unaligned_be32(val, data); + break; + default: + break; + } + return status; +} + +static int gpu_i2c_start(struct gpu_i2c_dev *i2cd) +{ + writel(I2C_MST_CNTL_GEN_START, i2cd->regs + I2C_MST_CNTL); + return gpu_i2c_check_status(i2cd); +} + +static int gpu_i2c_stop(struct gpu_i2c_dev *i2cd) +{ + writel(I2C_MST_CNTL_GEN_STOP, i2cd->regs + I2C_MST_CNTL); + return gpu_i2c_check_status(i2cd); +} + +static int gpu_i2c_write(struct gpu_i2c_dev *i2cd, u8 data) +{ + u32 val; + + writel(data, i2cd->regs + I2C_MST_DATA); + + val = I2C_MST_CNTL_CMD_WRITE | (1 << I2C_MST_CNTL_BURST_SIZE_SHIFT); + writel(val, i2cd->regs + I2C_MST_CNTL); + + return gpu_i2c_check_status(i2cd); +} + +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 */ + writel(msgs[i].addr, i2cd->regs + I2C_MST_ADDR); + /* gpu_i2c_read has implicit start */ + status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); + if (status < 0) + goto exit; + } else { + u8 addr = i2c_8bit_addr_from_msg(msgs + i); + + status = gpu_i2c_start(i2cd); + if (status < 0) { + if (i == 0) + send_stop = false; + goto exit; + } + + status = gpu_i2c_write(i2cd, addr); + if (status < 0) + goto exit; + + for (j = 0; j < msgs[i].len; j++) { + status = gpu_i2c_write(i2cd, msgs[i].buf[j]); + if (status < 0) + goto exit; + } + } + } + send_stop = false; + status = gpu_i2c_stop(i2cd); + if (status < 0) + 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; +} + +static const struct i2c_adapter_quirks gpu_i2c_quirks = { + .max_read_len = 4, + .max_comb_2nd_msg_len = 4, + .flags = I2C_AQ_COMB_WRITE_THEN_READ, +}; + +static u32 gpu_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm gpu_i2c_algorithm = { + .xfer = gpu_i2c_xfer, + .functionality = gpu_i2c_functionality, +}; + +/* + * This driver is for Nvidia GPU cards with USB Type-C interface. + * We want to identify the cards using vendor ID and class code only + * to avoid dependency of adding product id for any new card which + * requires this driver. + * Currently there is no class code defined for UCSI device over PCI + * so using UNKNOWN class for now and it will be updated when UCSI + * over PCI gets a class code. + * There is no other NVIDIA cards with UNKNOWN class code. Even if the + * driver gets loaded for an undesired card then eventually i2c_read() + * (initiated from UCSI i2c_client) will timeout or UCSI commands will + * timeout. + */ +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 +static const struct pci_device_id gpu_i2c_ids[] = { + { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_UNKNOWN << 8, 0xffffff00}, + { } +}; +MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); + +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), + { } +}; + +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(dev, sizeof(*i2cd), GFP_KERNEL); + if (!i2cd) + return -ENOMEM; + + i2cd->dev = dev; + dev_set_drvdata(dev, i2cd); + + status = pcim_enable_device(pdev); + 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) + return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n"); + + status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + 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; + 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 = dev; + status = i2c_add_adapter(&i2cd->adapter); + if (status < 0) + goto free_irq_vectors; + + 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: + i2c_del_adapter(&i2cd->adapter); +free_irq_vectors: + pci_free_irq_vectors(pdev); + return status; +} + +static void gpu_i2c_remove(struct pci_dev *pdev) +{ + 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, gpu_i2c_suspend, gpu_i2c_resume, + NULL); + +static struct pci_driver gpu_i2c_driver = { + .name = "nvidia-gpu", + .id_table = gpu_i2c_ids, + .probe = gpu_i2c_probe, + .remove = gpu_i2c_remove, + .driver = { + .pm = &gpu_i2c_driver_pm, + }, +}; + +module_pci_driver(gpu_i2c_driver); + +MODULE_AUTHOR("Ajay Gupta "); +MODULE_DESCRIPTION("Nvidia GPU I2C controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c new file mode 100644 index 000000000..2d18c1035 --- /dev/null +++ b/drivers/i2c/busses/i2c-ocores.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller + * (https://opencores.org/project/i2c/overview) + * + * Peter Korsgaard + * + * Support for the GRLIB port of the controller by + * Andreas Larsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * '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; + void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value); + u8 (*getreg)(struct ocores_i2c *i2c, int reg); +}; + +/* registers */ +#define OCI2C_PRELOW 0 +#define OCI2C_PREHIGH 1 +#define OCI2C_CONTROL 2 +#define OCI2C_DATA 3 +#define OCI2C_CMD 4 /* write only */ +#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */ + +#define OCI2C_CTRL_IEN 0x40 +#define OCI2C_CTRL_EN 0x80 + +#define OCI2C_CMD_START 0x91 +#define OCI2C_CMD_STOP 0x41 +#define OCI2C_CMD_READ 0x21 +#define OCI2C_CMD_WRITE 0x11 +#define OCI2C_CMD_READ_ACK 0x21 +#define OCI2C_CMD_READ_NACK 0x29 +#define OCI2C_CMD_IACK 0x01 + +#define OCI2C_STAT_IF 0x01 +#define OCI2C_STAT_TIP 0x02 +#define OCI2C_STAT_ARBLOST 0x20 +#define OCI2C_STAT_BUSY 0x40 +#define OCI2C_STAT_NACK 0x80 + +#define STATE_DONE 0 +#define STATE_START 1 +#define STATE_WRITE 2 +#define STATE_READ 3 +#define STATE_ERROR 4 + +#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)); +} + +static void oc_setreg_16(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_8(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16(struct ocores_i2c *i2c, int reg) +{ + return ioread16(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32(struct ocores_i2c *i2c, int reg) +{ + return ioread32(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16be(struct ocores_i2c *i2c, int reg) +{ + return ioread16be(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg) +{ + return ioread32be(i2c->base + (reg << i2c->reg_shift)); +} + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + i2c->setreg(i2c, reg, value); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + return i2c->getreg(i2c, reg); +} + +static void ocores_process(struct ocores_i2c *i2c, u8 stat) +{ + struct i2c_msg *msg = i2c->msg; + 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); + goto out; + } + + /* error? */ + if (stat & OCI2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { + i2c->state = + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & OCI2C_STAT_NACK) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + } else { + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + } + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { /* end? */ + /* send start? */ + if (!(msg->flags & I2C_M_NOSTART)) { + u8 addr = i2c_8bit_addr_from_msg(msg); + + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, addr); + 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); + goto out; + } + } + + if (i2c->state == STATE_READ) { + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); + } else { + 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); + + 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; +} + +/** + * 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_us: timeout in microseconds + * + * 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, + unsigned long timeout_us) +{ + u8 status; + + return read_poll_timeout_atomic(oc_getreg, status, + (status & mask) == val, + 0, timeout_us, false, + i2c, reg); +} + +/** + * 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) +{ + 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, 1000); + 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; + i2c->nmsgs = num; + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + + 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) +{ + int prescale; + int diff; + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + 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); + + diff = i2c->ip_clock_khz / (5 * (prescale + 1)) - i2c->bus_clock_khz; + if (abs(diff) > i2c->bus_clock_khz / 10) { + dev_err(dev, + "Unsupported clock settings: core: %d KHz, bus: %d KHz\n", + i2c->ip_clock_khz, i2c->bus_clock_khz); + return -EINVAL; + } + + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); + 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); + + return 0; +} + + +static u32 ocores_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ocores_algorithm = { + .xfer = ocores_xfer, + .xfer_atomic = ocores_xfer_polling, + .functionality = ocores_func, +}; + +static const struct i2c_adapter ocores_adapter = { + .owner = THIS_MODULE, + .name = "i2c-ocores", + .class = I2C_CLASS_DEPRECATED, + .algo = &ocores_algorithm, +}; + +static const struct of_device_id ocores_i2c_match[] = { + { + .compatible = "opencores,i2c-ocores", + .data = (void *)TYPE_OCORES, + }, + { + .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 + * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one + * 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)); + if (reg == OCI2C_PREHIGH) + return (u8)(rd >> 8); + else + return (u8)rd; +} + +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) { + curr = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PRELOW) + wr = (curr & 0xff00) | value; + else + wr = (((u32)value) << 8) | (curr & 0xff); + } else { + wr = value; + } + iowrite32be(wr, i2c->base + (rreg << i2c->reg_shift)); +} + +static int ocores_i2c_of_probe(struct platform_device *pdev, + struct ocores_i2c *i2c) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + u32 val; + u32 clock_frequency; + bool clock_frequency_present; + + if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) { + /* no 'reg-shift', check for deprecated 'regstep' */ + if (!of_property_read_u32(np, "regstep", &val)) { + if (!is_power_of_2(val)) { + dev_err(&pdev->dev, "invalid regstep %d\n", + val); + return -EINVAL; + } + i2c->reg_shift = ilog2(val); + dev_warn(&pdev->dev, + "regstep property deprecated, use reg-shift\n"); + } + } + + clock_frequency_present = !of_property_read_u32(np, "clock-frequency", + &clock_frequency); + i2c->bus_clock_khz = 100; + + 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"); + return -ENODEV; + } + i2c->ip_clock_khz = clock_frequency / 1000; + dev_warn(&pdev->dev, + "Deprecated usage of the 'clock-frequency' property, please update to 'opencores,ip-clock-frequency'\n"); + } else { + i2c->ip_clock_khz = val / 1000; + if (clock_frequency_present) + i2c->bus_clock_khz = clock_frequency / 1000; + } + } + + of_property_read_u32(pdev->dev.of_node, "reg-io-width", + &i2c->reg_io_width); + + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (long)match->data == TYPE_GRLIB) { + dev_dbg(&pdev->dev, "GRLIB variant of i2c-ocores\n"); + i2c->setreg = oc_setreg_grlib; + i2c->getreg = oc_getreg_grlib; + } + + return 0; +} +#else +#define ocores_i2c_of_probe(pdev, i2c) -ENODEV +#endif + +static int ocores_i2c_probe(struct platform_device *pdev) +{ + struct ocores_i2c *i2c; + struct ocores_i2c_platform_data *pdata; + struct resource *res; + int irq; + int ret; + int i; + + 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); + 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; + 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) + return ret; + } + + if (i2c->reg_io_width == 0) + i2c->reg_io_width = 1; /* Set to default value */ + + if (!i2c->setreg || !i2c->getreg) { + bool be = pdata ? pdata->big_endian : + of_device_is_big_endian(pdev->dev.of_node); + + switch (i2c->reg_io_width) { + case 1: + i2c->setreg = oc_setreg_8; + i2c->getreg = oc_getreg_8; + break; + + case 2: + i2c->setreg = be ? oc_setreg_16be : oc_setreg_16; + i2c->getreg = be ? oc_getreg_16be : oc_getreg_16; + break; + + case 4: + i2c->setreg = be ? oc_setreg_32be : oc_setreg_32; + i2c->getreg = be ? oc_getreg_32be : oc_getreg_32; + break; + + default: + dev_err(&pdev->dev, "Unsupported I/O width (%d)\n", + i2c->reg_io_width); + return -EINVAL; + } + } + + init_waitqueue_head(&i2c->wait); + + 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; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) + return ret; + + /* add in known devices to the bus */ + if (pdata) { + for (i = 0; i < pdata->num_devices; i++) + i2c_new_client_device(&i2c->adap, pdata->devices + i); + } + + return 0; +} + +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 */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); +} + +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 */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + 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; + + 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; + ret = ocores_init(dev, i2c); + if (ret) + clk_disable_unprepare(i2c->clk); + + return ret; +} + +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, + .driver = { + .name = "ocores-i2c", + .of_match_table = ocores_i2c_match, + .pm = pm_sleep_ptr(&ocores_i2c_pm), + }, +}; + +module_platform_driver(ocores_i2c_driver); + +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ocores-i2c"); diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c new file mode 100644 index 000000000..93a49e463 --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -0,0 +1,1002 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * 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 +#include +#include +#include +#include +#include + +#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) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_disable(i2c); + wake_up(&i2c->queue); + + return IRQ_HANDLED; +} + +static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) +{ + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns: 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + long time_left; + + /* + * Some chip revisions don't assert the irq in the interrupt + * controller. So we must poll for the IFLG change. + */ + if (i2c->broken_irq_mode) { + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->int_enable(i2c); + time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + i2c->adap.timeout); + i2c->int_disable(i2c); + + if (i2c->broken_irq_check && !time_left && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c) +{ + 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) +{ + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); +} + +/* + * Cleanup low-level state & enable high-level controller. + */ +static void octeon_i2c_hlc_enable(struct octeon_i2c *i2c) +{ + int try = 0; + u64 val; + + if (i2c->hlc_enabled) + return; + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_ctl_read(i2c); + if (!(val & (TWSI_CTL_STA | TWSI_CTL_STP))) + break; + + /* clear IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } + octeon_i2c_ctl_write(i2c, TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); +} + +static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + 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. + */ +static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) +{ + int time_left; + + /* + * Some cn38xx boards don't assert the irq in the interrupt + * controller. So we must poll for the valid bit change. + */ + if (i2c->broken_irq_mode) { + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_valid(i2c) && + time_before64(get_jiffies_64(), end)) + usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + + return octeon_i2c_hlc_test_valid(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->hlc_int_enable(i2c); + time_left = wait_event_timeout(i2c->queue, + octeon_i2c_hlc_test_valid(i2c), + i2c->adap.timeout); + i2c->hlc_int_disable(i2c); + if (!time_left) + octeon_i2c_hlc_int_clear(i2c); + + if (i2c->broken_irq_check && !time_left && + octeon_i2c_hlc_test_valid(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + return 0; +} + +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 OCTEON_REG_SW_TWSI. + */ + if (i2c->hlc_enabled) + stat = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); + else + stat = octeon_i2c_stat_read(i2c); + + switch (stat) { + /* Everything is fine */ + case STAT_IDLE: + case STAT_AD2W_ACK: + case STAT_RXADDR_ACK: + case STAT_TXADDR_ACK: + case STAT_TXDATA_ACK: + return 0; + + /* ACK allowed on pre-terminal bytes only */ + case STAT_RXDATA_ACK: + if (!final_read) + return 0; + return -EIO; + + /* NAK allowed on terminal byte only */ + case STAT_RXDATA_NAK: + if (final_read) + return 0; + return -EIO; + + /* Arbitration lost */ + case STAT_LOST_ARB_38: + case STAT_LOST_ARB_68: + case STAT_LOST_ARB_78: + case STAT_LOST_ARB_B0: + return -EAGAIN; + + /* 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 local target */ + case STAT_SLAVE_80: + case STAT_SLAVE_88: + case STAT_SLAVE_A0: + case STAT_SLAVE_A8: + case STAT_SLAVE_LOST: + case STAT_SLAVE_NAK: + case STAT_SLAVE_ACK: + return -EOPNOTSUPP; + + case STAT_TXDATA_NAK: + case STAT_BUS_ERROR: + return -EIO; + case STAT_TXADDR_NAK: + 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; + } +} + +static int octeon_i2c_recovery(struct octeon_i2c *i2c) +{ + int ret; + + ret = i2c_recover_bus(&i2c->adap); + if (ret) + /* recover failed, try hardware re-init */ + ret = octeon_i2c_init_lowlevel(i2c); + return ret; +} + +/** + * octeon_i2c_start - send START to the bus + * @i2c: The struct octeon_i2c + * + * Returns: 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c) +{ + int ret; + 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); + if (ret) + goto error; + + stat = octeon_i2c_stat_read(i2c); + if (stat == STAT_START || stat == STAT_REP_START) + /* START successful, bail out */ + return 0; + +error: + /* START failed, try to recover */ + ret = octeon_i2c_recovery(i2c); + return (ret) ? ret : -EAGAIN; +} + +/* send STOP to the bus */ +static void octeon_i2c_stop(struct octeon_i2c *i2c) +{ + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP); +} + +/** + * octeon_i2c_read - receive data from the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the location to store the data + * @rlength: Length of the data + * @recv_len: flag for length byte + * + * The address is sent over the bus, then the data is read. + * + * 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) +{ + int i, result, length = *rlength; + bool final_read = false; + + octeon_i2c_data_write(i2c, (target << 1) | 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + /* address OK ? */ + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; + + for (i = 0; i < length; i++) { + /* + * For the last byte to receive TWSI_CTL_AAK must not be set. + * + * A special case is I2C_M_RECV_LEN where we don't know the + * additional length yet. If recv_len is set we assume we're + * not reading the final byte and therefore need to set + * TWSI_CTL_AAK. + */ + if ((i + 1 == length) && !(recv_len && i == 0)) + final_read = true; + + /* clear iflg to allow next event */ + if (final_read) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + else + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + data[i] = octeon_i2c_data_read(i2c, &result); + if (result) + return result; + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + length += data[i]; + } + + result = octeon_i2c_check_status(i2c, final_read); + if (result) + return result; + } + *rlength = length; + return 0; +} + +/** + * octeon_i2c_write - send data to the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the data to be sent + * @length: Length of the data + * + * The address is sent over the bus, then the data. + * + * Returns: 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length) +{ + int i, result; + + octeon_i2c_data_write(i2c, target << 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; + + octeon_i2c_data_write(i2c, data[i]); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + } + + return 0; +} + +/* high-level-controller pure read of up to 8 bytes */ +static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_int_clear(i2c); + + 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; + + 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 + OCTEON_REG_SW_TWSI(i2c)); + if ((cmd & SW_TWSI_R) == 0) + return octeon_i2c_check_status(i2c, false); + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[0].len > 4) { + 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; + } + +err: + return ret; +} + +/* high-level-controller pure write of up to 8 bytes */ +static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_int_clear(i2c); + + 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; + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + cmd |= (u64)msgs[0].buf[j] << (8 * i); + + if (msgs[0].len > 4) { + u64 ext = 0; + + 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 + OCTEON_REG_SW_TWSI_EXT(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 + OCTEON_REG_SW_TWSI(i2c)); + if ((cmd & SW_TWSI_R) == 0) + return octeon_i2c_check_status(i2c, false); + +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) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + + 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; + + /* 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) + return octeon_i2c_check_status(i2c, false); + + for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) + msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[1].len > 4) { + 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; + } + +err: + return ret; +} + +/* high-level-controller composite write+write, m[0]len<=2, m[1]len<=8 */ +static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + bool set_ext = false; + int i, j, ret = 0; + u64 cmd, ext = 0; + + octeon_i2c_hlc_enable(i2c); + + 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; + + /* 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); + + if (msgs[1].len > 4) { + for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) + ext |= (u64)msgs[1].buf[j] << (8 * i); + set_ext = true; + } + if (set_ext) + octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); + + 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) + return octeon_i2c_check_status(i2c, false); + +err: + return ret; +} + +/** + * 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. + */ +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 (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; + } + } + } + } + + for (i = 0; ret == 0 && i < num; i++) { + struct i2c_msg *pmsg = &msgs[i]; + + /* zero-length messages are not supported */ + if (!pmsg->len) { + ret = -EOPNOTSUPP; + break; + } + + ret = octeon_i2c_start(i2c); + if (ret) + return ret; + + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + &pmsg->len, pmsg->flags & I2C_M_RECV_LEN); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + } + octeon_i2c_stop(i2c); +out: + return (ret != 0) ? ret : num; +} + +/* calculate and set clock divisors */ +void octeon_i2c_set_clock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + 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 >= 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) * ds; + tclk *= (1 << ndiv_idx); + 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; + + 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) / 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; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + 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) +{ + u8 status = 0; + int tries; + + /* reset controller */ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries && status != STAT_IDLE; tries--) { + udelay(1); + status = octeon_i2c_stat_read(i2c); + if (status == STAT_IDLE) + break; + } + + if (status != STAT_IDLE) { + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", + __func__, status); + return -EIO; + } + + /* toggle twice to force both teardowns */ + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_disable(i2c); + return 0; +} + +static int octeon_i2c_get_scl(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + u64 state; + + state = octeon_i2c_read_int(i2c); + return state & TWSI_INT_SCL; +} + +static void octeon_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + octeon_i2c_write_int(i2c, val ? 0 : TWSI_INT_SCL_OVR); +} + +static int octeon_i2c_get_sda(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + u64 state; + + state = octeon_i2c_read_int(i2c); + return state & TWSI_INT_SDA; +} + +static void octeon_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + octeon_i2c_hlc_disable(i2c); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); + /* wait for software reset to settle */ + udelay(5); + + /* + * Bring control register to a good state regardless + * of HLC state. + */ + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + octeon_i2c_write_int(i2c, 0); +} + +static void octeon_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + /* + * Generate STOP to finish the unfinished transaction. + * Can't generate STOP via the TWSI CTL register + * since it could bring the TWSI controller into an inoperable state. + */ + octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR); + udelay(5); + octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR); + udelay(5); + octeon_i2c_write_int(i2c, 0); +} + +struct i2c_bus_recovery_info octeon_i2c_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = octeon_i2c_get_scl, + .set_scl = octeon_i2c_set_scl, + .get_sda = octeon_i2c_get_sda, + .prepare_recovery = octeon_i2c_prepare_recovery, + .unprepare_recovery = octeon_i2c_unprepare_recovery, +}; diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h new file mode 100644 index 000000000..aba81477d --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-core.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Controller command patterns */ +#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ +#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ + +/* Controller opcode word (bits 60:57) */ +#define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ + +/* Controller extended opcode word (bits 34:32) */ +#define SW_TWSI_EOP_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) + +/* 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 /* 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 */ + +/* Status values */ +#define STAT_BUS_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_REP_START 0x10 +#define STAT_TXADDR_ACK 0x18 +#define STAT_TXADDR_NAK 0x20 +#define STAT_TXDATA_ACK 0x28 +#define STAT_TXDATA_NAK 0x30 +#define STAT_LOST_ARB_38 0x38 +#define STAT_RXADDR_ACK 0x40 +#define STAT_RXADDR_NAK 0x48 +#define STAT_RXDATA_ACK 0x50 +#define STAT_RXDATA_NAK 0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST 0xB8 +#define STAT_SLAVE_NAK 0xC0 +#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 */ +#define TWSI_INT_ST_INT BIT_ULL(0) +#define TWSI_INT_TS_INT BIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) +#define TWSI_INT_CORE_EN BIT_ULL(6) +#define TWSI_INT_SDA_OVR BIT_ULL(8) +#define TWSI_INT_SCL_OVR BIT_ULL(9) +#define TWSI_INT_SDA BIT_ULL(10) +#define TWSI_INT_SCL BIT_ULL(11) + +#define I2C_OCTEON_EVENT_WAIT 80 /* microseconds */ + +/* Register offsets */ +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 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; + struct i2c_adapter adap; + struct octeon_i2c_reg_offset roff; + struct clk *clk; + int irq; + int hlc_irq; /* For cn7890 only */ + u32 twsi_freq; + int sys_freq; + 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 *); + void (*int_disable)(struct octeon_i2c *); + void (*hlc_int_enable)(struct octeon_i2c *); + void (*hlc_int_disable)(struct octeon_i2c *); + atomic_t int_enable_cnt; + atomic_t hlc_int_enable_cnt; + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +}; + +static inline void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + +/** + * octeon_i2c_reg_write - write an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * @data: Value to be written + * + * 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 + OCTEON_REG_SW_TWSI(i2c)); + do { + tmp = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); + if (--tries < 0) + return; + } while ((tmp & SW_TWSI_V) != 0); +} + +#define octeon_i2c_ctl_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, val) +#define octeon_i2c_data_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, val) + +/** + * octeon_i2c_reg_read - read lower bits of an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline int octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg, + int *error) +{ + int tries = 1000; + u64 tmp; + + __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 + OCTEON_REG_SW_TWSI(i2c)); + if (--tries < 0) { + /* signal that the returned data is invalid */ + if (error) + *error = -EIO; + return 0; + } + } while ((tmp & SW_TWSI_V) != 0); + + return tmp & 0xFF; +} + +#define octeon_i2c_ctl_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL, NULL) +#define octeon_i2c_data_read(i2c, error) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA, error) +#define octeon_i2c_stat_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT, NULL) + +/** + * 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 + OCTEON_REG_TWSI_INT(i2c)); +} + +/** + * 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 + OCTEON_REG_TWSI_INT(i2c)); +} + +#define IS_LS_FREQ(twsi_freq) ((twsi_freq) <= I2C_MAX_FAST_MODE_FREQ) +#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 */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id); +int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c); +void octeon_i2c_set_clock(struct octeon_i2c *i2c); +extern struct i2c_bus_recovery_info octeon_i2c_recovery_info; diff --git a/drivers/i2c/busses/i2c-octeon-platdrv.c b/drivers/i2c/busses/i2c-octeon-platdrv.c new file mode 100644 index 000000000..edfca7b20 --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-platdrv.c @@ -0,0 +1,283 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "i2c-octeon-core.h" + +#define DRV_NAME "i2c-octeon" + +/** + * octeon_i2c_int_enable - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable(struct octeon_i2c *i2c) +{ + /* clear TS/ST/IFLG events */ + octeon_i2c_write_int(i2c, 0); +} + +/** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->int_enable_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* + * The interrupt can be disabled in two places, but we only + * want to make the disable_irq_nosync() call once, so keep + * track with the atomic variable. + */ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->hlc_int_enable_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq); +} + +/* HLC interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_disable(i2c); + wake_up(&i2c->queue); + + return IRQ_HANDLED; +} + +static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); +} + +static u32 octeon_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm octeon_i2c_algo = { + .xfer = octeon_i2c_xfer, + .functionality = octeon_i2c_functionality, +}; + +static const struct i2c_adapter octeon_i2c_ops = { + .owner = THIS_MODULE, + .name = "OCTEON adapter", + .algo = &octeon_i2c_algo, +}; + +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 octeon_i2c *i2c; + bool cn78xx_style; + + cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi"); + if (cn78xx_style) { + hlc_irq = platform_get_irq(pdev, 0); + if (hlc_irq < 0) + return hlc_irq; + + irq = platform_get_irq(pdev, 2); + if (irq < 0) + return irq; + } else { + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + } + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + result = -ENOMEM; + goto out; + } + i2c->dev = &pdev->dev; + + i2c->roff.sw_twsi = 0x00; + i2c->roff.twsi_int = 0x10; + i2c->roff.sw_twsi_ext = 0x18; + + i2c->twsi_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2c->twsi_base)) { + result = PTR_ERR(i2c->twsi_base); + goto out; + } + + /* + * "clock-rate" is a legacy binding, the official binding is + * "clock-frequency". Try the official one first and then + * fall back if it doesn't exist. + */ + if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) && + of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) { + dev_err(i2c->dev, + "no I2C 'clock-rate' or 'clock-frequency' property\n"); + result = -ENXIO; + goto out; + } + + i2c->sys_freq = octeon_get_io_clock_rate(); + + init_waitqueue_head(&i2c->queue); + + i2c->irq = irq; + + if (cn78xx_style) { + i2c->hlc_irq = hlc_irq; + + i2c->int_enable = octeon_i2c_int_enable78; + i2c->int_disable = octeon_i2c_int_disable78; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78; + i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78; + + irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN); + irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN); + + result = devm_request_irq(&pdev->dev, i2c->hlc_irq, + octeon_i2c_hlc_isr78, 0, + DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto out; + } + } else { + i2c->int_enable = octeon_i2c_int_enable; + i2c->int_disable = octeon_i2c_int_disable; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable; + i2c->hlc_int_disable = octeon_i2c_int_disable; + } + + result = devm_request_irq(&pdev->dev, i2c->irq, + octeon_i2c_isr, 0, DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto out; + } + + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + + result = octeon_i2c_init_lowlevel(i2c); + if (result) { + dev_err(i2c->dev, "init low level failed\n"); + goto out; + } + + octeon_i2c_set_clock(i2c); + + i2c->adap = octeon_i2c_ops; + i2c->adap.timeout = msecs_to_jiffies(2); + i2c->adap.retries = 5; + i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = node; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + result = i2c_add_adapter(&i2c->adap); + if (result < 0) + goto out; + dev_info(i2c->dev, "probed\n"); + return 0; + +out: + return result; +}; + +static void octeon_i2c_remove(struct platform_device *pdev) +{ + struct octeon_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); +}; + +static const struct of_device_id octeon_i2c_match[] = { + { .compatible = "cavium,octeon-3860-twsi", }, + { .compatible = "cavium,octeon-7890-twsi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_i2c_match); + +static struct platform_driver octeon_i2c_driver = { + .probe = octeon_i2c_probe, + .remove = octeon_i2c_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = octeon_i2c_match, + }, +}; + +module_platform_driver(octeon_i2c_driver); + +MODULE_AUTHOR("Michael Lawnick "); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c new file mode 100644 index 000000000..f02d294db --- /dev/null +++ b/drivers/i2c/busses/i2c-omap.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TI OMAP I2C master mode driver + * + * Copyright (C) 2003 MontaVista Software, Inc. + * Copyright (C) 2005 Nokia Corporation + * Copyright (C) 2004 - 2007 Texas Instruments. + * + * Originally written by MontaVista Software, Inc. + * Additional contributions by: + * Tony Lindgren + * Imre Deak + * Juha Yrjölä + * Syed Khasim + * Nishant Menon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C controller revisions */ +#define OMAP_I2C_OMAP1_REV_2 0x20 + +/* I2C controller revisions present on specific hardware */ +#define OMAP_I2C_REV_ON_2430 0x00000036 +#define OMAP_I2C_REV_ON_3430_3530 0x0000003C +#define OMAP_I2C_REV_ON_3630 0x00000040 +#define OMAP_I2C_REV_ON_4430_PLUS 0x50400002 + +/* timeout waiting for the controller to respond */ +#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +/* timeout for pm runtime autosuspend */ +#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */ + +/* timeout for making decision on bus free status */ +#define OMAP_I2C_BUS_FREE_TIMEOUT (msecs_to_jiffies(10)) + +/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */ +enum { + OMAP_I2C_REV_REG = 0, + OMAP_I2C_IE_REG, + OMAP_I2C_STAT_REG, + OMAP_I2C_IV_REG, + OMAP_I2C_WE_REG, + OMAP_I2C_SYSS_REG, + OMAP_I2C_BUF_REG, + OMAP_I2C_CNT_REG, + OMAP_I2C_DATA_REG, + OMAP_I2C_SYSC_REG, + OMAP_I2C_CON_REG, + OMAP_I2C_OA_REG, + OMAP_I2C_SA_REG, + OMAP_I2C_PSC_REG, + OMAP_I2C_SCLL_REG, + OMAP_I2C_SCLH_REG, + OMAP_I2C_SYSTEST_REG, + OMAP_I2C_BUFSTAT_REG, + /* only on OMAP4430 */ + OMAP_I2C_IP_V2_REVNB_LO, + OMAP_I2C_IP_V2_REVNB_HI, + OMAP_I2C_IP_V2_IRQSTATUS_RAW, + OMAP_I2C_IP_V2_IRQENABLE_SET, + OMAP_I2C_IP_V2_IRQENABLE_CLR, +}; + +/* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */ +#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drain int enable */ +#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ +#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ +#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ +#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Status Register (OMAP_I2C_STAT): */ +#define OMAP_I2C_STAT_XDR (1 << 14) /* TX Buffer draining */ +#define OMAP_I2C_STAT_RDR (1 << 13) /* RX Buffer draining */ +#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ +#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define OMAP_I2C_STAT_BF (1 << 8) /* Bus Free */ +#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C WE wakeup enable register */ +#define OMAP_I2C_WE_XDR_WE (1 << 14) /* TX drain wakup */ +#define OMAP_I2C_WE_RDR_WE (1 << 13) /* RX drain wakeup */ +#define OMAP_I2C_WE_AAS_WE (1 << 9) /* Address as slave wakeup*/ +#define OMAP_I2C_WE_BF_WE (1 << 8) /* Bus free wakeup */ +#define OMAP_I2C_WE_STC_WE (1 << 6) /* Start condition wakeup */ +#define OMAP_I2C_WE_GC_WE (1 << 5) /* General call wakeup */ +#define OMAP_I2C_WE_DRDY_WE (1 << 3) /* TX/RX data ready wakeup */ +#define OMAP_I2C_WE_ARDY_WE (1 << 2) /* Reg access ready wakeup */ +#define OMAP_I2C_WE_NACK_WE (1 << 1) /* No acknowledgment wakeup */ +#define OMAP_I2C_WE_AL_WE (1 << 0) /* Arbitration lost wakeup */ + +#define OMAP_I2C_WE_ALL (OMAP_I2C_WE_XDR_WE | OMAP_I2C_WE_RDR_WE | \ + OMAP_I2C_WE_AAS_WE | OMAP_I2C_WE_BF_WE | \ + OMAP_I2C_WE_STC_WE | OMAP_I2C_WE_GC_WE | \ + OMAP_I2C_WE_DRDY_WE | OMAP_I2C_WE_ARDY_WE | \ + OMAP_I2C_WE_NACK_WE | OMAP_I2C_WE_AL_WE) + +/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ +#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */ +#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ +#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */ + +/* I2C Configuration Register (OMAP_I2C_CON): */ +#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ +#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ +#define OMAP_I2C_CON_OPMODE_HS (1 << 12) /* High Speed support */ +#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ +#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ +#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */ +#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */ +#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */ +#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */ + +/* I2C SCL time value when Master */ +#define OMAP_I2C_SCLL_HSSCLL 8 +#define OMAP_I2C_SCLH_HSSCLH 8 + +/* I2C System Test Register (OMAP_I2C_SYSTEST): */ +#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */ +#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +/* Functional mode */ +#define OMAP_I2C_SYSTEST_SCL_I_FUNC (1 << 8) /* SCL line input value */ +#define OMAP_I2C_SYSTEST_SCL_O_FUNC (1 << 7) /* SCL line output value */ +#define OMAP_I2C_SYSTEST_SDA_I_FUNC (1 << 6) /* SDA line input value */ +#define OMAP_I2C_SYSTEST_SDA_O_FUNC (1 << 5) /* SDA line output value */ +/* SDA/SCL IO mode */ +#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */ +#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */ +#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */ +#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ + +/* OCP_SYSSTATUS bit definitions */ +#define SYSS_RESETDONE_MASK (1 << 0) + +/* OCP_SYSCONFIG bit definitions */ +#define SYSC_CLOCKACTIVITY_MASK (0x3 << 8) +#define SYSC_SIDLEMODE_MASK (0x3 << 3) +#define SYSC_ENAWAKEUP_MASK (1 << 2) +#define SYSC_SOFTRESET_MASK (1 << 1) +#define SYSC_AUTOIDLE_MASK (1 << 0) + +#define SYSC_IDLEMODE_SMART 0x2 +#define SYSC_CLOCKACTIVITY_FCLK 0x2 + +/* Errata definitions */ +#define I2C_OMAP_ERRATA_I207 (1 << 0) +#define I2C_OMAP_ERRATA_I462 (1 << 1) + +#define OMAP_I2C_IP_V2_INTERRUPTS_MASK 0x6FFF + +struct omap_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + int irq; + int reg_shift; /* bit shift for I2C register addresses */ + struct completion cmd_complete; + struct resource *ioarea; + u32 latency; /* maximum mpu wkup latency */ + void (*set_mpu_wkup_lat)(struct device *dev, + long latency); + u32 speed; /* Speed of bus in kHz */ + u32 flags; + u16 scheme; + u16 cmd_err; + u8 *buf; + u8 *regs; + size_t buf_len; + struct i2c_adapter adapter; + u8 threshold; + u8 fifo_size; /* use as flag and value + * fifo_size==0 implies no fifo + * if set, should be trsh+1 + */ + u32 rev; + unsigned b_hw:1; /* bad h/w fixes */ + unsigned bb_valid:1; /* true when BB-bit reflects + * the I2C bus state + */ + unsigned receiver:1; /* true when we're in receiver mode */ + u16 iestate; /* Saved interrupt register */ + u16 pscstate; + u16 scllstate; + u16 sclhstate; + u16 syscstate; + u16 westate; + u16 errata; + struct mux_state *mux_state; +}; + +static const u8 reg_map_ip_v1[] = { + [OMAP_I2C_REV_REG] = 0x00, + [OMAP_I2C_IE_REG] = 0x01, + [OMAP_I2C_STAT_REG] = 0x02, + [OMAP_I2C_IV_REG] = 0x03, + [OMAP_I2C_WE_REG] = 0x03, + [OMAP_I2C_SYSS_REG] = 0x04, + [OMAP_I2C_BUF_REG] = 0x05, + [OMAP_I2C_CNT_REG] = 0x06, + [OMAP_I2C_DATA_REG] = 0x07, + [OMAP_I2C_SYSC_REG] = 0x08, + [OMAP_I2C_CON_REG] = 0x09, + [OMAP_I2C_OA_REG] = 0x0a, + [OMAP_I2C_SA_REG] = 0x0b, + [OMAP_I2C_PSC_REG] = 0x0c, + [OMAP_I2C_SCLL_REG] = 0x0d, + [OMAP_I2C_SCLH_REG] = 0x0e, + [OMAP_I2C_SYSTEST_REG] = 0x0f, + [OMAP_I2C_BUFSTAT_REG] = 0x10, +}; + +static const u8 reg_map_ip_v2[] = { + [OMAP_I2C_REV_REG] = 0x04, + [OMAP_I2C_IE_REG] = 0x2c, + [OMAP_I2C_STAT_REG] = 0x28, + [OMAP_I2C_IV_REG] = 0x34, + [OMAP_I2C_WE_REG] = 0x34, + [OMAP_I2C_SYSS_REG] = 0x90, + [OMAP_I2C_BUF_REG] = 0x94, + [OMAP_I2C_CNT_REG] = 0x98, + [OMAP_I2C_DATA_REG] = 0x9c, + [OMAP_I2C_SYSC_REG] = 0x10, + [OMAP_I2C_CON_REG] = 0xa4, + [OMAP_I2C_OA_REG] = 0xa8, + [OMAP_I2C_SA_REG] = 0xac, + [OMAP_I2C_PSC_REG] = 0xb0, + [OMAP_I2C_SCLL_REG] = 0xb4, + [OMAP_I2C_SCLH_REG] = 0xb8, + [OMAP_I2C_SYSTEST_REG] = 0xbC, + [OMAP_I2C_BUFSTAT_REG] = 0xc0, + [OMAP_I2C_IP_V2_REVNB_LO] = 0x00, + [OMAP_I2C_IP_V2_REVNB_HI] = 0x04, + [OMAP_I2C_IP_V2_IRQSTATUS_RAW] = 0x24, + [OMAP_I2C_IP_V2_IRQENABLE_SET] = 0x2c, + [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) +{ + writew_relaxed(val, omap->base + + (omap->regs[reg] << omap->reg_shift)); +} + +static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *omap, int reg) +{ + return readw_relaxed(omap->base + + (omap->regs[reg] << omap->reg_shift)); +} + +static void __omap_i2c_init(struct omap_i2c_dev *omap) +{ + + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); + + /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ + omap_i2c_write_reg(omap, OMAP_I2C_PSC_REG, omap->pscstate); + + /* SCL low and high time values */ + omap_i2c_write_reg(omap, OMAP_I2C_SCLL_REG, omap->scllstate); + omap_i2c_write_reg(omap, OMAP_I2C_SCLH_REG, omap->sclhstate); + if (omap->rev >= OMAP_I2C_REV_ON_3430_3530) + omap_i2c_write_reg(omap, OMAP_I2C_WE_REG, omap->westate); + + /* Take the I2C module out of reset: */ + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + + /* + * NOTE: right after setting CON_EN, STAT_BB could be 0 while the + * bus is busy. It will be changed to 1 on the next IP FCLK clock. + * udelay(1) will be enough to fix that. + */ + + /* + * Don't write to this register if the IE state is 0 as it can + * cause deadlock. + */ + if (omap->iestate) + omap_i2c_write_reg(omap, OMAP_I2C_IE_REG, omap->iestate); +} + +static int omap_i2c_reset(struct omap_i2c_dev *omap) +{ + unsigned long timeout; + u16 sysc; + + if (omap->rev >= OMAP_I2C_OMAP1_REV_2) { + sysc = omap_i2c_read_reg(omap, OMAP_I2C_SYSC_REG); + + /* Disable I2C controller before soft reset */ + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, + omap_i2c_read_reg(omap, OMAP_I2C_CON_REG) & + ~(OMAP_I2C_CON_EN)); + + omap_i2c_write_reg(omap, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK); + /* For some reason we need to set the EN bit before the + * reset done bit gets set. */ + timeout = jiffies + OMAP_I2C_TIMEOUT; + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + while (!(omap_i2c_read_reg(omap, OMAP_I2C_SYSS_REG) & + SYSS_RESETDONE_MASK)) { + if (time_after(jiffies, timeout)) { + dev_warn(omap->dev, "timeout waiting " + "for controller reset\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + /* SYSC register is cleared by the reset; rewrite it */ + omap_i2c_write_reg(omap, OMAP_I2C_SYSC_REG, sysc); + + if (omap->rev > OMAP_I2C_REV_ON_3430_3530) { + /* Schedule I2C-bus monitoring on the next transfer */ + omap->bb_valid = 0; + } + } + + return 0; +} + +static int omap_i2c_init(struct omap_i2c_dev *omap) +{ + u16 psc = 0, scll = 0, sclh = 0; + u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; + unsigned long fclk_rate = 12000000; + unsigned long internal_clk = 0; + struct clk *fclk; + int error; + + if (omap->rev >= OMAP_I2C_REV_ON_3430_3530) { + /* + * Enabling all wakup sources to stop I2C freezing on + * WFI instruction. + * REVISIT: Some wkup sources might not be needed. + */ + omap->westate = OMAP_I2C_WE_ALL; + } + + if (omap->flags & OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK) { + /* + * The I2C functional clock is the armxor_ck, so there's + * no need to get "armxor_ck" separately. Now, if OMAP2420 + * always returns 12MHz for the functional clock, we can + * do this bit unconditionally. + */ + fclk = clk_get(omap->dev, "fck"); + if (IS_ERR(fclk)) { + error = PTR_ERR(fclk); + dev_err(omap->dev, "could not get fck: %i\n", error); + + return error; + } + + fclk_rate = clk_get_rate(fclk); + clk_put(fclk); + + /* TRM for 5912 says the I2C clock must be prescaled to be + * between 7 - 12 MHz. The XOR input clock is typically + * 12, 13 or 19.2 MHz. So we should have code that produces: + * + * XOR MHz Divider Prescaler + * 12 1 0 + * 13 2 1 + * 19.2 2 1 + */ + if (fclk_rate > 12000000) + psc = fclk_rate / 12000000; + } + + if (!(omap->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) { + + /* + * HSI2C controller internal clk rate should be 19.2 Mhz for + * HS and for all modes on 2430. On 34xx we can use lower rate + * to get longer filter period for better noise suppression. + * The filter is iclk (fclk for HS) period. + */ + if (omap->speed > 400 || + omap->flags & OMAP_I2C_FLAG_FORCE_19200_INT_CLK) + internal_clk = 19200; + else if (omap->speed > 100) + internal_clk = 9600; + else + internal_clk = 4000; + fclk = clk_get(omap->dev, "fck"); + if (IS_ERR(fclk)) { + error = PTR_ERR(fclk); + dev_err(omap->dev, "could not get fck: %i\n", error); + + return error; + } + fclk_rate = clk_get_rate(fclk) / 1000; + clk_put(fclk); + + /* Compute prescaler divisor */ + psc = fclk_rate / internal_clk; + psc = psc - 1; + + /* If configured for High Speed */ + if (omap->speed > 400) { + unsigned long scl; + + /* For first phase of HS mode */ + scl = internal_clk / 400; + fsscll = scl - (scl / 3) - 7; + fssclh = (scl / 3) - 5; + + /* For second phase of HS mode */ + scl = fclk_rate / omap->speed; + hsscll = scl - (scl / 3) - 7; + hssclh = (scl / 3) - 5; + } else if (omap->speed > 100) { + unsigned long scl; + + /* Fast mode */ + scl = internal_clk / omap->speed; + fsscll = scl - (scl / 3) - 7; + fssclh = (scl / 3) - 5; + } else { + /* Standard mode */ + fsscll = internal_clk / (omap->speed * 2) - 7; + fssclh = internal_clk / (omap->speed * 2) - 5; + } + scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; + sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh; + } else { + /* Program desired operating rate */ + fclk_rate /= (psc + 1) * 1000; + if (psc > 2) + psc = 2; + scll = fclk_rate / (omap->speed * 2) - 7 + psc; + sclh = fclk_rate / (omap->speed * 2) - 7 + psc; + } + + omap->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | + OMAP_I2C_IE_AL) | ((omap->fifo_size) ? + (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); + + omap->pscstate = psc; + omap->scllstate = scll; + omap->sclhstate = sclh; + + if (omap->rev <= OMAP_I2C_REV_ON_3430_3530) { + /* Not implemented */ + omap->bb_valid = 1; + } + + __omap_i2c_init(omap); + + return 0; +} + +/* + * Try bus recovery, but only if SDA is actually low. + */ +static int omap_i2c_recover_bus(struct omap_i2c_dev *omap) +{ + u16 systest; + + systest = omap_i2c_read_reg(omap, OMAP_I2C_SYSTEST_REG); + if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) && + (systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) + return 0; /* bus seems to already be fine */ + if (!(systest & OMAP_I2C_SYSTEST_SCL_I_FUNC)) + return -EBUSY; /* recovery would not fix SCL */ + return i2c_recover_bus(&omap->adapter); +} + +/* + * Waiting on Bus Busy + */ +static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap) +{ + unsigned long timeout; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { + if (time_after(jiffies, timeout)) + return omap_i2c_recover_bus(omap); + msleep(1); + } + + return 0; +} + +/* + * Wait while BB-bit doesn't reflect the I2C bus state + * + * In a multimaster environment, after IP software reset, BB-bit value doesn't + * correspond to the current bus state. It may happen what BB-bit will be 0, + * while the bus is busy due to another I2C master activity. + * Here are BB-bit values after reset: + * SDA SCL BB NOTES + * 0 0 0 1, 2 + * 1 0 0 1, 2 + * 0 1 1 + * 1 1 0 3 + * Later, if IP detect SDA=0 and SCL=1 (ACK) or SDA 1->0 while SCL=1 (START) + * combinations on the bus, it set BB-bit to 1. + * If IP detect SDA 0->1 while SCL=1 (STOP) combination on the bus, + * it set BB-bit to 0 and BF to 1. + * BB and BF bits correctly tracks the bus state while IP is suspended + * BB bit became valid on the next FCLK clock after CON_EN bit set + * + * NOTES: + * 1. Any transfer started when BB=0 and bus is busy wouldn't be + * completed by IP and results in controller timeout. + * 2. Any transfer started when BB=0 and SCL=0 results in IP + * starting to drive SDA low. In that case IP corrupt data + * on the bus. + * 3. Any transfer started in the middle of another master's transfer + * results in unpredictable results and data corruption + */ +static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *omap) +{ + unsigned long bus_free_timeout = 0; + unsigned long timeout; + int bus_free = 0; + u16 stat, systest; + + if (omap->bb_valid) + return 0; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (1) { + stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); + /* + * We will see BB or BF event in a case IP had detected any + * activity on the I2C bus. Now IP correctly tracks the bus + * state. BB-bit value is valid. + */ + if (stat & (OMAP_I2C_STAT_BB | OMAP_I2C_STAT_BF)) + break; + + /* + * Otherwise, we must look signals on the bus to make + * the right decision. + */ + systest = omap_i2c_read_reg(omap, OMAP_I2C_SYSTEST_REG); + if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) && + (systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) { + if (!bus_free) { + bus_free_timeout = jiffies + + OMAP_I2C_BUS_FREE_TIMEOUT; + bus_free = 1; + } + + /* + * SDA and SCL lines was high for 10 ms without bus + * activity detected. The bus is free. Consider + * BB-bit value is valid. + */ + if (time_after(jiffies, bus_free_timeout)) + break; + } else { + bus_free = 0; + } + + if (time_after(jiffies, timeout)) { + /* + * SDA or SCL were low for the entire timeout without + * any activity detected. Most likely, a slave is + * locking up the bus with no master driving the clock. + */ + dev_warn(omap->dev, "timeout waiting for bus ready\n"); + return omap_i2c_recover_bus(omap); + } + + msleep(1); + } + + omap->bb_valid = 1; + return 0; +} + +static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx) +{ + u16 buf; + + if (omap->flags & OMAP_I2C_FLAG_NO_FIFO) + return; + + /* + * Set up notification threshold based on message size. We're doing + * this to try and avoid draining feature as much as possible. Whenever + * we have big messages to transfer (bigger than our total fifo size) + * then we might use draining feature to transfer the remaining bytes. + */ + + omap->threshold = clamp(size, (u8) 1, omap->fifo_size); + + buf = omap_i2c_read_reg(omap, OMAP_I2C_BUF_REG); + + if (is_rx) { + /* Clear RX Threshold */ + buf &= ~(0x3f << 8); + buf |= ((omap->threshold - 1) << 8) | OMAP_I2C_BUF_RXFIF_CLR; + } else { + /* Clear TX Threshold */ + buf &= ~0x3f; + buf |= (omap->threshold - 1) | OMAP_I2C_BUF_TXFIF_CLR; + } + + omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, buf); + + if (omap->rev < OMAP_I2C_REV_ON_3630) + omap->b_hw = 1; /* Enable hardware fixes */ + + /* calculate wakeup latency constraint for MPU */ + if (omap->set_mpu_wkup_lat != NULL) + omap->latency = (1000000 * omap->threshold) / + (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, bool polling) +{ + struct omap_i2c_dev *omap = i2c_get_adapdata(adap); + 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); + + omap->receiver = !!(msg->flags & I2C_M_RD); + omap_i2c_resize_fifo(omap, msg->len, omap->receiver); + + omap_i2c_write_reg(omap, OMAP_I2C_SA_REG, msg->addr); + + /* REVISIT: Could the STB bit of I2C_CON be used with probing? */ + omap->buf = msg->buf; + omap->buf_len = msg->len; + + /* make sure writes to omap->buf_len are ordered */ + barrier(); + + omap_i2c_write_reg(omap, OMAP_I2C_CNT_REG, omap->buf_len); + + /* Clear the FIFO Buffers */ + w = omap_i2c_read_reg(omap, OMAP_I2C_BUF_REG); + w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w); + + if (!polling) + reinit_completion(&omap->cmd_complete); + omap->cmd_err = 0; + + w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; + + /* High speed configuration */ + if (omap->speed > 400) + w |= OMAP_I2C_CON_OPMODE_HS; + + if (msg->flags & I2C_M_STOP) + stop = 1; + if (msg->flags & I2C_M_TEN) + w |= OMAP_I2C_CON_XA; + if (!(msg->flags & I2C_M_RD)) + w |= OMAP_I2C_CON_TRX; + + if (!omap->b_hw && stop) + w |= OMAP_I2C_CON_STP; + /* + * NOTE: STAT_BB bit could became 1 here if another master occupy + * the bus. IP successfully complete transfer when the bus will be + * free again (BB reset to 0). + */ + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, w); + + /* + * Don't write stt and stp together on some hardware. + */ + if (omap->b_hw && stop) { + unsigned long delay = jiffies + OMAP_I2C_TIMEOUT; + u16 con = omap_i2c_read_reg(omap, OMAP_I2C_CON_REG); + while (con & OMAP_I2C_CON_STT) { + con = omap_i2c_read_reg(omap, OMAP_I2C_CON_REG); + + /* Let the user know if i2c is in a bad state */ + if (time_after(jiffies, delay)) { + dev_err(omap->dev, "controller timed out " + "waiting for start condition to finish\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + w |= OMAP_I2C_CON_STP; + w &= ~OMAP_I2C_CON_STT; + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, w); + } + + /* + * REVISIT: We should abort the transfer on signals, but the bus goes + * into arbitration and we're currently unable to recover from it. + */ + 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; + } + + if (likely(!omap->cmd_err)) + return 0; + + /* We have an error */ + if (omap->cmd_err & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF)) { + omap_i2c_reset(omap); + __omap_i2c_init(omap); + return -EIO; + } + + if (omap->cmd_err & OMAP_I2C_STAT_AL) + return -EAGAIN; + + if (omap->cmd_err & OMAP_I2C_STAT_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + + w = omap_i2c_read_reg(omap, OMAP_I2C_CON_REG); + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, w); + return -EREMOTEIO; + } + return -EIO; +} + + +/* + * Prepare controller for a transaction and call omap_i2c_xfer_msg + * to do the work during IRQ processing. + */ +static int +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; + int r; + + r = pm_runtime_get_sync(omap->dev); + if (r < 0) + goto out; + + r = omap_i2c_wait_for_bb_valid(omap); + if (r < 0) + goto out; + + r = omap_i2c_wait_for_bb(omap); + if (r < 0) + goto out; + + if (omap->set_mpu_wkup_lat != NULL) + 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)), + polling); + if (r != 0) + break; + } + + if (r == 0) + r = num; + + omap_i2c_wait_for_bb(omap); + + if (omap->set_mpu_wkup_lat != NULL) + omap->set_mpu_wkup_lat(omap->dev, -1); + +out: + 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) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_PROTOCOL_MANGLING; +} + +static inline void +omap_i2c_complete_cmd(struct omap_i2c_dev *omap, u16 err) +{ + omap->cmd_err |= err; + complete(&omap->cmd_complete); +} + +static inline void +omap_i2c_ack_stat(struct omap_i2c_dev *omap, u16 stat) +{ + omap_i2c_write_reg(omap, OMAP_I2C_STAT_REG, stat); +} + +static inline void i2c_omap_errata_i207(struct omap_i2c_dev *omap, u16 stat) +{ + /* + * I2C Errata(Errata Nos. OMAP2: 1.67, OMAP3: 1.8) + * Not applicable for OMAP4. + * Under certain rare conditions, RDR could be set again + * when the bus is busy, then ignore the interrupt and + * clear the interrupt. + */ + if (stat & OMAP_I2C_STAT_RDR) { + /* Step 1: If RDR is set, clear it */ + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RDR); + + /* Step 2: */ + if (!(omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_BB)) { + + /* Step 3: */ + if (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_RDR) { + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RDR); + dev_dbg(omap->dev, "RDR when bus is busy.\n"); + } + + } + } +} + +/* rev1 devices are apparently only on some 15xx */ +#ifdef CONFIG_ARCH_OMAP15XX + +static irqreturn_t +omap_i2c_omap1_isr(int this_irq, void *dev_id) +{ + struct omap_i2c_dev *omap = dev_id; + u16 iv, w; + + if (pm_runtime_suspended(omap->dev)) + return IRQ_NONE; + + iv = omap_i2c_read_reg(omap, OMAP_I2C_IV_REG); + switch (iv) { + case 0x00: /* None */ + break; + case 0x01: /* Arbitration lost */ + dev_err(omap->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(omap, OMAP_I2C_STAT_AL); + break; + case 0x02: /* No acknowledgement */ + omap_i2c_complete_cmd(omap, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP); + break; + case 0x03: /* Register access ready */ + omap_i2c_complete_cmd(omap, 0); + break; + case 0x04: /* Receive data ready */ + if (omap->buf_len) { + w = omap_i2c_read_reg(omap, OMAP_I2C_DATA_REG); + *omap->buf++ = w; + omap->buf_len--; + if (omap->buf_len) { + *omap->buf++ = w >> 8; + omap->buf_len--; + } + } else + dev_err(omap->dev, "RRDY IRQ while no data requested\n"); + break; + case 0x05: /* Transmit data ready */ + if (omap->buf_len) { + w = *omap->buf++; + omap->buf_len--; + if (omap->buf_len) { + w |= *omap->buf++ << 8; + omap->buf_len--; + } + omap_i2c_write_reg(omap, OMAP_I2C_DATA_REG, w); + } else + dev_err(omap->dev, "XRDY IRQ while no data to send\n"); + break; + default: + return IRQ_NONE; + } + + return IRQ_HANDLED; +} +#else +#define omap_i2c_omap1_isr NULL +#endif + +/* + * OMAP3430 Errata i462: When an XRDY/XDR is hit, wait for XUDF before writing + * data to DATA_REG. Otherwise some data bytes can be lost while transferring + * them from the memory to the I2C interface. + */ +static int errata_omap3_i462(struct omap_i2c_dev *omap) +{ + unsigned long timeout = 10000; + u16 stat; + + do { + stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); + if (stat & OMAP_I2C_STAT_XUDF) + break; + + if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(omap, (OMAP_I2C_STAT_XRDY | + OMAP_I2C_STAT_XDR)); + if (stat & 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_AL) { + dev_err(omap->dev, "Arbitration lost\n"); + omap->cmd_err |= OMAP_I2C_STAT_AL; + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AL); + } + + return -EIO; + } + + cpu_relax(); + } while (--timeout); + + if (!timeout) { + dev_err(omap->dev, "timeout waiting on XUDF bit\n"); + return 0; + } + + return 0; +} + +static void omap_i2c_receive_data(struct omap_i2c_dev *omap, u8 num_bytes, + bool is_rdr) +{ + u16 w; + + while (num_bytes--) { + w = omap_i2c_read_reg(omap, OMAP_I2C_DATA_REG); + *omap->buf++ = w; + omap->buf_len--; + + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (omap->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) { + *omap->buf++ = w >> 8; + omap->buf_len--; + } + } +} + +static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes, + bool is_xdr) +{ + u16 w; + + while (num_bytes--) { + w = *omap->buf++; + omap->buf_len--; + + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (omap->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) { + w |= *omap->buf++ << 8; + omap->buf_len--; + } + + if (omap->errata & I2C_OMAP_ERRATA_I462) { + int ret; + + ret = errata_omap3_i462(omap); + if (ret < 0) + return ret; + } + + omap_i2c_write_reg(omap, OMAP_I2C_DATA_REG, w); + } + + return 0; +} + +static int omap_i2c_xfer_data(struct omap_i2c_dev *omap) +{ + u16 bits; + u16 stat; + int err = 0, count = 0; + + do { + bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG); + stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); + stat &= bits; + + /* If we're in receiver mode, ignore XDR/XRDY */ + if (omap->receiver) + stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY); + else + stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY); + + if (!stat) { + /* my work here is done */ + err = -EAGAIN; + break; + } + + dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat); + if (count++ == 100) { + dev_warn(omap->dev, "Too much work in one IRQ\n"); + break; + } + + if (stat & 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) { + dev_err(omap->dev, "Arbitration lost\n"); + err |= OMAP_I2C_STAT_AL; + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AL); + } + + /* + * ProDB0017052: Clear ARDY bit twice + */ + if (stat & OMAP_I2C_STAT_ARDY) + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_ARDY); + + if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | + OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(omap, (OMAP_I2C_STAT_RRDY | + OMAP_I2C_STAT_RDR | + OMAP_I2C_STAT_XRDY | + OMAP_I2C_STAT_XDR | + OMAP_I2C_STAT_ARDY)); + break; + } + + if (stat & OMAP_I2C_STAT_RDR) { + u8 num_bytes = 1; + + if (omap->fifo_size) + num_bytes = omap->buf_len; + + if (omap->errata & I2C_OMAP_ERRATA_I207) { + i2c_omap_errata_i207(omap, stat); + num_bytes = (omap_i2c_read_reg(omap, + OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F; + } + + omap_i2c_receive_data(omap, num_bytes, true); + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RDR); + continue; + } + + if (stat & OMAP_I2C_STAT_RRDY) { + u8 num_bytes = 1; + + if (omap->threshold) + num_bytes = omap->threshold; + + omap_i2c_receive_data(omap, num_bytes, false); + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RRDY); + continue; + } + + if (stat & OMAP_I2C_STAT_XDR) { + u8 num_bytes = 1; + int ret; + + if (omap->fifo_size) + num_bytes = omap->buf_len; + + ret = omap_i2c_transmit_data(omap, num_bytes, true); + if (ret < 0) + break; + + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XDR); + continue; + } + + if (stat & OMAP_I2C_STAT_XRDY) { + u8 num_bytes = 1; + int ret; + + if (omap->threshold) + num_bytes = omap->threshold; + + ret = omap_i2c_transmit_data(omap, num_bytes, false); + if (ret < 0) + break; + + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XRDY); + continue; + } + + if (stat & OMAP_I2C_STAT_ROVR) { + dev_err(omap->dev, "Receive overrun\n"); + err |= OMAP_I2C_STAT_ROVR; + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_ROVR); + break; + } + + if (stat & OMAP_I2C_STAT_XUDF) { + dev_err(omap->dev, "Transmit underflow\n"); + err |= OMAP_I2C_STAT_XUDF; + omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XUDF); + break; + } + } while (stat); + + 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); + + return IRQ_HANDLED; +} + +static const struct i2c_algorithm omap_i2c_algo = { + .xfer = omap_i2c_xfer_irq, + .xfer_atomic = omap_i2c_xfer_polling, + .functionality = omap_i2c_func, +}; + +static const struct i2c_adapter_quirks omap_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +#ifdef CONFIG_OF +static struct omap_i2c_bus_platform_data omap2420_pdata = { + .rev = OMAP_I2C_IP_VERSION_1, + .flags = OMAP_I2C_FLAG_NO_FIFO | + OMAP_I2C_FLAG_SIMPLE_CLOCK | + OMAP_I2C_FLAG_16BIT_DATA_REG | + OMAP_I2C_FLAG_BUS_SHIFT_2, +}; + +static struct omap_i2c_bus_platform_data omap2430_pdata = { + .rev = OMAP_I2C_IP_VERSION_1, + .flags = OMAP_I2C_FLAG_BUS_SHIFT_2 | + OMAP_I2C_FLAG_FORCE_19200_INT_CLK, +}; + +static struct omap_i2c_bus_platform_data omap3_pdata = { + .rev = OMAP_I2C_IP_VERSION_1, + .flags = OMAP_I2C_FLAG_BUS_SHIFT_2, +}; + +static struct omap_i2c_bus_platform_data omap4_pdata = { + .rev = OMAP_I2C_IP_VERSION_2, +}; + +static const struct of_device_id omap_i2c_of_match[] = { + { + .compatible = "ti,omap4-i2c", + .data = &omap4_pdata, + }, + { + .compatible = "ti,omap3-i2c", + .data = &omap3_pdata, + }, + { + .compatible = "ti,omap2430-i2c", + .data = &omap2430_pdata, + }, + { + .compatible = "ti,omap2420-i2c", + .data = &omap2420_pdata, + }, + { } +}; +MODULE_DEVICE_TABLE(of, omap_i2c_of_match); +#endif + +#define OMAP_I2C_SCHEME(rev) ((rev & 0xc000) >> 14) + +#define OMAP_I2C_REV_SCHEME_0_MAJOR(rev) (rev >> 4) +#define OMAP_I2C_REV_SCHEME_0_MINOR(rev) (rev & 0xf) + +#define OMAP_I2C_REV_SCHEME_1_MAJOR(rev) ((rev & 0x0700) >> 7) +#define OMAP_I2C_REV_SCHEME_1_MINOR(rev) (rev & 0x1f) +#define OMAP_I2C_SCHEME_0 0 +#define OMAP_I2C_SCHEME_1 1 + +static int omap_i2c_get_scl(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC; +} + +static int omap_i2c_get_sda(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC; +} + +static void omap_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + if (val) + reg |= OMAP_I2C_SYSTEST_SCL_O; + else + reg &= ~OMAP_I2C_SYSTEST_SCL_O; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + /* enable test mode */ + reg |= OMAP_I2C_SYSTEST_ST_EN; + /* select SDA/SCL IO mode */ + reg |= 3 << OMAP_I2C_SYSTEST_TMODE_SHIFT; + /* set SCL to high-impedance state (reset value is 0) */ + reg |= OMAP_I2C_SYSTEST_SCL_O; + /* set SDA to high-impedance state (reset value is 0) */ + reg |= OMAP_I2C_SYSTEST_SDA_O; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + /* restore reset values */ + reg &= ~OMAP_I2C_SYSTEST_ST_EN; + reg &= ~OMAP_I2C_SYSTEST_TMODE_MASK; + reg &= ~OMAP_I2C_SYSTEST_SCL_O; + reg &= ~OMAP_I2C_SYSTEST_SDA_O; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = { + .get_scl = omap_i2c_get_scl, + .get_sda = omap_i2c_get_sda, + .set_scl = omap_i2c_set_scl, + .prepare_recovery = omap_i2c_prepare_recovery, + .unprepare_recovery = omap_i2c_unprepare_recovery, + .recover_bus = i2c_generic_scl_recovery, +}; + +static int +omap_i2c_probe(struct platform_device *pdev) +{ + struct omap_i2c_dev *omap; + struct i2c_adapter *adap; + const struct omap_i2c_bus_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct device_node *node = pdev->dev.of_node; + int irq; + int r; + u32 rev; + u16 minor, major; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + omap = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL); + if (!omap) + return -ENOMEM; + + omap->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(omap->base)) + return PTR_ERR(omap->base); + + if (pdev->dev.of_node) { + u32 freq = I2C_MAX_STANDARD_MODE_FREQ; + + pdata = device_get_match_data(&pdev->dev); + omap->flags = pdata->flags; + + of_property_read_u32(node, "clock-frequency", &freq); + /* convert DT freq value in Hz into kHz for speed */ + omap->speed = freq / 1000; + } else if (pdata != NULL) { + omap->speed = pdata->clkrate; + omap->flags = pdata->flags; + omap->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; + } + + omap->dev = &pdev->dev; + omap->irq = irq; + + platform_set_drvdata(pdev, omap); + init_completion(&omap->cmd_complete); + + omap->reg_shift = (omap->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3; + + pm_runtime_enable(omap->dev); + pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(omap->dev); + + r = pm_runtime_resume_and_get(omap->dev); + if (r < 0) + goto err_disable_pm; + + /* + * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2. + * On omap1/3/2 Offset 4 is IE Reg the bit [15:14] is 0 at reset. + * Also since the omap_i2c_read_reg uses reg_map_ip_* a + * readw_relaxed is done. + */ + rev = readw_relaxed(omap->base + 0x04); + + omap->scheme = OMAP_I2C_SCHEME(rev); + switch (omap->scheme) { + case OMAP_I2C_SCHEME_0: + omap->regs = (u8 *)reg_map_ip_v1; + omap->rev = omap_i2c_read_reg(omap, OMAP_I2C_REV_REG); + minor = OMAP_I2C_REV_SCHEME_0_MAJOR(omap->rev); + major = OMAP_I2C_REV_SCHEME_0_MAJOR(omap->rev); + break; + case OMAP_I2C_SCHEME_1: + default: + omap->regs = (u8 *)reg_map_ip_v2; + rev = (rev << 16) | + omap_i2c_read_reg(omap, OMAP_I2C_IP_V2_REVNB_LO); + minor = OMAP_I2C_REV_SCHEME_1_MINOR(rev); + major = OMAP_I2C_REV_SCHEME_1_MAJOR(rev); + omap->rev = rev; + } + + omap->errata = 0; + + if (omap->rev >= OMAP_I2C_REV_ON_2430 && + omap->rev < OMAP_I2C_REV_ON_4430_PLUS) + omap->errata |= I2C_OMAP_ERRATA_I207; + + if (omap->rev <= OMAP_I2C_REV_ON_3430_3530) + omap->errata |= I2C_OMAP_ERRATA_I462; + + if (!(omap->flags & OMAP_I2C_FLAG_NO_FIFO)) { + u16 s; + + /* Set up the fifo size - Get total size */ + s = (omap_i2c_read_reg(omap, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3; + omap->fifo_size = 0x8 << s; + + /* + * Set up notification threshold as half the total available + * size. This is to ensure that we can handle the status on int + * call back latencies. + */ + + omap->fifo_size = (omap->fifo_size / 2); + + if (omap->rev < OMAP_I2C_REV_ON_3630) + omap->b_hw = 1; /* Enable hardware fixes */ + + /* calculate wakeup latency constraint for MPU */ + if (omap->set_mpu_wkup_lat != NULL) + omap->latency = (1000000 * omap->fifo_size) / + (1000 * omap->speed / 8); + } + + omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(omap->mux_state)) { + r = PTR_ERR(omap->mux_state); + goto err_put_pm; + } + + /* reset ASAP, clearing any IRQs */ + r = omap_i2c_init(omap); + if (r) + goto err_put_pm; + + 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, + NULL, omap_i2c_isr_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + pdev->name, omap); + + if (r) { + dev_err(omap->dev, "failure requesting irq %i\n", omap->irq); + goto err_unuse_clocks; + } + + adap = &omap->adapter; + i2c_set_adapdata(adap, omap); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + strscpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + adap->algo = &omap_i2c_algo; + adap->quirks = &omap_i2c_quirks; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + adap->bus_recovery_info = &omap_i2c_bus_recovery_info; + + /* i2c device drivers may be active on return from add_adapter() */ + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) + goto err_unuse_clocks; + + dev_info(omap->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr, + major, minor, omap->speed); + + pm_runtime_put_autosuspend(omap->dev); + + return 0; + +err_unuse_clocks: + omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); +err_put_pm: + pm_runtime_put_sync(omap->dev); +err_disable_pm: + pm_runtime_dont_use_autosuspend(omap->dev); + pm_runtime_disable(&pdev->dev); + + return r; +} + +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) + dev_err(omap->dev, "Failed to resume hardware, skip disable\n"); + else + 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); +} + +static int omap_i2c_runtime_suspend(struct device *dev) +{ + struct omap_i2c_dev *omap = dev_get_drvdata(dev); + + omap->iestate = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG); + + if (omap->scheme == OMAP_I2C_SCHEME_0) + omap_i2c_write_reg(omap, OMAP_I2C_IE_REG, 0); + else + omap_i2c_write_reg(omap, OMAP_I2C_IP_V2_IRQENABLE_CLR, + OMAP_I2C_IP_V2_INTERRUPTS_MASK); + + if (omap->rev < OMAP_I2C_OMAP1_REV_2) { + omap_i2c_read_reg(omap, OMAP_I2C_IV_REG); /* Read clears */ + } else { + omap_i2c_write_reg(omap, OMAP_I2C_STAT_REG, omap->iestate); + + /* Flush posted write */ + omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); + } + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int omap_i2c_runtime_resume(struct device *dev) +{ + struct omap_i2c_dev *omap = dev_get_drvdata(dev); + + pinctrl_pm_select_default_state(dev); + + if (!omap->regs) + return 0; + + __omap_i2c_init(omap); + + 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 = { + 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) +}; + +static struct platform_driver omap_i2c_driver = { + .probe = omap_i2c_probe, + .remove = omap_i2c_remove, + .driver = { + .name = "omap_i2c", + .pm = pm_ptr(&omap_i2c_pm_ops), + .of_match_table = of_match_ptr(omap_i2c_of_match), + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init +omap_i2c_init_driver(void) +{ + return platform_driver_register(&omap_i2c_driver); +} +subsys_initcall(omap_i2c_init_driver); + +static void __exit omap_i2c_exit_driver(void) +{ + platform_driver_unregister(&omap_i2c_driver); +} +module_exit(omap_i2c_exit_driver); + +MODULE_AUTHOR("MontaVista Software, Inc. (and others)"); +MODULE_DESCRIPTION("TI OMAP I2C bus adapter"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap_i2c"); diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c new file mode 100644 index 000000000..c9b628923 --- /dev/null +++ b/drivers/i2c/busses/i2c-opal.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * IBM OPAL I2C driver + * Copyright (C) 2014 IBM + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int i2c_opal_translate_error(int rc) +{ + switch (rc) { + case OPAL_NO_MEM: + return -ENOMEM; + case OPAL_PARAMETER: + return -EINVAL; + case OPAL_I2C_ARBT_LOST: + return -EAGAIN; + case OPAL_I2C_TIMEOUT: + return -ETIMEDOUT; + case OPAL_I2C_NACK_RCVD: + return -ENXIO; + case OPAL_I2C_STOP_ERR: + return -EBUSY; + default: + return -EIO; + } +} + +static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req) +{ + struct opal_msg msg; + int token, rc; + + token = opal_async_get_token_interruptible(); + if (token < 0) { + if (token != -ERESTARTSYS) + pr_err("Failed to get the async token\n"); + + return token; + } + + rc = opal_i2c_request(token, bus_id, req); + if (rc != OPAL_ASYNC_COMPLETION) { + rc = i2c_opal_translate_error(rc); + goto exit; + } + + rc = opal_async_wait_response(token, &msg); + if (rc) + goto exit; + + rc = opal_get_async_rc(msg); + if (rc != OPAL_SUCCESS) { + rc = i2c_opal_translate_error(rc); + goto exit; + } + +exit: + opal_async_release_token(token); + return rc; +} + +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; + int rc, i; + + /* We only support fairly simple combinations here of one + * or two messages + */ + memset(&req, 0, sizeof(req)); + switch(num) { + case 1: + req.type = (msgs[0].flags & I2C_M_RD) ? + OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; + req.addr = cpu_to_be16(msgs[0].addr); + req.size = cpu_to_be32(msgs[0].len); + req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf)); + break; + case 2: + req.type = (msgs[1].flags & I2C_M_RD) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + req.addr = cpu_to_be16(msgs[0].addr); + req.subaddr_sz = msgs[0].len; + for (i = 0; i < msgs[0].len; i++) + req.subaddr = (req.subaddr << 8) | msgs[0].buf[i]; + req.subaddr = cpu_to_be32(req.subaddr); + req.size = cpu_to_be32(msgs[1].len); + req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf)); + break; + } + + rc = i2c_opal_send_request(opal_id, &req); + if (rc) + return rc; + + return num; +} + +static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + unsigned long opal_id = (unsigned long)adap->algo_data; + struct opal_i2c_request req; + u8 local[2]; + int rc; + + memset(&req, 0, sizeof(req)); + + req.addr = cpu_to_be16(addr); + switch (size) { + case I2C_SMBUS_BYTE: + req.buffer_ra = cpu_to_be64(__pa(&data->byte)); + req.size = cpu_to_be32(1); + fallthrough; + case I2C_SMBUS_QUICK: + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; + break; + case I2C_SMBUS_BYTE_DATA: + req.buffer_ra = cpu_to_be64(__pa(&data->byte)); + req.size = cpu_to_be32(1); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + case I2C_SMBUS_WORD_DATA: + if (!read_write) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + req.buffer_ra = cpu_to_be64(__pa(local)); + req.size = cpu_to_be32(2); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + req.buffer_ra = cpu_to_be64(__pa(&data->block[1])); + req.size = cpu_to_be32(data->block[0]); + req.subaddr = cpu_to_be32(command); + req.subaddr_sz = 1; + req.type = (read_write == I2C_SMBUS_READ) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; + break; + default: + return -EINVAL; + } + + rc = i2c_opal_send_request(opal_id, &req); + if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + + return rc; +} + +static u32 i2c_opal_func(struct i2c_adapter *adapter) +{ + 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_I2C_BLOCK; +} + +static const struct i2c_algorithm i2c_opal_algo = { + .xfer = i2c_opal_xfer, + .smbus_xfer = i2c_opal_smbus_xfer, + .functionality = i2c_opal_func, +}; + +/* + * For two messages, we basically support simple smbus transactions of a + * write-then-anything. + */ +static const struct i2c_adapter_quirks i2c_opal_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, + .max_comb_1st_msg_len = 4, +}; + +static int i2c_opal_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + const char *pname; + u32 opal_id; + int rc; + + if (!pdev->dev.of_node) + return -ENODEV; + + rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id); + if (rc) { + dev_err(&pdev->dev, "Missing ibm,opal-id property !\n"); + return -EIO; + } + + adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + adapter->algo = &i2c_opal_algo; + adapter->algo_data = (void *)(unsigned long)opal_id; + adapter->quirks = &i2c_opal_quirks; + adapter->dev.parent = &pdev->dev; + 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) + strscpy(adapter->name, pname, sizeof(adapter->name)); + else + strscpy(adapter->name, "opal", sizeof(adapter->name)); + + platform_set_drvdata(pdev, adapter); + rc = i2c_add_adapter(adapter); + if (rc) + dev_err(&pdev->dev, "Failed to register the i2c adapter\n"); + + return rc; +} + +static void i2c_opal_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + + i2c_del_adapter(adapter); +} + +static const struct of_device_id i2c_opal_of_match[] = { + { + .compatible = "ibm,opal-i2c", + }, + { } +}; +MODULE_DEVICE_TABLE(of, i2c_opal_of_match); + +static struct platform_driver i2c_opal_driver = { + .probe = i2c_opal_probe, + .remove = i2c_opal_remove, + .driver = { + .name = "i2c-opal", + .of_match_table = i2c_opal_of_match, + }, +}; + +static int __init i2c_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + return platform_driver_register(&i2c_opal_driver); +} +module_init(i2c_opal_init); + +static void __exit i2c_opal_exit(void) +{ + return platform_driver_unregister(&i2c_opal_driver); +} +module_exit(i2c_opal_exit); + +MODULE_AUTHOR("Neelesh Gupta "); +MODULE_DESCRIPTION("IBM OPAL I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c new file mode 100644 index 000000000..84a195e35 --- /dev/null +++ b/drivers/i2c/busses/i2c-owl.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Actions Semiconductor Owl SoC's I2C driver + * + * Copyright (c) 2014 Actions Semi Inc. + * Author: David Liu + * + * Copyright (c) 2018 Linaro Ltd. + * Author: Manivannan Sadhasivam + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C registers */ +#define OWL_I2C_REG_CTL 0x0000 +#define OWL_I2C_REG_CLKDIV 0x0004 +#define OWL_I2C_REG_STAT 0x0008 +#define OWL_I2C_REG_ADDR 0x000C +#define OWL_I2C_REG_TXDAT 0x0010 +#define OWL_I2C_REG_RXDAT 0x0014 +#define OWL_I2C_REG_CMD 0x0018 +#define OWL_I2C_REG_FIFOCTL 0x001C +#define OWL_I2C_REG_FIFOSTAT 0x0020 +#define OWL_I2C_REG_DATCNT 0x0024 +#define OWL_I2C_REG_RCNT 0x0028 + +/* I2Cx_CTL Bit Mask */ +#define OWL_I2C_CTL_RB BIT(1) +#define OWL_I2C_CTL_GBCC(x) (((x) & 0x3) << 2) +#define OWL_I2C_CTL_GBCC_NONE OWL_I2C_CTL_GBCC(0) +#define OWL_I2C_CTL_GBCC_START OWL_I2C_CTL_GBCC(1) +#define OWL_I2C_CTL_GBCC_STOP OWL_I2C_CTL_GBCC(2) +#define OWL_I2C_CTL_GBCC_RSTART OWL_I2C_CTL_GBCC(3) +#define OWL_I2C_CTL_IRQE BIT(5) +#define OWL_I2C_CTL_EN BIT(7) +#define OWL_I2C_CTL_AE BIT(8) +#define OWL_I2C_CTL_SHSM BIT(10) + +#define OWL_I2C_DIV_FACTOR(x) ((x) & 0xff) + +/* I2Cx_STAT Bit Mask */ +#define OWL_I2C_STAT_RACK BIT(0) +#define OWL_I2C_STAT_BEB BIT(1) +#define OWL_I2C_STAT_IRQP BIT(2) +#define OWL_I2C_STAT_LAB BIT(3) +#define OWL_I2C_STAT_STPD BIT(4) +#define OWL_I2C_STAT_STAD BIT(5) +#define OWL_I2C_STAT_BBB BIT(6) +#define OWL_I2C_STAT_TCB BIT(7) +#define OWL_I2C_STAT_LBST BIT(8) +#define OWL_I2C_STAT_SAMB BIT(9) +#define OWL_I2C_STAT_SRGC BIT(10) + +/* I2Cx_CMD Bit Mask */ +#define OWL_I2C_CMD_SBE BIT(0) +#define OWL_I2C_CMD_RBE BIT(4) +#define OWL_I2C_CMD_DE BIT(8) +#define OWL_I2C_CMD_NS BIT(9) +#define OWL_I2C_CMD_SE BIT(10) +#define OWL_I2C_CMD_MSS BIT(11) +#define OWL_I2C_CMD_WRS BIT(12) +#define OWL_I2C_CMD_SECL BIT(15) + +#define OWL_I2C_CMD_AS(x) (((x) & 0x7) << 1) +#define OWL_I2C_CMD_SAS(x) (((x) & 0x7) << 5) + +/* I2Cx_FIFOCTL Bit Mask */ +#define OWL_I2C_FIFOCTL_NIB BIT(0) +#define OWL_I2C_FIFOCTL_RFR BIT(1) +#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) +#define OWL_I2C_FIFOSTAT_TFD GENMASK(23, 16) +#define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8) + +/* I2C bus timeout */ +#define OWL_I2C_TIMEOUT_MS (4 * 1000) +#define OWL_I2C_TIMEOUT msecs_to_jiffies(OWL_I2C_TIMEOUT_MS) + +#define OWL_I2C_MAX_RETRIES 50 + +struct owl_i2c_dev { + struct i2c_adapter adap; + struct i2c_msg *msg; + struct completion msg_complete; + struct clk *clk; + spinlock_t lock; + void __iomem *base; + unsigned long clk_rate; + u32 bus_freq; + u32 msg_ptr; + int err; +}; + +static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state) +{ + unsigned int regval; + + regval = readl(reg); + + if (state) + regval |= val; + else + regval &= ~val; + + writel(regval, reg); +} + +static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev) +{ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, false); + mdelay(1); + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, true); + + /* Clear status registers */ + writel(0, i2c_dev->base + OWL_I2C_REG_STAT); +} + +static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev) +{ + unsigned int val, timeout = 0; + + /* Reset FIFO */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR, + true); + + /* Wait 50ms for FIFO reset complete */ + do { + val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL); + if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR))) + break; + usleep_range(500, 1000); + } while (timeout++ < OWL_I2C_MAX_RETRIES); + + if (timeout > OWL_I2C_MAX_RETRIES) { + dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev) +{ + unsigned int val; + + val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16); + + /* Set clock divider factor */ + writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV); +} + +static void owl_i2c_xfer_data(struct owl_i2c_dev *i2c_dev) +{ + struct i2c_msg *msg = i2c_dev->msg; + unsigned int stat, fifostat; + + i2c_dev->err = 0; + + /* Handle NACK from target */ + fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT); + if (fifostat & OWL_I2C_FIFOSTAT_RNB) { + i2c_dev->err = -ENXIO; + /* 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; + /* 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 */ + if (msg->flags & I2C_M_RD) { + while ((readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_RFE) && i2c_dev->msg_ptr < msg->len) { + msg->buf[i2c_dev->msg_ptr++] = readl(i2c_dev->base + + OWL_I2C_REG_RXDAT); + } + } else { + /* Handle the remaining bytes which were not sent */ + while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) { + writel(msg->buf[i2c_dev->msg_ptr++], + 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); + + /* 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(&i2c_dev->lock); + + return IRQ_HANDLED; +} + +static u32 owl_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int owl_i2c_check_bus_busy(struct i2c_adapter *adap) +{ + struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + unsigned long timeout; + + /* Check for Bus busy */ + timeout = jiffies + OWL_I2C_TIMEOUT; + while (readl(i2c_dev->base + OWL_I2C_REG_STAT) & OWL_I2C_STAT_BBB) { + if (time_after(jiffies, timeout)) { + dev_err(&adap->dev, "Bus busy timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +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; + unsigned long time_left, flags; + unsigned int i2c_cmd, val; + unsigned int addr; + int ret, idx; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + /* Reset I2C controller */ + owl_i2c_reset(i2c_dev); + + /* Set bus frequency */ + owl_i2c_set_freq(i2c_dev); + + /* + * Spinlock should be released before calling reset FIFO and + * bus busy check since those functions may sleep + */ + spin_unlock_irqrestore(&i2c_dev->lock, flags); + + /* Reset FIFO */ + ret = owl_i2c_reset_fifo(i2c_dev); + if (ret) + goto unlocked_err_exit; + + /* Check for bus busy */ + ret = owl_i2c_check_bus_busy(adap); + if (ret) + goto unlocked_err_exit; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + /* Check for Arbitration lost */ + val = readl(i2c_dev->base + OWL_I2C_REG_STAT); + if (val & OWL_I2C_STAT_LAB) { + val &= ~OWL_I2C_STAT_LAB; + writel(val, i2c_dev->base + OWL_I2C_REG_STAT); + ret = -EAGAIN; + goto err_exit; + } + + if (!atomic) + reinit_completion(&i2c_dev->msg_complete); + + /* Enable/disable I2C controller interrupt */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_IRQE, !atomic); + + /* + * 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 | + OWL_I2C_CMD_NS | OWL_I2C_CMD_DE | OWL_I2C_CMD_SBE; + + /* Handle repeated start condition */ + if (num > 1) { + /* Set internal address length and enable repeated start */ + i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) | + OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE; + + /* Write target address */ + addr = i2c_8bit_addr_from_msg(&msgs[0]); + writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT); + + /* Write internal register address */ + for (idx = 0; idx < msgs[0].len; idx++) + writel(msgs[0].buf[idx], + i2c_dev->base + OWL_I2C_REG_TXDAT); + + msg = &msgs[1]; + } else { + /* Set address length */ + i2c_cmd |= OWL_I2C_CMD_AS(1); + msg = &msgs[0]; + } + + i2c_dev->msg = msg; + i2c_dev->msg_ptr = 0; + + /* Set data count for the message */ + writel(msg->len, i2c_dev->base + OWL_I2C_REG_DATCNT); + + addr = i2c_8bit_addr_from_msg(msg); + writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT); + + if (!(msg->flags & I2C_M_RD)) { + /* Write data to FIFO */ + for (idx = 0; idx < msg->len; idx++) { + /* Check for FIFO full */ + if (readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_TFF) + break; + + writel(msg->buf[idx], + i2c_dev->base + OWL_I2C_REG_TXDAT); + } + + i2c_dev->msg_ptr = idx; + } + + /* Ignore the NACK if needed */ + if (msg->flags & I2C_M_IGNORE_NAK) + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_NIB, true); + else + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_NIB, false); + + /* Start the transfer */ + writel(i2c_cmd, i2c_dev->base + OWL_I2C_REG_CMD); + + spin_unlock_irqrestore(&i2c_dev->lock, flags); + + 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 (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); + goto err_exit; + } + + if (atomic) + owl_i2c_xfer_data(i2c_dev); + + ret = i2c_dev->err < 0 ? i2c_dev->err : num; + +err_exit: + spin_unlock_irqrestore(&i2c_dev->lock, flags); + +unlocked_err_exit: + /* Disable I2C controller */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, false); + + 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 = { + .xfer = owl_i2c_xfer, + .xfer_atomic = owl_i2c_xfer_atomic, + .functionality = owl_i2c_func, +}; + +static const struct i2c_adapter_quirks owl_i2c_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST, + .max_read_len = 240, + .max_write_len = 240, + .max_comb_1st_msg_len = 6, + .max_comb_2nd_msg_len = 240, +}; + +static int owl_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct owl_i2c_dev *i2c_dev; + int ret, irq; + + i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + 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) + return irq; + + if (of_property_read_u32(dev->of_node, "clock-frequency", + &i2c_dev->bus_freq)) + i2c_dev->bus_freq = I2C_MAX_STANDARD_MODE_FREQ; + + /* We support only frequencies of 100k and 400k for now */ + 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_enabled(dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(dev, "failed to enable clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + 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"); + return -EINVAL; + } + + init_completion(&i2c_dev->msg_complete); + spin_lock_init(&i2c_dev->lock); + i2c_dev->adap.owner = THIS_MODULE; + i2c_dev->adap.algo = &owl_i2c_algorithm; + i2c_dev->adap.timeout = OWL_I2C_TIMEOUT; + i2c_dev->adap.quirks = &owl_i2c_quirks; + i2c_dev->adap.dev.parent = dev; + i2c_dev->adap.dev.of_node = dev->of_node; + snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name), + "%s", "OWL I2C adapter"); + i2c_set_adapdata(&i2c_dev->adap, i2c_dev); + + platform_set_drvdata(pdev, i2c_dev); + + ret = devm_request_irq(dev, irq, owl_i2c_interrupt, 0, pdev->name, + i2c_dev); + if (ret) { + dev_err(dev, "failed to request irq %d\n", irq); + return ret; + } + + return i2c_add_adapter(&i2c_dev->adap); +} + +static const struct of_device_id owl_i2c_of_match[] = { + { .compatible = "actions,s500-i2c" }, + { .compatible = "actions,s700-i2c" }, + { .compatible = "actions,s900-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, owl_i2c_of_match); + +static struct platform_driver owl_i2c_driver = { + .probe = owl_i2c_probe, + .driver = { + .name = "owl-i2c", + .of_match_table = owl_i2c_of_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_platform_driver(owl_i2c_driver); + +MODULE_AUTHOR("David Liu "); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("Actions Semiconductor Owl SoC's I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c new file mode 100644 index 000000000..e438ec817 --- /dev/null +++ b/drivers/i2c/busses/i2c-parport.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ------------------------------------------------------------------------ * + * i2c-parport.c I2C bus over parallel port * + * ------------------------------------------------------------------------ * + Copyright (C) 2003-2011 Jean Delvare + + Based on older i2c-philips-par.c driver + Copyright (C) 1995-2000 Simon G. Vogl + With some changes from: + Frodo Looijaard + Kyösti Mälkki + + * ------------------------------------------------------------------------ */ + +#define pr_fmt(fmt) "i2c-parport: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ------------------------------------------------------ */ + +struct i2c_par { + struct pardevice *pdev; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; + struct list_head node; +}; + +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 ----------------------------------- */ + +static void port_write_data(struct parport *p, unsigned char d) +{ + parport_write_data(p, d); +} + +static void port_write_control(struct parport *p, unsigned char d) +{ + parport_write_control(p, d); +} + +static unsigned char port_read_data(struct parport *p) +{ + return parport_read_data(p); +} + +static unsigned char port_read_status(struct parport *p) +{ + return parport_read_status(p); +} + +static unsigned char port_read_control(struct parport *p) +{ + return parport_read_control(p); +} + +static void (* const port_write[])(struct parport *, unsigned char) = { + port_write_data, + NULL, + port_write_control, +}; + +static unsigned char (* const port_read[])(struct parport *) = { + port_read_data, + port_read_status, + port_read_control, +}; + +/* ----- Unified line operation functions --------------------------------- */ + +static inline void line_set(struct parport *data, int state, + const struct lineop *op) +{ + u8 oldval = port_read[op->port](data); + + /* Touch only the bit(s) needed */ + if ((op->inverted && !state) || (!op->inverted && state)) + port_write[op->port](data, oldval | op->val); + else + port_write[op->port](data, oldval & ~op->val); +} + +static inline int line_get(struct parport *data, + const struct lineop *op) +{ + u8 oldval = port_read[op->port](data); + + 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((struct parport *) data, state, &adapter_parm[type].setscl); +} + +static void parport_setsda(void *data, int state) +{ + line_set((struct parport *) data, state, &adapter_parm[type].setsda); +} + +static int parport_getscl(void *data) +{ + return line_get((struct parport *) data, &adapter_parm[type].getscl); +} + +static int parport_getsda(void *data) +{ + return line_get((struct parport *) data, &adapter_parm[type].getsda); +} + +/* Encapsulate the functions above in the correct structure. + Note that this is only a template, from which the real structures are + copied. The attaching code will set getscl to NULL for adapters that + cannot read SCL back, and will also make the data field point to + the parallel port structure. */ +static const struct i2c_algo_bit_data parport_algo_data = { + .setsda = parport_setsda, + .setscl = parport_setscl, + .getsda = parport_getsda, + .getscl = parport_getscl, + .udelay = 10, /* ~50 kbps */ + .timeout = HZ, +}; + +/* ----- I2c and parallel port call-back functions and structures --------- */ + +static void i2c_parport_irq(void *data) +{ + struct i2c_par *adapter = data; + struct i2c_client *ara = adapter->ara; + + if (ara) { + dev_dbg(&ara->dev, "SMBus alert received\n"); + i2c_handle_smbus_alert(ara); + } else + dev_dbg(&adapter->adapter.dev, + "SMBus alert received but no ARA client!\n"); +} + +static void i2c_parport_attach(struct parport *port) +{ + struct i2c_par *adapter; + 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; + if (port->number == parport[i]) + break; + } + if (i == MAX_DEVICE) { + pr_debug("Not using parport%d.\n", port->number); + return; + } + + adapter = kzalloc_obj(struct i2c_par); + if (!adapter) + return; + memset(&i2c_parport_cb, 0, sizeof(i2c_parport_cb)); + i2c_parport_cb.flags = PARPORT_FLAG_EXCL; + i2c_parport_cb.irq_func = i2c_parport_irq; + i2c_parport_cb.private = adapter; + + pr_debug("attaching to %s\n", port->name); + parport_disable_irq(port); + adapter->pdev = parport_register_dev_model(port, "i2c-parport", + &i2c_parport_cb, i); + if (!adapter->pdev) { + pr_err("Unable to register with parport\n"); + goto err_free; + } + + /* Fill the rest of the structure */ + adapter->adapter.owner = THIS_MODULE; + adapter->adapter.class = I2C_CLASS_HWMON; + 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 */ + if (!adapter_parm[type].getscl.val) { + adapter->algo_data.getscl = NULL; + adapter->algo_data.udelay = 50; /* ~10 kbps */ + } + adapter->algo_data.data = port; + adapter->adapter.algo_data = &adapter->algo_data; + adapter->adapter.dev.parent = port->physport->dev; + + if (parport_claim_or_block(adapter->pdev) < 0) { + dev_err(&adapter->pdev->dev, + "Could not claim parallel port\n"); + goto err_unregister; + } + + /* Reset hardware to a sane state (SCL and SDA high) */ + parport_setsda(port, 1); + parport_setscl(port, 1); + /* Other init if needed (power on...) */ + if (adapter_parm[type].init.val) { + line_set(port, 1, &adapter_parm[type].init); + /* Give powered devices some time to settle */ + msleep(100); + } + + if (i2c_bit_add_bus(&adapter->adapter) < 0) { + dev_err(&adapter->pdev->dev, "Unable to register with I2C\n"); + goto err_unregister; + } + + /* Setup SMBus alert if supported */ + if (adapter_parm[type].smbus_alert) { + 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 { + dev_warn(&adapter->pdev->dev, + "Failed to register ARA client\n"); + } + } + + /* Add the new adapter to the list */ + mutex_lock(&adapter_list_lock); + list_add_tail(&adapter->node, &adapter_list); + mutex_unlock(&adapter_list_lock); + return; + + err_unregister: + parport_release(adapter->pdev); + parport_unregister_device(adapter->pdev); + err_free: + kfree(adapter); +} + +static void i2c_parport_detach(struct parport *port) +{ + struct i2c_par *adapter, *_n; + + /* Walk the list */ + mutex_lock(&adapter_list_lock); + list_for_each_entry_safe(adapter, _n, &adapter_list, node) { + if (adapter->pdev->port == port) { + if (adapter->ara) { + parport_disable_irq(port); + i2c_unregister_device(adapter->ara); + } + i2c_del_adapter(&adapter->adapter); + + /* Un-init if needed (power off...) */ + if (adapter_parm[type].init.val) + line_set(port, 0, &adapter_parm[type].init); + + parport_release(adapter->pdev); + parport_unregister_device(adapter->pdev); + list_del(&adapter->node); + kfree(adapter); + } + } + mutex_unlock(&adapter_list_lock); +} + +static struct parport_driver i2c_parport_driver = { + .name = "i2c-parport", + .match_port = i2c_parport_attach, + .detach = i2c_parport_detach, +}; +module_parport_driver(i2c_parport_driver); + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("I2C bus over parallel port"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-pasemi-core.c b/drivers/i2c/busses/i2c-pasemi-core.c new file mode 100644 index 000000000..f4eca44ed --- /dev/null +++ b/drivers/i2c/busses/i2c-pasemi-core.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * SMBus host driver for PA Semi PWRficient + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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) + +/* + * 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, "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 = 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_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); + + 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) +{ + unsigned int status; + + 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; + } + } + + /* Controller timeout? */ + if (status & SMSTA_TOM) { + dev_err(smbus->dev, "Controller timeout, status 0x%08x\n", status); + return -EIO; + } + + /* 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); + + return 0; +} + +static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter, + struct i2c_msg *msg, int stop) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + int read, i, err; + u32 rd; + + read = msg->flags & I2C_M_RD ? 1 : 0; + + TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg)); + + if (read) { + TXFIFO_WR(smbus, msg->len | MTXFIFO_READ | + (stop ? MTXFIFO_STOP : 0)); + + err = pasemi_smb_waitready(smbus); + if (err) + goto reset_out; + + for (i = 0; i < msg->len; i++) { + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + msg->buf[i] = rd & MRXFIFO_DATA_M; + } + } else { + for (i = 0; i < msg->len - 1; i++) + TXFIFO_WR(smbus, msg->buf[i]); + + 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: + pasemi_reset(smbus); + return err; +} + +static int pasemi_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + int ret, i; + + 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))); + + return ret ? ret : num; +} + +static int pasemi_smb_xfer(struct i2c_adapter *adapter, + u16 addr, unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + unsigned int rd; + int read_flag, err; + int len = 0, i; + + /* All our ops take 8-bit shifted addresses */ + addr <<= 1; + read_flag = read_write == I2C_SMBUS_READ; + + err = pasemi_smb_clear(smbus); + if (err) + return err; + + switch (size) { + case I2C_SMBUS_QUICK: + TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START | + MTXFIFO_STOP); + break; + case I2C_SMBUS_BYTE: + TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START); + if (read_write) + TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ); + else + TXFIFO_WR(smbus, MTXFIFO_STOP | command); + break; + case I2C_SMBUS_BYTE_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP); + } else { + TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte); + } + break; + case I2C_SMBUS_WORD_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP); + } else { + TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8)); + } + break; + case I2C_SMBUS_BLOCK_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 1 | MTXFIFO_READ); + rd = RXFIFO_RD(smbus); + len = min_t(u8, (rd & MRXFIFO_DATA_M), + I2C_SMBUS_BLOCK_MAX); + TXFIFO_WR(smbus, len | MTXFIFO_READ | + MTXFIFO_STOP); + } else { + len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX); + TXFIFO_WR(smbus, len); + for (i = 1; i < len; i++) + TXFIFO_WR(smbus, data->block[i]); + TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP); + } + break; + case I2C_SMBUS_PROC_CALL: + read_write = I2C_SMBUS_READ; + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ); + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1); + read_write = I2C_SMBUS_READ; + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + TXFIFO_WR(smbus, len); + for (i = 1; i <= len; i++) + TXFIFO_WR(smbus, data->block[i]); + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ); + TXFIFO_WR(smbus, MTXFIFO_READ | 1); + rd = RXFIFO_RD(smbus); + len = min_t(u8, (rd & MRXFIFO_DATA_M), + I2C_SMBUS_BLOCK_MAX - len); + TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); + break; + + default: + dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); + return -EINVAL; + } + + err = pasemi_smb_waitready(smbus); + if (err) + goto reset_out; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->byte = rd & MRXFIFO_DATA_M; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->word = rd & MRXFIFO_DATA_M; + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->word |= (rd & MRXFIFO_DATA_M) << 8; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + data->block[0] = len; + for (i = 1; i <= len; i ++) { + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->block[i] = rd & MRXFIFO_DATA_M; + } + break; + } + + return 0; + + reset_out: + pasemi_reset(smbus); + return err; +} + +static u32 pasemi_smb_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_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C; +} + +static const struct i2c_algorithm smbus_algorithm = { + .xfer = pasemi_i2c_xfer, + .smbus_xfer = pasemi_smb_xfer, + .functionality = pasemi_smb_func, +}; + +int pasemi_i2c_common_probe(struct pasemi_smbus *smbus) +{ + int error; + + smbus->adapter.owner = THIS_MODULE; + snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), + "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 = smbus->dev; + smbus->use_irq = 0; + init_completion(&smbus->irq_completion); + + if (smbus->hw_rev != PASEMI_HW_REV_PCI) + smbus->hw_rev = reg_read(smbus, REG_REV); + + reg_write(smbus, REG_IMASK, 0); + + pasemi_reset(smbus); + + error = devm_i2c_add_adapter(smbus->dev, &smbus->adapter); + if (error) + return error; + + return 0; +} +EXPORT_SYMBOL_GPL(pasemi_i2c_common_probe); + +irqreturn_t pasemi_irq_handler(int irq, void *dev_id) +{ + struct pasemi_smbus *smbus = dev_id; + + reg_write(smbus, REG_IMASK, 0); + complete(&smbus->irq_completion); + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(pasemi_irq_handler); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Olof Johansson "); +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 000000000..88821f4e8 --- /dev/null +++ b/drivers/i2c/busses/i2c-pasemi-core.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000..b9ccb54ec --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 000000000..a486a37d3 --- /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 +#include +#include +#include +#include +#include +#include + +#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 "); +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 new file mode 100644 index 000000000..0cbf2f509 --- /dev/null +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -0,0 +1,201 @@ +// 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER "i2c-pca-isa" +#define IO_SIZE 4 + +static unsigned long base; +static int irq = -1; + +/* Data sheet recommends 59kHz for 100kHz operation due to variation + * in the actual clock rate */ +static int clock = 59000; + +static struct i2c_adapter pca_isa_ops; +static wait_queue_head_t pca_wait; + +static void pca_isa_writebyte(void *pd, int reg, int val) +{ +#ifdef DEBUG_IO + static char *names[] = { "T/O", "DAT", "ADR", "CON" }; + printk(KERN_DEBUG "*** write %s at %#lx <= %#04x\n", names[reg], + base+reg, val); +#endif + outb(val, base+reg); +} + +static int pca_isa_readbyte(void *pd, int reg) +{ + int res = inb(base+reg); +#ifdef DEBUG_IO + { + static char *names[] = { "STA", "DAT", "ADR", "CON" }; + printk(KERN_DEBUG "*** read %s => %#04x\n", names[reg], res); + } +#endif + return res; +} + +static int pca_isa_waitforcompletion(void *pd) +{ + unsigned long timeout; + long ret; + + if (irq > -1) { + ret = wait_event_timeout(pca_wait, + pca_isa_readbyte(pd, I2C_PCA_CON) + & I2C_PCA_CON_SI, pca_isa_ops.timeout); + } else { + /* Do polling */ + timeout = jiffies + pca_isa_ops.timeout; + do { + ret = time_before(jiffies, timeout); + if (pca_isa_readbyte(pd, I2C_PCA_CON) + & I2C_PCA_CON_SI) + break; + udelay(100); + } while (ret); + } + + return ret > 0; +} + +static void pca_isa_resetchip(void *pd) +{ + /* apparently only an external reset will do it. not a lot can be done */ + printk(KERN_WARNING DRIVER ": Haven't figured out how to do a reset yet\n"); +} + +static irqreturn_t pca_handler(int this_irq, void *dev_id) { + wake_up(&pca_wait); + return IRQ_HANDLED; +} + +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_cb = pca_isa_waitforcompletion, + .reset_chip = pca_isa_resetchip, +}; + +static struct i2c_adapter pca_isa_ops = { + .owner = THIS_MODULE, + .algo_data = &pca_isa_data, + .name = "PCA9564/PCA9665 ISA Adapter", + .timeout = HZ, +}; + +static int pca_isa_match(struct device *dev, unsigned int id) +{ + int match = base != 0; + + if (match) { + if (irq <= -1) + dev_warn(dev, "Using polling mode (specify irq)\n"); + } else + dev_err(dev, "Please specify I/O base\n"); + + return match; +} + +static int pca_isa_probe(struct device *dev, unsigned int id) +{ + init_waitqueue_head(&pca_wait); + + dev_info(dev, "i/o base %#08lx. irq %d\n", base, irq); + +#ifdef CONFIG_PPC + if (check_legacy_ioport(base)) { + dev_err(dev, "I/O address %#08lx is not available\n", base); + goto out; + } +#endif + + if (!request_region(base, IO_SIZE, "i2c-pca-isa")) { + dev_err(dev, "I/O address %#08lx is in use\n", base); + goto out; + } + + if (irq > -1) { + if (request_irq(irq, pca_handler, 0, "i2c-pca-isa", &pca_isa_ops) < 0) { + dev_err(dev, "Request irq%d failed\n", irq); + goto out_region; + } + } + + pca_isa_data.i2c_clock = clock; + if (i2c_pca_add_bus(&pca_isa_ops) < 0) { + dev_err(dev, "Failed to add i2c bus\n"); + goto out_irq; + } + + return 0; + + out_irq: + if (irq > -1) + free_irq(irq, &pca_isa_ops); + out_region: + release_region(base, IO_SIZE); + out: + return -ENODEV; +} + +static void pca_isa_remove(struct device *dev, unsigned int id) +{ + i2c_del_adapter(&pca_isa_ops); + + if (irq > -1) { + disable_irq(irq); + free_irq(irq, &pca_isa_ops); + } + release_region(base, IO_SIZE); +} + +static struct isa_driver pca_isa_driver = { + .match = pca_isa_match, + .probe = pca_isa_probe, + .remove = pca_isa_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER, + } +}; + +MODULE_AUTHOR("Ian Campbell "); +MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver"); +MODULE_LICENSE("GPL"); + +module_param_hw(base, ulong, ioport, 0); +MODULE_PARM_DESC(base, "I/O base address"); +module_param_hw(irq, int, irq, 0); +MODULE_PARM_DESC(irq, "IRQ"); +module_param(clock, int, 0); +MODULE_PARM_DESC(clock, "Clock rate in hertz.\n\t\t" + "For PCA9564: 330000,288000,217000,146000," + "88000,59000,44000,36000\n" + "\t\tFor PCA9665:\tStandard: 60300 - 100099\n" + "\t\t\t\tFast: 100100 - 400099\n" + "\t\t\t\tFast+: 400100 - 10000099\n" + "\t\t\t\tTurbo: Up to 1265800"); +module_isa_driver(pca_isa_driver, 1); diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c new file mode 100644 index 000000000..c0f35ebbe --- /dev/null +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c_pca_platform.c + * + * Platform driver for the PCA9564 I2C controller. + * + * Copyright (C) 2008 Pengutronix + * + + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct i2c_pca_pf_data { + void __iomem *reg_base; + int irq; /* if 0, use polling */ + struct gpio_desc *gpio; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_algo_pca_data algo_data; +}; + +/* Read/Write functions for different register alignments */ + +static int i2c_pca_pf_readbyte8(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg); +} + +static int i2c_pca_pf_readbyte16(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg * 2); +} + +static int i2c_pca_pf_readbyte32(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg * 4); +} + +static void i2c_pca_pf_writebyte8(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg); +} + +static void i2c_pca_pf_writebyte16(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg * 2); +} + +static void i2c_pca_pf_writebyte32(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg * 4); +} + + +static int i2c_pca_pf_waitforcompletion(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + unsigned long timeout; + long ret; + + if (i2c->irq) { + ret = wait_event_timeout(i2c->wait, + i2c->algo_data.read_byte(i2c, I2C_PCA_CON) + & I2C_PCA_CON_SI, i2c->adap.timeout); + } else { + /* Do polling */ + timeout = jiffies + i2c->adap.timeout; + do { + ret = time_before(jiffies, timeout); + if (i2c->algo_data.read_byte(i2c, I2C_PCA_CON) + & I2C_PCA_CON_SI) + break; + udelay(100); + } while (ret); + } + + return ret > 0; +} + +static void i2c_pca_pf_dummyreset(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + + dev_warn(&i2c->adap.dev, "No reset-pin found. Chip may get stuck!\n"); +} + +static void i2c_pca_pf_resetchip(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + + gpiod_set_value(i2c->gpio, 1); + ndelay(100); + gpiod_set_value(i2c->gpio, 0); +} + +static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) +{ + struct i2c_pca_pf_data *i2c = dev_id; + + if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) + return IRQ_NONE; + + wake_up(&i2c->wait); + + return IRQ_HANDLED; +} + + +static int i2c_pca_pf_probe(struct platform_device *pdev) +{ + struct i2c_pca_pf_data *i2c; + struct resource *res; + struct i2c_pca9564_pf_platform_data *platform_data = + dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int ret = 0; + int irq; + + irq = platform_get_irq_optional(pdev, 0); + /* If irq is 0, we do polling. */ + if (irq < 0) + irq = 0; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + 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->irq = irq; + + i2c->adap.nr = pdev->id; + i2c->adap.owner = THIS_MODULE; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), + "PCA9564/PCA9665 at 0x%08lx", + (unsigned long) res->start); + i2c->adap.algo_data = &i2c->algo_data; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; + + i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(i2c->gpio)) + return PTR_ERR(i2c->gpio); + + i2c->adap.timeout = HZ; + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &i2c->algo_data.i2c_clock); + if (ret) + i2c->algo_data.i2c_clock = 59000; + + if (platform_data) { + i2c->adap.timeout = platform_data->timeout; + i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed; + } + + i2c->algo_data.data = i2c; + i2c->algo_data.wait_for_completion_cb = i2c_pca_pf_waitforcompletion; + if (i2c->gpio) + i2c->algo_data.reset_chip = i2c_pca_pf_resetchip; + else + i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset; + + switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_32BIT: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte32; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte32; + break; + case IORESOURCE_MEM_16BIT: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte16; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte16; + break; + case IORESOURCE_MEM_8BIT: + default: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte8; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte8; + break; + } + + if (irq) { + ret = devm_request_irq(&pdev->dev, irq, i2c_pca_pf_handler, + IRQF_TRIGGER_FALLING, pdev->name, i2c); + if (ret) + return ret; + } + + ret = i2c_pca_add_numbered_bus(&i2c->adap); + if (ret) + return ret; + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "registered.\n"); + + return 0; +} + +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); +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_pca_of_match_table[] = { + { .compatible = "nxp,pca9564" }, + { .compatible = "nxp,pca9665" }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_pca_of_match_table); +#endif + +static struct platform_driver i2c_pca_pf_driver = { + .probe = i2c_pca_pf_probe, + .remove = i2c_pca_pf_remove, + .driver = { + .name = "i2c-pca-platform", + .of_match_table = of_match_ptr(i2c_pca_of_match_table), + }, +}; + +module_platform_driver(i2c_pca_pf_driver); + +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("I2C-PCA9564/PCA9665 platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c new file mode 100644 index 000000000..7b6a4e201 --- /dev/null +++ b/drivers/i2c/busses/i2c-piix4.c @@ -0,0 +1,1158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1998 - 2002 Frodo Looijaard and + Philip Edelbrock + +*/ + +/* + Supports: + Intel PIIX4, 440MX + 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 + SMBus interfaces. + The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS). + For devices supporting multiple ports the i2c_adapter should provide + an i2c_algorithm to access them. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-piix4.h" + +/* count for request_region */ +#define SMBIOSIZE 9 + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C + +/* Multi-port constants */ +#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 + +/* + * SB800 port is selected by bits 2:1 of the smb_en register (0x2c) + * or the smb_sel register (0x2e), depending on bit 0 of register 0x2f. + * Hudson-2/Bolton port is always selected by bits 2:1 of register 0x2f. + */ +#define SB800_PIIX4_PORT_IDX 0x2c +#define SB800_PIIX4_PORT_IDX_ALT 0x2e +#define SB800_PIIX4_PORT_IDX_SEL 0x2f +#define SB800_PIIX4_PORT_IDX_MASK 0x06 +#define SB800_PIIX4_PORT_IDX_SHIFT 1 + +/* 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 + PIIX4. DANGEROUS! */ +static int force; +module_param (force, int, 0); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr; +module_param_hw(force_addr, int, ioport, 0); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +static int srvrworks_csb5_delay; +static struct pci_driver piix4_driver; + +static const struct dmi_system_id piix4_dmi_blacklist[] = { + { + .ident = "Sapphire AM2RD790", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), + DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), + }, + }, + { + .ident = "DFI Lanparty UT 790FX", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), + DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), + }, + }, + { } +}; + +/* The IBM entry is in a separate table because we only check it + on Intel-based systems */ +static const struct dmi_system_id piix4_dmi_ibm[] = { + { + .ident = "IBM", + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, + }, + { } +}; + +/* + * SB800 globals + */ +static u8 piix4_port_sel_sb800; +static u8 piix4_port_mask_sb800; +static u8 piix4_port_shift_sb800; +static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { + " port 0", " port 2", " port 3", " port 4" +}; +static const char *piix4_aux_port_name_sb800 = " port 1"; + +struct i2c_piix4_adapdata { + unsigned short smba; + + /* SB800 */ + 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) +{ + unsigned char temp; + unsigned short piix4_smba; + + if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && + (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) + srvrworks_csb5_delay = 1; + + /* On some motherboards, it was reported that accessing the SMBus + caused severe hardware problems */ + if (dmi_check_system(piix4_dmi_blacklist)) { + dev_err(&PIIX4_dev->dev, + "Accessing the SMBus on this system is unsafe!\n"); + return -EPERM; + } + + /* Don't access SMBus on IBM systems which get corrupted eeproms */ + if (dmi_check_system(piix4_dmi_ibm) && + PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { + dev_err(&PIIX4_dev->dev, "IBM system detected; this module " + "may corrupt your serial eeprom! Refusing to load " + "module!\n"); + return -EPERM; + } + + /* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + dev_err(&PIIX4_dev->dev, "SMBus base address " + "uninitialized - upgrade BIOS or use " + "force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", + piix4_smba); + return -EBUSY; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); + + /* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " + "new address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { + /* This should never need to be done, but has been + * noted that many Dell machines have the SMBus + * interface on the PIIX4 disabled!? NOTE: This assumes + * I/O space and other allocations WERE done by the + * Bios! Don't complain if your hardware does weird + * things after enabling this. :') Check for Bios + * updates before resorting to this. + */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + dev_notice(&PIIX4_dev->dev, + "WARNING: SMBus interface has been FORCEFULLY ENABLED!\n"); + } else { + dev_err(&PIIX4_dev->dev, + "SMBus Host Controller not enabled!\n"); + release_region(piix4_smba, SMBIOSIZE); + return -ENODEV; + } + } + + if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) + dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); + else if ((temp & 0x0E) == 0) + dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); + else + dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " + "(or code out of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + dev_info(&PIIX4_dev->dev, + "SMBus Host Controller at 0x%x, revision %d\n", + piix4_smba, temp); + + 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 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) { + dev_err(&PIIX4_dev->dev, "SMBus does not support " + "forcing address!\n"); + return -EINVAL; + } + + /* Determine the address of the SMBus areas */ + if ((PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && + PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && + 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->vendor == PCI_VENDOR_ID_HYGON && + PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) + smb_en = 0x00; + else + smb_en = (aux) ? 0x28 : 0x2c; + + retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status, + &piix4_smba); + + if (retval) + return retval; + + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", + piix4_smba); + return -EBUSY; + } + + /* Aux SMBus does not support IRQ information */ + if (aux) { + dev_info(&PIIX4_dev->dev, + "Auxiliary SMBus Host Controller at 0x%x\n", + piix4_smba); + return piix4_smba; + } + + /* Request the SMBus I2C bus config region */ + if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { + dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " + "0x%x already in use!\n", piix4_smba + i2ccfg_offset); + release_region(piix4_smba, SMBIOSIZE); + return -EBUSY; + } + i2ccfg = inb_p(piix4_smba + i2ccfg_offset); + release_region(piix4_smba + i2ccfg_offset, 1); + + if (i2ccfg & 1) + dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); + else + dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); + + dev_info(&PIIX4_dev->dev, + "SMBus Host Controller at 0x%x, revision %d\n", + piix4_smba, i2ccfg >> 4); + + /* Find which register is used for port selection */ + 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; + } 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; + } + } else { + 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 retval; + } + + outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); + port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1); + piix4_port_sel_sb800 = (port_sel & 0x01) ? + SB800_PIIX4_PORT_IDX_ALT : + SB800_PIIX4_PORT_IDX; + piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; + piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; + piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); + } + + dev_info(&PIIX4_dev->dev, + "Using register 0x%02x for SMBus port selection\n", + (unsigned int)piix4_port_sel_sb800); + + return piix4_smba; +} + +static int piix4_setup_aux(struct pci_dev *PIIX4_dev, + const struct pci_device_id *id, + unsigned short base_reg_addr) +{ + /* Set up auxiliary SMBus controllers found on some + * AMD chipsets e.g. SP5100 (SB700 derivative) */ + + unsigned short piix4_smba; + + /* Read address of auxiliary SMBus controller */ + pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); + if ((piix4_smba & 1) == 0) { + dev_dbg(&PIIX4_dev->dev, + "Auxiliary SMBus controller not enabled\n"); + return -ENODEV; + } + + piix4_smba &= 0xfff0; + if (piix4_smba == 0) { + dev_dbg(&PIIX4_dev->dev, + "Auxiliary SMBus base address uninitialized\n"); + return -ENODEV; + } + + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " + "already in use!\n", piix4_smba); + return -EBUSY; + } + + dev_info(&PIIX4_dev->dev, + "Auxiliary SMBus Host Controller at 0x%x\n", + piix4_smba); + + return piix4_smba; +} + +int piix4_transaction(struct i2c_adapter *piix4_adapter, unsigned short piix4_smba) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&piix4_adapter->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " + "Resetting...\n", temp); + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&piix4_adapter->dev, "Successful!\n"); + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ + usleep_range(2000, 2100); + else + usleep_range(250, 500); + + while ((++timeout < MAX_TIMEOUT) && + ((temp = inb_p(SMBHSTSTS)) & 0x01)) + usleep_range(250, 500); + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) { + dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & 0x10) { + result = -EIO; + dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); + } + + if (temp & 0x08) { + result = -EIO; + dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be " + "locked until next hard reset. (sorry!)\n"); + /* Clock stops and target is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -ENXIO; + dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_err(&piix4_adapter->dev, "Failed reset at end of " + "transaction (%02x)\n", temp); + } + dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + 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, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + unsigned short piix4_smba = adapdata->smba; + int i, len; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p((addr << 1) | read_write, + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p((addr << 1) | read_write, + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + outb_p(len, SMBHSTDAT0); + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + status = piix4_transaction(adap, piix4_smba); + if (status) + return status; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +static uint8_t piix4_imc_read(uint8_t idx) +{ + outb_p(idx, KERNCZ_IMC_IDX); + return inb_p(KERNCZ_IMC_DATA); +} + +static void piix4_imc_write(uint8_t idx, uint8_t value) +{ + outb_p(idx, KERNCZ_IMC_IDX); + outb_p(value, KERNCZ_IMC_DATA); +} + +static int piix4_imc_sleep(void) +{ + int timeout = MAX_TIMEOUT; + + if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) + return -EBUSY; + + /* clear response register */ + piix4_imc_write(0x82, 0x00); + /* request ownership flag */ + piix4_imc_write(0x83, 0xB4); + /* kick off IMC Mailbox command 96 */ + piix4_imc_write(0x80, 0x96); + + while (timeout--) { + if (piix4_imc_read(0x82) == 0xfa) { + release_region(KERNCZ_IMC_IDX, 2); + return 0; + } + usleep_range(1000, 2000); + } + + release_region(KERNCZ_IMC_IDX, 2); + return -ETIMEDOUT; +} + +static void piix4_imc_wakeup(void) +{ + int timeout = MAX_TIMEOUT; + + if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) + return; + + /* clear response register */ + piix4_imc_write(0x82, 0x00); + /* release ownership flag */ + piix4_imc_write(0x83, 0xB5); + /* kick off IMC Mailbox command 96 */ + piix4_imc_write(0x80, 0x96); + + while (timeout--) { + if (piix4_imc_read(0x82) == 0xfa) + break; + usleep_range(1000, 2000); + } + + 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). + * Returns negative errno on error. + * + * Note: The selected port must be returned to the initial selection to avoid + * problems on certain systems. + */ +static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + unsigned short piix4_smba = adapdata->smba; + int retries = MAX_TIMEOUT; + int smbslvcnt; + u8 prev_port; + int retval; + + 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); + do { + outb_p(smbslvcnt | 0x10, SMBSLVCNT); + + /* Check the semaphore status */ + smbslvcnt = inb_p(SMBSLVCNT); + if (smbslvcnt & 0x10) + break; + + usleep_range(1000, 2000); + } while (--retries); + /* SMBus is still owned by the IMC, we give up */ + if (!retries) { + retval = -EBUSY; + goto release; + } + + /* + * Notify the IMC (Integrated Micro Controller) if required. + * Among other responsibilities, the IMC is in charge of monitoring + * the System fans and temperature sensors, and act accordingly. + * All this is done through SMBus and can/will collide + * with our transactions if they are long (BLOCK_DATA). + * Therefore we need to request the ownership flag during those + * transactions. + */ + if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) { + int ret; + + ret = piix4_imc_sleep(); + switch (ret) { + case -EBUSY: + dev_warn(&adap->dev, + "IMC base address index region 0x%x already in use.\n", + KERNCZ_IMC_IDX); + break; + case -ETIMEDOUT: + dev_warn(&adap->dev, + "Failed to communicate with the IMC.\n"); + break; + default: + break; + } + + /* If IMC communication fails do not retry */ + if (ret) { + dev_warn(&adap->dev, + "Continuing without IMC notification.\n"); + adapdata->notify_imc = false; + } + } + + prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg); + + retval = piix4_access(adap, addr, flags, read_write, + command, size, data); + + piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg); + + /* Release the semaphore */ + outb_p(smbslvcnt | 0x20, SMBSLVCNT); + + if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) + piix4_imc_wakeup(); + +release: + piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg); + return retval; +} + +static u32 piix4_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_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = piix4_access, + .functionality = piix4_func, +}; + +static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { + .smbus_xfer = piix4_access_sb800, + .functionality = piix4_func, +}; + +static const struct pci_device_id piix4_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, + { 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, + PCI_DEVICE_ID_SERVERWORKS_CSB5) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB6) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, + { 0, } +}; + +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, + u8 hw_port_nr, const char *name, + struct i2c_adapter **padap) +{ + struct i2c_adapter *adap; + struct i2c_piix4_adapdata *adapdata; + int retval; + + adap = kzalloc_obj(*adap); + if (adap == NULL) { + release_region(smba, SMBIOSIZE); + return -ENOMEM; + } + + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 + : &smbus_algorithm; + + adapdata = kzalloc_obj(*adapdata); + if (adapdata == NULL) { + kfree(adap); + release_region(smba, SMBIOSIZE); + 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; + adapdata->notify_imc = notify_imc; + + /* 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); + + i2c_set_adapdata(adap, adapdata); + + retval = i2c_add_adapter(adap); + if (retval) { + kfree(adapdata); + kfree(adap); + release_region(smba, SMBIOSIZE); + 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; +} + +static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, + bool notify_imc) +{ + struct i2c_piix4_adapdata *adapdata; + int port; + int retval; + + 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) + goto error; + } + + return retval; + +error: + dev_err(&dev->dev, + "Error setting up SB800 adapters. Unregistering!\n"); + while (--port >= 0) { + adapdata = i2c_get_adapdata(piix4_main_adapters[port]); + if (adapdata->smba) { + i2c_del_adapter(piix4_main_adapters[port]); + kfree(adapdata); + kfree(piix4_main_adapters[port]); + piix4_main_adapters[port] = NULL; + } + } + + return retval; +} + +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_HYGON) { + bool notify_imc = false; + is_sb800 = true; + + if ((dev->vendor == PCI_VENDOR_ID_AMD || + dev->vendor == PCI_VENDOR_ID_HYGON) && + dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { + u8 imc; + + /* + * Detect if IMC is active or not, this method is + * described on coreboot's AMD IMC notes + */ + pci_bus_read_config_byte(dev->bus, PCI_DEVFN(0x14, 3), + 0x40, &imc); + if (imc & 0x80) + notify_imc = true; + } + + /* base address location etc changed in SB800 */ + retval = piix4_setup_sb800(dev, id, 0); + if (retval < 0) + return retval; + + /* + * Try to register multiplexed main SMBus adapter, + * give up if we can't + */ + retval = piix4_add_adapters_sb800(dev, retval, notify_imc); + if (retval < 0) + return retval; + } else { + retval = piix4_setup(dev, id); + if (retval < 0) + return retval; + + /* Try to register main SMBus adapter, give up if we can't */ + 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 */ + retval = -ENODEV; + + if (dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) { + if (dev->revision < 0x40) { + retval = piix4_setup_aux(dev, id, 0x58); + } else { + /* SB800 added aux bus too */ + retval = piix4_setup_sb800(dev, id, 1); + } + } + + 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 || + 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, 1, + is_sb800 ? piix4_aux_port_name_sb800 : "", + &piix4_aux_adapter); + } + + return 0; +} + +static void piix4_adap_remove(struct i2c_adapter *adap) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + + if (adapdata->smba) { + i2c_del_adapter(adap); + if (adapdata->port == (0 << piix4_port_shift_sb800)) + release_region(adapdata->smba, SMBIOSIZE); + kfree(adapdata); + kfree(adap); + } +} + +static void piix4_remove(struct pci_dev *dev) +{ + int port = piix4_adapter_count; + + while (--port >= 0) { + if (piix4_main_adapters[port]) { + piix4_adap_remove(piix4_main_adapters[port]); + piix4_main_adapters[port] = NULL; + } + } + + if (piix4_aux_adapter) { + piix4_adap_remove(piix4_aux_adapter); + piix4_aux_adapter = NULL; + } +} + +static struct pci_driver piix4_driver = { + .name = "piix4_smbus", + .id_table = piix4_ids, + .probe = piix4_probe, + .remove = piix4_remove, +}; + +module_pci_driver(piix4_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_AUTHOR("Philip Edelbrock "); +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 000000000..36bc6ce82 --- /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 + * Sanket Goswami + */ + +#ifndef I2C_PIIX4_H +#define I2C_PIIX4_H + +#include + +/* 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-pnx.c b/drivers/i2c/busses/i2c-pnx.c new file mode 100644 index 000000000..e1cc2b2bd --- /dev/null +++ b/drivers/i2c/busses/i2c-pnx.c @@ -0,0 +1,756 @@ +/* + * Provides I2C support for Philips PNX010x/PNX4008 boards. + * + * Authors: Dennis Kovalev + * Vitaly Wool + * + * 2004-2006 (c) MontaVista Software, Inc. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ +#define I2C_PNX_REGION_SIZE 0x100 + +struct i2c_pnx_mif { + int ret; /* Return value */ + int mode; /* Interface mode */ + struct completion complete; /* I/O completion */ + u8 * buf; /* Data buffer */ + int len; /* Length of data buffer */ + int order; /* RX Bytes to order via TX */ +}; + +struct i2c_pnx_algo_data { + void __iomem *ioaddr; + struct i2c_pnx_mif mif; + int last; + struct clk *clk; + struct i2c_adapter adapter; + int irq; + u32 timeout; +}; + +enum { + mstatus_tdi = 0x00000001, + mstatus_afi = 0x00000002, + mstatus_nai = 0x00000004, + mstatus_drmi = 0x00000008, + mstatus_active = 0x00000020, + mstatus_scl = 0x00000040, + mstatus_sda = 0x00000080, + mstatus_rff = 0x00000100, + mstatus_rfe = 0x00000200, + mstatus_tff = 0x00000400, + mstatus_tfe = 0x00000800, +}; + +enum { + mcntrl_tdie = 0x00000001, + mcntrl_afie = 0x00000002, + mcntrl_naie = 0x00000004, + mcntrl_drmie = 0x00000008, + mcntrl_drsie = 0x00000010, + mcntrl_rffie = 0x00000020, + mcntrl_daie = 0x00000040, + mcntrl_tffie = 0x00000080, + mcntrl_reset = 0x00000100, + mcntrl_cdbmode = 0x00000400, +}; + +enum { + rw_bit = 1 << 0, + start_bit = 1 << 8, + stop_bit = 1 << 9, +}; + +#define I2C_REG_RX(a) ((a)->ioaddr) /* Rx FIFO reg (RO) */ +#define I2C_REG_TX(a) ((a)->ioaddr) /* Tx FIFO reg (WO) */ +#define I2C_REG_STS(a) ((a)->ioaddr + 0x04) /* Status reg (RO) */ +#define I2C_REG_CTL(a) ((a)->ioaddr + 0x08) /* Ctl reg */ +#define I2C_REG_CKL(a) ((a)->ioaddr + 0x0c) /* Clock divider low */ +#define I2C_REG_CKH(a) ((a)->ioaddr + 0x10) /* Clock divider high */ +#define I2C_REG_ADR(a) ((a)->ioaddr + 0x14) /* I2C address */ +#define I2C_REG_RFL(a) ((a)->ioaddr + 0x18) /* Rx FIFO level (RO) */ +#define I2C_REG_TFL(a) ((a)->ioaddr + 0x1c) /* Tx FIFO level (RO) */ +#define I2C_REG_RXB(a) ((a)->ioaddr + 0x20) /* Num of bytes Rx-ed (RO) */ +#define I2C_REG_TXB(a) ((a)->ioaddr + 0x24) /* Num of bytes Tx-ed (RO) */ +#define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ +#define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ + +static inline int wait_timeout(struct i2c_pnx_algo_data *data) +{ + long timeout = jiffies_to_msecs(data->timeout); + while (timeout > 0 && + (ioread32(I2C_REG_STS(data)) & mstatus_active)) { + mdelay(1); + timeout--; + } + return (timeout <= 0); +} + +static inline int wait_reset(struct i2c_pnx_algo_data *data) +{ + long timeout = jiffies_to_msecs(data->timeout); + while (timeout > 0 && + (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { + mdelay(1); + timeout--; + } + return (timeout <= 0); +} + +/** + * i2c_pnx_start - start a device + * @slave_addr: slave address + * @alg_data: pointer to local driver data structure + * + * Generate a START signal in the desired mode. + */ +static int i2c_pnx_start(unsigned char slave_addr, + struct i2c_pnx_algo_data *alg_data) +{ + dev_dbg(&alg_data->adapter.dev, "%s(): addr 0x%x mode %d\n", __func__, + slave_addr, alg_data->mif.mode); + + /* Check for 7 bit slave addresses only */ + if (slave_addr & ~0x7f) { + dev_err(&alg_data->adapter.dev, + "%s: Invalid slave address %x. Only 7-bit addresses are supported\n", + alg_data->adapter.name, slave_addr); + return -EINVAL; + } + + /* First, make sure bus is idle */ + if (wait_timeout(alg_data)) { + /* Somebody else is monopolizing the bus */ + dev_err(&alg_data->adapter.dev, + "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", + alg_data->adapter.name, slave_addr, + ioread32(I2C_REG_CTL(alg_data)), + ioread32(I2C_REG_STS(alg_data))); + return -EBUSY; + } else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) { + /* Sorry, we lost the bus */ + dev_err(&alg_data->adapter.dev, + "%s: Arbitration failure. Slave addr = %02x\n", + alg_data->adapter.name, slave_addr); + return -EIO; + } + + /* + * OK, I2C is enabled and we have the bus. + * Clear the current TDI and AFI status flags. + */ + iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi, + I2C_REG_STS(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): sending %#x\n", __func__, + (slave_addr << 1) | start_bit | alg_data->mif.mode); + + /* Write the slave address, START bit and R/W bit */ + iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode, + I2C_REG_TX(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): exit\n", __func__); + + return 0; +} + +/** + * i2c_pnx_stop - stop a device + * @alg_data: pointer to local driver data structure + * + * Generate a STOP signal to terminate the master transaction. + */ +static void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data) +{ + /* Only 1 msec max timeout due to interrupt context */ + long timeout = 1000; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + /* Write a STOP bit to TX FIFO */ + iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data)); + + /* Wait until the STOP is seen. */ + while (timeout > 0 && + (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) { + /* may be called from interrupt context */ + udelay(1); + timeout--; + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); +} + +/** + * i2c_pnx_master_xmit - transmit data to slave + * @alg_data: pointer to local driver data structure + * + * Sends one byte of data to the slave + */ +static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) +{ + u32 val; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + if (alg_data->mif.len > 0) { + /* We still have something to talk about... */ + val = *alg_data->mif.buf++; + + if (alg_data->mif.len == 1) + val |= stop_bit; + + alg_data->mif.len--; + iowrite32(val, I2C_REG_TX(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): xmit %#x [%d]\n", + __func__, val, alg_data->mif.len + 1); + + if (alg_data->mif.len == 0) { + if (alg_data->last) { + /* Wait until the STOP is seen. */ + if (wait_timeout(alg_data)) + dev_err(&alg_data->adapter.dev, + "The bus is still active after timeout\n"); + } + /* Disable master interrupts */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) & + ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), + I2C_REG_CTL(alg_data)); + + dev_dbg(&alg_data->adapter.dev, + "%s(): Waking up xfer routine.\n", + __func__); + + complete(&alg_data->mif.complete); + } + } else if (alg_data->mif.len == 0) { + /* zero-sized transfer */ + i2c_pnx_stop(alg_data); + + /* Disable master interrupts. */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) & + ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), + I2C_REG_CTL(alg_data)); + + dev_dbg(&alg_data->adapter.dev, + "%s(): Waking up xfer routine after zero-xfer.\n", + __func__); + + complete(&alg_data->mif.complete); + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + return 0; +} + +/** + * i2c_pnx_master_rcv - receive data from slave + * @alg_data: pointer to local driver data structure + * + * Reads one byte data from the slave + */ +static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) +{ + unsigned int val = 0; + u32 ctl = 0; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + /* Check, whether there is already data, + * or we didn't 'ask' for it yet. + */ + if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { + /* 'Asking' is done asynchronously, e.g. dummy TX of several + * bytes is done before the first actual RX arrives in FIFO. + * Therefore, ordered bytes (via TX) are counted separately. + */ + if (alg_data->mif.order) { + dev_dbg(&alg_data->adapter.dev, + "%s(): Write dummy data to fill Rx-fifo...\n", + __func__); + + if (alg_data->mif.order == 1) { + /* Last byte, do not acknowledge next rcv. */ + val |= stop_bit; + + /* + * Enable interrupt RFDAIE (data in Rx fifo), + * and disable DRMIE (need data for Tx) + */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl |= mcntrl_rffie | mcntrl_daie; + ctl &= ~mcntrl_drmie; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + } + + /* + * Now we'll 'ask' for data: + * For each byte we want to receive, we must + * write a (dummy) byte to the Tx-FIFO. + */ + iowrite32(val, I2C_REG_TX(alg_data)); + alg_data->mif.order--; + } + return 0; + } + + /* Handle data. */ + if (alg_data->mif.len > 0) { + val = ioread32(I2C_REG_RX(alg_data)); + *alg_data->mif.buf++ = (u8) (val & 0xff); + dev_dbg(&alg_data->adapter.dev, "%s(): rcv 0x%x [%d]\n", + __func__, val, alg_data->mif.len); + + alg_data->mif.len--; + if (alg_data->mif.len == 0) { + if (alg_data->last) + /* Wait until the STOP is seen. */ + if (wait_timeout(alg_data)) + dev_err(&alg_data->adapter.dev, + "The bus is still active after timeout\n"); + + /* Disable master interrupts */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie | mcntrl_daie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + complete(&alg_data->mif.complete); + } + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + return 0; +} + +static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id) +{ + struct i2c_pnx_algo_data *alg_data = dev_id; + u32 stat, ctl; + + dev_dbg(&alg_data->adapter.dev, + "%s(): mstat = %x mctrl = %x, mode = %d\n", + __func__, + ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data)), + alg_data->mif.mode); + stat = ioread32(I2C_REG_STS(alg_data)); + + /* let's see what kind of event this is */ + if (stat & mstatus_afi) { + /* We lost arbitration in the midst of a transfer */ + alg_data->mif.ret = -EIO; + + /* Disable master interrupts. */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + complete(&alg_data->mif.complete); + } else if (stat & mstatus_nai) { + /* Slave did not acknowledge, generate a STOP */ + dev_dbg(&alg_data->adapter.dev, + "%s(): Slave did not acknowledge, generating a STOP.\n", + __func__); + i2c_pnx_stop(alg_data); + + /* Disable master interrupts. */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + /* Our return value. */ + alg_data->mif.ret = -EIO; + + complete(&alg_data->mif.complete); + } else { + /* + * Two options: + * - Master Tx needs data. + * - There is data in the Rx-fifo + * The latter is only the case if we have requested for data, + * via a dummy write. (See 'i2c_pnx_master_rcv'.) + * We therefore check, as a sanity check, whether that interrupt + * has been enabled. + */ + if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) { + if (alg_data->mif.mode == I2C_SMBUS_WRITE) { + i2c_pnx_master_xmit(alg_data); + } else if (alg_data->mif.mode == I2C_SMBUS_READ) { + i2c_pnx_master_rcv(alg_data); + } + } + } + + /* Clear TDI and AFI bits */ + stat = ioread32(I2C_REG_STS(alg_data)); + iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data)); + + dev_dbg(&alg_data->adapter.dev, + "%s(): exiting, stat = %x ctrl = %x.\n", + __func__, ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data))); + + return IRQ_HANDLED; +} + +static void i2c_pnx_timeout(struct i2c_pnx_algo_data *alg_data) +{ + u32 ctl; + + dev_err(&alg_data->adapter.dev, + "Master timed out. stat = %04x, cntrl = %04x. Resetting master...\n", + ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data))); + + /* Reset master and disable interrupts */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + ctl |= mcntrl_reset; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + wait_reset(alg_data); + alg_data->mif.ret = -EIO; +} + +static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) +{ + u32 stat; + + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) { + dev_err(&alg_data->adapter.dev, + "%s: Bus is still active after xfer. Reset it...\n", + alg_data->adapter.name); + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(alg_data); + } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { + /* If there is data in the fifo's after transfer, + * flush fifo's by reset. + */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(alg_data); + } else if (stat & mstatus_nai) { + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(alg_data); + } +} + +/** + * i2c_pnx_xfer - generic transfer entry point + * @adap: pointer to I2C adapter structure + * @msgs: array of messages + * @num: number of messages + * + * Initiates the transfer + */ +static int +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, + "%s(): entering: %d messages, stat = %04x.\n", + __func__, num, ioread32(I2C_REG_STS(alg_data))); + + bus_reset_if_active(alg_data); + + /* Process transactions in a loop. */ + for (i = 0; rc >= 0 && i < num; i++) { + u8 addr; + + pmsg = &msgs[i]; + addr = pmsg->addr; + + if (pmsg->flags & I2C_M_TEN) { + dev_err(&alg_data->adapter.dev, + "%s: 10 bits addr not supported!\n", + alg_data->adapter.name); + rc = -EINVAL; + break; + } + + alg_data->mif.buf = pmsg->buf; + alg_data->mif.len = pmsg->len; + alg_data->mif.order = pmsg->len; + alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? + I2C_SMBUS_READ : I2C_SMBUS_WRITE; + alg_data->mif.ret = 0; + alg_data->last = (i == num - 1); + + dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n", + __func__, alg_data->mif.mode, alg_data->mif.len); + + + /* initialize the completion var */ + init_completion(&alg_data->mif.complete); + + /* Enable master interrupt */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie | + mcntrl_naie | mcntrl_drmie, + I2C_REG_CTL(alg_data)); + + /* Put start-code and slave-address on the bus. */ + rc = i2c_pnx_start(addr, alg_data); + if (rc < 0) + break; + + /* Wait for completion */ + 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++; + dev_dbg(&alg_data->adapter.dev, + "%s(): Complete, return code = %d.\n", + __func__, rc); + + /* Clear TDI and AFI bits in case they are set. */ + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) { + dev_dbg(&alg_data->adapter.dev, + "%s: TDI still set... clearing now.\n", + alg_data->adapter.name); + iowrite32(stat, I2C_REG_STS(alg_data)); + } + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) { + dev_dbg(&alg_data->adapter.dev, + "%s: AFI still set... clearing now.\n", + alg_data->adapter.name); + iowrite32(stat, I2C_REG_STS(alg_data)); + } + } + + bus_reset_if_active(alg_data); + + /* Cleanup to be sure... */ + alg_data->mif.buf = NULL; + alg_data->mif.len = 0; + alg_data->mif.order = 0; + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + if (completed != num) + return ((rc < 0) ? rc : -EREMOTEIO); + + return num; +} + +static u32 i2c_pnx_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm pnx_algorithm = { + .xfer = i2c_pnx_xfer, + .functionality = i2c_pnx_func, +}; + +static int i2c_pnx_controller_suspend(struct device *dev) +{ + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); + + clk_disable_unprepare(alg_data->clk); + + return 0; +} + +static int i2c_pnx_controller_resume(struct device *dev) +{ + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); + + return clk_prepare_enable(alg_data->clk); +} + +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) +{ + u32 speed = I2C_MAX_STANDARD_MODE_FREQ; + unsigned long tmp; + int ret = 0; + struct i2c_pnx_algo_data *alg_data; + unsigned long freq; + struct resource *res; + + alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL); + if (!alg_data) + return -ENOMEM; + + platform_set_drvdata(pdev, alg_data); + + alg_data->adapter.dev.parent = &pdev->dev; + alg_data->adapter.algo = &pnx_algorithm; + alg_data->adapter.algo_data = alg_data; + alg_data->adapter.nr = pdev->id; + + 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) { + of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &speed); + /* + * At this point, it is planned to add an OF timeout property. + * As soon as there is a consensus about how to call and handle + * this, sth. like the following can be put here: + * + * of_property_read_u32(pdev->dev.of_node, "timeout", + * &alg_data->timeout); + */ + } +#endif + alg_data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(alg_data->clk)) + return PTR_ERR(alg_data->clk); + + snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), + "%s", pdev->name); + + /* Register I/O resource */ + alg_data->ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(alg_data->ioaddr)) + return PTR_ERR(alg_data->ioaddr); + + ret = clk_prepare_enable(alg_data->clk); + if (ret) + return ret; + + freq = clk_get_rate(alg_data->clk); + + /* + * Clock Divisor High This value is the number of system clocks + * the serial clock (SCL) will be high. + * For example, if the system clock period is 50 ns and the maximum + * desired serial period is 10000 ns (100 kHz), then CLKHI would be + * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value + * programmed into CLKHI will vary from this slightly due to + * variations in the output pad's rise and fall times as well as + * the deglitching filter length. + */ + + tmp = (freq / speed) / 2 - 2; + if (tmp > 0x3FF) + tmp = 0x3FF; + iowrite32(tmp, I2C_REG_CKH(alg_data)); + iowrite32(tmp, I2C_REG_CKL(alg_data)); + + iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); + if (wait_reset(alg_data)) { + ret = -ENODEV; + goto out_clock; + } + init_completion(&alg_data->mif.complete); + + alg_data->irq = platform_get_irq(pdev, 0); + if (alg_data->irq < 0) { + ret = alg_data->irq; + goto out_clock; + } + ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt, + 0, pdev->name, alg_data); + if (ret) + goto out_clock; + + /* Register this adapter with the I2C subsystem */ + ret = i2c_add_numbered_adapter(&alg_data->adapter); + if (ret < 0) + goto out_clock; + + dev_dbg(&pdev->dev, "%s: Master at %pap, irq %d.\n", + alg_data->adapter.name, &res->start, alg_data->irq); + + return 0; + +out_clock: + clk_disable_unprepare(alg_data->clk); + return ret; +} + +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); +} + +#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 + +static struct platform_driver i2c_pnx_driver = { + .driver = { + .name = "pnx-i2c", + .of_match_table = of_match_ptr(i2c_pnx_of_match), + .pm = pm_sleep_ptr(&i2c_pnx_pm), + }, + .probe = i2c_pnx_probe, + .remove = i2c_pnx_remove, +}; + +static int __init i2c_adap_pnx_init(void) +{ + return platform_driver_register(&i2c_pnx_driver); +} + +static void __exit i2c_adap_pnx_exit(void) +{ + platform_driver_unregister(&i2c_pnx_driver); +} + +MODULE_AUTHOR("Vitaly Wool"); +MODULE_AUTHOR("Dennis Kovalev "); +MODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pnx-i2c"); + +/* We need to make sure I2C is initialized before USB */ +subsys_initcall(i2c_adap_pnx_init); +module_exit(i2c_adap_pnx_exit); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c new file mode 100644 index 000000000..f99a2cc72 --- /dev/null +++ b/drivers/i2c/busses/i2c-powermac.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c Support for Apple SMU Controller + + Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. + + + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("I2C driver for Apple PowerMac"); +MODULE_LICENSE("GPL"); + +/* + * SMBUS-type transfer entrypoint + */ +static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data* data) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read = (read_write == I2C_SMBUS_READ); + int addrdir = (addr << 1) | read; + int mode, subsize, len; + u32 subaddr; + u8 *buf; + u8 local[2]; + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) { + mode = pmac_i2c_mode_std; + subsize = 0; + subaddr = 0; + } else { + mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub; + subsize = 1; + subaddr = command; + } + + switch (size) { + case I2C_SMBUS_QUICK: + buf = NULL; + len = 0; + break; + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + buf = &data->byte; + len = 1; + break; + case I2C_SMBUS_WORD_DATA: + if (!read) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + buf = local; + len = 2; + break; + + /* Note that these are broken vs. the expected smbus API where + * on reads, the length is actually returned from the function, + * 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 ... + */ + case I2C_SMBUS_BLOCK_DATA: + buf = data->block; + len = data->block[0] + 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + buf = &data->block[1]; + len = data->block[0]; + break; + + default: + return -EINVAL; + } + + rc = pmac_i2c_open(bus, 0); + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); + return rc; + } + + rc = pmac_i2c_setmode(bus, mode); + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + mode, rc); + goto bail; + } + + rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len); + if (rc) { + if (rc == -ENXIO) + dev_dbg(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, " + "err %d\n", addrdir >> 1, size, rc); + else + dev_err(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, " + "err %d\n", addrdir >> 1, size, rc); + goto bail; + } + + if (size == I2C_SMBUS_WORD_DATA && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + + bail: + pmac_i2c_close(bus); + return rc; +} + +/* + * 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_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int addrdir; + + if (msgs->flags & I2C_M_TEN) + return -EINVAL; + addrdir = i2c_8bit_addr_from_msg(msgs); + + rc = pmac_i2c_open(bus, 0); + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); + return rc; + } + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + pmac_i2c_mode_std, rc); + goto bail; + } + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + if (rc < 0) { + if (rc == -ENXIO) + dev_dbg(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", + addrdir >> 1, rc); + else + dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", + addrdir >> 1, rc); + } + bail: + pmac_i2c_close(bus); + return rc < 0 ? rc : 1; +} + +static u32 i2c_powermac_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_BLOCK_DATA | I2C_FUNC_I2C; +} + +/* For now, we only handle smbus */ +static const struct i2c_algorithm i2c_powermac_algorithm = { + .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 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)); +} + +static u32 i2c_powermac_get_addr(struct i2c_adapter *adap, + struct pmac_i2c_bus *bus, + struct device_node *node) +{ + u32 prop; + int ret; + + /* First check for valid "reg" */ + ret = of_property_read_u32(node, "reg", &prop); + if (ret == 0) + return (prop & 0xff) >> 1; + + /* Then check old-style "i2c-address" */ + 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")) + return 0x60; + else if (of_node_name_eq(node, "deq")) + return 0x34; + + dev_warn(&adap->dev, "No i2c address for %pOF\n", node); + + return 0xffffffff; +} + +static void i2c_powermac_create_one(struct i2c_adapter *adap, + const char *type, + u32 addr) +{ + struct i2c_board_info info = {}; + struct i2c_client *newdev; + + strscpy(info.type, type, sizeof(info.type)); + info.addr = addr; + newdev = i2c_new_client_device(adap, &info); + if (IS_ERR(newdev)) + dev_err(&adap->dev, + "i2c-powermac: Failure to register missing %s\n", + type); +} + +static void i2c_powermac_add_missing(struct i2c_adapter *adap, + struct pmac_i2c_bus *bus, + bool found_onyx) +{ + struct device_node *busnode = pmac_i2c_get_bus_node(bus); + int rc; + + /* Check for the onyx audio codec */ +#define ONYX_REG_CONTROL 67 + if (of_device_is_compatible(busnode, "k2-i2c") && !found_onyx) { + union i2c_smbus_data data; + + rc = i2c_smbus_xfer(adap, 0x46, 0, I2C_SMBUS_READ, + ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA, + &data); + if (rc >= 0) + i2c_powermac_create_one(adap, "MAC,pcm3052", 0x46); + + rc = i2c_smbus_xfer(adap, 0x47, 0, I2C_SMBUS_READ, + ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA, + &data); + if (rc >= 0) + i2c_powermac_create_one(adap, "MAC,pcm3052", 0x47); + } +} + +static bool i2c_powermac_get_type(struct i2c_adapter *adap, + struct device_node *node, + u32 addr, char *type, int type_size) +{ + char tmp[16]; + + /* + * 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_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) { + snprintf(type, type_size, "MAC,%s", tmp); + return true; + } + + /* Now look for known workarounds */ + if (of_node_name_eq(node, "deq")) { + /* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */ + if (addr == 0x34) { + snprintf(type, type_size, "MAC,tas3001"); + return true; + } else if (addr == 0x35) { + snprintf(type, type_size, "MAC,tas3004"); + return true; + } + } + + dev_err(&adap->dev, "i2c-powermac: modalias failure on %pOF\n", node); + return false; +} + +static void i2c_powermac_register_devices(struct i2c_adapter *adap, + struct pmac_i2c_bus *bus) +{ + struct i2c_client *newdev; + struct device_node *node; + bool found_onyx = false; + + /* + * In some cases we end up with the via-pmu node itself, in this + * case we skip this function completely as the device-tree will + * not contain anything useful. + */ + if (of_node_name_eq(adap->dev.of_node, "via-pmu")) + return; + + for_each_child_of_node(adap->dev.of_node, node) { + struct i2c_board_info info = {}; + u32 addr; + + /* Get address & channel */ + addr = i2c_powermac_get_addr(adap, bus, node); + if (addr == 0xffffffff) + continue; + + /* Multibus setup, check channel */ + if (!pmac_i2c_match_adapter(node, adap)) + continue; + + dev_dbg(&adap->dev, "i2c-powermac: register %pOF\n", node); + + /* + * Keep track of some device existence to handle + * workarounds later. + */ + if (of_device_is_compatible(node, "pcm3052")) + found_onyx = true; + + /* Make up a modalias */ + if (!i2c_powermac_get_type(adap, node, addr, + info.type, sizeof(info.type))) { + continue; + } + + /* Fill out the rest of the info structure */ + info.addr = addr; + info.irq = irq_of_parse_and_map(node, 0); + info.fwnode = of_fwnode_handle(of_node_get(node)); + + 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); + /* We do not dispose of the interrupt mapping on + * purpose. It's not necessary (interrupt cannot be + * re-used) and somebody else might have grabbed it + * via direct DT lookup so let's not bother + */ + continue; + } + } + + /* Additional workarounds */ + i2c_powermac_add_missing(adap, bus, found_onyx); +} + +static int i2c_powermac_probe(struct platform_device *dev) +{ + struct pmac_i2c_bus *bus = dev_get_platdata(&dev->dev); + struct device_node *parent; + struct i2c_adapter *adapter; + int rc; + + if (bus == NULL) + return -EINVAL; + adapter = pmac_i2c_get_adapter(bus); + + /* Ok, now we need to make up a name for the interface that will + * match what we used to do in the past, that is basically the + * controller's parent device node for keywest. PMU didn't have a + * naming convention and SMU has a different one + */ + switch(pmac_i2c_get_type(bus)) { + case pmac_i2c_bus_keywest: + parent = of_get_parent(pmac_i2c_get_controller(bus)); + if (parent == NULL) + return -EINVAL; + snprintf(adapter->name, sizeof(adapter->name), "%pOFn %d", + parent, + pmac_i2c_get_channel(bus)); + of_node_put(parent); + break; + case pmac_i2c_bus_pmu: + snprintf(adapter->name, sizeof(adapter->name), "pmu %d", + pmac_i2c_get_channel(bus)); + break; + case pmac_i2c_bus_smu: + /* This is not what we used to do but I'm fixing drivers at + * the same time as this change + */ + snprintf(adapter->name, sizeof(adapter->name), "smu %d", + pmac_i2c_get_channel(bus)); + break; + default: + return -EINVAL; + } + + platform_set_drvdata(dev, adapter); + adapter->algo = &i2c_powermac_algorithm; + adapter->quirks = &i2c_powermac_quirks; + i2c_set_adapdata(adapter, bus); + adapter->dev.parent = &dev->dev; + + /* Clear of_node to skip automatic registration of i2c child nodes */ + adapter->dev.of_node = NULL; + rc = i2c_add_adapter(adapter); + if (rc) { + printk(KERN_ERR "i2c-powermac: Adapter %s registration " + "failed\n", adapter->name); + memset(adapter, 0, sizeof(*adapter)); + return rc; + } + + printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); + + /* Use custom child registration due to Apple device-tree funkyness */ + adapter->dev.of_node = dev->dev.of_node; + i2c_powermac_register_devices(adapter, bus); + + return 0; +} + +static struct platform_driver i2c_powermac_driver = { + .probe = i2c_powermac_probe, + .remove = i2c_powermac_remove, + .driver = { + .name = "i2c-powermac", + .bus = &platform_bus_type, + }, +}; + +module_platform_driver(i2c_powermac_driver); + +MODULE_ALIAS("platform:i2c-powermac"); diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c new file mode 100644 index 000000000..92a0647f0 --- /dev/null +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CE4100 PCI-I2C glue code for PXA's driver + * Author: Sebastian Andrzej Siewior + * + * The CE4100's I2C device is more or less the same one as found on PXA. + * It does not support target mode, the register slightly moved. This PCI + * device provides three bars, every contains a single I2C controller. + */ +#include +#include +#include +#include +#include +#include + +#define CE4100_PCI_I2C_DEVS 3 + +struct ce4100_devices { + struct platform_device *pdev[CE4100_PCI_I2C_DEVS]; +}; + +static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar) +{ + struct platform_device *pdev; + struct i2c_pxa_platform_data pdata; + struct resource res[2]; + struct device_node *child; + static int devnum; + int ret; + + memset(&pdata, 0, sizeof(struct i2c_pxa_platform_data)); + memset(&res, 0, sizeof(res)); + + res[0].flags = IORESOURCE_MEM; + res[0].start = pci_resource_start(dev, bar); + res[0].end = pci_resource_end(dev, bar); + + res[1].flags = IORESOURCE_IRQ; + res[1].start = dev->irq; + res[1].end = dev->irq; + + for_each_child_of_node(dev->dev.of_node, child) { + const void *prop; + struct resource r; + int ret; + + ret = of_address_to_resource(child, 0, &r); + if (ret < 0) + continue; + if (r.start != res[0].start) + continue; + if (r.end != res[0].end) + continue; + if (r.flags != res[0].flags) + continue; + + prop = of_get_property(child, "fast-mode", NULL); + if (prop) + pdata.fast_mode = 1; + + break; + } + + if (!child) { + dev_err(&dev->dev, "failed to match a DT node for bar %d.\n", + bar); + ret = -EINVAL; + goto out; + } + + pdev = platform_device_alloc("ce4100-i2c", devnum); + if (!pdev) { + of_node_put(child); + ret = -ENOMEM; + goto out; + } + pdev->dev.parent = &dev->dev; + + platform_device_set_of_node(pdev, child); + + ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + devnum++; + return pdev; +err: + platform_device_put(pdev); +out: + return ERR_PTR(ret); +} + +static int ce4100_i2c_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret; + int i; + struct ce4100_devices *sds; + + ret = pcim_enable_device(dev); + if (ret) + return ret; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Missing device tree node.\n"); + return -EINVAL; + } + sds = kzalloc_obj(*sds); + if (!sds) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) { + sds->pdev[i] = add_i2c_device(dev, i); + if (IS_ERR(sds->pdev[i])) { + ret = PTR_ERR(sds->pdev[i]); + while (--i >= 0) + platform_device_unregister(sds->pdev[i]); + goto err_dev_add; + } + } + pci_set_drvdata(dev, sds); + return 0; + +err_dev_add: + kfree(sds); + 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 = { + .driver = { + .suppress_bind_attrs = true, + }, + .name = "ce4100_i2c", + .id_table = ce4100_i2c_devices, + .probe = ce4100_i2c_probe, +}; +builtin_pci_driver(ce4100_i2c_driver); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c new file mode 100644 index 000000000..c9927a389 --- /dev/null +++ b/drivers/i2c/busses/i2c-pxa.c @@ -0,0 +1,1624 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c_adap_pxa.c + * + * I2C adapter for the PXA I2C bus access. + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * Copyright (C) 2004-2005 Deep Blue Solutions Ltd. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly separated algo/adap [FB] + * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Sep 2004: Major rework to ensure efficient bus handling [RMK] + * Dec 2004: Added support for PXA27x and slave device probing [Liam Girdwood] + * Feb 2005: Rework slave mode handling [RMK] + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 ISR_A3700_EBB (1 << 11) /* early bus busy for armada 3700 */ + +#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 */ + +/* 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; + u32 idbr; + u32 icr; + u32 isr; + u32 isar; + u32 ilcr; + u32 iwcr; + u32 fm; + u32 hs; +}; + +enum pxa_i2c_types { + REGS_PXA2XX, + REGS_PXA3XX, + REGS_CE4100, + REGS_PXA910, + REGS_A3700, +}; + +/* I2C register layout definitions */ +static struct pxa_reg_layout pxa_reg_layout[] = { + [REGS_PXA2XX] = { + .ibmr = 0x00, + .idbr = 0x08, + .icr = 0x10, + .isr = 0x18, + .isar = 0x20, + .fm = ICR_FM, + .hs = ICR_HS, + }, + [REGS_PXA3XX] = { + .ibmr = 0x00, + .idbr = 0x04, + .icr = 0x08, + .isr = 0x0c, + .isar = 0x10, + .fm = ICR_FM, + .hs = ICR_HS, + }, + [REGS_CE4100] = { + .ibmr = 0x14, + .idbr = 0x0c, + .icr = 0x00, + .isr = 0x04, + /* no isar register */ + .fm = ICR_FM, + .hs = ICR_HS, + }, + [REGS_PXA910] = { + .ibmr = 0x00, + .idbr = 0x08, + .icr = 0x10, + .isr = 0x18, + .isar = 0x20, + .ilcr = 0x28, + .iwcr = 0x30, + .fm = ICR_FM, + .hs = ICR_HS, + }, + [REGS_A3700] = { + .ibmr = 0x00, + .idbr = 0x04, + .icr = 0x08, + .isr = 0x0c, + .isar = 0x10, + .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[] = { + { .name = "pxa2xx-i2c", .driver_data = REGS_PXA2XX }, + { .name = "pxa3xx-pwri2c", .driver_data = REGS_PXA3XX }, + { .name = "ce4100-i2c", .driver_data = REGS_CE4100 }, + { .name = "pxa910-i2c", .driver_data = REGS_PXA910 }, + { .name = "armada-3700-i2c", .driver_data = REGS_A3700 }, + { } +}; +MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); + +struct pxa_i2c { + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + unsigned int slave_addr; + unsigned int req_slave_addr; + + struct i2c_adapter adap; + struct clk *clk; +#ifdef CONFIG_I2C_PXA_SLAVE + struct i2c_client *slave; +#endif + + unsigned int irqlogidx; + u32 isrlog[32]; + u32 icrlog[32]; + + void __iomem *reg_base; + void __iomem *reg_ibmr; + void __iomem *reg_idbr; + void __iomem *reg_icr; + void __iomem *reg_isr; + void __iomem *reg_isar; + void __iomem *reg_ilcr; + void __iomem *reg_iwcr; + + unsigned long iobase; + unsigned long iosize; + + int irq; + unsigned int use_pio :1; + unsigned int fast_mode :1; + unsigned int high_mode:1; + unsigned char master_code; + unsigned long rate; + bool highmode_enter; + u32 fm_mask; + u32 hs_mask; + u32 busy_mask; + + struct i2c_bus_recovery_info recovery; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_default; + struct pinctrl_state *pinctrl_recovery; + bool reset_before_xfer; +}; + +#define _IBMR(i2c) ((i2c)->reg_ibmr) +#define _IDBR(i2c) ((i2c)->reg_idbr) +#define _ICR(i2c) ((i2c)->reg_icr) +#define _ISR(i2c) ((i2c)->reg_isr) +#define _ISAR(i2c) ((i2c)->reg_isar) +#define _ILCR(i2c) ((i2c)->reg_ilcr) +#define _IWCR(i2c) ((i2c)->reg_iwcr) + +/* + * I2C Slave mode address + */ +#define I2C_PXA_SLAVE_ADDR 0x1 + +#ifdef DEBUG + +struct bits { + u32 mask; + const char *set; + const char *unset; +}; +#define PXA_BIT(m, s, u) { .mask = m, .set = s, .unset = u } + +static inline void +decode_bits(const char *prefix, const struct bits *bits, int num, u32 val) +{ + printk("%s %08x:", prefix, val); + while (num--) { + const char *str = val & bits->mask ? bits->set : bits->unset; + if (str) + pr_cont(" %s", str); + bits++; + } + pr_cont("\n"); +} + +static const struct bits isr_bits[] = { + PXA_BIT(ISR_RWM, "RX", "TX"), + PXA_BIT(ISR_ACKNAK, "NAK", "ACK"), + PXA_BIT(ISR_UB, "Bsy", "Rdy"), + PXA_BIT(ISR_IBB, "BusBsy", "BusRdy"), + PXA_BIT(ISR_SSD, "SlaveStop", NULL), + PXA_BIT(ISR_ALD, "ALD", NULL), + PXA_BIT(ISR_ITE, "TxEmpty", NULL), + PXA_BIT(ISR_IRF, "RxFull", NULL), + PXA_BIT(ISR_GCAD, "GenCall", NULL), + PXA_BIT(ISR_SAD, "SlaveAddr", NULL), + PXA_BIT(ISR_BED, "BusErr", NULL), +}; + +static void decode_ISR(unsigned int val) +{ + decode_bits(KERN_DEBUG "ISR", isr_bits, ARRAY_SIZE(isr_bits), val); +} + +#ifdef CONFIG_I2C_PXA_SLAVE +static const struct bits icr_bits[] = { + PXA_BIT(ICR_START, "START", NULL), + PXA_BIT(ICR_STOP, "STOP", NULL), + PXA_BIT(ICR_ACKNAK, "ACKNAK", NULL), + PXA_BIT(ICR_TB, "TB", NULL), + PXA_BIT(ICR_MA, "MA", NULL), + PXA_BIT(ICR_SCLE, "SCLE", "scle"), + PXA_BIT(ICR_IUE, "IUE", "iue"), + PXA_BIT(ICR_GCD, "GCD", NULL), + PXA_BIT(ICR_ITEIE, "ITEIE", NULL), + PXA_BIT(ICR_IRFIE, "IRFIE", NULL), + PXA_BIT(ICR_BEIE, "BEIE", NULL), + PXA_BIT(ICR_SSDIE, "SSDIE", NULL), + PXA_BIT(ICR_ALDIE, "ALDIE", NULL), + PXA_BIT(ICR_SADIE, "SADIE", NULL), + PXA_BIT(ICR_UR, "UR", "ur"), +}; + +static void decode_ICR(unsigned int val) +{ + decode_bits(KERN_DEBUG "ICR", icr_bits, ARRAY_SIZE(icr_bits), val); +} +#endif + +static unsigned int i2c_debug = DEBUG; + +static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname) +{ + dev_dbg(&i2c->adap.dev, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno, + readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); +} + +#define show_state(i2c) i2c_pxa_show_state(i2c, __LINE__, __func__) + +static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) +{ + unsigned int i; + struct device *dev = &i2c->adap.dev; + + dev_err(dev, "slave_0x%x error: %s\n", + i2c->req_slave_addr >> 1, why); + dev_err(dev, "msg_num: %d msg_idx: %d msg_ptr: %d\n", + i2c->msg_num, i2c->msg_idx, i2c->msg_ptr); + 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_err(dev, "log:"); + for (i = 0; i < i2c->irqlogidx; i++) + pr_cont(" [%03x:%05x]", i2c->isrlog[i], i2c->icrlog[i]); + pr_cont("\n"); +} + +#else /* ifdef DEBUG */ + +#define i2c_debug 0 + +#define show_state(i2c) do { } while (0) +#define decode_ISR(val) do { } while (0) +#define decode_ICR(val) do { } while (0) +#define i2c_pxa_scream_blue_murder(i2c, why) do { } while (0) + +#endif /* ifdef DEBUG / else */ + +static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret); + +static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) +{ + return !(readl(_ICR(i2c)) & ICR_SCLE); +} + +static void i2c_pxa_abort(struct pxa_i2c *i2c) +{ + int i = 250; + + if (i2c_pxa_is_slavemode(i2c)) { + dev_dbg(&i2c->adap.dev, "%s: called in slave mode\n", __func__); + return; + } + + while ((i > 0) && (readl(_IBMR(i2c)) & IBMR_SDAS) == 0) { + unsigned long icr = readl(_ICR(i2c)); + + icr &= ~ICR_START; + icr |= ICR_ACKNAK | ICR_STOP | ICR_TB; + + writel(icr, _ICR(i2c)); + + show_state(i2c); + + mdelay(1); + i --; + } + + writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP), + _ICR(i2c)); +} + +static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c) +{ + int timeout = DEF_TIMEOUT; + u32 isr; + + while (1) { + isr = readl(_ISR(i2c)); + if (!(isr & i2c->busy_mask)) + return 0; + + if (isr & ISR_SAD) + timeout += 4; + + if (!timeout--) + break; + + msleep(2); + show_state(i2c); + } + + show_state(i2c); + + return I2C_RETRY; +} + +static int i2c_pxa_wait_master(struct pxa_i2c *i2c) +{ + unsigned long timeout = jiffies + HZ*4; + + while (time_before(jiffies, timeout)) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); + + if (readl(_ISR(i2c)) & ISR_SAD) { + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: Slave detected\n", __func__); + goto out; + } + + /* wait for unit and bus being not busy, and we also do a + * quick check of the i2c lines themselves to ensure they've + * gone high... + */ + if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 && + readl(_IBMR(i2c)) == (IBMR_SCLS | IBMR_SDAS)) { + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__); + out: + return 0; +} + +static int i2c_pxa_set_master(struct pxa_i2c *i2c) +{ + if (i2c_debug) + dev_dbg(&i2c->adap.dev, "setting to bus master\n"); + + if ((readl(_ISR(i2c)) & i2c->busy_mask) != 0) { + dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__); + if (!i2c_pxa_wait_master(i2c)) { + dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__); + return I2C_RETRY; + } + } + + writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c)); + return 0; +} + +#ifdef CONFIG_I2C_PXA_SLAVE +static int i2c_pxa_wait_slave(struct pxa_i2c *i2c) +{ + unsigned long timeout = jiffies + HZ*1; + + /* wait for stop */ + + show_state(i2c); + + while (time_before(jiffies, timeout)) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); + + if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 || + (readl(_ISR(i2c)) & ISR_SAD) != 0 || + (readl(_ICR(i2c)) & ICR_SCLE) == 0) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__); + return 0; +} + +/* + * clear the hold on the bus, and take of anything else + * that has been configured + */ +static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode) +{ + show_state(i2c); + + if (errcode < 0) { + udelay(100); /* simple delay */ + } else { + /* we need to wait for the stop condition to end */ + + /* if we where in stop, then clear... */ + if (readl(_ICR(i2c)) & ICR_STOP) { + udelay(100); + writel(readl(_ICR(i2c)) & ~ICR_STOP, _ICR(i2c)); + } + + if (!i2c_pxa_wait_slave(i2c)) { + dev_err(&i2c->adap.dev, "%s: wait timedout\n", + __func__); + return; + } + } + + writel(readl(_ICR(i2c)) & ~(ICR_STOP|ICR_ACKNAK|ICR_MA), _ICR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); + + if (i2c_debug) { + dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", readl(_ICR(i2c)), readl(_ISR(i2c))); + decode_ICR(readl(_ICR(i2c))); + } +} +#else +#define i2c_pxa_set_slave(i2c, err) do { } while (0) +#endif + +static void i2c_pxa_do_reset(struct pxa_i2c *i2c) +{ + /* reset according to 9.8 */ + writel(ICR_UR, _ICR(i2c)); + writel(I2C_ISR_INIT, _ISR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c)); + + if (i2c->reg_isar && IS_ENABLED(CONFIG_I2C_PXA_SLAVE)) + writel(i2c->slave_addr, _ISAR(i2c)); + + /* set control register values */ + writel(I2C_ICR_INIT | (i2c->fast_mode ? i2c->fm_mask : 0), _ICR(i2c)); + writel(readl(_ICR(i2c)) | (i2c->high_mode ? i2c->hs_mask : 0), _ICR(i2c)); + +#ifdef CONFIG_I2C_PXA_SLAVE + dev_info(&i2c->adap.dev, "Enabling slave mode\n"); + writel(readl(_ICR(i2c)) | ICR_SADIE | ICR_ALDIE | ICR_SSDIE, _ICR(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 +/* + * PXA I2C Slave mode + */ + +static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) +{ + if (isr & ISR_BED) { + /* what should we do here? */ + } else { + u8 byte = 0; + + if (i2c->slave != NULL) + i2c_slave_event(i2c->slave, I2C_SLAVE_READ_PROCESSED, + &byte); + + 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) +{ + u8 byte = readl(_IDBR(i2c)); + + if (i2c->slave != NULL) + i2c_slave_event(i2c->slave, I2C_SLAVE_WRITE_RECEIVED, &byte); + + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); +} + +static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) +{ + int timeout; + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "SAD, mode is slave-%cx\n", + (isr & ISR_RWM) ? 'r' : 't'); + + 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 + * start condition... if this happens, we'd better back off + * and stop holding the poor thing up + */ + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); + + timeout = 0x10000; + + while (1) { + if ((readl(_IBMR(i2c)) & IBMR_SCLS) == IBMR_SCLS) + break; + + timeout--; + + if (timeout <= 0) { + dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n"); + break; + } + } + + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); +} + +static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) +{ + if (i2c_debug > 2) + dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop)\n"); + + if (i2c->slave != NULL) + i2c_slave_event(i2c->slave, I2C_SLAVE_STOP, NULL); + + if (i2c_debug > 2) + dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop) acked\n"); + + /* + * If we have a master-mode message waiting, + * kick it off now that the slave has completed. + */ + 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) +{ + if (isr & ISR_BED) { + /* what should we do here? */ + } else { + writel(0, _IDBR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); + } +} + +static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); +} + +static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) +{ + int timeout; + + /* + * slave could interrupt in the middle of us generating a + * start condition... if this happens, we'd better back off + * and stop holding the poor thing up + */ + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); + + timeout = 0x10000; + + while (1) { + if ((readl(_IBMR(i2c)) & IBMR_SCLS) == IBMR_SCLS) + break; + + timeout--; + + if (timeout <= 0) { + dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n"); + break; + } + } + + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); +} + +static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) +{ + if (i2c->msg) + i2c_pxa_master_complete(i2c, I2C_RETRY); +} +#endif + +/* + * PXA I2C Master mode + */ + +static inline void i2c_pxa_start_message(struct pxa_i2c *i2c) +{ + u32 icr; + + /* + * Step 1: target slave address into IDBR + */ + i2c->req_slave_addr = i2c_8bit_addr_from_msg(i2c->msg); + writel(i2c->req_slave_addr, _IDBR(i2c)); + + /* + * Step 2: initiate the write. + */ + icr = readl(_ICR(i2c)) & ~(ICR_STOP | ICR_ALDIE); + writel(icr | ICR_START | ICR_TB, _ICR(i2c)); +} + +static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c) +{ + u32 icr; + + /* Clear the START, STOP, ACK, TB and MA flags */ + icr = readl(_ICR(i2c)); + icr &= ~(ICR_START | ICR_STOP | ICR_ACKNAK | ICR_TB | ICR_MA); + writel(icr, _ICR(i2c)); +} + +/* + * PXA I2C send master code + * 1. Load master code to IDBR and send it. + * Note for HS mode, set ICR [GPIOEN]. + * 2. Wait until win arbitration. + */ +static int i2c_pxa_send_mastercode(struct pxa_i2c *i2c) +{ + u32 icr; + long time_left; + + spin_lock_irq(&i2c->lock); + i2c->highmode_enter = true; + writel(i2c->master_code, _IDBR(i2c)); + + icr = readl(_ICR(i2c)) & ~(ICR_STOP | ICR_ALDIE); + icr |= ICR_GPIOEN | ICR_START | ICR_TB | ICR_ITEIE; + writel(icr, _ICR(i2c)); + + spin_unlock_irq(&i2c->lock); + time_left = wait_event_timeout(i2c->wait, + i2c->highmode_enter == false, HZ * 1); + + i2c->highmode_enter = false; + + return (time_left == 0) ? I2C_RETRY : 0; +} + +/* + * i2c_pxa_master_complete - complete the message and wake up. + */ +static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret) +{ + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx ++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + if (!i2c->use_pio) + wake_up(&i2c->wait); +} + +static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + again: + /* + * If ISR_ALD is set, we lost arbitration. + */ + if (isr & ISR_ALD) { + /* + * Do we need to do anything here? The PXA docs + * are vague about what happens. + */ + i2c_pxa_scream_blue_murder(i2c, "ALD set"); + + /* + * We ignore this error. We seem to see spurious ALDs + * for seemingly no reason. If we handle them as I think + * they should, we end up causing an I2C error, which + * is painful for some systems. + */ + return; /* ignore */ + } + + if ((isr & ISR_BED) && + (!((i2c->msg->flags & I2C_M_IGNORE_NAK) && + (isr & ISR_ACKNAK)))) { + int ret = BUS_ERROR; + + /* + * I2C bus error - either the device NAK'd us, or + * something more serious happened. If we were NAK'd + * on the initial address phase, we can retry. + */ + if (isr & ISR_ACKNAK) { + if (i2c->msg_ptr == 0 && i2c->msg_idx == 0) + ret = NO_SLAVE; + else + ret = XFER_NAKED; + } + i2c_pxa_master_complete(i2c, ret); + } else if (isr & ISR_RWM) { + /* + * Read mode. We have just sent the address byte, and + * now we must initiate the transfer. + */ + if (i2c->msg_ptr == i2c->msg->len - 1 && + i2c->msg_idx == i2c->msg_num - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else if (i2c->msg_ptr < i2c->msg->len) { + /* + * Write mode. Write the next data byte. + */ + writel(i2c->msg->buf[i2c->msg_ptr++], _IDBR(i2c)); + + icr |= ICR_ALDIE | ICR_TB; + + /* + * If this is the last byte of the last message or last byte + * of any message with I2C_M_STOP (e.g. SCCB), send a STOP. + */ + if ((i2c->msg_ptr == i2c->msg->len) && + ((i2c->msg->flags & I2C_M_STOP) || + (i2c->msg_idx == i2c->msg_num - 1))) + icr |= ICR_STOP; + + } else if (i2c->msg_idx < i2c->msg_num - 1) { + /* + * Next segment of the message. + */ + i2c->msg_ptr = 0; + i2c->msg_idx ++; + i2c->msg++; + + /* + * If we aren't doing a repeated start and address, + * go back and try to send the next byte. Note that + * we do not support switching the R/W direction here. + */ + if (i2c->msg->flags & I2C_M_NOSTART) + goto again; + + /* + * Write the next address. + */ + 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. + */ + icr &= ~ICR_ALDIE; + icr |= ICR_START | ICR_TB; + } else { + if (i2c->msg->len == 0) + icr |= ICR_MA; + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + writel(icr, _ICR(i2c)); + show_state(i2c); +} + +static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + /* + * Read the byte. + */ + i2c->msg->buf[i2c->msg_ptr++] = readl(_IDBR(i2c)); + + if (i2c->msg_ptr < i2c->msg->len) { + /* + * If this is the last byte of the last + * message, send a STOP. + */ + if (i2c->msg_ptr == i2c->msg->len - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else { + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + writel(icr, _ICR(i2c)); +} + +#define VALID_INT_SOURCE (ISR_SSD | ISR_ALD | ISR_ITE | ISR_IRF | \ + ISR_SAD | ISR_BED) +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) +{ + struct pxa_i2c *i2c = dev_id; + u32 isr = readl(_ISR(i2c)); + + if (!(isr & VALID_INT_SOURCE)) + return IRQ_NONE; + + if (i2c_debug > 2 && 0) { + dev_dbg(&i2c->adap.dev, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, isr, readl(_ICR(i2c)), readl(_IBMR(i2c))); + decode_ISR(isr); + } + + if (i2c->irqlogidx < ARRAY_SIZE(i2c->isrlog)) + i2c->isrlog[i2c->irqlogidx++] = isr; + + show_state(i2c); + + /* + * Always clear all pending IRQs. + */ + writel(isr & VALID_INT_SOURCE, _ISR(i2c)); + + if (isr & ISR_SAD) + i2c_pxa_slave_start(i2c, isr); + if (isr & ISR_SSD) + i2c_pxa_slave_stop(i2c); + + if (i2c_pxa_is_slavemode(i2c)) { + if (isr & ISR_ITE) + i2c_pxa_slave_txempty(i2c, isr); + if (isr & ISR_IRF) + i2c_pxa_slave_rxfull(i2c, isr); + } else if (i2c->msg && (!i2c->highmode_enter)) { + if (isr & ISR_ITE) + i2c_pxa_irq_txempty(i2c, isr); + if (isr & ISR_IRF) + i2c_pxa_irq_rxfull(i2c, isr); + } else if ((isr & ISR_ITE) && i2c->highmode_enter) { + i2c->highmode_enter = false; + wake_up(&i2c->wait); + } else { + i2c_pxa_scream_blue_murder(i2c, "spurious irq"); + } + + 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; + } + + 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)) +{ + int ret, i; + + 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(&i2c->adap.dev, "Retrying transmission\n"); + udelay(100); + } + 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; + + if (i2c->reset_before_xfer) { + i2c_pxa_reset(i2c); + i2c->reset_before_xfer = false; + } + + 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 | + I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm i2c_pxa_algorithm = { + .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 +}; + +/* 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; + + /* + * Wait for the bus to become free. + */ + while (timeout-- && readl(_ISR(i2c)) & i2c->busy_mask) + 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 +}; + +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; + + if (!pdev->dev.of_node) + return 1; + + /* For device tree we always use the dynamic or alias-assigned ID */ + i2c->adap.nr = -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 = (kernel_ulong_t)device_get_match_data(&pdev->dev); + + return 0; +} + +static int i2c_pxa_probe_pdata(struct platform_device *pdev, + struct pxa_i2c *i2c, + enum pxa_i2c_types *i2c_types) +{ + struct i2c_pxa_platform_data *plat = dev_get_platdata(&pdev->dev); + const struct platform_device_id *id = platform_get_device_id(pdev); + + *i2c_types = id->driver_data; + if (plat) { + i2c->use_pio = plat->use_pio; + i2c->fast_mode = plat->fast_mode; + i2c->high_mode = plat->high_mode; + i2c->master_code = plat->master_code; + if (!i2c->master_code) + i2c->master_code = 0xe; + i2c->rate = plat->rate; + } + 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 & i2c->busy_mask) { + 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; + int ret, irq; + + i2c = devm_kzalloc(&dev->dev, sizeof(struct pxa_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + /* 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) + return irq; + + ret = i2c_pxa_init_recovery(i2c); + if (ret) + return ret; + + ret = i2c_pxa_probe_dt(dev, i2c, &i2c_type); + if (ret > 0) + ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type); + if (ret < 0) + return ret; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + strscpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name)); + + i2c->clk = devm_clk_get(&dev->dev, NULL); + 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; + i2c->hs_mask = pxa_reg_layout[i2c_type].hs; + + i2c->busy_mask = ISR_UB | ISR_IBB; + if (i2c_type == REGS_A3700) + i2c->busy_mask |= ISR_A3700_EBB; + + if (i2c_type != REGS_CE4100) + i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; + + if (i2c_type == REGS_PXA910) { + i2c->reg_ilcr = i2c->reg_base + pxa_reg_layout[i2c_type].ilcr; + i2c->reg_iwcr = i2c->reg_base + pxa_reg_layout[i2c_type].iwcr; + } + + i2c->iobase = res->start; + i2c->iosize = resource_size(res); + + i2c->irq = irq; + + i2c->slave_addr = I2C_PXA_SLAVE_ADDR; + i2c->highmode_enter = false; + + if (plat) { + i2c->adap.class = plat->class; + } + + if (i2c->high_mode) { + if (i2c->rate) { + clk_set_rate(i2c->clk, i2c->rate); + pr_info("i2c: <%s> set rate to %ld\n", + i2c->adap.name, clk_get_rate(i2c->clk)); + } else + pr_warn("i2c: <%s> clock rate not set\n", + i2c->adap.name); + } + + 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; + } else { + i2c->adap.algo = &i2c_pxa_algorithm; + ret = devm_request_irq(&dev->dev, irq, i2c_pxa_handler, + IRQF_SHARED | IRQF_NO_SUSPEND, + dev_name(&dev->dev), i2c); + if (ret) { + dev_err(&dev->dev, "failed to request irq: %d\n", ret); + goto ereqirq; + } + } + + /* + * Skip reset on Armada 3700 when recovery is used to avoid + * controller hang due to the pinctrl state changes done by + * the generic recovery initialization code. The reset will + * be performed later, prior to the first transfer. + */ + if (i2c_type == REGS_A3700 && i2c->adap.bus_recovery_info) + i2c->reset_before_xfer = true; + else + i2c_pxa_reset(i2c); + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) + goto ereqirq; + + platform_set_drvdata(dev, i2c); + +#ifdef CONFIG_I2C_PXA_SLAVE + dev_info(&i2c->adap.dev, " PXA I2C adapter, slave address %d\n", + i2c->slave_addr); +#else + dev_info(&i2c->adap.dev, " PXA I2C adapter\n"); +#endif + return 0; + +ereqirq: + clk_disable_unprepare(i2c->clk); + return ret; +} + +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); +} + +static int i2c_pxa_suspend_noirq(struct device *dev) +{ + struct pxa_i2c *i2c = dev_get_drvdata(dev); + + clk_disable(i2c->clk); + + return 0; +} + +static int i2c_pxa_resume_noirq(struct device *dev) +{ + struct pxa_i2c *i2c = dev_get_drvdata(dev); + + clk_enable(i2c->clk); + i2c_pxa_reset(i2c); + + return 0; +} + +static const struct dev_pm_ops i2c_pxa_dev_pm_ops = { + .suspend_noirq = i2c_pxa_suspend_noirq, + .resume_noirq = i2c_pxa_resume_noirq, +}; + +static struct platform_driver i2c_pxa_driver = { + .probe = i2c_pxa_probe, + .remove = i2c_pxa_remove, + .driver = { + .name = "pxa2xx-i2c", + .pm = pm_sleep_ptr(&i2c_pxa_dev_pm_ops), + .of_match_table = i2c_pxa_dt_ids, + }, + .id_table = i2c_pxa_id_table, +}; + +static int __init i2c_adap_pxa_init(void) +{ + return platform_driver_register(&i2c_pxa_driver); +} + +static void __exit i2c_adap_pxa_exit(void) +{ + platform_driver_unregister(&i2c_pxa_driver); +} + +MODULE_DESCRIPTION("Intel PXA2XX I2C adapter"); +MODULE_LICENSE("GPL"); + +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 000000000..25b6e4e9e --- /dev/null +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +// Copyright (c) 2017-2022 Linaro Limited. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 void 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)); + } +} + +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; + } + + cci_init(cci); + + 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); + 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 const struct dev_pm_ops qcom_cci_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_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 disable_clocks; + + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + goto disable_clocks; + + pm_runtime_use_autosuspend(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: + + 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); + } + } +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); + } + } +} + +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 "); +MODULE_AUTHOR("Loic Poulain "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c new file mode 100644 index 000000000..00013b41a --- /dev/null +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -0,0 +1,1336 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + +#define CREATE_TRACE_POINTS +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SE_I2C_TX_TRANS_LEN 0x26c +#define SE_I2C_RX_TRANS_LEN 0x270 +#define SE_I2C_SCL_COUNTERS 0x278 + +#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\ + M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN) +#define SE_I2C_ABORT BIT(1) + +/* M_CMD OP codes for I2C */ +#define I2C_WRITE 0x1 +#define I2C_READ 0x2 +#define I2C_WRITE_READ 0x3 +#define I2C_ADDR_ONLY 0x4 +#define I2C_BUS_CLEAR 0x6 +#define I2C_STOP_ON_BUS 0x7 +/* M_CMD params for I2C */ +#define PRE_CMD_DELAY BIT(0) +#define TIMESTAMP_BEFORE BIT(1) +#define STOP_STRETCH BIT(2) +#define TIMESTAMP_AFTER BIT(3) +#define POST_COMMAND_DELAY BIT(4) +#define IGNORE_ADD_NACK BIT(6) +#define READ_FINISHED_WITH_ACK BIT(7) +#define BYPASS_ADDR_PHASE BIT(8) +#define SLV_ADDR_MSK GENMASK(15, 9) +#define SLV_ADDR_SHFT 9 +/* I2C SCL COUNTER fields */ +#define HIGH_COUNTER_MSK GENMASK(29, 20) +#define HIGH_COUNTER_SHFT 20 +#define LOW_COUNTER_MSK GENMASK(19, 10) +#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, + ADDR_NACK, + DATA_NACK, + GP_IRQ2, + BUS_PROTO, + ARB_LOST, + GP_IRQ5, + GENI_OVERRUN, + GENI_ILLEGAL_CMD, + GENI_ABORT_DONE, + GENI_TIMEOUT, +}; + +#define DM_I2C_CB_ERR ((BIT(ADDR_NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \ + << 5) + +#define I2C_AUTO_SUSPEND_DELAY 250 +#define PACKING_BYTES_PW 4 + +#define ABORT_TIMEOUT HZ +#define CANCEL_TIMEOUT HZ +#define XFER_TIMEOUT HZ +#define RST_TIMEOUT HZ + +struct geni_i2c_desc { + bool no_dma_support; + unsigned int tx_fifo_depth; + int (*resources_init)(struct geni_se *se); + int (*set_rate)(struct geni_se *se, unsigned long freq); + int (*power_on)(struct geni_se *se); + int (*power_off)(struct geni_se *se); +}; + +#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; + int irq; + int err; + struct i2c_adapter adap; + struct completion done; + struct completion abort_done; + struct completion cancel_done; + struct completion tx_reset_done; + struct completion rx_reset_done; + struct i2c_msg *cur; + int cur_wr; + int cur_rd; + spinlock_t lock; + u32 clk_freq_out; + const struct geni_i2c_clk_fld *clk_fld; + void *dma_buf; + size_t xfer_len; + dma_addr_t dma_addr; + struct dma_chan *tx_c; + struct dma_chan *rx_c; + bool no_dma; + bool gpi_mode; + bool is_tx_multi_desc_xfer; + u32 num_msgs; + struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config; + const struct geni_i2c_desc *dev_data; +}; + +struct geni_i2c_err_log { + int err; + const char *msg; +}; + +static const struct geni_i2c_err_log gi2c_log[] = { + [GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"}, + [ADDR_NACK] = {-ENXIO, "NACK: target device unresponsive, check its power/reset-ln"}, + [DATA_NACK] = {-EIO, "Data NACK: TX transfer NACK"}, + [GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"}, + [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"}, + [GENI_ILLEGAL_CMD] = {-EIO, "Illegal cmd, check GENI cmd-state machine"}, + [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"}, + [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"}, +}; + +struct geni_i2c_clk_fld { + u32 clk_freq_out; + u8 clk_div; + u8 t_high_cnt; + u8 t_low_cnt; + u8 t_cycle_cnt; +}; + +/* + * Hardware uses the underlying formula to calculate time periods of + * SCL clock cycle. Firmware uses some additional cycles excluded from the + * below formula and it is confirmed that the time periods are within + * specification limits. + * + * time of high period of SCL: t_high = (t_high_cnt * clk_div) / source_clock + * time of low period of SCL: t_low = (t_low_cnt * clk_div) / source_clock + * time of full period of SCL: t_cycle = (t_cycle_cnt * clk_div) / source_clock + * clk_freq_out = t / t_cycle + * source_clock = 19.2 MHz + */ +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, 12, 9, 10, 26 }, + { 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) +{ + const struct geni_i2c_clk_fld *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; +} + +static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev); + const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; + u32 val; + + writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL); + + val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN; + writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG); + + val = itr->t_high_cnt << HIGH_COUNTER_SHFT; + val |= itr->t_low_cnt << LOW_COUNTER_SHFT; + val |= itr->t_cycle_cnt; + writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS); + trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out, + itr->clk_div, itr->t_high_cnt, + itr->t_low_cnt, itr->t_cycle_cnt); + return 0; +} + +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c) +{ + u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0); + u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS); + u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS); + u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS); + u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN); + u32 rx_st, tx_st; + + if (dma) { + rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT); + tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT); + } else { + rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS); + tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS); + } + dev_dbg(gi2c->se.dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n", + dma, tx_st, rx_st, m_stat); + dev_dbg(gi2c->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n", + m_cmd, geni_s, geni_ios); +} + +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err) +{ + if (!gi2c->err) + gi2c->err = gi2c_log[err].err; + if (gi2c->cur) + dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n", + gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags); + + trace_geni_i2c_err(gi2c->se.dev, gi2c_log[err].err, gi2c_log[err].msg); + + switch (err) { + case GENI_ABORT_DONE: + case ADDR_NACK: + case DATA_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; + } +} + +static void geni_i2c_check_addr_data_nack(struct geni_i2c_dev *gi2c) +{ + if (!readl_relaxed(gi2c->se.base + SE_GENI_M_GP_LENGTH)) + geni_i2c_err(gi2c, ADDR_NACK); + else if (!(gi2c->cur->flags & I2C_M_RD)) + geni_i2c_err(gi2c, DATA_NACK); +} + +static irqreturn_t geni_i2c_irq(int irq, void *dev) +{ + struct geni_i2c_dev *gi2c = dev; + void __iomem *base = gi2c->se.base; + int j, p; + u32 m_stat; + u32 rx_st; + u32 dm_tx_st; + u32 dm_rx_st; + u32 dma; + u32 val; + struct i2c_msg *cur; + + 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); + dm_rx_st = readl_relaxed(base + SE_DMA_RX_IRQ_STAT); + dma = readl_relaxed(base + SE_GENI_DMA_MODE_EN); + cur = gi2c->cur; + + trace_geni_i2c_irq(gi2c->se.dev, m_stat, rx_st, dm_tx_st, dm_rx_st); + + if (!cur || + m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) || + dm_rx_st & (DM_I2C_CB_ERR)) { + if (m_stat & M_GP_IRQ_1_EN) + geni_i2c_check_addr_data_nack(gi2c); + if (m_stat & M_GP_IRQ_3_EN) + geni_i2c_err(gi2c, BUS_PROTO); + if (m_stat & M_GP_IRQ_4_EN) + geni_i2c_err(gi2c, ARB_LOST); + if (m_stat & M_CMD_OVERRUN_EN) + geni_i2c_err(gi2c, GENI_OVERRUN); + if (m_stat & M_ILLEGAL_CMD_EN) + geni_i2c_err(gi2c, GENI_ILLEGAL_CMD); + if (m_stat & M_CMD_ABORT_EN) + geni_i2c_err(gi2c, GENI_ABORT_DONE); + if (m_stat & M_GP_IRQ_0_EN) + geni_i2c_err(gi2c, GP_IRQ0); + + /* Disable the TX Watermark interrupt to stop TX */ + if (!dma) + writel_relaxed(0, base + SE_GENI_TX_WATERMARK_REG); + } else if (dma) { + dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n", + dm_tx_st, dm_rx_st); + } else if (cur->flags & I2C_M_RD && + m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) { + u32 rxcnt = rx_st & RX_FIFO_WC_MSK; + + for (j = 0; j < rxcnt; j++) { + p = 0; + val = readl_relaxed(base + SE_GENI_RX_FIFOn); + while (gi2c->cur_rd < cur->len && p < sizeof(val)) { + cur->buf[gi2c->cur_rd++] = val & 0xff; + val >>= 8; + p++; + } + if (gi2c->cur_rd == cur->len) + break; + } + } else if (!(cur->flags & I2C_M_RD) && + m_stat & M_TX_FIFO_WATERMARK_EN) { + for (j = 0; j < gi2c->tx_wm; j++) { + u32 temp; + + val = 0; + p = 0; + while (gi2c->cur_wr < cur->len && p < sizeof(val)) { + temp = cur->buf[gi2c->cur_wr++]; + val |= temp << (p * 8); + p++; + } + writel_relaxed(val, base + SE_GENI_TX_FIFOn); + /* TX Complete, Disable the TX Watermark interrupt */ + if (gi2c->cur_wr == cur->len) { + writel_relaxed(0, base + SE_GENI_TX_WATERMARK_REG); + break; + } + } + } + + if (m_stat) + writel_relaxed(m_stat, base + SE_GENI_M_IRQ_CLEAR); + + if (dma && dm_tx_st) + writel_relaxed(dm_tx_st, base + SE_DMA_TX_IRQ_CLR); + if (dma && dm_rx_st) + writel_relaxed(dm_rx_st, base + SE_DMA_RX_IRQ_CLR); + + /* if this is err with done-bit not set, handle that through timeout. */ + if (m_stat & M_CMD_DONE_EN || + dm_tx_st & TX_DMA_DONE || + dm_rx_st & RX_DMA_DONE) + complete(&gi2c->done); + if (m_stat & M_CMD_CANCEL_EN) + complete(&gi2c->cancel_done); + if (m_stat & M_CMD_ABORT_EN) + complete(&gi2c->abort_done); + if (dm_tx_st & TX_RESET_DONE) + complete(&gi2c->tx_reset_done); + if (dm_rx_st & RX_RESET_DONE) + complete(&gi2c->rx_reset_done); + + spin_unlock(&gi2c->lock); + + return IRQ_HANDLED; +} + +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c) +{ + unsigned long time_left = ABORT_TIMEOUT; + unsigned long flags; + + reinit_completion(&gi2c->abort_done); + + spin_lock_irqsave(&gi2c->lock, flags); + geni_se_abort_m_cmd(&gi2c->se); + spin_unlock_irqrestore(&gi2c->lock, flags); + + time_left = wait_for_completion_timeout(&gi2c->abort_done, time_left); + if (!time_left) + dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n"); +} + +static void geni_i2c_cancel_xfer(struct geni_i2c_dev *gi2c) +{ + unsigned long time_left = msecs_to_jiffies(CANCEL_TIMEOUT); + unsigned long flags; + + reinit_completion(&gi2c->cancel_done); + + spin_lock_irqsave(&gi2c->lock, flags); + if (!gi2c->err) + geni_i2c_err(gi2c, GENI_TIMEOUT); + gi2c->cur = NULL; + geni_se_cancel_m_cmd(&gi2c->se); + spin_unlock_irqrestore(&gi2c->lock, flags); + + time_left = wait_for_completion_timeout(&gi2c->cancel_done, time_left); + if (!time_left) { + dev_err(gi2c->se.dev, "Timeout cancel_m_cmd\n"); + geni_i2c_abort_xfer(gi2c); + } +} + +static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c) +{ + unsigned long time_left = RST_TIMEOUT; + + reinit_completion(&gi2c->rx_reset_done); + writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST); + + time_left = wait_for_completion_timeout(&gi2c->rx_reset_done, time_left); + if (!time_left) + dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n"); +} + +static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c) +{ + unsigned long time_left = RST_TIMEOUT; + + reinit_completion(&gi2c->tx_reset_done); + writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST); + + time_left = wait_for_completion_timeout(&gi2c->tx_reset_done, time_left); + if (!time_left) + 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 && gi2c->err != gi2c_log[ADDR_NACK].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 && gi2c->err != gi2c_log[ADDR_NACK].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 = 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 = gi2c->no_dma ? NULL : i2c_get_dma_safe_msg_buf(msg, 32); + if (dma_buf) + geni_se_select_mode(se, GENI_SE_DMA); + else + geni_se_select_mode(se, GENI_SE_FIFO); + + writel_relaxed(len, se->base + SE_I2C_RX_TRANS_LEN); + geni_se_setup_m_cmd(se, I2C_READ, m_param); + + if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &rx_dma)) { + 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 || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err)) + geni_i2c_cancel_xfer(gi2c); + + geni_i2c_rx_msg_cleanup(gi2c, cur); + + return gi2c->err; +} + +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, + u32 m_param) +{ + 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 = gi2c->no_dma ? NULL : i2c_get_dma_safe_msg_buf(msg, 32); + if (dma_buf) + geni_se_select_mode(se, GENI_SE_DMA); + else + geni_se_select_mode(se, GENI_SE_FIFO); + + writel_relaxed(len, se->base + SE_I2C_TX_TRANS_LEN); + geni_se_setup_m_cmd(se, I2C_WRITE, m_param); + + if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &tx_dma)) { + 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 || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err)) + geni_i2c_cancel_xfer(gi2c); + + 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 = NULL; + dma_addr_t addr = 0; + 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; + + /* + * Skip TX DMA mapping for a read message (I2C_M_RD) to avoid + * programming an extra TX DMA TRE that would cause an unintended + * write cycle on the I2C bus before the actual read operation. + */ + if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) { + peripheral->multi_msg = true; + goto skip_tx_dma_map; + } + + 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; + } + +skip_tx_dma_map: + 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: + /* Avoid DMA unmap as the write operation skipped DMA mapping */ + if (dma_buf) { + 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; + + trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out, + itr->clk_div, itr->t_high_cnt, + itr->t_low_cnt, itr->t_cycle_cnt); + + 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 = kzalloc_objs(dma_addr_t, num); + 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; + reinit_completion(&gi2c->done); + 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]; + gi2c->err = 0; + reinit_completion(&gi2c->done); + 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 ret; + + ret = pm_runtime_get_sync(gi2c->se.dev); + if (ret < 0) { + dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret); + pm_runtime_put_noidle(gi2c->se.dev); + /* Set device in suspended since resume failed */ + pm_runtime_set_suspended(gi2c->se.dev); + return ret; + } + + ret = gi2c->dev_data->set_rate(&gi2c->se, gi2c->clk_freq_out); + if (ret) + return ret; + + if (gi2c->gpi_mode) + ret = geni_i2c_gpi_xfer(gi2c, msgs, num); + else + ret = geni_i2c_fifo_xfer(gi2c, msgs, num); + + pm_runtime_put_autosuspend(gi2c->se.dev); + gi2c->cur = NULL; + gi2c->err = 0; + return ret; +} + +static u32 geni_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm geni_i2c_algo = { + .xfer = geni_i2c_xfer, + .functionality = geni_i2c_func, +}; + +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_init(struct geni_i2c_dev *gi2c) +{ + u32 proto, tx_depth; + bool fifo_disable; + int ret; + + ret = pm_runtime_resume_and_get(gi2c->se.dev); + if (ret < 0) { + dev_err(gi2c->se.dev, "error turning on device :%d\n", ret); + return ret; + } + + proto = geni_se_read_proto(&gi2c->se); + if (proto == GENI_SE_INVALID_PROTO) { + ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C); + if (ret) { + dev_err_probe(gi2c->se.dev, ret, "i2c firmware load failed ret: %d\n", ret); + goto err; + } + } else if (proto != GENI_SE_I2C) { + ret = dev_err_probe(gi2c->se.dev, -ENXIO, "Invalid proto %d\n", proto); + goto err; + } + + if (gi2c->dev_data->no_dma_support) { + fifo_disable = false; + gi2c->no_dma = true; + } 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; + + dev_dbg(gi2c->se.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 && gi2c->se.core_clk) + tx_depth = gi2c->dev_data->tx_fifo_depth; + + if (!tx_depth) { + ret = dev_err_probe(gi2c->se.dev, -EINVAL, + "Invalid TX FIFO depth\n"); + goto err; + } + + 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(gi2c->se.dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); + } + +err: + pm_runtime_put(gi2c->se.dev); + return ret; +} + +static int geni_i2c_resources_init(struct geni_se *se) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev); + int ret; + + ret = geni_se_resources_init(&gi2c->se); + if (ret) + return ret; + + ret = geni_i2c_clk_map_idx(gi2c); + if (ret) + return dev_err_probe(gi2c->se.dev, ret, "Invalid clk frequency %d Hz\n", + gi2c->clk_freq_out); + + return geni_icc_set_bw_ab(&gi2c->se, GENI_DEFAULT_BW, GENI_DEFAULT_BW, + Bps_to_icc(gi2c->clk_freq_out)); +} + +static int geni_i2c_probe(struct platform_device *pdev) +{ + struct geni_i2c_dev *gi2c; + int ret; + struct device *dev = &pdev->dev; + + gi2c = devm_kzalloc(dev, sizeof(*gi2c), GFP_KERNEL); + if (!gi2c) + return -ENOMEM; + + 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->dev_data = device_get_match_data(&pdev->dev); + if (!gi2c->dev_data) + return -EINVAL; + + ret = device_property_read_u32(dev, "clock-frequency", + &gi2c->clk_freq_out); + if (ret) { + 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) + return gi2c->irq; + + gi2c->adap.algo = &geni_i2c_algo; + init_completion(&gi2c->done); + init_completion(&gi2c->abort_done); + init_completion(&gi2c->cancel_done); + init_completion(&gi2c->tx_reset_done); + init_completion(&gi2c->rx_reset_done); + spin_lock_init(&gi2c->lock); + platform_set_drvdata(pdev, gi2c); + + ret = gi2c->dev_data->resources_init(&gi2c->se); + if (ret) + return ret; + + /* 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 ret; + + i2c_set_adapdata(&gi2c->adap, gi2c); + gi2c->adap.dev.parent = dev; + gi2c->adap.dev.of_node = dev->of_node; + strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name)); + + pm_runtime_set_suspended(dev); + pm_runtime_set_autosuspend_delay(dev, I2C_AUTO_SUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = geni_i2c_init(gi2c); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&gi2c->adap); + if (ret) + return dev_err_probe(dev, ret, "Error adding i2c adapter\n"); + + dev_dbg(dev, "Geni-I2C adaptor successfully added\n"); + + return 0; +} + +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); +} + +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) +{ + int ret = 0; + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + disable_irq(gi2c->irq); + + if (gi2c->dev_data->power_off) { + ret = gi2c->dev_data->power_off(&gi2c->se); + if (ret) { + enable_irq(gi2c->irq); + return ret; + } + } + + return 0; +} + +static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) +{ + int ret = 0; + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + if (gi2c->dev_data->power_on) { + ret = gi2c->dev_data->power_on(&gi2c->se); + if (ret) + return ret; + } + + enable_irq(gi2c->irq); + + return 0; +} + +static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&gi2c->adap); + + ret = pm_runtime_force_suspend(dev); + if (ret) + i2c_mark_adapter_resumed(&gi2c->adap); + + return ret; +} + +static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + 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, geni_i2c_resume_noirq) + SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, + NULL) +}; + +static const struct geni_i2c_desc geni_i2c = { + .resources_init = geni_i2c_resources_init, + .set_rate = qcom_geni_i2c_conf, + .power_on = geni_se_resources_activate, + .power_off = geni_se_resources_deactivate, +}; + +static const struct geni_i2c_desc i2c_master_hub = { + .no_dma_support = true, + .tx_fifo_depth = 16, + .resources_init = geni_i2c_resources_init, + .set_rate = qcom_geni_i2c_conf, + .power_on = geni_se_resources_activate, + .power_off = geni_se_resources_deactivate, +}; + +static const struct geni_i2c_desc sa8255p_geni_i2c = { + .resources_init = geni_se_domain_attach, + .set_rate = geni_se_set_perf_opp, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id geni_i2c_acpi_match[] = { + { "QCOM0220", (kernel_ulong_t)&geni_i2c}, + { "QCOM0411", (kernel_ulong_t)&geni_i2c}, + { } +}; +MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match); +#endif + +static const struct of_device_id geni_i2c_dt_match[] = { + { .compatible = "qcom,geni-i2c", .data = &geni_i2c }, + { .compatible = "qcom,geni-i2c-master-hub", .data = &i2c_master_hub }, + { .compatible = "qcom,sa8255p-geni-i2c", .data = &sa8255p_geni_i2c }, + {} +}; +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), + }, +}; + +module_platform_driver(geni_i2c_driver); + +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c new file mode 100644 index 000000000..ee7915ee2 --- /dev/null +++ b/drivers/i2c/busses/i2c-qup.c @@ -0,0 +1,2042 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, Sony Mobile Communications AB. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* QUP Registers */ +#define QUP_CONFIG 0x000 +#define QUP_STATE 0x004 +#define QUP_IO_MODE 0x008 +#define QUP_SW_RESET 0x00c +#define QUP_OPERATIONAL 0x018 +#define QUP_ERROR_FLAGS 0x01c +#define QUP_ERROR_FLAGS_EN 0x020 +#define QUP_OPERATIONAL_MASK 0x028 +#define QUP_HW_VERSION 0x030 +#define QUP_MX_OUTPUT_CNT 0x100 +#define QUP_OUT_FIFO_BASE 0x110 +#define QUP_MX_WRITE_CNT 0x150 +#define QUP_MX_INPUT_CNT 0x200 +#define QUP_MX_READ_CNT 0x208 +#define QUP_IN_FIFO_BASE 0x218 +#define QUP_I2C_CLK_CTL 0x400 +#define QUP_I2C_STATUS 0x404 +#define QUP_I2C_MASTER_GEN 0x408 + +/* QUP States and reset values */ +#define QUP_RESET_STATE 0 +#define QUP_RUN_STATE 1 +#define QUP_PAUSE_STATE 3 +#define QUP_STATE_MASK 3 + +#define QUP_STATE_VALID BIT(2) +#define QUP_I2C_MAST_GEN BIT(4) +#define QUP_I2C_FLUSH BIT(6) + +#define QUP_OPERATIONAL_RESET 0x000ff0 +#define QUP_I2C_STATUS_RESET 0xfffffc + +/* QUP OPERATIONAL FLAGS */ +#define QUP_I2C_NACK_FLAG BIT(3) +#define QUP_OUT_NOT_EMPTY BIT(4) +#define QUP_IN_NOT_EMPTY BIT(5) +#define QUP_OUT_FULL BIT(6) +#define QUP_OUT_SVC_FLAG BIT(8) +#define QUP_IN_SVC_FLAG BIT(9) +#define QUP_MX_OUTPUT_DONE BIT(10) +#define QUP_MX_INPUT_DONE BIT(11) +#define OUT_BLOCK_WRITE_REQ BIT(12) +#define IN_BLOCK_READ_REQ BIT(13) + +/* I2C mini core related values */ +#define QUP_NO_INPUT BIT(7) +#define QUP_CLOCK_AUTO_GATE BIT(13) +#define I2C_MINI_CORE (2 << 8) +#define I2C_N_VAL 15 +#define I2C_N_VAL_V2 7 + +/* Most significant word offset in FIFO port */ +#define QUP_MSW_SHIFT (I2C_N_VAL + 1) + +/* Packing/Unpacking words in FIFOs, and IO modes */ +#define QUP_OUTPUT_BLK_MODE (1 << 10) +#define QUP_OUTPUT_BAM_MODE (3 << 10) +#define QUP_INPUT_BLK_MODE (1 << 12) +#define QUP_INPUT_BAM_MODE (3 << 12) +#define QUP_BAM_MODE (QUP_OUTPUT_BAM_MODE | QUP_INPUT_BAM_MODE) +#define QUP_UNPACK_EN BIT(14) +#define QUP_PACK_EN BIT(15) + +#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN) +#define QUP_V2_TAGS_EN 1 + +#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03) +#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07) +#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03) +#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07) + +/* QUP tags */ +#define QUP_TAG_START (1 << 8) +#define QUP_TAG_DATA (2 << 8) +#define QUP_TAG_STOP (3 << 8) +#define QUP_TAG_REC (4 << 8) +#define QUP_BAM_INPUT_EOT 0x93 +#define QUP_BAM_FLUSH_STOP 0x96 + +/* QUP v2 tags */ +#define QUP_TAG_V2_START 0x81 +#define QUP_TAG_V2_DATAWR 0x82 +#define QUP_TAG_V2_DATAWR_STOP 0x83 +#define QUP_TAG_V2_DATARD 0x85 +#define QUP_TAG_V2_DATARD_NACK 0x86 +#define QUP_TAG_V2_DATARD_STOP 0x87 + +/* Status, Error flags */ +#define I2C_STATUS_WR_BUFFER_FULL BIT(0) +#define I2C_STATUS_BUS_ACTIVE BIT(8) +#define I2C_STATUS_ERROR_MASK 0x38000fc +#define QUP_STATUS_ERROR_FLAGS 0x7c + +#define QUP_READ_LIMIT 256 +#define SET_BIT 0x1 +#define RESET_BIT 0x0 +#define ONE_BYTE 0x1 +#define QUP_I2C_MX_CONFIG_DURING_RUN BIT(31) + +/* Maximum transfer length for single DMA descriptor */ +#define MX_TX_RX_LEN SZ_64K +#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT) +/* Maximum transfer length for all DMA descriptors */ +#define MX_DMA_TX_RX_LEN (2 * MX_TX_RX_LEN) +#define MX_DMA_BLOCKS (MX_DMA_TX_RX_LEN / QUP_READ_LIMIT) + +/* + * Minimum transfer timeout for i2c transfers in seconds. It will be added on + * the top of maximum transfer time calculated from i2c bus speed to compensate + * the overheads. + */ +#define TOUT_MIN 2 + +/* Default values. Use these if FW query fails */ +#define DEFAULT_CLK_FREQ I2C_MAX_STANDARD_MODE_FREQ +#define DEFAULT_SRC_CLK 20000000 + +/* + * Max tags length (start, stop and maximum 2 bytes address) for each QUP + * data transfer + */ +#define QUP_MAX_TAGS_LEN 4 +/* Max data length for each DATARD tags */ +#define RECV_MAX_DATA_LEN 254 +/* 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"); + +/* + * count: no of blocks + * pos: current block number + * tx_tag_len: tx tag length for current block + * rx_tag_len: rx tag length for current block + * data_len: remaining data length for current message + * cur_blk_len: data length for current block + * total_tx_len: total tx length including tag bytes for current QUP transfer + * total_rx_len: total rx length including tag bytes for current QUP transfer + * tx_fifo_data_pos: current byte number in TX FIFO word + * tx_fifo_free: number of free bytes in current QUP block write. + * rx_fifo_data_pos: current byte number in RX FIFO word + * fifo_available: number of available bytes in RX FIFO for current + * QUP block read + * tx_fifo_data: QUP TX FIFO write works on word basis (4 bytes). New byte write + * to TX FIFO will be appended in this data and will be written to + * TX FIFO when all the 4 bytes are available. + * rx_fifo_data: QUP RX FIFO read works on word basis (4 bytes). This will + * contains the 4 bytes of RX data. + * cur_data: pointer to tell cur data position for current message + * cur_tx_tags: pointer to tell cur position in tags + * tx_tags_sent: all tx tag bytes have been written in FIFO word + * send_last_word: for tx FIFO, last word send is pending in current block + * rx_bytes_read: if all the bytes have been read from rx FIFO. + * rx_tags_fetched: all the rx tag bytes have been fetched from rx fifo word + * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer. + * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer. + * tags: contains tx tag bytes for current QUP transfer + */ +struct qup_i2c_block { + int count; + int pos; + int tx_tag_len; + int rx_tag_len; + int data_len; + int cur_blk_len; + int total_tx_len; + int total_rx_len; + int tx_fifo_data_pos; + int tx_fifo_free; + int rx_fifo_data_pos; + int fifo_available; + u32 tx_fifo_data; + u32 rx_fifo_data; + u8 *cur_data; + u8 *cur_tx_tags; + bool tx_tags_sent; + bool send_last_word; + bool rx_tags_fetched; + bool rx_bytes_read; + bool is_tx_blk_mode; + bool is_rx_blk_mode; + u8 tags[6]; +}; + +struct qup_i2c_tag { + u8 *start; + dma_addr_t addr; +}; + +struct qup_i2c_bam { + struct qup_i2c_tag tag; + struct dma_chan *dma; + struct scatterlist *sg; + unsigned int sg_cnt; +}; + +struct qup_i2c_dev { + struct device *dev; + void __iomem *base; + int irq; + struct clk *clk; + struct clk *pclk; + struct icc_path *icc_path; + struct i2c_adapter adap; + + int clk_ctl; + int out_fifo_sz; + int in_fifo_sz; + int out_blk_sz; + int in_blk_sz; + + int blk_xfer_limit; + unsigned long one_byte_t; + unsigned long xfer_timeout; + struct qup_i2c_block blk; + + struct i2c_msg *msg; + /* Current posion in user message buffer */ + int pos; + /* I2C protocol errors */ + u32 bus_err; + /* QUP core errors */ + u32 qup_err; + + /* To check if this is the last msg */ + bool is_last; + bool is_smbus_read; + + /* 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 */ + bool use_dma; + unsigned int max_xfer_sg_len; + unsigned int tag_buf_pos; + /* The threshold length above which block mode will be used */ + unsigned int blk_mode_threshold; + struct dma_pool *dpool; + struct qup_i2c_tag start_tag; + struct qup_i2c_bam brx; + struct qup_i2c_bam btx; + + struct completion xfer; + /* function to write data in tx fifo */ + void (*write_tx_fifo)(struct qup_i2c_dev *qup); + /* function to read data from rx fifo */ + void (*read_rx_fifo)(struct qup_i2c_dev *qup); + /* function to write tags in tx fifo for i2c read transfer */ + void (*write_rx_tags)(struct qup_i2c_dev *qup); +}; + +static irqreturn_t qup_i2c_interrupt(int irq, void *dev) +{ + struct qup_i2c_dev *qup = dev; + struct qup_i2c_block *blk = &qup->blk; + u32 bus_err; + u32 qup_err; + u32 opflags; + + bus_err = readl(qup->base + QUP_I2C_STATUS); + qup_err = readl(qup->base + QUP_ERROR_FLAGS); + opflags = readl(qup->base + QUP_OPERATIONAL); + + if (!qup->msg) { + /* Clear Error interrupt */ + writel(QUP_RESET_STATE, qup->base + QUP_STATE); + return IRQ_HANDLED; + } + + bus_err &= I2C_STATUS_ERROR_MASK; + qup_err &= QUP_STATUS_ERROR_FLAGS; + + /* Clear the error bits in QUP_ERROR_FLAGS */ + if (qup_err) + writel(qup_err, qup->base + QUP_ERROR_FLAGS); + + /* Clear the error bits in QUP_I2C_STATUS */ + if (bus_err) + writel(bus_err, qup->base + QUP_I2C_STATUS); + + /* + * Check for BAM mode and returns if already error has come for current + * transfer. In Error case, sometimes, QUP generates more than one + * interrupt. + */ + if (qup->use_dma && (qup->qup_err || qup->bus_err)) + return IRQ_HANDLED; + + /* Reset the QUP State in case of error */ + if (qup_err || bus_err) { + /* + * Don’t reset the QUP state in case of BAM mode. The BAM + * flush operation needs to be scheduled in transfer function + * which will clear the remaining schedule descriptors in BAM + * HW FIFO and generates the BAM interrupt. + */ + if (!qup->use_dma) + writel(QUP_RESET_STATE, qup->base + QUP_STATE); + goto done; + } + + if (opflags & QUP_OUT_SVC_FLAG) { + writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL); + + if (opflags & OUT_BLOCK_WRITE_REQ) { + blk->tx_fifo_free += qup->out_blk_sz; + if (qup->msg->flags & I2C_M_RD) + qup->write_rx_tags(qup); + else + qup->write_tx_fifo(qup); + } + } + + if (opflags & QUP_IN_SVC_FLAG) { + writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL); + + if (!blk->is_rx_blk_mode) { + blk->fifo_available += qup->in_fifo_sz; + qup->read_rx_fifo(qup); + } else if (opflags & IN_BLOCK_READ_REQ) { + blk->fifo_available += qup->in_blk_sz; + qup->read_rx_fifo(qup); + } + } + + if (qup->msg->flags & I2C_M_RD) { + if (!blk->rx_bytes_read) + return IRQ_HANDLED; + } else { + /* + * Ideally, QUP_MAX_OUTPUT_DONE_FLAG should be checked + * for FIFO mode also. But, QUP_MAX_OUTPUT_DONE_FLAG lags + * behind QUP_OUTPUT_SERVICE_FLAG sometimes. The only reason + * of interrupt for write message in FIFO mode is + * QUP_MAX_OUTPUT_DONE_FLAG condition. + */ + if (blk->is_tx_blk_mode && !(opflags & QUP_MX_OUTPUT_DONE)) + return IRQ_HANDLED; + } + +done: + qup->qup_err = qup_err; + qup->bus_err = bus_err; + complete(&qup->xfer); + return IRQ_HANDLED; +} + +static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup, + u32 req_state, u32 req_mask) +{ + int retries = 1; + u32 state; + + /* + * State transition takes 3 AHB clocks cycles + 3 I2C master clock + * cycles. So retry once after a 1uS delay. + */ + do { + state = readl(qup->base + QUP_STATE); + + if (state & QUP_STATE_VALID && + (state & req_mask) == req_state) + return 0; + + udelay(1); + } while (retries--); + + return -ETIMEDOUT; +} + +static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state) +{ + return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK); +} + +static void qup_i2c_flush(struct qup_i2c_dev *qup) +{ + u32 val = readl(qup->base + QUP_STATE); + + val |= QUP_I2C_FLUSH; + writel(val, qup->base + QUP_STATE); +} + +static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup) +{ + return qup_i2c_poll_state_mask(qup, 0, 0); +} + +static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup) +{ + return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN); +} + +static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state) +{ + if (qup_i2c_poll_state_valid(qup) != 0) + return -EIO; + + writel(state, qup->base + QUP_STATE); + + if (qup_i2c_poll_state(qup, state) != 0) + return -EIO; + return 0; +} + +/* Check if I2C bus returns to IDLE state */ +static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len) +{ + unsigned long timeout; + u32 status; + int ret = 0; + + timeout = jiffies + len * 4; + for (;;) { + status = readl(qup->base + QUP_I2C_STATUS); + if (!(status & I2C_STATUS_BUS_ACTIVE)) + break; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + break; + } + + usleep_range(len, len * 2); + } + + 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; + struct i2c_msg *msg = qup->msg; + u32 addr = i2c_8bit_addr_from_msg(msg); + u32 qup_tag; + int idx; + u32 val; + + if (qup->pos == 0) { + val = QUP_TAG_START | addr; + idx = 1; + blk->tx_fifo_free--; + } else { + val = 0; + idx = 0; + } + + while (blk->tx_fifo_free && qup->pos < msg->len) { + if (qup->pos == msg->len - 1) + qup_tag = QUP_TAG_STOP; + else + qup_tag = QUP_TAG_DATA; + + if (idx & 1) + val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT; + else + val = qup_tag | msg->buf[qup->pos]; + + /* Write out the pair and the last odd value */ + if (idx & 1 || qup->pos == msg->len - 1) + writel(val, qup->base + QUP_OUT_FIFO_BASE); + + qup->pos++; + idx++; + blk->tx_fifo_free--; + } +} + +static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup, + struct i2c_msg *msg) +{ + qup->blk.pos = 0; + qup->blk.data_len = msg->len; + qup->blk.count = DIV_ROUND_UP(msg->len, qup->blk_xfer_limit); +} + +static int qup_i2c_get_data_len(struct qup_i2c_dev *qup) +{ + int data_len; + + if (qup->blk.data_len > qup->blk_xfer_limit) + data_len = qup->blk_xfer_limit; + else + data_len = qup->blk.data_len; + + return data_len; +} + +static bool qup_i2c_check_msg_len(struct i2c_msg *msg) +{ + return ((msg->flags & I2C_M_RD) && (msg->flags & I2C_M_RECV_LEN)); +} + +static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup, + struct i2c_msg *msg) +{ + int len = 0; + + if (qup->is_smbus_read) { + tags[len++] = QUP_TAG_V2_DATARD_STOP; + tags[len++] = qup_i2c_get_data_len(qup); + } else { + tags[len++] = QUP_TAG_V2_START; + tags[len++] = addr & 0xff; + + if (msg->flags & I2C_M_TEN) + tags[len++] = addr >> 8; + + tags[len++] = QUP_TAG_V2_DATARD; + /* Read 1 byte indicating the length of the SMBus message */ + tags[len++] = 1; + } + return len; +} + +static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup, + struct i2c_msg *msg) +{ + u16 addr = i2c_8bit_addr_from_msg(msg); + int len = 0; + int data_len; + + int last = (qup->blk.pos == (qup->blk.count - 1)) && (qup->is_last); + + /* Handle tags for SMBus block read */ + if (qup_i2c_check_msg_len(msg)) + return qup_i2c_set_tags_smb(addr, tags, qup, msg); + + if (qup->blk.pos == 0) { + tags[len++] = QUP_TAG_V2_START; + tags[len++] = addr & 0xff; + + if (msg->flags & I2C_M_TEN) + tags[len++] = addr >> 8; + } + + /* Send _STOP commands for the last block */ + if (last) { + if (msg->flags & I2C_M_RD) + tags[len++] = QUP_TAG_V2_DATARD_STOP; + else + tags[len++] = QUP_TAG_V2_DATAWR_STOP; + } else { + if (msg->flags & I2C_M_RD) + tags[len++] = qup->blk.pos == (qup->blk.count - 1) ? + QUP_TAG_V2_DATARD_NACK : + QUP_TAG_V2_DATARD; + else + tags[len++] = QUP_TAG_V2_DATAWR; + } + + data_len = qup_i2c_get_data_len(qup); + + /* 0 implies 256 bytes */ + if (data_len == QUP_READ_LIMIT) + tags[len++] = 0; + else + tags[len++] = data_len; + + return len; +} + + +static void qup_i2c_bam_cb(void *data) +{ + struct qup_i2c_dev *qup = data; + + complete(&qup->xfer); +} + +static int qup_sg_set_buf(struct scatterlist *sg, void *buf, + unsigned int buflen, struct qup_i2c_dev *qup, + int dir) +{ + int ret; + + sg_set_buf(sg, buf, buflen); + ret = dma_map_sg(qup->dev, sg, 1, dir); + if (!ret) + return -EINVAL; + + return 0; +} + +static void qup_i2c_rel_dma(struct qup_i2c_dev *qup) +{ + if (qup->btx.dma) + dma_release_channel(qup->btx.dma); + if (qup->brx.dma) + dma_release_channel(qup->brx.dma); + qup->btx.dma = NULL; + qup->brx.dma = NULL; +} + +static int qup_i2c_req_dma(struct qup_i2c_dev *qup) +{ + int err; + + if (!qup->btx.dma) { + 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; + dev_err(qup->dev, "\n tx channel not available"); + return err; + } + } + + if (!qup->brx.dma) { + 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); + qup->brx.dma = NULL; + qup_i2c_rel_dma(qup); + return err; + } + } + return 0; +} + +static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg) +{ + int ret = 0, limit = QUP_READ_LIMIT; + u32 len = 0, blocks, rem; + u32 i = 0, tlen, tx_len = 0; + u8 *tags; + + qup->blk_xfer_limit = QUP_READ_LIMIT; + qup_i2c_set_blk_data(qup, msg); + + blocks = qup->blk.count; + rem = msg->len - (blocks - 1) * limit; + + if (msg->flags & I2C_M_RD) { + while (qup->blk.pos < blocks) { + tlen = (i == (blocks - 1)) ? rem : limit; + tags = &qup->start_tag.start[qup->tag_buf_pos + len]; + len += qup_i2c_set_tags(tags, qup, msg); + qup->blk.data_len -= tlen; + + /* scratch buf to read the start and len tags */ + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++], + &qup->brx.tag.start[0], + 2, qup, DMA_FROM_DEVICE); + + if (ret) + return ret; + + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++], + &msg->buf[limit * i], + tlen, qup, + DMA_FROM_DEVICE); + if (ret) + return ret; + + i++; + qup->blk.pos = i; + } + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++], + &qup->start_tag.start[qup->tag_buf_pos], + len, qup, DMA_TO_DEVICE); + if (ret) + return ret; + + qup->tag_buf_pos += len; + } else { + while (qup->blk.pos < blocks) { + tlen = (i == (blocks - 1)) ? rem : limit; + tags = &qup->start_tag.start[qup->tag_buf_pos + tx_len]; + len = qup_i2c_set_tags(tags, qup, msg); + qup->blk.data_len -= tlen; + + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++], + tags, len, + qup, DMA_TO_DEVICE); + if (ret) + return ret; + + tx_len += len; + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++], + &msg->buf[limit * i], + tlen, qup, DMA_TO_DEVICE); + if (ret) + return ret; + i++; + qup->blk.pos = i; + } + + qup->tag_buf_pos += tx_len; + } + + return 0; +} + +static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup) +{ + struct dma_async_tx_descriptor *txd, *rxd = NULL; + int ret = 0; + dma_cookie_t cookie_rx, cookie_tx; + u32 len = 0; + u32 tx_cnt = qup->btx.sg_cnt, rx_cnt = qup->brx.sg_cnt; + + /* schedule the EOT and FLUSH I2C tags */ + len = 1; + if (rx_cnt) { + qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT; + len++; + + /* scratch buf to read the BAM EOT FLUSH tags */ + ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++], + &qup->brx.tag.start[0], + 1, qup, DMA_FROM_DEVICE); + if (ret) + return ret; + } + + qup->btx.tag.start[len - 1] = QUP_BAM_FLUSH_STOP; + ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++], &qup->btx.tag.start[0], + len, qup, DMA_TO_DEVICE); + if (ret) + return ret; + + txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_cnt, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_PREP_FENCE); + if (!txd) { + dev_err(qup->dev, "failed to get tx desc\n"); + ret = -EINVAL; + goto desc_err; + } + + if (!rx_cnt) { + txd->callback = qup_i2c_bam_cb; + txd->callback_param = qup; + } + + cookie_tx = dmaengine_submit(txd); + if (dma_submit_error(cookie_tx)) { + ret = -EINVAL; + goto desc_err; + } + + dma_async_issue_pending(qup->btx.dma); + + if (rx_cnt) { + rxd = dmaengine_prep_slave_sg(qup->brx.dma, qup->brx.sg, + rx_cnt, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!rxd) { + dev_err(qup->dev, "failed to get rx desc\n"); + ret = -EINVAL; + + /* abort TX descriptors */ + dmaengine_terminate_sync(qup->btx.dma); + goto desc_err; + } + + rxd->callback = qup_i2c_bam_cb; + rxd->callback_param = qup; + cookie_rx = dmaengine_submit(rxd); + if (dma_submit_error(cookie_rx)) { + ret = -EINVAL; + goto desc_err; + } + + dma_async_issue_pending(qup->brx.dma); + } + + if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) + ret = -ETIMEDOUT; + + if (ret || qup->bus_err || qup->qup_err) { + reinit_completion(&qup->xfer); + + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) { + dev_err(qup->dev, "change to run state timed out"); + goto desc_err; + } + + qup_i2c_flush(qup); + + /* wait for remaining interrupts to occur */ + if (!wait_for_completion_timeout(&qup->xfer, HZ)) + dev_err(qup->dev, "flush timed out\n"); + + ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO; + } + +desc_err: + dma_unmap_sg(qup->dev, qup->btx.sg, tx_cnt, DMA_TO_DEVICE); + + if (rx_cnt) + dma_unmap_sg(qup->dev, qup->brx.sg, rx_cnt, + DMA_FROM_DEVICE); + + return ret; +} + +static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup) +{ + qup->btx.sg_cnt = 0; + qup->brx.sg_cnt = 0; + qup->tag_buf_pos = 0; +} + +static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct qup_i2c_dev *qup = i2c_get_adapdata(adap); + 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); + + if (ret) + goto out; + + writel(0, qup->base + QUP_MX_INPUT_CNT); + writel(0, qup->base + QUP_MX_OUTPUT_CNT); + + /* set BAM mode */ + writel(QUP_REPACK_EN | QUP_BAM_MODE, qup->base + QUP_IO_MODE); + + /* mask fifo irqs */ + writel((0x3 << 8), qup->base + QUP_OPERATIONAL_MASK); + + /* set RUN STATE */ + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + goto out; + + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); + qup_i2c_bam_clear_tag_buffers(qup); + + for (idx = 0; idx < num; idx++) { + qup->msg = msg + idx; + qup->is_last = idx == (num - 1); + + ret = qup_i2c_bam_make_desc(qup, qup->msg); + if (ret) + break; + + /* + * Make DMA descriptor and schedule the BAM transfer if its + * already crossed the maximum length. Since the memory for all + * tags buffers have been taken for 2 maximum possible + * transfers length so it will never cross the buffer actual + * length. + */ + if (qup->btx.sg_cnt > qup->max_xfer_sg_len || + qup->brx.sg_cnt > qup->max_xfer_sg_len || + qup->is_last) { + ret = qup_i2c_bam_schedule_desc(qup); + if (ret) + break; + + qup_i2c_bam_clear_tag_buffers(qup); + } + } + +out: + disable_irq(qup->irq); + + qup->msg = NULL; + return ret; +} + +static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup, + struct i2c_msg *msg) +{ + unsigned long left; + int ret = 0; + + left = wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout); + if (!left) { + writel(1, qup->base + QUP_SW_RESET); + ret = -ETIMEDOUT; + } + + if (qup->bus_err || qup->qup_err) + ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO; + + return ret; +} + +static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + struct i2c_msg *msg = qup->msg; + u32 val = 0; + int idx = 0; + + while (blk->fifo_available && qup->pos < msg->len) { + if ((idx & 1) == 0) { + /* Reading 2 words at time */ + val = readl(qup->base + QUP_IN_FIFO_BASE); + msg->buf[qup->pos++] = val & 0xFF; + } else { + msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT; + } + idx++; + blk->fifo_available--; + } + + if (qup->pos == msg->len) + blk->rx_bytes_read = true; +} + +static void qup_i2c_write_rx_tags_v1(struct qup_i2c_dev *qup) +{ + struct i2c_msg *msg = qup->msg; + u32 addr, len, val; + + addr = i2c_8bit_addr_from_msg(msg); + + /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */ + len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len; + + val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr; + writel(val, qup->base + QUP_OUT_FIFO_BASE); +} + +static void qup_i2c_conf_v1(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + 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; + 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; + writel(0, qup->base + QUP_MX_WRITE_CNT); + writel(blk->total_tx_len, qup->base + QUP_MX_OUTPUT_CNT); + } else { + writel(0, qup->base + QUP_MX_OUTPUT_CNT); + writel(blk->total_tx_len, qup->base + QUP_MX_WRITE_CNT); + } + + if (blk->total_rx_len) { + if (blk->is_rx_blk_mode) { + io_mode |= QUP_INPUT_BLK_MODE; + writel(0, qup->base + QUP_MX_READ_CNT); + writel(blk->total_rx_len, qup->base + QUP_MX_INPUT_CNT); + } else { + writel(0, qup->base + QUP_MX_INPUT_CNT); + writel(blk->total_rx_len, qup->base + QUP_MX_READ_CNT); + } + } else { + qup_config |= QUP_NO_INPUT; + } + + writel(qup_config, qup->base + QUP_CONFIG); + writel(io_mode, qup->base + QUP_IO_MODE); +} + +static void qup_i2c_clear_blk_v1(struct qup_i2c_block *blk) +{ + blk->tx_fifo_free = 0; + blk->fifo_available = 0; + blk->rx_bytes_read = false; +} + +static int qup_i2c_conf_xfer_v1(struct qup_i2c_dev *qup, bool is_rx) +{ + struct qup_i2c_block *blk = &qup->blk; + int ret; + + qup_i2c_clear_blk_v1(blk); + qup_i2c_conf_v1(qup); + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + return ret; + + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); + + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + if (ret) + return ret; + + reinit_completion(&qup->xfer); + enable_irq(qup->irq); + if (!blk->is_tx_blk_mode) { + blk->tx_fifo_free = qup->out_fifo_sz; + + if (is_rx) + qup_i2c_write_rx_tags_v1(qup); + else + qup_i2c_write_tx_fifo_v1(qup); + } + + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + goto err; + + ret = qup_i2c_wait_for_complete(qup, qup->msg); + if (ret) + goto err; + + ret = qup_i2c_bus_active(qup, ONE_BYTE); + +err: + disable_irq(qup->irq); + return ret; +} + +static int qup_i2c_write_one(struct qup_i2c_dev *qup) +{ + struct i2c_msg *msg = qup->msg; + struct qup_i2c_block *blk = &qup->blk; + + qup->pos = 0; + blk->total_tx_len = msg->len + 1; + blk->total_rx_len = 0; + + return qup_i2c_conf_xfer_v1(qup, false); +} + +static int qup_i2c_read_one(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + + qup->pos = 0; + blk->total_tx_len = 2; + blk->total_rx_len = qup->msg->len; + + return qup_i2c_conf_xfer_v1(qup, true); +} + +static int qup_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], + int num) +{ + struct qup_i2c_dev *qup = i2c_get_adapdata(adap); + int ret, idx; + + ret = pm_runtime_get_sync(qup->dev); + if (ret < 0) + goto out; + + qup->bus_err = 0; + qup->qup_err = 0; + + writel(1, qup->base + QUP_SW_RESET); + ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); + if (ret) + goto out; + + /* Configure QUP as I2C mini core */ + writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG); + + for (idx = 0; idx < num; idx++) { + if (qup_i2c_poll_state_i2c_master(qup)) { + ret = -EIO; + goto out; + } + + if (qup_i2c_check_msg_len(&msgs[idx])) { + ret = -EINVAL; + goto out; + } + + qup->msg = &msgs[idx]; + if (msgs[idx].flags & I2C_M_RD) + ret = qup_i2c_read_one(qup); + else + ret = qup_i2c_write_one(qup); + + if (ret) + break; + + ret = qup_i2c_change_state(qup, QUP_RESET_STATE); + if (ret) + break; + } + + if (ret == 0) + ret = num; +out: + + pm_runtime_put_autosuspend(qup->dev); + + return ret; +} + +/* + * Configure registers related with reconfiguration during run and call it + * before each i2c sub transfer. + */ +static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2; + + if (blk->is_tx_blk_mode) + writel(qup->config_run | blk->total_tx_len, + qup->base + QUP_MX_OUTPUT_CNT); + else + writel(qup->config_run | blk->total_tx_len, + qup->base + QUP_MX_WRITE_CNT); + + if (blk->total_rx_len) { + if (blk->is_rx_blk_mode) + writel(qup->config_run | blk->total_rx_len, + qup->base + QUP_MX_INPUT_CNT); + else + writel(qup->config_run | blk->total_rx_len, + qup->base + QUP_MX_READ_CNT); + } else { + qup_config |= QUP_NO_INPUT; + } + + writel(qup_config, qup->base + QUP_CONFIG); +} + +/* + * Configure registers related with transfer mode (FIFO/Block) + * before starting of i2c transfer. It will be called only once in + * QUP RESET state. + */ +static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + u32 io_mode = QUP_REPACK_EN; + + if (blk->is_tx_blk_mode) { + io_mode |= QUP_OUTPUT_BLK_MODE; + writel(0, qup->base + QUP_MX_WRITE_CNT); + } else { + writel(0, qup->base + QUP_MX_OUTPUT_CNT); + } + + if (blk->is_rx_blk_mode) { + io_mode |= QUP_INPUT_BLK_MODE; + writel(0, qup->base + QUP_MX_READ_CNT); + } else { + writel(0, qup->base + QUP_MX_INPUT_CNT); + } + + writel(io_mode, qup->base + QUP_IO_MODE); +} + +/* Clear required variables before starting of any QUP v2 sub transfer. */ +static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk) +{ + blk->send_last_word = false; + blk->tx_tags_sent = false; + blk->tx_fifo_data = 0; + blk->tx_fifo_data_pos = 0; + blk->tx_fifo_free = 0; + + blk->rx_tags_fetched = false; + blk->rx_bytes_read = false; + blk->rx_fifo_data = 0; + blk->rx_fifo_data_pos = 0; + blk->fifo_available = 0; +} + +/* Receive data from RX FIFO for read message in QUP v2 i2c transfer. */ +static void qup_i2c_recv_data(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + int j; + + for (j = blk->rx_fifo_data_pos; + blk->cur_blk_len && blk->fifo_available; + blk->cur_blk_len--, blk->fifo_available--) { + if (j == 0) + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE); + + *(blk->cur_data++) = blk->rx_fifo_data; + blk->rx_fifo_data >>= 8; + + if (j == 3) + j = 0; + else + j++; + } + + blk->rx_fifo_data_pos = j; +} + +/* Receive tags for read message in QUP v2 i2c transfer. */ +static void qup_i2c_recv_tags(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE); + blk->rx_fifo_data >>= blk->rx_tag_len * 8; + blk->rx_fifo_data_pos = blk->rx_tag_len; + blk->fifo_available -= blk->rx_tag_len; +} + +/* + * Read the data and tags from RX FIFO. Since in read case, the tags will be + * preceded by received data bytes so + * 1. Check if rx_tags_fetched is false i.e. the start of QUP block so receive + * all tag bytes and discard that. + * 2. Read the data from RX FIFO. When all the data bytes have been read then + * set rx_bytes_read to true. + */ +static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + + if (!blk->rx_tags_fetched) { + qup_i2c_recv_tags(qup); + blk->rx_tags_fetched = true; + } + + qup_i2c_recv_data(qup); + if (!blk->cur_blk_len) + blk->rx_bytes_read = true; +} + +/* + * Write bytes in TX FIFO for write message in QUP v2 i2c transfer. QUP TX FIFO + * write works on word basis (4 bytes). Append new data byte write for TX FIFO + * in tx_fifo_data and write to TX FIFO when all the 4 bytes are present. + */ +static void +qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned int *len) +{ + struct qup_i2c_block *blk = &qup->blk; + unsigned int j; + + for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free; + (*len)--, blk->tx_fifo_free--) { + blk->tx_fifo_data |= *(*data)++ << (j * 8); + if (j == 3) { + writel(blk->tx_fifo_data, + qup->base + QUP_OUT_FIFO_BASE); + blk->tx_fifo_data = 0x0; + j = 0; + } else { + j++; + } + } + + blk->tx_fifo_data_pos = j; +} + +/* Transfer tags for read message in QUP v2 i2c transfer. */ +static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len); + if (blk->tx_fifo_data_pos) + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE); +} + +/* + * Write the data and tags in TX FIFO. Since in write case, both tags and data + * need to be written and QUP write tags can have maximum 256 data length, so + * + * 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 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 + * then more data is pending otherwise following 3 cases can be possible + * a. if tx_fifo_data_pos is zero i.e. all the data bytes in this block + * have been written in TX FIFO so nothing else is required. + * b. tx_fifo_free is non zero i.e tx FIFO is free so copy the remaining data + * from tx_fifo_data to tx FIFO. Since, qup_i2c_write_blk_data do write + * in 4 bytes and FIFO space is in multiple of 4 bytes so tx_fifo_free + * will be always greater than or equal to 4 bytes. + * c. tx_fifo_free is zero. In this case, last few bytes (less than 4 + * bytes) are copied to tx_fifo_data but couldn't be sent because of + * FIFO full so make send_last_word true. + */ +static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup) +{ + struct qup_i2c_block *blk = &qup->blk; + + if (!blk->tx_tags_sent) { + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, + &blk->tx_tag_len); + blk->tx_tags_sent = true; + } + + if (blk->send_last_word) + goto send_last_word; + + qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len); + if (!blk->cur_blk_len) { + if (!blk->tx_fifo_data_pos) + return; + + if (blk->tx_fifo_free) + goto send_last_word; + + blk->send_last_word = true; + } + + return; + +send_last_word: + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE); +} + +/* + * Main transfer function which read or write i2c data. + * The QUP v2 supports reconfiguration during run in which multiple i2c sub + * transfers can be scheduled. + */ +static int +qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool is_first, + bool change_pause_state) +{ + struct qup_i2c_block *blk = &qup->blk; + struct i2c_msg *msg = qup->msg; + int ret; + + /* + * Check if its SMBus Block read for which the top level read will be + * done into 2 QUP reads. One with message length 1 while other one is + * with actual length. + */ + if (qup_i2c_check_msg_len(msg)) { + if (qup->is_smbus_read) { + /* + * If the message length is already read in + * the first byte of the buffer, account for + * that by setting the offset + */ + blk->cur_data += 1; + is_first = false; + } else { + change_pause_state = false; + } + } + + qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN; + + qup_i2c_clear_blk_v2(blk); + qup_i2c_conf_count_v2(qup); + + /* If it is first sub transfer, then configure i2c bus clocks */ + if (is_first) { + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + return ret; + + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); + + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + if (ret) + return ret; + } + + reinit_completion(&qup->xfer); + enable_irq(qup->irq); + /* + * In FIFO mode, tx FIFO can be written directly while in block mode the + * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt + */ + if (!blk->is_tx_blk_mode) { + blk->tx_fifo_free = qup->out_fifo_sz; + + if (is_rx) + qup_i2c_write_rx_tags_v2(qup); + else + qup_i2c_write_tx_fifo_v2(qup); + } + + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + goto err; + + ret = qup_i2c_wait_for_complete(qup, msg); + if (ret) + goto err; + + /* Move to pause state for all the transfers, except last one */ + if (change_pause_state) { + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + if (ret) + goto err; + } + +err: + disable_irq(qup->irq); + return ret; +} + +/* + * Transfer one read/write message in i2c transfer. It splits the message into + * multiple of blk_xfer_limit data length blocks and schedule each + * QUP block individually. + */ +static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx) +{ + int ret = 0; + unsigned int data_len, i; + struct i2c_msg *msg = qup->msg; + struct qup_i2c_block *blk = &qup->blk; + u8 *msg_buf = msg->buf; + + qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT; + qup_i2c_set_blk_data(qup, msg); + + for (i = 0; i < blk->count; i++) { + data_len = qup_i2c_get_data_len(qup); + blk->pos = i; + blk->cur_tx_tags = blk->tags; + blk->cur_blk_len = data_len; + blk->tx_tag_len = + qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg); + + blk->cur_data = msg_buf; + + if (is_rx) { + blk->total_tx_len = blk->tx_tag_len; + blk->rx_tag_len = 2; + blk->total_rx_len = blk->rx_tag_len + data_len; + } else { + blk->total_tx_len = blk->tx_tag_len + data_len; + blk->total_rx_len = 0; + } + + ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i, + !qup->is_last || i < blk->count - 1); + if (ret) + return ret; + + /* Handle SMBus block read length */ + if (qup_i2c_check_msg_len(msg) && msg->len == 1 && + !qup->is_smbus_read) { + if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + + msg->len = msg->buf[0]; + qup->is_smbus_read = true; + ret = qup_i2c_xfer_v2_msg(qup, msg_id, true); + qup->is_smbus_read = false; + if (ret) + return ret; + + msg->len += 1; + } + + msg_buf += data_len; + blk->data_len -= qup->blk_xfer_limit; + } + + return ret; +} + +/* + * QUP v2 supports 3 modes + * Programmed IO using FIFO mode : Less than FIFO size + * Programmed IO using Block mode : Greater than FIFO size + * DMA using BAM : Appropriate for any transaction size but the address should + * be DMA applicable + * + * This function determines the mode which will be used for this transfer. An + * i2c transfer contains multiple message. Following are the rules to determine + * the mode used. + * 1. Determine complete length, maximum tx and rx length for complete transfer. + * 2. If complete transfer length is greater than fifo size then use the DMA + * mode. + * 3. In FIFO or block mode, tx and rx can operate in different mode so check + * for maximum tx and rx length to determine mode. + */ +static int +qup_i2c_determine_mode_v2(struct qup_i2c_dev *qup, + struct i2c_msg msgs[], int num) +{ + int idx; + bool no_dma = false; + unsigned int max_tx_len = 0, max_rx_len = 0, total_len = 0; + + /* All i2c_msgs should be transferred using either dma or cpu */ + for (idx = 0; idx < num; idx++) { + if (msgs[idx].flags & I2C_M_RD) + max_rx_len = max_t(unsigned int, max_rx_len, + msgs[idx].len); + else + max_tx_len = max_t(unsigned int, max_tx_len, + msgs[idx].len); + + if (is_vmalloc_addr(msgs[idx].buf)) + no_dma = true; + + total_len += msgs[idx].len; + } + + if (!no_dma && qup->is_dma && + (total_len > qup->out_fifo_sz || total_len > qup->in_fifo_sz)) { + qup->use_dma = true; + } else { + qup->blk.is_tx_blk_mode = max_tx_len > qup->out_fifo_sz - + QUP_MAX_TAGS_LEN; + qup->blk.is_rx_blk_mode = max_rx_len > qup->in_fifo_sz - + READ_RX_TAGS_LEN; + } + + return 0; +} + +static int qup_i2c_xfer_v2(struct i2c_adapter *adap, + struct i2c_msg msgs[], + int num) +{ + struct qup_i2c_dev *qup = i2c_get_adapdata(adap); + int ret, idx = 0; + + qup->bus_err = 0; + qup->qup_err = 0; + + ret = pm_runtime_get_sync(qup->dev); + if (ret < 0) + goto out; + + ret = qup_i2c_determine_mode_v2(qup, msgs, num); + if (ret) + goto out; + + writel(1, qup->base + QUP_SW_RESET); + ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); + if (ret) + goto out; + + /* Configure QUP as I2C mini core */ + writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG); + writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN); + + if (qup_i2c_poll_state_i2c_master(qup)) { + ret = -EIO; + goto out; + } + + if (qup->use_dma) { + reinit_completion(&qup->xfer); + ret = qup_i2c_bam_xfer(adap, &msgs[0], num); + qup->use_dma = false; + } else { + qup_i2c_conf_mode_v2(qup); + + for (idx = 0; idx < num; idx++) { + qup->msg = &msgs[idx]; + qup->is_last = idx == (num - 1); + + ret = qup_i2c_xfer_v2_msg(qup, idx, + !!(msgs[idx].flags & I2C_M_RD)); + if (ret) + break; + } + qup->msg = NULL; + } + + if (!ret) + ret = qup_i2c_bus_active(qup, ONE_BYTE); + + if (!ret) + qup_i2c_change_state(qup, QUP_RESET_STATE); + + if (ret == 0) + ret = num; +out: + pm_runtime_put_autosuspend(qup->dev); + + return ret; +} + +static u32 qup_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL_ALL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm qup_i2c_algo = { + .xfer = qup_i2c_xfer, + .functionality = qup_i2c_func, +}; + +static const struct i2c_algorithm qup_i2c_algo_v2 = { + .xfer = qup_i2c_xfer_v2, + .functionality = qup_i2c_func, +}; + +/* + * The QUP block will issue a NACK and STOP on the bus when reaching + * the end of the read, the length of the read is specified as one byte + * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. + */ +static const struct i2c_adapter_quirks qup_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, + .max_read_len = QUP_READ_LIMIT, +}; + +static const struct i2c_adapter_quirks qup_i2c_quirks_v2 = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static int qup_i2c_enable_clocks(struct qup_i2c_dev *qup) +{ + int ret; + + ret = clk_prepare_enable(qup->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(qup->pclk); + if (ret) { + clk_disable_unprepare(qup->clk); + return ret; + } + + return 0; +} + +static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) +{ + u32 config; + + qup_i2c_change_state(qup, QUP_RESET_STATE); + clk_disable_unprepare(qup->clk); + 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); + +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; + u32 io_mode, hw_ver, size; + int ret, fs_div, hs_div; + u32 src_clk_freq = DEFAULT_SRC_CLK; + u32 clk_freq = DEFAULT_CLK_FREQ; + int blocks; + bool is_qup_v1; + + qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL); + if (!qup) + return -ENOMEM; + + qup->dev = &pdev->dev; + init_completion(&qup->xfer); + platform_set_drvdata(pdev, qup); + + if (scl_freq) { + dev_notice(qup->dev, "Using override frequency of %u\n", scl_freq); + clk_freq = scl_freq; + } else { + ret = device_property_read_u32(qup->dev, "clock-frequency", &clk_freq); + if (ret) { + dev_notice(qup->dev, "using default clock-frequency %d", + DEFAULT_CLK_FREQ); + } + } + + 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; + } else { + qup->adap.algo = &qup_i2c_algo_v2; + qup->adap.quirks = &qup_i2c_quirks_v2; + is_qup_v1 = false; + if (acpi_match_device(qup_i2c_acpi_match, qup->dev)) + goto nodma; + else + ret = qup_i2c_req_dma(qup); + + if (ret == -EPROBE_DEFER) + goto fail_dma; + else if (ret != 0) + goto nodma; + + qup->max_xfer_sg_len = (MX_BLOCKS << 1); + blocks = (MX_DMA_BLOCKS << 1) + 1; + qup->btx.sg = devm_kcalloc(&pdev->dev, + blocks, sizeof(*qup->btx.sg), + GFP_KERNEL); + if (!qup->btx.sg) { + ret = -ENOMEM; + goto fail_dma; + } + sg_init_table(qup->btx.sg, blocks); + + qup->brx.sg = devm_kcalloc(&pdev->dev, + blocks, sizeof(*qup->brx.sg), + GFP_KERNEL); + if (!qup->brx.sg) { + ret = -ENOMEM; + goto fail_dma; + } + sg_init_table(qup->brx.sg, blocks); + + /* 2 tag bytes for each block + 5 for start, stop tags */ + size = blocks * 2 + 5; + + qup->start_tag.start = devm_kzalloc(&pdev->dev, + size, GFP_KERNEL); + if (!qup->start_tag.start) { + ret = -ENOMEM; + goto fail_dma; + } + + qup->brx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL); + if (!qup->brx.tag.start) { + ret = -ENOMEM; + goto fail_dma; + } + + qup->btx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL); + if (!qup->btx.tag.start) { + ret = -ENOMEM; + 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_MAX_FAST_MODE_PLUS_FREQ) { + dev_err(qup->dev, "clock frequency not supported %d\n", + clk_freq); + ret = -EINVAL; + goto fail_dma; + } + + 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) { + ret = qup->irq; + goto fail_dma; + } + + if (has_acpi_companion(qup->dev)) { + ret = device_property_read_u32(qup->dev, + "src-clock-hz", &src_clk_freq); + if (ret) { + dev_notice(qup->dev, "using default src-clock-hz %d", + DEFAULT_SRC_CLK); + } + ACPI_COMPANION_SET(&qup->adap.dev, ACPI_COMPANION(qup->dev)); + } else { + qup->clk = devm_clk_get(qup->dev, "core"); + if (IS_ERR(qup->clk)) { + dev_err(qup->dev, "Could not get core clock\n"); + 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"); + ret = PTR_ERR(qup->pclk); + goto fail_dma; + } + ret = qup_i2c_enable_clocks(qup); + if (ret) + goto fail_dma; + 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, + * so we reset the core before registering for interrupts. + */ + writel(1, qup->base + QUP_SW_RESET); + ret = qup_i2c_poll_state_valid(qup); + if (ret) + goto fail; + + ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "i2c_qup", qup); + if (ret) { + dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq); + goto fail; + } + + hw_ver = readl(qup->base + QUP_HW_VERSION); + dev_dbg(qup->dev, "Revision %x\n", hw_ver); + + io_mode = readl(qup->base + QUP_IO_MODE); + + /* + * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' + * associated with each byte written/received + */ + size = QUP_OUTPUT_BLOCK_SIZE(io_mode); + if (size >= ARRAY_SIZE(blk_sizes)) { + ret = -EIO; + goto fail; + } + qup->out_blk_sz = blk_sizes[size]; + + size = QUP_INPUT_BLOCK_SIZE(io_mode); + if (size >= ARRAY_SIZE(blk_sizes)) { + ret = -EIO; + goto fail; + } + qup->in_blk_sz = blk_sizes[size]; + + if (is_qup_v1) { + /* + * in QUP v1, QUP_CONFIG uses N as 15 i.e 16 bits constitutes a + * single transfer but the block size is in bytes so divide the + * in_blk_sz and out_blk_sz by 2 + */ + qup->in_blk_sz /= 2; + qup->out_blk_sz /= 2; + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1; + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1; + qup->write_rx_tags = qup_i2c_write_rx_tags_v1; + } else { + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v2; + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v2; + qup->write_rx_tags = qup_i2c_write_rx_tags_v2; + } + + size = QUP_OUTPUT_FIFO_SIZE(io_mode); + qup->out_fifo_sz = qup->out_blk_sz * (2 << size); + + size = QUP_INPUT_FIFO_SIZE(io_mode); + qup->in_fifo_sz = qup->in_blk_sz * (2 << size); + + hs_div = 3; + 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 { + /* 33%/66% duty cycle */ + fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3; + qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff); + } + + /* + * Time it takes for a byte to be clocked out on the bus. + * Each byte takes 9 clock cycles (8 bits + 1 ack). + */ + one_bit_t = (USEC_PER_SEC / clk_freq) + 1; + qup->one_byte_t = one_bit_t * 9; + qup->xfer_timeout = TOUT_MIN * HZ + + usecs_to_jiffies(MX_DMA_TX_RX_LEN * qup->one_byte_t); + + dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", + qup->in_blk_sz, qup->in_fifo_sz, + qup->out_blk_sz, qup->out_fifo_sz); + + i2c_set_adapdata(&qup->adap, qup); + qup->adap.dev.parent = qup->dev; + qup->adap.dev.of_node = pdev->dev.of_node; + qup->is_last = true; + + 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); + pm_runtime_set_active(qup->dev); + pm_runtime_enable(qup->dev); + + ret = i2c_add_adapter(&qup->adap); + if (ret) + goto fail_runtime; + + return 0; + +fail_runtime: + pm_runtime_disable(qup->dev); + pm_runtime_set_suspended(qup->dev); +fail: + qup_i2c_disable_clocks(qup); +fail_dma: + if (qup->btx.dma) + dma_release_channel(qup->btx.dma); + if (qup->brx.dma) + dma_release_channel(qup->brx.dma); + return ret; +} + +static void qup_i2c_remove(struct platform_device *pdev) +{ + struct qup_i2c_dev *qup = platform_get_drvdata(pdev); + + if (qup->is_dma) { + dma_release_channel(qup->btx.dma); + dma_release_channel(qup->brx.dma); + } + + disable_irq(qup->irq); + qup_i2c_disable_clocks(qup); + i2c_del_adapter(&qup->adap); + pm_runtime_disable(qup->dev); + pm_runtime_set_suspended(qup->dev); +} + +static int qup_i2c_pm_suspend_runtime(struct device *device) +{ + struct qup_i2c_dev *qup = dev_get_drvdata(device); + + dev_dbg(device, "pm_runtime: suspending...\n"); + qup_i2c_disable_clocks(qup); + return 0; +} + +static int qup_i2c_pm_resume_runtime(struct device *device) +{ + struct qup_i2c_dev *qup = dev_get_drvdata(device); + + dev_dbg(device, "pm_runtime: resuming...\n"); + return qup_i2c_enable_clocks(qup); +} + +static int qup_i2c_suspend(struct device *device) +{ + if (!pm_runtime_suspended(device)) + return qup_i2c_pm_suspend_runtime(device); + return 0; +} + +static int qup_i2c_resume(struct device *device) +{ + int ret; + + ret = qup_i2c_pm_resume_runtime(device); + if (ret) + return ret; + + pm_request_autosuspend(device); + return 0; +} + +static const struct dev_pm_ops qup_i2c_qup_pm_ops = { + 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[] = { + { .compatible = "qcom,i2c-qup-v1.1.1" }, + { .compatible = "qcom,i2c-qup-v2.1.1" }, + { .compatible = "qcom,i2c-qup-v2.2.1" }, + {} +}; +MODULE_DEVICE_TABLE(of, qup_i2c_dt_match); + +static struct platform_driver qup_i2c_driver = { + .probe = qup_i2c_probe, + .remove = qup_i2c_remove, + .driver = { + .name = "i2c_qup", + .pm = pm_ptr(&qup_i2c_qup_pm_ops), + .of_match_table = qup_i2c_dt_match, + .acpi_match_table = ACPI_PTR(qup_i2c_acpi_match), + }, +}; + +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 new file mode 100644 index 000000000..755064804 --- /dev/null +++ b/drivers/i2c/busses/i2c-rcar.c @@ -0,0 +1,1300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Renesas R-Car I2C unit + * + * Copyright (C) 2014-19 Wolfram Sang + * Copyright (C) 2011-2019 Renesas Electronics Corporation + * + * Copyright (C) 2012-14 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This file is based on the drivers/i2c/busses/i2c-sh7760.c + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offsets */ +#define ICSCR 0x00 /* slave ctrl */ +#define ICMCR 0x04 /* master ctrl */ +#define ICSSR 0x08 /* slave status */ +#define ICMSR 0x0C /* master status */ +#define ICSIER 0x10 /* slave irq enable */ +#define ICMIER 0x14 /* master irq enable */ +#define ICCCR 0x18 /* clock dividers */ +#define ICSAR 0x1C /* slave address */ +#define ICMAR 0x20 /* master address */ +#define ICRXTX 0x24 /* data port */ +#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 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 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 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 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 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 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_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 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_NO_RST_STAT BIT(26) +#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, 26) + +#define ID_SLAVE_NACK BIT(0) + +enum rcar_i2c_type { + I2C_RCAR_GEN1, + I2C_RCAR_GEN2, + I2C_RCAR_GEN3, + I2C_RCAR_GEN4, + I2C_RCAR_GEN5, +}; + +struct rcar_i2c_priv { + u32 flags; + void __iomem *io; + struct i2c_adapter adap; + struct i2c_msg *msg; + int msgs_left; + struct clk *clk; + + wait_queue_head_t wait; + + int pos; + u32 icccr; + u16 schd; + u16 scld; + u8 smd; + u8 recovery_icmcr; /* protected by adapter lock */ + enum rcar_i2c_type devtype; + struct i2c_client *slave; + + struct resource *res; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; + struct scatterlist sg; + 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) + +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val) +{ + writel(val, priv->io + reg); +} + +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) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + if (val) + priv->recovery_icmcr |= FSCL; + else + priv->recovery_icmcr &= ~FSCL; + + rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr); +} + +static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + if (val) + priv->recovery_icmcr |= FSDA; + else + 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, + .set_scl = rcar_i2c_set_scl, + .set_sda = rcar_i2c_set_sda, + .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 */ + rcar_i2c_write(priv, ICMIER, 0); + rcar_i2c_write(priv, ICMCR, MDBS); + rcar_i2c_write(priv, ICMSR, 0); + /* start clock */ + 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 ret; + u32 val; + + 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); + } + + return ret; +} + +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) +{ + 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 */ + i2c_parse_fw_timings(dev, &t, false); + priv->smd = RCAR_DEFAULT_SMD; + + /* + * calculate SCL clock + * see + * 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 + * intd : LSI internal delay + * clkp : peripheral_clk + * F[] : integer up-valuation + */ + rate = clk_get_rate(priv->clk); + cdf = rate / 20000000; + 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 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 = DIV_ROUND_CLOSEST(ick, 1000000); + round = DIV_ROUND_CLOSEST(round * sum, 1000); + + 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); + + if (x == 0 || x * RCAR_SCLD_RATIO > 0xffff) + goto err_no_val; + + 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)); + 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); +} + +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--; + rcar_i2c_prepare_msg(priv); + /* ICMSR handling must come afterwards in the irq handler */ +} + +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; + + /* 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 && + priv->dma_direction == DMA_FROM_DEVICE) + priv->flags |= ID_P_NO_RXDMA; + + priv->dma_direction = DMA_NONE; + + /* Disable DMA Master Received/Transmitted, must be last! */ + rcar_i2c_write(priv, ICDMAER, 0); +} + +static void rcar_i2c_dma_callback(void *data) +{ + struct rcar_i2c_priv *priv = data; + + priv->pos += sg_dma_len(&priv->sg); + + rcar_i2c_cleanup_dma(priv, false); +} + +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; + bool read = msg->flags & I2C_M_RD; + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct dma_chan *chan = read ? priv->dma_rx : priv->dma_tx; + struct dma_async_tx_descriptor *txdesc; + dma_addr_t dma_addr; + dma_cookie_t cookie; + unsigned char *buf; + int len; + + /* Do various checks to see if DMA is feasible at all */ + 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) { + /* + * The last two bytes needs to be fetched using PIO in + * order for the STOP phase to work. + */ + buf = priv->msg->buf; + len = priv->msg->len - 2; + } else { + /* + * First byte in message was sent using PIO. + */ + buf = priv->msg->buf + 1; + len = priv->msg->len - 1; + } + + 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 false; + } + + sg_dma_len(&priv->sg) = len; + sg_dma_address(&priv->sg) = dma_addr; + + priv->dma_direction = dir; + + txdesc = dmaengine_prep_slave_sg(chan, &priv->sg, 1, + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_dbg(dev, "dma prep slave sg failed, using PIO\n"); + rcar_i2c_cleanup_dma(priv, false); + return false; + } + + txdesc->callback = rcar_i2c_dma_callback; + txdesc->callback_param = priv; + + cookie = dmaengine_submit(txdesc); + if (dma_submit_error(cookie)) { + dev_dbg(dev, "submitting dma failed, using PIO\n"); + rcar_i2c_cleanup_dma(priv, false); + return false; + } + + /* Enable DMA Master Received/Transmitted */ + if (read) + rcar_i2c_write(priv, ICDMAER, RMDMAE); + else + 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 (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) { + /* + * Prepare next data to ICRXTX register. + * This data will go to _SHIFT_ register. + * + * * + * [ICRXTX] -> [SHIFT] -> [I2C bus] + */ + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]); + priv->pos++; + } else { + /* + * The last data was pushed to ICRXTX on _PREV_ empty irq. + * It is on _SHIFT_ register, and will sent to I2C bus. + * + * * + * [ICRXTX] -> [SHIFT] -> [I2C bus] + */ + + 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 + rcar_i2c_next_msg(priv); + } + + 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. + */ + rcar_i2c_dma(priv); + } else if (priv->pos < msg->len) { + /* get received data */ + 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_ 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_REP_AFTER_RD; + } + } + + if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG)) + rcar_i2c_next_msg(priv); + + 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); + + if (!ssr_filtered) + return false; + + /* address detected */ + if (ssr_filtered & SAR) { + /* read or write request */ + if (ssr_raw & STM) { + i2c_slave_event(priv->slave, I2C_SLAVE_READ_REQUESTED, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR); + } else { + 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); + } + + /* 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, 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) { + value = rcar_i2c_read(priv, ICRXTX); + ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value); + 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); + } + + /* master wants to read from us */ + if (ssr_filtered & SDE) { + i2c_slave_event(priv->slave, I2C_SLAVE_READ_PROCESSED, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSSR, ~SDE & 0xff); + } + + return true; +} + +/* + * 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) +{ + if (!msr) { + if (rcar_i2c_slave_irq(priv)) + return IRQ_HANDLED; + + return IRQ_NONE; + } + + /* Arbitration lost */ + if (msr & MAL) { + priv->flags |= ID_DONE | ID_ARBLOST; + goto out; + } + + /* Nack */ + if (msr & MNR) { + /* HW automatically sends STOP after received NACK */ + if (priv->flags & ID_P_NOT_ATOMIC) + rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); + priv->flags |= ID_NACK; + goto out; + } + + /* Stop */ + if (msr & MST) { + priv->msgs_left--; /* The last message also made it */ + priv->flags |= ID_DONE; + goto out; + } + + if (rcar_i2c_is_recv(priv)) + rcar_i2c_irq_recv(priv, msr); + else + rcar_i2c_irq_send(priv, msr); + +out: + if (priv->flags & ID_DONE) { + rcar_i2c_write(priv, ICMIER, 0); + rcar_i2c_write(priv, ICMSR, 0); + 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) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + 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)); + return chan; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", + chan_name, ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return chan; +} + +static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv, + struct i2c_msg *msg) +{ + struct device *dev = rcar_i2c_priv_to_dev(priv); + bool read; + struct dma_chan *chan; + enum dma_transfer_direction dir; + + read = msg->flags & I2C_M_RD; + + chan = read ? priv->dma_rx : priv->dma_tx; + if (PTR_ERR(chan) != -EPROBE_DEFER) + return; + + dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + chan = rcar_i2c_request_dma_chan(dev, dir, priv->res->start + ICRXTX); + + if (read) + priv->dma_rx = chan; + else + priv->dma_tx = chan; +} + +static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv) +{ + if (!IS_ERR(priv->dma_tx)) { + dma_release_channel(priv->dma_tx); + priv->dma_tx = ERR_PTR(-EPROBE_DEFER); + } + + if (!IS_ERR(priv->dma_rx)) { + dma_release_channel(priv->dma_rx); + priv->dma_rx = ERR_PTR(-EPROBE_DEFER); + } +} + +/* 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 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; + + if (priv->flags & ID_P_NO_RST_STAT) + return 0; + + 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, + struct i2c_msg *msgs, + int num) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + struct device *dev = rcar_i2c_priv_to_dev(priv); + 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 */ + ret = rcar_i2c_bus_barrier(priv); + if (ret < 0) + goto out; + + /* 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); + + for (i = 0; i < num; i++) + rcar_i2c_request_dma(priv, msgs + i); + + 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, true); + + 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 */ + } +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 */ + } +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_reg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + if (priv->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + /* Keep device active for slave address detection logic */ + pm_runtime_get_sync(rcar_i2c_priv_to_dev(priv)); + + priv->slave = slave; + rcar_i2c_write(priv, ICSAR, slave->addr); + rcar_i2c_write(priv, ICSSR, 0); + rcar_i2c_write(priv, ICSIER, SAR); + rcar_i2c_write(priv, ICSCR, SIE | SDBS); + + return 0; +} + +static int rcar_unreg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + WARN_ON(!priv->slave); + + /* ensure no irq is running before clearing ptr */ + disable_irq(priv->irq); + rcar_i2c_reset_slave(priv); + enable_irq(priv->irq); + + priv->slave = NULL; + + pm_runtime_put(rcar_i2c_priv_to_dev(priv)); + + return 0; +} + +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) + */ + 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 = { + .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 = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static const struct of_device_id rcar_i2c_dt_ids[] = { + { .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 }, + { .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 }, + { .compatible = "renesas,i2c-r8a7790", .data = (void *)I2C_RCAR_GEN2 }, + { .compatible = "renesas,i2c-r8a7791", .data = (void *)I2C_RCAR_GEN2 }, + { .compatible = "renesas,i2c-r8a7792", .data = (void *)I2C_RCAR_GEN2 }, + { .compatible = "renesas,i2c-r8a7793", .data = (void *)I2C_RCAR_GEN2 }, + { .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 }, + /* 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 }, + { .compatible = "renesas,rcar-gen5-i2c", .data = (void *)I2C_RCAR_GEN5 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids); + +static int rcar_i2c_probe(struct platform_device *pdev) +{ + struct rcar_i2c_priv *priv; + struct i2c_adapter *adap; + struct device *dev = &pdev->dev; + 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) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "cannot get clock\n"); + return PTR_ERR(priv->clk); + } + + priv->io = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->res); + if (IS_ERR(priv->io)) + return PTR_ERR(priv->io); + + priv->devtype = (kernel_ulong_t)of_device_get_match_data(dev); + init_waitqueue_head(&priv->wait); + + adap = &priv->adap; + adap->nr = pdev->id; + adap->algo = &rcar_i2c_algo; + adap->class = I2C_CLASS_DEPRECATED; + adap->retries = 3; + adap->dev.parent = dev; + adap->dev.of_node = dev->of_node; + adap->bus_recovery_info = &rcar_i2c_bri; + adap->quirks = &rcar_i2c_quirks; + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name, sizeof(adap->name)); + + /* Init DMA */ + sg_init_table(&priv->sg, 1); + priv->dma_direction = DMA_NONE; + priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER); + + /* Activate device for clock calculation */ + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + 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); + /* Some SCMI firmware does not support reading reset status */ + if (ret == -ENOTSUPP) + priv->flags |= ID_P_NO_RST_STAT; + else if (ret < 0) + goto out_pm_put; + + /* hard reset disturbs HostNotify local target, so disable it */ + priv->flags &= ~ID_P_HOST_NOTIFY; + } + + 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", priv->irq); + goto out_pm_put; + } + + platform_set_drvdata(pdev, priv); + + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) + 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: + if (priv->flags & ID_P_PM_BLOCKED) + pm_runtime_put(dev); + out_pm_disable: + pm_runtime_disable(dev); + return ret; +} + +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, +}; + +module_platform_driver(rcar_i2c_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c new file mode 100644 index 000000000..2fa48219b --- /dev/null +++ b/drivers/i2c/busses/i2c-riic.c @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RIIC driver + * + * Copyright (C) 2013 Wolfram Sang + * Copyright (C) 2013 Renesas Solutions Corp. + */ + +/* + * This i2c core has a lot of interrupts, namely 8. We use their chaining as + * some kind of state machine. + * + * 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 target address + RW bit in every case. + * + * 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 + * the message as completed (includes sending STOP) there. + * + * 3b) Read case: We switch over to receive interrupt (RIE). One dummy read is + * needed to start clocking, then we keep receiving until we are done. Note + * that we use the RDRFS mode all the time, i.e. we ACK/NACK every byte by + * writing to the ACKBT bit. I tried using the RDRFS mode only at the end of a + * message to create the final NACK as sketched in the datasheet. This caused + * some subtle races (when byte n was processed and byte n+1 was already + * waiting), though, and I started with the safe approach. + * + * 4) If we got a NACK somewhere, we flag the error and stop the transmission + * via NAKIE. + * + * Also check the comments in the interrupt routines for some gory details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 BIT(5) +#define ICMR3_ACKWP BIT(4) +#define ICMR3_ACKBT BIT(3) + +#define ICFER_FMPE BIT(7) + +#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 BIT(4) +#define ICSR2_STOP BIT(3) + +#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; + struct i2c_msg *msg; + 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 { + int res_num; + irq_handler_t isr; + 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) +{ + 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 = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + riic->err = riic_bus_barrier(riic); + if (riic->err) + goto out; + + reinit_completion(&riic->msg_done); + + riic_writeb(riic, 0, RIIC_ICSR2); + + for (i = 0, start_bit = ICCR2_ST; i < num; i++) { + riic->bytes_left = RIIC_INIT_MSG; + riic->buf = msgs[i].buf; + riic->msg = &msgs[i]; + riic->is_last = (i == num - 1); + + riic_writeb(riic, ICIER_NAKIE | ICIER_TIE, RIIC_ICIER); + + riic_writeb(riic, start_bit, RIIC_ICCR2); + + time_left = wait_for_completion_timeout(&riic->msg_done, riic->adapter.timeout); + if (time_left == 0) + riic->err = -ETIMEDOUT; + + if (riic->err) + break; + + start_bit = ICCR2_RS; + } + + out: + pm_runtime_put_autosuspend(dev); + + return riic->err ?: num; +} + +static irqreturn_t riic_tdre_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + u8 val; + + if (!riic->bytes_left) + return IRQ_NONE; + + if (riic->bytes_left == RIIC_INIT_MSG) { + if (riic->msg->flags & I2C_M_RD) + /* On read, switch over to receive interrupt */ + riic_clear_set_bit(riic, ICIER_TIE, ICIER_RIE, RIIC_ICIER); + else + /* On write, initialize length */ + riic->bytes_left = riic->msg->len; + + val = i2c_8bit_addr_from_msg(riic->msg); + } else { + val = *riic->buf; + riic->buf++; + riic->bytes_left--; + } + + /* + * Switch to transmission ended interrupt when done. Do check here + * after bytes_left was initialized to support SMBUS_QUICK (new msg has + * 0 length then) + */ + if (riic->bytes_left == 0) + riic_clear_set_bit(riic, ICIER_TIE, ICIER_TEIE, RIIC_ICIER); + + /* + * This acks the TIE interrupt. We get another TIE immediately if our + * 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)! + */ + riic_writeb(riic, val, RIIC_ICDRT); + + return IRQ_HANDLED; +} + +static irqreturn_t riic_tend_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + + if (riic_readb(riic, RIIC_ICSR2) & ICSR2_NACKF) { + /* We got a NACKIE */ + 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; + } + + if (riic->is_last || riic->err) { + riic_clear_set_bit(riic, ICIER_TEIE, ICIER_SPIE, RIIC_ICIER); + 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); + complete(&riic->msg_done); + } + + return IRQ_HANDLED; +} + +static irqreturn_t riic_rdrf_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + + if (!riic->bytes_left) + return IRQ_NONE; + + if (riic->bytes_left == RIIC_INIT_MSG) { + riic->bytes_left = riic->msg->len; + riic_readb(riic, RIIC_ICDRR); /* dummy read */ + return IRQ_HANDLED; + } + + if (riic->bytes_left == 1) { + /* STOP must come before we set ACKBT! */ + if (riic->is_last) { + riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); + riic_writeb(riic, ICCR2_SP, RIIC_ICCR2); + } + + riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3); + + } else { + riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3); + } + + /* Reading acks the RIE interrupt */ + *riic->buf = riic_readb(riic, RIIC_ICDRR); + riic->buf++; + riic->bytes_left--; + + return IRQ_HANDLED; +} + +static irqreturn_t riic_stop_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + + /* read back registers to confirm writes have fully propagated */ + 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 = { + .xfer = riic_xfer, + .functionality = riic_func, +}; + +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; + + 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); + + /* + * Assume the default register settings: + * FER.SCLE = 1 (SCL sync circuit enabled, adds 2 or 3 cycles) + * FER.NFE = 1 (noise circuit enabled) + * MR3.NF = 0 (1 cycle of noise filtered out) + * + * Freq (CKS=000) = (I2CCLK + tr + tf)/ (BRH + 3 + 1) + (BRL + 3 + 1) + * Freq (CKS!=000) = (I2CCLK + tr + tf)/ (BRH + 2 + 1) + (BRL + 2 + 1) + */ + + /* + * Determine reference clock rate. We must be able to get the desired + * 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 ?: 1); + + for (cks = 0; cks <= 7; cks++) { + /* + * 60% low time must be less than BRL + 2 + 1 + * BRL max register value is 0x1F. + */ + brl = ((total_ticks * 6) / 10); + if (brl <= (0x1F + 3)) + break; + + total_ticks = DIV_ROUND_UP(total_ticks, 2); + rate /= 2; + } + + if (brl > (0x1F + 3)) + return dev_err_probe(dev, -EINVAL, "invalid speed (%uHz). Too slow.\n", + t->bus_freq_hz); + + brh = total_ticks - brl; + + /* Remove automatic clock ticks for sync circuit and NF */ + if (cks == 0) { + brl -= 4; + brh -= 4; + } else { + brl -= 3; + brh -= 3; + } + + /* + * Remove clock ticks for rise and fall times. Convert ns to clock + * ticks. + */ + 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) + brl = 1; + if (brh < 1) + brh = 1; + + 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 / 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! */ + riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1); + riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1); + + riic_writeb(riic, ICMR1_CKS(cks), RIIC_ICMR1); + riic_writeb(riic, brh | ICBR_RESERVED, RIIC_ICBRH); + riic_writeb(riic, brl | ICBR_RESERVED, RIIC_ICBRL); + + riic_writeb(riic, 0, RIIC_ICSER); + riic_writeb(riic, ICMR3_ACKWP | ICMR3_RDRFS, RIIC_ICMR3); + + if (fast_mode_plus && t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) + riic_clear_set_bit(riic, 0, ICFER_FMPE, RIIC_ICFER); + + riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1); + + pm_runtime_put_autosuspend(dev); + return 0; +} + +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" }, + { .res_num = 3, .isr = riic_stop_isr, .name = "riic-stop" }, + { .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; + int i, ret; + + riic = devm_kzalloc(dev, sizeof(*riic), GFP_KERNEL); + if (!riic) + return -ENOMEM; + + riic->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(riic->base)) + return PTR_ERR(riic->base); + + riic->clk = devm_clk_get(dev, NULL); + if (IS_ERR(riic->clk)) + return dev_err_probe(dev, PTR_ERR(riic->clk), + "missing controller clock"); + + 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"); + + 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 ret; + } + + + adap = &riic->adapter; + i2c_set_adapdata(adap, riic); + strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &riic_algo; + 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(dev, &riic->i2c_t, true); + + /* 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) + goto out; + + platform_set_drvdata(pdev, riic); + + 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 void riic_i2c_remove(struct platform_device *pdev) +{ + struct riic_dev *riic = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + 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) +{ + /* + * 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 riic_i2c_resume(struct device *dev) +{ + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int riic_i2c_suspend_noirq(struct device *dev) +{ + struct riic_dev *riic = dev_get_drvdata(dev); + + 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_force_suspend(dev); + + return reset_control_assert(riic->rstc); +} + +static int riic_i2c_resume_noirq(struct device *dev) +{ + struct riic_dev *riic = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(riic->rstc); + if (ret) + return ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + reset_control_assert(riic->rstc); + 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 = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend_noirq, riic_i2c_resume_noirq) + SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume) +}; + +static const struct of_device_id riic_i2c_dt_ids[] = { + { .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 = { + .probe = riic_i2c_probe, + .remove = riic_i2c_remove, + .driver = { + .name = "i2c-riic", + .of_match_table = riic_i2c_dt_ids, + .pm = pm_ptr(&riic_i2c_pm_ops), + }, +}; + +module_platform_driver(riic_i2c_driver); + +MODULE_DESCRIPTION("Renesas RIIC adapter"); +MODULE_AUTHOR("Wolfram Sang "); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, riic_i2c_dt_ids); diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c new file mode 100644 index 000000000..fcede9f6e --- /dev/null +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -0,0 +1,1413 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for I2C adapter in Rockchip RK3xxx SoC + * + * Max Schwarz + * based on the patches by Rockchip Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Register Map */ +#define REG_CON 0x00 /* control register */ +#define REG_CLKDIV 0x04 /* clock divisor register */ +#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 */ +#define REG_IPD 0x1c /* interrupt pending */ +#define REG_FCNT 0x20 /* finished count */ + +/* Data buffer offsets */ +#define TXBUFFER_BASE 0x100 +#define RXBUFFER_BASE 0x200 + +/* REG_CON bits */ +#define REG_CON_EN BIT(0) +enum { + REG_CON_MOD_TX = 0, /* transmit data */ + REG_CON_MOD_REGISTER_TX, /* select register and restart */ + REG_CON_MOD_RX, /* receive data */ + REG_CON_MOD_REGISTER_RX, /* broken: transmits read addr AND writes + * register addr */ +}; +#define REG_CON_MOD(mod) ((mod) << 1) +#define REG_CON_MOD_MASK (BIT(1) | BIT(2)) +#define REG_CON_START BIT(3) +#define REG_CON_STOP BIT(4) +#define REG_CON_LASTACK BIT(5) /* 1: send NACK after last received byte */ +#define REG_CON_ACTACK BIT(6) /* 1: stop if NACK is received */ + +#define REG_CON_TUNING_MASK GENMASK_ULL(15, 8) + +#define REG_CON_SDA_CFG(cfg) ((cfg) << 8) +#define REG_CON_STA_CFG(cfg) ((cfg) << 12) +#define REG_CON_STO_CFG(cfg) ((cfg) << 14) + +/* REG_MRXADDR bits */ +#define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */ + +/* 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) /* 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 */ +#define REG_INT_ALL 0x7f + +/* Constants */ +#define WAIT_TIMEOUT 1000 /* ms */ +#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ + +/** + * 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 + * @min_setup_start_ns: min set-up time for a repeated START conditio + * @max_data_hold_ns: max data hold time + * @min_data_setup_ns: min data set-up time + * @min_setup_stop_ns: min set-up time for STOP condition + * @min_hold_buffer_ns: min bus free time between a STOP and + * START condition + */ +struct i2c_spec_values { + unsigned long min_hold_start_ns; + unsigned long min_low_ns; + unsigned long min_high_ns; + unsigned long min_setup_start_ns; + unsigned long max_data_hold_ns; + unsigned long min_data_setup_ns; + unsigned long min_setup_stop_ns; + unsigned long min_hold_buffer_ns; +}; + +static const struct i2c_spec_values standard_mode_spec = { + .min_hold_start_ns = 4000, + .min_low_ns = 4700, + .min_high_ns = 4000, + .min_setup_start_ns = 4700, + .max_data_hold_ns = 3450, + .min_data_setup_ns = 250, + .min_setup_stop_ns = 4000, + .min_hold_buffer_ns = 4700, +}; + +static const struct i2c_spec_values fast_mode_spec = { + .min_hold_start_ns = 600, + .min_low_ns = 1300, + .min_high_ns = 600, + .min_setup_start_ns = 600, + .max_data_hold_ns = 900, + .min_data_setup_ns = 100, + .min_setup_stop_ns = 600, + .min_hold_buffer_ns = 1300, +}; + +static const struct i2c_spec_values fast_mode_plus_spec = { + .min_hold_start_ns = 260, + .min_low_ns = 500, + .min_high_ns = 260, + .min_setup_start_ns = 260, + .max_data_hold_ns = 400, + .min_data_setup_ns = 50, + .min_setup_stop_ns = 260, + .min_hold_buffer_ns = 500, +}; + +/** + * 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, + * setup/hold start time and setup stop time for + * v1's calc_timings, the tuning should all be 0 + * for old hardware anyone using v0's calc_timings. + */ +struct rk3x_i2c_calced_timings { + unsigned long div_low; + unsigned long div_high; + unsigned int tuning; +}; + +enum rk3x_i2c_state { + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_STOP +}; + +/** + * 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 + */ +struct rk3x_i2c_soc_data { + int grf_offset; + int (*calc_timings)(unsigned long, struct i2c_timings *, + struct rk3x_i2c_calced_timings *); +}; + +/** + * struct rk3x_i2c - private data of the controller + * @adap: corresponding I2C adapter + * @dev: device for this controller + * @soc_data: related soc data struct + * @regs: virtual memory area + * @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 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 + * @processed: byte length which has been send or received + * @error: error code for i2c transfer + */ +struct rk3x_i2c { + struct i2c_adapter adap; + struct device *dev; + const struct rk3x_i2c_soc_data *soc_data; + + /* Hardware resources */ + void __iomem *regs; + struct clk *clk; + struct clk *pclk; + struct notifier_block clk_rate_nb; + int irq; + + /* Settings */ + struct i2c_timings t; + + /* Synchronization & notification */ + spinlock_t lock; + wait_queue_head_t wait; + bool busy; + + /* Current message */ + struct i2c_msg *msg; + u8 addr; + unsigned int mode; + bool is_last_msg; + + /* I2C state machine */ + enum rk3x_i2c_state state; + unsigned int processed; + int error; +}; + +static inline void i2c_writel(struct rk3x_i2c *i2c, u32 value, + unsigned int offset) +{ + writel(value, i2c->regs + offset); +} + +static inline u32 i2c_readl(struct rk3x_i2c *i2c, unsigned int offset) +{ + return readl(i2c->regs + offset); +} + +/* Reset all interrupt pending bits */ +static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c) +{ + i2c_writel(i2c, REG_INT_ALL, REG_IPD); +} + +/** + * 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) +{ + u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + + i2c_writel(i2c, REG_INT_START, REG_IEN); + + /* enable adapter with correct mode, send START condition */ + val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; + + /* if we want to react to NACK, set ACTACK bit */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) + val |= REG_CON_ACTACK; + + i2c_writel(i2c, val, REG_CON); +} + +/** + * 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) +{ + unsigned int ctrl; + + i2c->processed = 0; + i2c->msg = NULL; + i2c->error = error; + + if (i2c->is_last_msg) { + /* Enable stop interrupt */ + i2c_writel(i2c, REG_INT_STOP, REG_IEN); + + i2c->state = STATE_STOP; + + ctrl = i2c_readl(i2c, REG_CON); + ctrl |= REG_CON_STOP; + i2c_writel(i2c, ctrl, REG_CON); + } else { + /* Signal rk3x_i2c_xfer to start the next message. */ + i2c->busy = false; + i2c->state = STATE_IDLE; + + /* + * The HW is actually not capable of REPEATED START. But we can + * get the intended effect by resetting its internal state + * and issuing an ordinary START. + */ + ctrl = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + i2c_writel(i2c, ctrl, REG_CON); + + /* signal that we are finished with the current msg */ + wake_up(&i2c->wait); + } +} + +/** + * 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) +{ + unsigned int len = i2c->msg->len - i2c->processed; + u32 con; + + con = i2c_readl(i2c, REG_CON); + + /* + * The hw can read up to 32 bytes at a time. If we need more than one + * chunk, send an ACK after the last byte of the current chunk. + */ + if (len > 32) { + len = 32; + con &= ~REG_CON_LASTACK; + } else { + con |= REG_CON_LASTACK; + } + + /* make sure we are in plain RX mode if we read a second chunk */ + if (i2c->processed != 0) { + con &= ~REG_CON_MOD_MASK; + con |= REG_CON_MOD(REG_CON_MOD_RX); + } + + i2c_writel(i2c, con, REG_CON); + i2c_writel(i2c, len, REG_MRXCNT); +} + +/** + * 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) +{ + unsigned int i, j; + u32 cnt = 0; + u32 val; + u8 byte; + + for (i = 0; i < 8; ++i) { + val = 0; + for (j = 0; j < 4; ++j) { + if ((i2c->processed == i2c->msg->len) && (cnt != 0)) + break; + + if (i2c->processed == 0 && cnt == 0) + byte = (i2c->addr & 0x7f) << 1; + else + byte = i2c->msg->buf[i2c->processed++]; + + val |= byte << (j * 8); + cnt++; + } + + i2c_writel(i2c, val, TXBUFFER_BASE + 4 * i); + + if (i2c->processed == i2c->msg->len) + break; + } + + i2c_writel(i2c, cnt, REG_MTXCNT); +} + + +/* IRQ handlers for individual states */ + +static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, unsigned int ipd) +{ + if (!(ipd & REG_INT_START)) { + rk3x_i2c_stop(i2c, -EIO); + dev_warn(i2c->dev, "unexpected irq in START: 0x%x\n", ipd); + rk3x_i2c_clean_ipd(i2c); + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_START, REG_IPD); + + /* disable start bit */ + i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON); + + /* enable appropriate interrupts and transition */ + if (i2c->mode == REG_CON_MOD_TX) { + i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_WRITE; + rk3x_i2c_fill_transmit_buf(i2c); + } else { + /* in any other case, we are going to be reading. */ + i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_READ; + rk3x_i2c_prepare_read(i2c); + } +} + +static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd) +{ + if (!(ipd & REG_INT_MBTF)) { + rk3x_i2c_stop(i2c, -EIO); + dev_err(i2c->dev, "unexpected irq in WRITE: 0x%x\n", ipd); + rk3x_i2c_clean_ipd(i2c); + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_MBTF, REG_IPD); + + /* are we finished? */ + if (i2c->processed == i2c->msg->len) + rk3x_i2c_stop(i2c, i2c->error); + else + rk3x_i2c_fill_transmit_buf(i2c); +} + +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 val; + u8 byte; + + /* we only care for MBRF here. */ + if (!(ipd & REG_INT_MBRF)) + return; + + /* 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) + len = 32; + + /* read the data from receive buffer */ + for (i = 0; i < len; ++i) { + if (i % 4 == 0) + val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4); + + byte = (val >> ((i % 4) * 8)) & 0xff; + i2c->msg->buf[i2c->processed++] = byte; + } + + /* are we finished? */ + if (i2c->processed == i2c->msg->len) + rk3x_i2c_stop(i2c, i2c->error); + else + rk3x_i2c_prepare_read(i2c); +} + +static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, unsigned int ipd) +{ + unsigned int con; + + if (!(ipd & REG_INT_STOP)) { + rk3x_i2c_stop(i2c, -EIO); + dev_err(i2c->dev, "unexpected irq in STOP: 0x%x\n", ipd); + rk3x_i2c_clean_ipd(i2c); + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_STOP, REG_IPD); + + /* disable STOP bit */ + con = i2c_readl(i2c, REG_CON); + con &= ~REG_CON_STOP; + i2c_writel(i2c, con, REG_CON); + + i2c->busy = false; + i2c->state = STATE_IDLE; + + /* signal rk3x_i2c_xfer that we are finished */ + wake_up(&i2c->wait); +} + +static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id) +{ + struct rk3x_i2c *i2c = dev_id; + unsigned int ipd; + + spin_lock(&i2c->lock); + + ipd = i2c_readl(i2c, REG_IPD); + if (i2c->state == STATE_IDLE) { + dev_warn(i2c->dev, "irq in STATE_IDLE, ipd = 0x%x\n", ipd); + rk3x_i2c_clean_ipd(i2c); + goto out; + } + + dev_dbg(i2c->dev, "IRQ: state %d, ipd: %x\n", i2c->state, ipd); + + /* Clean interrupt bits we don't care about */ + ipd &= ~(REG_INT_BRF | REG_INT_BTF); + + if (ipd & REG_INT_NAKRCV) { + /* + * We got a NACK in the last operation. Depending on whether + * IGNORE_NAK is set, we have to stop the operation and report + * an error. + */ + i2c_writel(i2c, REG_INT_NAKRCV, REG_IPD); + + ipd &= ~REG_INT_NAKRCV; + + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) + rk3x_i2c_stop(i2c, -ENXIO); + } + + /* is there anything left to handle? */ + if ((ipd & REG_INT_ALL) == 0) + goto out; + + switch (i2c->state) { + case STATE_START: + rk3x_i2c_handle_start(i2c, ipd); + break; + case STATE_WRITE: + rk3x_i2c_handle_write(i2c, ipd); + break; + case STATE_READ: + rk3x_i2c_handle_read(i2c, ipd); + break; + case STATE_STOP: + rk3x_i2c_handle_stop(i2c, ipd); + break; + case STATE_IDLE: + break; + } + +out: + spin_unlock(&i2c->lock); + return IRQ_HANDLED; +} + +/** + * rk3x_i2c_get_spec - Get timing values of I2C specification + * @speed: Desired SCL frequency + * + * Return: Matched i2c_spec_values. + */ +static const struct i2c_spec_values *rk3x_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; +} + +/** + * 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 + * + * 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. + */ +static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate, + struct i2c_timings *t, + struct rk3x_i2c_calced_timings *t_calc) +{ + unsigned long min_low_ns, min_high_ns; + unsigned long max_low_ns, min_total_ns; + + unsigned long clk_rate_khz, scl_rate_khz; + + unsigned long min_low_div, min_high_div; + unsigned long max_low_div; + + unsigned long min_div_for_hold, min_total_div; + unsigned long extra_div, extra_low_div, ideal_low_div; + + unsigned long data_hold_buffer_ns = 50; + const struct i2c_spec_values *spec; + int ret = 0; + + /* Only support standard-mode and fast-mode */ + 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)) + t->bus_freq_hz = 1000; + + /* + * min_low_ns: The minimum number of ns we need to hold low to + * meet I2C specification, should include fall time. + * min_high_ns: The minimum number of ns we need to hold high to + * meet I2C specification, should include rise time. + * max_low_ns: The maximum number of ns we can hold low to meet + * I2C specification. + * + * Note: max_low_ns should be (maximum data hold time * 2 - buffer) + * This is because the i2c host on Rockchip holds the data line + * for half the low time. + */ + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + min_high_ns = t->scl_rise_ns + spec->min_high_ns; + + /* + * Timings for repeated start: + * - controller appears to drop SDA at .875x (7/8) programmed clk high. + * - controller appears to keep SCL high for 2x programmed clk high. + * + * We need to account for those rules in picking our "high" time so + * we meet tSU;STA and tHD;STA times. + */ + min_high_ns = max(min_high_ns, DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns) * 1000, 875)); + min_high_ns = max(min_high_ns, DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns + t->sda_fall_ns + + spec->min_high_ns), 2)); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + max_low_ns = spec->max_data_hold_ns * 2 - data_hold_buffer_ns; + min_total_ns = min_low_ns + min_high_ns; + + /* Adjust to avoid overflow */ + clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = t->bus_freq_hz / 1000; + + /* + * We need the total div to be >= this number + * so we don't clock too fast. + */ + min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + /* These are the min dividers needed for min hold times. */ + min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + min_div_for_hold = (min_low_div + min_high_div); + + /* + * This is the maximum divider so we don't go over the maximum. + * We don't round up here (we round down) since this is a maximum. + */ + max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000); + + if (min_low_div > max_low_div) { + WARN_ONCE(true, + "Conflicting, min_low_div %lu, max_low_div %lu\n", + min_low_div, max_low_div); + max_low_div = min_low_div; + } + + if (min_div_for_hold > min_total_div) { + /* + * Time needed to meet hold requirements is important. + * Just use that. + */ + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; + } else { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + */ + extra_div = min_total_div - min_div_for_hold; + + /* + * We'll try to split things up perfectly evenly, + * biasing slightly towards having a higher div + * for low (spend more time low). + */ + ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, + scl_rate_khz * 8 * min_total_ns); + + /* Don't allow it to go over the maximum */ + if (ideal_low_div > max_low_div) + ideal_low_div = max_low_div; + + /* + * Handle when the ideal low div is going to take up + * more than we have. + */ + if (ideal_low_div > min_low_div + extra_div) + ideal_low_div = min_low_div + extra_div; + + /* Give low the "ideal" and give high whatever extra is left */ + extra_low_div = ideal_low_div - min_low_div; + t_calc->div_low = ideal_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * Adjust to the fact that the hardware has an implicit "+1". + * NOTE: Above calculations always produce div_low > 0 and div_high > 0. + */ + t_calc->div_low--; + t_calc->div_high--; + + /* Give the tuning value 0, that would not update con register */ + t_calc->tuning = 0; + /* Maximum divider supported by hw is 0xffff */ + if (t_calc->div_low > 0xffff) { + t_calc->div_low = 0xffff; + ret = -EINVAL; + } + + if (t_calc->div_high > 0xffff) { + t_calc->div_high = 0xffff; + ret = -EINVAL; + } + + return ret; +} + +/** + * 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 + * + * 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. + * + * l = divl + 1; + * h = divh + 1; + * s = sda_update_config + 1; + * u = start_setup_config + 1; + * p = stop_setup_config + 1; + * T = Tclk_i2c; + * + * tHigh = 8 * h * T; + * tLow = 8 * l * T; + * + * tHD;sda = (l * s + 1) * T; + * tSU;sda = [(8 - s) * l + 1] * T; + * tI2C = 8 * (l + h) * T; + * + * tSU;sta = (8h * u + 1) * T; + * tHD;sta = [8h * (u + 1) - 1] * T; + * tSU;sto = (8h * p + 1) * T; + */ +static int rk3x_i2c_v1_calc_timings(unsigned long clk_rate, + struct i2c_timings *t, + struct rk3x_i2c_calced_timings *t_calc) +{ + unsigned long min_low_ns, min_high_ns; + unsigned long min_setup_start_ns, min_setup_data_ns; + unsigned long min_setup_stop_ns, max_hold_data_ns; + + unsigned long clk_rate_khz, scl_rate_khz; + + unsigned long min_low_div, min_high_div; + + unsigned long min_div_for_hold, min_total_div; + unsigned long extra_div, extra_low_div; + unsigned long sda_update_cfg, stp_sta_cfg, stp_sto_cfg; + + const struct i2c_spec_values *spec; + int ret = 0; + + /* Support standard-mode, fast-mode and fast-mode plus */ + 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)) + t->bus_freq_hz = 1000; + + /* + * min_low_ns: The minimum number of ns we need to hold low to + * meet I2C specification, should include fall time. + * min_high_ns: The minimum number of ns we need to hold high to + * meet I2C specification, should include rise time. + */ + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + + /* calculate min-divh and min-divl */ + clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = t->bus_freq_hz / 1000; + min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + min_high_ns = t->scl_rise_ns + spec->min_high_ns; + min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + + /* + * Final divh and divl must be greater than 0, otherwise the + * hardware would not output the i2c clk. + */ + min_high_div = (min_high_div < 1) ? 2 : min_high_div; + min_low_div = (min_low_div < 1) ? 2 : min_low_div; + + /* These are the min dividers needed for min hold times. */ + min_div_for_hold = (min_low_div + min_high_div); + + /* + * This is the maximum divider so we don't go over the maximum. + * We don't round up here (we round down) since this is a maximum. + */ + if (min_div_for_hold >= min_total_div) { + /* + * Time needed to meet hold requirements is important. + * Just use that. + */ + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; + } else { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + * We'll try to split things up by the scale of min_low_div and + * min_high_div, biasing slightly towards having a higher div + * for low (spend more time low). + */ + extra_div = min_total_div - min_div_for_hold; + extra_low_div = DIV_ROUND_UP(min_low_div * extra_div, + min_div_for_hold); + + t_calc->div_low = min_low_div + extra_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * calculate sda data hold count by the rules, data_upd_st:3 + * is a appropriate value to reduce calculated times. + */ + for (sda_update_cfg = 3; sda_update_cfg > 0; sda_update_cfg--) { + max_hold_data_ns = DIV_ROUND_UP((sda_update_cfg + * (t_calc->div_low) + 1) + * 1000000, clk_rate_khz); + min_setup_data_ns = DIV_ROUND_UP(((8 - sda_update_cfg) + * (t_calc->div_low) + 1) + * 1000000, clk_rate_khz); + if ((max_hold_data_ns < spec->max_data_hold_ns) && + (min_setup_data_ns > spec->min_data_setup_ns)) + break; + } + + /* calculate setup start config */ + min_setup_start_ns = t->scl_rise_ns + spec->min_setup_start_ns; + stp_sta_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns + - 1000000, 8 * 1000000 * (t_calc->div_high)); + + /* calculate setup stop config */ + min_setup_stop_ns = t->scl_rise_ns + spec->min_setup_stop_ns; + stp_sto_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_stop_ns + - 1000000, 8 * 1000000 * (t_calc->div_high)); + + t_calc->tuning = REG_CON_SDA_CFG(--sda_update_cfg) | + REG_CON_STA_CFG(--stp_sta_cfg) | + REG_CON_STO_CFG(--stp_sto_cfg); + + t_calc->div_low--; + t_calc->div_high--; + + /* Maximum divider supported by hw is 0xffff */ + if (t_calc->div_low > 0xffff) { + t_calc->div_low = 0xffff; + ret = -EINVAL; + } + + if (t_calc->div_high > 0xffff) { + t_calc->div_high = 0xffff; + ret = -EINVAL; + } + + return ret; +} + +static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) +{ + struct i2c_timings *t = &i2c->t; + struct rk3x_i2c_calced_timings calc; + u64 t_low_ns, t_high_ns; + unsigned long flags; + u32 val; + int ret; + + ret = i2c->soc_data->calc_timings(clk_rate, t, &calc); + WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz); + + clk_enable(i2c->pclk); + + spin_lock_irqsave(&i2c->lock, flags); + val = i2c_readl(i2c, REG_CON); + val &= ~REG_CON_TUNING_MASK; + val |= calc.tuning; + i2c_writel(i2c, val, REG_CON); + i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), + REG_CLKDIV); + spin_unlock_irqrestore(&i2c->lock, flags); + + clk_disable(i2c->pclk); + + t_low_ns = div_u64(8ULL * HZ_PER_GHZ * (calc.div_low + 1), clk_rate); + t_high_ns = div_u64(8ULL * HZ_PER_GHZ * (calc.div_high + 1), clk_rate); + dev_dbg(i2c->dev, + "CLK %lukHz, Req %luns, Act low %lluns high %lluns\n", + clk_rate / HZ_PER_KHZ, + HZ_PER_GHZ / t->bus_freq_hz, + t_low_ns, t_high_ns); +} + +/** + * rk3x_i2c_clk_notifier_cb - Clock rate change callback + * @nb: Pointer to notifier block + * @event: Notification reason + * @data: Pointer to notification data object + * + * The callback checks whether a valid bus frequency can be generated after the + * change. If so, the change is acknowledged, otherwise the change is aborted. + * New dividers are written to the HW in the pre- or post change notification + * depending on the scaling direction. + * + * Code adapted from i2c-cadence.c. + * + * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK + * to acknowledge the change, NOTIFY_DONE if the notification is + * considered irrelevant. + */ +static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long + event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb); + struct rk3x_i2c_calced_timings calc; + + switch (event) { + case PRE_RATE_CHANGE: + /* + * Try the calculation (but don't store the result) ahead of + * time to see if we need to block the clock change. Timings + * shouldn't actually take effect until rk3x_i2c_adapt_div(). + */ + if (i2c->soc_data->calc_timings(ndata->new_rate, &i2c->t, + &calc) != 0) + return NOTIFY_STOP; + + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->new_rate); + + return NOTIFY_OK; + case POST_RATE_CHANGE: + /* scale down */ + if (ndata->new_rate < ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->new_rate); + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->old_rate); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +/** + * 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 + * + * 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) +{ + u32 addr = (msgs[0].addr & 0x7f) << 1; + int ret = 0; + + /* + * 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 target address and the target register + * address in this case. + */ + + if (num >= 2 && msgs[0].len < 4 && + !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) { + u32 reg_addr = 0; + int i; + + dev_dbg(i2c->dev, "Combined write/read from addr 0x%x\n", + addr >> 1); + + /* Fill MRXRADDR with the register address(es) */ + for (i = 0; i < msgs[0].len; ++i) { + reg_addr |= msgs[0].buf[i] << (i * 8); + reg_addr |= REG_MRXADDR_VALID(i); + } + + /* msgs[0] is handled by hw. */ + i2c->msg = &msgs[1]; + + i2c->mode = REG_CON_MOD_REGISTER_TX; + + i2c_writel(i2c, addr | REG_MRXADDR_VALID(0), REG_MRXADDR); + i2c_writel(i2c, reg_addr, REG_MRXRADDR); + + ret = 2; + } else { + /* + * We'll have to do it the boring way and process the msgs + * one-by-one. + */ + + if (msgs[0].flags & I2C_M_RD) { + addr |= 1; /* set read bit */ + + /* + * We have to transmit the target addr first. Use + * MOD_REGISTER_TX for that purpose. + */ + i2c->mode = REG_CON_MOD_REGISTER_TX; + i2c_writel(i2c, addr | REG_MRXADDR_VALID(0), + REG_MRXADDR); + i2c_writel(i2c, 0, REG_MRXRADDR); + } else { + i2c->mode = REG_CON_MOD_TX; + } + + i2c->msg = &msgs[0]; + + ret = 1; + } + + i2c->addr = msgs[0].addr; + i2c->busy = true; + i2c->state = STATE_START; + i2c->processed = 0; + i2c->error = 0; + + rk3x_i2c_clean_ipd(i2c); + + return ret; +} + +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 flags; + long time_left; + u32 val; + int ret = 0; + int i; + + spin_lock_irqsave(&i2c->lock, flags); + + clk_enable(i2c->clk); + clk_enable(i2c->pclk); + + i2c->is_last_msg = false; + + /* + * Process msgs. We can handle more than one message at once (see + * rk3x_i2c_setup()). + */ + for (i = 0; i < num; i += ret) { + ret = rk3x_i2c_setup(i2c, msgs + i, num - i); + + if (ret < 0) { + dev_err(i2c->dev, "rk3x_i2c_setup() failed\n"); + break; + } + + if (i + ret >= num) + i2c->is_last_msg = true; + + spin_unlock_irqrestore(&i2c->lock, flags); + + if (!polling) { + rk3x_i2c_start(i2c); + + time_left = wait_event_timeout(i2c->wait, !i2c->busy, + msecs_to_jiffies(WAIT_TIMEOUT)); + } else { + disable_irq(i2c->irq); + rk3x_i2c_start(i2c); + + time_left = rk3x_i2c_wait_xfer_poll(i2c); + + 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; + val |= REG_CON_EN | REG_CON_STOP; + i2c_writel(i2c, val, REG_CON); + + i2c->state = STATE_IDLE; + + ret = -ETIMEDOUT; + break; + } + + if (i2c->error) { + ret = i2c->error; + break; + } + } + + clk_disable(i2c->pclk); + clk_disable(i2c->clk); + + spin_unlock_irqrestore(&i2c->lock, flags); + + 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); + + rk3x_i2c_adapt_div(i2c, clk_get_rate(i2c->clk)); + + return 0; +} + +static u32 rk3x_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; +} + +static const struct i2c_algorithm rk3x_i2c_algorithm = { + .xfer = rk3x_i2c_xfer, + .xfer_atomic = rk3x_i2c_xfer_polling, + .functionality = rk3x_i2c_func, +}; + +static const struct rk3x_i2c_soc_data rv1108_soc_data = { + .grf_offset = -1, + .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, +}; + +static const struct rk3x_i2c_soc_data rk3188_soc_data = { + .grf_offset = 0x0a4, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3228_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3288_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3399_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + +static const struct of_device_id rk3x_i2c_match[] = { + { + .compatible = "rockchip,rv1108-i2c", + .data = &rv1108_soc_data + }, + { + .compatible = "rockchip,rv1126-i2c", + .data = &rv1126_soc_data + }, + { + .compatible = "rockchip,rk3066-i2c", + .data = &rk3066_soc_data + }, + { + .compatible = "rockchip,rk3188-i2c", + .data = &rk3188_soc_data + }, + { + .compatible = "rockchip,rk3228-i2c", + .data = &rk3228_soc_data + }, + { + .compatible = "rockchip,rk3288-i2c", + .data = &rk3288_soc_data + }, + { + .compatible = "rockchip,rk3399-i2c", + .data = &rk3399_soc_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3x_i2c_match); + +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; + int ret = 0; + int bus_nr; + u32 value; + int irq; + unsigned long clk_rate; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + match = of_match_node(rk3x_i2c_match, np); + i2c->soc_data = match->data; + + /* use common interface to get I2C timing properties */ + i2c_parse_fw_timings(&pdev->dev, &i2c->t, true); + + 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; + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + i2c->dev = &pdev->dev; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + i2c->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2c->regs)) + return PTR_ERR(i2c->regs); + + /* Try to set the I2C adapter number from dt */ + bus_nr = of_alias_get_id(np, "i2c"); + + /* + * Switch to new interface if the SoC also offers the old one. + * The control bit is located in the GRF register space. + */ + if (i2c->soc_data->grf_offset >= 0) { + struct regmap *grf; + + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, + "rk3x-i2c needs 'rockchip,grf' property\n"); + return PTR_ERR(grf); + } + + if (bus_nr < 0) { + dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias"); + return -EINVAL; + } + + /* 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) { + dev_err(i2c->dev, "Could not write to GRF: %d\n", ret); + return ret; + } + } + + /* IRQ setup */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq, + 0, dev_name(&pdev->dev), i2c); + if (ret < 0) { + dev_err(&pdev->dev, "cannot request IRQ\n"); + return ret; + } + + i2c->irq = irq; + + platform_set_drvdata(pdev, i2c); + + if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { + /* Only one clock to use for bus clock and peripheral clock */ + i2c->clk = devm_clk_get(&pdev->dev, NULL); + i2c->pclk = i2c->clk; + } else { + i2c->clk = devm_clk_get(&pdev->dev, "i2c"); + i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); + } + + 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) { + dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret); + return ret; + } + ret = clk_prepare(i2c->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret); + goto err_clk; + } + + i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb; + ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb); + if (ret != 0) { + dev_err(&pdev->dev, "Unable to register clock notifier\n"); + 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) + goto err_clk_notifier; + + return 0; + +err_clk_notifier: + clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); +err_pclk: + clk_unprepare(i2c->pclk); +err_clk: + clk_unprepare(i2c->clk); + return ret; +} + +static void rk3x_i2c_remove(struct platform_device *pdev) +{ + struct rk3x_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + + clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); + clk_unprepare(i2c->pclk); + clk_unprepare(i2c->clk); +} + +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, + .driver = { + .name = "rk3x-i2c", + .of_match_table = rk3x_i2c_match, + .pm = &rk3x_i2c_pm_ops, + }, +}; + +module_platform_driver(rk3x_i2c_driver); + +MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver"); +MODULE_AUTHOR("Max Schwarz "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c new file mode 100644 index 000000000..412fa8e37 --- /dev/null +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for RobotFuzz OSIF + * + * Copyright (c) 2013 Andrew Lunn + * Copyright (c) 2007 Barry Carter + * + * Based on the i2c-tiny-usb by + * + * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org) + */ + +#include +#include +#include +#include +#include +#include + +#define OSIFI2C_READ 20 +#define OSIFI2C_WRITE 21 +#define OSIFI2C_STOP 22 +#define OSIFI2C_STATUS 23 +#define OSIFI2C_SET_BIT_RATE 24 + +#define STATUS_ADDRESS_ACK 0 +#define STATUS_ADDRESS_NAK 2 + +struct osif_priv { + struct usb_device *usb_dev; + struct usb_interface *interface; + struct i2c_adapter adapter; + unsigned char status; +}; + +static int osif_usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct osif_priv *priv = adapter->algo_data; + + return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | + USB_DIR_IN, value, index, data, len, 2000); +} + +static int osif_usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + + struct osif_priv *priv = adapter->algo_data; + + return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, data, len, 2000); +} + +static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct osif_priv *priv = adapter->algo_data; + struct i2c_msg *pmsg; + int ret; + int i; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + if (pmsg->flags & I2C_M_RD) { + ret = osif_usb_read(adapter, OSIFI2C_READ, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len); + if (ret != pmsg->len) { + dev_err(&adapter->dev, "failure reading data\n"); + return -EREMOTEIO; + } + } else { + ret = osif_usb_write(adapter, OSIFI2C_WRITE, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len); + if (ret != pmsg->len) { + dev_err(&adapter->dev, "failure writing data\n"); + return -EREMOTEIO; + } + } + + ret = osif_usb_write(adapter, OSIFI2C_STOP, 0, 0, NULL, 0); + if (ret) { + dev_err(&adapter->dev, "failure sending STOP\n"); + return -EREMOTEIO; + } + + /* read status */ + ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0, + &priv->status, 1); + if (ret != 1) { + dev_err(&adapter->dev, "failure reading status\n"); + return -EREMOTEIO; + } + + if (priv->status != STATUS_ADDRESS_ACK) { + dev_dbg(&adapter->dev, "status = %d\n", priv->status); + return -EREMOTEIO; + } + } + + return i; +} + +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 = { + .xfer = osif_xfer, + .functionality = osif_func, +}; + +#define USB_OSIF_VENDOR_ID 0x1964 +#define USB_OSIF_PRODUCT_ID 0x0001 + +static const struct usb_device_id osif_table[] = { + { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, + { } +}; +MODULE_DEVICE_TABLE(usb, osif_table); + +static int osif_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ret; + struct osif_priv *priv; + u16 version; + + priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->usb_dev = interface_to_usbdev(interface); + priv->interface = interface; + + usb_set_intfdata(interface, priv); + + 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), + "OSIF at bus %03d device %03d", + priv->usb_dev->bus->busnum, priv->usb_dev->devnum); + + /* + * Set bus frequency. The frequency is: + * 120,000,000 / ( 16 + 2 * div * 4^prescale). + * Using dev = 52, prescale = 0 give 100KHz */ + ret = osif_usb_write(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0, + NULL, 0); + if (ret) { + dev_err(&interface->dev, "failure sending bit rate"); + return ret; + } + + i2c_add_adapter(&(priv->adapter)); + + version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice); + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d", + version >> 8, version & 0xff, + priv->usb_dev->bus->busnum, priv->usb_dev->devnum); + + return 0; +} + +static void osif_disconnect(struct usb_interface *interface) +{ + struct osif_priv *priv = usb_get_intfdata(interface); + + i2c_del_adapter(&(priv->adapter)); + usb_set_intfdata(interface, NULL); +} + +static struct usb_driver osif_driver = { + .name = "RobotFuzz Open Source InterFace, OSIF", + .probe = osif_probe, + .disconnect = osif_disconnect, + .id_table = osif_table, +}; + +module_usb_driver(osif_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_AUTHOR("Barry Carter "); +MODULE_DESCRIPTION("RobotFuzz OSIF driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c new file mode 100644 index 000000000..3a8225b06 --- /dev/null +++ b/drivers/i2c/busses/i2c-rtl9300.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum rtl9300_bus_freq { + RTL9300_I2C_STD_FREQ, // 100kHz + RTL9300_I2C_FAST_FREQ, // 400kHz + RTL9300_I2C_SUPER_FAST_FREQ, // 2.5MHz + RTL9300_I2C_SLOW_FREQ, // 50kHz +}; + +#define RTL9300_I2C_MAX_SUPER_FAST_FREQ 2500000 +#define RTL9300_I2C_MAX_SLOW_FREQ 50000 + + +struct rtl9300_i2c; + +struct rtl9300_i2c_chan { + struct i2c_adapter adap; + struct rtl9300_i2c *i2c; + enum rtl9300_bus_freq bus_freq; + u8 sda_num; + u32 clk_div; +}; + +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, + F_BUSY, + F_CLK_DIV, + F_EXT_SCK_5MS, + + /* 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); + int (*config_chan)(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan); + void (*config_clock)(u32 clock_freq, struct rtl9300_i2c_chan *chan); + int (*misc_init)(struct rtl9300_i2c *i2c); + u32 rd_reg; + u32 wd_reg; + u8 max_nchan; + u8 max_data_len; + u8 reg_addr_8bit_len; +}; + +#define RTL9300_I2C_MUX_NCHAN 8 +#define RTL9310_I2C_MUX_NCHAN 12 +#define RTL9607_I2C_MUX_NCHAN 1 + +#define RTL9300_I2C_MAX_DATA_LEN 16 +#define RTL9607_I2C_MAX_DATA_LEN 4 + +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 rd_reg; + u32 wd_reg; + u8 scl_num; + u8 sda_num; + struct mutex lock; + struct clk *clk; +}; + +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 RTL9300_REG_ADDR_8BIT_LEN 1 + +#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 + +#define RTL9607_I2C_CONFIG 0x22f50 +#define RTL9607_IO_MODE_EN 0x23014 +#define RTL9607_I2C_IND_WD 0x0 +#define RTL9607_I2C_IND_ADR 0x8 +#define RTL9607_I2C_IND_CMD 0x10 +#define RTL9607_I2C_IND_RD 0x18 +#define RTL9607_REG_ADDR_8BIT_LEN 0 + +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) +{ + const 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 = 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 rtl9607_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan) +{ + const 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_CLK_DIV], chan->clk_div); + if (ret) + return ret; + + drv_data = device_get_match_data(i2c->dev); + ret = drv_data->select_scl(i2c, i2c->scl_num); + if (ret) + return ret; + + i2c->sda_num = chan->sda_num; + return 0; +} + +static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan) +{ + struct rtl9300_i2c *i2c = chan->i2c; + + 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; + case RTL9300_I2C_MAX_SUPER_FAST_FREQ: + chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ; + break; + case RTL9300_I2C_MAX_SLOW_FREQ: + chan->bus_freq = RTL9300_I2C_SLOW_FREQ; + break; + default: + dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n", + chan->sda_num, clock_freq); + break; + } +} + +static void rtl9607_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan) +{ + struct rtl9300_i2c *i2c = chan->i2c; + + chan->clk_div = clk_get_rate(i2c->clk) / clock_freq - 1; +} + +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->rd_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->wd_reg, vals, ARRAY_SIZE(vals)); +} + +static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data) +{ + return regmap_write(i2c->regmap, i2c->wd_reg, data); +} + +static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer) +{ + const struct rtl9300_i2c_drv_data *drv_data; + int ret; + + drv_data = device_get_match_data(i2c->dev); + if (xfer->data_len < 1 || xfer->data_len > drv_data->max_data_len) + 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_BUSY], 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->rd_reg, &val); + if (ret) + return ret; + + *xfer->data = val & 0xff; + break; + case RTL9300_I2C_XFER_WORD: + ret = regmap_read(i2c->regmap, i2c->rd_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); + const struct rtl9300_i2c_drv_data *drv_data; + struct rtl9300_i2c *i2c = chan->i2c; + struct rtl9300_i2c_xfer xfer = {0}; + int ret; + + if (addr > 0x7f) + return -EINVAL; + + guard(rtl9300_i2c)(i2c); + + drv_data = device_get_match_data(i2c->dev); + ret = drv_data->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 = drv_data->reg_addr_8bit_len; + + 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_init(struct rtl9300_i2c *i2c) +{ + /* only use standard read format */ + return regmap_field_write(i2c->fields[F_RD_MODE], 0); +} + +static int rtl9607_i2c_init(struct rtl9300_i2c *i2c) +{ + return regmap_field_write(i2c->fields[F_EXT_SCK_5MS], 1); +} + +static int rtl9300_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtl9300_i2c *i2c; + const 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 = 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->rd_reg = i2c->reg_base + drv_data->rd_reg; + i2c->wd_reg = i2c->reg_base + drv_data->wd_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; + + i2c->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(i2c->clk)) + return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n"); + + i = 0; + for_each_child_of_node_scoped(dev->of_node, child) { + struct rtl9300_i2c_chan *chan = &i2c->chans[i]; + struct i2c_adapter *adap = &chan->adap; + + ret = of_property_read_u32(child, "reg", &sda_num); + if (ret) + return ret; + + ret = of_property_read_u32(child, "clock-frequency", &clock_freq); + if (ret) + clock_freq = I2C_MAX_STANDARD_MODE_FREQ; + + chan->sda_num = sda_num; + chan->i2c = i2c; + + drv_data->config_clock(clock_freq, chan); + + 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 = 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; + + ret = drv_data->misc_init(i2c); + 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), + [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0), + }, + .select_scl = rtl9300_i2c_select_scl, + .config_chan = rtl9300_i2c_config_chan, + .config_clock = rtl9300_i2c_config_clock, + .misc_init = rtl9300_i2c_init, + .rd_reg = RTL9300_I2C_MST_DATA_WORD0, + .wd_reg = RTL9300_I2C_MST_DATA_WORD0, + .max_nchan = RTL9300_I2C_MUX_NCHAN, + .max_data_len = RTL9300_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN, +}; + +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), + [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0), + }, + .select_scl = rtl9310_i2c_select_scl, + .config_chan = rtl9300_i2c_config_chan, + .config_clock = rtl9300_i2c_config_clock, + .misc_init = rtl9300_i2c_init, + .rd_reg = RTL9310_I2C_MST_DATA_CTRL, + .wd_reg = RTL9310_I2C_MST_DATA_CTRL, + .max_nchan = RTL9310_I2C_MUX_NCHAN, + .max_data_len = RTL9300_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN, +}; + +static const struct rtl9300_i2c_drv_data rtl9607_i2c_drv_data = { + .field_desc = { + [F_SCL_SEL] = GLB_REG_FIELD(RTL9607_IO_MODE_EN, 13, 14), + [F_EXT_SCK_5MS] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 26, 26), + [F_DEV_ADDR] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 14, 20), + [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 12, 13), + [F_DATA_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 10, 11), + [F_CLK_DIV] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 0, 9), + [F_I2C_FAIL] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 3, 3), + [F_BUSY] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 2, 2), + [F_RWOP] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 1, 1), + [F_I2C_TRIG] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 0, 0), + [F_MEM_ADDR] = MST_REG_FIELD(RTL9607_I2C_IND_ADR, 0, 31), + }, + .select_scl = rtl9310_i2c_select_scl, + .config_chan = rtl9607_i2c_config_chan, + .config_clock = rtl9607_i2c_config_clock, + .misc_init = rtl9607_i2c_init, + .rd_reg = RTL9607_I2C_IND_RD, + .wd_reg = RTL9607_I2C_IND_WD, + .max_nchan = RTL9607_I2C_MUX_NCHAN, + .max_data_len = RTL9607_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9607_REG_ADDR_8BIT_LEN, +}; + +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 }, + { .compatible = "realtek,rtl9607-i2c", .data = (void *) &rtl9607_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 000000000..206a99e72 --- /dev/null +++ b/drivers/i2c/busses/i2c-rzv2m.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Renesas RZ/V2M I2C unit + * + * Copyright (C) 2016-2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 ret; + + 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 new file mode 100644 index 000000000..15e14a6fe --- /dev/null +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* linux/drivers/i2c/busses/i2c-s3c2410.c + * + * Copyright (C) 2004,2005,2009 Simtec Electronics + * Ben Dooks + * + * S3C2410 I2C Controller +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* see s3c2410x user guide, v1.1, section 9 (p447) for more info */ + +#define S3C2410_IICCON 0x00 +#define S3C2410_IICSTAT 0x04 +#define S3C2410_IICADD 0x08 +#define S3C2410_IICDS 0x0C +#define S3C2440_IICLC 0x10 + +#define S3C2410_IICCON_ACKEN (1 << 7) +#define S3C2410_IICCON_TXDIV_16 (0 << 6) +#define S3C2410_IICCON_TXDIV_512 (1 << 6) +#define S3C2410_IICCON_IRQEN (1 << 5) +#define S3C2410_IICCON_IRQPEND (1 << 4) +#define S3C2410_IICCON_SCALE(x) ((x) & 0xf) +#define S3C2410_IICCON_SCALEMASK (0xf) + +#define S3C2410_IICSTAT_MASTER_RX (2 << 6) +#define S3C2410_IICSTAT_MASTER_TX (3 << 6) +#define S3C2410_IICSTAT_SLAVE_RX (0 << 6) +#define S3C2410_IICSTAT_SLAVE_TX (1 << 6) +#define S3C2410_IICSTAT_MODEMASK (3 << 6) + +#define S3C2410_IICSTAT_START (1 << 5) +#define S3C2410_IICSTAT_BUSBUSY (1 << 5) +#define S3C2410_IICSTAT_TXRXEN (1 << 4) +#define S3C2410_IICSTAT_ARBITR (1 << 3) +#define S3C2410_IICSTAT_ASSLAVE (1 << 2) +#define S3C2410_IICSTAT_ADDR0 (1 << 1) +#define S3C2410_IICSTAT_LASTBIT (1 << 0) + +#define S3C2410_IICLC_SDA_DELAY0 (0 << 0) +#define S3C2410_IICLC_SDA_DELAY5 (1 << 0) +#define S3C2410_IICLC_SDA_DELAY10 (2 << 0) +#define S3C2410_IICLC_SDA_DELAY15 (3 << 0) +#define S3C2410_IICLC_SDA_DELAY_MASK (3 << 0) + +#define S3C2410_IICLC_FILTER_ON (1 << 2) + +/* Treat S3C2410 as baseline hardware, anything else is supported via quirks */ +#define QUIRK_S3C2440 (1 << 0) +#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 + +/* Exynos5 Sysreg offset */ +#define EXYNOS5_SYS_I2C_CFG 0x0234 + +/* i2c controller state */ +enum s3c24xx_i2c_state { + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_STOP +}; + +struct s3c24xx_i2c { + wait_queue_head_t wait; + kernel_ulong_t quirks; + + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + + unsigned int tx_setup; + unsigned int irq; + + enum s3c24xx_i2c_state state; + unsigned long clkrate; + + void __iomem *regs; + struct clk *clk; + struct device *dev; + struct i2c_adapter adap; + + struct s3c2410_platform_i2c *pdata; + struct gpio_desc *gpios[2]; + struct pinctrl *pctrl; + struct regmap *sysreg; + unsigned int sys_i2c_cfg; +}; + +static const struct platform_device_id s3c24xx_driver_ids[] = { + { + .name = "s3c2410-i2c", + .driver_data = 0, + }, { + .name = "s3c2440-i2c", + .driver_data = QUIRK_S3C2440, + }, { + .name = "s3c2440-hdmiphy-i2c", + .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO, + }, { } +}; +MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); + +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,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, + { .compatible = "samsung,s3c2440-hdmiphy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, + {}, +}; +MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); +#endif + +/* + * Get controller type either from device tree or platform device variant. + */ +static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) +{ + if (pdev->dev.of_node) + return (kernel_ulong_t)of_device_get_match_data(&pdev->dev); + + return platform_get_device_id(pdev)->driver_data; +} + +/* + * Complete the message and wake up the caller, using the given return code, + * or zero to mean ok. + */ +static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) +{ + dev_dbg(i2c->dev, "master_complete %d\n", ret); + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + if (!(i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC))) + wake_up(&i2c->wait); +} + +static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); +} + +static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); +} + +/* irq enable/disable functions */ +static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); +} + +static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); +} + +static bool is_ack(struct s3c24xx_i2c *i2c) +{ + int tries; + + for (tries = 50; tries; --tries) { + 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; + } + usleep_range(1000, 2000); + } + dev_err(i2c->dev, "ack was not received\n"); + return false; +} + +/* + * put the start of a message onto the bus + */ +static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, + struct i2c_msg *msg) +{ + unsigned int addr = (msg->addr & 0x7f) << 1; + unsigned long stat; + unsigned long iiccon; + + stat = 0; + stat |= S3C2410_IICSTAT_TXRXEN; + + if (msg->flags & I2C_M_RD) { + stat |= S3C2410_IICSTAT_MASTER_RX; + addr |= 1; + } else + stat |= S3C2410_IICSTAT_MASTER_TX; + + if (msg->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + /* todo - check for whether ack wanted or not */ + s3c24xx_i2c_enable_ack(i2c); + + iiccon = readl(i2c->regs + S3C2410_IICCON); + writel(stat, i2c->regs + S3C2410_IICSTAT); + + dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); + writeb(addr, i2c->regs + S3C2410_IICDS); + + /* + * delay here to ensure the data byte has gotten onto the bus + * before the transaction is started + */ + ndelay(i2c->tx_setup); + + dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); + writel(iiccon, i2c->regs + S3C2410_IICCON); + + stat |= S3C2410_IICSTAT_START; + writel(stat, i2c->regs + S3C2410_IICSTAT); +} + +static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) +{ + unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT); + + dev_dbg(i2c->dev, "STOP\n"); + + /* + * The datasheet says that the STOP sequence should be: + * 1) I2CSTAT.5 = 0 - Clear BUSY (or 'generate STOP') + * 2) I2CCON.4 = 0 - Clear IRQPEND + * 3) Wait until the stop condition takes effect. + * 4*) I2CSTAT.4 = 0 - Clear TXRXEN + * + * Where, step "4*" is only for buses with the "HDMIPHY" quirk. + * + * However, after much experimentation, it appears that: + * a) normal buses automatically clear BUSY and transition from + * Master->Slave when they complete generating a STOP condition. + * Therefore, step (3) can be done in doxfer() by polling I2CCON.4 + * after starting the STOP generation here. + * b) HDMIPHY bus does neither, so there is no way to do step 3. + * There is no indication when this bus has finished generating + * STOP. + * + * In fact, we have found that as soon as the IRQPEND bit is cleared in + * step 2, the HDMIPHY bus generates the STOP condition, and then + * immediately starts transferring another data byte, even though the + * bus is supposedly stopped. This is presumably because the bus is + * still in "Master" mode, and its BUSY bit is still set. + * + * To avoid these extra post-STOP transactions on HDMI phy devices, we + * just disable Serial Output on the bus (I2CSTAT.4 = 0) directly, + * instead of first generating a proper STOP condition. This should + * float SDA & SCK terminating the transfer. Subsequent transfers + * start with a proper START condition, and proceed normally. + * + * The HDMIPHY bus is an internal bus that always has exactly two + * devices, the host as Master and the HDMIPHY device as the slave. + * Skipping the STOP condition has been tested on this bus and works. + */ + if (i2c->quirks & QUIRK_HDMIPHY) { + /* Stop driving the I2C pins */ + iicstat &= ~S3C2410_IICSTAT_TXRXEN; + } else { + /* stop the transfer */ + iicstat &= ~S3C2410_IICSTAT_START; + } + writel(iicstat, i2c->regs + S3C2410_IICSTAT); + + i2c->state = STATE_STOP; + + s3c24xx_i2c_master_complete(i2c, ret); + s3c24xx_i2c_disable_irq(i2c); +} + +/* + * helper functions to determine the current state in the set of + * messages we are sending + */ + +/* + * returns TRUE if the current message is the last in the set + */ +static inline int is_lastmsg(struct s3c24xx_i2c *i2c) +{ + return i2c->msg_idx >= (i2c->msg_num - 1); +} + +/* + * returns TRUE if we this is the last byte in the current message + */ +static inline int is_msglast(struct s3c24xx_i2c *i2c) +{ + /* + * msg->len is always 1 for the first byte of smbus block read. + * Actual length will be read from slave. More bytes will be + * read according to the length then. + */ + if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) + return 0; + + return i2c->msg_ptr == i2c->msg->len-1; +} + +/* + * returns TRUE if we reached the end of the current message + */ +static inline int is_msgend(struct s3c24xx_i2c *i2c) +{ + return i2c->msg_ptr >= i2c->msg->len; +} + +/* + * process an interrupt and work out what to do + */ +static void i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) +{ + unsigned long tmp; + unsigned char byte; + + switch (i2c->state) { + + case STATE_IDLE: + dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); + goto out; + + case STATE_STOP: + dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); + s3c24xx_i2c_disable_irq(i2c); + goto out_ack; + + case STATE_START: + /* + * last thing we did was send a start condition on the + * bus, or started a new i2c message + */ + if (iicstat & S3C2410_IICSTAT_LASTBIT && + !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + /* ack was not received... */ + dev_dbg(i2c->dev, "ack was not received\n"); + s3c24xx_i2c_stop(i2c, -ENXIO); + goto out_ack; + } + + if (i2c->msg->flags & I2C_M_RD) + i2c->state = STATE_READ; + else + i2c->state = STATE_WRITE; + + /* + * Terminate the transfer if there is nothing to do + * as this is used by the i2c probe to find devices. + */ + if (is_lastmsg(i2c) && i2c->msg->len == 0) { + s3c24xx_i2c_stop(i2c, 0); + goto out_ack; + } + + if (i2c->state == STATE_READ) + goto prepare_read; + + /* + * 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 + * end of the message, and if so, work out what to do + */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & S3C2410_IICSTAT_LASTBIT) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + s3c24xx_i2c_stop(i2c, -ECONNREFUSED); + goto out_ack; + } + } + + retry_write: + + if (!is_msgend(i2c)) { + byte = i2c->msg->buf[i2c->msg_ptr++]; + writeb(byte, i2c->regs + S3C2410_IICDS); + + /* + * delay after writing the byte to allow the + * data setup time on the bus, as writing the + * data to the register causes the first bit + * to appear on SDA, and SCL will change as + * soon as the interrupt is acknowledged + */ + ndelay(i2c->tx_setup); + + } else if (!is_lastmsg(i2c)) { + /* we need to go to the next i2c message */ + + dev_dbg(i2c->dev, "WRITE: Next Message\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + /* check to see if we need to do another message */ + if (i2c->msg->flags & I2C_M_NOSTART) { + + if (i2c->msg->flags & I2C_M_RD) { + /* + * cannot do this, the controller + * 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; + } else { + /* send the new start */ + s3c24xx_i2c_message_start(i2c, i2c->msg); + i2c->state = STATE_START; + } + + } else { + /* send stop */ + s3c24xx_i2c_stop(i2c, 0); + } + break; + + case STATE_READ: + /* + * we have a byte of data in the data register, do + * something with it, and then work out whether we are + * going to do any more read/write + */ + byte = readb(i2c->regs + S3C2410_IICDS); + i2c->msg->buf[i2c->msg_ptr++] = byte; + + /* Add actual length to read for smbus block read */ + if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) { + if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) { + s3c24xx_i2c_stop(i2c, -EPROTO); + break; + } + i2c->msg->len += byte; + } + prepare_read: + if (is_msglast(i2c)) { + /* last byte of buffer */ + + if (is_lastmsg(i2c)) + s3c24xx_i2c_disable_ack(i2c); + + } else if (is_msgend(i2c)) { + /* + * ok, we've read the entire buffer, see if there + * is anything else we need to do + */ + if (is_lastmsg(i2c)) { + /* last message, send stop and complete */ + dev_dbg(i2c->dev, "READ: Send Stop\n"); + + s3c24xx_i2c_stop(i2c, 0); + } else { + /* go to the next transfer */ + dev_dbg(i2c->dev, "READ: Next Transfer\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + } + } + + break; + } + + /* acknowlegde the IRQ and get back on with the work */ + + out_ack: + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~S3C2410_IICCON_IRQPEND; + writel(tmp, i2c->regs + S3C2410_IICCON); + out: + return; +} + +/* + * top level IRQ servicing routine + */ +static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) +{ + struct s3c24xx_i2c *i2c = dev_id; + unsigned long status; + unsigned long tmp; + + status = readl(i2c->regs + S3C2410_IICSTAT); + + if (status & S3C2410_IICSTAT_ARBITR) { + /* deal with arbitration loss */ + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + + if (i2c->state == STATE_IDLE) { + dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); + + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~S3C2410_IICCON_IRQPEND; + writel(tmp, i2c->regs + S3C2410_IICCON); + goto out; + } + + /* + * pretty much this leaves us with the fact that we've + * transmitted or received whatever byte we last sent + */ + i2c_s3c_irq_nextbyte(i2c, status); + + out: + return IRQ_HANDLED; +} + +/* + * Disable the bus so that we won't get any interrupts from now on, or try + * to drive any lines. This is the default state when we don't have + * anything to send/receive. + * + * If there is an event on the bus, or we have a pre-existing event at + * kernel boot time, we may not notice the event and the I2C controller + * will lock the bus with the I2C clock line low indefinitely. + */ +static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + /* Stop driving the I2C pins */ + tmp = readl(i2c->regs + S3C2410_IICSTAT); + tmp &= ~S3C2410_IICSTAT_TXRXEN; + writel(tmp, i2c->regs + S3C2410_IICSTAT); + + /* We don't expect any interrupts now, and don't want send acks */ + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~(S3C2410_IICCON_IRQEN | S3C2410_IICCON_IRQPEND | + S3C2410_IICCON_ACKEN); + writel(tmp, i2c->regs + S3C2410_IICCON); +} + + +/* + * get the i2c bus for a master transaction + */ +static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) +{ + unsigned long iicstat; + int timeout = 400; + + while (timeout-- > 0) { + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + + if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) + return 0; + + msleep(1); + } + + return -ETIMEDOUT; +} + +/* + * wait for the i2c bus to become idle. + */ +static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c) +{ + unsigned long iicstat; + ktime_t start, now; + unsigned long delay; + int spins; + + /* ensure the stop has been through the bus */ + + dev_dbg(i2c->dev, "waiting for bus idle\n"); + + start = now = ktime_get(); + + /* + * Most of the time, the bus is already idle within a few usec of the + * end of a transaction. However, really slow i2c devices can stretch + * the clock, delaying STOP generation. + * + * On slower SoCs this typically happens within a very small number of + * instructions so busy wait briefly to avoid scheduling overhead. + */ + spins = 3; + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + while ((iicstat & S3C2410_IICSTAT_START) && --spins) { + cpu_relax(); + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } + + /* + * If we do get an appreciable delay as a compromise between idle + * detection latency for the normal, fast case, and system load in the + * slow device case, use an exponential back off in the polling loop, + * up to 1/10th of the total timeout, then continue to poll at a + * constant rate up to the timeout. + */ + delay = 1; + while ((iicstat & S3C2410_IICSTAT_START) && + ktime_us_delta(now, start) < S3C2410_IDLE_TIMEOUT) { + usleep_range(delay, 2 * delay); + if (delay < S3C2410_IDLE_TIMEOUT / 10) + delay <<= 1; + now = ktime_get(); + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } + + if (iicstat & S3C2410_IICSTAT_START) + dev_warn(i2c->dev, "timeout waiting for bus idle\n"); +} + +/* + * this starts an i2c transfer + */ +static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + long time_left = 0; + int ret; + + ret = s3c24xx_i2c_set_master(i2c); + if (ret != 0) { + dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); + ret = -EAGAIN; + goto out; + } + + i2c->msg = msgs; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->state = STATE_START; + + s3c24xx_i2c_enable_irq(i2c); + s3c24xx_i2c_message_start(i2c, msgs); + + if (i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC)) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + unsigned long stat = readl(i2c->regs + S3C2410_IICSTAT); + + 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"); + } + } else { + time_left = 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 (time_left == 0) + dev_dbg(i2c->dev, "timeout\n"); + else if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + /* For QUIRK_HDMIPHY, bus is already disabled */ + if (i2c->quirks & QUIRK_HDMIPHY) + goto out; + + s3c24xx_i2c_wait_idle(i2c); + + s3c24xx_i2c_disable_bus(i2c); + + out: + i2c->state = STATE_IDLE; + + return ret; +} + +/* + * first port of call from the i2c bus code when an message needs + * transferring across the i2c bus. + */ +static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data; + int retry; + int ret; + + ret = clk_enable(i2c->clk); + if (ret) + return ret; + + for (retry = 0; retry < adap->retries; retry++) { + + ret = s3c24xx_i2c_doxfer(i2c, msgs, num); + + if (ret != -EAGAIN) { + clk_disable(i2c->clk); + return ret; + } + + dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); + + udelay(100); + } + + clk_disable(i2c->clk); + 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_ALL | I2C_FUNC_NOSTART | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* i2c bus registration info */ +static const struct i2c_algorithm s3c24xx_i2c_algorithm = { + .xfer = s3c24xx_i2c_xfer, + .xfer_atomic = s3c24xx_i2c_xfer_atomic, + .functionality = s3c24xx_i2c_func, +}; + +/* + * return the divisor settings for a given frequency + */ +static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, + unsigned int *div1, unsigned int *divs) +{ + unsigned int calc_divs = clkin / wanted; + unsigned int calc_div1; + + if (calc_divs > (16*16)) + calc_div1 = 512; + else + calc_div1 = 16; + + calc_divs += calc_div1-1; + calc_divs /= calc_div1; + + if (calc_divs == 0) + calc_divs = 1; + if (calc_divs > 17) + calc_divs = 17; + + *divs = calc_divs; + *div1 = calc_div1; + + return clkin / (calc_divs * calc_div1); +} + +/* + * work out a divisor for the user requested frequency setting, + * either by the requested frequency, or scanning the acceptable + * range of frequencies until something is found + */ +static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) +{ + struct s3c2410_platform_i2c *pdata = i2c->pdata; + unsigned long clkin = clk_get_rate(i2c->clk); + unsigned int divs, div1; + unsigned long target_frequency; + u32 iiccon; + int freq; + + i2c->clkrate = clkin; + clkin /= 1000; /* clkin now in KHz */ + + dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency); + + target_frequency = pdata->frequency ?: I2C_MAX_STANDARD_MODE_FREQ; + + target_frequency /= 1000; /* Target frequency now in KHz */ + + freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs); + + if (freq > target_frequency) { + dev_err(i2c->dev, + "Unable to achieve desired frequency %luKHz." \ + " Lowest achievable %dKHz\n", target_frequency, freq); + return -EINVAL; + } + + *got = freq; + + iiccon = readl(i2c->regs + S3C2410_IICCON); + iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512); + iiccon |= (divs-1); + + if (div1 == 512) + iiccon |= S3C2410_IICCON_TXDIV_512; + + if (i2c->quirks & QUIRK_POLL) + iiccon |= S3C2410_IICCON_SCALE(2); + + writel(iiccon, i2c->regs + S3C2410_IICCON); + + if (i2c->quirks & QUIRK_S3C2440) { + unsigned long sda_delay; + + if (pdata->sda_delay) { + sda_delay = clkin * pdata->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= S3C2410_IICLC_FILTER_ON; + } else + sda_delay = 0; + + dev_dbg(i2c->dev, "IICLC=%08lx\n", sda_delay); + writel(sda_delay, i2c->regs + S3C2440_IICLC); + } + + return 0; +} + +#ifdef CONFIG_OF +static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c) +{ + int i; + + if (i2c->quirks & QUIRK_NO_GPIO) + return 0; + + 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; +} + +#else +static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c) +{ + return 0; +} +#endif + +/* + * initialise the controller, set the IO lines and frequency + */ +static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) +{ + struct s3c2410_platform_i2c *pdata; + unsigned int freq; + + /* get the plafrom data */ + + pdata = i2c->pdata; + + /* write slave address */ + + writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); + + dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); + + writel(0, i2c->regs + S3C2410_IICCON); + writel(0, i2c->regs + S3C2410_IICSTAT); + + /* we need to work out the divisors for the clock... */ + + if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { + dev_err(i2c->dev, "cannot meet bus frequency required\n"); + return -EINVAL; + } + + /* todo - check that the i2c lines aren't being dragged anywhere */ + + dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); + dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02x\n", + readl(i2c->regs + S3C2410_IICCON)); + + return 0; +} + +#ifdef CONFIG_OF +/* + * Parse the device tree node and retreive the platform data. + */ +static void +s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) +{ + struct s3c2410_platform_i2c *pdata = i2c->pdata; + int id; + + if (!np) + return; + + pdata->bus_num = -1; /* i2c bus number is dynamically assigned */ + of_property_read_u32(np, "samsung,i2c-sda-delay", &pdata->sda_delay); + of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr); + of_property_read_u32(np, "samsung,i2c-max-bus-freq", + (u32 *)&pdata->frequency); + /* + * Exynos5's legacy i2c controller and new high speed i2c + * controller have muxed interrupt sources. By default the + * interrupts for 4-channel HS-I2C controller are enabled. + * If nodes for first four channels of legacy i2c controller + * are available then re-configure the interrupts via the + * system register. + */ + id = of_alias_get_id(np, "i2c"); + i2c->sysreg = syscon_regmap_lookup_by_phandle(np, + "samsung,sysreg-phandle"); + if (IS_ERR(i2c->sysreg)) + return; + + regmap_update_bits(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, BIT(id), 0); +} +#else +static void +s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { } +#endif + +static int s3c24xx_i2c_probe(struct platform_device *pdev) +{ + struct s3c24xx_i2c *i2c; + struct s3c2410_platform_i2c *pdata = NULL; + struct resource *res; + int ret; + + if (!pdev->dev.of_node) { + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + } + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!i2c->pdata) + return -ENOMEM; + + i2c->quirks = s3c24xx_get_device_quirks(pdev); + i2c->sysreg = ERR_PTR(-ENOENT); + if (pdata) + memcpy(i2c->pdata, pdata, sizeof(*pdata)); + else + s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c); + + 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; + i2c->adap.class = I2C_CLASS_DEPRECATED; + i2c->tx_setup = 50; + + init_waitqueue_head(&i2c->wait); + + /* find the clock and enable it */ + i2c->dev = &pdev->dev; + i2c->clk = devm_clk_get(&pdev->dev, "i2c"); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return -ENOENT; + } + + dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); + + /* map the registers */ + i2c->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2c->regs)) + return PTR_ERR(i2c->regs); + + dev_dbg(&pdev->dev, "registers %p (%p)\n", + i2c->regs, res); + + /* setup info block for the i2c core */ + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev); + + /* inititalise the i2c gpio lines */ + if (i2c->pdata->cfg_gpio) + i2c->pdata->cfg_gpio(to_platform_device(i2c->dev)); + else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) + return -EINVAL; + + /* initialise the i2c controller */ + ret = clk_prepare_enable(i2c->clk); + if (ret) { + dev_err(&pdev->dev, "I2C clock enable failed\n"); + return ret; + } + + ret = s3c24xx_i2c_init(i2c); + clk_disable(i2c->clk); + if (ret != 0) { + dev_err(&pdev->dev, "I2C controller init failed\n"); + clk_unprepare(i2c->clk); + return ret; + } + + /* + * find the IRQ for this unit (note, this relies on the init call to + * ensure no current IRQs pending + */ + if (!(i2c->quirks & QUIRK_POLL)) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + clk_unprepare(i2c->clk); + return ret; + } + + ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, + 0, dev_name(&pdev->dev), i2c); + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + 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 + * the platform data, so if unset it will now default to always + * being bus 0. + */ + i2c->adap.nr = i2c->pdata->bus_num; + i2c->adap.dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, i2c); + + pm_runtime_enable(&pdev->dev); + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + pm_runtime_disable(&pdev->dev); + clk_unprepare(i2c->clk); + return ret; + } + + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); + return 0; +} + +static void s3c24xx_i2c_remove(struct platform_device *pdev) +{ + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + + clk_unprepare(i2c->clk); + + pm_runtime_disable(&pdev->dev); + + i2c_del_adapter(&i2c->adap); +} + +static int s3c24xx_i2c_suspend_noirq(struct device *dev) +{ + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c->adap); + + if (!IS_ERR(i2c->sysreg)) + regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg); + + return 0; +} + +static int s3c24xx_i2c_resume_noirq(struct device *dev) +{ + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(i2c->sysreg)) + regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg); + + ret = clk_enable(i2c->clk); + if (ret) + return ret; + s3c24xx_i2c_init(i2c); + clk_disable(i2c->clk); + i2c_mark_adapter_resumed(&i2c->adap); + + return 0; +} + +static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq, + s3c24xx_i2c_resume_noirq) +}; + +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 = pm_sleep_ptr(&s3c24xx_i2c_dev_pm_ops), + .of_match_table = of_match_ptr(s3c24xx_i2c_match), + }, +}; + +static int __init i2c_adap_s3c_init(void) +{ + return platform_driver_register(&s3c24xx_i2c_driver); +} +subsys_initcall(i2c_adap_s3c_init); + +static void __exit i2c_adap_s3c_exit(void) +{ + platform_driver_unregister(&s3c24xx_i2c_driver); +} +module_exit(i2c_adap_s3c_exit); + +MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c new file mode 100644 index 000000000..324a63efa --- /dev/null +++ b/drivers/i2c/busses/i2c-scmi.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMBus driver for ACPI SMBus CMI + * + * Copyright (C) 2009 Crane Cai + */ + +#include +#include +#include +#include +#include +#include +#include + +struct smbus_methods_t { + char *mt_info; + char *mt_sbr; + char *mt_sbw; +}; + +struct acpi_smbus_cmi { + acpi_handle handle; + struct i2c_adapter adapter; + u8 cap_info:1; + u8 cap_read:1; + u8 cap_write:1; + const struct smbus_methods_t *methods; +}; + +static const struct smbus_methods_t smbus_methods = { + .mt_info = "_SBI", + .mt_sbr = "_SBR", + .mt_sbw = "_SBW", +}; + +/* Some IBM BIOSes omit the leading underscore */ +static const struct smbus_methods_t ibm_smbus_methods = { + .mt_info = "SBI_", + .mt_sbr = "SBR_", + .mt_sbw = "SBW_", +}; + +static const struct acpi_device_id acpi_smbus_cmi_ids[] = { + {"SMBUS01", (kernel_ulong_t)&smbus_methods}, + {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods}, + {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods}, + {"", 0} +}; +MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids); + +#define ACPI_SMBUS_STATUS_OK 0x00 +#define ACPI_SMBUS_STATUS_FAIL 0x07 +#define ACPI_SMBUS_STATUS_DNAK 0x10 +#define ACPI_SMBUS_STATUS_DERR 0x11 +#define ACPI_SMBUS_STATUS_CMD_DENY 0x12 +#define ACPI_SMBUS_STATUS_UNKNOWN 0x13 +#define ACPI_SMBUS_STATUS_ACC_DENY 0x17 +#define ACPI_SMBUS_STATUS_TIMEOUT 0x18 +#define ACPI_SMBUS_STATUS_NOTSUP 0x19 +#define ACPI_SMBUS_STATUS_BUSY 0x1a +#define ACPI_SMBUS_STATUS_PEC 0x1f + +#define ACPI_SMBUS_PRTCL_WRITE 0x00 +#define ACPI_SMBUS_PRTCL_READ 0x01 +#define ACPI_SMBUS_PRTCL_QUICK 0x02 +#define ACPI_SMBUS_PRTCL_BYTE 0x04 +#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06 +#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08 +#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a + + +static int +acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + int result = 0; + struct acpi_smbus_cmi *smbus_cmi = adap->algo_data; + unsigned char protocol; + acpi_status status = 0; + struct acpi_object_list input; + union acpi_object mt_params[5]; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object *pkg; + char *method; + int len = 0; + + dev_dbg(&adap->dev, "access size: %d %s\n", size, + (read_write) ? "READ" : "WRITE"); + switch (size) { + case I2C_SMBUS_QUICK: + protocol = ACPI_SMBUS_PRTCL_QUICK; + command = 0; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } + break; + + case I2C_SMBUS_BYTE: + protocol = ACPI_SMBUS_PRTCL_BYTE; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } else { + command = 0; + } + break; + + case I2C_SMBUS_BYTE_DATA: + protocol = ACPI_SMBUS_PRTCL_BYTE_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 1; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->byte; + } + break; + + case I2C_SMBUS_WORD_DATA: + protocol = ACPI_SMBUS_PRTCL_WORD_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 2; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->word; + } + break; + + case I2C_SMBUS_BLOCK_DATA: + protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA; + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = len; + mt_params[4].type = ACPI_TYPE_BUFFER; + mt_params[4].buffer.length = len; + mt_params[4].buffer.pointer = data->block + 1; + } + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (read_write == I2C_SMBUS_READ) { + protocol |= ACPI_SMBUS_PRTCL_READ; + method = smbus_cmi->methods->mt_sbr; + input.count = 3; + } else { + protocol |= ACPI_SMBUS_PRTCL_WRITE; + method = smbus_cmi->methods->mt_sbw; + input.count = 5; + } + + input.pointer = mt_params; + mt_params[0].type = ACPI_TYPE_INTEGER; + mt_params[0].integer.value = protocol; + mt_params[1].type = ACPI_TYPE_INTEGER; + mt_params[1].integer.value = addr; + mt_params[2].type = ACPI_TYPE_INTEGER; + mt_params[2].integer.value = command; + + status = acpi_evaluate_object(smbus_cmi->handle, method, &input, + &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(smbus_cmi->handle, + "Failed to evaluate %s: %i\n", method, status); + return -EIO; + } + + pkg = buffer.pointer; + if (pkg && pkg->type == ACPI_TYPE_PACKAGE) + obj = pkg->package.elements; + else { + acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); + result = -EIO; + goto out; + } + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); + result = -EIO; + goto out; + } + + result = obj->integer.value; + acpi_handle_debug(smbus_cmi->handle, "%s return status: %i\n", method, + result); + + switch (result) { + case ACPI_SMBUS_STATUS_OK: + result = 0; + break; + case ACPI_SMBUS_STATUS_BUSY: + result = -EBUSY; + goto out; + case ACPI_SMBUS_STATUS_TIMEOUT: + result = -ETIMEDOUT; + goto out; + case ACPI_SMBUS_STATUS_DNAK: + result = -ENXIO; + goto out; + default: + result = -EIO; + goto out; + } + + if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK) + goto out; + + obj = pkg->package.elements + 1; + if (obj->type != ACPI_TYPE_INTEGER) { + acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); + result = -EIO; + goto out; + } + + len = obj->integer.value; + obj = pkg->package.elements + 2; + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + if (obj->type != ACPI_TYPE_INTEGER) { + acpi_handle_err(smbus_cmi->handle, + "Invalid argument type\n"); + result = -EIO; + goto out; + } + if (len == 2) + data->word = obj->integer.value; + else + data->byte = obj->integer.value; + break; + case I2C_SMBUS_BLOCK_DATA: + if (obj->type != ACPI_TYPE_BUFFER) { + acpi_handle_err(smbus_cmi->handle, + "Invalid argument type\n"); + result = -EIO; + goto out; + } + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + data->block[0] = len; + memcpy(data->block + 1, obj->buffer.pointer, len); + break; + } + +out: + kfree(buffer.pointer); + dev_dbg(&adap->dev, "Transaction status: %i\n", result); + return result; +} + +static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter) +{ + struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data; + u32 ret; + + ret = smbus_cmi->cap_read | smbus_cmi->cap_write ? + I2C_FUNC_SMBUS_QUICK : 0; + + ret |= smbus_cmi->cap_read ? + (I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0; + + ret |= smbus_cmi->cap_write ? + (I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0; + + return ret; +} + +static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { + .smbus_xfer = acpi_smbus_cmi_access, + .functionality = acpi_smbus_cmi_func, +}; + + +static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, + const char *name) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_handle *handle = smbus_cmi->handle; + union acpi_object *obj; + acpi_status status; + + if (!strcmp(name, smbus_cmi->methods->mt_info)) { + status = acpi_evaluate_object(smbus_cmi->handle, + smbus_cmi->methods->mt_info, + NULL, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "Failed to evaluate %s: %i\n", + smbus_cmi->methods->mt_info, status); + return -EIO; + } + + obj = buffer.pointer; + if (obj && obj->type == ACPI_TYPE_PACKAGE) + obj = obj->package.elements; + else { + acpi_handle_err(handle, "Invalid argument type\n"); + kfree(buffer.pointer); + return -EIO; + } + + if (obj->type != ACPI_TYPE_INTEGER) { + acpi_handle_err(handle, "Invalid argument type\n"); + kfree(buffer.pointer); + return -EIO; + } else + acpi_handle_debug(handle, "SMBus CMI Version %x\n", + (int)obj->integer.value); + + kfree(buffer.pointer); + smbus_cmi->cap_info = 1; + } else if (!strcmp(name, smbus_cmi->methods->mt_sbr)) + smbus_cmi->cap_read = 1; + else if (!strcmp(name, smbus_cmi->methods->mt_sbw)) + smbus_cmi->cap_write = 1; + else + acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name); + + return 0; +} + +static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_smbus_cmi *smbus_cmi = context; + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status)) + acpi_smbus_cmi_add_cap(smbus_cmi, node_name); + + return AE_OK; +} + +static int smbus_cmi_probe(struct platform_device *device) +{ + struct device *dev = &device->dev; + struct acpi_smbus_cmi *smbus_cmi; + int ret; + + smbus_cmi = kzalloc_obj(struct acpi_smbus_cmi); + if (!smbus_cmi) + return -ENOMEM; + + 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; + + acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, + acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); + + if (smbus_cmi->cap_info == 0) { + ret = -ENODEV; + goto err; + } + + snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), + "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; + smbus_cmi->adapter.dev.parent = &device->dev; + + ret = i2c_add_adapter(&smbus_cmi->adapter); + if (ret) { + dev_err(&device->dev, "Couldn't register adapter!\n"); + goto err; + } + + return 0; + +err: + kfree(smbus_cmi); + return ret; +} + +static void smbus_cmi_remove(struct platform_device *device) +{ + struct acpi_smbus_cmi *smbus_cmi = platform_get_drvdata(device); + + i2c_del_adapter(&smbus_cmi->adapter); + kfree(smbus_cmi); +} + +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_platform_driver(smbus_cmi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Crane Cai "); +MODULE_DESCRIPTION("ACPI SMBus CMI driver"); diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c new file mode 100644 index 000000000..ba0cbb36a --- /dev/null +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -0,0 +1,562 @@ +/* + * I2C bus driver for the SH7760 I2C Interfaces. + * + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss + * + * licensed under the terms outlined in the file COPYING. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* register offsets */ +#define I2CSCR 0x0 /* slave ctrl */ +#define I2CMCR 0x4 /* master ctrl */ +#define I2CSSR 0x8 /* slave status */ +#define I2CMSR 0xC /* master status */ +#define I2CSIER 0x10 /* slave irq enable */ +#define I2CMIER 0x14 /* master irq enable */ +#define I2CCCR 0x18 /* clock dividers */ +#define I2CSAR 0x1c /* slave address */ +#define I2CMAR 0x20 /* master address */ +#define I2CRXTX 0x24 /* data port */ +#define I2CFCR 0x28 /* fifo control */ +#define I2CFSR 0x2C /* fifo status */ +#define I2CFIER 0x30 /* fifo irq enable */ +#define I2CRFDR 0x34 /* rx fifo count */ +#define I2CTFDR 0x38 /* tx fifo count */ + +#define REGSIZE 0x3C + +#define MCR_MDBS 0x80 /* non-fifo mode switch */ +#define MCR_FSCL 0x40 /* override SCL pin */ +#define MCR_FSDA 0x20 /* override SDA pin */ +#define MCR_OBPC 0x10 /* override pins */ +#define MCR_MIE 0x08 /* master if enable */ +#define MCR_TSBE 0x04 +#define MCR_FSB 0x02 /* force stop bit */ +#define MCR_ESG 0x01 /* en startbit gen. */ + +#define MSR_MNR 0x40 /* nack received */ +#define MSR_MAL 0x20 /* arbitration lost */ +#define MSR_MST 0x10 /* sent a stop */ +#define MSR_MDE 0x08 +#define MSR_MDT 0x04 +#define MSR_MDR 0x02 +#define MSR_MAT 0x01 /* slave addr xfer done */ + +#define MIE_MNRE 0x40 /* nack irq en */ +#define MIE_MALE 0x20 /* arblos irq en */ +#define MIE_MSTE 0x10 /* stop irq en */ +#define MIE_MDEE 0x08 +#define MIE_MDTE 0x04 +#define MIE_MDRE 0x02 +#define MIE_MATE 0x01 /* address sent irq en */ + +#define FCR_RFRST 0x02 /* reset rx fifo */ +#define FCR_TFRST 0x01 /* reset tx fifo */ + +#define FSR_TEND 0x04 /* last byte sent */ +#define FSR_RDF 0x02 /* rx fifo trigger */ +#define FSR_TDFE 0x01 /* tx fifo empty */ + +#define FIER_TEIE 0x04 /* tx fifo empty irq en */ +#define FIER_RXIE 0x02 /* rx fifo trig irq en */ +#define FIER_TXIE 0x01 /* tx fifo trig irq en */ + +#define FIFO_SIZE 16 + +struct cami2c { + void __iomem *iobase; + struct i2c_adapter adap; + + /* message processing */ + struct i2c_msg *msg; +#define IDF_SEND 1 +#define IDF_RECV 2 +#define IDF_STOP 4 + int flags; + +#define IDS_DONE 1 +#define IDS_ARBLOST 2 +#define IDS_NACK 4 + int status; + struct completion xfer_done; + + int irq; + struct resource *ioarea; +}; + +static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) +{ + __raw_writel(val, (unsigned long)cam->iobase + reg); +} + +static inline unsigned long IN32(struct cami2c *cam, int reg) +{ + return __raw_readl((unsigned long)cam->iobase + reg); +} + +static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) +{ + struct cami2c *id = ptr; + struct i2c_msg *msg = id->msg; + char *data = msg->buf; + unsigned long msr, fsr, fier, len; + + msr = IN32(id, I2CMSR); + fsr = IN32(id, I2CFSR); + + /* arbitration lost */ + if (msr & MSR_MAL) { + OUT32(id, I2CMCR, 0); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + id->status |= IDS_DONE | IDS_ARBLOST; + goto out; + } + + if (msr & MSR_MNR) { + /* NACK handling is very screwed up. After receiving a + * NAK IRQ one has to wait a bit before writing to any + * registers, or the ctl will lock up. After that delay + * do a normal i2c stop. Then wait at least 1 ms before + * attempting another transfer or ctl will stop working + */ + udelay(100); /* wait or risk ctl hang */ + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + OUT32(id, I2CFIER, 0); + OUT32(id, I2CMIER, MIE_MSTE); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + id->status |= IDS_NACK; + msr &= ~MSR_MAT; + fsr = 0; + /* In some cases the MST bit is also set. */ + } + + /* i2c-stop was sent */ + if (msr & MSR_MST) { + id->status |= IDS_DONE; + goto out; + } + + /* i2c slave addr was sent; set to "normal" operation */ + if (msr & MSR_MAT) + OUT32(id, I2CMCR, MCR_MIE); + + fier = IN32(id, I2CFIER); + + if (fsr & FSR_RDF) { + len = IN32(id, I2CRFDR); + if (msg->len <= len) { + if (id->flags & IDF_STOP) { + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + OUT32(id, I2CFIER, 0); + /* manual says: wait >= 0.5 SCL times */ + udelay(5); + /* next int should be MST */ + } else { + id->status |= IDS_DONE; + /* keep the RDF bit: ctrl holds SCL low + * until the setup for the next i2c_msg + * clears this bit. + */ + fsr &= ~FSR_RDF; + } + } + while (msg->len && len) { + *data++ = IN32(id, I2CRXTX); + msg->len--; + len--; + } + + if (msg->len) { + len = (msg->len >= FIFO_SIZE) ? FIFO_SIZE - 1 + : msg->len - 1; + + OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xf) << 4)); + } + + } else if (id->flags & IDF_SEND) { + if ((fsr & FSR_TEND) && (msg->len < 1)) { + if (id->flags & IDF_STOP) { + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + } else { + id->status |= IDS_DONE; + /* keep the TEND bit: ctl holds SCL low + * until the setup for the next i2c_msg + * clears this bit. + */ + fsr &= ~FSR_TEND; + } + } + if (fsr & FSR_TDFE) { + while (msg->len && (IN32(id, I2CTFDR) < FIFO_SIZE)) { + OUT32(id, I2CRXTX, *data++); + msg->len--; + } + + if (msg->len < 1) { + fier &= ~FIER_TXIE; + OUT32(id, I2CFIER, fier); + } else { + len = (msg->len >= FIFO_SIZE) ? 2 : 0; + OUT32(id, I2CFCR, + FCR_RFRST | ((len & 3) << 2)); + } + } + } +out: + if (id->status & IDS_DONE) { + OUT32(id, I2CMIER, 0); + OUT32(id, I2CFIER, 0); + id->msg = NULL; + complete(&id->xfer_done); + } + /* clear status flags and ctrl resumes work */ + OUT32(id, I2CMSR, ~msr); + OUT32(id, I2CFSR, ~fsr); + OUT32(id, I2CSSR, 0); + + return IRQ_HANDLED; +} + + +/* prepare and start a master receive operation */ +static void sh7760_i2c_mrecv(struct cami2c *id) +{ + int len; + + id->flags |= IDF_RECV; + + /* set the slave addr reg; otherwise rcv wont work! */ + OUT32(id, I2CSAR, 0xfe); + OUT32(id, I2CMAR, (id->msg->addr << 1) | 1); + + /* adjust rx fifo trigger */ + if (id->msg->len >= FIFO_SIZE) + len = FIFO_SIZE - 1; /* trigger at fifo full */ + else + len = id->msg->len - 1; /* trigger before all received */ + + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xF) << 4)); + + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); + OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); + OUT32(id, I2CFIER, FIER_RXIE); +} + +/* prepare and start a master send operation */ +static void sh7760_i2c_msend(struct cami2c *id) +{ + int len; + + id->flags |= IDF_SEND; + + /* set the slave addr reg; otherwise xmit wont work! */ + OUT32(id, I2CSAR, 0xfe); + OUT32(id, I2CMAR, (id->msg->addr << 1) | 0); + + /* adjust tx fifo trigger */ + if (id->msg->len >= FIFO_SIZE) + len = 2; /* trig: 2 bytes left in TX fifo */ + else + len = 0; /* trig: 8 bytes left in TX fifo */ + + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFCR, FCR_RFRST | ((len & 3) << 2)); + + while (id->msg->len && IN32(id, I2CTFDR) < FIFO_SIZE) { + OUT32(id, I2CRXTX, *(id->msg->buf)); + (id->msg->len)--; + (id->msg->buf)++; + } + + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); + OUT32(id, I2CFSR, 0); + OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); + OUT32(id, I2CFIER, FIER_TEIE | (id->msg->len ? FIER_TXIE : 0)); +} + +static inline int sh7760_i2c_busy_check(struct cami2c *id) +{ + return (IN32(id, I2CMCR) & MCR_FSDA); +} + +static int sh7760_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct cami2c *id = adap->algo_data; + int i, retr; + + if (sh7760_i2c_busy_check(id)) { + dev_err(&adap->dev, "sh7760-i2c%d: bus busy!\n", adap->nr); + return -EBUSY; + } + + i = 0; + while (i < num) { + retr = adap->retries; +retry: + id->flags = ((i == (num-1)) ? IDF_STOP : 0); + id->status = 0; + id->msg = msgs; + init_completion(&id->xfer_done); + + if (msgs->flags & I2C_M_RD) + sh7760_i2c_mrecv(id); + else + sh7760_i2c_msend(id); + + wait_for_completion(&id->xfer_done); + + if (id->status == 0) { + num = -EIO; + break; + } + + if (id->status & IDS_NACK) { + /* wait a bit or i2c module stops working */ + mdelay(1); + num = -EREMOTEIO; + break; + } + + if (id->status & IDS_ARBLOST) { + if (retr--) { + mdelay(2); + goto retry; + } + num = -EREMOTEIO; + break; + } + + msgs++; + i++; + } + + id->msg = NULL; + id->flags = 0; + id->status = 0; + + OUT32(id, I2CMCR, 0); + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMIER, 0); + OUT32(id, I2CFIER, 0); + + /* reset slave module registers too: master mode enables slave + * module for receive ops (ack, data). Without this reset, + * eternal bus activity might be reported after NACK / ARBLOST. + */ + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + OUT32(id, I2CSSR, 0); + + return num; +} + +static u32 sh7760_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm sh7760_i2c_algo = { + .xfer = sh7760_i2c_master_xfer, + .functionality = sh7760_i2c_func, +}; + +/* calculate CCR register setting for a desired scl clock. SCL clock is + * derived from I2C module clock (iclk) which in turn is derived from + * peripheral module clock (mclk, usually around 33MHz): + * iclk = mclk/(CDF + 1). iclk must be < 20MHz. + * scl = iclk/(SCGD*8 + 20). + */ +static int calc_CCR(unsigned long scl_hz) +{ + struct clk *mclk; + unsigned long mck, m1, dff, odff, iclk; + signed char cdf, cdfm; + int scgd, scgdm, scgds; + + mclk = clk_get(NULL, "peripheral_clk"); + if (IS_ERR(mclk)) { + return PTR_ERR(mclk); + } else { + mck = mclk->rate; + clk_put(mclk); + } + + odff = scl_hz; + scgdm = cdfm = m1 = 0; + for (cdf = 3; cdf >= 0; cdf--) { + iclk = mck / (1 + cdf); + if (iclk >= 20000000) + continue; + scgds = ((iclk / scl_hz) - 20) >> 3; + for (scgd = scgds; (scgd < 63) && scgd <= scgds + 1; scgd++) { + m1 = iclk / (20 + (scgd << 3)); + dff = abs(scl_hz - m1); + if (dff < odff) { + odff = dff; + cdfm = cdf; + scgdm = scgd; + } + } + } + /* fail if more than 25% off of requested SCL */ + if (odff > (scl_hz >> 2)) + return -EINVAL; + + /* create a CCR register value */ + return ((scgdm << 2) | cdfm); +} + +static int sh7760_i2c_probe(struct platform_device *pdev) +{ + struct sh7760_i2c_platdata *pd; + struct resource *res; + struct cami2c *id; + int ret; + + pd = dev_get_platdata(&pdev->dev); + if (!pd) { + dev_err(&pdev->dev, "no platform_data!\n"); + ret = -ENODEV; + goto out0; + } + + id = kzalloc_obj(*id); + if (!id) { + ret = -ENOMEM; + goto out0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mmio resources\n"); + ret = -ENODEV; + goto out1; + } + + id->ioarea = request_mem_region(res->start, REGSIZE, pdev->name); + if (!id->ioarea) { + dev_err(&pdev->dev, "mmio already reserved\n"); + ret = -EBUSY; + goto out1; + } + + id->iobase = ioremap(res->start, REGSIZE); + if (!id->iobase) { + dev_err(&pdev->dev, "cannot ioremap\n"); + ret = -ENODEV; + goto out2; + } + + 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; + id->adap.retries = 3; + id->adap.algo_data = id; + id->adap.dev.parent = &pdev->dev; + snprintf(id->adap.name, sizeof(id->adap.name), + "SH7760 I2C at %08lx", (unsigned long)res->start); + + OUT32(id, I2CMCR, 0); + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMIER, 0); + OUT32(id, I2CMAR, 0); + OUT32(id, I2CSIER, 0); + OUT32(id, I2CSAR, 0); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSSR, 0); + OUT32(id, I2CFIER, 0); + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFSR, 0); + + ret = calc_CCR(pd->speed_khz * 1000); + if (ret < 0) { + dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", + pd->speed_khz); + goto out3; + } + OUT32(id, I2CCCR, ret); + + if (request_irq(id->irq, sh7760_i2c_irq, 0, + SH7760_I2C_DEVNAME, id)) { + dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); + ret = -EBUSY; + goto out3; + } + + ret = i2c_add_numbered_adapter(&id->adap); + if (ret < 0) + goto out4; + + platform_set_drvdata(pdev, id); + + dev_info(&pdev->dev, "%d kHz mmio %08x irq %d\n", + pd->speed_khz, res->start, id->irq); + + return 0; + +out4: + free_irq(id->irq, id); +out3: + iounmap(id->iobase); +out2: + release_resource(id->ioarea); + kfree(id->ioarea); +out1: + kfree(id); +out0: + return ret; +} + +static void sh7760_i2c_remove(struct platform_device *pdev) +{ + struct cami2c *id = platform_get_drvdata(pdev); + + i2c_del_adapter(&id->adap); + free_irq(id->irq, id); + iounmap(id->iobase); + release_resource(id->ioarea); + kfree(id->ioarea); + kfree(id); +} + +static struct platform_driver sh7760_i2c_drv = { + .driver = { + .name = SH7760_I2C_DEVNAME, + }, + .probe = sh7760_i2c_probe, + .remove = sh7760_i2c_remove, +}; + +module_platform_driver(sh7760_i2c_drv); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SH7760 I2C bus driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c new file mode 100644 index 000000000..dae8967f8 --- /dev/null +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SuperH Mobile I2C Controller + * + * Copyright (C) 2014-19 Wolfram Sang + * Copyright (C) 2008 Magnus Damm + * + * Portions of the code based on out-of-tree driver i2c-sh7343.c + * Copyright (c) 2006 Carlos Munoz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Transmit operation: */ +/* */ +/* 0 byte transmit */ +/* BUS: S A8 ACK P(*) */ +/* IRQ: DTE WAIT */ +/* ICIC: */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 */ +/* */ +/* 1 byte transmit */ +/* BUS: S A8 ACK D8(1) ACK P(*) */ +/* IRQ: DTE WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) */ +/* */ +/* 2 byte transmit */ +/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */ +/* IRQ: DTE WAIT WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) D8(2) */ +/* */ +/* 3 bytes or more, +---------+ gets repeated */ +/* */ +/* */ +/* Receive operation: */ +/* */ +/* 0 byte receive - not supported since slave may hold SDA low */ +/* */ +/* 1 byte receive [TX] | [RX] */ +/* BUS: S A8 ACK | D8(1) ACK P(*) */ +/* IRQ: DTE WAIT | WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) */ +/* */ +/* 2 byte receive [TX]| [RX] */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P(*) */ +/* IRQ: DTE WAIT | WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) */ +/* */ +/* 3 byte receive [TX] | [RX] (*) */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */ +/* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) D8(3) */ +/* */ +/* 4 bytes or more, this part is repeated +---------+ */ +/* */ +/* */ +/* Interrupt order and BUSY flag */ +/* ___ _ */ +/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ +/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ +/* */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P(*) */ +/* ___ */ +/* WAIT IRQ ________________________________/ \___________ */ +/* TACK IRQ ____________________________________/ \_______ */ +/* DTE IRQ __________________________________________/ \_ */ +/* AL IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +/* _______________________________________________ */ +/* BUSY __/ \_ */ +/* */ +/* (*) The STOP condition is only sent by the master at the end of the last */ +/* I2C message or if the I2C_M_STOP flag is set. Similarly, the BUSY bit is */ +/* only cleared after the STOP condition, so, between messages we have to */ +/* poll for the DTE bit. */ +/* */ + +enum sh_mobile_i2c_op { + OP_START = 0, + OP_TX_FIRST, + OP_TX, + OP_TX_STOP, + OP_TX_TO_RX, + OP_RX, + OP_RX_STOP, + OP_RX_STOP_DATA, +}; + +struct sh_mobile_i2c_data { + struct device *dev; + void __iomem *reg; + struct i2c_adapter adap; + unsigned long bus_speed; + unsigned int clks_per_count; + struct clk *clk; + u_int8_t icic; + u_int8_t flags; + u_int16_t iccl; + u_int16_t icch; + + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + int pos; + int sr; + bool send_stop; + bool stop_after_dma; + bool atomic_xfer; + + struct resource *res; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; + struct scatterlist sg; + enum dma_data_direction dma_direction; + u8 *dma_buf; +}; + +struct sh_mobile_dt_config { + int clks_per_count; + int (*setup)(struct sh_mobile_i2c_data *pd); +}; + +#define IIC_FLAG_HAS_ICIC67 (1 << 0) + +/* Register offsets */ +#define ICDR 0x00 +#define ICCR 0x04 +#define ICSR 0x08 +#define ICIC 0x0c +#define ICCL 0x10 +#define ICCH 0x14 +#define ICSTART 0x70 + +/* Register bits */ +#define ICCR_ICE 0x80 +#define ICCR_RACK 0x40 +#define ICCR_TRS 0x10 +#define ICCR_BBSY 0x04 +#define ICCR_SCP 0x01 + +#define ICSR_SCLM 0x80 +#define ICSR_SDAM 0x40 +#define SW_DONE 0x20 +#define ICSR_BUSY 0x10 +#define ICSR_AL 0x08 +#define ICSR_TACK 0x04 +#define ICSR_WAIT 0x02 +#define ICSR_DTE 0x01 + +#define ICIC_ICCLB8 0x80 +#define ICIC_ICCHB8 0x40 +#define ICIC_TDMAE 0x20 +#define ICIC_RDMAE 0x10 +#define ICIC_ALE 0x08 +#define ICIC_TACKE 0x04 +#define ICIC_WAITE 0x02 +#define ICIC_DTEE 0x01 + +#define ICSTART_ICSTART 0x10 + +static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data) +{ + if (offs == ICIC) + data |= pd->icic; + + iowrite8(data, pd->reg + offs); +} + +static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs) +{ + return ioread8(pd->reg + offs); +} + +static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs, + unsigned char set, unsigned char clr) +{ + iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr); +} + +static u32 sh_mobile_i2c_iccl(unsigned long count_khz, u32 tLOW, u32 tf) +{ + /* + * Conditional expression: + * ICCL >= COUNT_CLK * (tLOW + tf) + * + * SH-Mobile IIC hardware starts counting the LOW period of + * the SCL signal (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return (((count_khz * (tLOW + tf)) + 5000) / 10000); +} + +static u32 sh_mobile_i2c_icch(unsigned long count_khz, u32 tHIGH, u32 tf) +{ + /* + * Conditional expression: + * ICCH >= COUNT_CLK * (tHIGH + tf) + * + * SH-Mobile IIC hardware is aware of SCL transition period 'tr', + * and can ignore it. SH-Mobile IIC controller starts counting + * the HIGH period of the SCL signal (tHIGH) after the SCL input + * voltage increases at VIH. + * + * Afterward it turned out calculating ICCH using only tHIGH spec + * will result in violation of the tHD;STA timing spec. We need + * to take into account the fall time of SDA signal (tf) at START + * condition, in order to meet both tHIGH and tHD;STA specs. + */ + return (((count_khz * (tHIGH + tf)) + 5000) / 10000); +} + +static int sh_mobile_i2c_check_timing(struct sh_mobile_i2c_data *pd) +{ + u16 max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff; + + if (pd->iccl > max_val || pd->icch > max_val) { + dev_err(pd->dev, "timing values out of range: L/H=0x%x/0x%x\n", + pd->iccl, pd->icch); + return -EINVAL; + } + + /* one more bit of ICCL in ICIC */ + if (pd->iccl & 0x100) + pd->icic |= ICIC_ICCLB8; + else + pd->icic &= ~ICIC_ICCLB8; + + /* one more bit of ICCH in ICIC */ + if (pd->icch & 0x100) + pd->icic |= ICIC_ICCHB8; + else + pd->icic &= ~ICIC_ICCHB8; + + dev_dbg(pd->dev, "timing values: L/H=0x%x/0x%x\n", pd->iccl, pd->icch); + return 0; +} + +static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd) +{ + unsigned long i2c_clk_khz; + u32 tHIGH, tLOW, tf; + + i2c_clk_khz = clk_get_rate(pd->clk) / 1000 / pd->clks_per_count; + + 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 == 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 */ + } else { + dev_err(pd->dev, "unrecognized bus speed %lu Hz\n", + pd->bus_speed); + return -EINVAL; + } + + pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf); + pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf); + + return sh_mobile_i2c_check_timing(pd); +} + +static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd) +{ + unsigned long clks_per_cycle; + + /* L = 5, H = 4, L + H = 9 */ + clks_per_cycle = clk_get_rate(pd->clk) / pd->bus_speed; + pd->iccl = DIV_ROUND_UP(clks_per_cycle * 5 / 9 - 1, pd->clks_per_count); + pd->icch = DIV_ROUND_UP(clks_per_cycle * 4 / 9 - 5, pd->clks_per_count); + + 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 ret = 0; + unsigned long flags; + + dev_dbg(pd->dev, "op %d\n", op); + + spin_lock_irqsave(&pd->lock, flags); + + switch (op) { + 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 client address */ + iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg)); + break; + case OP_TX: /* write 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 + : ICCR_ICE | ICCR_TRS | ICCR_BBSY); + break; + case OP_TX_TO_RX: /* select read mode */ + iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP); + break; + case OP_RX: /* just read data */ + ret = iic_rd(pd, ICDR); + break; + case OP_RX_STOP: /* enable DTE interrupt, issue stop */ + 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 */ + 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; + } + + spin_unlock_irqrestore(&pd->lock, flags); + + dev_dbg(pd->dev, "op %d, data out 0x%02x\n", op, ret); + return ret; +} + +static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) +{ + if (pd->pos == pd->msg->len) { + i2c_op(pd, OP_TX_STOP); + return 1; + } + + if (pd->pos == -1) + i2c_op(pd, OP_TX_FIRST); + else + i2c_op(pd, OP_TX); + + pd->pos++; + return 0; +} + +static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) +{ + int real_pos; + + /* 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) + 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); +} + +static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) +{ + struct sh_mobile_i2c_data *pd = dev_id; + unsigned char sr; + int wakeup = 0; + + sr = iic_rd(pd, ICSR); + pd->sr |= sr; /* remember state */ + + dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr, + str_read_write(pd->msg->flags & I2C_M_RD), + pd->pos, pd->msg->len); + + /* Kick off TxDMA after preface was done */ + if (pd->dma_direction == DMA_TO_DEVICE && pd->pos == 0) + iic_set_clr(pd, ICIC, ICIC_TDMAE, 0); + else if (sr & (ICSR_AL | ICSR_TACK)) + /* don't interrupt transaction - continue to issue stop */ + iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK)); + else if (pd->msg->flags & I2C_M_RD) + wakeup = sh_mobile_i2c_isr_rx(pd); + else + wakeup = sh_mobile_i2c_isr_tx(pd); + + /* Kick off RxDMA after preface was done */ + if (pd->dma_direction == DMA_FROM_DEVICE && pd->pos == 1) + iic_set_clr(pd, ICIC, ICIC_RDMAE, 0); + + if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */ + iic_wr(pd, ICSR, sr & ~ICSR_WAIT); + + if (wakeup) { + pd->sr |= SW_DONE; + if (!pd->atomic_xfer) + wake_up(&pd->wait); + } + + /* defeat write posting to avoid spurious WAIT interrupts */ + iic_rd(pd, ICSR); + + return IRQ_HANDLED; +} + +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_dma_callback(void *data) +{ + struct sh_mobile_i2c_data *pd = data; + + sh_mobile_i2c_cleanup_dma(pd, false); + pd->pos = pd->msg->len; + pd->stop_after_dma = true; + + iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); +} + +static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, dma_addr_t port_addr) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + 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)); + return chan; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return chan; +} + +static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) +{ + bool read = pd->msg->flags & I2C_M_RD; + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct dma_chan *chan = read ? pd->dma_rx : pd->dma_tx; + struct dma_async_tx_descriptor *txdesc; + dma_addr_t dma_addr; + dma_cookie_t cookie; + + if (PTR_ERR(chan) == -EPROBE_DEFER) { + if (read) + chan = pd->dma_rx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, + pd->res->start + ICDR); + else + chan = pd->dma_tx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, + pd->res->start + ICDR); + } + + if (IS_ERR(chan)) + return; + + dma_addr = dma_map_single(chan->device->dev, pd->dma_buf, pd->msg->len, dir); + if (dma_mapping_error(chan->device->dev, dma_addr)) { + dev_dbg(pd->dev, "dma map failed, using PIO\n"); + return; + } + + sg_dma_len(&pd->sg) = pd->msg->len; + sg_dma_address(&pd->sg) = dma_addr; + + pd->dma_direction = dir; + + txdesc = dmaengine_prep_slave_sg(chan, &pd->sg, 1, + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, + 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, false); + return; + } + + txdesc->callback = sh_mobile_i2c_dma_callback; + txdesc->callback_param = 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, false); + return; + } + + dma_async_issue_pending(chan); +} + +static void start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, + bool do_init) +{ + if (do_init) { + /* Initialize channel registers */ + iic_wr(pd, ICCR, ICCR_SCP); + + /* Enable channel and configure rx ack */ + iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP); + + /* Set the clock */ + iic_wr(pd, ICCL, pd->iccl & 0xff); + iic_wr(pd, ICCH, pd->icch & 0xff); + } + + pd->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); + + /* Enable all interrupts to begin with */ + iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); +} + +static int poll_dte(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + if (val & ICSR_DTE) + break; + + if (val & ICSR_TACK) + return -ENXIO; + + udelay(10); + } + + return i ? 0 : -ETIMEDOUT; +} + +static int poll_busy(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); + + /* the interrupt handler may wake us up before the + * transfer is finished, so poll the hardware + * until we're done. + */ + if (!(val & ICSR_BUSY)) { + /* handle missing acknowledge and arbitration lost */ + val |= pd->sr; + if (val & ICSR_TACK) + return -ENXIO; + if (val & ICSR_AL) + return -EAGAIN; + break; + } + + udelay(10); + } + + return i ? 0 : -ETIMEDOUT; +} + +static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd, + struct i2c_msg *msgs, int num) +{ + struct i2c_msg *msg; + int err = 0; + int i; + long time_left; + + /* Wake up device and enable clock */ + pm_runtime_get_sync(pd->dev); + + /* Process all messages */ + for (i = 0; i < num; i++) { + bool do_start = pd->send_stop || !i; + msg = &msgs[i]; + pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP; + pd->stop_after_dma = false; + + start_ch(pd, msg, do_start); + + if (do_start) + 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 (!time_left) { + if (pd->dma_direction != DMA_NONE) + sh_mobile_i2c_cleanup_dma(pd, true); + + err = -ETIMEDOUT; + break; + } + + if (pd->send_stop) + err = poll_busy(pd); + else + err = poll_dte(pd); + if (err < 0) + break; + } + + /* Disable channel */ + iic_wr(pd, ICCR, ICCR_SCP); + + /* Disable clock and mark device as idle */ + pm_runtime_put_sync(pd->dev); + + 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, + .xfer = sh_mobile_i2c_xfer, + .xfer_atomic = sh_mobile_i2c_xfer_atomic, +}; + +static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN_READ, +}; + +/* + * 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) +{ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); + iic_rd(pd, ICCR); /* dummy read */ + + iic_set_clr(pd, ICSTART, ICSTART_ICSTART, 0); + iic_rd(pd, ICSTART); /* dummy read */ + + udelay(10); + + iic_wr(pd, ICCR, ICCR_SCP); + iic_wr(pd, ICSTART, 0); + + udelay(10); + + iic_wr(pd, ICCR, ICCR_TRS); + udelay(10); + iic_wr(pd, ICCR, 0); + udelay(10); + iic_wr(pd, ICCR, ICCR_TRS); + udelay(10); + + return sh_mobile_i2c_v2_init(pd); +} + +static const struct sh_mobile_dt_config default_dt_config = { + .clks_per_count = 1, + .setup = sh_mobile_i2c_init, +}; + +static const struct sh_mobile_dt_config fast_clock_dt_config = { + .clks_per_count = 2, + .setup = sh_mobile_i2c_v2_init, +}; + +static const struct sh_mobile_dt_config r8a7740_dt_config = { + .clks_per_count = 1, + .setup = sh_mobile_i2c_r8a7740_workaround, +}; + +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 = &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,iic-r8a7795", .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 }, + {}, +}; +MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids); + +static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd) +{ + if (!IS_ERR(pd->dma_tx)) { + dma_release_channel(pd->dma_tx); + pd->dma_tx = ERR_PTR(-EPROBE_DEFER); + } + + if (!IS_ERR(pd->dma_rx)) { + dma_release_channel(pd->dma_rx); + pd->dma_rx = ERR_PTR(-EPROBE_DEFER); + } +} + +static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd) +{ + struct device_node *np = dev_of_node(&dev->dev); + int k = 0, ret; + + 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 %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++; + } + } + + return k > 0 ? 0 : -ENOENT; +} + +static int sh_mobile_i2c_probe(struct platform_device *dev) +{ + struct sh_mobile_i2c_data *pd; + struct i2c_adapter *adap; + const struct sh_mobile_dt_config *config; + int ret; + u32 bus_speed; + + pd = devm_kzalloc(&dev->dev, sizeof(struct sh_mobile_i2c_data), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(pd->clk)) { + dev_err(&dev->dev, "cannot get clock\n"); + return PTR_ERR(pd->clk); + } + + ret = sh_mobile_i2c_hook_irqs(dev, pd); + if (ret) + return ret; + + pd->dev = &dev->dev; + platform_set_drvdata(dev, pd); + + 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) ? I2C_MAX_STANDARD_MODE_FREQ : bus_speed; + pd->clks_per_count = 1; + + /* Newer variants come with two new bits in ICIC */ + if (resource_size(pd->res) > 0x17) + pd->flags |= IIC_FLAG_HAS_ICIC67; + + pm_runtime_enable(&dev->dev); + pm_runtime_get_sync(&dev->dev); + + config = of_device_get_match_data(&dev->dev); + if (config) { + pd->clks_per_count = config->clks_per_count; + ret = config->setup(pd); + } else { + ret = sh_mobile_i2c_init(pd); + } + + pm_runtime_put_sync(&dev->dev); + if (ret) + return ret; + + /* Init DMA */ + sg_init_table(&pd->sg, 1); + pd->dma_direction = DMA_NONE; + pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER); + + /* setup the private data */ + adap = &pd->adap; + i2c_set_adapdata(adap, pd); + + adap->owner = THIS_MODULE; + adap->algo = &sh_mobile_i2c_algorithm; + adap->quirks = &sh_mobile_i2c_quirks; + adap->dev.parent = &dev->dev; + adap->retries = 5; + adap->nr = dev->id; + adap->dev.of_node = dev->dev.of_node; + + strscpy(adap->name, dev->name, sizeof(adap->name)); + + spin_lock_init(&pd->lock); + init_waitqueue_head(&pd->wait); + + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) { + sh_mobile_i2c_release_dma(pd); + return ret; + } + + dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz\n", adap->nr, pd->bus_speed); + + return 0; +} + +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, +}; + +static int __init sh_mobile_i2c_adap_init(void) +{ + return platform_driver_register(&sh_mobile_i2c_driver); +} +subsys_initcall(sh_mobile_i2c_adap_init); + +static void __exit sh_mobile_i2c_adap_exit(void) +{ + platform_driver_unregister(&sh_mobile_i2c_driver); +} +module_exit(sh_mobile_i2c_adap_exit); + +MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); +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 new file mode 100644 index 000000000..49f8f4f1b --- /dev/null +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -0,0 +1,186 @@ +// 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 + */ + +#include +#include +#include +#include +#include +#include +#include + + +struct i2c_algo_sibyte_data { + void *data; /* private data */ + int bus; /* which bus */ + void *reg_base; /* CSR base */ +}; + +/* ----- global defines ----------------------------------------------- */ +#define SMB_CSR(a,r) ((long)(a->reg_base + r)) + + +static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + int data_bytes = 0; + int error; + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + switch (size) { + case I2C_SMBUS_QUICK: + csr_out32((V_SMB_ADDR(addr) | + (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | + V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_BYTE_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_LB(data->byte), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_WORD_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 2; + } else { + csr_out32(V_SMB_LB(data->word & 0xff), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32(V_SMB_MB(data->word >> 8), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + default: + return -EOPNOTSUPP; + } + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); + if (error & M_SMB_ERROR) { + /* Clear error bit by writing a 1 */ + csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); + return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO; + } + + if (data_bytes == 1) + data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; + if (data_bytes == 2) + data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; + + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm i2c_sibyte_algo = { + .smbus_xfer = smbus_xfer, + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + + /* Register new adapter to i2c module... */ + i2c_adap->algo = &i2c_sibyte_algo; + + /* Set the requested frequency. */ + csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); + csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); + + return i2c_add_numbered_adapter(i2c_adap); +} + + +static struct i2c_algo_sibyte_data sibyte_board_data[2] = { + { NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) }, + { NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) } +}; + +static struct i2c_adapter sibyte_board_adapter[2] = { + { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = NULL, + .algo_data = &sibyte_board_data[0], + .nr = 0, + .name = "SiByte SMBus 0", + }, + { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = NULL, + .algo_data = &sibyte_board_data[1], + .nr = 1, + .name = "SiByte SMBus 1", + }, +}; + +static int __init i2c_sibyte_init(void) +{ + pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n"); + if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0) + return -ENODEV; + if (i2c_sibyte_add_bus(&sibyte_board_adapter[1], + K_SMB_FREQ_400KHZ) < 0) { + i2c_del_adapter(&sibyte_board_adapter[0]); + return -ENODEV; + } + return 0; +} + +static void __exit i2c_sibyte_exit(void) +{ + i2c_del_adapter(&sibyte_board_adapter[0]); + i2c_del_adapter(&sibyte_board_adapter[1]); +} + +module_init(i2c_sibyte_init); +module_exit(i2c_sibyte_exit); + +MODULE_AUTHOR("Kip Walker (Broadcom Corp.)"); +MODULE_AUTHOR("Steven J. Hill "); +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 new file mode 100644 index 000000000..b4ca256a0 --- /dev/null +++ b/drivers/i2c/busses/i2c-simtec.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2005 Simtec Electronics + * Ben Dooks + * + * Simtec Generic I2C Controller +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct simtec_i2c_data { + struct resource *ioarea; + void __iomem *reg; + struct i2c_adapter adap; + struct i2c_algo_bit_data bit; +}; + +#define CMD_SET_SDA (1<<2) +#define CMD_SET_SCL (1<<3) + +#define STATE_SDA (1<<0) +#define STATE_SCL (1<<1) + +/* i2c bit-bus functions */ + +static void simtec_i2c_setsda(void *pw, int state) +{ + struct simtec_i2c_data *pd = pw; + writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg); +} + +static void simtec_i2c_setscl(void *pw, int state) +{ + struct simtec_i2c_data *pd = pw; + writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg); +} + +static int simtec_i2c_getsda(void *pw) +{ + struct simtec_i2c_data *pd = pw; + return readb(pd->reg) & STATE_SDA ? 1 : 0; +} + +static int simtec_i2c_getscl(void *pw) +{ + struct simtec_i2c_data *pd = pw; + return readb(pd->reg) & STATE_SCL ? 1 : 0; +} + +/* device registration */ + +static int simtec_i2c_probe(struct platform_device *dev) +{ + struct simtec_i2c_data *pd; + struct resource *res; + int size; + int ret; + + pd = kzalloc_obj(struct simtec_i2c_data); + if (pd == NULL) + return -ENOMEM; + + platform_set_drvdata(dev, pd); + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&dev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err; + } + + size = resource_size(res); + + pd->ioarea = request_mem_region(res->start, size, dev->name); + if (pd->ioarea == NULL) { + dev_err(&dev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err; + } + + pd->reg = ioremap(res->start, size); + if (pd->reg == NULL) { + dev_err(&dev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_res; + } + + /* setup the private data */ + + pd->adap.owner = THIS_MODULE; + pd->adap.algo_data = &pd->bit; + pd->adap.dev.parent = &dev->dev; + + strscpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); + + pd->bit.data = pd; + pd->bit.setsda = simtec_i2c_setsda; + pd->bit.setscl = simtec_i2c_setscl; + pd->bit.getsda = simtec_i2c_getsda; + pd->bit.getscl = simtec_i2c_getscl; + pd->bit.timeout = HZ; + pd->bit.udelay = 20; + + ret = i2c_bit_add_bus(&pd->adap); + if (ret) + goto err_all; + + return 0; + + err_all: + iounmap(pd->reg); + + err_res: + release_mem_region(pd->ioarea->start, size); + + err: + kfree(pd); + return ret; +} + +static void simtec_i2c_remove(struct platform_device *dev) +{ + struct simtec_i2c_data *pd = platform_get_drvdata(dev); + + i2c_del_adapter(&pd->adap); + + iounmap(pd->reg); + release_mem_region(pd->ioarea->start, resource_size(pd->ioarea)); + kfree(pd); +} + +/* device driver */ + +static struct platform_driver simtec_i2c_driver = { + .driver = { + .name = "simtec-i2c", + }, + .probe = simtec_i2c_probe, + .remove = simtec_i2c_remove, +}; + +module_platform_driver(simtec_i2c_driver); + +MODULE_DESCRIPTION("Simtec Generic I2C Bus driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:simtec-i2c"); diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c new file mode 100644 index 000000000..ca06fffb8 --- /dev/null +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + +*/ + +/* Note: we assume there can only be one SIS5595 with one SMBus interface */ + +/* + Note: all have mfr. ID 0x1039. + SUPPORTED PCI ID + 5595 0008 + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 646 0008 0646 + 648 0008 0648 + 650 0008 0650 + 651 0008 0651 + 730 0008 0730 + 735 0008 0735 + 745 0008 0745 + 746 0008 0746 +*/ + +/* TO DO: + * Add Block Transfers (ugly, but supported by the adapter) + * Add adapter resets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_645, + PCI_DEVICE_ID_SI_646, + PCI_DEVICE_ID_SI_648, + PCI_DEVICE_ID_SI_650, + PCI_DEVICE_ID_SI_651, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_735, + PCI_DEVICE_ID_SI_745, + PCI_DEVICE_ID_SI_746, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but that ID + shows up in other chips so we use the 5511 + ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0, /* terminates the list */ +}; + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* SIS5595 SMBus registers */ +#define SMB_STS_LO 0x00 +#define SMB_STS_HI 0x01 +#define SMB_CTL_LO 0x02 +#define SMB_CTL_HI 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCNT 0x06 +#define SMB_CNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_HAA 0x13 + +/* PCI Address Constants */ +#define SMB_INDEX 0x38 +#define SMB_DAT 0x39 +#define SIS5595_ENABLE_REG 0x40 +#define ACPI_BASE 0x90 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS5595 constants */ +#define SIS5595_QUICK 0x00 +#define SIS5595_BYTE 0x02 +#define SIS5595_BYTE_DATA 0x04 +#define SIS5595_WORD_DATA 0x06 +#define SIS5595_PROC_CALL 0x08 +#define SIS5595_BLOCK_DATA 0x0A + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static u16 force_addr; +module_param_hw(force_addr, ushort, ioport, 0); +MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller"); + +static struct pci_driver sis5595_driver; +static unsigned short sis5595_base; +static struct pci_dev *sis5595_pdev; + +static u8 sis5595_read(u8 reg) +{ + outb(reg, sis5595_base + SMB_INDEX); + return inb(sis5595_base + SMB_DAT); +} + +static void sis5595_write(u8 reg, u8 data) +{ + outb(reg, sis5595_base + SMB_INDEX); + outb(data, sis5595_base + SMB_DAT); +} + +static int sis5595_setup(struct pci_dev *SIS5595_dev) +{ + u16 a; + u8 val; + int *i; + int retval; + + /* Look for imposters */ + for (i = blacklist; *i != 0; i++) { + struct pci_dev *dev; + dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); + if (dev) { + dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); + pci_dev_put(dev); + return -ENODEV; + } + } + + /* Determine the address of the SMBus areas */ + pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); + if (sis5595_base == 0 && force_addr == 0) { + dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if (force_addr) + sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); + dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base); + + /* NB: We grab just the two SMBus registers here, but this may still + * interfere with ACPI :-( */ + retval = acpi_check_region(sis5595_base + SMB_INDEX, 2, + sis5595_driver.name); + if (retval) + return retval; + + if (!request_region(sis5595_base + SMB_INDEX, 2, + sis5595_driver.name)) { + dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n", + sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1); + return -ENODEV; + } + + if (force_addr) { + dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base); + retval = pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base); + if (retval != PCIBIOS_SUCCESSFUL) + goto error; + 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! */ + dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n"); + goto error; + } + } + + 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"); + retval = pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80); + if (retval != PCIBIOS_SUCCESSFUL) + goto error; + 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? */ + dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n"); + goto error; + } + } + + /* Everything is happy */ + return 0; + +error: + release_region(sis5595_base + SMB_INDEX, 2); + return -ENODEV; +} + +static int sis5595_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp); + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { + dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&adap->dev, "Successful!\n"); + } + } + + /* start the transaction by setting bit 4 */ + sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis5595_read(SMB_STS_LO); + } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & 0x10) { + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + result = -ENXIO; + } + + if (temp & 0x20) { + dev_err(&adap->dev, "Bus collision! SMBus may be locked until " + "next hard reset (or not...)\n"); + /* Clock stops and target is stuck in mid-transmission */ + result = -EIO; + } + + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) { + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + } + + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) + dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp); + + return result; +} + +/* Return negative errno on error. */ +static s32 sis5595_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS5595_QUICK; + break; + case I2C_SMBUS_BYTE: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_CMD, command); + size = SIS5595_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_BYTE, data->byte); + size = SIS5595_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis5595_write(SMB_BYTE, data->word & 0xff); + sis5595_write(SMB_BYTE + 1, + (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + sis5595_write(SMB_CTL_LO, ((size & 0x0E))); + + status = sis5595_transaction(adap); + if (status) + return status; + + if ((size != SIS5595_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) + return 0; + + + switch (size) { + case SIS5595_BYTE: + case SIS5595_BYTE_DATA: + data->byte = sis5595_read(SMB_BYTE); + break; + case SIS5595_WORD_DATA: + case SIS5595_PROC_CALL: + data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static u32 sis5595_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; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis5595_access, + .functionality = sis5595_func, +}; + +static struct i2c_adapter sis5595_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id sis5595_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, sis5595_ids); + +static int sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int err; + + if (sis5595_setup(dev)) { + dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + sis5595_adapter.dev.parent = &dev->dev; + + snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name), + "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX); + err = i2c_add_adapter(&sis5595_adapter); + if (err) { + release_region(sis5595_base + SMB_INDEX, 2); + return err; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + sis5595_pdev = pci_dev_get(dev); + return -ENODEV; +} + +static struct pci_driver sis5595_driver = { + .name = "sis5595_smbus", + .id_table = sis5595_ids, + .probe = sis5595_probe, +}; + +static int __init i2c_sis5595_init(void) +{ + return pci_register_driver(&sis5595_driver); +} + +static void __exit i2c_sis5595_exit(void) +{ + pci_unregister_driver(&sis5595_driver); + if (sis5595_pdev) { + i2c_del_adapter(&sis5595_adapter); + release_region(sis5595_base + SMB_INDEX, 2); + pci_dev_put(sis5595_pdev); + sis5595_pdev = NULL; + } +} + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("SIS5595 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_sis5595_init); +module_exit(i2c_sis5595_exit); diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c new file mode 100644 index 000000000..3d0638c2b --- /dev/null +++ b/drivers/i2c/busses/i2c-sis630.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 2002,2003 Alexander Malysh + +*/ + +/* + Status: beta + + Supports: + SIS 630 + SIS 730 + SIS 964 + + Notable differences between chips: + +------------------------+--------------------+-------------------+ + | | SIS630/730 | SIS964 | + +------------------------+--------------------+-------------------+ + | Clock | 14kHz/56kHz | 55.56kHz/27.78kHz | + | SMBus registers offset | 0x80 | 0xE0 | + | SMB_CNT | Bit 1 = Slave Busy | Bit 1 = Bus probe | + | (not used yet) | Bit 3 is reserved | Bit 3 = Last byte | + | SMB_PCOUNT | Offset + 0x06 | Offset + 0x14 | + | SMB_COUNT | 4:0 bits | 5:0 bits | + +------------------------+--------------------+-------------------+ + (Other differences don't affect the functions provided by the driver) + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* SIS964 id is defined here as we are the only file using it */ +#define PCI_DEVICE_ID_SI_964 0x0964 + +/* SIS630/730/964 SMBus registers */ +#define SMB_STS 0x00 /* status */ +#define SMB_CNT 0x02 /* control */ +#define SMBHOST_CNT 0x03 /* host control */ +#define SMB_ADDR 0x04 /* address */ +#define SMB_CMD 0x05 /* command */ +#define SMB_COUNT 0x07 /* byte count */ +#define SMB_BYTE 0x08 /* ~0x8F data byte field */ + +/* SMB_STS register */ +#define BYTE_DONE_STS 0x10 /* Byte Done Status / Block Array */ +#define SMBCOL_STS 0x04 /* Collision */ +#define SMBERR_STS 0x02 /* Device error */ + +/* SMB_CNT register */ +#define MSTO_EN 0x40 /* Host Master Timeout Enable */ +#define SMBCLK_SEL 0x20 /* Host master clock selection */ +#define SMB_PROBE 0x02 /* Bus Probe/Slave busy */ +#define SMB_HOSTBUSY 0x01 /* Host Busy */ + +/* SMBHOST_CNT register */ +#define SMB_KILL 0x20 /* Kill */ +#define SMB_START 0x10 /* Start */ + +/* register count for request_region + * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 + */ +#define SIS630_SMB_IOREGION 20 + +/* PCI address constants */ +/* acpi base address register */ +#define SIS630_ACPI_BASE_REG 0x74 +/* bios control register */ +#define SIS630_BIOS_CTL_REG 0x40 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS630 constants */ +#define SIS630_QUICK 0x00 +#define SIS630_BYTE 0x01 +#define SIS630_BYTE_DATA 0x02 +#define SIS630_WORD_DATA 0x03 +#define SIS630_PCALL 0x04 +#define SIS630_BLOCK_DATA 0x05 + +static struct pci_driver sis630_driver; + +/* insmod parameters */ +static bool high_clock; +static bool force; +module_param(high_clock, bool, 0); +MODULE_PARM_DESC(high_clock, + "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only)."); +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); + +/* SMBus base address */ +static unsigned short smbus_base; + +/* supported chips */ +static int supported[] = { + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_760, + 0 /* terminates the list */ +}; + +static inline u8 sis630_read(u8 reg) +{ + return inb(smbus_base + reg); +} + +static inline void sis630_write(u8 reg, u8 data) +{ + outb(data, smbus_base + reg); +} + +static int sis630_transaction_start(struct i2c_adapter *adap, int size, + u8 *oldclock) +{ + int temp; + + /* Make sure the SMBus host is ready to start transmitting. */ + temp = sis630_read(SMB_CNT); + if ((temp & (SMB_PROBE | SMB_HOSTBUSY)) != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%02x). Resetting...\n", temp); + /* kill smbus transaction */ + sis630_write(SMBHOST_CNT, SMB_KILL); + + temp = sis630_read(SMB_CNT); + if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { + dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&adap->dev, "Successful!\n"); + } + } + + /* save old clock, so we can prevent machine for hung */ + *oldclock = sis630_read(SMB_CNT); + + dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock); + + /* disable timeout interrupt, + * set Host Master Clock to 56KHz if requested */ + if (high_clock) + sis630_write(SMB_CNT, SMBCLK_SEL); + else + sis630_write(SMB_CNT, (*oldclock & ~MSTO_EN)); + + /* clear all sticky bits */ + temp = sis630_read(SMB_STS); + sis630_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size */ + sis630_write(SMBHOST_CNT, SMB_START | (size & 0x07)); + + return 0; +} + +static int sis630_transaction_wait(struct i2c_adapter *adap, int size) +{ + int temp, result = 0, timeout = 0; + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis630_read(SMB_STS); + /* check if block transmitted */ + if (size == SIS630_BLOCK_DATA && (temp & BYTE_DONE_STS)) + break; + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & SMBERR_STS) { + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + result = -ENXIO; + } + + if (temp & SMBCOL_STS) { + dev_err(&adap->dev, "Bus collision!\n"); + result = -EAGAIN; + } + + return result; +} + +static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) +{ + /* clear all status "sticky" bits */ + sis630_write(SMB_STS, 0xFF); + + dev_dbg(&adap->dev, + "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); + + /* + * restore old Host Master Clock if high_clock is set + * and oldclock was not 56KHz + */ + if (high_clock && !(oldclock & SMBCLK_SEL)) + sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL); + + dev_dbg(&adap->dev, + "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); +} + +static int sis630_transaction(struct i2c_adapter *adap, int size) +{ + int result = 0; + u8 oldclock = 0; + + result = sis630_transaction_start(adap, size, &oldclock); + if (!result) { + result = sis630_transaction_wait(adap, size); + sis630_transaction_end(adap, oldclock); + } + + return result; +} + +static int sis630_block_data(struct i2c_adapter *adap, + union i2c_smbus_data *data, int read_write) +{ + int i, len = 0, rc = 0; + u8 oldclock = 0; + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + else if (len > 32) + len = 32; + sis630_write(SMB_COUNT, len); + for (i = 1; i <= len; i++) { + dev_dbg(&adap->dev, + "set data 0x%02x\n", data->block[i]); + /* set data */ + sis630_write(SMB_BYTE + (i - 1) % 8, data->block[i]); + if (i == 8 || (len < 8 && i == len)) { + dev_dbg(&adap->dev, + "start trans len=%d i=%d\n", len, i); + /* first transaction */ + rc = sis630_transaction_start(adap, + SIS630_BLOCK_DATA, &oldclock); + if (rc) + return rc; + } else if ((i - 1) % 8 == 7 || i == len) { + dev_dbg(&adap->dev, + "trans_wait len=%d i=%d\n", len, i); + if (i > 8) { + dev_dbg(&adap->dev, + "clear smbary_sts" + " len=%d i=%d\n", len, i); + /* + If this is not first transaction, + we must clear sticky bit. + clear SMBARY_STS + */ + sis630_write(SMB_STS, BYTE_DONE_STS); + } + rc = sis630_transaction_wait(adap, + SIS630_BLOCK_DATA); + if (rc) { + dev_dbg(&adap->dev, + "trans_wait failed\n"); + break; + } + } + } + } else { + /* read request */ + data->block[0] = len = 0; + rc = sis630_transaction_start(adap, + SIS630_BLOCK_DATA, &oldclock); + if (rc) + return rc; + do { + rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); + if (rc) { + dev_dbg(&adap->dev, "trans_wait failed\n"); + break; + } + /* if this first transaction then read byte count */ + if (len == 0) + data->block[0] = sis630_read(SMB_COUNT); + + /* just to be sure */ + if (data->block[0] > 32) + data->block[0] = 32; + + dev_dbg(&adap->dev, + "block data read len=0x%x\n", data->block[0]); + + for (i = 0; i < 8 && len < data->block[0]; i++, len++) { + dev_dbg(&adap->dev, + "read i=%d len=%d\n", i, len); + data->block[len + 1] = sis630_read(SMB_BYTE + + i); + } + + dev_dbg(&adap->dev, + "clear smbary_sts len=%d i=%d\n", len, i); + + /* clear SMBARY_STS */ + sis630_write(SMB_STS, BYTE_DONE_STS); + } while (len < data->block[0]); + } + + sis630_transaction_end(adap, oldclock); + + return rc; +} + +/* Return negative errno on error. */ +static s32 sis630_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS630_QUICK; + break; + case I2C_SMBUS_BYTE: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_CMD, command); + size = SIS630_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_BYTE, data->byte); + size = SIS630_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis630_write(SMB_BYTE, data->word & 0xff); + sis630_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS630_PCALL : SIS630_WORD_DATA); + break; + case I2C_SMBUS_BLOCK_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + size = SIS630_BLOCK_DATA; + return sis630_block_data(adap, data, read_write); + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + status = sis630_transaction(adap, size); + if (status) + return status; + + if ((size != SIS630_PCALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) { + return 0; + } + + switch (size) { + case SIS630_BYTE: + case SIS630_BYTE_DATA: + data->byte = sis630_read(SMB_BYTE); + break; + case SIS630_PCALL: + case SIS630_WORD_DATA: + data->word = sis630_read(SMB_BYTE) + + (sis630_read(SMB_BYTE + 1) << 8); + break; + } + + return 0; +} + +static u32 sis630_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; +} + +static int sis630_setup(struct pci_dev *sis630_dev) +{ + unsigned char b; + struct pci_dev *dummy = NULL; + int retval, i; + /* acpi base address */ + unsigned short acpi_base; + + /* check for supported SiS devices */ + for (i = 0; supported[i] > 0; i++) { + dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy); + if (dummy) + break; /* found */ + } + + if (dummy) { + pci_dev_put(dummy); + } else if (force) { + dev_err(&sis630_dev->dev, + "WARNING: Can't detect SIS630 compatible device, but " + "loading because of force option enabled\n"); + } else { + return -ENODEV; + } + + /* + Enable ACPI first , so we can accsess reg 74-75 + in acpi io space and read acpi base addr + */ + if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, &b)) { + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't read bios ctl reg\n"); + goto exit; + } + /* if ACPI already enabled , do nothing */ + if (!(b & 0x80) && + pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) { + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't enable ACPI\n"); + goto exit; + } + + /* Determine the ACPI base address */ + if (pci_read_config_word(sis630_dev, + SIS630_ACPI_BASE_REG, &acpi_base)) { + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't determine ACPI base address\n"); + goto exit; + } + + dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04hx\n", acpi_base); + + if (supported[i] == PCI_DEVICE_ID_SI_760) + smbus_base = acpi_base + 0xE0; + else + smbus_base = acpi_base + 0x80; + + dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base); + + retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, + sis630_driver.name); + if (retval) + goto exit; + + /* Everything is happy, let's grab the memory and set things up. */ + if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, + sis630_driver.name)) { + retval = dev_err_probe(&sis630_dev->dev, -EBUSY, + "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); + goto exit; + } + + retval = 0; + +exit: + if (retval) + smbus_base = 0; + return retval; +} + + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis630_access, + .functionality = sis630_func, +}; + +static struct i2c_adapter sis630_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, + .retries = 3 +}; + +static const struct pci_device_id sis630_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) }, + { 0, } +}; + +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)) + return dev_err_probe(&dev->dev, -ENODEV, + "Compatible bus not detected, module not inserted.\n"); + + /* set up the sysfs linkage to our parent device */ + sis630_adapter.dev.parent = &dev->dev; + + snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), + "SMBus SIS630 adapter at %04x", smbus_base + SMB_STS); + + ret = i2c_add_adapter(&sis630_adapter); + if (ret) + goto release_region; + + return 0; + +release_region: + release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION); + return ret; +} + +static void sis630_remove(struct pci_dev *dev) +{ + if (smbus_base) { + i2c_del_adapter(&sis630_adapter); + release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION); + smbus_base = 0; + } +} + + +static struct pci_driver sis630_driver = { + .name = "sis630_smbus", + .id_table = sis630_ids, + .probe = sis630_probe, + .remove = sis630_remove, +}; + +module_pci_driver(sis630_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Malysh "); +MODULE_DESCRIPTION("SIS630 SMBus driver"); diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c new file mode 100644 index 000000000..eee41dc9d --- /dev/null +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 2003 Mark M. Hoffman + +*/ + +/* + This module must be considered BETA unless and until + the chipset manufacturer releases a datasheet. + The register definitions are based on the SiS630. + + This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c) + for just about every machine for which users have reported. + If this module isn't detecting your 96x south bridge, have a + look there. + + We assume there can only be one SiS96x with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* base address register in PCI config space */ +#define SIS96x_BAR 0x04 + +/* SiS96x SMBus registers */ +#define SMB_STS 0x00 +#define SMB_EN 0x01 +#define SMB_CNT 0x02 +#define SMB_HOST_CNT 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCOUNT 0x06 +#define SMB_COUNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV_ADDR 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_SAA 0x13 + +/* register count for request_region */ +#define SMB_IOSIZE 0x20 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SiS96x SMBus constants */ +#define SIS96x_QUICK 0x00 +#define SIS96x_BYTE 0x01 +#define SIS96x_BYTE_DATA 0x02 +#define SIS96x_WORD_DATA 0x03 +#define SIS96x_PROC_CALL 0x04 +#define SIS96x_BLOCK_DATA 0x05 + +static struct pci_driver sis96x_driver; +static struct i2c_adapter sis96x_adapter; +static u16 sis96x_smbus_base; + +static inline u8 sis96x_read(u8 reg) +{ + return inb(sis96x_smbus_base + reg) ; +} + +static inline void sis96x_write(u8 reg, u8 data) +{ + outb(data, sis96x_smbus_base + reg) ; +} + +/* Execute a SMBus transaction. + int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA + */ +static int sis96x_transaction(int size) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size); + + /* Make sure the SMBus host is ready to start transmitting */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + + dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). " + "Resetting...\n", temp); + + /* kill the transaction */ + sis96x_write(SMB_HOST_CNT, 0x20); + + /* check it again */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&sis96x_adapter.dev, "Successful\n"); + } + } + + /* Turn off timeout interrupts, set fast host clock */ + sis96x_write(SMB_CNT, 0x20); + + /* clear all (sticky) status flags */ + temp = sis96x_read(SMB_STS); + sis96x_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size bits */ + sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis96x_read(SMB_STS); + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp); + result = -ETIMEDOUT; + } + + /* device error - probably missing ACK */ + if (temp & 0x02) { + dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n"); + result = -ENXIO; + } + + /* bus collision */ + if (temp & 0x04) { + dev_dbg(&sis96x_adapter.dev, "Bus collision!\n"); + result = -EIO; + } + + /* Finish up by resetting the bus */ + sis96x_write(SMB_STS, temp); + if ((temp = sis96x_read(SMB_STS))) { + dev_dbg(&sis96x_adapter.dev, "Failed reset at " + "end of transaction! (0x%02x)\n", temp); + } + + return result; +} + +/* Return negative errno on error. */ +static s32 sis96x_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS96x_QUICK; + break; + + case I2C_SMBUS_BYTE: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_CMD, command); + size = SIS96x_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_BYTE, data->byte); + size = SIS96x_BYTE_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis96x_write(SMB_BYTE, data->word & 0xff); + sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS96x_PROC_CALL : SIS96x_WORD_DATA); + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + status = sis96x_transaction(size); + if (status) + return status; + + if ((size != SIS96x_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK))) + return 0; + + switch (size) { + case SIS96x_BYTE: + case SIS96x_BYTE_DATA: + data->byte = sis96x_read(SMB_BYTE); + break; + + case SIS96x_WORD_DATA: + case SIS96x_PROC_CALL: + data->word = sis96x_read(SMB_BYTE) + + (sis96x_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static u32 sis96x_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; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis96x_access, + .functionality = sis96x_func, +}; + +static struct i2c_adapter sis96x_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id sis96x_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, sis96x_ids); + +static int sis96x_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 ww = 0; + int retval; + + if (sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EBUSY, "Only one device supported.\n"); + + pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); + if (ww != PCI_CLASS_SERIAL_SMBUS) + return dev_err_probe(&dev->dev, -ENODEV, + "Unsupported device class 0x%04x!\n", ww); + + sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); + if (!sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EINVAL, + "SiS96x SMBus base address not initialized!\n"); + + dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", + sis96x_smbus_base); + + retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]); + if (retval) + return -ENODEV; + + /* Everything is happy, let's grab the memory and set things up. */ + if (!request_region(sis96x_smbus_base, SMB_IOSIZE, + sis96x_driver.name)) { + dev_err_probe(&dev->dev, -EINVAL, + "SMBus registers 0x%04x-0x%04x already in use!\n", + sis96x_smbus_base, sis96x_smbus_base + SMB_IOSIZE - 1); + + sis96x_smbus_base = 0; + return -EINVAL; + } + + /* set up the sysfs linkage to our parent device */ + sis96x_adapter.dev.parent = &dev->dev; + + snprintf(sis96x_adapter.name, sizeof(sis96x_adapter.name), + "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); + + if ((retval = i2c_add_adapter(&sis96x_adapter))) { + dev_err_probe(&dev->dev, retval, "Couldn't register adapter!\n"); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } + + return retval; +} + +static void sis96x_remove(struct pci_dev *dev) +{ + if (sis96x_smbus_base) { + i2c_del_adapter(&sis96x_adapter); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } +} + +static struct pci_driver sis96x_driver = { + .name = "sis96x_smbus", + .id_table = sis96x_ids, + .probe = sis96x_probe, + .remove = sis96x_remove, +}; + +module_pci_driver(sis96x_driver); + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("SiS96x SMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c new file mode 100644 index 000000000..c42925f9a --- /dev/null +++ b/drivers/i2c/busses/i2c-sprd.c @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + * SPDX-License-Identifier: (GPL-2.0+ OR MIT) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_CTL 0x00 +#define I2C_ADDR_CFG 0x04 +#define I2C_COUNT 0x08 +#define I2C_RX 0x0c +#define I2C_TX 0x10 +#define I2C_STATUS 0x14 +#define I2C_HSMODE_CFG 0x18 +#define I2C_VERSION 0x1c +#define ADDR_DVD0 0x20 +#define ADDR_DVD1 0x24 +#define ADDR_STA0_DVD 0x28 +#define ADDR_RST 0x2c + +/* I2C_CTL */ +#define STP_EN BIT(20) +#define FIFO_AF_LVL_MASK GENMASK(19, 16) +#define FIFO_AF_LVL 16 +#define FIFO_AE_LVL_MASK GENMASK(15, 12) +#define FIFO_AE_LVL 12 +#define I2C_DMA_EN BIT(11) +#define FULL_INTEN BIT(10) +#define EMPTY_INTEN BIT(9) +#define I2C_DVD_OPT BIT(8) +#define I2C_OUT_OPT BIT(7) +#define I2C_TRIM_OPT BIT(6) +#define I2C_HS_MODE BIT(4) +#define I2C_MODE BIT(3) +#define I2C_EN BIT(2) +#define I2C_INT_EN BIT(1) +#define I2C_START BIT(0) + +/* I2C_STATUS */ +#define SDA_IN BIT(21) +#define SCL_IN BIT(20) +#define FIFO_FULL BIT(4) +#define FIFO_EMPTY BIT(3) +#define I2C_INT BIT(2) +#define I2C_RX_ACK BIT(1) +#define I2C_BUSY BIT(0) + +/* ADDR_RST */ +#define I2C_RST BIT(0) + +#define I2C_FIFO_DEEP 12 +#define I2C_FIFO_FULL_THLD 15 +#define I2C_FIFO_EMPTY_THLD 4 +#define I2C_DATA_STEP 8 +#define I2C_ADDR_DVD0_CALC(high, low) \ + ((((high) & GENMASK(15, 0)) << 16) | ((low) & GENMASK(15, 0))) +#define I2C_ADDR_DVD1_CALC(high, low) \ + (((high) & GENMASK(31, 16)) | (((low) & GENMASK(31, 16)) >> 16)) + +/* 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 { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct i2c_msg *msg; + struct clk *clk; + u32 src_clk; + u32 bus_freq; + struct completion complete; + u8 *buf; + u32 count; + int irq; + int err; +}; + +static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count) +{ + writel(count, i2c_dev->base + I2C_COUNT); +} + +static void sprd_i2c_send_stop(struct sprd_i2c *i2c_dev, int stop) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (stop) + writel(tmp & ~STP_EN, i2c_dev->base + I2C_CTL); + else + writel(tmp | STP_EN, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp & ~I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_ack(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_RX_ACK, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_clear_irq(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_INT, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_reset_fifo(struct sprd_i2c *i2c_dev) +{ + writel(I2C_RST, i2c_dev->base + ADDR_RST); +} + +static void sprd_i2c_set_devaddr(struct sprd_i2c *i2c_dev, struct i2c_msg *m) +{ + writel(m->addr << 1, i2c_dev->base + I2C_ADDR_CFG); +} + +static void sprd_i2c_write_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + writeb(buf[i], i2c_dev->base + I2C_TX); +} + +static void sprd_i2c_read_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + buf[i] = readb(i2c_dev->base + I2C_RX); +} + +static void sprd_i2c_set_full_thld(struct sprd_i2c *i2c_dev, u32 full_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AF_LVL_MASK; + tmp |= full_thld << FIFO_AF_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_empty_thld(struct sprd_i2c *i2c_dev, u32 empty_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AE_LVL_MASK; + tmp |= empty_thld << FIFO_AE_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_full_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= FULL_INTEN; + else + tmp &= ~FULL_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_empty_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= EMPTY_INTEN; + else + tmp &= ~EMPTY_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_opt_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp | I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_opt_mode(struct sprd_i2c *i2c_dev, int rw) +{ + u32 cmd = readl(i2c_dev->base + I2C_CTL) & ~I2C_MODE; + + writel(cmd | rw << 3, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_data_transfer(struct sprd_i2c *i2c_dev) +{ + u32 i2c_count = i2c_dev->count; + u32 need_tran = i2c_count <= I2C_FIFO_DEEP ? i2c_count : I2C_FIFO_DEEP; + struct i2c_msg *msg = i2c_dev->msg; + + if (msg->flags & I2C_M_RD) { + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, I2C_FIFO_FULL_THLD); + i2c_dev->count -= I2C_FIFO_FULL_THLD; + i2c_dev->buf += I2C_FIFO_FULL_THLD; + + /* + * If the read data count is larger than rx fifo full threshold, + * we should enable the rx fifo full interrupt to read data + * again. + */ + if (i2c_dev->count >= I2C_FIFO_FULL_THLD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + } else { + sprd_i2c_write_bytes(i2c_dev, i2c_dev->buf, need_tran); + i2c_dev->buf += need_tran; + i2c_dev->count -= need_tran; + + /* + * If the write data count is arger than tx fifo depth which + * means we can not write all data in one time, then we should + * enable the tx fifo empty interrupt to write again. + */ + if (i2c_count > I2C_FIFO_DEEP) + sprd_i2c_set_fifo_empty_int(i2c_dev, 1); + } +} + +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; + i2c_dev->count = msg->len; + + reinit_completion(&i2c_dev->complete); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_set_devaddr(i2c_dev, msg); + sprd_i2c_set_count(i2c_dev, msg->len); + + if (msg->flags & I2C_M_RD) { + sprd_i2c_opt_mode(i2c_dev, 1); + sprd_i2c_send_stop(i2c_dev, 1); + } else { + sprd_i2c_opt_mode(i2c_dev, 0); + sprd_i2c_send_stop(i2c_dev, !!is_last_msg); + } + + /* + * We should enable rx fifo full interrupt to get data when receiving + * full data. + */ + if (msg->flags & I2C_M_RD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + else + sprd_i2c_data_transfer(i2c_dev); + + sprd_i2c_opt_start(i2c_dev); + + 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_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct sprd_i2c *i2c_dev = i2c_adap->algo_data; + int im, ret; + + ret = pm_runtime_resume_and_get(i2c_dev->dev); + if (ret < 0) + return ret; + + for (im = 0; im < num - 1; im++) { + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im], 0); + if (ret) + goto err_msg; + } + + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im++], 1); + +err_msg: + pm_runtime_put_autosuspend(i2c_dev->dev); + + return ret < 0 ? ret : im; +} + +static u32 sprd_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sprd_i2c_algo = { + .xfer = sprd_i2c_xfer, + .functionality = sprd_i2c_func, +}; + +static void sprd_i2c_set_clk(struct sprd_i2c *i2c_dev, u32 freq) +{ + u32 apb_clk = i2c_dev->src_clk; + /* + * From I2C databook, the prescale calculation formula: + * prescale = freq_i2c / (4 * freq_scl) - 1; + */ + u32 i2c_dvd = apb_clk / (4 * freq) - 1; + /* + * From I2C databook, the high period of SCL clock is recommended as + * 40% (2/5), and the low period of SCL clock is recommended as 60% + * (3/5), then the formula should be: + * high = (prescale * 2 * 2) / 5 + * low = (prescale * 2 * 3) / 5 + */ + u32 high = ((i2c_dvd << 1) * 2) / 5; + u32 low = ((i2c_dvd << 1) * 3) / 5; + u32 div0 = I2C_ADDR_DVD0_CALC(high, low); + u32 div1 = I2C_ADDR_DVD1_CALC(high, low); + + writel(div0, i2c_dev->base + ADDR_DVD0); + writel(div1, i2c_dev->base + ADDR_DVD1); + + /* Start hold timing = hold time(us) * source clock */ + if (freq == I2C_MAX_FAST_MODE_FREQ) + writel((6 * apb_clk) / 10000000, i2c_dev->base + ADDR_STA0_DVD); + else if (freq == I2C_MAX_STANDARD_MODE_FREQ) + writel((4 * apb_clk) / 1000000, i2c_dev->base + ADDR_STA0_DVD); +} + +static void sprd_i2c_enable(struct sprd_i2c *i2c_dev) +{ + u32 tmp = I2C_DVD_OPT; + + writel(tmp, i2c_dev->base + I2C_CTL); + + sprd_i2c_set_full_thld(i2c_dev, I2C_FIFO_FULL_THLD); + sprd_i2c_set_empty_thld(i2c_dev, I2C_FIFO_EMPTY_THLD); + + sprd_i2c_set_clk(i2c_dev, i2c_dev->bus_freq); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + + tmp = readl(i2c_dev->base + I2C_CTL); + writel(tmp | I2C_EN | I2C_INT_EN, i2c_dev->base + I2C_CTL); +} + +static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_dev->count; + + /* + * 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 target. + */ + if (i2c_tran && ack) { + sprd_i2c_data_transfer(i2c_dev); + return IRQ_HANDLED; + } + + i2c_dev->err = 0; + + /* + * If we did not get one ACK from target when writing data, we should + * return -EIO to notify users. + */ + if (!ack) + i2c_dev->err = -EIO; + else if (msg->flags & I2C_M_RD && i2c_dev->count) + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, i2c_dev->count); + + /* Transmission is done and clear ack and start operation */ + sprd_i2c_clear_ack(i2c_dev); + sprd_i2c_clear_start(i2c_dev); + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +static irqreturn_t sprd_i2c_isr(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_dev->count; + + /* + * 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 written + * done all data, then we can finish this transmission. + * + * When reading data, if conut < rx fifo full threshold, which + * means we can read all data in one time, then we can finish this + * transmission too. + */ + if (!i2c_tran || !ack) { + sprd_i2c_clear_start(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + } + + sprd_i2c_set_fifo_empty_int(i2c_dev, 0); + sprd_i2c_set_fifo_full_int(i2c_dev, 0); + + return IRQ_WAKE_THREAD; +} + +static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev) +{ + struct clk *clk_i2c, *clk_parent; + + clk_i2c = devm_clk_get(i2c_dev->dev, "i2c"); + if (IS_ERR(clk_i2c)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the i2c clock\n", + i2c_dev->adap.nr); + clk_i2c = NULL; + } + + clk_parent = devm_clk_get(i2c_dev->dev, "source"); + if (IS_ERR(clk_parent)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the source clock\n", + i2c_dev->adap.nr); + clk_parent = NULL; + } + + if (clk_set_parent(clk_i2c, clk_parent)) + i2c_dev->src_clk = clk_get_rate(clk_i2c); + else + i2c_dev->src_clk = 26000000; + + dev_dbg(i2c_dev->dev, "i2c%d set source clock is %d\n", + i2c_dev->adap.nr, i2c_dev->src_clk); + + i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable"); + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->clk), + "i2c%d can't get the enable clock\n", + i2c_dev->adap.nr); + + return 0; +} + +static int sprd_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sprd_i2c *i2c_dev; + u32 prop; + int ret; + + pdev->id = of_alias_get_id(dev->of_node, "i2c"); + + i2c_dev = devm_kzalloc(dev, sizeof(struct sprd_i2c), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + 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) + 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 = I2C_MAX_STANDARD_MODE_FREQ; + i2c_dev->adap.owner = THIS_MODULE; + i2c_dev->dev = dev; + i2c_dev->adap.retries = 3; + i2c_dev->adap.algo = &sprd_i2c_algo; + i2c_dev->adap.algo_data = i2c_dev; + i2c_dev->adap.dev.parent = dev; + i2c_dev->adap.nr = pdev->id; + i2c_dev->adap.dev.of_node = dev->of_node; + + if (!of_property_read_u32(dev->of_node, "clock-frequency", &prop)) + i2c_dev->bus_freq = prop; + + /* We only support 100k and 400k now, otherwise will return error. */ + if (i2c_dev->bus_freq != I2C_MAX_STANDARD_MODE_FREQ && + i2c_dev->bus_freq != I2C_MAX_FAST_MODE_FREQ) + return -EINVAL; + + ret = sprd_i2c_clk_init(i2c_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, i2c_dev); + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + pm_runtime_set_autosuspend_delay(i2c_dev->dev, SPRD_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + goto err_rpm_put; + + ret = devm_request_threaded_irq(dev, i2c_dev->irq, + sprd_i2c_isr, sprd_i2c_isr_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) + goto err_rpm_put; + + ret = i2c_add_numbered_adapter(&i2c_dev->adap); + if (ret) { + dev_err_probe(&pdev->dev, ret, "add adapter failed\n"); + goto err_rpm_put; + } + + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; + +err_rpm_put: + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + clk_disable_unprepare(i2c_dev->clk); + return ret; +} + +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) + dev_err(&pdev->dev, "Failed to resume device (%pe)\n", ERR_PTR(ret)); + + i2c_del_adapter(&i2c_dev->adap); + + if (ret >= 0) + clk_disable_unprepare(i2c_dev->clk); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); +} + +static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c_dev->adap); + return pm_runtime_force_suspend(dev); +} + +static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); + + i2c_mark_adapter_resumed(&i2c_dev->adap); + return pm_runtime_force_resume(dev); +} + +static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev) +{ + 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 *dev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + return 0; +} + +static const struct dev_pm_ops sprd_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(sprd_i2c_runtime_suspend, + sprd_i2c_runtime_resume, NULL) + + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sprd_i2c_suspend_noirq, + sprd_i2c_resume_noirq) +}; + +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, + .remove = sprd_i2c_remove, + .driver = { + .name = "sprd-i2c", + .of_match_table = sprd_i2c_of_match, + .pm = &sprd_i2c_pm_ops, + }, +}; + +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 new file mode 100644 index 000000000..0a2b43b55 --- /dev/null +++ b/drivers/i2c/busses/i2c-st.c @@ -0,0 +1,890 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 STMicroelectronics + * + * I2C controller driver, used in STMicroelectronics devices. + * + * Author: Maxime Coquelin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SSC registers */ +#define SSC_BRG 0x000 +#define SSC_TBUF 0x004 +#define SSC_RBUF 0x008 +#define SSC_CTL 0x00C +#define SSC_IEN 0x010 +#define SSC_STA 0x014 +#define SSC_I2C 0x018 +#define SSC_SLAD 0x01C +#define SSC_REP_START_HOLD 0x020 +#define SSC_START_HOLD 0x024 +#define SSC_REP_START_SETUP 0x028 +#define SSC_DATA_SETUP 0x02C +#define SSC_STOP_SETUP 0x030 +#define SSC_BUS_FREE 0x034 +#define SSC_TX_FSTAT 0x038 +#define SSC_RX_FSTAT 0x03C +#define SSC_PRE_SCALER_BRG 0x040 +#define SSC_CLR 0x080 +#define SSC_NOISE_SUPP_WIDTH 0x100 +#define SSC_PRSCALER 0x104 +#define SSC_NOISE_SUPP_WIDTH_DATAOUT 0x108 +#define SSC_PRSCALER_DATAOUT 0x10c + +/* SSC Control */ +#define SSC_CTL_DATA_WIDTH_9 0x8 +#define SSC_CTL_DATA_WIDTH_MSK 0xf +#define SSC_CTL_BM 0xf +#define SSC_CTL_HB BIT(4) +#define SSC_CTL_PH BIT(5) +#define SSC_CTL_PO BIT(6) +#define SSC_CTL_SR BIT(7) +#define SSC_CTL_MS BIT(8) +#define SSC_CTL_EN BIT(9) +#define SSC_CTL_LPB BIT(10) +#define SSC_CTL_EN_TX_FIFO BIT(11) +#define SSC_CTL_EN_RX_FIFO BIT(12) +#define SSC_CTL_EN_CLST_RX BIT(13) + +/* SSC Interrupt Enable */ +#define SSC_IEN_RIEN BIT(0) +#define SSC_IEN_TIEN BIT(1) +#define SSC_IEN_TEEN BIT(2) +#define SSC_IEN_REEN BIT(3) +#define SSC_IEN_PEEN BIT(4) +#define SSC_IEN_AASEN BIT(6) +#define SSC_IEN_STOPEN BIT(7) +#define SSC_IEN_ARBLEN BIT(8) +#define SSC_IEN_NACKEN BIT(10) +#define SSC_IEN_REPSTRTEN BIT(11) +#define SSC_IEN_TX_FIFO_HALF BIT(12) +#define SSC_IEN_RX_FIFO_HALF_FULL BIT(14) + +/* SSC Status */ +#define SSC_STA_RIR BIT(0) +#define SSC_STA_TIR BIT(1) +#define SSC_STA_TE BIT(2) +#define SSC_STA_RE BIT(3) +#define SSC_STA_PE BIT(4) +#define SSC_STA_CLST BIT(5) +#define SSC_STA_AAS BIT(6) +#define SSC_STA_STOP BIT(7) +#define SSC_STA_ARBL BIT(8) +#define SSC_STA_BUSY BIT(9) +#define SSC_STA_NACK BIT(10) +#define SSC_STA_REPSTRT BIT(11) +#define SSC_STA_TX_FIFO_HALF BIT(12) +#define SSC_STA_TX_FIFO_FULL BIT(13) +#define SSC_STA_RX_FIFO_HALF BIT(14) + +/* SSC I2C Control */ +#define SSC_I2C_I2CM BIT(0) +#define SSC_I2C_STRTG BIT(1) +#define SSC_I2C_STOPG BIT(2) +#define SSC_I2C_ACKG BIT(3) +#define SSC_I2C_AD10 BIT(4) +#define SSC_I2C_TXENB BIT(5) +#define SSC_I2C_REPSTRTG BIT(11) +#define SSC_I2C_SLAVE_DISABLE BIT(12) + +/* SSC Tx FIFO Status */ +#define SSC_TX_FSTAT_STATUS 0x07 + +/* SSC Rx FIFO Status */ +#define SSC_RX_FSTAT_STATUS 0x07 + +/* SSC Clear bit operation */ +#define SSC_CLR_SSCAAS BIT(6) +#define SSC_CLR_SSCSTOP BIT(7) +#define SSC_CLR_SSCARBL BIT(8) +#define SSC_CLR_NACK BIT(10) +#define SSC_CLR_REPSTRT BIT(11) + +/* SSC Clock Prescaler */ +#define SSC_PRSC_VALUE 0x0f + + +#define SSC_TXFIFO_SIZE 0x8 +#define SSC_RXFIFO_SIZE 0x8 + +enum st_i2c_mode { + I2C_MODE_STANDARD, + I2C_MODE_FAST, + I2C_MODE_END, +}; + +/** + * struct st_i2c_timings - per-Mode tuning parameters + * @rate: I2C bus rate + * @rep_start_hold: I2C repeated start hold time requirement + * @rep_start_setup: I2C repeated start set up time requirement + * @start_hold: I2C start hold time requirement + * @data_setup_time: I2C data set up time requirement + * @stop_setup_time: I2C stop set up time requirement + * @bus_free_time: I2C bus free time requirement + * @sda_pulse_min_limit: I2C SDA pulse mini width limit + */ +struct st_i2c_timings { + u32 rate; + u32 rep_start_hold; + u32 rep_start_setup; + u32 start_hold; + u32 data_setup_time; + u32 stop_setup_time; + u32 bus_free_time; + u32 sda_pulse_min_limit; +}; + +/** + * struct st_i2c_client - client specific data + * @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 + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct st_i2c_client { + u8 addr; + u32 count; + u32 xfered; + u8 *buf; + int result; + bool stop; +}; + +/** + * struct st_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 + * @irq: interrupt line for th controller + * @clk: hw ssc block clock + * @mode: I2C mode of the controller. Standard or Fast only supported + * @scl_min_width_us: SCL line minimum pulse width in us + * @sda_min_width_us: SDA line minimum pulse width in us + * @client: I2C transfert information + * @busy: I2C transfer on-going + */ +struct st_i2c_dev { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct completion complete; + int irq; + struct clk *clk; + int mode; + u32 scl_min_width_us; + u32 sda_min_width_us; + struct st_i2c_client client; + bool busy; +}; + +static inline void st_i2c_set_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) | mask, reg); +} + +static inline void st_i2c_clr_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) & ~mask, reg); +} + +/* + * From I2C Specifications v0.5. + * + * All the values below have +10% margin added to be + * compatible with some out-of-spec devices, + * like HDMI link of the Toshiba 19AV600 TV. + */ +static struct st_i2c_timings i2c_timings[] = { + [I2C_MODE_STANDARD] = { + .rate = I2C_MAX_STANDARD_MODE_FREQ, + .rep_start_hold = 4400, + .rep_start_setup = 5170, + .start_hold = 4400, + .data_setup_time = 275, + .stop_setup_time = 4400, + .bus_free_time = 5170, + }, + [I2C_MODE_FAST] = { + .rate = I2C_MAX_FAST_MODE_FREQ, + .rep_start_hold = 660, + .rep_start_setup = 660, + .start_hold = 660, + .data_setup_time = 110, + .stop_setup_time = 660, + .bus_free_time = 1430, + }, +}; + +static void st_i2c_flush_rx_fifo(struct st_i2c_dev *i2c_dev) +{ + int count, i; + + /* + * Counter only counts up to 7 but fifo size is 8... + * When fifo is full, counter is 0 and RIR bit of status register is + * set + */ + if (readl_relaxed(i2c_dev->base + SSC_STA) & SSC_STA_RIR) + count = SSC_RXFIFO_SIZE; + else + count = readl_relaxed(i2c_dev->base + SSC_RX_FSTAT) & + SSC_RX_FSTAT_STATUS; + + for (i = 0; i < count; i++) + readl_relaxed(i2c_dev->base + SSC_RBUF); +} + +static void st_i2c_soft_reset(struct st_i2c_dev *i2c_dev) +{ + /* + * FIFO needs to be emptied before reseting the IP, + * else the controller raises a BUSY error. + */ + st_i2c_flush_rx_fifo(i2c_dev); + + st_i2c_set_bits(i2c_dev->base + SSC_CTL, SSC_CTL_SR); + st_i2c_clr_bits(i2c_dev->base + SSC_CTL, SSC_CTL_SR); +} + +/** + * st_i2c_hw_config() - Prepare SSC block, calculate and apply tuning timings + * @i2c_dev: Controller's private data + */ +static void st_i2c_hw_config(struct st_i2c_dev *i2c_dev) +{ + unsigned long rate; + u32 val, ns_per_clk; + struct st_i2c_timings *t = &i2c_timings[i2c_dev->mode]; + + st_i2c_soft_reset(i2c_dev); + + val = SSC_CLR_REPSTRT | SSC_CLR_NACK | SSC_CLR_SSCARBL | + SSC_CLR_SSCAAS | SSC_CLR_SSCSTOP; + writel_relaxed(val, i2c_dev->base + SSC_CLR); + + /* SSC Control register setup */ + val = SSC_CTL_PO | SSC_CTL_PH | SSC_CTL_HB | SSC_CTL_DATA_WIDTH_9; + writel_relaxed(val, i2c_dev->base + SSC_CTL); + + rate = clk_get_rate(i2c_dev->clk); + ns_per_clk = HZ_PER_GHZ / rate; + + /* Baudrate */ + val = rate / (2 * t->rate); + writel_relaxed(val, i2c_dev->base + SSC_BRG); + + /* Pre-scaler baudrate */ + writel_relaxed(1, i2c_dev->base + SSC_PRE_SCALER_BRG); + + /* Enable I2C mode */ + writel_relaxed(SSC_I2C_I2CM, i2c_dev->base + SSC_I2C); + + /* Repeated start hold time */ + val = t->rep_start_hold / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_REP_START_HOLD); + + /* Repeated start set up time */ + val = t->rep_start_setup / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_REP_START_SETUP); + + /* Start hold time */ + val = t->start_hold / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_START_HOLD); + + /* Data set up time */ + val = t->data_setup_time / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_DATA_SETUP); + + /* Stop set up time */ + val = t->stop_setup_time / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_STOP_SETUP); + + /* Bus free time */ + val = t->bus_free_time / ns_per_clk; + writel_relaxed(val, i2c_dev->base + SSC_BUS_FREE); + + /* Prescalers set up */ + val = rate / 10000000; + writel_relaxed(val, i2c_dev->base + SSC_PRSCALER); + writel_relaxed(val, i2c_dev->base + SSC_PRSCALER_DATAOUT); + + /* Noise suppression witdh */ + val = i2c_dev->scl_min_width_us * rate / 100000000; + writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH); + + /* Noise suppression max output data delay width */ + val = i2c_dev->sda_min_width_us * rate / 100000000; + writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH_DATAOUT); +} + +static int st_i2c_recover_bus(struct i2c_adapter *i2c_adap) +{ + struct st_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + u32 ctl; + + dev_dbg(i2c_dev->dev, "Trying to recover bus\n"); + + /* + * SSP IP is dual role SPI/I2C to generate 9 clock pulses + * we switch to SPI node, 9 bit words and write a 0. This + * has been validate with a oscilloscope and is easier + * than switching to GPIO mode. + */ + + /* Disable interrupts */ + writel_relaxed(0, i2c_dev->base + SSC_IEN); + + st_i2c_hw_config(i2c_dev); + + ctl = SSC_CTL_EN | SSC_CTL_MS | SSC_CTL_EN_RX_FIFO | SSC_CTL_EN_TX_FIFO; + st_i2c_set_bits(i2c_dev->base + SSC_CTL, ctl); + + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM); + usleep_range(8000, 10000); + + writel_relaxed(0, i2c_dev->base + SSC_TBUF); + usleep_range(2000, 4000); + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM); + + return 0; +} + +static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev) +{ + u32 sta; + int i, ret; + + for (i = 0; i < 10; i++) { + sta = readl_relaxed(i2c_dev->base + SSC_STA); + if (!(sta & SSC_STA_BUSY)) + return 0; + + usleep_range(2000, 4000); + } + + dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta); + + ret = i2c_recover_bus(&i2c_dev->adap); + if (ret) { + dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret); + return ret; + } + + return -EBUSY; +} + +/** + * st_i2c_write_tx_fifo() - Write a byte in the Tx FIFO + * @i2c_dev: Controller's private data + * @byte: Data to write in the Tx FIFO + */ +static inline void st_i2c_write_tx_fifo(struct st_i2c_dev *i2c_dev, u8 byte) +{ + u16 tbuf = byte << 1; + + writel_relaxed(tbuf | 1, i2c_dev->base + SSC_TBUF); +} + +/** + * st_i2c_wr_fill_tx_fifo() - Fill the Tx FIFO in write mode + * @i2c_dev: Controller's private data + * + * This functions fills the Tx FIFO with I2C transfert buffer when + * in write mode. + */ +static void st_i2c_wr_fill_tx_fifo(struct st_i2c_dev *i2c_dev) +{ + struct st_i2c_client *c = &i2c_dev->client; + u32 tx_fstat, sta; + int i; + + sta = readl_relaxed(i2c_dev->base + SSC_STA); + if (sta & SSC_STA_TX_FIFO_FULL) + return; + + tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT); + tx_fstat &= SSC_TX_FSTAT_STATUS; + + 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, u32 max) +{ + struct st_i2c_client *c = &i2c_dev->client; + u32 tx_fstat, sta; + int i; + + sta = readl_relaxed(i2c_dev->base + SSC_STA); + if (sta & SSC_STA_TX_FIFO_FULL) + return; + + tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT); + tx_fstat &= SSC_TX_FSTAT_STATUS; + + for (i = min(max, SSC_TXFIFO_SIZE - tx_fstat); + i > 0; i--, c->xfered++) + st_i2c_write_tx_fifo(i2c_dev, 0xff); +} + +static void st_i2c_read_rx_fifo(struct st_i2c_dev *i2c_dev) +{ + struct st_i2c_client *c = &i2c_dev->client; + u32 i, sta; + u16 rbuf; + + sta = readl_relaxed(i2c_dev->base + SSC_STA); + if (sta & SSC_STA_RIR) { + i = SSC_RXFIFO_SIZE; + } else { + i = readl_relaxed(i2c_dev->base + SSC_RX_FSTAT); + i &= SSC_RX_FSTAT_STATUS; + } + + for (; (i > 0) && (c->count > 0); i--, c->count--) { + rbuf = readl_relaxed(i2c_dev->base + SSC_RBUF) >> 1; + *c->buf++ = (u8)rbuf & 0xff; + } + + if (i) { + dev_err(i2c_dev->dev, "Unexpected %d bytes in rx fifo\n", i); + st_i2c_flush_rx_fifo(i2c_dev); + } +} + +/** + * st_i2c_terminate_xfer() - Send either STOP or REPSTART condition + * @i2c_dev: Controller's private data + */ +static void st_i2c_terminate_xfer(struct st_i2c_dev *i2c_dev) +{ + struct st_i2c_client *c = &i2c_dev->client; + + st_i2c_clr_bits(i2c_dev->base + SSC_IEN, SSC_IEN_TEEN); + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STRTG); + + if (c->stop) { + st_i2c_set_bits(i2c_dev->base + SSC_IEN, SSC_IEN_STOPEN); + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG); + } else { + st_i2c_set_bits(i2c_dev->base + SSC_IEN, SSC_IEN_REPSTRTEN); + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_REPSTRTG); + } +} + +/** + * st_i2c_handle_write() - Handle FIFO empty interrupt in case of write + * @i2c_dev: Controller's private data + */ +static void st_i2c_handle_write(struct st_i2c_dev *i2c_dev) +{ + struct st_i2c_client *c = &i2c_dev->client; + + st_i2c_flush_rx_fifo(i2c_dev); + + if (!c->count) + /* End of xfer, send stop or repstart */ + st_i2c_terminate_xfer(i2c_dev); + else + st_i2c_wr_fill_tx_fifo(i2c_dev); +} + +/** + * 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) +{ + struct st_i2c_client *c = &i2c_dev->client; + u32 ien; + + /* Trash the address read back */ + if (!c->xfered) { + readl_relaxed(i2c_dev->base + SSC_RBUF); + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_TXENB); + } else { + st_i2c_read_rx_fifo(i2c_dev); + } + + if (!c->count) { + /* End of xfer, send stop or repstart */ + st_i2c_terminate_xfer(i2c_dev); + } else if (c->count == 1) { + /* Penultimate byte to xfer, disable ACK gen. */ + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_ACKG); + + /* Last received byte is to be handled by NACK interrupt */ + ien = SSC_IEN_NACKEN | SSC_IEN_ARBLEN; + writel_relaxed(ien, i2c_dev->base + SSC_IEN); + + st_i2c_rd_fill_tx_fifo(i2c_dev, c->count); + } else { + st_i2c_rd_fill_tx_fifo(i2c_dev, c->count - 1); + } +} + +/** + * st_i2c_isr_thread() - Interrupt routine + * @irq: interrupt number + * @data: Controller's private data + */ +static irqreturn_t st_i2c_isr_thread(int irq, void *data) +{ + struct st_i2c_dev *i2c_dev = data; + struct st_i2c_client *c = &i2c_dev->client; + u32 sta, ien; + int it; + + ien = readl_relaxed(i2c_dev->base + SSC_IEN); + sta = readl_relaxed(i2c_dev->base + SSC_STA); + + /* Use __fls() to check error bits first */ + it = __fls(sta & ien); + if (it < 0) { + dev_dbg(i2c_dev->dev, "spurious it (sta=0x%04x, ien=0x%04x)\n", + sta, ien); + return IRQ_NONE; + } + + switch (1 << it) { + case SSC_STA_TE: + if (c->addr & I2C_M_RD) + st_i2c_handle_read(i2c_dev); + else + st_i2c_handle_write(i2c_dev); + break; + + case SSC_STA_STOP: + case SSC_STA_REPSTRT: + writel_relaxed(0, i2c_dev->base + SSC_IEN); + complete(&i2c_dev->complete); + break; + + case SSC_STA_NACK: + writel_relaxed(SSC_CLR_NACK, i2c_dev->base + SSC_CLR); + + /* Last received byte handled by NACK interrupt */ + if ((c->addr & I2C_M_RD) && (c->count == 1) && (c->xfered)) { + st_i2c_handle_read(i2c_dev); + break; + } + + it = SSC_IEN_STOPEN | SSC_IEN_ARBLEN; + writel_relaxed(it, i2c_dev->base + SSC_IEN); + + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG); + c->result = -EIO; + break; + + case SSC_STA_ARBL: + writel_relaxed(SSC_CLR_SSCARBL, i2c_dev->base + SSC_CLR); + + it = SSC_IEN_STOPEN | SSC_IEN_ARBLEN; + writel_relaxed(it, i2c_dev->base + SSC_IEN); + + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG); + c->result = -EAGAIN; + break; + + default: + dev_err(i2c_dev->dev, + "it %d unhandled (sta=0x%04x)\n", it, sta); + } + + /* + * Read IEN register to ensure interrupt mask write is effective + * before re-enabling interrupt at GIC level, and thus avoid spurious + * interrupts. + */ + readl(i2c_dev->base + SSC_IEN); + + return IRQ_HANDLED; +} + +/** + * st_i2c_xfer_msg() - Transfer a single I2C message + * @i2c_dev: Controller's private data + * @msg: I2C message to transfer + * @is_first: first message of the sequence + * @is_last: last message of the sequence + */ +static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg, + bool is_first, bool is_last) +{ + struct st_i2c_client *c = &i2c_dev->client; + u32 ctl, i2c, it; + unsigned long time_left; + int ret; + + c->addr = i2c_8bit_addr_from_msg(msg); + c->buf = msg->buf; + c->count = msg->len; + c->xfered = 0; + c->result = 0; + c->stop = is_last; + + reinit_completion(&i2c_dev->complete); + + ctl = SSC_CTL_EN | SSC_CTL_MS | SSC_CTL_EN_RX_FIFO | SSC_CTL_EN_TX_FIFO; + st_i2c_set_bits(i2c_dev->base + SSC_CTL, ctl); + + i2c = SSC_I2C_TXENB; + if (c->addr & I2C_M_RD) + i2c |= SSC_I2C_ACKG; + st_i2c_set_bits(i2c_dev->base + SSC_I2C, i2c); + + /* Write target address */ + st_i2c_write_tx_fifo(i2c_dev, c->addr); + + /* Pre-fill Tx fifo with data in case of write */ + if (!(c->addr & I2C_M_RD)) + st_i2c_wr_fill_tx_fifo(i2c_dev); + + it = SSC_IEN_NACKEN | SSC_IEN_TEEN | SSC_IEN_ARBLEN; + writel_relaxed(it, i2c_dev->base + SSC_IEN); + + if (is_first) { + ret = st_i2c_wait_free_bus(i2c_dev); + if (ret) + return ret; + + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STRTG); + } + + time_left = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + ret = c->result; + + if (!time_left) + ret = -ETIMEDOUT; + + i2c = SSC_I2C_STOPG | SSC_I2C_REPSTRTG; + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, i2c); + + writel_relaxed(SSC_CLR_SSCSTOP | SSC_CLR_REPSTRT, + i2c_dev->base + SSC_CLR); + + return ret; +} + +/** + * st_i2c_xfer() - Transfer a single I2C message + * @i2c_adap: Adapter pointer to the controller + * @msgs: Pointer to data to be written. + * @num: Number of messages to be executed + */ +static int st_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct st_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + int ret, i; + + i2c_dev->busy = true; + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(i2c_dev->dev, "Failed to prepare_enable clock\n"); + return ret; + } + + pinctrl_pm_select_default_state(i2c_dev->dev); + + st_i2c_hw_config(i2c_dev); + + for (i = 0; (i < num) && !ret; i++) + ret = st_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0, i == num - 1); + + pinctrl_pm_select_idle_state(i2c_dev->dev); + + clk_disable_unprepare(i2c_dev->clk); + + i2c_dev->busy = false; + + return (ret < 0) ? ret : i; +} + +static int st_i2c_suspend(struct device *dev) +{ + struct st_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + if (i2c_dev->busy) + return -EBUSY; + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int st_i2c_resume(struct device *dev) +{ + pinctrl_pm_select_default_state(dev); + /* Go in idle state if available */ + pinctrl_pm_select_idle_state(dev); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume); + +static u32 st_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm st_i2c_algo = { + .xfer = st_i2c_xfer, + .functionality = st_i2c_func, +}; + +static struct i2c_bus_recovery_info st_i2c_recovery_info = { + .recover_bus = st_i2c_recover_bus, +}; + +static int st_i2c_of_get_deglitch(struct device_node *np, + struct st_i2c_dev *i2c_dev) +{ + int ret; + + ret = of_property_read_u32(np, "st,i2c-min-scl-pulse-width-us", + &i2c_dev->scl_min_width_us); + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-scl-pulse-width-us invalid\n"); + + ret = of_property_read_u32(np, "st,i2c-min-sda-pulse-width-us", + &i2c_dev->sda_min_width_us); + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-sda-pulse-width-us invalid\n"); + + return 0; +} + +static int st_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct st_i2c_dev *i2c_dev; + struct resource *res; + u32 clk_rate; + struct i2c_adapter *adap; + int ret; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &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) + return dev_err_probe(&pdev->dev, -EINVAL, "IRQ missing or invalid\n"); + + i2c_dev->clk = of_clk_get_by_name(np, "ssc"); + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk), + "Unable to request clock\n"); + + i2c_dev->mode = I2C_MODE_STANDARD; + ret = of_property_read_u32(np, "clock-frequency", &clk_rate); + if (!ret && (clk_rate == I2C_MAX_FAST_MODE_FREQ)) + i2c_dev->mode = I2C_MODE_FAST; + + i2c_dev->dev = &pdev->dev; + + ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq, + NULL, st_i2c_isr_thread, + IRQF_ONESHOT, pdev->name, i2c_dev); + if (ret) + return ret; + + pinctrl_pm_select_default_state(i2c_dev->dev); + /* In case idle state available, select it */ + pinctrl_pm_select_idle_state(i2c_dev->dev); + + ret = st_i2c_of_get_deglitch(np, i2c_dev); + if (ret) + return ret; + + adap = &i2c_dev->adap; + i2c_set_adapdata(adap, i2c_dev); + snprintf(adap->name, sizeof(adap->name), "ST I2C(%pa)", &res->start); + adap->owner = THIS_MODULE; + adap->timeout = 2 * HZ; + adap->retries = 0; + adap->algo = &st_i2c_algo; + adap->bus_recovery_info = &st_i2c_recovery_info; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + ret = i2c_add_adapter(adap); + if (ret) + return ret; + + platform_set_drvdata(pdev, i2c_dev); + + dev_info(i2c_dev->dev, "%s initialized\n", adap->name); + + return 0; +} + +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); +} + +static const struct of_device_id st_i2c_match[] = { + { .compatible = "st,comms-ssc-i2c", }, + { .compatible = "st,comms-ssc4-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_i2c_match); + +static struct platform_driver st_i2c_driver = { + .driver = { + .name = "st-i2c", + .of_match_table = st_i2c_match, + .pm = pm_sleep_ptr(&st_i2c_pm), + }, + .probe = st_i2c_probe, + .remove = st_i2c_remove, +}; + +module_platform_driver(st_i2c_driver); + +MODULE_AUTHOR("Maxime Coquelin "); +MODULE_DESCRIPTION("STMicroelectronics I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c new file mode 100644 index 000000000..064e47d6c --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c-stm32.c + * + * Copyright (C) M'boumba Cedric Madianga 2017 + * Author: M'boumba Cedric Madianga + */ + +#include "i2c-stm32.h" + +/* Functions for DMA support */ +struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, + dma_addr_t phy_addr, + u32 txdr_offset, + u32 rxdr_offset) +{ + struct stm32_i2c_dma *dma; + struct dma_slave_config dma_sconfig; + int ret; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return ERR_PTR(-ENOMEM); + + /* Request and configure I2C TX dma channel */ + 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; + } + + memset(&dma_sconfig, 0, sizeof(dma_sconfig)); + dma_sconfig.dst_addr = phy_addr + txdr_offset; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.dst_maxburst = 1; + dma_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); + if (ret < 0) { + dev_err_probe(dev, ret, "can't configure tx channel\n"); + goto fail_tx; + } + + /* Request and configure I2C RX dma channel */ + 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; + } + + memset(&dma_sconfig, 0, sizeof(dma_sconfig)); + dma_sconfig.src_addr = phy_addr + rxdr_offset; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 1; + dma_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); + if (ret < 0) { + dev_err_probe(dev, ret, "can't configure rx channel\n"); + goto fail_rx; + } + + init_completion(&dma->dma_complete); + + 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 dma; + +fail_rx: + dma_release_channel(dma->chan_rx); +fail_tx: + dma_release_channel(dma->chan_tx); +fail_al: + devm_kfree(dev, dma); + + return ERR_PTR(ret); +} + +void stm32_i2c_dma_free(struct stm32_i2c_dma *dma) +{ + dma->dma_buf = 0; + dma->dma_len = 0; + + dma_release_channel(dma->chan_tx); + dma->chan_tx = NULL; + + dma_release_channel(dma->chan_rx); + dma->chan_rx = NULL; + + dma->chan_using = NULL; +} + +int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma, + bool rd_wr, u32 len, u8 *buf, + dma_async_tx_callback callback, + void *dma_async_param) +{ + struct dma_async_tx_descriptor *txdesc; + int ret; + + if (rd_wr) { + dma->chan_using = dma->chan_rx; + dma->dma_transfer_dir = DMA_DEV_TO_MEM; + dma->dma_data_dir = DMA_FROM_DEVICE; + } else { + dma->chan_using = dma->chan_tx; + dma->dma_transfer_dir = DMA_MEM_TO_DEV; + dma->dma_data_dir = DMA_TO_DEVICE; + } + + dma->dma_len = len; + + dma->dma_buf = dma_map_single(dev, buf, dma->dma_len, + dma->dma_data_dir); + if (dma_mapping_error(dev, dma->dma_buf)) { + dev_err(dev, "DMA mapping failed\n"); + return -EINVAL; + } + + txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, + dma->dma_len, + dma->dma_transfer_dir, + DMA_PREP_INTERRUPT); + if (!txdesc) { + dev_err(dev, "Not able to get desc for DMA xfer\n"); + ret = -EINVAL; + goto err; + } + + reinit_completion(&dma->dma_complete); + + txdesc->callback = callback; + txdesc->callback_param = dma_async_param; + ret = dma_submit_error(dmaengine_submit(txdesc)); + if (ret < 0) { + dev_err(dev, "DMA submit failed\n"); + goto err; + } + + dma_async_issue_pending(dma->chan_using); + + return 0; + +err: + 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 new file mode 100644 index 000000000..2c2189390 --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * i2c-stm32.h + * + * Copyright (C) M'boumba Cedric Madianga 2017 + * Copyright (C) STMicroelectronics 2017 + * Author: M'boumba Cedric Madianga + * + */ + +#ifndef _I2C_STM32_H +#define _I2C_STM32_H + +#include +#include +#include + +enum stm32_i2c_speed { + STM32_I2C_SPEED_STANDARD, /* 100 kHz */ + STM32_I2C_SPEED_FAST, /* 400 kHz */ + STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */ + STM32_I2C_SPEED_END, +}; + +/** + * struct stm32_i2c_dma - DMA specific data + * @chan_tx: dma channel for TX transfer + * @chan_rx: dma channel for RX transfer + * @chan_using: dma channel used for the current transfer (TX or RX) + * @dma_buf: dma buffer + * @dma_len: dma buffer len + * @dma_transfer_dir: dma transfer direction indicator + * @dma_data_dir: dma transfer mode indicator + * @dma_complete: dma transfer completion + */ +struct stm32_i2c_dma { + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct dma_chan *chan_using; + dma_addr_t dma_buf; + unsigned int dma_len; + enum dma_transfer_direction dma_transfer_dir; + enum dma_data_direction dma_data_dir; + struct completion dma_complete; +}; + +struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, + dma_addr_t phy_addr, + u32 txdr_offset, u32 rxdr_offset); + +void stm32_i2c_dma_free(struct stm32_i2c_dma *dma); + +int stm32_i2c_prep_dma_xfer(struct device *dev, struct stm32_i2c_dma *dma, + bool rd_wr, u32 len, u8 *buf, + dma_async_tx_callback callback, + void *dma_async_param); + +#endif /* _I2C_STM32_H */ diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c new file mode 100644 index 000000000..579ab29ef --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STMicroelectronics STM32 I2C controller + * + * This I2C controller is described in the STM32F429/439 Soc reference manual. + * Please see below a link to the documentation: + * http://www.st.com/resource/en/reference_manual/DM00031020.pdf + * + * Copyright (C) M'boumba Cedric Madianga 2016 + * Copyright (C) STMicroelectronics 2017 + * Author: M'boumba Cedric Madianga + * + * This driver is based on i2c-st.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-stm32.h" + +/* STM32F4 I2C offset registers */ +#define STM32F4_I2C_CR1 0x00 +#define STM32F4_I2C_CR2 0x04 +#define STM32F4_I2C_DR 0x10 +#define STM32F4_I2C_SR1 0x14 +#define STM32F4_I2C_SR2 0x18 +#define STM32F4_I2C_CCR 0x1C +#define STM32F4_I2C_TRISE 0x20 +#define STM32F4_I2C_FLTR 0x24 + +/* STM32F4 I2C control 1*/ +#define STM32F4_I2C_CR1_POS BIT(11) +#define STM32F4_I2C_CR1_ACK BIT(10) +#define STM32F4_I2C_CR1_STOP BIT(9) +#define STM32F4_I2C_CR1_START BIT(8) +#define STM32F4_I2C_CR1_PE BIT(0) + +/* STM32F4 I2C control 2 */ +#define STM32F4_I2C_CR2_FREQ_MASK GENMASK(5, 0) +#define STM32F4_I2C_CR2_FREQ(n) ((n) & STM32F4_I2C_CR2_FREQ_MASK) +#define STM32F4_I2C_CR2_ITBUFEN BIT(10) +#define STM32F4_I2C_CR2_ITEVTEN BIT(9) +#define STM32F4_I2C_CR2_ITERREN BIT(8) +#define STM32F4_I2C_CR2_IRQ_MASK (STM32F4_I2C_CR2_ITBUFEN | \ + STM32F4_I2C_CR2_ITEVTEN | \ + STM32F4_I2C_CR2_ITERREN) + +/* STM32F4 I2C Status 1 */ +#define STM32F4_I2C_SR1_AF BIT(10) +#define STM32F4_I2C_SR1_ARLO BIT(9) +#define STM32F4_I2C_SR1_BERR BIT(8) +#define STM32F4_I2C_SR1_TXE BIT(7) +#define STM32F4_I2C_SR1_RXNE BIT(6) +#define STM32F4_I2C_SR1_BTF BIT(2) +#define STM32F4_I2C_SR1_ADDR BIT(1) +#define STM32F4_I2C_SR1_SB BIT(0) +#define STM32F4_I2C_SR1_ITEVTEN_MASK (STM32F4_I2C_SR1_BTF | \ + STM32F4_I2C_SR1_ADDR | \ + STM32F4_I2C_SR1_SB) +#define STM32F4_I2C_SR1_ITBUFEN_MASK (STM32F4_I2C_SR1_TXE | \ + STM32F4_I2C_SR1_RXNE) +#define STM32F4_I2C_SR1_ITERREN_MASK (STM32F4_I2C_SR1_AF | \ + STM32F4_I2C_SR1_ARLO | \ + STM32F4_I2C_SR1_BERR) + +/* STM32F4 I2C Status 2 */ +#define STM32F4_I2C_SR2_BUSY BIT(1) + +/* STM32F4 I2C Control Clock */ +#define STM32F4_I2C_CCR_CCR_MASK GENMASK(11, 0) +#define STM32F4_I2C_CCR_CCR(n) ((n) & STM32F4_I2C_CCR_CCR_MASK) +#define STM32F4_I2C_CCR_FS BIT(15) +#define STM32F4_I2C_CCR_DUTY BIT(14) + +/* STM32F4 I2C Trise */ +#define STM32F4_I2C_TRISE_VALUE_MASK GENMASK(5, 0) +#define STM32F4_I2C_TRISE_VALUE(n) ((n) & STM32F4_I2C_TRISE_VALUE_MASK) + +#define STM32F4_I2C_MIN_STANDARD_FREQ 2U +#define STM32F4_I2C_MIN_FAST_FREQ 6U +#define STM32F4_I2C_MAX_FREQ 46U +#define HZ_TO_MHZ 1000000 + +/** + * struct stm32f4_i2c_msg - client specific data + * @addr: 8-bit target addr, including r/w bit + * @count: number of bytes to be transferred + * @buf: data buffer + * @result: result of the transfer + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct stm32f4_i2c_msg { + u8 addr; + u32 count; + u8 *buf; + int result; + bool stop; +}; + +/** + * struct stm32f4_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 or Fast are supported + * @parent_rate: I2C clock parent rate in MHz + * @msg: I2C transfer information + */ +struct stm32f4_i2c_dev { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct completion complete; + struct clk *clk; + int speed; + int parent_rate; + struct stm32f4_i2c_msg msg; +}; + +static inline void stm32f4_i2c_set_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) | mask, reg); +} + +static inline void stm32f4_i2c_clr_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) & ~mask, reg); +} + +static void stm32f4_i2c_disable_irq(struct stm32f4_i2c_dev *i2c_dev) +{ + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_IRQ_MASK); +} + +static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 freq; + u32 cr2 = 0; + + i2c_dev->parent_rate = clk_get_rate(i2c_dev->clk); + freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ); + + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) { + /* + * To reach 100 kHz, the parent clk frequency should be between + * a minimum value of 2 MHz and a maximum value of 46 MHz due + * to hardware limitation + */ + if (freq < STM32F4_I2C_MIN_STANDARD_FREQ || + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for standard mode\n"); + } else { + /* + * To be as close as possible to 400 kHz, the parent clk + * frequency should be between a minimum value of 6 MHz and a + * maximum value of 46 MHz due to hardware limitation + */ + if (freq < STM32F4_I2C_MIN_FAST_FREQ || + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for fast mode\n"); + } + + cr2 |= STM32F4_I2C_CR2_FREQ(freq); + writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2); + + return 0; +} + +static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ); + u32 trise; + + /* + * These bits must be programmed with the maximum SCL rise time given in + * the I2C bus specification, incremented by 1. + * + * In standard mode, the maximum allowed SCL rise time is 1000 ns. + * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to + * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be + * programmed with 0x9. (1000 ns / 125 ns + 1) + * So, for I2C standard mode TRISE = FREQ[5:0] + 1 + * + * In fast mode, the maximum allowed SCL rise time is 300 ns. + * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to + * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be + * programmed with 0x3. (300 ns / 125 ns + 1) + * So, for I2C fast mode TRISE = FREQ[5:0] * 300 / 1000 + 1 + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that parent rate + * is not higher than 46 MHz . As a result trise is at most 4 bits wide + * and so fits into the TRISE bits [5:0]. + */ + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) + trise = freq + 1; + else + trise = freq * 3 / 10 + 1; + + writel_relaxed(STM32F4_I2C_TRISE_VALUE(trise), + i2c_dev->base + STM32F4_I2C_TRISE); +} + +static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 val; + u32 ccr = 0; + + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) { + /* + * 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 * 2) + * + * For example with parent rate = 2 MHz: + * 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 + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that + * 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 / (I2C_MAX_STANDARD_MODE_FREQ * 2); + } else { + /* + * In fast mode, we compute CCR with duty = 0 as with low + * frequencies we are not able to reach 400 kHz. + * In that case: + * t_scl_high = CCR * I2C parent clk period + * t_scl_low = 2 * CCR * I2C parent clk period + * So, CCR = I2C parent rate / (400 kHz * 3) + * + * For example with parent rate = 6 MHz: + * CCR = 6000000 / (400000 * 3) = 5 + * t_scl_high = 5 * (1 / 6000000) = 833 ns > 600 ns + * t_scl_low = 2 * 5 * (1 / 6000000) = 1667 ns > 1300 ns + * t_scl_high + t_scl_low = 2500 ns so 400 kHz is reached + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that + * 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, I2C_MAX_FAST_MODE_FREQ * 3); + + /* Select Fast mode */ + ccr |= STM32F4_I2C_CCR_FS; + } + + ccr |= STM32F4_I2C_CCR_CCR(val); + writel_relaxed(ccr, i2c_dev->base + STM32F4_I2C_CCR); +} + +/** + * stm32f4_i2c_hw_config() - Prepare I2C block + * @i2c_dev: Controller's private data + */ +static int stm32f4_i2c_hw_config(struct stm32f4_i2c_dev *i2c_dev) +{ + int ret; + + ret = stm32f4_i2c_set_periph_clk_freq(i2c_dev); + if (ret) + return ret; + + stm32f4_i2c_set_rise_time(i2c_dev); + + stm32f4_i2c_set_speed_mode(i2c_dev); + + /* Enable I2C */ + writel_relaxed(STM32F4_I2C_CR1_PE, i2c_dev->base + STM32F4_I2C_CR1); + + return 0; +} + +static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F4_I2C_SR2, + status, + !(status & STM32F4_I2C_SR2_BUSY), + 10, 1000); + if (ret) { + dev_dbg(i2c_dev->dev, "bus not free\n"); + ret = -EBUSY; + } + + return ret; +} + +/** + * stm32f4_i2c_write_byte() - Write a byte in the data register + * @i2c_dev: Controller's private data + * @byte: Data to write in the register + */ +static void stm32f4_i2c_write_byte(struct stm32f4_i2c_dev *i2c_dev, u8 byte) +{ + writel_relaxed(byte, i2c_dev->base + STM32F4_I2C_DR); +} + +/** + * stm32f4_i2c_write_msg() - Fill the data register in write mode + * @i2c_dev: Controller's private data + * + * This function fills the data register with I2C transfer buffer + */ +static void stm32f4_i2c_write_msg(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + + stm32f4_i2c_write_byte(i2c_dev, *msg->buf++); + msg->count--; +} + +static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 rbuf; + + rbuf = readl_relaxed(i2c_dev->base + STM32F4_I2C_DR); + *msg->buf++ = rbuf; + msg->count--; +} + +static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg; + + stm32f4_i2c_disable_irq(i2c_dev); + + reg = i2c_dev->base + STM32F4_I2C_CR1; + if (msg->stop) + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + else + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + + complete(&i2c_dev->complete); +} + +/** + * stm32f4_i2c_handle_write() - Handle FIFO empty interrupt in case of write + * @i2c_dev: Controller's private data + */ +static void stm32f4_i2c_handle_write(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + if (msg->count) { + stm32f4_i2c_write_msg(i2c_dev); + if (!msg->count) { + /* + * Disable buffer interrupts for RX not empty and TX + * empty events + */ + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); + } + } else { + stm32f4_i2c_terminate_xfer(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_read() - Handle FIFO empty interrupt in case of read + * @i2c_dev: Controller's private data + * + * This function is called when a new data is received in data register + */ +static void stm32f4_i2c_handle_read(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + switch (msg->count) { + case 1: + stm32f4_i2c_disable_irq(i2c_dev); + stm32f4_i2c_read_msg(i2c_dev); + complete(&i2c_dev->complete); + break; + /* + * For 2-byte reception, 3-byte reception and for Data N-2, N-1 and N + * for N-byte reception with N > 3, we do not have to read the data + * register when RX not empty event occurs as we have to wait for byte + * transferred finished event before reading data. + * So, here we just disable buffer interrupt in order to avoid another + * system preemption due to RX not empty event. + */ + case 2: + case 3: + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); + break; + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + default: + stm32f4_i2c_read_msg(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_rx_done() - Handle byte transfer finished interrupt + * in case of read + * @i2c_dev: Controller's private data + * + * This function is called when a new data is received in the shift register + * but data register has not been read yet. + */ +static void stm32f4_i2c_handle_rx_done(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg; + u32 mask; + int i; + + switch (msg->count) { + case 2: + /* + * In order to correctly send the Stop or Repeated Start + * condition on the I2C bus, the STOP/START bit has to be set + * before reading the last two bytes (data N-1 and N). + * After that, we could read the last two bytes, disable + * remaining interrupts and notify the end of xfer to the + * client + */ + reg = i2c_dev->base + STM32F4_I2C_CR1; + if (msg->stop) + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + else + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + + for (i = 2; i > 0; i--) + stm32f4_i2c_read_msg(i2c_dev); + + reg = i2c_dev->base + STM32F4_I2C_CR2; + mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN; + stm32f4_i2c_clr_bits(reg, mask); + + complete(&i2c_dev->complete); + break; + case 3: + /* + * In order to correctly generate the NACK pulse after the last + * received data byte, we have to enable NACK before reading N-2 + * data + */ + reg = i2c_dev->base + STM32F4_I2C_CR1; + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK); + stm32f4_i2c_read_msg(i2c_dev); + break; + default: + stm32f4_i2c_read_msg(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_rx_addr() - Handle address matched interrupt in case of + * controller receiver + * @i2c_dev: Controller's private data + */ +static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 cr1; + + switch (msg->count) { + case 0: + stm32f4_i2c_terminate_xfer(i2c_dev); + + /* Clear ADDR flag */ + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + case 1: + /* + * Single byte reception: + * Enable NACK and reset POS (Acknowledge position). + * Then, clear ADDR flag and set STOP or RepSTART. + * In that way, the NACK and STOP or RepStart pulses will be + * sent as soon as the byte will be received in shift register + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 &= ~(STM32F4_I2C_CR1_ACK | STM32F4_I2C_CR1_POS); + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + + if (msg->stop) + cr1 |= STM32F4_I2C_CR1_STOP; + else + cr1 |= STM32F4_I2C_CR1_START; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + break; + case 2: + /* + * 2-byte reception: + * Enable NACK, set POS (NACK position) and clear ADDR flag. + * In that way, NACK will be sent for the next byte which will + * be received in the shift register instead of the current + * one. + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 &= ~STM32F4_I2C_CR1_ACK; + cr1 |= STM32F4_I2C_CR1_POS; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + + default: + /* + * N-byte reception: + * 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 + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 |= STM32F4_I2C_CR1_ACK; + cr1 &= ~STM32F4_I2C_CR1_POS; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + } +} + +/** + * stm32f4_i2c_isr_event() - Interrupt routine for I2C bus event + * @irq: interrupt number + * @data: Controller's private data + */ +static irqreturn_t stm32f4_i2c_isr_event(int irq, void *data) +{ + struct stm32f4_i2c_dev *i2c_dev = data; + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 possible_status = STM32F4_I2C_SR1_ITEVTEN_MASK; + u32 status, ien, event, cr2; + + cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2); + ien = cr2 & STM32F4_I2C_CR2_IRQ_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + if (ien & STM32F4_I2C_CR2_ITBUFEN) + possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK; + + status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1); + event = status & possible_status; + if (!event) { + dev_dbg(i2c_dev->dev, + "spurious evt irq (status=0x%08x, ien=0x%08x)\n", + status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & STM32F4_I2C_SR1_SB) + stm32f4_i2c_write_byte(i2c_dev, msg->addr); + + /* I2C Address sent */ + if (event & STM32F4_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + stm32f4_i2c_handle_rx_addr(i2c_dev); + else + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + + /* + * Enable buffer interrupts for RX not empty and TX empty + * events + */ + cr2 |= STM32F4_I2C_CR2_ITBUFEN; + writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2); + } + + /* TX empty */ + if ((event & STM32F4_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + stm32f4_i2c_handle_write(i2c_dev); + + /* RX not empty */ + if ((event & STM32F4_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + stm32f4_i2c_handle_read(i2c_dev); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception : a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & STM32F4_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + stm32f4_i2c_handle_rx_done(i2c_dev); + else + stm32f4_i2c_handle_write(i2c_dev); + } + + return IRQ_HANDLED; +} + +/** + * stm32f4_i2c_isr_error() - Interrupt routine for I2C bus error + * @irq: interrupt number + * @data: Controller's private data + */ +static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data) +{ + struct stm32f4_i2c_dev *i2c_dev = data; + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg; + u32 status; + + status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1); + + /* Arbitration lost */ + if (status & STM32F4_I2C_SR1_ARLO) { + status &= ~STM32F4_I2C_SR1_ARLO; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EAGAIN; + } + + /* + * Acknowledge failure: + * In controller transmitter mode a Stop must be generated by software + */ + if (status & STM32F4_I2C_SR1_AF) { + if (!(msg->addr & I2C_M_RD)) { + reg = i2c_dev->base + STM32F4_I2C_CR1; + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + } + status &= ~STM32F4_I2C_SR1_AF; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EIO; + } + + /* Bus error */ + if (status & STM32F4_I2C_SR1_BERR) { + status &= ~STM32F4_I2C_SR1_BERR; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EIO; + } + + stm32f4_i2c_disable_irq(i2c_dev); + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +/** + * stm32f4_i2c_xfer_msg() - Transfer a single I2C message + * @i2c_dev: Controller's private data + * @msg: I2C message to transfer + * @is_first: first message of the sequence + * @is_last: last message of the sequence + */ +static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev, + struct i2c_msg *msg, bool is_first, + bool is_last) +{ + struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1; + unsigned long time_left; + u32 mask; + int ret; + + f4_msg->addr = i2c_8bit_addr_from_msg(msg); + f4_msg->buf = msg->buf; + f4_msg->count = msg->len; + f4_msg->result = 0; + f4_msg->stop = is_last; + + reinit_completion(&i2c_dev->complete); + + /* Enable events and errors interrupts */ + mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN; + stm32f4_i2c_set_bits(i2c_dev->base + STM32F4_I2C_CR2, mask); + + if (is_first) { + ret = stm32f4_i2c_wait_free_bus(i2c_dev); + if (ret) + return ret; + + /* START generation */ + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + } + + time_left = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + ret = f4_msg->result; + + if (!time_left) + ret = -ETIMEDOUT; + + return ret; +} + +/** + * stm32f4_i2c_xfer() - Transfer combined I2C message + * @i2c_adap: Adapter pointer to the controller + * @msgs: Pointer to data to be written. + * @num: Number of messages to be executed + */ +static int stm32f4_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], + int num) +{ + struct stm32f4_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + int ret, i; + + ret = clk_enable(i2c_dev->clk); + if (ret) { + dev_err(i2c_dev->dev, "Failed to enable clock\n"); + return ret; + } + + for (i = 0; i < num && !ret; i++) + ret = stm32f4_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0, + i == num - 1); + + clk_disable(i2c_dev->clk); + + return (ret < 0) ? ret : num; +} + +static u32 stm32f4_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm stm32f4_i2c_algo = { + .xfer = stm32f4_i2c_xfer, + .functionality = stm32f4_i2c_func, +}; + +static int stm32f4_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct stm32f4_i2c_dev *i2c_dev; + struct resource *res; + u32 irq_event, irq_error, clk_rate; + struct i2c_adapter *adap; + struct reset_control *rst; + int ret; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + irq_event = irq_of_parse_and_map(np, 0); + if (!irq_event) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ event missing or invalid\n"); + + irq_error = irq_of_parse_and_map(np, 1); + if (!irq_error) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ error missing or invalid\n"); + + 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 clock\n"); + + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + 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 >= I2C_MAX_FAST_MODE_FREQ) + i2c_dev->speed = STM32_I2C_SPEED_FAST; + + i2c_dev->dev = &pdev->dev; + + ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0, + pdev->name, i2c_dev); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0, + pdev->name, i2c_dev); + if (ret) + return ret; + + ret = stm32f4_i2c_hw_config(i2c_dev); + if (ret) + return ret; + + adap = &i2c_dev->adap; + i2c_set_adapdata(adap, i2c_dev); + snprintf(adap->name, sizeof(adap->name), "STM32 I2C(%pa)", &res->start); + adap->owner = THIS_MODULE; + adap->timeout = 2 * HZ; + adap->retries = 0; + adap->algo = &stm32f4_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + ret = i2c_add_adapter(adap); + if (ret) + return ret; + + platform_set_drvdata(pdev, i2c_dev); + + clk_disable(i2c_dev->clk); + + dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n"); + + return 0; +} + +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); +} + +static const struct of_device_id stm32f4_i2c_match[] = { + { .compatible = "st,stm32f4-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32f4_i2c_match); + +static struct platform_driver stm32f4_i2c_driver = { + .driver = { + .name = "stm32f4-i2c", + .of_match_table = stm32f4_i2c_match, + }, + .probe = stm32f4_i2c_probe, + .remove = stm32f4_i2c_remove, +}; + +module_platform_driver(stm32f4_i2c_driver); + +MODULE_AUTHOR("M'boumba Cedric Madianga "); +MODULE_DESCRIPTION("STMicroelectronics STM32F4 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c new file mode 100644 index 000000000..6bd281a9e --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -0,0 +1,2529 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STMicroelectronics STM32F7 I2C controller + * + * This I2C controller is described in the STM32F75xxx and STM32F74xxx Soc + * reference manual. + * Please see below a link to the documentation: + * http://www.st.com/resource/en/reference_manual/dm00124865.pdf + * + * Copyright (C) M'boumba Cedric Madianga 2017 + * Copyright (C) STMicroelectronics 2017 + * Author: M'boumba Cedric Madianga + * + * This driver is based on i2c-stm32f4.c + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-stm32.h" + +/* STM32F7 I2C registers */ +#define STM32F7_I2C_CR1 0x00 +#define STM32F7_I2C_CR2 0x04 +#define STM32F7_I2C_OAR1 0x08 +#define STM32F7_I2C_OAR2 0x0C +#define STM32F7_I2C_PECR 0x20 +#define STM32F7_I2C_TIMINGR 0x10 +#define STM32F7_I2C_ISR 0x18 +#define STM32F7_I2C_ICR 0x1C +#define STM32F7_I2C_RXDR 0x24 +#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) +#define STM32F7_I2C_CR1_NACKIE BIT(4) +#define STM32F7_I2C_CR1_ADDRIE BIT(3) +#define STM32F7_I2C_CR1_RXIE BIT(2) +#define STM32F7_I2C_CR1_TXIE BIT(1) +#define STM32F7_I2C_CR1_PE BIT(0) +#define STM32F7_I2C_ALL_IRQ_MASK (STM32F7_I2C_CR1_ERRIE \ + | STM32F7_I2C_CR1_TCIE \ + | STM32F7_I2C_CR1_STOPIE \ + | STM32F7_I2C_CR1_NACKIE \ + | STM32F7_I2C_CR1_RXIE \ + | STM32F7_I2C_CR1_TXIE) +#define STM32F7_I2C_XFER_IRQ_MASK (STM32F7_I2C_CR1_TCIE \ + | STM32F7_I2C_CR1_STOPIE \ + | STM32F7_I2C_CR1_NACKIE \ + | STM32F7_I2C_CR1_RXIE \ + | STM32F7_I2C_CR1_TXIE) + +/* STM32F7 I2C control 2 */ +#define STM32F7_I2C_CR2_PECBYTE BIT(26) +#define STM32F7_I2C_CR2_RELOAD BIT(24) +#define STM32F7_I2C_CR2_NBYTES_MASK GENMASK(23, 16) +#define STM32F7_I2C_CR2_NBYTES(n) (((n) & 0xff) << 16) +#define STM32F7_I2C_CR2_NACK BIT(15) +#define STM32F7_I2C_CR2_STOP BIT(14) +#define STM32F7_I2C_CR2_START BIT(13) +#define STM32F7_I2C_CR2_HEAD10R BIT(12) +#define STM32F7_I2C_CR2_ADD10 BIT(11) +#define STM32F7_I2C_CR2_RD_WRN BIT(10) +#define STM32F7_I2C_CR2_SADD10_MASK GENMASK(9, 0) +#define STM32F7_I2C_CR2_SADD10(n) (((n) & \ + STM32F7_I2C_CR2_SADD10_MASK)) +#define STM32F7_I2C_CR2_SADD7_MASK GENMASK(7, 1) +#define STM32F7_I2C_CR2_SADD7(n) (((n) & 0x7f) << 1) + +/* STM32F7 I2C Own Address 1 */ +#define STM32F7_I2C_OAR1_OA1EN BIT(15) +#define STM32F7_I2C_OAR1_OA1MODE BIT(10) +#define STM32F7_I2C_OAR1_OA1_10_MASK GENMASK(9, 0) +#define STM32F7_I2C_OAR1_OA1_10(n) (((n) & \ + STM32F7_I2C_OAR1_OA1_10_MASK)) +#define STM32F7_I2C_OAR1_OA1_7_MASK GENMASK(7, 1) +#define STM32F7_I2C_OAR1_OA1_7(n) (((n) & 0x7f) << 1) +#define STM32F7_I2C_OAR1_MASK (STM32F7_I2C_OAR1_OA1_7_MASK \ + | STM32F7_I2C_OAR1_OA1_10_MASK \ + | STM32F7_I2C_OAR1_OA1EN \ + | STM32F7_I2C_OAR1_OA1MODE) + +/* STM32F7 I2C Own Address 2 */ +#define STM32F7_I2C_OAR2_OA2EN BIT(15) +#define STM32F7_I2C_OAR2_OA2MSK_MASK GENMASK(10, 8) +#define STM32F7_I2C_OAR2_OA2MSK(n) (((n) & 0x7) << 8) +#define STM32F7_I2C_OAR2_OA2_7_MASK GENMASK(7, 1) +#define STM32F7_I2C_OAR2_OA2_7(n) (((n) & 0x7f) << 1) +#define STM32F7_I2C_OAR2_MASK (STM32F7_I2C_OAR2_OA2MSK_MASK \ + | STM32F7_I2C_OAR2_OA2_7_MASK \ + | STM32F7_I2C_OAR2_OA2EN) + +/* STM32F7 I2C Interrupt Status */ +#define STM32F7_I2C_ISR_ADDCODE_MASK GENMASK(23, 17) +#define STM32F7_I2C_ISR_ADDCODE_GET(n) \ + (((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) +#define STM32F7_I2C_ISR_TCR BIT(7) +#define STM32F7_I2C_ISR_TC BIT(6) +#define STM32F7_I2C_ISR_STOPF BIT(5) +#define STM32F7_I2C_ISR_NACKF BIT(4) +#define STM32F7_I2C_ISR_ADDR BIT(3) +#define STM32F7_I2C_ISR_RXNE BIT(2) +#define STM32F7_I2C_ISR_TXIS BIT(1) +#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) +#define STM32F7_I2C_ICR_STOPCF BIT(5) +#define STM32F7_I2C_ICR_NACKCF BIT(4) +#define STM32F7_I2C_ICR_ADDRCF BIT(3) + +/* STM32F7 I2C Timing */ +#define STM32F7_I2C_TIMINGR_PRESC(n) (((n) & 0xf) << 28) +#define STM32F7_I2C_TIMINGR_SCLDEL(n) (((n) & 0xf) << 20) +#define STM32F7_I2C_TIMINGR_SDADEL(n) (((n) & 0xf) << 16) +#define STM32F7_I2C_TIMINGR_SCLH(n) (((n) & 0xff) << 8) +#define STM32F7_I2C_TIMINGR_SCLL(n) ((n) & 0xff) + +#define STM32F7_I2C_MAX_LEN 0xff +#define STM32F7_I2C_DMA_LEN_MIN 0x16 +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 15 + +#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */ +#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */ + +#define STM32F7_I2C_RISE_TIME_DEFAULT 25 /* ns */ +#define STM32F7_I2C_FALL_TIME_DEFAULT 10 /* ns */ + +#define STM32F7_PRESC_MAX BIT(4) +#define STM32F7_SCLDEL_MAX BIT(4) +#define STM32F7_SDADEL_MAX BIT(4) +#define STM32F7_SCLH_MAX BIT(8) +#define STM32F7_SCLL_MAX BIT(8) + +#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) + * @fall_max: Max fall time of both SDA and SCL signals (ns) + * @rise_max: Max rise time of both SDA and SCL signals (ns) + * @hddat_min: Min data hold time (ns) + * @vddat_max: Max data valid time (ns) + * @sudat_min: Min data setup time (ns) + * @l_min: Min low period of the SCL clock (ns) + * @h_min: Min high period of the SCL clock (ns) + */ +struct stm32f7_i2c_spec { + u32 rate; + u32 fall_max; + u32 rise_max; + u32 hddat_min; + u32 vddat_max; + u32 sudat_min; + u32 l_min; + u32 h_min; +}; + +/** + * struct stm32f7_i2c_setup - private I2C timing setup parameters + * @speed_freq: I2C speed frequency (Hz) + * @clock_src: I2C clock source frequency (Hz) + * @rise_time: Rise time (ns) + * @fall_time: Fall time (ns) + * @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 { + u32 speed_freq; + u32 clock_src; + u32 rise_time; + u32 fall_time; + u32 fmp_clr_offset; + bool single_it_line; + bool fmp_cr1_bit; +}; + +/** + * struct stm32f7_i2c_timings - private I2C output parameters + * @node: List entry + * @presc: Prescaler value + * @scldel: Data setup time + * @sdadel: Data hold time + * @sclh: SCL high period (master mode) + * @scll: SCL low period (master mode) + */ +struct stm32f7_i2c_timings { + struct list_head node; + u8 presc; + u8 scldel; + u8 sdadel; + u8 sclh; + u8 scll; +}; + +/** + * struct stm32f7_i2c_msg - client specific data + * @addr: 8-bit or 10-bit slave addr, including r/w bit + * @count: number of bytes to be transferred + * @buf: data buffer + * @result: result of the transfer + * @stop: last I2C msg to be sent, i.e. STOP to be generated + * @smbus: boolean to know if the I2C IP is used in SMBus mode + * @size: type of SMBus protocol + * @read_write: direction of SMBus protocol + * SMBus block read and SMBus block write - block read process call protocols + * @smbus_buf: buffer to be used for SMBus protocol transfer. It will + * contain a maximum of 32 bytes of data + byte command + byte count + PEC + * This buffer has to be 32-bit aligned to be compliant with memory address + * register in DMA mode. + */ +struct stm32f7_i2c_msg { + u16 addr; + u32 count; + u8 *buf; + int result; + bool stop; + bool smbus; + int size; + char read_write; + u8 smbus_buf[I2C_SMBUS_BLOCK_MAX + 3] __aligned(4); +}; + +/** + * 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 + * @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 + * @f7_msg: customized i2c msg for driver usage + * @setup: I2C timing input setup + * @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; + struct device *dev; + void __iomem *base; + struct completion complete; + struct clk *clk; + unsigned int bus_rate; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_id; + struct stm32f7_i2c_msg f7_msg; + struct stm32f7_i2c_setup setup; + 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 stm32f7_i2c_specs[] = { + { + .rate = I2C_MAX_STANDARD_MODE_FREQ, + .fall_max = 300, + .rise_max = 1000, + .hddat_min = 0, + .vddat_max = 3450, + .sudat_min = 250, + .l_min = 4700, + .h_min = 4000, + }, + { + .rate = I2C_MAX_FAST_MODE_FREQ, + .fall_max = 300, + .rise_max = 300, + .hddat_min = 0, + .vddat_max = 900, + .sudat_min = 100, + .l_min = 1300, + .h_min = 600, + }, + { + .rate = I2C_MAX_FAST_MODE_PLUS_FREQ, + .fall_max = 100, + .rise_max = 120, + .hddat_min = 0, + .vddat_max = 450, + .sudat_min = 50, + .l_min = 500, + .h_min = 260, + }, +}; + +static const struct stm32f7_i2c_setup stm32f7_setup = { + .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, + .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, +}; + +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) +{ + writel_relaxed(readl_relaxed(reg) | mask, reg); +} + +static inline void stm32f7_i2c_clr_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) & ~mask, reg); +} + +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; + /* + * Truncate instead of rounding to closest: if the clock period is + * overestimated, the computed SCL timings will come out shorter on + * the wire, which can push the bus above the target rate and below + * the spec's tLOW/tHIGH minimums. + */ + u32 i2cclk = NSEC_PER_SEC / setup->clock_src; + u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, + setup->speed_freq); + u32 clk_error_prev = i2cbus; + u32 tsync; + u32 af_delay_min, af_delay_max; + u32 dnf_delay; + u32 clk_min, clk_max; + int sdadel_min, sdadel_max; + int scldel_min; + struct stm32f7_i2c_timings *v, *_v, *s; + struct list_head solutions; + u16 p, l, a, h; + int ret = 0; + + specs = stm32f7_get_specs(setup->speed_freq); + if (specs == ERR_PTR(-EINVAL)) + return dev_err_probe(i2c_dev->dev, -EINVAL, "speed out of bound {%d}\n", + setup->speed_freq); + + if ((setup->rise_time > specs->rise_max) || + (setup->fall_time > specs->fall_max)) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", + setup->rise_time, specs->rise_max, + setup->fall_time, specs->fall_max); + + i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); + if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "DNF out of bound %d/%d\n", i2c_dev->dnf * i2cclk, + STM32F7_I2C_DNF_MAX * i2cclk); + + /* Analog and Digital Filters */ + af_delay_min = + (i2c_dev->analog_filter ? + STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0); + af_delay_max = + (i2c_dev->analog_filter ? + STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); + dnf_delay = i2c_dev->dnf * i2cclk; + + sdadel_min = specs->hddat_min + setup->fall_time - + af_delay_min - (i2c_dev->dnf + 3) * i2cclk; + + sdadel_max = specs->vddat_max - setup->rise_time - + af_delay_max - (i2c_dev->dnf + 4) * i2cclk; + + scldel_min = setup->rise_time + specs->sudat_min; + + if (sdadel_min < 0) + sdadel_min = 0; + if (sdadel_max < 0) + sdadel_max = 0; + + dev_dbg(i2c_dev->dev, "SDADEL(min/max): %i/%i, SCLDEL(Min): %i\n", + sdadel_min, sdadel_max, scldel_min); + + INIT_LIST_HEAD(&solutions); + /* Compute possible values for PRESC, SCLDEL and SDADEL */ + for (p = 0; p < STM32F7_PRESC_MAX; p++) { + for (l = 0; l < STM32F7_SCLDEL_MAX; l++) { + u32 scldel = (l + 1) * (p + 1) * i2cclk; + + if (scldel < scldel_min) + continue; + + for (a = 0; a < STM32F7_SDADEL_MAX; a++) { + u32 sdadel = (a * (p + 1) + 1) * i2cclk; + + if (((sdadel >= sdadel_min) && + (sdadel <= sdadel_max)) && + (p != p_prev)) { + v = kmalloc_obj(*v); + if (!v) { + ret = -ENOMEM; + goto exit; + } + + v->presc = p; + v->scldel = l; + v->sdadel = a; + p_prev = p; + + list_add_tail(&v->node, + &solutions); + break; + } + } + + if (p_prev == p) + break; + } + } + + if (list_empty(&solutions)) { + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no Prescaler solution\n"); + goto exit; + } + + tsync = af_delay_min + dnf_delay + (2 * i2cclk); + s = NULL; + 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 + * and High Period. Provided: + * - SCL Low Period has to be higher than SCL Clock Low Period + * defined by I2C Specification. I2C Clock has to be lower than + * (SCL Low Period - Analog/Digital filters) / 4. + * - SCL High Period has to be lower than SCL Clock High Period + * defined by I2C Specification + * - I2C Clock has to be lower than SCL High Period + */ + list_for_each_entry(v, &solutions, node) { + u32 prescaler = (v->presc + 1) * i2cclk; + + for (l = 0; l < STM32F7_SCLL_MAX; l++) { + u32 tscl_l = (l + 1) * prescaler + tsync; + + if ((tscl_l < specs->l_min) || + (i2cclk >= + ((tscl_l - af_delay_min - dnf_delay) / 4))) { + continue; + } + + for (h = 0; h < STM32F7_SCLH_MAX; h++) { + u32 tscl_h = (h + 1) * prescaler + tsync; + u32 tscl = tscl_l + tscl_h + + setup->rise_time + setup->fall_time; + + if ((tscl >= clk_min) && (tscl <= clk_max) && + (tscl_h >= specs->h_min) && + (i2cclk < tscl_h)) { + int clk_error = tscl - i2cbus; + + if (clk_error < 0) + clk_error = -clk_error; + + if (clk_error < clk_error_prev) { + clk_error_prev = clk_error; + v->scll = l; + v->sclh = h; + s = v; + } + } + } + } + } + + if (!s) { + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no solution at all\n"); + goto exit; + } + + output->presc = s->presc; + output->scldel = s->scldel; + output->sdadel = s->sdadel; + output->scll = s->scll; + output->sclh = s->sclh; + + dev_dbg(i2c_dev->dev, + "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n", + output->presc, + output->scldel, output->sdadel, + output->scll, output->sclh); + +exit: + /* Release list and memory */ + list_for_each_entry_safe(v, _v, &solutions, node) { + list_del(&v->node); + kfree(v); + } + + 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; + + 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) + return dev_err_probe(i2c_dev->dev, -EINVAL, "Invalid bus speed (%i>%i)\n", + t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ); + + 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) + return dev_err_probe(i2c_dev->dev, -EINVAL, "clock rate is 0\n"); + + if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) + i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; + + i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, + "i2c-analog-filter"); + + do { + ret = stm32f7_i2c_compute_timing(i2c_dev, setup, + &i2c_dev->timing); + if (ret) { + dev_err_probe(i2c_dev->dev, ret, + "failed to compute I2C timings.\n"); + 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); + + if (ret) + return dev_err_probe(i2c_dev->dev, ret, "Impossible to compute I2C timings.\n"); + + 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", + str_on_off(i2c_dev->analog_filter), i2c_dev->dnf); + + i2c_dev->bus_rate = setup->speed_freq; + + return 0; +} + +static void stm32f7_i2c_disable_dma_req(struct stm32f7_i2c_dev *i2c_dev) +{ + void __iomem *base = i2c_dev->base; + u32 mask = STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN; + + stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask); +} + +static void stm32f7_i2c_dma_callback(void *arg) +{ + struct stm32f7_i2c_dev *i2c_dev = arg; + struct stm32_i2c_dma *dma = i2c_dev->dma; + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + + stm32f7_i2c_disable_dma_req(i2c_dev); + 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); +} + +static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_timings *t = &i2c_dev->timing; + u32 timing = 0; + + /* Timing settings */ + timing |= STM32F7_I2C_TIMINGR_PRESC(t->presc); + timing |= STM32F7_I2C_TIMINGR_SCLDEL(t->scldel); + timing |= STM32F7_I2C_TIMINGR_SDADEL(t->sdadel); + timing |= STM32F7_I2C_TIMINGR_SCLH(t->sclh); + timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll); + writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR); + + /* 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); +} + +static void stm32f7_i2c_write_tx_data(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + + if (f7_msg->count) { + writeb_relaxed(*f7_msg->buf++, base + STM32F7_I2C_TXDR); + f7_msg->count--; + } +} + +static void stm32f7_i2c_read_rx_data(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + + if (f7_msg->count) { + *f7_msg->buf++ = readb_relaxed(base + STM32F7_I2C_RXDR); + f7_msg->count--; + } else { + /* Flush RX buffer has no data is expected */ + readb_relaxed(base + STM32F7_I2C_RXDR); + } +} + +static void stm32f7_i2c_reload(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + u32 cr2; + + if (i2c_dev->use_dma) + f7_msg->count -= STM32F7_I2C_MAX_LEN; + + cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); + + cr2 &= ~STM32F7_I2C_CR2_NBYTES_MASK; + if (f7_msg->count > STM32F7_I2C_MAX_LEN) { + cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN); + } else { + cr2 &= ~STM32F7_I2C_CR2_RELOAD; + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + } + + writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2); +} + +static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + u32 cr2; + u8 *val; + + /* + * For I2C_SMBUS_BLOCK_DATA && I2C_SMBUS_BLOCK_PROC_CALL, the first + * data received inform us how many data will follow. + */ + stm32f7_i2c_read_rx_data(i2c_dev); + + /* + * Update NBYTES with the value read to continue the transfer + */ + val = f7_msg->buf - sizeof(u8); + f7_msg->count = *val; + cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); + cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD); + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2); +} + +static void stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap) +{ + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + + stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_PE); + + stm32f7_i2c_hw_config(i2c_dev); +} + +static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev) +{ + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F7_I2C_ISR, + status, + !(status & STM32F7_I2C_ISR_BUSY), + 10, 1000); + if (!ret) + return 0; + + stm32f7_i2c_release_bus(&i2c_dev->adap); + + return -EBUSY; +} + +static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, + struct i2c_msg *msg) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + u8 *dma_buf; + u32 cr1, cr2; + int ret; + + f7_msg->addr = msg->addr; + f7_msg->buf = msg->buf; + f7_msg->count = msg->len; + f7_msg->result = 0; + f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1); + + cr1 = readl_relaxed(base + STM32F7_I2C_CR1); + cr2 = readl_relaxed(base + STM32F7_I2C_CR2); + + /* Set transfer direction */ + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + if (msg->flags & I2C_M_RD) + cr2 |= STM32F7_I2C_CR2_RD_WRN; + + /* Set slave address */ + cr2 &= ~(STM32F7_I2C_CR2_HEAD10R | STM32F7_I2C_CR2_ADD10); + if (msg->flags & I2C_M_TEN) { + cr2 &= ~STM32F7_I2C_CR2_SADD10_MASK; + cr2 |= STM32F7_I2C_CR2_SADD10(f7_msg->addr); + cr2 |= STM32F7_I2C_CR2_ADD10; + } else { + cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK; + cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr); + } + + /* Set nb bytes to transfer and reload if needed */ + cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD); + if (f7_msg->count > STM32F7_I2C_MAX_LEN) { + cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN); + cr2 |= STM32F7_I2C_CR2_RELOAD; + } else { + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + } + + /* Enable NACK, STOP, error and transfer complete interrupts */ + cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE | + STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE; + + /* Clear DMA req and TX/RX interrupt */ + cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE | + STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN); + + /* Configure DMA or enable RX/TX interrupt */ + i2c_dev->use_dma = false; + 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) { + if (msg->flags & I2C_M_RD) + cr1 |= STM32F7_I2C_CR1_RXIE; + else + cr1 |= STM32F7_I2C_CR1_TXIE; + } else { + if (msg->flags & I2C_M_RD) + cr1 |= STM32F7_I2C_CR1_RXDMAEN; + else + 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; + + i2c_dev->master_mode = true; + + /* Write configurations registers */ + writel_relaxed(cr1, base + STM32F7_I2C_CR1); + writel_relaxed(cr2, base + STM32F7_I2C_CR2); +} + +static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, + unsigned short flags, u8 command, + union i2c_smbus_data *data) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + struct device *dev = i2c_dev->dev; + void __iomem *base = i2c_dev->base; + u32 cr1, cr2; + int i, ret; + + f7_msg->result = 0; + reinit_completion(&i2c_dev->complete); + + cr2 = readl_relaxed(base + STM32F7_I2C_CR2); + cr1 = readl_relaxed(base + STM32F7_I2C_CR1); + + /* Set transfer direction */ + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + if (f7_msg->read_write) + cr2 |= STM32F7_I2C_CR2_RD_WRN; + + /* Set slave address */ + cr2 &= ~(STM32F7_I2C_CR2_ADD10 | STM32F7_I2C_CR2_SADD7_MASK); + cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr); + + f7_msg->smbus_buf[0] = command; + switch (f7_msg->size) { + case I2C_SMBUS_QUICK: + f7_msg->stop = true; + f7_msg->count = 0; + break; + case I2C_SMBUS_BYTE: + f7_msg->stop = true; + f7_msg->count = 1; + break; + case I2C_SMBUS_BYTE_DATA: + if (f7_msg->read_write) { + f7_msg->stop = false; + f7_msg->count = 1; + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + } else { + f7_msg->stop = true; + f7_msg->count = 2; + f7_msg->smbus_buf[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (f7_msg->read_write) { + f7_msg->stop = false; + f7_msg->count = 1; + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + } else { + f7_msg->stop = true; + f7_msg->count = 3; + f7_msg->smbus_buf[1] = data->word & 0xff; + f7_msg->smbus_buf[2] = data->word >> 8; + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (f7_msg->read_write) { + f7_msg->stop = false; + f7_msg->count = 1; + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + } else { + f7_msg->stop = true; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX || + !data->block[0]) { + dev_err(dev, "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + f7_msg->count = data->block[0] + 2; + for (i = 1; i < f7_msg->count; i++) + f7_msg->smbus_buf[i] = data->block[i - 1]; + } + break; + case I2C_SMBUS_PROC_CALL: + f7_msg->stop = false; + f7_msg->count = 3; + f7_msg->smbus_buf[1] = data->word & 0xff; + f7_msg->smbus_buf[2] = data->word >> 8; + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + f7_msg->read_write = I2C_SMBUS_READ; + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + f7_msg->stop = false; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX - 1) { + dev_err(dev, "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + f7_msg->count = data->block[0] + 2; + for (i = 1; i < f7_msg->count; i++) + f7_msg->smbus_buf[i] = data->block[i - 1]; + 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; + } + + f7_msg->buf = f7_msg->smbus_buf; + + /* Configure PEC */ + if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) { + cr1 |= STM32F7_I2C_CR1_PECEN; + if (!f7_msg->read_write) { + cr2 |= STM32F7_I2C_CR2_PECBYTE; + f7_msg->count++; + } + } else { + cr1 &= ~STM32F7_I2C_CR1_PECEN; + cr2 &= ~STM32F7_I2C_CR2_PECBYTE; + } + + /* Set number of bytes to be transferred */ + cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD); + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + + /* Enable NACK, STOP, error and transfer complete interrupts */ + cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE | + STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE; + + /* Clear DMA req and TX/RX interrupt */ + cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE | + STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN); + + /* 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, + cr2 & STM32F7_I2C_CR2_RD_WRN, + 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->use_dma) { + if (cr2 & STM32F7_I2C_CR2_RD_WRN) + cr1 |= STM32F7_I2C_CR1_RXIE; + else + cr1 |= STM32F7_I2C_CR1_TXIE; + } else { + if (cr2 & STM32F7_I2C_CR2_RD_WRN) + cr1 |= STM32F7_I2C_CR1_RXDMAEN; + else + cr1 |= STM32F7_I2C_CR1_TXDMAEN; + } + + /* Set Start bit */ + cr2 |= STM32F7_I2C_CR2_START; + + i2c_dev->master_mode = true; + + /* Write configurations registers */ + writel_relaxed(cr1, base + STM32F7_I2C_CR1); + writel_relaxed(cr2, base + STM32F7_I2C_CR2); + + return 0; +} + +static void stm32f7_i2c_smbus_rep_start(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + u32 cr1, cr2; + int ret; + + cr2 = readl_relaxed(base + STM32F7_I2C_CR2); + cr1 = readl_relaxed(base + STM32F7_I2C_CR1); + + /* Set transfer direction */ + cr2 |= STM32F7_I2C_CR2_RD_WRN; + + switch (f7_msg->size) { + case I2C_SMBUS_BYTE_DATA: + f7_msg->count = 1; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + f7_msg->count = 2; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + f7_msg->count = 1; + cr2 |= STM32F7_I2C_CR2_RELOAD; + break; + } + + f7_msg->buf = f7_msg->smbus_buf; + f7_msg->stop = true; + + /* Add one byte for PEC if needed */ + 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); + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + + /* + * Configure RX/TX interrupt: + */ + cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE); + cr1 |= STM32F7_I2C_CR1_RXIE; + + /* + * Configure DMA or enable RX/TX interrupt: + * For I2C_SMBUS_BLOCK_DATA and I2C_SMBUS_BLOCK_PROC_CALL we don't use + * dma as we don't know in advance how many data will be received + */ + cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE | + STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN); + + i2c_dev->use_dma = false; + if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN && + f7_msg->size != I2C_SMBUS_BLOCK_DATA && + f7_msg->size != I2C_SMBUS_BLOCK_PROC_CALL) { + ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma, + cr2 & STM32F7_I2C_CR2_RD_WRN, + 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->use_dma) + cr1 |= STM32F7_I2C_CR1_RXIE; + else + cr1 |= STM32F7_I2C_CR1_RXDMAEN; + + /* Configure Repeated Start */ + cr2 |= STM32F7_I2C_CR2_START; + + /* Write configurations registers */ + writel_relaxed(cr1, base + STM32F7_I2C_CR1); + writel_relaxed(cr2, base + STM32F7_I2C_CR2); +} + +static int stm32f7_i2c_smbus_check_pec(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + u8 count, internal_pec, received_pec; + + internal_pec = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR); + + switch (f7_msg->size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + received_pec = f7_msg->smbus_buf[1]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + received_pec = f7_msg->smbus_buf[2]; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + count = f7_msg->smbus_buf[0]; + received_pec = f7_msg->smbus_buf[count]; + break; + default: + dev_err(i2c_dev->dev, "Unsupported smbus protocol for PEC\n"); + return -EINVAL; + } + + if (internal_pec != received_pec) { + dev_err(i2c_dev->dev, "Bad PEC 0x%02x vs. 0x%02x\n", + internal_pec, received_pec); + return -EBADMSG; + } + + return 0; +} + +static bool stm32f7_i2c_is_addr_match(struct i2c_client *slave, u32 addcode) +{ + u32 addr; + + if (!slave) + return false; + + if (slave->flags & I2C_CLIENT_TEN) { + /* + * For 10-bit addr, addcode = 11110XY with + * X = Bit 9 of slave address + * Y = Bit 8 of slave address + */ + addr = slave->addr >> 8; + addr |= 0x78; + if (addr == addcode) + return true; + } else { + addr = slave->addr & 0x7f; + if (addr == addcode) + return true; + } + + return false; +} + +static void stm32f7_i2c_slave_start(struct stm32f7_i2c_dev *i2c_dev) +{ + struct i2c_client *slave = i2c_dev->slave_running; + void __iomem *base = i2c_dev->base; + u32 mask; + u8 value = 0; + + if (i2c_dev->slave_dir) { + /* Notify i2c slave that new read transfer is starting */ + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); + + /* + * Disable slave TX config in case of I2C combined message + * (I2C Write followed by I2C Read) + */ + mask = STM32F7_I2C_CR2_RELOAD; + stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR2, mask); + mask = STM32F7_I2C_CR1_SBC | STM32F7_I2C_CR1_RXIE | + STM32F7_I2C_CR1_TCIE; + stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask); + + /* Enable TX empty, STOP, NACK interrupts */ + mask = STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE | + 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); + + /* Set reload mode to be able to ACK/NACK each received byte */ + mask = STM32F7_I2C_CR2_RELOAD; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask); + + /* + * Set STOP, NACK, RX empty and transfer complete interrupts.* + * Set Slave Byte Control to be able to ACK/NACK each data + * byte received + */ + mask = STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE | + STM32F7_I2C_CR1_SBC | STM32F7_I2C_CR1_RXIE | + STM32F7_I2C_CR1_TCIE; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); + } +} + +static void stm32f7_i2c_slave_addr(struct stm32f7_i2c_dev *i2c_dev) +{ + void __iomem *base = i2c_dev->base; + u32 isr, addcode, dir, mask; + int i; + + isr = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + addcode = STM32F7_I2C_ISR_ADDCODE_GET(isr); + dir = isr & STM32F7_I2C_ISR_DIR; + + for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { + if (stm32f7_i2c_is_addr_match(i2c_dev->slave[i], addcode)) { + i2c_dev->slave_running = i2c_dev->slave[i]; + i2c_dev->slave_dir = dir; + + /* Start I2C slave processing */ + stm32f7_i2c_slave_start(i2c_dev); + + /* Clear ADDR flag */ + mask = STM32F7_I2C_ICR_ADDRCF; + writel_relaxed(mask, base + STM32F7_I2C_ICR); + break; + } + } +} + +static int stm32f7_i2c_get_slave_id(struct stm32f7_i2c_dev *i2c_dev, + struct i2c_client *slave, int *id) +{ + int i; + + for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { + if (i2c_dev->slave[i] == slave) { + *id = i; + return 0; + } + } + + dev_err(i2c_dev->dev, "Slave 0x%x not registered\n", slave->addr); + + return -ENODEV; +} + +static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev, + struct i2c_client *slave, int *id) +{ + struct device *dev = i2c_dev->dev; + int i; + + /* + * 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 + */ + 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; + return 0; + } + } + +fail: + dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr); + + return -EINVAL; +} + +static bool stm32f7_i2c_is_slave_registered(struct stm32f7_i2c_dev *i2c_dev) +{ + int i; + + for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { + if (i2c_dev->slave[i]) + return true; + } + + return false; +} + +static bool stm32f7_i2c_is_slave_busy(struct stm32f7_i2c_dev *i2c_dev) +{ + int i, busy; + + busy = 0; + for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { + if (i2c_dev->slave[i]) + busy++; + } + + return i == busy; +} + +static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev, u32 status) +{ + void __iomem *base = i2c_dev->base; + u32 cr2, mask; + u8 val; + int ret; + + /* Slave transmitter mode */ + if (status & STM32F7_I2C_ISR_TXIS) { + i2c_slave_event(i2c_dev->slave_running, + I2C_SLAVE_READ_PROCESSED, + &val); + + /* Write data byte */ + writel_relaxed(val, base + STM32F7_I2C_TXDR); + } + + /* Transfer Complete Reload for Slave receiver mode */ + if (status & STM32F7_I2C_ISR_TCR || status & STM32F7_I2C_ISR_RXNE) { + /* + * Read data byte then set NBYTES to receive next byte or NACK + * the current received byte + */ + val = readb_relaxed(i2c_dev->base + STM32F7_I2C_RXDR); + ret = i2c_slave_event(i2c_dev->slave_running, + I2C_SLAVE_WRITE_RECEIVED, + &val); + if (!ret) { + cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); + cr2 |= STM32F7_I2C_CR2_NBYTES(1); + writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2); + } else { + mask = STM32F7_I2C_CR2_NACK; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask); + } + } + + /* NACK received */ + if (status & STM32F7_I2C_ISR_NACKF) { + dev_dbg(i2c_dev->dev, "<%s>: Receive NACK\n", __func__); + writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR); + } + + /* STOP received */ + if (status & STM32F7_I2C_ISR_STOPF) { + /* Disable interrupts */ + stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_XFER_IRQ_MASK); + + if (i2c_dev->slave_dir) { + /* + * Flush TX buffer in order to not used the byte in + * TXDR for the next transfer + */ + mask = STM32F7_I2C_ISR_TXE; + stm32f7_i2c_set_bits(base + STM32F7_I2C_ISR, mask); + } + + /* Clear STOP flag */ + writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR); + + /* Notify i2c slave that a STOP flag has been detected */ + i2c_slave_event(i2c_dev->slave_running, I2C_SLAVE_STOP, &val); + + i2c_dev->slave_running = NULL; + } + + /* Address match received */ + if (status & STM32F7_I2C_ISR_ADDR) + stm32f7_i2c_slave_addr(i2c_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t stm32f7_i2c_handle_isr_errs(struct stm32f7_i2c_dev *i2c_dev, u32 status) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + u16 addr = f7_msg->addr; + void __iomem *base = i2c_dev->base; + struct device *dev = i2c_dev->dev; + + /* 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); + + /* RX not empty */ + 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 (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; + } + + if (status & STM32F7_I2C_ISR_TCR) { + if (f7_msg->smbus) + stm32f7_i2c_smbus_reload(i2c_dev); + else + 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 (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); + } + } + + /* 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 + 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); + + i2c_dev->master_mode = false; + complete(&i2c_dev->complete); + } + + return IRQ_HANDLED; +} + +static irqreturn_t stm32f7_i2c_isr_error_thread(int irq, void *data) +{ + struct stm32f7_i2c_dev *i2c_dev = data; + u32 status; + + status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + + return stm32f7_i2c_handle_isr_errs(i2c_dev, status); +} + +static int stm32f7_i2c_wait_polling(struct stm32f7_i2c_dev *i2c_dev) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), i2c_dev->adap.timeout); + + while (ktime_compare(ktime_get(), timeout) < 0) { + udelay(5); + stm32f7_i2c_isr_event(0, i2c_dev); + + if (completion_done(&i2c_dev->complete)) + return 1; + } + + return 0; +} + +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); + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + struct stm32_i2c_dma *dma = i2c_dev->dma; + unsigned long time_left; + int ret; + + i2c_dev->msg = msgs; + i2c_dev->msg_num = num; + i2c_dev->msg_id = 0; + f7_msg->smbus = false; + + ret = pm_runtime_resume_and_get(i2c_dev->dev); + if (ret < 0) + return ret; + + ret = stm32f7_i2c_wait_free_bus(i2c_dev); + if (ret) + goto pm_free; + + reinit_completion(&i2c_dev->complete); + + stm32f7_i2c_xfer_msg(i2c_dev, msgs); + + 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_sync(dma->chan_using); + stm32f7_i2c_wait_free_bus(i2c_dev); + ret = -ETIMEDOUT; + } + +pm_free: + 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, + union i2c_smbus_data *data) +{ + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adapter); + 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 time_left; + int i, ret; + + f7_msg->addr = addr; + f7_msg->size = size; + f7_msg->read_write = read_write; + f7_msg->smbus = true; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = stm32f7_i2c_wait_free_bus(i2c_dev); + if (ret) + goto pm_free; + + ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data); + if (ret) + goto pm_free; + + time_left = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + 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(dev, "Access to slave 0x%x timed out\n", f7_msg->addr); + if (i2c_dev->use_dma) + dmaengine_terminate_sync(dma->chan_using); + stm32f7_i2c_wait_free_bus(i2c_dev); + ret = -ETIMEDOUT; + goto pm_free; + } + + /* Check PEC */ + if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) { + ret = stm32f7_i2c_smbus_check_pec(i2c_dev); + if (ret) + goto pm_free; + } + + if (read_write && size != I2C_SMBUS_QUICK) { + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = f7_msg->smbus_buf[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = f7_msg->smbus_buf[0] | + (f7_msg->smbus_buf[1] << 8); + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i <= f7_msg->smbus_buf[0]; i++) + data->block[i] = f7_msg->smbus_buf[i]; + break; + default: + dev_err(dev, "Unsupported smbus transaction\n"); + ret = -EINVAL; + } + } + +pm_free: + 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); + void __iomem *base = i2c_dev->base; + struct device *dev = i2c_dev->dev; + u32 oar1, oar2, mask; + int id, ret; + + if (slave->flags & I2C_CLIENT_PEC) { + dev_err(dev, "SMBus PEC not supported in slave mode\n"); + return -EINVAL; + } + + if (stm32f7_i2c_is_slave_busy(i2c_dev)) { + dev_err(dev, "Too much slave registered\n"); + return -EBUSY; + } + + ret = stm32f7_i2c_get_free_slave_id(i2c_dev, slave, &id); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + 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; + if (slave->flags & I2C_CLIENT_TEN) { + oar1 |= STM32F7_I2C_OAR1_OA1_10(slave->addr); + oar1 |= STM32F7_I2C_OAR1_OA1MODE; + } else { + oar1 |= STM32F7_I2C_OAR1_OA1_7(slave->addr); + } + oar1 |= STM32F7_I2C_OAR1_OA1EN; + i2c_dev->slave[id] = slave; + writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1); + break; + + case 2: + /* Configure Own Address 2 */ + oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2); + oar2 &= ~STM32F7_I2C_OAR2_MASK; + if (slave->flags & I2C_CLIENT_TEN) { + ret = -EOPNOTSUPP; + goto pm_free; + } + + oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr); + oar2 |= STM32F7_I2C_OAR2_OA2EN; + i2c_dev->slave[id] = slave; + writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2); + break; + + default: + dev_err(dev, "I2C slave id not supported\n"); + ret = -ENODEV; + goto pm_free; + } + + /* Enable ACK */ + stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR2, STM32F7_I2C_CR2_NACK); + + /* Enable Address match interrupt, error interrupt and enable I2C */ + mask = STM32F7_I2C_CR1_ADDRIE | STM32F7_I2C_CR1_ERRIE | + STM32F7_I2C_CR1_PE; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); + + ret = 0; +pm_free: + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) + stm32f7_i2c_enable_wakeup(i2c_dev, false); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) +{ + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter); + void __iomem *base = i2c_dev->base; + u32 mask; + int id, ret; + + ret = stm32f7_i2c_get_slave_id(i2c_dev, slave, &id); + if (ret) + return ret; + + WARN_ON(!i2c_dev->slave[id]); + + ret = pm_runtime_resume_and_get(i2c_dev->dev); + if (ret < 0) + return ret; + + if (id == 1) { + mask = STM32F7_I2C_OAR1_OA1EN; + stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask); + } 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)) { + stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); + stm32f7_i2c_enable_wakeup(i2c_dev, false); + } + + 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; + + i2c_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg-fmp"); + if (IS_ERR(i2c_dev->regmap)) + /* Optional */ + return 0; + + ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1, + &i2c_dev->fmp_sreg); + 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; + + 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) +{ + 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 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, + .unreg_slave = stm32f7_i2c_unreg_slave, +}; + +static int stm32f7_i2c_probe(struct platform_device *pdev) +{ + struct stm32f7_i2c_dev *i2c_dev; + const struct stm32f7_i2c_setup *setup; + struct resource *res; + struct i2c_adapter *adap; + struct reset_control *rst; + dma_addr_t phy_addr; + int irq_error, irq_event, ret; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + setup = of_device_get_match_data(&pdev->dev); + if (!setup) + return dev_err_probe(&pdev->dev, -ENODEV, "Can't get device data\n"); + 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 = platform_get_irq(pdev, 0); + if (irq_event < 0) + return irq_event; + + i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node, + "wakeup-source"); + + 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)) + 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->dev = &pdev->dev; + + ret = devm_request_threaded_irq(&pdev->dev, irq_event, + stm32f7_i2c_isr_event, + stm32f7_i2c_isr_event_thread, + IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) + return ret; + + 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 ret; + } + + ret = stm32f7_i2c_setup_timing(i2c_dev, &i2c_dev->setup); + if (ret) + 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); + snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", + &res->start); + adap->owner = THIS_MODULE; + adap->timeout = 8 * HZ; + adap->retries = 3; + adap->algo = &stm32f7_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + /* Init DMA config if supported */ + 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_probe(i2c_dev->dev, ret, "Failed to set wake up irq\n"); + goto clr_wakeup_capable; + } + } + + platform_set_drvdata(pdev, i2c_dev); + + pm_runtime_set_autosuspend_delay(i2c_dev->dev, + STM32F7_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + pm_runtime_get_noresume(&pdev->dev); + + 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_probe(i2c_dev->dev, ret, + "failed to enable SMBus Host-Notify protocol\n"); + 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_probe(i2c_dev->dev, ret, + "failed to enable SMBus alert protocol\n"); + goto i2c_disable_smbus_host; + } + } + + dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); + + 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); + + 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 void stm32f7_i2c_remove(struct platform_device *pdev) +{ + struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + 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); + + 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); + + if (i2c_dev->dma) { + stm32_i2c_dma_free(i2c_dev->dma); + i2c_dev->dma = NULL; + } + + stm32f7_i2c_write_fm_plus_bits(i2c_dev, false); +} + +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(i2c_dev->clk); + + return 0; +} + +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_enable(i2c_dev->clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + } + + return 0; +} + +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); + +static struct platform_driver stm32f7_i2c_driver = { + .driver = { + .name = "stm32f7-i2c", + .of_match_table = stm32f7_i2c_match, + .pm = &stm32f7_i2c_pm_ops, + }, + .probe = stm32f7_i2c_probe, + .remove = stm32f7_i2c_remove, +}; + +module_platform_driver(stm32f7_i2c_driver); + +MODULE_AUTHOR("M'boumba Cedric Madianga "); +MODULE_DESCRIPTION("STMicroelectronics STM32F7 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c new file mode 100644 index 000000000..4f70b12a9 --- /dev/null +++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c @@ -0,0 +1,315 @@ +/* + * P2WI (Push-Pull Two Wire Interface) bus driver. + * + * Author: Boris BREZILLON + * + * 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. + * + * 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 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) + * - there's no Ack bit after each byte transfer + * + * This means this bus cannot be used to interface with standard SMBus + * devices (the only known device to support this interface is the AXP221 + * PMIC). + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +/* P2WI registers */ +#define P2WI_CTRL 0x0 +#define P2WI_CCR 0x4 +#define P2WI_INTE 0x8 +#define P2WI_INTS 0xc +#define P2WI_DADDR0 0x10 +#define P2WI_DADDR1 0x14 +#define P2WI_DLEN 0x18 +#define P2WI_DATA0 0x1c +#define P2WI_DATA1 0x20 +#define P2WI_LCR 0x24 +#define P2WI_PMCR 0x28 + +/* CTRL fields */ +#define P2WI_CTRL_START_TRANS BIT(7) +#define P2WI_CTRL_ABORT_TRANS BIT(6) +#define P2WI_CTRL_GLOBAL_INT_ENB BIT(1) +#define P2WI_CTRL_SOFT_RST BIT(0) + +/* CLK CTRL fields */ +#define P2WI_CCR_SDA_OUT_DELAY(v) (((v) & 0x7) << 8) +#define P2WI_CCR_MAX_CLK_DIV 0xff +#define P2WI_CCR_CLK_DIV(v) ((v) & P2WI_CCR_MAX_CLK_DIV) + +/* STATUS fields */ +#define P2WI_INTS_TRANS_ERR_ID(v) (((v) >> 8) & 0xff) +#define P2WI_INTS_LOAD_BSY BIT(2) +#define P2WI_INTS_TRANS_ERR BIT(1) +#define P2WI_INTS_TRANS_OVER BIT(0) + +/* DATA LENGTH fields*/ +#define P2WI_DLEN_READ BIT(4) +#define P2WI_DLEN_DATA_LENGTH(v) ((v - 1) & 0x7) + +/* LINE CTRL fields*/ +#define P2WI_LCR_SCL_STATE BIT(5) +#define P2WI_LCR_SDA_STATE BIT(4) +#define P2WI_LCR_SCL_CTL BIT(3) +#define P2WI_LCR_SCL_CTL_EN BIT(2) +#define P2WI_LCR_SDA_CTL BIT(1) +#define P2WI_LCR_SDA_CTL_EN BIT(0) + +/* PMU MODE CTRL fields */ +#define P2WI_PMCR_PMU_INIT_SEND BIT(31) +#define P2WI_PMCR_PMU_INIT_DATA(v) (((v) & 0xff) << 16) +#define P2WI_PMCR_PMU_MODE_REG(v) (((v) & 0xff) << 8) +#define P2WI_PMCR_PMU_DEV_ADDR(v) ((v) & 0xff) + +#define P2WI_MAX_FREQ 6000000 + +struct p2wi { + struct i2c_adapter adapter; + struct completion complete; + unsigned int status; + void __iomem *regs; + struct clk *clk; + struct reset_control *rstc; + int target_addr; +}; + +static irqreturn_t p2wi_interrupt(int irq, void *dev_id) +{ + struct p2wi *p2wi = dev_id; + unsigned long status; + + status = readl(p2wi->regs + P2WI_INTS); + p2wi->status = status; + + /* Clear interrupts */ + status &= (P2WI_INTS_LOAD_BSY | P2WI_INTS_TRANS_ERR | + P2WI_INTS_TRANS_OVER); + writel(status, p2wi->regs + P2WI_INTS); + + complete(&p2wi->complete); + + return IRQ_HANDLED; +} + +static u32 p2wi_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static int p2wi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct p2wi *p2wi = i2c_get_adapdata(adap); + unsigned long dlen = P2WI_DLEN_DATA_LENGTH(1); + + if (p2wi->target_addr >= 0 && addr != p2wi->target_addr) { + dev_err(&adap->dev, "invalid P2WI address\n"); + return -EINVAL; + } + + if (!data) + return -EINVAL; + + writel(command, p2wi->regs + P2WI_DADDR0); + + if (read_write == I2C_SMBUS_READ) + dlen |= P2WI_DLEN_READ; + else + writel(data->byte, p2wi->regs + P2WI_DATA0); + + writel(dlen, p2wi->regs + P2WI_DLEN); + + if (readl(p2wi->regs + P2WI_CTRL) & P2WI_CTRL_START_TRANS) { + dev_err(&adap->dev, "P2WI bus busy\n"); + return -EBUSY; + } + + reinit_completion(&p2wi->complete); + + writel(P2WI_INTS_LOAD_BSY | P2WI_INTS_TRANS_ERR | P2WI_INTS_TRANS_OVER, + p2wi->regs + P2WI_INTE); + + writel(P2WI_CTRL_START_TRANS | P2WI_CTRL_GLOBAL_INT_ENB, + p2wi->regs + P2WI_CTRL); + + wait_for_completion(&p2wi->complete); + + if (p2wi->status & P2WI_INTS_LOAD_BSY) { + dev_err(&adap->dev, "P2WI bus busy\n"); + return -EBUSY; + } + + if (p2wi->status & P2WI_INTS_TRANS_ERR) { + dev_err(&adap->dev, "P2WI bus xfer error\n"); + return -ENXIO; + } + + if (read_write == I2C_SMBUS_READ) + data->byte = readl(p2wi->regs + P2WI_DATA0); + + return 0; +} + +static const struct i2c_algorithm p2wi_algo = { + .smbus_xfer = p2wi_smbus_xfer, + .functionality = p2wi_functionality, +}; + +static const struct of_device_id p2wi_of_match_table[] = { + { .compatible = "allwinner,sun6i-a31-p2wi" }, + {} +}; +MODULE_DEVICE_TABLE(of, p2wi_of_match_table); + +static int p2wi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *childnp; + unsigned long parent_clk_freq; + u32 clk_freq = I2C_MAX_STANDARD_MODE_FREQ; + struct p2wi *p2wi; + u32 target_addr; + int clk_div; + int irq; + int ret; + + of_property_read_u32(np, "clock-frequency", &clk_freq); + if (clk_freq > P2WI_MAX_FREQ) + return dev_err_probe(dev, -EINVAL, + "required clock-frequency (%u Hz) is too high (max = 6MHz)", + clk_freq); + + if (clk_freq == 0) + return dev_err_probe(dev, -EINVAL, "clock-frequency is set to 0 in DT\n"); + + if (of_get_child_count(np) > 1) + return dev_err_probe(dev, -EINVAL, "P2WI only supports one target device\n"); + + p2wi = devm_kzalloc(dev, sizeof(struct p2wi), GFP_KERNEL); + if (!p2wi) + return -ENOMEM; + + 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 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", &target_addr); + if (ret) + return dev_err_probe(dev, -EINVAL, + "invalid target address on node %pOF\n", childnp); + + p2wi->target_addr = target_addr; + } + + p2wi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(p2wi->regs)) + return PTR_ERR(p2wi->regs); + + strscpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name)); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + p2wi->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(p2wi->clk)) + return dev_err_probe(dev, PTR_ERR(p2wi->clk), + "failed to enable clk\n"); + + parent_clk_freq = clk_get_rate(p2wi->clk); + + p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(p2wi->rstc)) + return dev_err_probe(dev, PTR_ERR(p2wi->rstc), + "failed to retrieve reset controller\n"); + + ret = reset_control_deassert(p2wi->rstc); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset line\n"); + + init_completion(&p2wi->complete); + p2wi->adapter.dev.parent = dev; + p2wi->adapter.algo = &p2wi_algo; + p2wi->adapter.owner = THIS_MODULE; + p2wi->adapter.dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, p2wi); + i2c_set_adapdata(&p2wi->adapter, p2wi); + + ret = devm_request_irq(dev, irq, p2wi_interrupt, 0, pdev->name, p2wi); + if (ret) + goto err_reset_assert; + + writel(P2WI_CTRL_SOFT_RST, p2wi->regs + P2WI_CTRL); + + clk_div = parent_clk_freq / clk_freq; + if (!clk_div) { + dev_warn(dev, + "clock-frequency is too high, setting it to %lu Hz\n", + parent_clk_freq); + clk_div = 1; + } else if (clk_div > P2WI_CCR_MAX_CLK_DIV) { + dev_warn(dev, + "clock-frequency is too low, setting it to %lu Hz\n", + parent_clk_freq / P2WI_CCR_MAX_CLK_DIV); + clk_div = P2WI_CCR_MAX_CLK_DIV; + } + + writel(P2WI_CCR_SDA_OUT_DELAY(1) | P2WI_CCR_CLK_DIV(clk_div), + p2wi->regs + P2WI_CCR); + + ret = i2c_add_adapter(&p2wi->adapter); + if (!ret) + return 0; + +err_reset_assert: + reset_control_assert(p2wi->rstc); + + return ret; +} + +static void p2wi_remove(struct platform_device *dev) +{ + struct p2wi *p2wi = platform_get_drvdata(dev); + + reset_control_assert(p2wi->rstc); + i2c_del_adapter(&p2wi->adapter); +} + +static struct platform_driver p2wi_driver = { + .probe = p2wi_probe, + .remove = p2wi_remove, + .driver = { + .name = "i2c-sunxi-p2wi", + .of_match_table = p2wi_of_match_table, + }, +}; +module_platform_driver(p2wi_driver); + +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Allwinner P2WI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c new file mode 100644 index 000000000..0216e398f --- /dev/null +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2012 FUJITSU SEMICONDUCTOR LIMITED + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WAIT_PCLK(n, rate) \ + ndelay(DIV_ROUND_UP(DIV_ROUND_UP(HZ_PER_GHZ, rate), n) + 10) + +/* I2C register address definitions */ +#define SYNQUACER_I2C_REG_BSR (0x00 << 2) // Bus Status +#define SYNQUACER_I2C_REG_BCR (0x01 << 2) // Bus Control +#define SYNQUACER_I2C_REG_CCR (0x02 << 2) // Clock Control +#define SYNQUACER_I2C_REG_ADR (0x03 << 2) // Address +#define SYNQUACER_I2C_REG_DAR (0x04 << 2) // Data +#define SYNQUACER_I2C_REG_CSR (0x05 << 2) // Expansion CS +#define SYNQUACER_I2C_REG_FSR (0x06 << 2) // Bus Clock Freq +#define SYNQUACER_I2C_REG_BC2R (0x07 << 2) // Bus Control 2 + +/* I2C register bit definitions */ +#define SYNQUACER_I2C_BSR_FBT BIT(0) // First Byte Transfer +#define SYNQUACER_I2C_BSR_GCA BIT(1) // General Call Address +#define SYNQUACER_I2C_BSR_AAS BIT(2) // Address as Slave +#define SYNQUACER_I2C_BSR_TRX BIT(3) // Transfer/Receive +#define SYNQUACER_I2C_BSR_LRB BIT(4) // Last Received Bit +#define SYNQUACER_I2C_BSR_AL BIT(5) // Arbitration Lost +#define SYNQUACER_I2C_BSR_RSC BIT(6) // Repeated Start Cond. +#define SYNQUACER_I2C_BSR_BB BIT(7) // Bus Busy + +#define SYNQUACER_I2C_BCR_INT BIT(0) // Interrupt +#define SYNQUACER_I2C_BCR_INTE BIT(1) // Interrupt Enable +#define SYNQUACER_I2C_BCR_GCAA BIT(2) // Gen. Call Access Ack. +#define SYNQUACER_I2C_BCR_ACK BIT(3) // Acknowledge +#define SYNQUACER_I2C_BCR_MSS BIT(4) // Master Slave Select +#define SYNQUACER_I2C_BCR_SCC BIT(5) // Start Condition Cont. +#define SYNQUACER_I2C_BCR_BEIE BIT(6) // Bus Error Int Enable +#define SYNQUACER_I2C_BCR_BER BIT(7) // Bus Error + +#define SYNQUACER_I2C_CCR_CS_MASK (0x1f) // CCR Clock Period Sel. +#define SYNQUACER_I2C_CCR_EN BIT(5) // Enable +#define SYNQUACER_I2C_CCR_FM BIT(6) // Speed Mode Select + +#define SYNQUACER_I2C_CSR_CS_MASK (0x3f) // CSR Clock Period Sel. + +#define SYNQUACER_I2C_BC2R_SCLL BIT(0) // SCL Low Drive +#define SYNQUACER_I2C_BC2R_SDAL BIT(1) // SDA Low Drive +#define SYNQUACER_I2C_BC2R_SCLS BIT(4) // SCL Status +#define SYNQUACER_I2C_BC2R_SDAS BIT(5) // SDA Status + +/* PCLK frequency */ +#define SYNQUACER_I2C_BUS_CLK_FR(rate) (((rate) / 20000000) + 1) + +/* STANDARD MODE frequency */ +#define SYNQUACER_I2C_CLK_MASTER_STD(rate) \ + 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), I2C_MAX_FAST_MODE_FREQ) - 2) * 2, 3) + +/* (clkrate <= 18000000) */ +/* calculate the value of CS bits in CCR register on standard mode */ +#define SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rate) \ + ((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 65) \ + & SYNQUACER_I2C_CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on standard mode */ +#define SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rate) 0x00 + +/* calculate the value of CS bits in CCR register on fast mode */ +#define SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rate) \ + ((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \ + & SYNQUACER_I2C_CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on fast mode */ +#define SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rate) 0x00 + +/* (clkrate > 18000000) */ +/* calculate the value of CS bits in CCR register on standard mode */ +#define SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rate) \ + ((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) \ + & SYNQUACER_I2C_CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on standard mode */ +#define SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rate) \ + (((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) >> 5) \ + & SYNQUACER_I2C_CSR_CS_MASK) + +/* calculate the value of CS bits in CCR register on fast mode */ +#define SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rate) \ + ((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \ + & SYNQUACER_I2C_CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on fast mode */ +#define SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rate) \ + (((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) >> 5) \ + & SYNQUACER_I2C_CSR_CS_MASK) + +/* min I2C clock frequency 14M */ +#define SYNQUACER_I2C_MIN_CLK_RATE (14 * 1000000) +/* max I2C clock frequency 200M */ +#define SYNQUACER_I2C_MAX_CLK_RATE (200 * 1000000) +/* I2C clock frequency 18M */ +#define SYNQUACER_I2C_CLK_RATE_18M (18 * 1000000) + +#define SYNQUACER_I2C_SPEED_FM 400 // Fast Mode +#define SYNQUACER_I2C_SPEED_SM 100 // Standard Mode + +enum i2c_state { + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE +}; + +struct synquacer_i2c { + struct completion completion; + + struct i2c_msg *msg; + u32 msg_num; + u32 msg_idx; + u32 msg_ptr; + + int irq; + struct device *dev; + void __iomem *base; + u32 pclkrate; + u32 speed_khz; + u32 timeout_ms; + enum i2c_state state; + struct i2c_adapter adapter; +}; + +static inline int is_lastmsg(struct synquacer_i2c *i2c) +{ + return i2c->msg_idx >= (i2c->msg_num - 1); +} + +static inline int is_msglast(struct synquacer_i2c *i2c) +{ + return i2c->msg_ptr == (i2c->msg->len - 1); +} + +static inline int is_msgend(struct synquacer_i2c *i2c) +{ + return i2c->msg_ptr >= i2c->msg->len; +} + +static inline unsigned long calc_timeout_ms(struct synquacer_i2c *i2c, + struct i2c_msg *msgs, + int num) +{ + unsigned long bit_count = 0; + int i; + + for (i = 0; i < num; i++, msgs++) + bit_count += msgs->len; + + return DIV_ROUND_UP((bit_count * 9 + num * 10) * 3, 200) + 10; +} + +static void synquacer_i2c_stop(struct synquacer_i2c *i2c, int ret) +{ + /* + * clear IRQ (INT=0, BER=0) + * set Stop Condition (MSS=0) + * Interrupt Disable + */ + writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR); + + i2c->state = STATE_IDLE; + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + complete(&i2c->completion); +} + +static void synquacer_i2c_hw_init(struct synquacer_i2c *i2c) +{ + unsigned char ccr_cs, csr_cs; + u32 rt = i2c->pclkrate; + + /* Set own Address */ + writeb(0, i2c->base + SYNQUACER_I2C_REG_ADR); + + /* Set PCLK frequency */ + writeb(SYNQUACER_I2C_BUS_CLK_FR(i2c->pclkrate), + i2c->base + SYNQUACER_I2C_REG_FSR); + + switch (i2c->speed_khz) { + case SYNQUACER_I2C_SPEED_FM: + if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) { + ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rt); + csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rt); + } else { + ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rt); + csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rt); + } + + /* Set Clock and enable, Set fast mode */ + writeb(ccr_cs | SYNQUACER_I2C_CCR_FM | + SYNQUACER_I2C_CCR_EN, + i2c->base + SYNQUACER_I2C_REG_CCR); + writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR); + break; + case SYNQUACER_I2C_SPEED_SM: + if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) { + ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rt); + csr_cs = SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rt); + } else { + ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rt); + csr_cs = SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rt); + } + + /* Set Clock and enable, Set standard mode */ + writeb(ccr_cs | SYNQUACER_I2C_CCR_EN, + i2c->base + SYNQUACER_I2C_REG_CCR); + writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR); + break; + default: + WARN_ON(1); + } + + /* clear IRQ (INT=0, BER=0), Interrupt Disable */ + writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR); + writeb(0, i2c->base + SYNQUACER_I2C_REG_BC2R); +} + +static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c) +{ + /* Disable clock */ + writeb(0, i2c->base + SYNQUACER_I2C_REG_CCR); + writeb(0, i2c->base + SYNQUACER_I2C_REG_CSR); + + WAIT_PCLK(100, i2c->pclkrate); +} + +static int synquacer_i2c_master_start(struct synquacer_i2c *i2c, + struct i2c_msg *pmsg) +{ + unsigned char bsr, bcr; + + writeb(i2c_8bit_addr_from_msg(pmsg), i2c->base + SYNQUACER_I2C_REG_DAR); + + dev_dbg(i2c->dev, "slave:0x%02x\n", pmsg->addr); + + /* Generate Start Condition */ + bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); + bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR); + dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr); + + if ((bsr & SYNQUACER_I2C_BSR_BB) && + !(bcr & SYNQUACER_I2C_BCR_MSS)) { + dev_dbg(i2c->dev, "bus is busy"); + return -EBUSY; + } + + if (bsr & SYNQUACER_I2C_BSR_BB) { /* Bus is busy */ + dev_dbg(i2c->dev, "Continuous Start"); + writeb(bcr | SYNQUACER_I2C_BCR_SCC, + i2c->base + SYNQUACER_I2C_REG_BCR); + } else { + if (bcr & SYNQUACER_I2C_BCR_MSS) { + dev_dbg(i2c->dev, "not in master mode"); + return -EAGAIN; + } + dev_dbg(i2c->dev, "Start Condition"); + /* Start Condition + Enable Interrupts */ + writeb(bcr | SYNQUACER_I2C_BCR_MSS | + SYNQUACER_I2C_BCR_INTE | SYNQUACER_I2C_BCR_BEIE, + i2c->base + SYNQUACER_I2C_REG_BCR); + } + + WAIT_PCLK(10, i2c->pclkrate); + + /* get BSR & BCR registers */ + bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); + bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR); + dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr); + + if ((bsr & SYNQUACER_I2C_BSR_AL) || + !(bcr & SYNQUACER_I2C_BCR_MSS)) { + dev_dbg(i2c->dev, "arbitration lost\n"); + return -EAGAIN; + } + + return 0; +} + +static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + unsigned char bsr; + unsigned long time_left; + int ret; + + synquacer_i2c_hw_init(i2c); + bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); + if (bsr & SYNQUACER_I2C_BSR_BB) { + dev_err(i2c->dev, "cannot get bus (bus busy)\n"); + return -EBUSY; + } + + reinit_completion(&i2c->completion); + + i2c->msg = msgs; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->state = STATE_START; + + ret = synquacer_i2c_master_start(i2c, i2c->msg); + if (ret < 0) { + dev_dbg(i2c->dev, "Address failed: (%d)\n", ret); + return ret; + } + + 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; + } + + ret = i2c->msg_idx; + if (ret != num) { + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + return -EAGAIN; + } + + /* wait 2 clock periods to ensure the stop has been through the bus */ + udelay(DIV_ROUND_UP(2 * 1000, i2c->speed_khz)); + + return ret; +} + +static irqreturn_t synquacer_i2c_isr(int irq, void *dev_id) +{ + struct synquacer_i2c *i2c = dev_id; + + unsigned char byte; + unsigned char bsr, bcr; + int ret; + + bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR); + bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); + dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr); + + if (bcr & SYNQUACER_I2C_BCR_BER) { + dev_err(i2c->dev, "bus error\n"); + synquacer_i2c_stop(i2c, -EAGAIN); + goto out; + } + if ((bsr & SYNQUACER_I2C_BSR_AL) || + !(bcr & SYNQUACER_I2C_BCR_MSS)) { + dev_dbg(i2c->dev, "arbitration lost\n"); + synquacer_i2c_stop(i2c, -EAGAIN); + goto out; + } + + switch (i2c->state) { + case STATE_START: + if (bsr & SYNQUACER_I2C_BSR_LRB) { + dev_dbg(i2c->dev, "ack was not received\n"); + synquacer_i2c_stop(i2c, -EAGAIN); + goto out; + } + + if (i2c->msg->flags & I2C_M_RD) + i2c->state = STATE_READ; + else + i2c->state = STATE_WRITE; + + if (is_lastmsg(i2c) && i2c->msg->len == 0) { + synquacer_i2c_stop(i2c, 0); + goto out; + } + + if (i2c->state == STATE_READ) + goto prepare_read; + fallthrough; + + case STATE_WRITE: + if (bsr & SYNQUACER_I2C_BSR_LRB) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + synquacer_i2c_stop(i2c, -EAGAIN); + goto out; + } + + if (!is_msgend(i2c)) { + writeb(i2c->msg->buf[i2c->msg_ptr++], + i2c->base + SYNQUACER_I2C_REG_DAR); + + /* clear IRQ, and continue */ + writeb(SYNQUACER_I2C_BCR_BEIE | + SYNQUACER_I2C_BCR_MSS | + SYNQUACER_I2C_BCR_INTE, + i2c->base + SYNQUACER_I2C_REG_BCR); + break; + } + if (is_lastmsg(i2c)) { + synquacer_i2c_stop(i2c, 0); + break; + } + dev_dbg(i2c->dev, "WRITE: Next Message\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + /* send the new start */ + ret = synquacer_i2c_master_start(i2c, i2c->msg); + if (ret < 0) { + dev_dbg(i2c->dev, "restart error (%d)\n", ret); + synquacer_i2c_stop(i2c, -EAGAIN); + break; + } + i2c->state = STATE_START; + break; + + case STATE_READ: + byte = readb(i2c->base + SYNQUACER_I2C_REG_DAR); + if (!(bsr & SYNQUACER_I2C_BSR_FBT)) /* data */ + i2c->msg->buf[i2c->msg_ptr++] = byte; + else /* address */ + dev_dbg(i2c->dev, "address:0x%02x. ignore it.\n", byte); + +prepare_read: + if (is_msglast(i2c)) { + writeb(SYNQUACER_I2C_BCR_MSS | + SYNQUACER_I2C_BCR_BEIE | + SYNQUACER_I2C_BCR_INTE, + i2c->base + SYNQUACER_I2C_REG_BCR); + break; + } + if (!is_msgend(i2c)) { + writeb(SYNQUACER_I2C_BCR_MSS | + SYNQUACER_I2C_BCR_BEIE | + SYNQUACER_I2C_BCR_INTE | + SYNQUACER_I2C_BCR_ACK, + i2c->base + SYNQUACER_I2C_REG_BCR); + break; + } + if (is_lastmsg(i2c)) { + /* last message, send stop and complete */ + dev_dbg(i2c->dev, "READ: Send Stop\n"); + synquacer_i2c_stop(i2c, 0); + break; + } + dev_dbg(i2c->dev, "READ: Next Transfer\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + ret = synquacer_i2c_master_start(i2c, i2c->msg); + if (ret < 0) { + dev_dbg(i2c->dev, "restart error (%d)\n", ret); + synquacer_i2c_stop(i2c, -EAGAIN); + } else { + i2c->state = STATE_START; + } + break; + default: + dev_err(i2c->dev, "called in err STATE (%d)\n", i2c->state); + break; + } + +out: + WAIT_PCLK(10, i2c->pclkrate); + return IRQ_HANDLED; +} + +static int synquacer_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct synquacer_i2c *i2c; + int retry; + int ret; + + i2c = i2c_get_adapdata(adap); + i2c->timeout_ms = calc_timeout_ms(i2c, msgs, num); + + dev_dbg(i2c->dev, "calculated timeout %d ms\n", i2c->timeout_ms); + + for (retry = 0; retry <= adap->retries; retry++) { + ret = synquacer_i2c_doxfer(i2c, msgs, num); + if (ret != -EAGAIN) + return ret; + + dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); + + synquacer_i2c_hw_reset(i2c); + } + return -EIO; +} + +static u32 synquacer_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm synquacer_i2c_algo = { + .xfer = synquacer_i2c_xfer, + .functionality = synquacer_i2c_functionality, +}; + +static const struct i2c_adapter synquacer_i2c_ops = { + .owner = THIS_MODULE, + .name = "synquacer_i2c-adapter", + .algo = &synquacer_i2c_algo, + .retries = 5, +}; + +static int synquacer_i2c_probe(struct platform_device *pdev) +{ + struct synquacer_i2c *i2c; + struct clk *pclk; + u32 bus_speed; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + bus_speed = i2c_acpi_find_bus_speed(&pdev->dev); + if (!bus_speed) + device_property_read_u32(&pdev->dev, "clock-frequency", + &bus_speed); + + device_property_read_u32(&pdev->dev, "socionext,pclk-rate", + &i2c->pclkrate); + + 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"); + + if (pclk) + i2c->pclkrate = clk_get_rate(pclk); + + if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE || + i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) + return dev_err_probe(&pdev->dev, -EINVAL, + "PCLK missing or out of range (%d)\n", + i2c->pclkrate); + + 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) + return i2c->irq; + + ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr, + 0, dev_name(&pdev->dev), i2c); + if (ret < 0) + return ret; + + 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 < I2C_MAX_FAST_MODE_FREQ) + i2c->speed_khz = SYNQUACER_I2C_SPEED_SM; + else + i2c->speed_khz = SYNQUACER_I2C_SPEED_FM; + + synquacer_i2c_hw_init(i2c); + + ret = i2c_add_numbered_adapter(&i2c->adapter); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to add bus to i2c core\n"); + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "%s: synquacer_i2c adapter\n", + dev_name(&i2c->adapter.dev)); + + return 0; +} + +static void synquacer_i2c_remove(struct platform_device *pdev) +{ + struct synquacer_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adapter); +}; + +static const struct of_device_id synquacer_i2c_dt_ids[] __maybe_unused = { + { .compatible = "socionext,synquacer-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, synquacer_i2c_dt_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id synquacer_i2c_acpi_ids[] = { + { "SCX0003" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids); +#endif + +static struct platform_driver synquacer_i2c_driver = { + .probe = synquacer_i2c_probe, + .remove = synquacer_i2c_remove, + .driver = { + .name = "synquacer_i2c", + .of_match_table = of_match_ptr(synquacer_i2c_dt_ids), + .acpi_match_table = ACPI_PTR(synquacer_i2c_acpi_ids), + }, +}; +module_platform_driver(synquacer_i2c_driver); + +MODULE_AUTHOR("Fujitsu Semiconductor Ltd"); +MODULE_DESCRIPTION("Socionext SynQuacer I2C Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c new file mode 100644 index 000000000..debaf5189 --- /dev/null +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TAOS evaluation modules + * These devices include an I2C controller which can be controlled over the + * serial port. + * + * Copyright (C) 2007 Jean Delvare + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAOS_BUFFER_SIZE 63 + +#define TAOS_STATE_INIT 0 +#define TAOS_STATE_IDLE 1 +#define TAOS_STATE_EOFF 2 +#define TAOS_STATE_RECV 3 + +#define TAOS_CMD_RESET 0x12 +#define TAOS_CMD_ECHO_ON '+' +#define TAOS_CMD_ECHO_OFF '-' + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +struct taos_data { + struct i2c_adapter adapter; + struct i2c_client *client; + int state; + u8 addr; /* last used address */ + unsigned char buffer[TAOS_BUFFER_SIZE]; + unsigned int pos; /* position inside the buffer */ +}; + +/* TAOS TSL2550 EVM */ +static const struct i2c_board_info tsl2550_info = { + I2C_BOARD_INFO("tsl2550", 0x39), +}; + +/* Instantiate i2c devices based on the adapter name */ +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_client_device(adapter, &tsl2550_info); + } + + return ERR_PTR(-ENODEV); +} + +static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct serio *serio = adapter->algo_data; + struct taos_data *taos = serio_get_drvdata(serio); + char *p; + + /* Encode our transaction. "@" is for the device address, "$" for the + SMBus command and "#" for the data. */ + p = taos->buffer; + + /* The device remembers the last used address, no need to send it + again if it's the same */ + if (addr != taos->addr) + p += sprintf(p, "@%02X", addr); + + switch (size) { + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + sprintf(p, "$#%02X", command); + else + sprintf(p, "$"); + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) + sprintf(p, "$%02X#%02X", command, data->byte); + else + sprintf(p, "$%02X", command); + break; + default: + dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + /* Send the transaction to the TAOS EVM */ + dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer); + for (p = taos->buffer; *p; p++) + serio_write(serio, *p); + + taos->addr = addr; + + /* Start the transaction and read the answer */ + taos->pos = 0; + taos->state = TAOS_STATE_RECV; + serio_write(serio, read_write == I2C_SMBUS_WRITE ? '>' : '<'); + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(150)); + if (taos->state != TAOS_STATE_IDLE + || taos->pos != 5) { + dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n", + taos->pos); + return -EIO; + } + dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer); + + /* Interpret the returned string */ + p = taos->buffer + 1; + p[3] = '\0'; + if (!strcmp(p, "NAK")) + return -ENODEV; + + if (read_write == I2C_SMBUS_WRITE) { + if (!strcmp(p, "ACK")) + return 0; + } else { + if (p[0] == 'x') { + /* + * Voluntarily dropping error code of kstrtou8 since all + * error code that it could return are invalid according + * to Documentation/i2c/fault-codes.rst. + */ + if (kstrtou8(p + 1, 16, &data->byte)) + return -EPROTO; + return 0; + } + } + + return -EIO; +} + +static u32 taos_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA; +} + +static const struct i2c_algorithm taos_algorithm = { + .smbus_xfer = taos_smbus_xfer, + .functionality = taos_smbus_func, +}; + +static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct taos_data *taos = serio_get_drvdata(serio); + + switch (taos->state) { + case TAOS_STATE_INIT: + taos->buffer[taos->pos++] = data; + if (data == ':' + || taos->pos == TAOS_BUFFER_SIZE - 1) { + taos->buffer[taos->pos] = '\0'; + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + } + break; + case TAOS_STATE_EOFF: + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + break; + case TAOS_STATE_RECV: + taos->buffer[taos->pos++] = data; + if (data == ']') { + taos->buffer[taos->pos] = '\0'; + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + } + break; + } + + return IRQ_HANDLED; +} + +/* Extract the adapter name from the buffer received after reset. + The buffer is modified and a pointer inside the buffer is returned. */ +static char *taos_adapter_name(char *buffer) +{ + char *start, *end; + + start = strstr(buffer, "TAOS "); + if (!start) + return NULL; + + end = strchr(start, '\r'); + if (!end) + return NULL; + *end = '\0'; + + return start; +} + +static int taos_connect(struct serio *serio, struct serio_driver *drv) +{ + struct taos_data *taos; + struct i2c_adapter *adapter; + char *name; + int err; + + taos = kzalloc_obj(struct taos_data); + if (!taos) { + err = -ENOMEM; + goto exit; + } + taos->state = TAOS_STATE_INIT; + serio_set_drvdata(serio, taos); + + err = serio_open(serio, drv); + if (err) + goto exit_kfree; + + adapter = &taos->adapter; + adapter->owner = THIS_MODULE; + adapter->algo = &taos_algorithm; + adapter->algo_data = serio; + adapter->dev.parent = &serio->dev; + + /* Reset the TAOS evaluation module to identify it */ + serio_write(serio, TAOS_CMD_RESET); + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(2000)); + + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM reset failed (state=%d, " + "pos=%d)\n", taos->state, taos->pos); + goto exit_close; + } + + name = taos_adapter_name(taos->buffer); + if (!name) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM identification failed\n"); + goto exit_close; + } + strscpy(adapter->name, name, sizeof(adapter->name)); + + /* Turn echo off for better performance */ + taos->state = TAOS_STATE_EOFF; + serio_write(serio, TAOS_CMD_ECHO_OFF); + + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(250)); + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM echo off failed " + "(state=%d)\n", taos->state); + goto exit_close; + } + + err = i2c_add_adapter(adapter); + if (err) + goto exit_close; + dev_info(&serio->dev, "Connected to TAOS EVM\n"); + + taos->client = taos_instantiate_device(adapter); + return 0; + + exit_close: + serio_close(serio); + exit_kfree: + kfree(taos); + exit: + return err; +} + +static void taos_disconnect(struct serio *serio) +{ + struct taos_data *taos = serio_get_drvdata(serio); + + i2c_unregister_device(taos->client); + i2c_del_adapter(&taos->adapter); + serio_close(serio); + kfree(taos); + + dev_info(&serio->dev, "Disconnected from TAOS EVM\n"); +} + +static const struct serio_device_id taos_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TAOSEVM, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(serio, taos_serio_ids); + +static struct serio_driver taos_drv = { + .driver = { + .name = "taos-evm", + }, + .description = "TAOS evaluation module driver", + .id_table = taos_serio_ids, + .connect = taos_connect, + .disconnect = taos_disconnect, + .interrupt = taos_interrupt, +}; + +module_serio_driver(taos_drv); + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("TAOS evaluation module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c new file mode 100644 index 000000000..bb0de6db6 --- /dev/null +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -0,0 +1,345 @@ +// 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Serialized I2C message header size is 6 bytes and includes address, flags + * and length + */ +#define SERIALI2C_HDR_SIZE 6 + +struct tegra_bpmp_i2c { + struct i2c_adapter adapter; + struct device *dev; + + struct tegra_bpmp *bpmp; + unsigned int bus; +}; + +/* + * Linux flags are translated to BPMP defined I2C flags that are used in BPMP + * firmware I2C driver to avoid any issues in future if Linux I2C flags are + * changed. + */ +static void tegra_bpmp_xlate_flags(u16 flags, u16 *out) +{ + if (flags & I2C_M_TEN) + *out |= SERIALI2C_TEN; + + if (flags & I2C_M_RD) + *out |= SERIALI2C_RD; + + if (flags & I2C_M_STOP) + *out |= SERIALI2C_STOP; + + if (flags & I2C_M_NOSTART) + *out |= SERIALI2C_NOSTART; + + if (flags & I2C_M_REV_DIR_ADDR) + *out |= SERIALI2C_REV_DIR_ADDR; + + if (flags & I2C_M_IGNORE_NAK) + *out |= SERIALI2C_IGNORE_NAK; + + if (flags & I2C_M_NO_RD_ACK) + *out |= SERIALI2C_NO_RD_ACK; + + if (flags & I2C_M_RECV_LEN) + *out |= SERIALI2C_RECV_LEN; +} + +/* + * 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] + * ... + * + * The flags are translated from Linux kernel representation to seriali2c + * representation. Any undefined flag being set causes an error. + * + * The data is there only for writes. Reads have the data transferred in the + * other direction, and thus data is not present. + * + * See deserialize_i2c documentation for the data format in the other direction. + */ +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; + + for (i = 0; i < num; i++) { + struct i2c_msg *msg = &msgs[i]; + u16 flags = 0; + + tegra_bpmp_xlate_flags(msg->flags, &flags); + + buf[pos++] = msg->addr & 0xff; + buf[pos++] = (msg->addr & 0xff00) >> 8; + buf[pos++] = flags & 0xff; + buf[pos++] = (flags & 0xff00) >> 8; + buf[pos++] = msg->len & 0xff; + buf[pos++] = (msg->len & 0xff00) >> 8; + + if ((flags & SERIALI2C_RD) == 0) { + for (j = 0; j < msg->len; j++) + buf[pos++] = msg->buf[j]; + } + } + + request->xfer.data_size = pos; +} + +/* + * 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: + * + * - !I2C_M_RD, len == 5, data == a0 01 02 03 04 + * - !I2C_M_RD, len == 1, data == a0 + * - I2C_M_RD, len == 2, data == [uninitialized buffer 1] + * - !I2C_M_RD, len == 1, data == a2 + * - I2C_M_RD, len == 2, data == [uninitialized buffer 2] + * + * ...then the data in the BPMP -> CPU direction would be 4 bytes total, and + * would contain 2 bytes that will go to uninitialized buffer 1, and 2 bytes + * that will go to uninitialized buffer 2. + */ +static int tegra_bpmp_i2c_deserialize(struct tegra_bpmp_i2c *i2c, + struct mrq_i2c_response *response, + struct i2c_msg *msgs, + unsigned int num) +{ + size_t size = response->xfer.data_size, len = 0, pos = 0; + char *buf = response->xfer.data_buf; + unsigned int i; + + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + len += msgs[i].len; + + if (len != size) + return -EINVAL; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { + memcpy(msgs[i].buf, buf + pos, msgs[i].len); + pos += msgs[i].len; + } + } + + return 0; +} + +static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num) +{ + size_t tx_len = 0, rx_len = 0; + unsigned int i; + + for (i = 0; i < num; i++) + if (!(msgs[i].flags & I2C_M_RD)) + tx_len += SERIALI2C_HDR_SIZE + msgs[i].len; + + if (tx_len > TEGRA_I2C_IPC_MAX_IN_BUF_SIZE) + return -EINVAL; + + for (i = 0; i < num; i++) + if ((msgs[i].flags & I2C_M_RD)) + rx_len += msgs[i].len; + + if (rx_len > TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE) + return -EINVAL; + + return 0; +} + +static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, + struct mrq_i2c_request *request, + struct mrq_i2c_response *response, + bool atomic) +{ + struct tegra_bpmp_message msg; + int err; + + request->cmd = CMD_I2C_XFER; + request->xfer.bus_id = i2c->bus; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_I2C; + msg.tx.data = request; + msg.tx.size = sizeof(*request); + msg.rx.data = response; + msg.rx.size = sizeof(*response); + + if (atomic) + err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg); + else + err = tegra_bpmp_transfer(i2c->bpmp, &msg); + + 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_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; + struct mrq_i2c_request request; + int err; + + err = tegra_bpmp_i2c_msg_len_check(msgs, num); + if (err < 0) { + dev_err(i2c->dev, "unsupported message length\n"); + return err; + } + + memset(&request, 0, sizeof(request)); + memset(&response, 0, sizeof(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; + } + + err = tegra_bpmp_i2c_deserialize(i2c, &response, msgs, num); + if (err < 0) { + dev_err(i2c->dev, "failed to deserialize message: %d\n", err); + return err; + } + + 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 | + I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm tegra_bpmp_i2c_algo = { + .xfer = tegra_bpmp_i2c_xfer, + .xfer_atomic = tegra_bpmp_i2c_xfer_atomic, + .functionality = tegra_bpmp_i2c_func, +}; + +static int tegra_bpmp_i2c_probe(struct platform_device *pdev) +{ + struct tegra_bpmp_i2c *i2c; + u32 value; + int err; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->dev = &pdev->dev; + + i2c->bpmp = dev_get_drvdata(pdev->dev.parent); + if (!i2c->bpmp) + return -ENODEV; + + err = of_property_read_u32(pdev->dev.of_node, "nvidia,bpmp-bus-id", + &value); + if (err < 0) + return err; + + i2c->bus = value; + + i2c_set_adapdata(&i2c->adapter, i2c); + i2c->adapter.owner = THIS_MODULE; + 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; + i2c->adapter.dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, i2c); + + return i2c_add_adapter(&i2c->adapter); +} + +static void tegra_bpmp_i2c_remove(struct platform_device *pdev) +{ + struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adapter); +} + +static const struct of_device_id tegra_bpmp_i2c_of_match[] = { + { .compatible = "nvidia,tegra186-bpmp-i2c", }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_bpmp_i2c_of_match); + +static struct platform_driver tegra_bpmp_i2c_driver = { + .driver = { + .name = "tegra-bpmp-i2c", + .of_match_table = tegra_bpmp_i2c_of_match, + }, + .probe = tegra_bpmp_i2c_probe, + .remove = tegra_bpmp_i2c_remove, +}; +module_platform_driver(tegra_bpmp_i2c_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus controller driver"); +MODULE_AUTHOR("Shardar Shariff Md "); +MODULE_AUTHOR("Juha-Matti Tilli"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c new file mode 100644 index 000000000..9c281b490 --- /dev/null +++ b/drivers/i2c/busses/i2c-tegra.c @@ -0,0 +1,2511 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/i2c/busses/i2c-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BYTES_PER_FIFO_WORD 4 + +#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_SL_CNFG_NACK BIT(1) +#define I2C_SL_CNFG_NEWSL BIT(2) + +#define I2C_FIFO_CONTROL_TX_FLUSH BIT(1) +#define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) +#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5) +#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2) + +#define I2C_FIFO_STATUS_TX GENMASK(7, 4) +#define I2C_FIFO_STATUS_RX GENMASK(3, 0) + +#define I2C_INT_BUS_CLR_DONE BIT(11) +#define I2C_INT_PACKET_XFER_COMPLETE BIT(7) +#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_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_REG3 0x008 +#define DVC_CTRL_REG3_SW_PROG BIT(26) +#define DVC_CTRL_REG3_I2C_DONE_INTR_EN BIT(30) +#define DVC_STATUS 0x00c +#define DVC_STATUS_I2C_DONE_INTR BIT(30) + +#define I2C_ERR_NONE 0x00 +#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 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_HS_MODE BIT(22) +#define I2C_HEADER_CONT_ON_NAK BIT(21) +#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_SLAVE_ADDR_SHIFT 1 + +#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_BC_STATUS BIT(0) + +#define I2C_MSTR_CONFIG_LOAD BIT(0) + +#define I2C_MST_CORE_CLKEN_OVR BIT(0) + +#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8) +#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0) +#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_THIGH GENMASK(13, 8) +#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0) +#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_RX_FLUSH BIT(0) +#define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1) +#define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4) +#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16) + +#define I2C_MST_FIFO_STATUS_TX GENMASK(23, 16) +#define I2C_MST_FIFO_STATUS_RX GENMASK(7, 0) + +#define I2C_SW_MUTEX_REQUEST GENMASK(3, 0) +#define I2C_SW_MUTEX_GRANT GENMASK(7, 4) +#define I2C_SW_MUTEX_ID_CCPLEX 9 + +/* SW mutex acquire timeout value in microseconds. */ +#define I2C_SW_MUTEX_TIMEOUT_US (25 * USEC_PER_MSEC) + +/* configuration load timeout in microseconds */ +#define I2C_CONFIG_LOAD_TIMEOUT 1000000 + +/* packet header size in bytes */ +#define I2C_PACKET_HEADER_SIZE 12 + +/* + * 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 + +struct tegra_i2c_regs { + unsigned int cnfg; + unsigned int status; + unsigned int sl_cnfg; + unsigned int sl_addr1; + unsigned int sl_addr2; + unsigned int tlow_sext; + unsigned int tx_fifo; + unsigned int rx_fifo; + unsigned int packet_transfer_status; + unsigned int fifo_control; + unsigned int fifo_status; + unsigned int int_mask; + unsigned int int_status; + unsigned int clk_divisor; + unsigned int bus_clear_cnfg; + unsigned int bus_clear_status; + unsigned int config_load; + unsigned int clken_override; + unsigned int interface_timing_0; + unsigned int interface_timing_1; + unsigned int hs_interface_timing_0; + unsigned int hs_interface_timing_1; + unsigned int master_reset_cntrl; + unsigned int mst_fifo_control; + unsigned int mst_fifo_status; + unsigned int fairness_arb; + unsigned int sw_mutex; +}; + +static const struct tegra_i2c_regs tegra20_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x050, + .rx_fifo = 0x054, + .packet_transfer_status = 0x058, + .fifo_control = 0x05c, + .fifo_status = 0x060, + .int_mask = 0x064, + .int_status = 0x068, + .clk_divisor = 0x06c, + .bus_clear_cnfg = 0x084, + .bus_clear_status = 0x088, + .config_load = 0x08c, + .clken_override = 0x090, + .interface_timing_0 = 0x094, + .interface_timing_1 = 0x098, + .hs_interface_timing_0 = 0x09c, + .hs_interface_timing_1 = 0x0a0, + .master_reset_cntrl = 0x0a8, + .mst_fifo_control = 0x0b4, + .mst_fifo_status = 0x0b8, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) +static const struct tegra_i2c_regs tegra20_dvc_i2c_regs = { + .cnfg = 0x040, + .status = 0x05c, + .tx_fifo = 0x060, + .rx_fifo = 0x064, + .packet_transfer_status = 0x068, + .fifo_control = 0x06c, + .fifo_status = 0x070, + .int_mask = 0x074, + .int_status = 0x078, + .clk_divisor = 0x07c, + .bus_clear_cnfg = 0x094, + .bus_clear_status = 0x098, + .config_load = 0x09c, + .clken_override = 0x0a0, + .interface_timing_0 = 0x0a4, + .interface_timing_1 = 0x0a8, + .hs_interface_timing_0 = 0x0ac, + .hs_interface_timing_1 = 0x0b0, + .master_reset_cntrl = 0x0b8, + .mst_fifo_control = 0x0c4, + .mst_fifo_status = 0x0c8, +}; +#endif + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_i2c_regs tegra210_vi_i2c_regs = { + .cnfg = 0x0c00, + .status = 0x0c70, + .tlow_sext = 0x0cd0, + .tx_fifo = 0x0d40, + .rx_fifo = 0x0d50, + .packet_transfer_status = 0x0d60, + .fifo_control = 0x0d70, + .fifo_status = 0x0d80, + .int_mask = 0x0d90, + .int_status = 0x0da0, + .clk_divisor = 0x0db0, + .bus_clear_cnfg = 0x0e10, + .bus_clear_status = 0x0e20, + .config_load = 0x0e30, + .clken_override = 0x0e40, + .interface_timing_0 = 0x0e50, + .interface_timing_1 = 0x0e60, + .hs_interface_timing_0 = 0x0e70, + .hs_interface_timing_1 = 0x0e80, + .master_reset_cntrl = 0x0ea0, + .mst_fifo_control = 0x0ed0, + .mst_fifo_status = 0x0ee0, +}; +#endif + +static const struct tegra_i2c_regs tegra264_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x050, + .rx_fifo = 0x054, + .packet_transfer_status = 0x058, + .fifo_control = 0x05c, + .fifo_status = 0x060, + .int_mask = 0x064, + .int_status = 0x068, + .clk_divisor = 0x06c, + .bus_clear_cnfg = 0x084, + .bus_clear_status = 0x088, + .config_load = 0x08c, + .clken_override = 0x090, + .interface_timing_0 = 0x094, + .interface_timing_1 = 0x098, + .hs_interface_timing_0 = 0x09c, + .hs_interface_timing_1 = 0x0a0, + .master_reset_cntrl = 0x0a8, + .mst_fifo_control = 0x0b4, + .mst_fifo_status = 0x0b8, + .fairness_arb = 0x0e8, + .sw_mutex = 0x0ec, +}; + +static const struct tegra_i2c_regs tegra410_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x054, + .rx_fifo = 0x058, + .packet_transfer_status = 0x05c, + .fifo_control = 0x060, + .fifo_status = 0x064, + .int_mask = 0x068, + .int_status = 0x06c, + .clk_divisor = 0x070, + .bus_clear_cnfg = 0x088, + .bus_clear_status = 0x08c, + .config_load = 0x090, + .clken_override = 0x094, + .interface_timing_0 = 0x098, + .interface_timing_1 = 0x09c, + .hs_interface_timing_0 = 0x0a0, + .hs_interface_timing_1 = 0x0a4, + .master_reset_cntrl = 0x0ac, + .mst_fifo_control = 0x0b8, + .mst_fifo_status = 0x0bc, + .fairness_arb = 0x0ec, + .sw_mutex = 0x0f0, +}; + +/* + * 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, + MSG_END_REPEAT_START, + MSG_END_CONTINUE, +}; + +/* + * tegra_i2c_variant: Identifies the variant of I2C controller. + * @TEGRA_I2C_VARIANT_DEFAULT: Identifies the default I2C controller. + * @TEGRA_I2C_VARIANT_DVC: Identifies the DVC I2C controller, has a different register layout. + * @TEGRA_I2C_VARIANT_VI: Identifies the VI I2C controller, has a different register layout. + */ +enum tegra_i2c_variant { + TEGRA_I2C_VARIANT_DEFAULT, + TEGRA_I2C_VARIANT_DVC, + TEGRA_I2C_VARIANT_VI, +}; + +/** + * 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 + * 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_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 + * applicable if there is no fast clock source (i.e. single + * clock source). + * @has_multi_master_mode: The I2C controller supports running in single-master + * or multi-master mode. + * @has_slcg_override_reg: The I2C controller supports a register that + * overrides the second level clock gating. + * @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. + * @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_mode: Low period of the clock in fast mode. + * @thigh_fast_mode: High period of the clock in fast mode. + * @tlow_fastplus_mode: Low period of the clock in fast-plus mode. + * @thigh_fastplus_mode: High period of the clock in fast-plus mode. + * @tlow_hs_mode: Low period of the clock in HS mode. + * @thigh_hs_mode: High period of the clock in HS mode. + * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions + * in standard mode. + * @setup_hold_time_fast_mode: Setup and hold time for start and stop + * conditions in fast mode. + * @setup_hold_time_fastplus_mode: Setup and hold time for start and stop + * conditions in fast-plus mode. + * @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. + * @enable_hs_mode_support: Enable support for high speed (HS) mode transfers. + * @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs. + * @has_fairarb_reg: Has fairness arbitration register for SMBUS/MCTP support. + * @variant: This represents the I2C controller variant. + * @regs: Register offsets for the specific SoC variant. + */ +struct tegra_i2c_hw_feature { + bool has_continue_xfer_support; + bool has_per_pkt_xfer_complete_irq; + bool has_config_load_reg; + 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_mode; + u32 thigh_fast_mode; + u32 tlow_fastplus_mode; + u32 thigh_fastplus_mode; + u32 tlow_hs_mode; + u32 thigh_hs_mode; + u32 setup_hold_time_std_mode; + u32 setup_hold_time_fast_mode; + u32 setup_hold_time_fastplus_mode; + u32 setup_hold_time_hs_mode; + bool has_interface_timing_reg; + bool enable_hs_mode_support; + bool has_mutex; + bool has_fairarb_reg; + enum tegra_i2c_variant variant; + const struct tegra_i2c_regs *regs; +}; + +/** + * struct tegra_i2c_dev - per device I2C context + * @dev: device reference for power management + * @hw: Tegra I2C HW feature + * @adapter: core I2C layer adapter information + * @div_clk: clock reference for div clock of I2C controller + * @clocks: array of I2C controller clocks + * @nclocks: number of clocks in the array + * @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 + * @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_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 + * @is_mctp: indicates that the I2C controller is used as an MCTP controller + * @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; + struct i2c_adapter adapter; + + const struct tegra_i2c_hw_feature *hw; + unsigned int cont_id; + unsigned int irq; + + phys_addr_t base_phys; + void __iomem *base; + + 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; + + 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 is_mctp; + bool atomic_mode; + bool dma_mode; + bool msg_read; +}; + +#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && \ + (dev)->hw->variant == TEGRA_I2C_VARIANT_DVC) +#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && \ + (dev)->hw->variant == TEGRA_I2C_VARIANT_VI) + +static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, + unsigned int reg) +{ + writel_relaxed(val, i2c_dev->base + reg); +} + +static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg) +{ + return readl_relaxed(i2c_dev->base + reg); +} + +static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg) +{ + writel_relaxed(val, i2c_dev->base + reg); + + /* read back register to make sure that register writes completed */ + if (reg != i2c_dev->hw->regs->tx_fifo) + readl_relaxed(i2c_dev->base + reg); + else if (IS_VI(i2c_dev)) + readl_relaxed(i2c_dev->base + i2c_dev->hw->regs->int_status); +} + +static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg) +{ + return readl_relaxed(i2c_dev->base + reg); +} + +static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data, + unsigned int reg, unsigned int len) +{ + writesl(i2c_dev->base + 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 int reg, unsigned int len) +{ + readsl(i2c_dev->base + reg, data, len); +} + +static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev) +{ + unsigned int reg = i2c_dev->hw->regs->sw_mutex; + u32 val, id; + + val = readl(i2c_dev->base + reg); + id = FIELD_GET(I2C_SW_MUTEX_GRANT, val); + + return id == I2C_SW_MUTEX_ID_CCPLEX; +} + +static bool tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev) +{ + unsigned int reg = i2c_dev->hw->regs->sw_mutex; + u32 val, id; + + val = readl(i2c_dev->base + reg); + id = FIELD_GET(I2C_SW_MUTEX_GRANT, val); + if (id != 0 && id != I2C_SW_MUTEX_ID_CCPLEX) + return false; + + val = FIELD_PREP(I2C_SW_MUTEX_REQUEST, I2C_SW_MUTEX_ID_CCPLEX); + writel(val, i2c_dev->base + reg); + + return tegra_i2c_mutex_acquired(i2c_dev); +} + +static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev) +{ + bool locked; + int ret; + + if (!i2c_dev->hw->has_mutex) + return 0; + + if (i2c_dev->atomic_mode) + ret = read_poll_timeout_atomic(tegra_i2c_mutex_trylock, locked, locked, + USEC_PER_MSEC, I2C_SW_MUTEX_TIMEOUT_US, + false, i2c_dev); + else + ret = read_poll_timeout(tegra_i2c_mutex_trylock, locked, locked, USEC_PER_MSEC, + I2C_SW_MUTEX_TIMEOUT_US, false, i2c_dev); + + if (ret) + dev_warn(i2c_dev->dev, "failed to acquire mutex\n"); + + return ret; +} + +static void tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev) +{ + unsigned int reg = i2c_dev->hw->regs->sw_mutex; + u32 val, id; + + if (!i2c_dev->hw->has_mutex) + return; + + val = readl(i2c_dev->base + reg); + + id = FIELD_GET(I2C_SW_MUTEX_GRANT, val); + if (WARN(id && id != I2C_SW_MUTEX_ID_CCPLEX, + "unable to unlock mutex, mutex is owned by: %u\n", id)) + return; + + writel(0, i2c_dev->base + reg); +} + +static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask; + + int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) & ~mask; + i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask); +} + +static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask; + + int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) | mask; + i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask); +} + +static void tegra_i2c_dma_complete(void *args) +{ + struct tegra_i2c_dev *i2c_dev = args; + + complete(&i2c_dev->dma_complete); +} + +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len) +{ + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction dir; + + dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len); + + reinit_completion(&i2c_dev->dma_complete); + + dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + + 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; + } + + 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 void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev) +{ + 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; + } + + if (i2c_dev->dma_chan) { + dma_release_channel(i2c_dev->dma_chan); + i2c_dev->dma_chan = NULL; + } +} + +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) +{ + dma_addr_t dma_phys; + u32 *dma_buf; + int err; + + if (IS_VI(i2c_dev)) + return 0; + + if (!of_property_present(i2c_dev->dev->of_node, "dmas")) { + dev_dbg(i2c_dev->dev, "DMA not available, falling back to PIO\n"); + return 0; + } + + 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; + } + + /* + * The same channel will be used for both RX and TX. + * Keeping the name as "tx" for backward compatibility + * with existing devicetrees. + */ + 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; + } + + i2c_dev->dma_dev = dmaengine_get_dma_device(i2c_dev->dma_chan); + i2c_dev->dma_buf_size = i2c_dev->hw->quirks->max_write_len + + I2C_PACKET_HEADER_SIZE; + + dma_buf = dma_alloc_coherent(i2c_dev->dma_dev, i2c_dev->dma_buf_size, + &dma_phys, GFP_KERNEL | __GFP_NOWARN); + if (!dma_buf) { + err = dev_err_probe(i2c_dev->dev, -ENOMEM, + "failed to allocate DMA buffer\n"); + 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, falling back to PIO\n"); + return 0; + } + + return err; +} + +/* + * One of the Tegra I2C blocks is inside the DVC (Digital Voltage Controller) + * 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(). + */ +static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + + val = dvc_readl(i2c_dev, DVC_CTRL_REG3); + val |= DVC_CTRL_REG3_SW_PROG; + val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG3); + + val = dvc_readl(i2c_dev, DVC_CTRL_REG1); + val |= DVC_CTRL_REG1_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG1); +} + +static void tegra_i2c_vi_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 value; + + value = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, 2) | + FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, 4); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->interface_timing_0); + + 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_dev->hw->regs->interface_timing_1); + + value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, 3) | + FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, 8); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->hs_interface_timing_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_dev->hw->regs->hs_interface_timing_1); + + value = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND; + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->bus_clear_cnfg); + + i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->tlow_sext); +} + +static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev, + u32 reg, u32 mask, u32 delay_us, + u32 timeout_us) +{ + void __iomem *addr = i2c_dev->base + reg; + u32 val; + + if (!i2c_dev->atomic_mode) + return readl_relaxed_poll_timeout(addr, val, !(val & mask), + delay_us, timeout_us); + + return readl_relaxed_poll_timeout_atomic(addr, val, !(val & mask), + delay_us, timeout_us); +} + +static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) +{ + u32 mask, val, offset; + int err; + + if (i2c_dev->hw->has_mst_fifo) { + mask = I2C_MST_FIFO_CONTROL_TX_FLUSH | + I2C_MST_FIFO_CONTROL_RX_FLUSH; + offset = i2c_dev->hw->regs->mst_fifo_control; + } else { + mask = I2C_FIFO_CONTROL_TX_FLUSH | + I2C_FIFO_CONTROL_RX_FLUSH; + offset = i2c_dev->hw->regs->fifo_control; + } + + 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_wait_for_config_load(struct tegra_i2c_dev *i2c_dev) +{ + int err; + + if (!i2c_dev->hw->has_config_load_reg) + return 0; + + i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, i2c_dev->hw->regs->config_load); + + err = tegra_i2c_poll_register(i2c_dev, i2c_dev->hw->regs->config_load, 0xffffffff, + 1000, I2C_CONFIG_LOAD_TIMEOUT); + if (err) { + dev_err(i2c_dev->dev, "failed to load config\n"); + return err; + } + + 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_dev->hw->regs->master_reset_cntrl); + fsleep(2); + + i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->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; + u32 max_bus_freq_hz; + 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 | + FIELD_PREP(I2C_CNFG_DEBOUNCE_CNT, 2); + + if (i2c_dev->hw->has_multi_master_mode) + val |= I2C_CNFG_MULTI_MASTER_MODE; + + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->cnfg); + i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->int_mask); + + if (IS_VI(i2c_dev)) + tegra_i2c_vi_init(i2c_dev); + + /* Disable fairness arbitration if not an MCTP controller */ + if (i2c_dev->hw->has_fairarb_reg && !i2c_dev->is_mctp) + i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->fairness_arb); + + if (i2c_dev->hw->enable_hs_mode_support) + max_bus_freq_hz = I2C_MAX_HIGH_SPEED_MODE_FREQ; + else + max_bus_freq_hz = I2C_MAX_FAST_MODE_PLUS_FREQ; + + if (WARN_ON(t->bus_freq_hz > max_bus_freq_hz)) + t->bus_freq_hz = max_bus_freq_hz; + + if (t->bus_freq_hz <= 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; + } else if (t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ) { + tlow = i2c_dev->hw->tlow_fast_mode; + thigh = i2c_dev->hw->thigh_fast_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fast_mode; + non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode; + } else if (t->bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ) { + tlow = i2c_dev->hw->tlow_fastplus_mode; + thigh = i2c_dev->hw->thigh_fastplus_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fastplus_mode; + non_hs_mode = i2c_dev->hw->clk_divisor_fast_plus_mode; + } else { + /* + * When using HS mode, i.e. when the bus frequency is greater than fast plus mode, + * the non-hs timing registers will be used for sending the master code byte for + * transition to HS mode. Configure the non-hs timing registers for Fast Mode to + * send the master code byte at 400kHz. + */ + tlow = i2c_dev->hw->tlow_fast_mode; + thigh = i2c_dev->hw->thigh_fast_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fast_mode; + non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode; + } + + /* 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_dev->hw->regs->clk_divisor); + + 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_dev->hw->regs->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_dev->hw->regs->interface_timing_1); + + /* Write HS mode registers. These will get used only for HS mode*/ + if (i2c_dev->hw->enable_hs_mode_support) { + tlow = i2c_dev->hw->tlow_hs_mode; + thigh = i2c_dev->hw->thigh_hs_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_hs_mode; + + val = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, thigh) | + FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, tlow); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->hs_interface_timing_0); + i2c_writel(i2c_dev, tsu_thd, i2c_dev->hw->regs->hs_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_dev->hw->regs->sl_cnfg); + + sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL; + i2c_writel(i2c_dev, sl_cfg, i2c_dev->hw->regs->sl_cnfg); + i2c_writel(i2c_dev, 0xfc, i2c_dev->hw->regs->sl_addr1); + i2c_writel(i2c_dev, 0x00, i2c_dev->hw->regs->sl_addr2); + } + + err = tegra_i2c_flush_fifos(i2c_dev); + if (err) + return err; + + if (i2c_dev->multimaster_mode && i2c_dev->hw->has_slcg_override_reg) + i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, i2c_dev->hw->regs->clken_override); + + err = tegra_i2c_wait_for_config_load(i2c_dev); + if (err) + return err; + + return 0; +} + +static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev) +{ + u32 cnfg; + + /* + * NACK interrupt is generated before the I2C controller generates + * 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->timings.bus_freq_hz)); + + cnfg = i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg); + if (cnfg & I2C_CNFG_PACKET_MODE_EN) + i2c_writel(i2c_dev, cnfg & ~I2C_CNFG_PACKET_MODE_EN, i2c_dev->hw->regs->cnfg); + + 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_dev->hw->regs->mst_fifo_status); + rx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_RX, val); + } else { + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->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_dev->hw->regs->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_dev->hw->regs->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_dev->hw->regs->mst_fifo_status); + tx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_TX, val); + } else { + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->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_dev->hw->regs->tx_fifo, words_to_transfer); + else + i2c_writesl(i2c_dev, buf, i2c_dev->hw->regs->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_dev->hw->regs->tx_fifo); + } + + return 0; +} + +static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) +{ + 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_dev->hw->regs->int_status); + + if (status == 0) { + dev_warn(i2c_dev->dev, "IRQ status 0 %08x %08x %08x\n", + i2c_readl(i2c_dev, i2c_dev->hw->regs->packet_transfer_status), + i2c_readl(i2c_dev, i2c_dev->hw->regs->status), + i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg)); + i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; + goto 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; + if (status & I2C_INT_ARBITRATION_LOST) + i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST; + goto err; + } + + /* + * 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->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_dev->hw->regs->int_status); + 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) { + 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: + /* 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_dev->hw->regs->int_status); + + 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: + return IRQ_HANDLED; +} + +static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, + size_t len) +{ + struct dma_slave_config slv_config = {0}; + u32 val, reg, dma_burst, reg_offset; + int err; + + if (i2c_dev->hw->has_mst_fifo) + reg = i2c_dev->hw->regs->mst_fifo_control; + else + reg = i2c_dev->hw->regs->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 = i2c_dev->hw->regs->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 = i2c_dev->hw->regs->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_dev->hw->regs->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; + + reinit_completion(&i2c_dev->msg_complete); + + val = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND | + I2C_BC_TERMINATE; + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg); + + err = tegra_i2c_wait_for_config_load(i2c_dev); + if (err) + return err; + + val |= I2C_BC_ENABLE; + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg); + tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); + + time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete, 50); + tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); + + if (time_left == 0) { + dev_err(i2c_dev->dev, "failed to clear bus\n"); + return -ETIMEDOUT; + } + + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->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_dev->hw->regs->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_dev->hw->regs->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; + + if (i2c_dev->timings.bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) + packet_header |= I2C_HEADER_HS_MODE; + + if (i2c_dev->dma_mode && !i2c_dev->msg_read) + *dma_buf++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->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 (!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); + dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n", + i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask)); + + 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) { + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n", + time_left, completion_done(&i2c_dev->msg_complete), + i2c_dev->msg_err); + + i2c_dev->dma_mode = false; + + err = tegra_i2c_error_recover(i2c_dev, msg); + if (err) + return err; + + return 0; +} + +static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + 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; + } + + ret = tegra_i2c_mutex_lock(i2c_dev); + if (ret) { + pm_runtime_put(i2c_dev->dev); + return ret; + } + + for (i = 0; i < num; i++) { + 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; + } + + tegra_i2c_mutex_unlock(i2c_dev); + pm_runtime_put(i2c_dev->dev); + + return ret ?: i; +} + +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); + int ret; + + i2c_dev->atomic_mode = true; + ret = tegra_i2c_xfer(adap, msgs, num); + i2c_dev->atomic_mode = false; + + return ret; +} + +static u32 tegra_i2c_func(struct i2c_adapter *adap) +{ + 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; + + if (i2c_dev->hw->has_continue_xfer_support) + ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA; + + return ret; +} + +static const struct i2c_algorithm tegra_i2c_algo = { + .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 = 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, + .clk_divisor_hs_mode = 3, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) +static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = { + .has_continue_xfer_support = false, + .has_per_pkt_xfer_complete_irq = false, + .clk_divisor_hs_mode = 3, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DVC, + .regs = &tegra20_dvc_i2c_regs, +}; +#endif + +static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = false, + .clk_divisor_hs_mode = 3, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = true, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_mode = 0, + .setup_hold_time_fastplus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_mode = 0, + .setup_hold_time_fastplus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_VI, + .regs = &tegra210_vi_i2c_regs, +}; +#endif + +static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_mode = 0, + .setup_hold_time_fastplus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, + .enable_hs_mode_support = false, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +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_mode = 0x2, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x2, + .thigh_fastplus_mode = 0x2, + .tlow_hs_mode = 0x8, + .thigh_hs_mode = 0x3, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_mode = 0x02020202, + .setup_hold_time_fastplus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x090909, + .has_interface_timing_reg = true, + .enable_hs_mode_support = true, + .has_mutex = false, + .has_fairarb_reg = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, +}; + +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 = 9, + .clk_divisor_std_mode = 0x7a, + .clk_divisor_fast_mode = 0x40, + .clk_divisor_fast_plus_mode = 0x14, + .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_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x4, + .tlow_hs_mode = 0x3, + .thigh_hs_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_mode = 0x04010101, + .setup_hold_time_fastplus_mode = 0x04020202, + .setup_hold_time_hs_mode = 0x030303, + .has_interface_timing_reg = true, + .enable_hs_mode_support = true, + .has_mutex = true, + .has_fairarb_reg = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra264_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_mode = 0x1d, + .clk_divisor_fast_mode = 0x15, + .clk_divisor_fast_plus_mode = 0x8, + .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_mode = 0x2, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x2, + .thigh_fastplus_mode = 0x2, + .tlow_hs_mode = 0x4, + .thigh_hs_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_mode = 0x02020202, + .setup_hold_time_fastplus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x090909, + .has_interface_timing_reg = true, + .enable_hs_mode_support = true, + .has_mutex = true, + .has_fairarb_reg = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra264_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 2, + .clk_divisor_std_mode = 0x3f, + .clk_divisor_fast_mode = 0x2f, + .clk_divisor_fast_plus_mode = 0x11, + .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_mode = 0x2, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x2, + .thigh_fastplus_mode = 0x2, + .tlow_hs_mode = 0x5, + .thigh_hs_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_mode = 0x02020202, + .setup_hold_time_fastplus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x0b0b0b, + .has_interface_timing_reg = true, + .enable_hs_mode_support = true, + .has_mutex = true, + .has_fairarb_reg = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra410_i2c_regs, +}; + +static const struct of_device_id tegra_i2c_of_match[] = { + { .compatible = "nvidia,tegra264-i2c", .data = &tegra264_i2c_hw, }, + { .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_vi_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_dvc_i2c_hw, }, +#endif + {}, +}; +MODULE_DEVICE_TABLE(of, tegra_i2c_of_match); + +static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) +{ + 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; + i2c_dev->is_mctp = device_property_present(i2c_dev->dev, "mctp-controller"); +} + +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_probe(i2c_dev->dev, err, "failed to enable div-clk\n"); + 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_probe(i2c_dev->dev, ret, "runtime resume failed\n"); + 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; + int err; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + 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->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + i2c_dev->base_phys = res->start; + + err = platform_get_irq(pdev, 0); + if (err < 0) + return err; + + i2c_dev->irq = err; + + /* interrupt will be enabled during of transfer time */ + irq_set_status_flags(i2c_dev->irq, IRQ_NOAUTOEN); + + 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; + + tegra_i2c_parse_dt(i2c_dev); + + err = tegra_i2c_init_clocks(i2c_dev); + if (err) + return err; + + err = tegra_i2c_init_dma(i2c_dev); + if (err) + goto release_clocks; + + /* + * 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. + * + * Devices with pinctrl states cannot be marked IRQ-safe as the pinctrl + * state transitions during runtime PM require mutexes. + */ + if (!IS_VI(i2c_dev) && !has_acpi_companion(i2c_dev->dev) && !i2c_dev->dev->pins) + pm_runtime_irq_safe(i2c_dev->dev); + + pm_runtime_enable(i2c_dev->dev); + + 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; + i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.nr = pdev->id; + ACPI_COMPANION_SET(&i2c_dev->adapter.dev, ACPI_COMPANION(&pdev->dev)); + + if (i2c_dev->hw->supports_bus_clear) + i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + + strscpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev), + sizeof(i2c_dev->adapter.name)); + + err = i2c_add_numbered_adapter(&i2c_dev->adapter); + if (err) + goto release_rpm; + + return 0; + +release_rpm: + pm_runtime_disable(i2c_dev->dev); + + tegra_i2c_release_dma(i2c_dev); +release_clocks: + tegra_i2c_release_clocks(i2c_dev); + + return err; +} + +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); + + tegra_i2c_release_dma(i2c_dev); + tegra_i2c_release_clocks(i2c_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; + + /* + * 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) +{ + /* + * Bring the controller up and hold a usage count so it stays + * available until the noirq phase. + */ + return pm_runtime_resume_and_get(dev); +} + +static int __maybe_unused tegra_i2c_suspend_noirq(struct device *dev) +{ + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c_dev->adapter); + + /* + * Runtime PM is already disabled at this point, so invoke the + * runtime_suspend callback directly to put the controller down. + */ + return tegra_i2c_runtime_suspend(dev); +} + +static int __maybe_unused tegra_i2c_resume_noirq(struct device *dev) +{ + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); + int err; + + /* + * Runtime PM is still disabled at this point, so invoke the + * runtime_resume callback directly to bring the controller back up + * before re-initializing the hardware. The adapter is then marked + * resumed so that consumers can issue transfers from their own + * resume_noirq() handlers and onwards. + */ + err = tegra_i2c_runtime_resume(dev); + if (err) + return err; + + err = tegra_i2c_init(i2c_dev); + if (err) + return err; + + i2c_mark_adapter_resumed(&i2c_dev->adapter); + + return 0; +} + +static int __maybe_unused tegra_i2c_resume(struct device *dev) +{ + pm_runtime_put(dev); + + return 0; +} + +static const struct dev_pm_ops tegra_i2c_pm = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend_noirq, + tegra_i2c_resume_noirq) + SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume, + NULL) +}; + +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}, + {.id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_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", + .of_match_table = tegra_i2c_of_match, + .acpi_match_table = tegra_i2c_acpi_match, + .pm = &tegra_i2c_pm, + }, +}; +module_platform_driver(tegra_i2c_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 new file mode 100644 index 000000000..3959f23fc --- /dev/null +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -0,0 +1,277 @@ +/* + * Cavium ThunderX i2c driver. + * + * Copyright (C) 2015,2016 Cavium Inc. + * Authors: Fred Martin + * Jan Glauber + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-octeon-core.h" + +#define DRV_NAME "i2c-thunderx" + +#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012 + +#define SYS_FREQ_DEFAULT 800000000 +#define OTX2_REF_FREQ_DEFAULT 100000000 + +#define TWSI_INT_ENA_W1C 0x1028 +#define TWSI_INT_ENA_W1S 0x1030 + +/* + * Enable the CORE interrupt. + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void thunder_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_CORE_INT, + i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +/* + * Disable the CORE interrupt. + */ +static void thunder_i2c_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_CORE_INT, + i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT, + i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +static void thunder_i2c_hlc_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT, + i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static u32 thunderx_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm thunderx_i2c_algo = { + .xfer = octeon_i2c_xfer, + .functionality = thunderx_i2c_functionality, +}; + +static const struct i2c_adapter thunderx_i2c_ops = { + .owner = THIS_MODULE, + .name = "ThunderX adapter", + .algo = &thunderx_i2c_algo, +}; + +static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c) +{ + int ret; + + if (acpi_disabled) { + /* DT */ + i2c->clk = clk_get(dev, NULL); + if (IS_ERR(i2c->clk)) { + i2c->clk = NULL; + goto skip; + } + + ret = clk_prepare_enable(i2c->clk); + if (ret) + goto skip; + i2c->sys_freq = clk_get_rate(i2c->clk); + } else { + /* ACPI */ + if (device_property_read_u32(dev, "sclk", &i2c->sys_freq)) + device_property_read_u32(dev, "ioclk", &i2c->sys_freq); + } + +skip: + if (!i2c->sys_freq) + i2c->sys_freq = SYS_FREQ_DEFAULT; +} + +static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) +{ + if (!clk) + return; + clk_disable_unprepare(clk); + clk_put(clk); +} + +static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, + struct device_node *node) +{ + struct i2c_client *ara; + + if (!node) + return -EINVAL; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return -EINVAL; + + ara = i2c_new_smbus_alert_device(&i2c->adap, &i2c->alert_data); + if (IS_ERR(ara)) + return PTR_ERR(ara); + + i2c->ara = ara; + + return 0; +} + +static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ + /* TODO: ACPI support */ + if (!acpi_disabled) + return -EOPNOTSUPP; + + return thunder_i2c_smbus_setup_of(i2c, node); +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ + i2c_unregister_device(i2c->ara); +} + +static int thunder_i2c_probe_pci(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct octeon_i2c *i2c; + int ret; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + 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); + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_request_all_regions(pdev, DRV_NAME); + if (ret) + return ret; + + i2c->twsi_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!i2c->twsi_base) + return -EINVAL; + + thunder_i2c_clock_enable(dev, i2c); + ret = device_property_read_u32(dev, "clock-frequency", &i2c->twsi_freq); + if (ret) + i2c->twsi_freq = I2C_MAX_STANDARD_MODE_FREQ; + + init_waitqueue_head(&i2c->queue); + + i2c->int_enable = thunder_i2c_int_enable; + i2c->int_disable = thunder_i2c_int_disable; + i2c->hlc_int_enable = thunder_i2c_hlc_int_enable; + i2c->hlc_int_disable = thunder_i2c_hlc_int_disable; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); + if (ret < 0) + goto error; + + ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), octeon_i2c_isr, 0, + DRV_NAME, i2c); + if (ret) + goto error; + + ret = octeon_i2c_init_lowlevel(i2c); + 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; + i2c->adap.retries = 5; + i2c->adap.class = I2C_CLASS_HWMON; + 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); + + ret = i2c_add_adapter(&i2c->adap); + if (ret) + goto error; + + dev_info(i2c->dev, "Probed. Set system clock to %u\n", i2c->sys_freq); + + ret = thunder_i2c_smbus_setup(i2c, pdev->dev.of_node); + if (ret) + dev_info(dev, "SMBUS alert not active on this bus\n"); + + return 0; + +error: + thunder_i2c_clock_disable(dev, i2c->clk); + return ret; +} + +static void thunder_i2c_remove_pci(struct pci_dev *pdev) +{ + struct octeon_i2c *i2c = pci_get_drvdata(pdev); + + thunder_i2c_smbus_remove(i2c); + thunder_i2c_clock_disable(&pdev->dev, i2c->clk); + i2c_del_adapter(&i2c->adap); +} + +static const struct pci_device_id thunder_i2c_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_TWSI) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, thunder_i2c_pci_id_table); + +static struct pci_driver thunder_i2c_pci_driver = { + .name = DRV_NAME, + .id_table = thunder_i2c_pci_id_table, + .probe = thunder_i2c_probe_pci, + .remove = thunder_i2c_remove_pci, +}; + +module_pci_driver(thunder_i2c_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fred Martin "); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium ThunderX SOC"); diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c new file mode 100644 index 000000000..73355a56a --- /dev/null +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -0,0 +1,302 @@ +// 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) + */ + +#include +#include +#include +#include +#include +#include + +/* include interfaces to usb layer */ +#include + +/* include interface to i2c layer */ +#include + +/* commands via USB, must match command ids in the firmware */ +#define CMD_ECHO 0 +#define CMD_GET_FUNC 1 +#define CMD_SET_DELAY 2 +#define CMD_GET_STATUS 3 + +#define CMD_I2C_IO 4 +#define CMD_I2C_IO_BEGIN (1<<0) +#define CMD_I2C_IO_END (1<<1) + +/* i2c bit delay, default is 10us -> 100kHz max + (in practice, due to additional delays in the i2c bitbanging + code this results in a i2c clock of about 50kHz) */ +static unsigned short delay = 10; +module_param(delay, ushort, 0); +MODULE_PARM_DESC(delay, "bit delay in microseconds " + "(default is 10us for 100kHz max)"); + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +/* ----- begin of i2c layer ---------------------------------------------- */ + +#define STATUS_IDLE 0 +#define STATUS_ADDRESS_ACK 1 +#define STATUS_ADDRESS_NAK 2 + +static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + unsigned char *pstatus; + struct i2c_msg *pmsg; + int i, ret; + + pstatus = kmalloc_obj(*pstatus); + if (!pstatus) + return -ENOMEM; + + for (i = 0 ; i < num ; i++) { + int cmd = CMD_I2C_IO; + + if (i == 0) + cmd |= CMD_I2C_IO_BEGIN; + + if (i == num-1) + cmd |= CMD_I2C_IO_END; + + pmsg = &msgs[i]; + + dev_dbg(&adapter->dev, + " %d: %s (flags %d) %d bytes to 0x%02x\n", + i, str_read_write(pmsg->flags & I2C_M_RD), + pmsg->flags, pmsg->len, pmsg->addr); + + /* and directly send the message */ + if (pmsg->flags & I2C_M_RD) { + /* read data */ + if (usb_read(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len) != pmsg->len) { + dev_err(&adapter->dev, + "failure reading data\n"); + ret = -EIO; + goto out; + } + } else { + /* write data */ + if (usb_write(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len) != pmsg->len) { + dev_err(&adapter->dev, + "failure writing data\n"); + ret = -EIO; + goto out; + } + } + + /* read status */ + if (usb_read(adapter, CMD_GET_STATUS, 0, 0, pstatus, 1) != 1) { + dev_err(&adapter->dev, "failure reading status\n"); + ret = -EIO; + goto out; + } + + dev_dbg(&adapter->dev, " status = %d\n", *pstatus); + if (*pstatus == STATUS_ADDRESS_NAK) { + ret = -ENXIO; + goto out; + } + } + + ret = i; +out: + kfree(pstatus); + return ret; +} + +static u32 usb_func(struct i2c_adapter *adapter) +{ + __le32 *pfunc; + u32 ret; + + pfunc = kmalloc_obj(*pfunc); + + /* get functionality from adapter */ + if (!pfunc || usb_read(adapter, CMD_GET_FUNC, 0, 0, pfunc, + sizeof(*pfunc)) != sizeof(*pfunc)) { + dev_err(&adapter->dev, "failure reading functionality\n"); + ret = 0; + goto out; + } + + ret = le32_to_cpup(pfunc); +out: + kfree(pfunc); + 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 = { + .xfer = usb_xfer, + .functionality = usb_func, +}; + +/* ----- end of i2c layer ------------------------------------------------ */ + +/* ----- begin of usb layer ---------------------------------------------- */ + +/* + * Initially the usb i2c interface uses a vid/pid pair donated by + * Future Technology Devices International Ltd., later a pair was + * bought from EZPrototypes + */ +static const struct usb_device_id i2c_tiny_usb_table[] = { + { USB_DEVICE(0x0403, 0xc631) }, /* FTDI */ + { USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, i2c_tiny_usb_table); + +/* Structure to hold all of our device specific stuff */ +struct i2c_tiny_usb { + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ +}; + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + void *dmadata = kmalloc(len, GFP_KERNEL); + int ret; + + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | + USB_DIR_IN, value, index, dmadata, len, 2000); + + memcpy(data, dmadata, len); + kfree(dmadata); + return ret; +} + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + void *dmadata = kmemdup(data, len, GFP_KERNEL); + int ret; + + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, dmadata, len, 2000); + + kfree(dmadata); + return ret; +} + +static int i2c_tiny_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct i2c_tiny_usb *dev; + 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_obj(*dev); + if (!dev) + goto error; + + dev->usb_dev = interface_to_usbdev(interface); + dev->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + version = le16_to_cpu(dev->usb_dev->descriptor.bcdDevice); + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + /* 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), + "i2c-tiny-usb at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { + retval = dev_err_probe(&dev->adapter.dev, -EIO, + "failure setting delay to %dus\n", delay); + goto error; + } + + dev->adapter.dev.parent = &dev->interface->dev; + + /* and finally attach to i2c layer */ + i2c_add_adapter(&dev->adapter); + + /* inform user about successful attachment to i2c layer */ + dev_info(&dev->adapter.dev, "connected i2c-tiny-usb device\n"); + + return 0; + + error: + kfree(dev); + + return retval; +} + +static void i2c_tiny_usb_disconnect(struct usb_interface *interface) +{ + struct i2c_tiny_usb *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + kfree(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver i2c_tiny_usb_driver = { + .name = "i2c-tiny-usb", + .probe = i2c_tiny_usb_probe, + .disconnect = i2c_tiny_usb_disconnect, + .id_table = i2c_tiny_usb_table, +}; + +module_usb_driver(i2c_tiny_usb_driver); + +/* ----- end of usb layer ------------------------------------------------ */ + +MODULE_AUTHOR("Till Harbaum "); +MODULE_DESCRIPTION("i2c-tiny-usb driver v1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c new file mode 100644 index 000000000..7190802a4 --- /dev/null +++ b/drivers/i2c/busses/i2c-uniphier-f.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2015 Masahiro Yamada + */ + +#include +#include +#include +#include +#include +#include +#include + +#define UNIPHIER_FI2C_CR 0x00 /* control register */ +#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 (target addr) */ +#define UNIPHIER_FI2C_DTTX_RD BIT(0) /* read transaction */ +#define UNIPHIER_FI2C_DTRX 0x04 /* RX FIFO */ +#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 */ +#define UNIPHIER_FI2C_DSUT 0x1c /* data setup time control */ +#define UNIPHIER_FI2C_INT 0x20 /* interrupt status */ +#define UNIPHIER_FI2C_IE 0x24 /* interrupt enable */ +#define UNIPHIER_FI2C_IC 0x28 /* interrupt clear */ +#define UNIPHIER_FI2C_INT_TE BIT(9) /* TX FIFO empty */ +#define UNIPHIER_FI2C_INT_RF BIT(8) /* RX FIFO full */ +#define UNIPHIER_FI2C_INT_TC BIT(7) /* send complete (STOP) */ +#define UNIPHIER_FI2C_INT_RC BIT(6) /* receive complete (STOP) */ +#define UNIPHIER_FI2C_INT_TB BIT(5) /* sent specified bytes */ +#define UNIPHIER_FI2C_INT_RB BIT(4) /* received specified bytes */ +#define UNIPHIER_FI2C_INT_NA BIT(2) /* no ACK */ +#define UNIPHIER_FI2C_INT_AL BIT(1) /* arbitration lost */ +#define UNIPHIER_FI2C_SR 0x2c /* status register */ +#define UNIPHIER_FI2C_SR_DB BIT(12) /* device busy */ +#define UNIPHIER_FI2C_SR_STS BIT(11) /* stop condition detected */ +#define UNIPHIER_FI2C_SR_BB BIT(8) /* bus busy */ +#define UNIPHIER_FI2C_SR_RFF BIT(3) /* RX FIFO full */ +#define UNIPHIER_FI2C_SR_RNE BIT(2) /* RX FIFO not empty */ +#define UNIPHIER_FI2C_SR_TNF BIT(1) /* TX FIFO not full */ +#define UNIPHIER_FI2C_SR_TFE BIT(0) /* TX FIFO empty */ +#define UNIPHIER_FI2C_RST 0x34 /* reset control */ +#define UNIPHIER_FI2C_RST_TBRST BIT(2) /* clear TX FIFO */ +#define UNIPHIER_FI2C_RST_RBRST BIT(1) /* clear RX FIFO */ +#define UNIPHIER_FI2C_RST_RST BIT(0) /* forcible bus reset */ +#define UNIPHIER_FI2C_BM 0x38 /* bus monitor */ +#define UNIPHIER_FI2C_BM_SDAO BIT(3) /* output for SDA line */ +#define UNIPHIER_FI2C_BM_SDAS BIT(2) /* readback of SDA line */ +#define UNIPHIER_FI2C_BM_SCLO BIT(1) /* output for SCL line */ +#define UNIPHIER_FI2C_BM_SCLS BIT(0) /* readback of SCL line */ +#define UNIPHIER_FI2C_NOISE 0x3c /* noise filter control */ +#define UNIPHIER_FI2C_TBC 0x40 /* TX byte count setting */ +#define UNIPHIER_FI2C_RBC 0x44 /* RX byte count setting */ +#define UNIPHIER_FI2C_TBCM 0x48 /* TX byte count monitor */ +#define UNIPHIER_FI2C_RBCM 0x4c /* RX byte count monitor */ +#define UNIPHIER_FI2C_BRST 0x50 /* bus reset */ +#define UNIPHIER_FI2C_BRST_FOEN BIT(1) /* normal operation */ +#define UNIPHIER_FI2C_BRST_RSCL BIT(0) /* release SCL */ + +#define UNIPHIER_FI2C_INT_FAULTS \ + (UNIPHIER_FI2C_INT_NA | UNIPHIER_FI2C_INT_AL) +#define UNIPHIER_FI2C_INT_STOP \ + (UNIPHIER_FI2C_INT_TC | UNIPHIER_FI2C_INT_RC) + +#define UNIPHIER_FI2C_RD BIT(0) +#define UNIPHIER_FI2C_STOP BIT(1) +#define UNIPHIER_FI2C_MANUAL_NACK BIT(2) +#define UNIPHIER_FI2C_BYTE_WISE BIT(3) +#define UNIPHIER_FI2C_DEFER_STOP_COMP BIT(4) + +#define UNIPHIER_FI2C_FIFO_SIZE 8 + +struct uniphier_fi2c_priv { + struct completion comp; + struct i2c_adapter adap; + void __iomem *membase; + struct clk *clk; + unsigned int len; + u8 *buf; + u32 enabled_irqs; + int error; + unsigned int flags; + unsigned int busy_cnt; + unsigned int clk_cycle; + spinlock_t lock; /* IRQ synchronization */ +}; + +static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv, + bool first) +{ + int fifo_space = UNIPHIER_FI2C_FIFO_SIZE; + + /* + * TX-FIFO stores target address in it for the first access. + * Decrement the counter. + */ + if (first) + fifo_space--; + + while (priv->len) { + if (fifo_space-- <= 0) + break; + + writel(*priv->buf++, priv->membase + UNIPHIER_FI2C_DTTX); + priv->len--; + } +} + +static void uniphier_fi2c_drain_rxfifo(struct uniphier_fi2c_priv *priv) +{ + int fifo_left = priv->flags & UNIPHIER_FI2C_BYTE_WISE ? + 1 : UNIPHIER_FI2C_FIFO_SIZE; + + while (priv->len) { + if (fifo_left-- <= 0) + break; + + *priv->buf++ = readl(priv->membase + UNIPHIER_FI2C_DTRX); + priv->len--; + } +} + +static void uniphier_fi2c_set_irqs(struct uniphier_fi2c_priv *priv) +{ + writel(priv->enabled_irqs, priv->membase + UNIPHIER_FI2C_IE); +} + +static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv, + u32 mask) +{ + writel(mask, priv->membase + UNIPHIER_FI2C_IC); +} + +static void uniphier_fi2c_stop(struct uniphier_fi2c_priv *priv) +{ + priv->enabled_irqs |= UNIPHIER_FI2C_INT_STOP; + uniphier_fi2c_set_irqs(priv); + writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STO, + priv->membase + UNIPHIER_FI2C_CR); +} + +static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id) +{ + struct uniphier_fi2c_priv *priv = dev_id; + u32 irq_status; + + spin_lock(&priv->lock); + + irq_status = readl(priv->membase + UNIPHIER_FI2C_INT); + irq_status &= priv->enabled_irqs; + + if (irq_status & UNIPHIER_FI2C_INT_STOP) + goto complete; + + if (unlikely(irq_status & UNIPHIER_FI2C_INT_AL)) { + priv->error = -EAGAIN; + goto complete; + } + + if (unlikely(irq_status & UNIPHIER_FI2C_INT_NA)) { + priv->error = -ENXIO; + if (priv->flags & UNIPHIER_FI2C_RD) { + /* + * work around a hardware bug: + * The receive-completed interrupt is never set even if + * STOP condition is detected after the address phase + * of read transaction fails to get ACK. + * To avoid time-out error, we issue STOP here, + * but do not wait for its completion. + * It should be checked after exiting this handler. + */ + uniphier_fi2c_stop(priv); + priv->flags |= UNIPHIER_FI2C_DEFER_STOP_COMP; + goto complete; + } + goto stop; + } + + if (irq_status & UNIPHIER_FI2C_INT_TE) { + if (!priv->len) + goto data_done; + + uniphier_fi2c_fill_txfifo(priv, false); + goto handled; + } + + if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) { + uniphier_fi2c_drain_rxfifo(priv); + /* + * If the number of bytes to read is multiple of the FIFO size + * (msg->len == 8, 16, 24, ...), the INT_RF bit is set a little + * earlier than INT_RB. We wait for INT_RB to confirm the + * completion of the current message. + */ + if (!priv->len && (irq_status & UNIPHIER_FI2C_INT_RB)) + goto data_done; + + if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) { + if (priv->len <= UNIPHIER_FI2C_FIFO_SIZE && + !(priv->flags & UNIPHIER_FI2C_BYTE_WISE)) { + priv->enabled_irqs |= UNIPHIER_FI2C_INT_RB; + uniphier_fi2c_set_irqs(priv); + priv->flags |= UNIPHIER_FI2C_BYTE_WISE; + } + if (priv->len <= 1) + writel(UNIPHIER_FI2C_CR_MST | + UNIPHIER_FI2C_CR_NACK, + priv->membase + UNIPHIER_FI2C_CR); + } + + goto handled; + } + + spin_unlock(&priv->lock); + + return IRQ_NONE; + +data_done: + if (priv->flags & UNIPHIER_FI2C_STOP) { +stop: + uniphier_fi2c_stop(priv); + } else { +complete: + priv->enabled_irqs = 0; + uniphier_fi2c_set_irqs(priv); + complete(&priv->comp); + } + +handled: + /* + * This controller makes a pause while any bit of the IRQ status is + * asserted. Clear the asserted bit to kick the controller just before + * exiting the handler. + */ + uniphier_fi2c_clear_irqs(priv, irq_status); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr, + bool repeat) +{ + priv->enabled_irqs |= UNIPHIER_FI2C_INT_TE; + uniphier_fi2c_set_irqs(priv); + + /* do not use TX byte counter */ + writel(0, priv->membase + UNIPHIER_FI2C_TBC); + /* set target address */ + writel(UNIPHIER_FI2C_DTTX_CMD | addr << 1, + priv->membase + UNIPHIER_FI2C_DTTX); + /* + * First chunk of data. For a repeated START condition, do not write + * data to the TX fifo here to avoid the timing issue. + */ + if (!repeat) + uniphier_fi2c_fill_txfifo(priv, true); +} + +static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr) +{ + priv->flags |= UNIPHIER_FI2C_RD; + + if (likely(priv->len < 256)) { + /* + * If possible, use RX byte counter. + * It can automatically handle NACK for the last byte. + */ + writel(priv->len, priv->membase + UNIPHIER_FI2C_RBC); + priv->enabled_irqs |= UNIPHIER_FI2C_INT_RF | + UNIPHIER_FI2C_INT_RB; + } else { + /* + * The byte counter can not count over 256. In this case, + * do not use it at all. Drain data when FIFO gets full, + * but treat the last portion as a special case. + */ + writel(0, priv->membase + UNIPHIER_FI2C_RBC); + priv->flags |= UNIPHIER_FI2C_MANUAL_NACK; + priv->enabled_irqs |= UNIPHIER_FI2C_INT_RF; + } + + uniphier_fi2c_set_irqs(priv); + + /* set target address with RD bit */ + writel(UNIPHIER_FI2C_DTTX_CMD | UNIPHIER_FI2C_DTTX_RD | addr << 1, + priv->membase + UNIPHIER_FI2C_DTTX); +} + +static void uniphier_fi2c_reset(struct uniphier_fi2c_priv *priv) +{ + writel(UNIPHIER_FI2C_RST_RST, priv->membase + UNIPHIER_FI2C_RST); +} + +static void uniphier_fi2c_prepare_operation(struct uniphier_fi2c_priv *priv) +{ + writel(UNIPHIER_FI2C_BRST_FOEN | UNIPHIER_FI2C_BRST_RSCL, + priv->membase + UNIPHIER_FI2C_BRST); +} + +static void uniphier_fi2c_recover(struct uniphier_fi2c_priv *priv) +{ + uniphier_fi2c_reset(priv); + i2c_recover_bus(&priv->adap); +} + +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; + + priv->len = msg->len; + priv->buf = msg->buf; + priv->enabled_irqs = UNIPHIER_FI2C_INT_FAULTS; + priv->error = 0; + priv->flags = 0; + + if (stop) + priv->flags |= UNIPHIER_FI2C_STOP; + + reinit_completion(&priv->comp); + uniphier_fi2c_clear_irqs(priv, U32_MAX); + writel(UNIPHIER_FI2C_RST_TBRST | UNIPHIER_FI2C_RST_RBRST, + priv->membase + UNIPHIER_FI2C_RST); /* reset TX/RX FIFO */ + + spin_lock_irqsave(&priv->lock, flags); + + if (is_read) + uniphier_fi2c_rx_init(priv, msg->addr); + else + uniphier_fi2c_tx_init(priv, msg->addr, repeat); + + /* + * 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. + */ + if (!repeat) + writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STA, + priv->membase + UNIPHIER_FI2C_CR); + + spin_unlock_irqrestore(&priv->lock, flags); + + time_left = wait_for_completion_timeout(&priv->comp, adap->timeout); + + spin_lock_irqsave(&priv->lock, flags); + priv->enabled_irqs = 0; + uniphier_fi2c_set_irqs(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + if (!time_left) { + uniphier_fi2c_recover(priv); + return -ETIMEDOUT; + } + + if (unlikely(priv->flags & UNIPHIER_FI2C_DEFER_STOP_COMP)) { + u32 status; + int ret; + + ret = readl_poll_timeout(priv->membase + UNIPHIER_FI2C_SR, + status, + (status & UNIPHIER_FI2C_SR_STS) && + !(status & UNIPHIER_FI2C_SR_BB), + 1, 20); + if (ret) { + dev_err(&adap->dev, + "stop condition was not completed.\n"); + uniphier_fi2c_recover(priv); + return ret; + } + } + + return priv->error; +} + +static int uniphier_fi2c_check_bus_busy(struct i2c_adapter *adap) +{ + struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); + + if (readl(priv->membase + UNIPHIER_FI2C_SR) & UNIPHIER_FI2C_SR_DB) { + if (priv->busy_cnt++ > 3) { + /* + * If bus busy continues too long, it is probably + * in a wrong state. Try bus recovery. + */ + uniphier_fi2c_recover(priv); + priv->busy_cnt = 0; + } + + return -EAGAIN; + } + + priv->busy_cnt = 0; + return 0; +} + +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; + int ret; + + ret = uniphier_fi2c_check_bus_busy(adap); + if (ret) + return ret; + + for (msg = msgs; msg < emsg; msg++) { + /* 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_xfer_one(adap, msg, repeat, stop); + if (ret) + return ret; + + repeat = !stop; + } + + return num; +} + +static u32 uniphier_fi2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm uniphier_fi2c_algo = { + .xfer = uniphier_fi2c_xfer, + .functionality = uniphier_fi2c_functionality, +}; + +static int uniphier_fi2c_get_scl(struct i2c_adapter *adap) +{ + struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); + + return !!(readl(priv->membase + UNIPHIER_FI2C_BM) & + UNIPHIER_FI2C_BM_SCLS); +} + +static void uniphier_fi2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); + + writel(val ? UNIPHIER_FI2C_BRST_RSCL : 0, + priv->membase + UNIPHIER_FI2C_BRST); +} + +static int uniphier_fi2c_get_sda(struct i2c_adapter *adap) +{ + struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); + + return !!(readl(priv->membase + UNIPHIER_FI2C_BM) & + UNIPHIER_FI2C_BM_SDAS); +} + +static void uniphier_fi2c_unprepare_recovery(struct i2c_adapter *adap) +{ + uniphier_fi2c_prepare_operation(i2c_get_adapdata(adap)); +} + +static struct i2c_bus_recovery_info uniphier_fi2c_bus_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = uniphier_fi2c_get_scl, + .set_scl = uniphier_fi2c_set_scl, + .get_sda = uniphier_fi2c_get_sda, + .unprepare_recovery = uniphier_fi2c_unprepare_recovery, +}; + +static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv) +{ + unsigned int cyc = priv->clk_cycle; + u32 tmp; + + tmp = readl(priv->membase + UNIPHIER_FI2C_CR); + tmp |= UNIPHIER_FI2C_CR_MST; + writel(tmp, priv->membase + UNIPHIER_FI2C_CR); + + uniphier_fi2c_reset(priv); + + /* + * Standard-mode: tLOW + tHIGH = 10 us + * Fast-mode: tLOW + tHIGH = 2.5 us + */ + writel(cyc, priv->membase + UNIPHIER_FI2C_CYC); + /* + * Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us, tBUF = 4.7 us + * Fast-mode: tLOW = 1.3 us, tHIGH = 0.6 us, tBUF = 1.3 us + * "tLow/tHIGH = 5/4" meets both. + */ + writel(cyc * 5 / 9, priv->membase + UNIPHIER_FI2C_LCTL); + /* + * Standard-mode: tHD;STA = 4.0 us, tSU;STA = 4.7 us, tSU;STO = 4.0 us + * Fast-mode: tHD;STA = 0.6 us, tSU;STA = 0.6 us, tSU;STO = 0.6 us + */ + writel(cyc / 2, priv->membase + UNIPHIER_FI2C_SSUT); + /* + * Standard-mode: tSU;DAT = 250 ns + * Fast-mode: tSU;DAT = 100 ns + */ + writel(cyc / 16, priv->membase + UNIPHIER_FI2C_DSUT); + + uniphier_fi2c_prepare_operation(priv); +} + +static int uniphier_fi2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_fi2c_priv *priv; + u32 bus_speed; + unsigned long clk_rate; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + 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) + return irq; + + if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed)) + bus_speed = I2C_MAX_STANDARD_MODE_FREQ; + + 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_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) + 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); + spin_lock_init(&priv->lock); + priv->adap.owner = THIS_MODULE; + priv->adap.algo = &uniphier_fi2c_algo; + priv->adap.dev.parent = dev; + priv->adap.dev.of_node = dev->of_node; + 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); + + uniphier_fi2c_hw_init(priv); + + ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0, + pdev->name, priv); + if (ret) + return ret; + + return i2c_add_adapter(&priv->adap); +} + +static void uniphier_fi2c_remove(struct platform_device *pdev) +{ + struct uniphier_fi2c_priv *priv = platform_get_drvdata(pdev); + + i2c_del_adapter(&priv->adap); +} + +static int __maybe_unused uniphier_fi2c_suspend(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_fi2c_resume(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_fi2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_fi2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_fi2c_suspend, uniphier_fi2c_resume) +}; + +static const struct of_device_id uniphier_fi2c_match[] = { + { .compatible = "socionext,uniphier-fi2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_fi2c_match); + +static struct platform_driver uniphier_fi2c_drv = { + .probe = uniphier_fi2c_probe, + .remove = uniphier_fi2c_remove, + .driver = { + .name = "uniphier-fi2c", + .of_match_table = uniphier_fi2c_match, + .pm = &uniphier_fi2c_pm_ops, + }, +}; +module_platform_driver(uniphier_fi2c_drv); + +MODULE_AUTHOR("Masahiro Yamada "); +MODULE_DESCRIPTION("UniPhier FIFO-builtin I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c new file mode 100644 index 000000000..ad7bbf5e2 --- /dev/null +++ b/drivers/i2c/busses/i2c-uniphier.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2015 Masahiro Yamada + */ + +#include +#include +#include +#include +#include +#include + +#define UNIPHIER_I2C_DTRM 0x00 /* TX register */ +#define UNIPHIER_I2C_DTRM_IRQEN BIT(11) /* enable interrupt */ +#define UNIPHIER_I2C_DTRM_STA BIT(10) /* start condition */ +#define UNIPHIER_I2C_DTRM_STO BIT(9) /* stop condition */ +#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 = 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 /* 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 */ +#define UNIPHIER_I2C_BRST_RSCL BIT(0) /* release SCL */ +#define UNIPHIER_I2C_HOLD 0x14 /* hold time control */ +#define UNIPHIER_I2C_BSTS 0x18 /* bus status monitor */ +#define UNIPHIER_I2C_BSTS_SDA BIT(1) /* readback of SDA line */ +#define UNIPHIER_I2C_BSTS_SCL BIT(0) /* readback of SCL line */ +#define UNIPHIER_I2C_NOISE 0x1c /* noise filter control */ +#define UNIPHIER_I2C_SETUP 0x20 /* setup time control */ + +struct uniphier_i2c_priv { + struct completion comp; + struct i2c_adapter adap; + void __iomem *membase; + struct clk *clk; + unsigned int busy_cnt; + unsigned int clk_cycle; +}; + +static irqreturn_t uniphier_i2c_interrupt(int irq, void *dev_id) +{ + struct uniphier_i2c_priv *priv = dev_id; + + /* + * This hardware uses edge triggered interrupt. Do not touch the + * hardware registers in this handler to make sure to catch the next + * interrupt edge. Just send a complete signal and return. + */ + complete(&priv->comp); + + return IRQ_HANDLED; +} + +static int uniphier_i2c_xfer_byte(struct i2c_adapter *adap, u32 txdata, + u32 *rxdatap) +{ + struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap); + unsigned long time_left; + u32 rxdata; + + reinit_completion(&priv->comp); + + txdata |= UNIPHIER_I2C_DTRM_IRQEN; + writel(txdata, priv->membase + UNIPHIER_I2C_DTRM); + + time_left = wait_for_completion_timeout(&priv->comp, adap->timeout); + if (unlikely(!time_left)) + return -ETIMEDOUT; + + rxdata = readl(priv->membase + UNIPHIER_I2C_DREC); + if (rxdatap) + *rxdatap = rxdata; + + return 0; +} + +static int uniphier_i2c_send_byte(struct i2c_adapter *adap, u32 txdata) +{ + u32 rxdata; + int ret; + + ret = uniphier_i2c_xfer_byte(adap, txdata, &rxdata); + if (ret) + return ret; + + if (unlikely(rxdata & UNIPHIER_I2C_DREC_LAB)) + return -EAGAIN; + + if (unlikely(rxdata & UNIPHIER_I2C_DREC_LRB)) + return -ENXIO; + + return 0; +} + +static int uniphier_i2c_tx(struct i2c_adapter *adap, u16 addr, u16 len, + const u8 *buf) +{ + int ret; + + ret = uniphier_i2c_send_byte(adap, addr << 1 | + UNIPHIER_I2C_DTRM_STA | + UNIPHIER_I2C_DTRM_NACK); + if (ret) + return ret; + + while (len--) { + ret = uniphier_i2c_send_byte(adap, + UNIPHIER_I2C_DTRM_NACK | *buf++); + if (ret) + return ret; + } + + return 0; +} + +static int uniphier_i2c_rx(struct i2c_adapter *adap, u16 addr, u16 len, + u8 *buf) +{ + int ret; + + ret = uniphier_i2c_send_byte(adap, addr << 1 | + UNIPHIER_I2C_DTRM_STA | + UNIPHIER_I2C_DTRM_NACK | + UNIPHIER_I2C_DTRM_RD); + if (ret) + return ret; + + while (len--) { + u32 rxdata; + + ret = uniphier_i2c_xfer_byte(adap, + len ? 0 : UNIPHIER_I2C_DTRM_NACK, + &rxdata); + if (ret) + return ret; + *buf++ = rxdata; + } + + return 0; +} + +static int uniphier_i2c_stop(struct i2c_adapter *adap) +{ + return uniphier_i2c_send_byte(adap, UNIPHIER_I2C_DTRM_STO | + UNIPHIER_I2C_DTRM_NACK); +} + +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; + + if (is_read) + ret = uniphier_i2c_rx(adap, msg->addr, msg->len, msg->buf); + else + ret = uniphier_i2c_tx(adap, msg->addr, msg->len, msg->buf); + + if (ret == -EAGAIN) /* could not acquire bus. bail out without STOP */ + return ret; + + if (ret == -ETIMEDOUT) { + /* This error is fatal. Needs recovery. */ + stop = false; + recovery = true; + } + + if (stop) { + int ret2 = uniphier_i2c_stop(adap); + + if (ret2) { + /* Failed to issue STOP. The bus needs recovery. */ + recovery = true; + ret = ret ?: ret2; + } + } + + if (recovery) + i2c_recover_bus(adap); + + return ret; +} + +static int uniphier_i2c_check_bus_busy(struct i2c_adapter *adap) +{ + struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap); + + if (!(readl(priv->membase + UNIPHIER_I2C_DREC) & + UNIPHIER_I2C_DREC_BBN)) { + if (priv->busy_cnt++ > 3) { + /* + * If bus busy continues too long, it is probably + * in a wrong state. Try bus recovery. + */ + i2c_recover_bus(adap); + priv->busy_cnt = 0; + } + + return -EAGAIN; + } + + priv->busy_cnt = 0; + return 0; +} + +static int uniphier_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct i2c_msg *msg, *emsg = msgs + num; + int ret; + + ret = uniphier_i2c_check_bus_busy(adap); + if (ret) + return ret; + + for (msg = msgs; msg < emsg; msg++) { + /* 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_xfer_one(adap, msg, stop); + if (ret) + return ret; + } + + return num; +} + +static u32 uniphier_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm uniphier_i2c_algo = { + .xfer = uniphier_i2c_xfer, + .functionality = uniphier_i2c_functionality, +}; + +static void uniphier_i2c_reset(struct uniphier_i2c_priv *priv, bool reset_on) +{ + u32 val = UNIPHIER_I2C_BRST_RSCL; + + val |= reset_on ? 0 : UNIPHIER_I2C_BRST_FOEN; + writel(val, priv->membase + UNIPHIER_I2C_BRST); +} + +static int uniphier_i2c_get_scl(struct i2c_adapter *adap) +{ + struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap); + + return !!(readl(priv->membase + UNIPHIER_I2C_BSTS) & + UNIPHIER_I2C_BSTS_SCL); +} + +static void uniphier_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap); + + writel(val ? UNIPHIER_I2C_BRST_RSCL : 0, + priv->membase + UNIPHIER_I2C_BRST); +} + +static int uniphier_i2c_get_sda(struct i2c_adapter *adap) +{ + struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap); + + return !!(readl(priv->membase + UNIPHIER_I2C_BSTS) & + UNIPHIER_I2C_BSTS_SDA); +} + +static void uniphier_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + uniphier_i2c_reset(i2c_get_adapdata(adap), false); +} + +static struct i2c_bus_recovery_info uniphier_i2c_bus_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = uniphier_i2c_get_scl, + .set_scl = uniphier_i2c_set_scl, + .get_sda = uniphier_i2c_get_sda, + .unprepare_recovery = uniphier_i2c_unprepare_recovery, +}; + +static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv) +{ + unsigned int cyc = priv->clk_cycle; + + uniphier_i2c_reset(priv, true); + + /* + * Bit30-16: clock cycles of tLOW. + * Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us + * Fast-mode: tLOW = 1.3 us, tHIGH = 0.6 us + * "tLow/tHIGH = 5/4" meets both. + */ + writel((cyc * 5 / 9 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK); + + uniphier_i2c_reset(priv, false); +} + +static int uniphier_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_i2c_priv *priv; + u32 bus_speed; + unsigned long clk_rate; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + 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) + return irq; + + if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed)) + bus_speed = I2C_MAX_STANDARD_MODE_FREQ; + + 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_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) + 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); + priv->adap.owner = THIS_MODULE; + priv->adap.algo = &uniphier_i2c_algo; + priv->adap.dev.parent = dev; + priv->adap.dev.of_node = dev->of_node; + 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); + + uniphier_i2c_hw_init(priv); + + ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name, + priv); + if (ret) + return ret; + + return i2c_add_adapter(&priv->adap); +} + +static void uniphier_i2c_remove(struct platform_device *pdev) +{ + struct uniphier_i2c_priv *priv = platform_get_drvdata(pdev); + + i2c_del_adapter(&priv->adap); +} + +static int __maybe_unused uniphier_i2c_suspend(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_i2c_resume(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_i2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_i2c_suspend, uniphier_i2c_resume) +}; + +static const struct of_device_id uniphier_i2c_match[] = { + { .compatible = "socionext,uniphier-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_i2c_match); + +static struct platform_driver uniphier_i2c_drv = { + .probe = uniphier_i2c_probe, + .remove = uniphier_i2c_remove, + .driver = { + .name = "uniphier-i2c", + .of_match_table = uniphier_i2c_match, + .pm = &uniphier_i2c_pm_ops, + }, +}; +module_platform_driver(uniphier_i2c_drv); + +MODULE_AUTHOR("Masahiro Yamada "); +MODULE_DESCRIPTION("UniPhier I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-usbio.c b/drivers/i2c/busses/i2c-usbio.c new file mode 100644 index 000000000..259754e5f --- /dev/null +++ b/drivers/i2c/busses/i2c-usbio.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Intel Corporation. + * Copyright (c) 2025 Red Hat, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#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 */ + { "INTC1118" }, /* NVL */ + { } +}; + +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 "); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USBIO"); diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c new file mode 100644 index 000000000..a1ab6ef6f --- /dev/null +++ b/drivers/i2c/busses/i2c-versatile.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c-versatile.c + * + * Copyright (C) 2006 ARM Ltd. + * written by Russell King, Deep Blue Solutions Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_CONTROL 0x00 +#define I2C_CONTROLS 0x00 +#define I2C_CONTROLC 0x04 +#define SCL (1 << 0) +#define SDA (1 << 1) + +struct i2c_versatile { + struct i2c_adapter adap; + struct i2c_algo_bit_data algo; + void __iomem *base; +}; + +static void i2c_versatile_setsda(void *data, int state) +{ + struct i2c_versatile *i2c = data; + + writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); +} + +static void i2c_versatile_setscl(void *data, int state) +{ + struct i2c_versatile *i2c = data; + + writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); +} + +static int i2c_versatile_getsda(void *data) +{ + struct i2c_versatile *i2c = data; + return !!(readl(i2c->base + I2C_CONTROL) & SDA); +} + +static int i2c_versatile_getscl(void *data) +{ + struct i2c_versatile *i2c = data; + return !!(readl(i2c->base + I2C_CONTROL) & SCL); +} + +static const struct i2c_algo_bit_data i2c_versatile_algo = { + .setsda = i2c_versatile_setsda, + .setscl = i2c_versatile_setscl, + .getsda = i2c_versatile_getsda, + .getscl = i2c_versatile_getscl, + .udelay = 30, + .timeout = HZ, +}; + +static int i2c_versatile_probe(struct platform_device *dev) +{ + struct i2c_versatile *i2c; + int ret; + + i2c = devm_kzalloc(&dev->dev, sizeof(struct i2c_versatile), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + 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; + 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; + i2c->algo = i2c_versatile_algo; + i2c->algo.data = i2c; + + i2c->adap.nr = dev->id; + ret = i2c_bit_add_numbered_bus(&i2c->adap); + if (ret < 0) + return ret; + + platform_set_drvdata(dev, i2c); + + return 0; +} + +static void i2c_versatile_remove(struct platform_device *dev) +{ + struct i2c_versatile *i2c = platform_get_drvdata(dev); + + i2c_del_adapter(&i2c->adap); +} + +static const struct of_device_id i2c_versatile_match[] = { + { .compatible = "arm,versatile-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_versatile_match); + +static struct platform_driver i2c_versatile_driver = { + .probe = i2c_versatile_probe, + .remove = i2c_versatile_remove, + .driver = { + .name = "versatile-i2c", + .of_match_table = i2c_versatile_match, + }, +}; + +static int __init i2c_versatile_init(void) +{ + return platform_driver_register(&i2c_versatile_driver); +} + +static void __exit i2c_versatile_exit(void) +{ + platform_driver_unregister(&i2c_versatile_driver); +} + +subsys_initcall(i2c_versatile_init); +module_exit(i2c_versatile_exit); + +MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:versatile-i2c"); diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c new file mode 100644 index 000000000..2c26a5788 --- /dev/null +++ b/drivers/i2c/busses/i2c-via.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c Support for Via Technologies 82C586B South Bridge + + Copyright (c) 1998, 1999 Kyösti Mälkki + +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* Power management registers */ +#define PM_CFG_REVID 0x08 /* silicon revision code */ +#define PM_CFG_IOBASE0 0x20 +#define PM_CFG_IOBASE1 0x48 + +#define I2C_DIR (pm_io_base+0x40) +#define I2C_OUT (pm_io_base+0x42) +#define I2C_IN (pm_io_base+0x44) +#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 + +static struct pci_driver vt586b_driver; +static u16 pm_io_base; + +/* + It does not appear from the datasheet that the GPIO pins are + open drain. So a we set a low value by setting the direction to + output and a high value by setting the direction to input and + relying on the required I2C pullup. The data value is initialized + to 0 in via_init() and never changed. +*/ +static void bit_via_setscl(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, I2C_DIR); +} + +static void bit_via_setsda(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, I2C_DIR); +} + +static int bit_via_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL)); +} + +static int bit_via_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA)); +} + + +static struct i2c_algo_bit_data bit_data = { + .setsda = bit_via_setsda, + .setscl = bit_via_setscl, + .getsda = bit_via_getsda, + .getscl = bit_via_getscl, + .udelay = 5, + .timeout = HZ +}; + +static struct i2c_adapter vt586b_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .name = "VIA i2c", + .algo_data = &bit_data, +}; + + +static const struct pci_device_id vt586b_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, vt586b_ids); + +static int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + u16 base; + u8 rev; + int res; + + 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); + + switch (rev) { + case 0x00: + base = PM_CFG_IOBASE0; + break; + case 0x01: + case 0x10: + base = PM_CFG_IOBASE1; + break; + + default: + base = PM_CFG_IOBASE1; + /* later revision */ + } + + pci_read_config_word(dev, base, &pm_io_base); + pm_io_base &= (0xff << 8); + + 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); + + /* set up the sysfs linkage to our parent device */ + vt586b_adapter.dev.parent = &dev->dev; + + res = i2c_bit_add_bus(&vt586b_adapter); + if ( res < 0 ) { + release_region(I2C_DIR, IOSPACE); + pm_io_base = 0; + return res; + } + return 0; +} + +static void vt586b_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&vt586b_adapter); + release_region(I2C_DIR, IOSPACE); + pm_io_base = 0; +} + + +static struct pci_driver vt586b_driver = { + .name = "vt586b_smbus", + .id_table = vt586b_ids, + .probe = vt586b_probe, + .remove = vt586b_remove, +}; + +module_pci_driver(vt586b_driver); + +MODULE_AUTHOR("Kyösti Mälkki "); +MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-viai2c-common.c b/drivers/i2c/busses/i2c-viai2c-common.c new file mode 100644 index 000000000..5a53ed95a --- /dev/null +++ b/drivers/i2c/busses/i2c-viai2c-common.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#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 "); +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 000000000..00f177332 --- /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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 000000000..d3dda7534 --- /dev/null +++ b/drivers/i2c/busses/i2c-viai2c-wmt.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Wondermedia I2C Controller Driver + * + * Copyright (C) 2012 Tony Prisk + * + * Derived from GPLv2+ licensed source: + * - Copyright (C) 2008 WonderMedia Technologies, Inc. + */ + +#include +#include +#include +#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 err; + + 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 "); +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 000000000..764a2dbc9 --- /dev/null +++ b/drivers/i2c/busses/i2c-viai2c-zhaoxin.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright(c) 2024 Shanghai Zhaoxin Semiconductor Corporation. + * All rights reserved. + */ + +#include +#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 error; + + 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 new file mode 100644 index 000000000..c58843609 --- /dev/null +++ b/drivers/i2c/busses/i2c-viapro.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 1998 - 2002 Frodo Looijaard , + Philip Edelbrock , Kyösti Mälkki , + Mark D. Studebaker + Copyright (C) 2005 - 2008 Jean Delvare + +*/ + +/* + Supports the following VIA south bridges: + + Chip name PCI ID REV I2C block + VT82C596A 0x3050 no + VT82C596B 0x3051 no + VT82C686A 0x3057 0x30 no + VT82C686B 0x3057 0x40 yes + VT8231 0x8235 no? + VT8233 0x3074 yes + VT8233A 0x3147 yes? + VT8235 0x3177 yes + VT8237R 0x3227 yes + VT8237A 0x3337 yes + VT8237S 0x3372 yes + VT8251 0x3287 yes + CX700 0x8324 yes + VX800/VX820 0x8353 yes + VX855/VX875 0x8409 yes + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pci_dev *vt596_pdev; + +#define SMBBA1 0x90 +#define SMBBA2 0x80 +#define SMBBA3 0xD0 + +/* SMBus address offsets */ +static unsigned short vt596_smba; +#define SMBHSTSTS (vt596_smba + 0) +#define SMBHSTCNT (vt596_smba + 2) +#define SMBHSTCMD (vt596_smba + 3) +#define SMBHSTADD (vt596_smba + 4) +#define SMBHSTDAT0 (vt596_smba + 5) +#define SMBHSTDAT1 (vt596_smba + 6) +#define SMBBLKDAT (vt596_smba + 7) + +/* PCI Address Constants */ + +/* SMBus data in configuration space can be found in two places, + We try to select the better one */ + +static unsigned short SMBHSTCFG = 0xD2; + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* VT82C596 constants */ +#define VT596_QUICK 0x00 +#define VT596_BYTE 0x04 +#define VT596_BYTE_DATA 0x08 +#define VT596_WORD_DATA 0x0C +#define VT596_PROC_CALL 0x10 +#define VT596_BLOCK_DATA 0x14 +#define VT596_I2C_BLOCK_DATA 0x34 + + +/* If force is set to anything different from 0, we forcibly enable the + VT596. DANGEROUS! */ +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the VT596 at the given address. VERY DANGEROUS! */ +static u16 force_addr; +module_param_hw(force_addr, ushort, ioport, 0); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the SMBus at the given address. " + "EXTREMELY DANGEROUS!"); + + +static struct pci_driver vt596_driver; +static struct i2c_adapter vt596_adapter; + +#define FEATURE_I2CBLOCK (1<<0) +static unsigned int vt596_features; + +#ifdef DEBUG +static void vt596_dump_regs(const char *msg, u8 size) +{ + dev_dbg(&vt596_adapter.dev, "%s: STS=%02x CNT=%02x CMD=%02x ADD=%02x " + "DAT=%02x,%02x\n", msg, inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); + + if (size == VT596_BLOCK_DATA + || size == VT596_I2C_BLOCK_DATA) { + int i; + + dev_dbg(&vt596_adapter.dev, "BLK="); + for (i = 0; i < I2C_SMBUS_BLOCK_MAX / 2; i++) + printk("%02x,", inb_p(SMBBLKDAT)); + printk("\n"); + dev_dbg(&vt596_adapter.dev, " "); + for (; i < I2C_SMBUS_BLOCK_MAX - 1; i++) + printk("%02x,", inb_p(SMBBLKDAT)); + printk("%02x\n", inb_p(SMBBLKDAT)); + } +} +#else +static inline void vt596_dump_regs(const char *msg, u8 size) { } +#endif + +/* Return -1 on error, 0 on success */ +static int vt596_transaction(u8 size) +{ + int temp; + int result = 0; + int timeout = 0; + + vt596_dump_regs("Transaction (pre)", size); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). " + "Resetting...\n", temp); + + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + dev_err(&vt596_adapter.dev, "SMBus reset failed! " + "(0x%02x)\n", temp); + return -EBUSY; + } + } + + /* Start the transaction by setting bit 6 */ + outb_p(0x40 | size, SMBHSTCNT); + + /* We will always wait for a fraction of a second */ + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (++timeout < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) { + result = -ETIMEDOUT; + dev_err(&vt596_adapter.dev, "SMBus timeout!\n"); + } + + if (temp & 0x10) { + result = -EIO; + dev_err(&vt596_adapter.dev, "Transaction failed (0x%02x)\n", + size); + } + + if (temp & 0x08) { + result = -EIO; + dev_err(&vt596_adapter.dev, "SMBus collision!\n"); + } + + if (temp & 0x04) { + result = -ENXIO; + dev_dbg(&vt596_adapter.dev, "No response\n"); + } + + /* Resetting status register */ + if (temp & 0x1F) + outb_p(temp, SMBHSTSTS); + + vt596_dump_regs("Transaction (post)", size); + + return result; +} + +/* Return negative errno on error, 0 on success */ +static s32 vt596_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int i; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + size = VT596_QUICK; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = VT596_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = VT596_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = VT596_WORD_DATA; + break; + case I2C_SMBUS_PROC_CALL: + outb_p(command, SMBHSTCMD); + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + size = VT596_PROC_CALL; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (!(vt596_features & FEATURE_I2CBLOCK)) + goto exit_unsupported; + if (read_write == I2C_SMBUS_READ) + outb_p(data->block[0], SMBHSTDAT0); + fallthrough; + case I2C_SMBUS_BLOCK_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + u8 len = data->block[0]; + if (len > I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX; + outb_p(len, SMBHSTDAT0); + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = (size == I2C_SMBUS_I2C_BLOCK_DATA) ? + VT596_I2C_BLOCK_DATA : VT596_BLOCK_DATA; + break; + default: + goto exit_unsupported; + } + + outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD); + + status = vt596_transaction(size); + if (status) + return status; + + if (size == VT596_PROC_CALL) + read_write = I2C_SMBUS_READ; + + if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) + return 0; + + switch (size) { + case VT596_BYTE: + case VT596_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_WORD_DATA: + case VT596_PROC_CALL: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case VT596_I2C_BLOCK_DATA: + case VT596_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; + +exit_unsupported: + dev_warn(&vt596_adapter.dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; +} + +static u32 vt596_func(struct i2c_adapter *adapter) +{ + u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; + + if (vt596_features & FEATURE_I2CBLOCK) + func |= I2C_FUNC_SMBUS_I2C_BLOCK; + return func; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = vt596_access, + .functionality = vt596_func, +}; + +static struct i2c_adapter vt596_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, +}; + +static int vt596_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned char temp; + int error; + + /* Determine the address of the SMBus areas */ + if (force_addr) { + vt596_smba = force_addr & 0xfff0; + force = 0; + goto found; + } + + if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) || + !(vt596_smba & 0x0001)) { + /* try 2nd address and config reg. for 596 */ + if (id->device == PCI_DEVICE_ID_VIA_82C596_3 && + !pci_read_config_word(pdev, SMBBA2, &vt596_smba) && + (vt596_smba & 0x0001)) { + SMBHSTCFG = 0x84; + } else { + /* no matches at all */ + return dev_err_probe(&pdev->dev, -ENODEV, + "Cannot configure " + "SMBus I/O Base address\n"); + } + } + + vt596_smba &= 0xfff0; + 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)) + 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 + sure, we disable the VT596 first. */ + if (force_addr) { + pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(pdev, id->driver_data, vt596_smba); + pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); + dev_warn(&pdev->dev, "WARNING: SMBus interface set to new " + "address 0x%04x!\n", vt596_smba); + } else if (!(temp & 0x01)) { + if (force) { + /* NOTE: This assumes I/O space and other allocations + * WERE done by the Bios! Don't complain if your + * hardware does weird things after enabling this. + * :') Check for Bios updates before resorting to + * this. + */ + pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); + dev_info(&pdev->dev, "Enabling SMBus device\n"); + } else { + error = dev_err_probe(&pdev->dev, -ENODEV, + "SMBUS: Error: Host SMBus " + "controller not enabled! - " + "upgrade BIOS or use force=1\n"); + goto release_region; + } + } + + dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CX700: + case PCI_DEVICE_ID_VIA_VX800: + case PCI_DEVICE_ID_VIA_VX855: + case PCI_DEVICE_ID_VIA_VX900: + case PCI_DEVICE_ID_VIA_8251: + case PCI_DEVICE_ID_VIA_8237: + case PCI_DEVICE_ID_VIA_8237A: + case PCI_DEVICE_ID_VIA_8237S: + case PCI_DEVICE_ID_VIA_8235: + case PCI_DEVICE_ID_VIA_8233A: + case PCI_DEVICE_ID_VIA_8233_0: + vt596_features |= FEATURE_I2CBLOCK; + break; + case PCI_DEVICE_ID_VIA_82C686_4: + /* The VT82C686B (rev 0x40) does support I2C block + transactions, but the VT82C686A (rev 0x30) doesn't */ + if (pdev->revision >= 0x40) + vt596_features |= FEATURE_I2CBLOCK; + break; + } + + vt596_adapter.dev.parent = &pdev->dev; + snprintf(vt596_adapter.name, sizeof(vt596_adapter.name), + "SMBus Via Pro adapter at %04x", vt596_smba); + + vt596_pdev = pci_dev_get(pdev); + error = i2c_add_adapter(&vt596_adapter); + if (error) { + pci_dev_put(vt596_pdev); + vt596_pdev = NULL; + goto release_region; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + return -ENODEV; + +release_region: + release_region(vt596_smba, 8); + return error; +} + +static const struct pci_device_id vt596_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_0), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233A), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237S), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX900), + .driver_data = SMBBA3 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, vt596_ids); + +static struct pci_driver vt596_driver = { + .name = "vt596_smbus", + .id_table = vt596_ids, + .probe = vt596_probe, +}; + +static int __init i2c_vt596_init(void) +{ + return pci_register_driver(&vt596_driver); +} + + +static void __exit i2c_vt596_exit(void) +{ + pci_unregister_driver(&vt596_driver); + if (vt596_pdev != NULL) { + i2c_del_adapter(&vt596_adapter); + release_region(vt596_smba, 8); + pci_dev_put(vt596_pdev); + vt596_pdev = NULL; + } +} + +MODULE_AUTHOR("Kyosti Malkki "); +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("vt82c596 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_vt596_init); +module_exit(i2c_vt596_exit); diff --git a/drivers/i2c/busses/i2c-viperboard.c b/drivers/i2c/busses/i2c-viperboard.c new file mode 100644 index 000000000..f596efcc2 --- /dev/null +++ b/drivers/i2c/busses/i2c-viperboard.c @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nano River Technologies viperboard i2c controller driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct vprbrd_i2c { + struct i2c_adapter i2c; + u8 bus_freq_param; +}; + +/* i2c bus frequency module parameter */ +static u8 i2c_bus_param; +static unsigned int i2c_bus_freq = 100; +module_param(i2c_bus_freq, int, 0); +MODULE_PARM_DESC(i2c_bus_freq, + "i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000"); + +static int vprbrd_i2c_status(struct i2c_adapter *i2c, + struct vprbrd_i2c_status *status, bool prev_error) +{ + u16 bytes_xfer; + int ret; + struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; + + /* check for protocol error */ + bytes_xfer = sizeof(struct vprbrd_i2c_status); + + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, + status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS); + + if (ret != bytes_xfer) + prev_error = true; + + if (prev_error) { + dev_err(&i2c->dev, "failure in usb communication\n"); + return -EREMOTEIO; + } + + dev_dbg(&i2c->dev, " status = %d\n", status->status); + if (status->status != 0x00) { + dev_err(&i2c->dev, "failure: i2c protocol error\n"); + return -EPROTO; + } + return 0; +} + +static int vprbrd_i2c_receive(struct usb_device *usb_dev, + struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer) +{ + int ret, bytes_actual; + int error = 0; + + /* send the read request */ + ret = usb_bulk_msg(usb_dev, + usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg, + sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual, + VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) + || (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) { + dev_err(&usb_dev->dev, "failure transmitting usb\n"); + error = -EREMOTEIO; + } + + /* read the actual data */ + ret = usb_bulk_msg(usb_dev, + usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg, + bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) || (bytes_xfer != bytes_actual)) { + dev_err(&usb_dev->dev, "failure receiving usb\n"); + error = -EREMOTEIO; + } + return error; +} + +static int vprbrd_i2c_addr(struct usb_device *usb_dev, + struct vprbrd_i2c_addr_msg *amsg) +{ + int ret, bytes_actual; + + ret = usb_bulk_msg(usb_dev, + usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg, + sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual, + VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) || + (sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) { + dev_err(&usb_dev->dev, "failure transmitting usb\n"); + return -EREMOTEIO; + } + return 0; +} + +static int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg) +{ + int ret; + u16 remain_len, len1, len2, start = 0x0000; + struct vprbrd_i2c_read_msg *rmsg = + (struct vprbrd_i2c_read_msg *)vb->buf; + + remain_len = msg->len; + rmsg->header.cmd = VPRBRD_I2C_CMD_READ; + while (remain_len > 0) { + rmsg->header.addr = cpu_to_le16(start + 0x4000); + if (remain_len <= 255) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len; + rmsg->header.len1 = 0x00; + rmsg->header.len2 = 0x00; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 510) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len - 255; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0x00; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 512) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len - 510; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 767) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 512; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 1022) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 767; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 1024) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 1022; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0xff; + remain_len = 0; + } else { + len1 = 512; + len2 = 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = 0x02; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0xff; + remain_len -= 1024; + start += 1024; + } + rmsg->header.tf1 = cpu_to_le16(len1); + rmsg->header.tf2 = cpu_to_le16(len2); + + /* first read transfer */ + ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1); + if (ret < 0) + return ret; + /* copy the received data */ + memcpy(msg->buf + start, rmsg, len1); + + /* second read transfer if necessary */ + if (len2 > 0) { + ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2); + if (ret < 0) + return ret; + /* copy the received data */ + memcpy(msg->buf + start + 512, rmsg, len2); + } + } + return 0; +} + +static int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg) +{ + int ret, bytes_actual; + u16 remain_len, bytes_xfer, + start = 0x0000; + struct vprbrd_i2c_write_msg *wmsg = + (struct vprbrd_i2c_write_msg *)vb->buf; + + remain_len = msg->len; + wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE; + wmsg->header.last = 0x00; + wmsg->header.chan = 0x00; + wmsg->header.spi = 0x0000; + while (remain_len > 0) { + wmsg->header.addr = cpu_to_le16(start + 0x4000); + if (remain_len > 503) { + wmsg->header.len1 = 0xff; + wmsg->header.len2 = 0xf8; + remain_len -= 503; + bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr); + start += 503; + } else if (remain_len > 255) { + wmsg->header.len1 = 0xff; + wmsg->header.len2 = (remain_len - 255); + bytes_xfer = remain_len + + sizeof(struct vprbrd_i2c_write_hdr); + remain_len = 0; + } else { + wmsg->header.len1 = remain_len; + wmsg->header.len2 = 0x00; + bytes_xfer = remain_len + + sizeof(struct vprbrd_i2c_write_hdr); + remain_len = 0; + } + memcpy(wmsg->data, msg->buf + start, + bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr)); + + ret = usb_bulk_msg(vb->usb_dev, + usb_sndbulkpipe(vb->usb_dev, + VPRBRD_EP_OUT), wmsg, + bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); + if ((ret < 0) || (bytes_xfer != bytes_actual)) + return -EREMOTEIO; + } + return 0; +} + +static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs, + int num) +{ + struct i2c_msg *pmsg; + int i, ret, + error = 0; + struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; + struct vprbrd_i2c_addr_msg *amsg = + (struct vprbrd_i2c_addr_msg *)vb->buf; + struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf; + + for (i = 0 ; i < num ; i++) { + pmsg = &msgs[i]; + + dev_dbg(&i2c->dev, + " %d: %s (flags %d) %d bytes to 0x%02x\n", + i, str_read_write(pmsg->flags & I2C_M_RD), + pmsg->flags, pmsg->len, pmsg->addr); + + mutex_lock(&vb->lock); + /* directly send the message */ + if (pmsg->flags & I2C_M_RD) { + /* read data */ + amsg->cmd = VPRBRD_I2C_CMD_ADDR; + amsg->unknown2 = 0x00; + amsg->unknown3 = 0x00; + amsg->addr = pmsg->addr; + amsg->unknown1 = 0x01; + amsg->len = cpu_to_le16(pmsg->len); + /* send the addr and len, we're interested to board */ + ret = vprbrd_i2c_addr(vb->usb_dev, amsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_read(vb, pmsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_status(i2c, smsg, error); + if (ret < 0) + error = ret; + /* in case of protocol error, return the error */ + if (error < 0) + goto error; + } else { + /* write data */ + ret = vprbrd_i2c_write(vb, pmsg); + + amsg->cmd = VPRBRD_I2C_CMD_ADDR; + amsg->unknown2 = 0x00; + amsg->unknown3 = 0x00; + amsg->addr = pmsg->addr; + amsg->unknown1 = 0x00; + amsg->len = cpu_to_le16(pmsg->len); + /* send the addr, the data goes to to board */ + ret = vprbrd_i2c_addr(vb->usb_dev, amsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_status(i2c, smsg, error); + if (ret < 0) + error = ret; + + if (error < 0) + goto error; + } + mutex_unlock(&vb->lock); + } + return num; +error: + mutex_unlock(&vb->lock); + return error; +} + +static u32 vprbrd_i2c_func(struct i2c_adapter *i2c) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* This is the actual algorithm we define */ +static const struct i2c_algorithm vprbrd_algorithm = { + .xfer = vprbrd_i2c_xfer, + .functionality = vprbrd_i2c_func, +}; + +static const struct i2c_adapter_quirks vprbrd_quirks = { + .max_read_len = 2048, + .max_write_len = 2048, +}; + +static int vprbrd_i2c_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_i2c *vb_i2c; + int ret; + int pipe; + + vb_i2c = devm_kzalloc(&pdev->dev, sizeof(*vb_i2c), GFP_KERNEL); + if (vb_i2c == NULL) + return -ENOMEM; + + /* setup i2c adapter description */ + vb_i2c->i2c.owner = THIS_MODULE; + vb_i2c->i2c.class = I2C_CLASS_HWMON; + vb_i2c->i2c.algo = &vprbrd_algorithm; + vb_i2c->i2c.quirks = &vprbrd_quirks; + vb_i2c->i2c.algo_data = vb; + /* save the param in usb capabable memory */ + vb_i2c->bus_freq_param = i2c_bus_param; + + snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name), + "viperboard at bus %03d device %03d", + vb->usb_dev->bus->busnum, vb->usb_dev->devnum); + + /* setting the bus frequency */ + if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ) + && (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) { + pipe = usb_sndctrlpipe(vb->usb_dev, 0); + ret = usb_control_msg(vb->usb_dev, pipe, + VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, &vb_i2c->bus_freq_param, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret != 1) + return dev_err_probe(&pdev->dev, -EIO, + "failure setting i2c_bus_freq to %d\n", + i2c_bus_freq); + } else { + return dev_err_probe(&pdev->dev, -EIO, + "invalid i2c_bus_freq setting:%d\n", i2c_bus_freq); + } + + vb_i2c->i2c.dev.parent = &pdev->dev; + + /* attach to i2c layer */ + i2c_add_adapter(&vb_i2c->i2c); + + platform_set_drvdata(pdev, vb_i2c); + + return 0; +} + +static void vprbrd_i2c_remove(struct platform_device *pdev) +{ + struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&vb_i2c->i2c); +} + +static struct platform_driver vprbrd_i2c_driver = { + .driver.name = "viperboard-i2c", + .probe = vprbrd_i2c_probe, + .remove = vprbrd_i2c_remove, +}; + +static int __init vprbrd_i2c_init(void) +{ + switch (i2c_bus_freq) { + case 6000: + i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ; + break; + case 3000: + i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ; + break; + case 1000: + i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ; + break; + case 400: + i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ; + break; + case 200: + i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ; + break; + case 100: + i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; + break; + case 10: + i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ; + break; + default: + pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq); + i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; + } + + return platform_driver_register(&vprbrd_i2c_driver); +} +subsys_initcall(vprbrd_i2c_init); + +static void __exit vprbrd_i2c_exit(void) +{ + platform_driver_unregister(&vprbrd_i2c_driver); +} +module_exit(vprbrd_i2c_exit); + +MODULE_AUTHOR("Lars Poeschel "); +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 000000000..5da6fef92 --- /dev/null +++ b/drivers/i2c/busses/i2c-virtio.c @@ -0,0 +1,280 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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 = kzalloc_objs(*reqs, num); + 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)); + + virtio_device_ready(vdev); + + 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 "); +MODULE_AUTHOR("Conghui Chen "); +MODULE_DESCRIPTION("Virtio i2c bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c new file mode 100644 index 000000000..b29dec66b --- /dev/null +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * X-Gene SLIMpro I2C Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Feng Kan + * Author: Hieu Le + * + * This driver provides support for X-Gene SLIMpro I2C device access + * using the APM X-Gene SLIMpro mailbox driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */ +#define MAILBOX_I2C_INDEX 0 +#define SLIMPRO_IIC_BUS 1 /* Use I2C bus 1 only */ + +#define SMBUS_CMD_LEN 1 +#define BYTE_DATA 1 +#define WORD_DATA 2 +#define BLOCK_DATA 3 + +#define SLIMPRO_IIC_I2C_PROTOCOL 0 +#define SLIMPRO_IIC_SMB_PROTOCOL 1 + +#define SLIMPRO_IIC_READ 0 +#define SLIMPRO_IIC_WRITE 1 + +#define IIC_SMB_WITHOUT_DATA_LEN 0 +#define IIC_SMB_WITH_DATA_LEN 1 + +#define SLIMPRO_DEBUG_MSG 0 +#define SLIMPRO_MSG_TYPE_SHIFT 28 +#define SLIMPRO_DBG_SUBTYPE_I2C1READ 4 +#define SLIMPRO_DBGMSG_TYPE_SHIFT 24 +#define SLIMPRO_DBGMSG_TYPE_MASK 0x0F000000U +#define SLIMPRO_IIC_DEV_SHIFT 23 +#define SLIMPRO_IIC_DEV_MASK 0x00800000U +#define SLIMPRO_IIC_DEVID_SHIFT 13 +#define SLIMPRO_IIC_DEVID_MASK 0x007FE000U +#define SLIMPRO_IIC_RW_SHIFT 12 +#define SLIMPRO_IIC_RW_MASK 0x00001000U +#define SLIMPRO_IIC_PROTO_SHIFT 11 +#define SLIMPRO_IIC_PROTO_MASK 0x00000800U +#define SLIMPRO_IIC_ADDRLEN_SHIFT 8 +#define SLIMPRO_IIC_ADDRLEN_MASK 0x00000700U +#define SLIMPRO_IIC_DATALEN_SHIFT 0 +#define SLIMPRO_IIC_DATALEN_MASK 0x000000FFU + +/* + * SLIMpro I2C message encode + * + * dev - Controller number (0-based) + * chip - I2C chip address + * op - SLIMPRO_IIC_READ or SLIMPRO_IIC_WRITE + * proto - SLIMPRO_IIC_SMB_PROTOCOL or SLIMPRO_IIC_I2C_PROTOCOL + * addrlen - Length of the address field + * datalen - Length of the data field + */ +#define SLIMPRO_IIC_ENCODE_MSG(dev, chip, op, proto, addrlen, datalen) \ + ((SLIMPRO_DEBUG_MSG << SLIMPRO_MSG_TYPE_SHIFT) | \ + ((SLIMPRO_DBG_SUBTYPE_I2C1READ << SLIMPRO_DBGMSG_TYPE_SHIFT) & \ + SLIMPRO_DBGMSG_TYPE_MASK) | \ + ((dev << SLIMPRO_IIC_DEV_SHIFT) & SLIMPRO_IIC_DEV_MASK) | \ + ((chip << SLIMPRO_IIC_DEVID_SHIFT) & SLIMPRO_IIC_DEVID_MASK) | \ + ((op << SLIMPRO_IIC_RW_SHIFT) & SLIMPRO_IIC_RW_MASK) | \ + ((proto << SLIMPRO_IIC_PROTO_SHIFT) & SLIMPRO_IIC_PROTO_MASK) | \ + ((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_MASK) | \ + ((datalen << SLIMPRO_IIC_DATALEN_SHIFT) & SLIMPRO_IIC_DATALEN_MASK)) + +#define SLIMPRO_MSG_TYPE(v) (((v) & 0xF0000000) >> 28) + +/* + * Encode for upper address for block data + */ +#define SLIMPRO_IIC_ENCODE_FLAG_BUFADDR 0x80000000 +#define SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(a) ((u32) (((a) << 30) \ + & 0x40000000)) +#define SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(a) ((u32) (((a) >> 12) \ + & 0x3FF00000)) +#define SLIMPRO_IIC_ENCODE_ADDR(a) ((a) & 0x000FFFFF) + +#define SLIMPRO_IIC_MSG_DWORD_COUNT 3 + +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; +}; + +#define to_slimpro_i2c_dev(cl) \ + container_of(cl, struct slimpro_i2c_dev, mbox_client) + +enum slimpro_i2c_version { + XGENE_SLIMPRO_I2C_V1 = 0, + XGENE_SLIMPRO_I2C_V2 = 1, +}; + +/* + * This function tests and clears a bitmask then returns its old value + */ +static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) +{ + u16 ret, val; + + val = le16_to_cpu(READ_ONCE(*addr)); + ret = val & mask; + val &= ~mask; + WRITE_ONCE(*addr, cpu_to_le16(val)); + + return ret; +} + +static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg) +{ + struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl); + + /* + * Response message format: + * mssg[0] is the return code of the operation + * mssg[1] is the first data word + * mssg[2] is NOT used + */ + if (ctx->resp_msg) + *ctx->resp_msg = ((u32 *)mssg)[1]; + + if (ctx->mbox_client.tx_block) + complete(&ctx->rd_complete); +} + +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 __iomem *generic_comm_base = + ctx->pcc_chan->shmem; + + /* Check if platform sends interrupt */ + if (!xgene_word_tst_and_clr(&generic_comm_base->status, + PCC_STATUS_SCI_DOORBELL)) + return; + + if (xgene_word_tst_and_clr(&generic_comm_base->status, + PCC_STATUS_CMD_COMPLETE)) { + msg = generic_comm_base + 1; + + /* Response message msg[1] contains the return value. */ + if (ctx->resp_msg) + *ctx->resp_msg = ((u32 *)msg)[1]; + + complete(&ctx->rd_complete); + } +} + +static void slimpro_i2c_pcc_tx_prepare(struct slimpro_i2c_dev *ctx, u32 *msg) +{ + struct acpi_pcct_shared_memory __iomem *generic_comm_base = + ctx->pcc_chan->shmem; + u32 *ptr = (void *)(generic_comm_base + 1); + u16 status; + int i; + + WRITE_ONCE(generic_comm_base->signature, + 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_INTR)); + + status = le16_to_cpu(READ_ONCE(generic_comm_base->status)); + status &= ~PCC_STATUS_CMD_COMPLETE; + WRITE_ONCE(generic_comm_base->status, cpu_to_le16(status)); + + /* Copy the message to the PCC comm space */ + for (i = 0; i < SLIMPRO_IIC_MSG_DWORD_COUNT; i++) + WRITE_ONCE(ptr[i], cpu_to_le32(msg[i])); +} + +static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx) +{ + if (ctx->mbox_client.tx_block || !acpi_disabled) { + if (!wait_for_completion_timeout(&ctx->rd_complete, + msecs_to_jiffies(MAILBOX_OP_TIMEOUT))) + return -ETIMEDOUT; + } + + /* Check of invalid data or no device */ + if (*ctx->resp_msg == 0xffffffff) + return -ENODEV; + + return 0; +} + +static int slimpro_i2c_send_msg(struct slimpro_i2c_dev *ctx, + u32 *msg, + u32 *data) +{ + int rc; + + ctx->resp_msg = data; + + if (!acpi_disabled) { + reinit_completion(&ctx->rd_complete); + slimpro_i2c_pcc_tx_prepare(ctx, msg); + } + + rc = mbox_send_message(ctx->mbox_chan, msg); + if (rc < 0) + goto err; + + rc = start_i2c_msg_xfer(ctx); + +err: + if (!acpi_disabled) + mbox_chan_txdone(ctx->mbox_chan, 0); + + ctx->resp_msg = NULL; + + return rc; +} + +static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, + u32 readlen, u32 *data) +{ + u32 msg[3]; + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, + SLIMPRO_IIC_READ, protocol, addrlen, readlen); + msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = 0; + + return slimpro_i2c_send_msg(ctx, msg, data); +} + +static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, u32 writelen, + u32 data) +{ + u32 msg[3]; + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, + SLIMPRO_IIC_WRITE, protocol, addrlen, writelen); + msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = data; + + return slimpro_i2c_send_msg(ctx, msg, msg); +} + +static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr, + u32 addrlen, u32 protocol, u32 readlen, + u32 with_data_len, void *data) +{ + dma_addr_t paddr; + u32 msg[3]; + int rc; + + paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE); + if (dma_mapping_error(ctx->dev, paddr)) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); + return -ENOMEM; + } + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ, + protocol, addrlen, readlen); + msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR | + SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(with_data_len) | + SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | + SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = (u32)paddr; + + rc = slimpro_i2c_send_msg(ctx, msg, msg); + + /* Copy to destination */ + memcpy(data, ctx->dma_buffer, readlen); + + dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE); + return rc; +} + +static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, u32 writelen, + void *data) +{ + dma_addr_t paddr; + 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); + if (dma_mapping_error(ctx->dev, paddr)) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); + return -ENOMEM; + } + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE, + protocol, addrlen, writelen); + msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR | + SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | + SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = (u32)paddr; + + if (ctx->mbox_client.tx_block) + reinit_completion(&ctx->rd_complete); + + rc = slimpro_i2c_send_msg(ctx, msg, msg); + + dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE); + return rc; +} + +static int xgene_slimpro_i2c_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct slimpro_i2c_dev *ctx = i2c_get_adapdata(adap); + int ret = -EOPNOTSUPP; + u32 val; + + switch (size) { + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, 0, 0, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, &val); + data->byte = val; + } else { + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + 0, 0); + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, &val); + data->byte = val; + } else { + val = data->byte; + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, val); + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + WORD_DATA, &val); + data->word = val; + } else { + val = data->word; + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + WORD_DATA, val); + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_blkrd(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + I2C_SMBUS_BLOCK_MAX + 1, + IIC_SMB_WITH_DATA_LEN, + &data->block[0]); + + } else { + ret = slimpro_i2c_blkwr(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + data->block[0] + 1, + &data->block[0]); + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_blkrd(ctx, addr, + command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_I2C_PROTOCOL, + I2C_SMBUS_BLOCK_MAX, + IIC_SMB_WITHOUT_DATA_LEN, + &data->block[1]); + } else { + ret = slimpro_i2c_blkwr(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_I2C_PROTOCOL, + data->block[0], + &data->block[1]); + } + break; + default: + break; + } + return ret; +} + +/* +* Return list of supported functionality. +*/ +static u32 xgene_slimpro_i2c_func(struct i2c_adapter *adapter) +{ + 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 xgene_slimpro_i2c_algorithm = { + .smbus_xfer = xgene_slimpro_i2c_xfer, + .functionality = xgene_slimpro_i2c_func, +}; + +static int xgene_slimpro_i2c_probe(struct platform_device *pdev) +{ + struct slimpro_i2c_dev *ctx; + struct i2c_adapter *adapter; + struct mbox_client *cl; + int rc; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = &pdev->dev; + platform_set_drvdata(pdev, ctx); + cl = &ctx->mbox_client; + + /* Request mailbox channel */ + cl->dev = &pdev->dev; + init_completion(&ctx->rd_complete); + cl->tx_tout = MAILBOX_OP_TIMEOUT; + cl->knows_txdone = false; + if (acpi_disabled) { + 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)) + return dev_err_probe(&pdev->dev, PTR_ERR(ctx->mbox_chan), + "i2c mailbox channel request failed\n"); + } else { + struct pcc_mbox_chan *pcc_chan; + const struct acpi_device_id *acpi_id; + + acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, + &pdev->dev); + if (!acpi_id) + return -EINVAL; + + 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; + 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"); + + ctx->pcc_chan = pcc_chan; + ctx->mbox_chan = pcc_chan->mchan; + + if (!ctx->mbox_chan->mbox->txdone_irq) { + rc = dev_err_probe(&pdev->dev, -ENOENT, + "PCC IRQ not supported\n"); + goto mbox_err; + } + + } + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (rc) + dev_warn(&pdev->dev, "Unable to set dma mask\n"); + + /* Setup I2C adapter */ + adapter = &ctx->adapter; + snprintf(adapter->name, sizeof(adapter->name), "MAILBOX I2C"); + adapter->algo = &xgene_slimpro_i2c_algorithm; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + adapter->dev.of_node = pdev->dev.of_node; + ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev)); + i2c_set_adapdata(adapter, ctx); + rc = i2c_add_adapter(adapter); + if (rc) + goto mbox_err; + + dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n"); + return 0; + +mbox_err: + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->pcc_chan); + + return rc; +} + +static void xgene_slimpro_i2c_remove(struct platform_device *pdev) +{ + struct slimpro_i2c_dev *ctx = platform_get_drvdata(pdev); + + i2c_del_adapter(&ctx->adapter); + + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->pcc_chan); +} + +static const struct of_device_id xgene_slimpro_i2c_dt_ids[] = { + {.compatible = "apm,xgene-slimpro-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = { + {"APMC0D40", XGENE_SLIMPRO_I2C_V1}, + {"APMC0D8B", XGENE_SLIMPRO_I2C_V2}, + {} +}; +MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids); +#endif + +static struct platform_driver xgene_slimpro_i2c_driver = { + .probe = xgene_slimpro_i2c_probe, + .remove = xgene_slimpro_i2c_remove, + .driver = { + .name = "xgene-slimpro-i2c", + .of_match_table = of_match_ptr(xgene_slimpro_i2c_dt_ids), + .acpi_match_table = ACPI_PTR(xgene_slimpro_i2c_acpi_ids) + }, +}; + +module_platform_driver(xgene_slimpro_i2c_driver); + +MODULE_DESCRIPTION("APM X-Gene SLIMpro I2C driver"); +MODULE_AUTHOR("Feng Kan "); +MODULE_AUTHOR("Hieu Le "); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c new file mode 100644 index 000000000..3e7735e1d --- /dev/null +++ b/drivers/i2c/busses/i2c-xiic.c @@ -0,0 +1,1570 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i2c-xiic.c + * Copyright (c) 2002-2007 Xilinx Inc. + * Copyright (c) 2009-2010 Intel Corporation + * + * 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. + * Mocean Laboratories forked off the GNU/Linux platform work into a + * separate company called Pelagicore AB, which committed the code to the + * kernel. + */ + +/* Supports: + * Xilinx IIC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, + STATE_ERROR, + STATE_START +}; + +enum xiic_endian { + LITTLE, + 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 + * @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; + 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) + +/* + * 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 */ + +/* + * 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 */ +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ + +/* Status Register masks */ +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ +#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */ + +/* Interrupt Status Register masks Interrupt occurs when... */ +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ + +/* The following constants specify the depth of the FIFOs */ +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ + +/* The following constants specify groups of interrupts that are typically + * enabled or disables at the same time + */ +#define XIIC_TX_INTERRUPTS \ +(XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK) + +#define XIIC_TX_RX_INTERRUPTS (XIIC_INTR_RX_FULL_MASK | XIIC_TX_INTERRUPTS) + +/* + * Tx Fifo upper bit masks. + */ +#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */ +#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */ + +/* 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 + * to allow other registers to be added and still match the memory map of the + * interrupt controller registers + */ +#define XIIC_DGIER_OFFSET 0x1C /* Device Global Interrupt Enable Register */ +#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XIIC_IIER_OFFSET 0x28 /* Interrupt Enable Register */ +#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */ + +#define XIIC_RESET_MASK 0xAUL + +#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 + * in the register + */ +#define XIIC_GINTR_ENABLE_MASK 0x80000000UL + +#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 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. + * Only the least significant byte [doublet] of the register are ever + * accessed. This requires an offset of 3 [2] from the base address for + * big-endian systems. + */ + +static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) +{ + if (i2c->endianness == LITTLE) + iowrite8(value, i2c->base + reg); + else + iowrite8(value, i2c->base + reg + 3); +} + +static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) +{ + u8 ret; + + if (i2c->endianness == LITTLE) + ret = ioread8(i2c->base + reg); + else + ret = ioread8(i2c->base + reg + 3); + return ret; +} + +static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) +{ + if (i2c->endianness == LITTLE) + iowrite16(value, i2c->base + reg); + else + iowrite16be(value, i2c->base + reg + 2); +} + +static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) +{ + if (i2c->endianness == LITTLE) + iowrite32(value, i2c->base + reg); + else + iowrite32be(value, i2c->base + reg); +} + +static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) +{ + u32 ret; + + if (i2c->endianness == LITTLE) + ret = ioread32(i2c->base + reg); + else + ret = ioread32be(i2c->base + reg); + return ret; +} + +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); +} + +static inline void xiic_irq_clr_en(struct xiic_i2c *i2c, u32 mask) +{ + xiic_irq_clr(i2c, mask); + xiic_irq_en(i2c, mask); +} + +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)) { + 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 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); + + /* Reset Tx Fifo. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); + + /* make sure RX fifo is empty */ + ret = xiic_clear_rx_fifo(i2c); + if (ret) + return ret; + + /* Enable interrupts */ + 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) +{ + u8 cr; + + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Disable IIC Device. */ + cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET); + 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, cr = 0, bytes_to_read = 0; + u32 bytes_rem = 0; + int i; + + bytes_in_fifo = xiic_getreg8(i2c, XIIC_RFO_REG_OFFSET) + 1; + + 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); + + 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); + } + + 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) +{ + /* return the actual space left in the FIFO */ + return IIC_TX_FIFO_DEPTH - xiic_getreg8(i2c, XIIC_TFO_REG_OFFSET) - 1; +} + +static void xiic_fill_tx_fifo(struct xiic_i2c *i2c) +{ + u8 fifo_space = xiic_tx_fifo_space(i2c); + int len = xiic_tx_space(i2c); + + len = (len > fifo_space) ? fifo_space : len; + + 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) && 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); + } + 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, enum xilinx_i2c_state code) +{ + i2c->tx_msg = NULL; + i2c->rx_msg = NULL; + i2c->nmsgs = 0; + i2c->state = code; + complete(&i2c->completion); +} + +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. + * To find which interrupts are pending; AND interrupts pending with + * interrupts masked. + */ + mutex_lock(&i2c->lock); + isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET); + pend = isr & ier; + + dev_dbg(i2c->adap.dev.parent, "%s: IER: 0x%x, ISR: 0x%x, pend: 0x%x\n", + __func__, ier, isr, pend); + 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))) { + /* bus arbritration lost, or... + * Transmit error _OR_ RX completed + * if this happens when RX_FULL is not set + * this is probably a TX error + */ + + dev_dbg(i2c->adap.dev.parent, "%s error\n", __func__); + + /* dynamic mode seem to suffer from problems if we just flushes + * fifos and the next message is a TX with len 0 (only addr) + * reset the IP instead of just flush fifos + */ + ret = xiic_reinit(i2c); + if (ret < 0) + dev_dbg(i2c->adap.dev.parent, "reinit failed\n"); + + 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 */ + + clr |= XIIC_INTR_RX_FULL_MASK; + if (!i2c->rx_msg) { + dev_dbg(i2c->adap.dev.parent, + "%s unexpected RX IRQ\n", __func__); + xiic_clear_rx_fifo(i2c); + goto out; + } + + xiic_read_rx(i2c); + if (xiic_rx_space(i2c) == 0) { + /* this is the last part of the message */ + i2c->rx_msg = NULL; + + /* also clear TX error if there (RX complete) */ + clr |= (isr & XIIC_INTR_TX_ERROR_MASK); + + dev_dbg(i2c->adap.dev.parent, + "%s end of message, nmsgs: %d\n", + __func__, i2c->nmsgs); + + /* send next message if this wasn't the last, + * otherwise the transfer will be finialise when + * receiving the bus not busy interrupt + */ + if (i2c->nmsgs > 1) { + i2c->nmsgs--; + i2c->tx_msg++; + dev_dbg(i2c->adap.dev.parent, + "%s will start next...\n", __func__); + xfer_more = 1; + } + } + } + if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) { + /* Transmit register/FIFO is empty or ½ empty */ + + clr |= (pend & + (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)); + + if (!i2c->tx_msg) { + dev_dbg(i2c->adap.dev.parent, + "%s unexpected TX IRQ\n", __func__); + goto out; + } + + 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); + /* 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++; + xfer_more = 1; + } else { + xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK); + + dev_dbg(i2c->adap.dev.parent, + "%s Got TX IRQ but no more to do...\n", + __func__); + } + } + } + + 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; +} + +static int xiic_bus_busy(struct xiic_i2c *i2c) +{ + u8 sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); + + return (sr & XIIC_SR_BUS_BUSY_MASK) ? -EBUSY : 0; +} + +static int xiic_wait_not_busy(struct xiic_i2c *i2c) +{ + int tries = 3; + int err; + + /* 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--) { + 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) +{ + u16 rx_watermark; + u8 cr = 0, rfd_set = 0; + struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg; + + 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); + + if (rx_watermark > 0) + bytes--; + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, bytes); + + /* write the address */ + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + 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_setreg16(i2c, XIIC_DTR_REG_OFFSET, val); + + 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 */ + xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK); + + /* 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; + + 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 (i2c->dynamic) { + /* write the address */ + 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); + + /* 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)); + + 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; + + 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)); + } + + /* Write address to FIFO */ + data = i2c_8bit_addr_from_msg(msg); + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data); + + /* Fill fifo */ + xiic_fill_tx_fifo(i2c); + + 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 fifo_space = xiic_tx_fifo_space(i2c); + + 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; + 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; + + 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; + } + + 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->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; + + 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) +{ + struct xiic_i2c *i2c = i2c_get_adapdata(adap); + int err; + + dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); + + err = pm_runtime_resume_and_get(i2c->dev); + if (err < 0) + return err; + + err = xiic_start_xfer(i2c, msgs, num); + if (err < 0) + goto out; + + 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; + } else { + err = (i2c->state == STATE_DONE) ? num : -EIO; + } + mutex_unlock(&i2c->lock); + +out: + 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 | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm xiic_algorithm = { + .xfer = xiic_xfer, + .xfer_atomic = xiic_xfer_atomic, + .functionality = xiic_func, +}; + +static const struct i2c_adapter xiic_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_DEPRECATED, + .algo = &xiic_algorithm, +}; + +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); + +static int xiic_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct xiic_i2c *i2c; + struct xiic_i2c_platform_data *pdata; + const struct xiic_version_data *data; + struct resource *res; + int ret, irq; + u8 i; + u32 sr; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + data = device_get_match_data(dev); + if (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); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + pdata = dev_get_platdata(dev); + + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = xiic_adapter; + i2c->adap.nr = pdev->id; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + device_set_node(&i2c->adap.dev, fwnode); + snprintf(i2c->adap.name, sizeof(i2c->adap.name), + DRIVER_NAME " %s", pdev->name); + + ret = devm_mutex_init(dev, &i2c->lock); + if (ret) + return ret; + + spin_lock_init(&i2c->atomic_lock); + + if (is_of_node(fwnode)) { + i2c->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(i2c->clk)) + return dev_err_probe(dev, PTR_ERR(i2c->clk), + "failed to enable input clock.\n"); + } + + i2c->dev = dev; + + pm_runtime_set_autosuspend_delay(dev, XIIC_PM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + /* SCL frequency configuration */ + i2c->input_clk = clk_get_rate(i2c->clk); + ret = device_property_read_u32(dev, "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(dev, irq, NULL, xiic_process, + IRQF_ONESHOT, pdev->name, i2c); + if (ret) + return ret; + + i2c->singlemaster = device_property_read_bool(dev, "single-master"); + + /* + * Detect endianness + * Try to reset the TX FIFO. Then check the EMPTY flag. If it is not + * set, assume that the endianness was wrong and swap. + */ + i2c->endianness = LITTLE; + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + /* Reset is cleared in xiic_reinit */ + sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); + if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK)) + i2c->endianness = BIG; + + ret = xiic_reinit(i2c); + if (ret) + return dev_err_probe(dev, ret, "Cannot xiic_reinit\n"); + + /* add i2c adapter to i2c tree */ + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret) { + xiic_deinit(i2c); + return ret; + } + + if (pdata) { + /* add in known devices to the bus */ + for (i = 0; i < pdata->num_devices; i++) + i2c_new_client_device(&i2c->adap, pdata->devices + i); + } + + dev_dbg(dev, "mmio %pR irq %d scl clock frequency %d\n", + res, irq, i2c->i2c_clk); + + return 0; +} + +static void xiic_i2c_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct xiic_i2c *i2c = platform_get_drvdata(pdev); + int ret; + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + dev_warn(dev, "Failed to activate device for removal (%pe)\n", + ERR_PTR(ret)); + else + xiic_deinit(i2c); + + pm_runtime_put_sync(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, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(xiic_of_match), + .pm = &xiic_dev_pm_ops, + }, +}; + +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"); diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c new file mode 100644 index 000000000..ad0e5cd21 --- /dev/null +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2003-2015 Broadcom Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XLP9XX_I2C_DIV 0x0 +#define XLP9XX_I2C_CTRL 0x1 +#define XLP9XX_I2C_CMD 0x2 +#define XLP9XX_I2C_STATUS 0x3 +#define XLP9XX_I2C_MTXFIFO 0x4 +#define XLP9XX_I2C_MRXFIFO 0x5 +#define XLP9XX_I2C_MFIFOCTRL 0x6 +#define XLP9XX_I2C_STXFIFO 0x7 +#define XLP9XX_I2C_SRXFIFO 0x8 +#define XLP9XX_I2C_SFIFOCTRL 0x9 +#define XLP9XX_I2C_SLAVEADDR 0xA +#define XLP9XX_I2C_OWNADDR 0xB +#define XLP9XX_I2C_FIFOWCNT 0xC +#define XLP9XX_I2C_INTEN 0xD +#define XLP9XX_I2C_INTST 0xE +#define XLP9XX_I2C_WAITCNT 0xF +#define XLP9XX_I2C_TIMEOUT 0X10 +#define XLP9XX_I2C_GENCALLADDR 0x11 + +#define XLP9XX_I2C_STATUS_BUSY BIT(0) + +#define XLP9XX_I2C_CMD_START BIT(7) +#define XLP9XX_I2C_CMD_STOP BIT(6) +#define XLP9XX_I2C_CMD_READ BIT(5) +#define XLP9XX_I2C_CMD_WRITE BIT(4) +#define XLP9XX_I2C_CMD_ACK BIT(3) + +#define XLP9XX_I2C_CTRL_MCTLEN_SHIFT 16 +#define XLP9XX_I2C_CTRL_MCTLEN_MASK 0xffff0000 +#define XLP9XX_I2C_CTRL_RST BIT(8) +#define XLP9XX_I2C_CTRL_EN BIT(6) +#define XLP9XX_I2C_CTRL_MASTER BIT(4) +#define XLP9XX_I2C_CTRL_FIFORD BIT(1) +#define XLP9XX_I2C_CTRL_ADDMODE BIT(0) + +#define XLP9XX_I2C_INTEN_NACKADDR BIT(25) +#define XLP9XX_I2C_INTEN_SADDR BIT(13) +#define XLP9XX_I2C_INTEN_DATADONE BIT(12) +#define XLP9XX_I2C_INTEN_ARLOST BIT(11) +#define XLP9XX_I2C_INTEN_MFIFOFULL BIT(4) +#define XLP9XX_I2C_INTEN_MFIFOEMTY BIT(3) +#define XLP9XX_I2C_INTEN_MFIFOHI BIT(2) +#define XLP9XX_I2C_INTEN_BUSERR BIT(0) + +#define XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT 8 +#define XLP9XX_I2C_MFIFOCTRL_LOTH_SHIFT 0 +#define XLP9XX_I2C_MFIFOCTRL_RST BIT(16) + +#define XLP9XX_I2C_SLAVEADDR_RW BIT(0) +#define XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT 1 + +#define XLP9XX_I2C_IP_CLK_FREQ 133000000UL +#define XLP9XX_I2C_FIFO_SIZE 0x80U +#define XLP9XX_I2C_TIMEOUT_MS 1000 +#define XLP9XX_I2C_BUSY_TIMEOUT 50 + +#define XLP9XX_I2C_FIFO_WCNT_MASK 0xff +#define XLP9XX_I2C_STATUS_ERRMASK (XLP9XX_I2C_INTEN_ARLOST | \ + XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_BUSERR) + +struct xlp9xx_i2c_dev { + struct device *dev; + struct i2c_adapter adapter; + struct completion msg_complete; + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; + int irq; + bool msg_read; + bool len_recv; + bool client_pec; + u32 __iomem *base; + u32 msg_buf_remaining; + u32 msg_len; + u32 ip_clk_hz; + u32 clk_hz; + u32 msg_err; + u8 *msg_buf; +}; + +static inline void xlp9xx_write_i2c_reg(struct xlp9xx_i2c_dev *priv, + unsigned long reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static inline u32 xlp9xx_read_i2c_reg(struct xlp9xx_i2c_dev *priv, + unsigned long reg) +{ + return readl(priv->base + reg); +} + +static void xlp9xx_i2c_mask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) +{ + u32 inten; + + inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) & ~mask; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); +} + +static void xlp9xx_i2c_unmask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) +{ + u32 inten; + + inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) | mask; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); +} + +static void xlp9xx_i2c_update_rx_fifo_thres(struct xlp9xx_i2c_dev *priv) +{ + u32 thres; + + if (priv->len_recv) + /* interrupt after the first read to examine + * the length byte before proceeding further + */ + thres = 1; + else if (priv->msg_buf_remaining > XLP9XX_I2C_FIFO_SIZE) + thres = XLP9XX_I2C_FIFO_SIZE; + else + thres = priv->msg_buf_remaining; + + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, + thres << XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT); +} + +static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv) +{ + u32 len, i; + u8 *buf = priv->msg_buf; + + len = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE); + for (i = 0; i < len; i++) + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MTXFIFO, buf[i]); + priv->msg_buf_remaining -= len; + priv->msg_buf += len; +} + +static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv) +{ + u32 val, len; + + /* + * Update receive length. Re-read len to get the latest value, + * and then add 4 to have a minimum value that can be safely + * written. This is to account for the byte read above, the + * transfer in progress and any delays in the register I/O + */ + val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); + len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & + XLP9XX_I2C_FIFO_WCNT_MASK; + len = max_t(u32, priv->msg_len, len + 4); + if (len >= I2C_SMBUS_BLOCK_MAX + 2) + return; + val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | + (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); +} + +static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv) +{ + u32 len, i; + u8 rlen, *buf = priv->msg_buf; + + len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & + XLP9XX_I2C_FIFO_WCNT_MASK; + if (!len) + return; + if (priv->len_recv) { + /* read length byte */ + rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); + + /* + * We expect at least 2 interrupts for I2C_M_RECV_LEN + * transactions. The length is updated during the first + * interrupt, and the buffer contents are only copied + * during subsequent interrupts. If in case the interrupts + * get merged we would complete the transaction without + * copying out the bytes from RX fifo. To avoid this now we + * drain the fifo as and when data is available. + * We drained the rlen byte already, decrement total length + * by one. + */ + + len--; + if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) { + rlen = 0; /*abort transfer */ + priv->msg_buf_remaining = 0; + priv->msg_len = 0; + xlp9xx_i2c_update_rlen(priv); + return; + } + + *buf++ = rlen; + if (priv->client_pec) + ++rlen; /* account for error check byte */ + /* update remaining bytes and message length */ + priv->msg_buf_remaining = rlen; + priv->msg_len = rlen + 1; + xlp9xx_i2c_update_rlen(priv); + priv->len_recv = false; + } + + len = min(priv->msg_buf_remaining, len); + for (i = 0; i < len; i++, buf++) + *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); + + priv->msg_buf_remaining -= len; + priv->msg_buf = buf; + + if (priv->msg_buf_remaining) + xlp9xx_i2c_update_rx_fifo_thres(priv); +} + +static irqreturn_t xlp9xx_i2c_isr(int irq, void *dev_id) +{ + struct xlp9xx_i2c_dev *priv = dev_id; + u32 status; + + status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTST); + if (status == 0) + return IRQ_NONE; + + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTST, status); + if (status & XLP9XX_I2C_STATUS_ERRMASK) { + priv->msg_err = status; + goto xfer_done; + } + + /* SADDR ACK for SMBUS_QUICK */ + if ((status & XLP9XX_I2C_INTEN_SADDR) && (priv->msg_len == 0)) + goto xfer_done; + + if (!priv->msg_read) { + if (status & XLP9XX_I2C_INTEN_MFIFOEMTY) { + /* TX FIFO got empty, fill it up again */ + if (priv->msg_buf_remaining) + xlp9xx_i2c_fill_tx_fifo(priv); + else + xlp9xx_i2c_mask_irq(priv, + XLP9XX_I2C_INTEN_MFIFOEMTY); + } + } else { + if (status & (XLP9XX_I2C_INTEN_DATADONE | + XLP9XX_I2C_INTEN_MFIFOHI)) { + /* data is in FIFO, read it */ + if (priv->msg_buf_remaining) + xlp9xx_i2c_drain_rx_fifo(priv); + } + } + + /* Transfer complete */ + if (status & XLP9XX_I2C_INTEN_DATADONE) + goto xfer_done; + + return IRQ_HANDLED; + +xfer_done: + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + complete(&priv->msg_complete); + return IRQ_HANDLED; +} + +static int xlp9xx_i2c_check_bus_status(struct xlp9xx_i2c_dev *priv) +{ + u32 status; + u32 busy_timeout = XLP9XX_I2C_BUSY_TIMEOUT; + + while (busy_timeout) { + status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_STATUS); + if ((status & XLP9XX_I2C_STATUS_BUSY) == 0) + break; + + busy_timeout--; + usleep_range(1000, 1100); + } + + if (!busy_timeout) + return -EIO; + + return 0; +} + +static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv) +{ + u32 prescale; + + /* + * The controller uses 5 * SCL clock internally. + * So prescale value should be divided by 5. + */ + prescale = DIV_ROUND_UP(priv->ip_clk_hz, priv->clk_hz); + prescale = ((prescale - 8) / 5) - 1; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_RST); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_EN | + XLP9XX_I2C_CTRL_MASTER); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_DIV, prescale); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + + return 0; +} + +static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, + int last_msg) +{ + unsigned long timeleft; + u32 intr_mask, cmd, val, len; + + priv->msg_buf = msg->buf; + priv->msg_buf_remaining = priv->msg_len = msg->len; + priv->msg_err = 0; + priv->msg_read = (msg->flags & I2C_M_RD); + reinit_completion(&priv->msg_complete); + + /* Reset FIFO */ + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, + XLP9XX_I2C_MFIFOCTRL_RST); + + /* set slave addr */ + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR, + (msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) | + (priv->msg_read ? XLP9XX_I2C_SLAVEADDR_RW : 0)); + + /* Build control word for transfer */ + val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); + if (!priv->msg_read) + val &= ~XLP9XX_I2C_CTRL_FIFORD; + else + val |= XLP9XX_I2C_CTRL_FIFORD; /* read */ + + if (msg->flags & I2C_M_TEN) + val |= XLP9XX_I2C_CTRL_ADDMODE; /* 10-bit address mode*/ + else + val &= ~XLP9XX_I2C_CTRL_ADDMODE; + + priv->len_recv = msg->flags & I2C_M_RECV_LEN; + len = priv->len_recv ? I2C_SMBUS_BLOCK_MAX + 2 : msg->len; + priv->client_pec = msg->flags & I2C_CLIENT_PEC; + + /* set FIFO threshold if reading */ + if (priv->msg_read) + xlp9xx_i2c_update_rx_fifo_thres(priv); + + /* set data length to be transferred */ + val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | + (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); + + /* fill fifo during tx */ + if (!priv->msg_read) + xlp9xx_i2c_fill_tx_fifo(priv); + + /* set interrupt mask */ + intr_mask = (XLP9XX_I2C_INTEN_ARLOST | XLP9XX_I2C_INTEN_BUSERR | + XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_DATADONE); + + if (priv->msg_read) { + intr_mask |= XLP9XX_I2C_INTEN_MFIFOHI; + if (msg->len == 0) + intr_mask |= XLP9XX_I2C_INTEN_SADDR; + } else { + if (msg->len == 0) + intr_mask |= XLP9XX_I2C_INTEN_SADDR; + else + intr_mask |= XLP9XX_I2C_INTEN_MFIFOEMTY; + } + xlp9xx_i2c_unmask_irq(priv, intr_mask); + + /* set cmd reg */ + cmd = XLP9XX_I2C_CMD_START; + if (msg->len) + cmd |= (priv->msg_read ? + XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE); + if (last_msg) + cmd |= XLP9XX_I2C_CMD_STOP; + + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, cmd); + + timeleft = msecs_to_jiffies(XLP9XX_I2C_TIMEOUT_MS); + timeleft = wait_for_completion_timeout(&priv->msg_complete, timeleft); + + if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR) { + dev_dbg(priv->dev, "transfer error %x!\n", priv->msg_err); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, XLP9XX_I2C_CMD_STOP); + return -EIO; + } else if (priv->msg_err & XLP9XX_I2C_INTEN_NACKADDR) { + return -ENXIO; + } + + if (timeleft == 0) { + dev_dbg(priv->dev, "i2c transfer timed out!\n"); + xlp9xx_i2c_init(priv); + return -ETIMEDOUT; + } + + /* update msg->len with actual received length */ + if (msg->flags & I2C_M_RECV_LEN) { + if (!priv->msg_len) + return -EPROTO; + msg->len = priv->msg_len; + } + return 0; +} + +static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int i, ret; + struct xlp9xx_i2c_dev *priv = i2c_get_adapdata(adap); + + ret = xlp9xx_i2c_check_bus_status(priv); + if (ret) { + xlp9xx_i2c_init(priv); + ret = xlp9xx_i2c_check_bus_status(priv); + if (ret) + return ret; + } + + for (i = 0; i < num; i++) { + ret = xlp9xx_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret != 0) + return ret; + } + + return num; +} + +static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm xlp9xx_i2c_algo = { + .xfer = xlp9xx_i2c_xfer, + .functionality = xlp9xx_i2c_functionality, +}; + +static int xlp9xx_i2c_get_frequency(struct platform_device *pdev, + struct xlp9xx_i2c_dev *priv) +{ + struct clk *clk; + u32 freq; + int err; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + priv->ip_clk_hz = XLP9XX_I2C_IP_CLK_FREQ; + dev_dbg(&pdev->dev, "using default input frequency %u\n", + priv->ip_clk_hz); + } else { + priv->ip_clk_hz = clk_get_rate(clk); + } + + err = device_property_read_u32(&pdev->dev, "clock-frequency", &freq); + if (err) { + freq = I2C_MAX_STANDARD_MODE_FREQ; + dev_dbg(&pdev->dev, "using default frequency %u\n", freq); + } else if (freq == 0 || freq > I2C_MAX_FAST_MODE_FREQ) { + dev_warn(&pdev->dev, "invalid frequency %u, using default\n", + freq); + freq = I2C_MAX_STANDARD_MODE_FREQ; + } + priv->clk_hz = freq; + + return 0; +} + +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; + + ara = i2c_new_smbus_alert_device(&priv->adapter, &priv->alert_data); + if (IS_ERR(ara)) + return PTR_ERR(ara); + + priv->ara = ara; + + return 0; +} + +static int xlp9xx_i2c_probe(struct platform_device *pdev) +{ + struct xlp9xx_i2c_dev *priv; + int err = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + 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) + return priv->irq; + /* SMBAlert irq */ + priv->alert_data.irq = platform_get_irq(pdev, 1); + if (priv->alert_data.irq <= 0) + priv->alert_data.irq = 0; + + xlp9xx_i2c_get_frequency(pdev, priv); + xlp9xx_i2c_init(priv); + + err = devm_request_irq(&pdev->dev, priv->irq, xlp9xx_i2c_isr, 0, + pdev->name, priv); + if (err) + return err; + + init_completion(&priv->msg_complete); + priv->adapter.dev.parent = &pdev->dev; + priv->adapter.algo = &xlp9xx_i2c_algo; + priv->adapter.class = I2C_CLASS_HWMON; + ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); + priv->adapter.dev.of_node = pdev->dev.of_node; + priv->dev = &pdev->dev; + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), "xlp9xx-i2c"); + i2c_set_adapdata(&priv->adapter, priv); + + err = i2c_add_adapter(&priv->adapter); + if (err) + return err; + + err = xlp9xx_i2c_smbus_setup(priv, pdev); + if (err) + dev_dbg(&pdev->dev, "No active SMBus alert %d\n", err); + + platform_set_drvdata(pdev, priv); + dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr); + + return 0; +} + +static void xlp9xx_i2c_remove(struct platform_device *pdev) +{ + struct xlp9xx_i2c_dev *priv; + + priv = platform_get_drvdata(pdev); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + synchronize_irq(priv->irq); + i2c_del_adapter(&priv->adapter); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, 0); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = { + {"BRCM9007", 0}, + {"CAV9007", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids); +#endif + +static struct platform_driver xlp9xx_i2c_driver = { + .probe = xlp9xx_i2c_probe, + .remove = xlp9xx_i2c_remove, + .driver = { + .name = "xlp9xx-i2c", + .acpi_match_table = ACPI_PTR(xlp9xx_i2c_acpi_ids), + }, +}; + +module_platform_driver(xlp9xx_i2c_driver); + +MODULE_AUTHOR("Subhendu Sekhar Behera "); +MODULE_DESCRIPTION("XLP9XX/5XX I2C Bus Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c new file mode 100644 index 000000000..0cd0a18a7 --- /dev/null +++ b/drivers/i2c/busses/scx200_acb.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 ACCESS.bus support + Also supports the AMD CS5535 and AMD CS5536 + + Based on i2c-keywest.c which is: + Copyright (c) 2001 Benjamin Herrenschmidt + Copyright (c) 2000 Philip Edelbrock + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver"); +MODULE_ALIAS("platform:cs5535-smb"); +MODULE_LICENSE("GPL"); + +#define MAX_DEVICES 4 +static int base[MAX_DEVICES] = { 0x820, 0x840 }; +module_param_hw_array(base, int, ioport, NULL, 0); +MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers"); + +#define POLL_TIMEOUT (HZ/5) + +enum scx200_acb_state { + state_idle, + state_address, + state_command, + state_repeat_start, + state_quick, + state_read, + state_write, +}; + +static const char *scx200_acb_state_name[] = { + "idle", + "address", + "command", + "repeat_start", + "quick", + "read", + "write", +}; + +/* Physical interface */ +struct scx200_acb_iface { + struct scx200_acb_iface *next; + struct i2c_adapter adapter; + unsigned base; + struct mutex mutex; + + /* State machine data */ + enum scx200_acb_state state; + int result; + u8 address_byte; + u8 command; + u8 *ptr; + char needs_reset; + unsigned len; +}; + +/* Register Definitions */ +#define ACBSDA (iface->base + 0) +#define ACBST (iface->base + 1) +#define ACBST_SDAST 0x40 /* SDA Status */ +#define ACBST_BER 0x20 +#define ACBST_NEGACK 0x10 /* Negative Acknowledge */ +#define ACBST_STASTR 0x08 /* Stall After Start */ +#define ACBST_MASTER 0x02 +#define ACBCST (iface->base + 2) +#define ACBCST_BB 0x02 +#define ACBCTL1 (iface->base + 3) +#define ACBCTL1_STASTRE 0x80 +#define ACBCTL1_NMINTE 0x40 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 +#define ACBADDR (iface->base + 4) +#define ACBCTL2 (iface->base + 5) +#define ACBCTL2_ENABLE 0x01 + +/************************************************************************/ + +static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) +{ + const char *errmsg; + + dev_dbg(&iface->adapter.dev, "state %s, status = 0x%02x\n", + scx200_acb_state_name[iface->state], status); + + if (status & ACBST_BER) { + errmsg = "bus error"; + goto error; + } + if (!(status & ACBST_MASTER)) { + errmsg = "not master"; + goto error; + } + if (status & ACBST_NEGACK) { + dev_dbg(&iface->adapter.dev, "negative ack in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -ENXIO; + + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + outb(ACBST_STASTR | ACBST_NEGACK, ACBST); + + /* Reset the status register */ + outb(0, ACBST); + return; + } + + switch (iface->state) { + case state_idle: + dev_warn(&iface->adapter.dev, "interrupt in idle state\n"); + break; + + case state_address: + /* Do a pointer write first */ + outb(iface->address_byte & ~1, ACBSDA); + + iface->state = state_command; + break; + + case state_command: + outb(iface->command, ACBSDA); + + if (iface->address_byte & 1) + iface->state = state_repeat_start; + else + iface->state = state_write; + break; + + case state_repeat_start: + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + fallthrough; + + case state_quick: + if (iface->address_byte & 1) { + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + outb(iface->address_byte, ACBSDA); + + iface->state = state_read; + } else { + outb(iface->address_byte, ACBSDA); + + iface->state = state_write; + } + break; + + case state_read: + /* Set ACK if _next_ byte will be the last one */ + if (iface->len == 2) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + + if (iface->len == 1) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + } + + *iface->ptr++ = inb(ACBSDA); + --iface->len; + + break; + + case state_write: + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + break; + } + + outb(*iface->ptr++, ACBSDA); + --iface->len; + + break; + } + + return; + + error: + dev_err(&iface->adapter.dev, + "%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg, + scx200_acb_state_name[iface->state], iface->address_byte, + iface->len, status); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_acb_poll(struct scx200_acb_iface *iface) +{ + u8 status; + unsigned long timeout; + + timeout = jiffies + POLL_TIMEOUT; + while (1) { + status = inb(ACBST); + + /* Reset the status register to avoid the hang */ + outb(0, ACBST); + + if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) { + scx200_acb_machine(iface, status); + return; + } + if (time_after(jiffies, timeout)) + break; + cpu_relax(); + cond_resched(); + } + + dev_err(&iface->adapter.dev, "timeout in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_acb_reset(struct scx200_acb_iface *iface) +{ + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + /* Polling mode */ + outb(0, ACBCTL1); + /* Disable slave address */ + outb(0, ACBADDR); + /* Enable the ACCESS.bus device */ + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + /* Free STALL after START */ + outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1); + /* Send a STOP */ + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + /* Clear BER, NEGACK and STASTR bits */ + outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST); + /* Clear BB bit */ + outb(inb(ACBCST) | ACBCST_BB, ACBCST); +} + +static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, + u16 address, unsigned short flags, + char rw, u8 command, int size, + union i2c_smbus_data *data) +{ + struct scx200_acb_iface *iface = i2c_get_adapdata(adapter); + int len; + u8 *buffer; + u16 cur_word; + int rc; + + switch (size) { + case I2C_SMBUS_QUICK: + len = 0; + buffer = NULL; + break; + + case I2C_SMBUS_BYTE: + len = 1; + buffer = rw ? &data->byte : &command; + break; + + case I2C_SMBUS_BYTE_DATA: + len = 1; + buffer = &data->byte; + break; + + case I2C_SMBUS_WORD_DATA: + len = 2; + cur_word = cpu_to_le16(data->word); + buffer = (u8 *)&cur_word; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + buffer = &data->block[1]; + break; + + default: + return -EINVAL; + } + + dev_dbg(&adapter->dev, + "size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", + size, address, command, len, rw); + + if (!len && rw == I2C_SMBUS_READ) { + dev_dbg(&adapter->dev, "zero length read\n"); + return -EINVAL; + } + + mutex_lock(&iface->mutex); + + iface->address_byte = (address << 1) | rw; + iface->command = command; + iface->ptr = buffer; + iface->len = len; + iface->result = -EINVAL; + iface->needs_reset = 0; + + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) + iface->state = state_quick; + else + iface->state = state_address; + + while (iface->state != state_idle) + scx200_acb_poll(iface); + + if (iface->needs_reset) + scx200_acb_reset(iface); + + rc = iface->result; + + mutex_unlock(&iface->mutex); + + if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ) + data->word = le16_to_cpu(cur_word); + +#ifdef DEBUG + dev_dbg(&adapter->dev, "transfer done, result: %d", rc); + if (buffer) { + int i; + printk(" data:"); + for (i = 0; i < len; ++i) + printk(" %02x", buffer[i]); + } + printk("\n"); +#endif + + return rc; +} + +static u32 scx200_acb_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_I2C_BLOCK; +} + +/* For now, we only handle combined mode (smbus) */ +static const struct i2c_algorithm scx200_acb_algorithm = { + .smbus_xfer = scx200_acb_smbus_xfer, + .functionality = scx200_acb_func, +}; + +static struct scx200_acb_iface *scx200_acb_list; +static DEFINE_MUTEX(scx200_acb_list_mutex); + +static int scx200_acb_probe(struct scx200_acb_iface *iface) +{ + u8 val; + + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + + if (inb(ACBCTL2) != 0x70) { + pr_debug("ACBCTL2 readback failed\n"); + return -ENXIO; + } + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if (val) { + pr_debug("disabled, but ACBCTL1=0x%02x\n", val); + return -ENXIO; + } + + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { + pr_debug("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", + val); + return -ENXIO; + } + + return 0; +} + +static struct scx200_acb_iface *scx200_create_iface(const char *text, + struct device *dev, int index) +{ + struct scx200_acb_iface *iface; + struct i2c_adapter *adapter; + + iface = kzalloc_obj(*iface); + if (!iface) + return NULL; + + adapter = &iface->adapter; + i2c_set_adapdata(adapter, iface); + 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; + adapter->dev.parent = dev; + + mutex_init(&iface->mutex); + + return iface; +} + +static int scx200_acb_create(struct scx200_acb_iface *iface) +{ + struct i2c_adapter *adapter; + int rc; + + adapter = &iface->adapter; + + rc = scx200_acb_probe(iface); + if (rc) { + pr_warn("probe failed\n"); + return rc; + } + + scx200_acb_reset(iface); + + if (i2c_add_adapter(adapter) < 0) { + pr_err("failed to register\n"); + return -ENODEV; + } + + if (!adapter->dev.parent) { + /* If there's no dev, we're tracking (ISA) ifaces manually */ + mutex_lock(&scx200_acb_list_mutex); + iface->next = scx200_acb_list; + scx200_acb_list = iface; + mutex_unlock(&scx200_acb_list_mutex); + } + + return 0; +} + +static struct scx200_acb_iface *scx200_create_dev(const char *text, + unsigned long base, int index, struct device *dev) +{ + struct scx200_acb_iface *iface; + int rc; + + iface = scx200_create_iface(text, dev, index); + + if (iface == NULL) + return NULL; + + if (!request_region(base, 8, iface->adapter.name)) { + pr_err("can't allocate io 0x%lx-0x%lx\n", base, base + 8 - 1); + goto errout_free; + } + + iface->base = base; + rc = scx200_acb_create(iface); + + if (rc == 0) + return iface; + + release_region(base, 8); + errout_free: + kfree(iface); + return NULL; +} + +static int scx200_probe(struct platform_device *pdev) +{ + struct scx200_acb_iface *iface; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + 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) + return -EIO; + + dev_info(&pdev->dev, "SCx200 device '%s' registered\n", + iface->adapter.name); + platform_set_drvdata(pdev, iface); + + return 0; +} + +static void scx200_cleanup_iface(struct scx200_acb_iface *iface) +{ + i2c_del_adapter(&iface->adapter); + release_region(iface->base, 8); + kfree(iface); +} + +static void scx200_remove(struct platform_device *pdev) +{ + struct scx200_acb_iface *iface; + + iface = platform_get_drvdata(pdev); + scx200_cleanup_iface(iface); +} + +static struct platform_driver scx200_pci_driver = { + .driver = { + .name = "cs5535-smb", + }, + .probe = scx200_probe, + .remove = scx200_remove, +}; + +static const struct pci_device_id scx200_isa[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) }, + { 0, } +}; + +static __init void scx200_scan_isa(void) +{ + int i; + + if (!pci_dev_present(scx200_isa)) + return; + + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] == 0) + continue; + + /* XXX: should we care about failures? */ + scx200_create_dev("SCx200", base[i], i, NULL); + } +} + +static int __init scx200_acb_init(void) +{ + pr_debug("NatSemi SCx200 ACCESS.bus Driver\n"); + + /* First scan for ISA-based devices */ + scx200_scan_isa(); /* XXX: should we care about errors? */ + + /* If at least one bus was created, init must succeed */ + if (scx200_acb_list) + return 0; + + /* No ISA devices; register the platform driver for PCI-based devices */ + return platform_driver_register(&scx200_pci_driver); +} + +static void __exit scx200_acb_cleanup(void) +{ + struct scx200_acb_iface *iface; + + platform_driver_unregister(&scx200_pci_driver); + + mutex_lock(&scx200_acb_list_mutex); + while ((iface = scx200_acb_list) != NULL) { + scx200_acb_list = iface->next; + mutex_unlock(&scx200_acb_list_mutex); + + scx200_cleanup_iface(iface); + + mutex_lock(&scx200_acb_list_mutex); + } + mutex_unlock(&scx200_acb_list_mutex); +} + +module_init(scx200_acb_init); +module_exit(scx200_acb_cleanup); diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c new file mode 100644 index 000000000..e6d2af659 --- /dev/null +++ b/drivers/i2c/i2c-atr.c @@ -0,0 +1,945 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C Address Translator + * + * Copyright (c) 2019,2022 Luca Ceresoli + * Copyright (c) 2022,2023 Tomi Valkeinen + * + * Originally based on i2c-mux.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * @use_mask: Mask of used aliases + * @aliases: Array of aliases, must hold exactly @size elements + */ +struct i2c_atr_alias_pool { + size_t size; + bool shared; + + /* Protects aliases and use_mask */ + spinlock_t lock; + unsigned long *use_mask; + u16 aliases[] __counted_by(size); +}; + +/** + * 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_flex(*alias_pool, aliases, num_aliases); + if (!alias_pool) + return ERR_PTR(-ENOMEM); + + alias_pool->size = num_aliases; + + alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL); + if (!alias_pool->use_mask) { + ret = -ENOMEM; + goto err_free_alias_pool; + } + + alias_pool->shared = shared; + + spin_lock_init(&alias_pool->lock); + + return alias_pool; + +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); +} + +/* 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_obj(*c2a); + 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_flex(*atr, adapter, max_adapters); + 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_obj(*chan); + 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", ®); + 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 "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("I2C Address Translator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c new file mode 100644 index 000000000..735fd13fe --- /dev/null +++ b/drivers/i2c/i2c-boardinfo.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-boardinfo.c - collect pre-declarations of I2C devices + */ + +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + + +/* These symbols are exported ONLY FOR the i2c core. + * No other users will be supported. + */ +DECLARE_RWSEM(__i2c_board_lock); +EXPORT_SYMBOL_GPL(__i2c_board_lock); + +LIST_HEAD(__i2c_board_list); +EXPORT_SYMBOL_GPL(__i2c_board_list); + +int __i2c_first_dynamic_bus_num; +EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); + + +/** + * i2c_register_board_info - statically declare I2C devices + * @busnum: identifies the bus to which these devices belong + * @info: vector of i2c device descriptors + * @len: how many descriptors in the vector; may be zero to reserve + * the specified bus number. + * + * Systems using the Linux I2C driver stack can declare tables of board info + * while they initialize. This should be done in board-specific init code + * near arch_initcall() time, or equivalent, before any I2C adapter driver is + * registered. For example, mainboard init code could define several devices, + * as could the init code for each daughtercard in a board stack. + * + * The I2C devices will be created later, after the adapter for the relevant + * bus has been registered. After that moment, standard driver model tools + * are used to bind "new style" I2C drivers to the devices. The bus number + * for any device declared using this routine is not available for dynamic + * allocation. + * + * 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. + */ +int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) +{ + int status; + + down_write(&__i2c_board_lock); + + /* dynamic bus numbers will be assigned after the last static one */ + if (busnum >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = busnum + 1; + + for (status = 0; len; len--, info++) { + struct i2c_devinfo *devinfo; + + devinfo = kzalloc_obj(*devinfo); + if (!devinfo) { + pr_debug("i2c-core: can't register boardinfo!\n"); + status = -ENOMEM; + break; + } + + devinfo->busnum = busnum; + devinfo->board_info = *info; + + if (info->resources) { + devinfo->board_info.resources = + kmemdup(info->resources, + info->num_resources * + sizeof(*info->resources), + GFP_KERNEL); + if (!devinfo->board_info.resources) { + status = -ENOMEM; + kfree(devinfo); + break; + } + } + + list_add_tail(&devinfo->list, &__i2c_board_list); + } + + up_write(&__i2c_board_lock); + + return status; +} diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c new file mode 100644 index 000000000..8f3bdd501 --- /dev/null +++ b/drivers/i2c/i2c-core-acpi.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core ACPI support code + * + * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +struct i2c_acpi_handler_data { + struct acpi_connection_info info; + struct i2c_adapter *adapter; +}; + +struct gsb_buffer { + u8 status; + u8 len; + union { + u16 wdata; + u8 bdata; + DECLARE_FLEX_ARRAY(u8, data); + }; +} __packed; + +struct i2c_acpi_lookup { + struct i2c_board_info *info; + acpi_handle adapter_handle; + acpi_handle device_handle; + acpi_handle search_handle; + int n; + int index; + u32 speed; + u32 min_speed; + u32 force_speed; +}; + +/** + * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches + * @ares: ACPI resource + * @i2c: Pointer to I2cSerialBus resource will be returned here + * + * Checks if the given ACPI resource is of type I2cSerialBus. + * In this case, returns a pointer to it to the caller. + * + * Returns true if resource type is of I2cSerialBus, otherwise false. + */ +bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c) +{ + struct acpi_resource_i2c_serialbus *sb; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return false; + + sb = &ares->data.i2c_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) + return false; + + *i2c = sb; + return true; +} +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 + * + * Return: + * The number of I2cSerialBus resources in the ACPI-device's + * resource-list; or a negative error code. + * + * Specifically returns -ENOENT when no resources found. + */ +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 ?: -ENOENT; +} +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; + struct i2c_board_info *info = lookup->info; + struct acpi_resource_i2c_serialbus *sb; + acpi_status status; + + if (info->addr || !i2c_acpi_get_i2c_resource(ares, &sb)) + return 1; + + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + + status = acpi_get_handle(lookup->device_handle, + sb->resource_source.string_ptr, + &lookup->adapter_handle); + if (ACPI_FAILURE(status)) + return 1; + + info->addr = sb->slave_address; + lookup->speed = sb->connection_speed; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + + return 1; +} + +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) +{ + struct i2c_board_info *info = lookup->info; + struct list_head resource_list; + int ret; + + if (acpi_bus_get_status(adev)) + return -EINVAL; + + if (!acpi_dev_ready_for_enumeration(adev)) + return -ENODEV; + + /* + * ACPI video devices, which are handled by the acpi-video driver, + * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. + */ + if (acpi_dev_is_video_device(adev)) + return -ENODEV; + + memset(info, 0, sizeof(*info)); + lookup->device_handle = acpi_device_handle(adev); + + /* Look up for I2cSerialBus resource */ + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, lookup); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resource_list); + + if (!info->addr) + return -EINVAL; + + 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 i2c_acpi_lookup lookup; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + 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 (!device_match_acpi_handle(&adapter->dev, lookup.adapter_handle)) + return -ENODEV; + } else { + struct acpi_device *adapter_adev; + + /* The adapter must be present */ + 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) + return -ENODEV; + } + + info->fwnode = acpi_fwnode_handle(adev); + if (adapter_handle) + *adapter_handle = lookup.adapter_handle; + + acpi_set_modalias(adev, dev_name(&adev->dev), info->type, + sizeof(info->type)); + + return 0; +} + +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 (IS_ERR(i2c_new_client_device(adapter, info))) + adev->power.flags.ignore_parent = false; +} + +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 = acpi_fetch_acpi_dev(handle); + struct i2c_board_info info; + + if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL)) + return AE_OK; + + i2c_acpi_register_device(adapter, adev, &info); + + return AE_OK; +} + +#define I2C_ACPI_MAX_SCAN_DEPTH 32 + +/** + * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter + * @adap: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +void i2c_acpi_register_devices(struct i2c_adapter *adap) +{ + struct acpi_device *adev; + acpi_status status; + + if (!has_acpi_companion(&adap->dev)) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_add_device, NULL, + adap, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); + + if (!adap->dev.parent) + return; + + adev = ACPI_COMPANION(adap->dev.parent); + if (!adev) + return; + + 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 }, + { "ELAN0678", 0 }, + { "ELAN06FA", 0 }, + { "ELAN1300", 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 = acpi_fetch_acpi_dev(handle); + + if (!adev || i2c_acpi_do_lookup(adev, lookup)) + return AE_OK; + + if (lookup->search_handle != lookup->adapter_handle) + return AE_OK; + + 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; +} + +/** + * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI + * @dev: The device owning the bus + * + * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves + * devices connected to this bus and use the speed of slowest device. + * + * Returns the speed in Hz or zero + */ +u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + struct i2c_acpi_lookup lookup; + struct i2c_board_info dummy; + acpi_status status; + + if (!has_acpi_companion(dev)) + return 0; + + memset(&lookup, 0, sizeof(lookup)); + lookup.search_handle = ACPI_HANDLE(dev); + lookup.min_speed = UINT_MAX; + lookup.info = &dummy; + lookup.index = -1; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_lookup_speed, NULL, + &lookup, NULL); + + if (ACPI_FAILURE(status)) { + dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); + return 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); + +struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) +{ + 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) + put_device(dev); + + return adapter; +} +EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); + +static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) +{ + return i2c_find_device_by_fwnode(acpi_fwnode_handle(adev)); +} + +static struct i2c_adapter *i2c_acpi_find_adapter_by_adev(struct acpi_device *adev) +{ + return i2c_find_adapter_by_fwnode(acpi_fwnode_handle(adev)); +} + +static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, + void *arg) +{ + struct acpi_device *adev = arg; + struct i2c_board_info info; + acpi_handle adapter_handle; + struct i2c_adapter *adapter; + struct i2c_client *client; + + switch (value) { + case ACPI_RECONFIG_DEVICE_ADD: + if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle)) + break; + + adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); + if (!adapter) + 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) { + 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); + } + + break; + } + + return NOTIFY_OK; +} + +struct notifier_block i2c_acpi_notifier = { + .notifier_call = i2c_acpi_notify, +}; + +/** + * 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 + * + * By default the i2c subsys creates an i2c-client for the first I2cSerialBus + * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus + * 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_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_by_fwnode(struct fwnode_handle *fwnode, + int index, + struct i2c_board_info *info) +{ + struct i2c_acpi_lookup lookup; + struct i2c_adapter *adapter; + struct acpi_device *adev; + LIST_HEAD(resource_list); + int ret; + + adev = to_acpi_device_node(fwnode); + if (!adev) + return ERR_PTR(-ENODEV); + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + lookup.device_handle = acpi_device_handle(adev); + lookup.index = index; + + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, &lookup); + if (ret < 0) + return ERR_PTR(ret); + + acpi_dev_free_resource_list(&resource_list); + + if (!info->addr) + return ERR_PTR(-EADDRNOTAVAIL); + + adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); + if (!adapter) + return ERR_PTR(-EPROBE_DEFER); + + 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 driver->flags & I2C_DRV_ACPI_WAIVE_D0_PROBE && + adev && adev->power.state_for_enumeration >= adev->power.state; +} +EXPORT_SYMBOL_GPL(i2c_acpi_waive_d0_probe); + +#ifdef CONFIG_ACPI_I2C_OPREGION +static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[2]; + int ret; + u8 *buffer; + + buffer = kzalloc(data_len, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = &cmd; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = data_len; + msgs[1].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + /* Getting a NACK is unfortunately normal with some DSTDs */ + if (ret == -EREMOTEIO) + dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", + data_len, client->addr, cmd, ret); + else + dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", + data_len, client->addr, cmd, ret); + /* 2 transfers must have completed successfully */ + } else if (ret == 2) { + memcpy(data, buffer, data_len); + ret = 0; + } else { + ret = -EIO; + } + + kfree(buffer); + return ret; +} + +static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[1]; + u8 *buffer; + int ret = AE_OK; + + buffer = kzalloc(data_len + 1, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + buffer[0] = cmd; + memcpy(buffer + 1, data, data_len); + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = data_len + 1; + msgs[0].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + + kfree(buffer); + + if (ret < 0) { + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); + return ret; + } + + /* 1 transfer must have completed successfully */ + return (ret == 1) ? 0 : -EIO; +} + +static acpi_status +i2c_acpi_space_handler(u32 function, acpi_physical_address command, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct gsb_buffer *gsb = (struct gsb_buffer *)value64; + struct i2c_acpi_handler_data *data = handler_context; + struct acpi_connection_info *info = &data->info; + struct acpi_resource_i2c_serialbus *sb; + struct i2c_adapter *adapter = data->adapter; + struct i2c_client *client; + struct acpi_resource *ares; + u32 accessor_type = function >> 16; + u8 action = function & ACPI_IO_MASK; + acpi_status ret; + int status; + + ret = acpi_buffer_to_resource(info->connection, info->length, &ares); + if (ACPI_FAILURE(ret)) + return ret; + + client = kzalloc_obj(*client); + if (!client) { + ret = AE_NO_MEMORY; + goto err; + } + + if (!value64 || !i2c_acpi_get_i2c_resource(ares, &sb)) { + ret = AE_BAD_PARAMETER; + goto err; + } + + client->adapter = adapter; + client->addr = sb->slave_address; + + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + client->flags |= I2C_CLIENT_TEN; + + switch (accessor_type) { + case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte(client); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte(client, gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BYTE: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte_data(client, command); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte_data(client, command, + gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_WORD: + if (action == ACPI_READ) { + status = i2c_smbus_read_word_data(client, command); + if (status >= 0) { + gsb->wdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_word_data(client, command, + gsb->wdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BLOCK: + if (action == ACPI_READ) { + status = i2c_smbus_read_block_data(client, command, + gsb->data); + if (status >= 0) { + gsb->len = status; + status = 0; + } + } else { + status = i2c_smbus_write_block_data(client, command, + gsb->len, gsb->data); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: + if (action == ACPI_READ) { + status = acpi_gsb_i2c_read_bytes(client, command, + gsb->data, info->access_length); + } else { + status = acpi_gsb_i2c_write_bytes(client, command, + gsb->data, info->access_length); + } + break; + + default: + dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", + accessor_type, client->addr); + ret = AE_BAD_PARAMETER; + goto err; + } + + gsb->status = status; + + err: + kfree(client); + ACPI_FREE(ares); + return ret; +} + + +int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle; + struct i2c_acpi_handler_data *data; + acpi_status status; + + if (!adapter->dev.parent) + return -ENODEV; + + handle = ACPI_HANDLE(adapter->dev.parent); + + if (!handle) + return -ENODEV; + + data = kzalloc_obj(struct i2c_acpi_handler_data); + if (!data) + return -ENOMEM; + + data->adapter = adapter; + status = acpi_bus_attach_private_data(handle, (void *)data); + if (ACPI_FAILURE(status)) { + kfree(data); + return -ENOMEM; + } + + status = acpi_install_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &i2c_acpi_space_handler, + NULL, + data); + if (ACPI_FAILURE(status)) { + dev_err(&adapter->dev, "Error installing i2c space handler\n"); + acpi_bus_detach_private_data(handle); + kfree(data); + return -ENOMEM; + } + + return 0; +} + +void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle; + struct i2c_acpi_handler_data *data; + acpi_status status; + + if (!adapter->dev.parent) + return; + + handle = ACPI_HANDLE(adapter->dev.parent); + + if (!handle) + return; + + acpi_remove_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &i2c_acpi_space_handler); + + status = acpi_bus_get_private_data(handle, (void **)&data); + if (ACPI_SUCCESS(status)) + kfree(data); + + acpi_bus_detach_private_data(handle); +} +#endif /* CONFIG_ACPI_I2C_OPREGION */ diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c new file mode 100644 index 000000000..ddaacf876 --- /dev/null +++ b/drivers/i2c/i2c-core-base.c @@ -0,0 +1,2720 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki + * Mux support by Rodolfo Giometti and + * Michael Lawnick + * + * Copyright (C) 2013-2017 Wolfram Sang + */ + +#define pr_fmt(fmt) "i2c-core: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include + +#define I2C_ADDR_OFFSET_TEN_BIT 0xa000 +#define I2C_ADDR_OFFSET_SLAVE 0x1000 + +#define I2C_ADDR_7BITS_MAX 0x77 +#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1) + +#define I2C_ADDR_DEVICE_ID 0x7c + +/* + * core_lock protects i2c_adapter_idr, and guarantees that device detection, + * deletion of detected devices are serialized + */ +static DEFINE_MUTEX(core_lock); +static DEFINE_IDR(i2c_adapter_idr); + +static void i2c_deregister_clients(struct i2c_adapter *adap); +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); + return 0; +} + +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) +{ + if (!(id && client)) + return NULL; + + while (id->name[0]) { + if (strcmp(client->name, id->name) == 0) + return id; + id++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(i2c_match_id); + +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); + const struct i2c_driver *driver; + + + /* Attempt an OF style match */ + if (i2c_of_match_device(drv->of_match_table, client)) + return 1; + + /* Then ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + driver = to_i2c_driver(drv); + + /* Finally an I2C match */ + if (i2c_match_id(driver->id_table, client)) + return 1; + + return 0; +} + +static int i2c_device_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + const struct i2c_client *client = to_i2c_client(dev); + int rc; + + rc = of_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + return add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, client->name); +} + +/* i2c bus recovery routines */ +static int get_scl_gpio_value(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap->bus_recovery_info->scl_gpiod); +} + +static void set_scl_gpio_value(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap->bus_recovery_info->scl_gpiod, val); +} + +static int get_sda_gpio_value(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap->bus_recovery_info->sda_gpiod); +} + +static void set_sda_gpio_value(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val); +} + +static int i2c_generic_bus_free(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + int ret = -EOPNOTSUPP; + + if (bri->get_bus_free) + ret = bri->get_bus_free(adap); + else if (bri->get_sda) + ret = bri->get_sda(adap); + + if (ret < 0) + return ret; + + return ret ? 0 : -EBUSY; +} + +/* + * We are generating clock pulses. ndelay() determines durating of clk pulses. + * We will generate clock with rate 100 KHz and so duration of both clock levels + * is: delay in ns = (10^6 / 100) / 2 + */ +#define RECOVERY_NDELAY 5000 +#define RECOVERY_CLK_CNT 9 + +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 = 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. Note that we must honour tsu:sto, 4us, but lets use 5us + * here for simplicity. + */ + bri->set_scl(adap, scl); + ndelay(RECOVERY_NDELAY); + if (bri->set_sda) + bri->set_sda(adap, scl); + ndelay(RECOVERY_NDELAY / 2); + + /* + * By this time SCL is high, as we need to give 9 falling-rising edges + */ + while (i++ < RECOVERY_CLK_CNT * 2) { + if (scl) { + /* SCL shouldn't be low here */ + if (!bri->get_scl(adap)) { + dev_err(&adap->dev, + "SCL is stuck low, exit recovery\n"); + ret = -EBUSY; + break; + } + } + + scl = !scl; + bri->set_scl(adap, scl); + /* Creating STOP again, see above */ + 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); + + if (scl) { + ret = i2c_generic_bus_free(adap); + if (ret == 0) + break; + } + } + + /* If we can't check bus status, assume recovery worked */ + if (ret == -EOPNOTSUPP) + ret = 0; + + if (bri->unprepare_recovery) + bri->unprepare_recovery(adap); + if (bri->pinctrl) + pinctrl_select_state(bri->pinctrl, bri->pins_default); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery); + +int i2c_recover_bus(struct i2c_adapter *adap) +{ + if (!adap->bus_recovery_info) + 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_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 0; + + if (i2c_gpio_init_recovery(adap) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (!bri->recover_bus) { + err_str = "no suitable method provided"; + is_error_level = false; + goto err; + } + + if (bri->scl_gpiod && bri->recover_bus == i2c_generic_scl_recovery) { + bri->get_scl = get_scl_gpio_value; + bri->set_scl = set_scl_gpio_value; + if (bri->sda_gpiod) { + bri->get_sda = get_sda_gpio_value; + if (gpiod_get_direction(bri->sda_gpiod) == GPIO_LINE_DIRECTION_OUT || + gpiod_is_single_ended(bri->sda_gpiod)) + bri->set_sda = set_sda_gpio_value; + } + } 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"; + goto err; + } + if (!bri->set_sda && !bri->get_sda) { + err_str = "either get_sda() or set_sda() needed"; + goto err; + } + } + + return 0; + err: + 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) +{ + struct i2c_adapter *adap = client->adapter; + unsigned int irq; + + if (!adap->host_notify_domain) + return -ENXIO; + + if (client->flags & I2C_CLIENT_TEN) + return -EINVAL; + + irq = irq_create_mapping(adap->host_notify_domain, client->addr); + + return irq > 0 ? irq : -ENXIO; +} + +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; + + client->irq = client->init_irq; + + 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 (is_of_node(fwnode)) { + irq = fwnode_irq_get_byname(fwnode, "irq"); + if (irq == -EINVAL || irq == -ENODATA) + 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 < 0) + irq = 0; + + 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 && + !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; + + 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); + + if (wakeirq > 0 && wakeirq != client->irq) + status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); + else if (client->irq > 0) + status = dev_pm_set_wake_irq(dev, client->irq); + else + status = 0; + + if (status) + dev_warn(&client->dev, "failed to set up wakeup irq\n"); + } + + dev_dbg(dev, "probe\n"); + + status = of_clk_set_defaults(to_of_node(fwnode), false); + if (status < 0) + goto err_clear_wakeup_irq; + + 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; + + 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_release_driver_resources; + + return 0; + +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 void i2c_device_remove(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_driver *driver; + + driver = to_i2c_driver(dev->driver); + if (driver->remove) { + dev_dbg(dev, "remove\n"); + + driver->remove(client); + } + + 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; + if (client->flags & I2C_CLIENT_HOST_NOTIFY) + pm_runtime_put(&client->adapter->dev); +} + +static void i2c_device_shutdown(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client || !dev->driver) + return; + 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) +{ + kfree(to_i2c_client(dev)); +} + +static ssize_t +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_RO(name); + +static ssize_t +modalias_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int len; + + len = of_device_modalias(dev, buf, PAGE_SIZE); + if (len != -ENODEV) + return len; + + 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_RO(modalias); + +static struct attribute *i2c_dev_attrs[] = { + &dev_attr_name.attr, + /* modalias helps coldplug: modprobe $(cat .../modalias) */ + &dev_attr_modalias.attr, + NULL +}; +ATTRIBUTE_GROUPS(i2c_dev); + +const struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, + .probe = i2c_device_probe, + .remove = i2c_device_remove, + .shutdown = i2c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i2c_bus_type); + +const struct device_type i2c_client_type = { + .groups = i2c_dev_groups, + .uevent = i2c_device_uevent, + .release = i2c_client_dev_release, +}; +EXPORT_SYMBOL_GPL(i2c_client_type); + + +/** + * i2c_verify_client - return parameter as i2c_client, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_client. + */ +struct i2c_client *i2c_verify_client(struct device *dev) +{ + return (dev->type == &i2c_client_type) + ? to_i2c_client(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_client); + + +/* Return a unique address which takes the flags of the client into account */ +static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client) +{ + unsigned short addr = client->addr; + + /* For some client flags, add an arbitrary offset to avoid collisions */ + if (client->flags & I2C_CLIENT_TEN) + addr |= I2C_ADDR_OFFSET_TEN_BIT; + + if (client->flags & I2C_CLIENT_SLAVE) + addr |= I2C_ADDR_OFFSET_SLAVE; + + return addr; +} + +/* This is a permissive address validity check, I2C address map constraints + * are purposely not enforced, except for the general call address. */ +static int i2c_check_addr_validity(unsigned int addr, unsigned short flags) +{ + if (flags & I2C_CLIENT_TEN) { + /* 10-bit address, all values are valid */ + if (addr > 0x3ff) + return -EINVAL; + } else { + /* 7-bit address, reject the general call address */ + if (addr == 0x00 || addr > 0x7f) + return -EINVAL; + } + return 0; +} + +/* And this is a strict address validity check, used when probing. If a + * device uses a reserved address, then it shouldn't be probed. 7-bit + * addressing is assumed, 10-bit address devices are rare and should be + * explicitly enumerated. */ +int i2c_check_7bit_addr_validity_strict(unsigned short addr) +{ + /* + * Reserved addresses per I2C specification: + * 0x00 General call address / START byte + * 0x01 CBUS address + * 0x02 Reserved for different bus format + * 0x03 Reserved for future purposes + * 0x04-0x07 Hs-mode master code + * 0x78-0x7b 10-bit slave addressing + * 0x7c-0x7f Reserved for future purposes + */ + if (addr < 0x08 || addr > 0x77) + return -EINVAL; + return 0; +} + +static int __i2c_check_addr_busy(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + int addr = *(int *)addrp; + + if (client && i2c_encode_flags_to_addr(client) == addr) + return -EBUSY; + return 0; +} + +/* walk up mux tree */ +static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, + __i2c_check_addr_busy); + + if (!result && parent) + result = i2c_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2c_check_mux_children); + else + result = __i2c_check_addr_busy(dev, addrp); + + return result; +} + +static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2c_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2c_check_mux_children); + + return result; +} + +/** + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree + */ +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter)); +} + +/** + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT + * trylocks only this branch in the adapter tree + */ +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + return rt_mutex_trylock(&adapter->bus_lock); +} + +/** + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree + */ +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + rt_mutex_unlock(&adapter->bus_lock); +} + +static void i2c_dev_set_name(struct i2c_adapter *adap, + struct i2c_client *client, + struct i2c_board_info const *info) +{ + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + + if (info && info->dev_name) { + dev_set_name(&client->dev, "i2c-%s", info->dev_name); + return; + } + + if (adev) { + dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); + return; + } + + dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), + i2c_encode_flags_to_addr(client)); +} + +int i2c_dev_irq_from_resources(const struct resource *resources, + unsigned int num_resources) +{ + struct irq_data *irqd; + int i; + + for (i = 0; i < num_resources; i++) { + const struct resource *r = &resources[i]; + + if (resource_type(r) != IORESOURCE_IRQ) + continue; + + if (r->flags & IORESOURCE_BITS) { + irqd = irq_get_irq_data(r->start); + if (!irqd) + break; + + irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); + } + + return r->start; + } + + 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_client_device - instantiate an i2c device + * @adap: the adapter managing the device + * @info: describes one I2C device; bus_num is ignored + * Context: can sleep + * + * Create an i2c device. Binding is handled through driver model + * probe()/remove() methods. A driver may be bound to this device when we + * return from this function, or any later moment (e.g. maybe hotplugging will + * load the driver module). This call is not appropriate for use by mainboard + * initialization logic, which usually runs during an arch_initcall() long + * before any i2c_adapter could exist. + * + * This returns the new i2c client, which may be saved for later use with + * i2c_unregister_device(); or an ERR_PTR to describe the error. + */ +struct i2c_client * +i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ + struct fwnode_handle *fwnode = info->fwnode; + struct i2c_client *client; + bool need_put = false; + int status; + + client = kzalloc_obj(*client); + if (!client) + return ERR_PTR(-ENOMEM); + + client->adapter = adap; + + client->dev.platform_data = info->platform_data; + client->flags = info->flags; + client->addr = info->addr; + + client->init_irq = info->irq; + if (!client->init_irq) + client->init_irq = i2c_dev_irq_from_resources(info->resources, + info->num_resources); + + strscpy(client->name, info->type, sizeof(client->name)); + + status = i2c_check_addr_validity(client->addr, client->flags); + if (status) { + dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", + client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); + 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) + goto out_err; + + client->dev.parent = &client->adapter->dev; + client->dev.bus = &i2c_bus_type; + client->dev.type = &i2c_client_type; + + device_enable_async_suspend(&client->dev); + + device_set_node(&client->dev, fwnode_handle_get(fwnode)); + + if (info->swnode) { + status = device_add_software_node(&client->dev, info->swnode); + if (status) { + dev_err(&adap->dev, + "Failed to add software node to client %s: %d\n", + client->name, status); + goto out_err_put_fwnode; + } + } + + i2c_dev_set_name(adap, client, info); + status = device_register(&client->dev); + if (status) + 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_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: + if (need_put) + put_device(&client->dev); + else + kfree(client); + return ERR_PTR(status); +} +EXPORT_SYMBOL_GPL(i2c_new_client_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) +{ + struct fwnode_handle *fwnode; + + if (IS_ERR_OR_NULL(client)) + return; + + 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); + + 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 (IS_ERR_OR_NULL(fwnode)) + return NULL; + + dev = bus_find_device_by_fwnode(&i2c_bus_type, fwnode); + if (!dev) + return NULL; + + 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[] = { + { .name = "dummy" }, + { .name = "smbus_host_notify" }, + { } +}; + +static int dummy_probe(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver dummy_driver = { + .driver.name = "dummy", + .probe = dummy_probe, + .id_table = dummy_id, +}; + +/** + * 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 + * + * This returns an I2C client bound to the "dummy" driver, intended for use + * with devices that consume multiple addresses. Examples of such chips + * include various EEPROMS (like 24c04 and 24c08 models). + * + * These dummy devices have two main uses. First, most I2C and SMBus calls + * except i2c_transfer() need a client handle; the dummy will be that handle. + * And second, this prevents the specified address from being bound to a + * different driver. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or an ERR_PTR to describe the error. + */ +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_client_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_dummy_device); + +static void devm_i2c_release_dummy(void *client) +{ + i2c_unregister_device(client); +} + +/** + * 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 + * @default_addr: Used as a fallback if no secondary address was specified + * Context: can sleep + * + * I2C clients can be composed of multiple I2C slaves bound together in a single + * component. The I2C client driver then binds to the master I2C slave and needs + * to create I2C dummy clients to communicate with all the other slaves. + * + * This function creates and returns an I2C dummy client whose I2C address is + * retrieved from the platform firmware based on the given slave name. If no + * address is specified by the firmware default_addr is used. + * + * On DT-based platforms the address is retrieved from the "reg" property entry + * 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 an ERR_PTR to describe the error. + */ +struct i2c_client *i2c_new_ancillary_device(struct i2c_client *client, + const char *name, + u16 default_addr) +{ + struct device_node *np = client->dev.of_node; + u32 addr = default_addr; + int i; + + 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_device(client->adapter, addr); +} +EXPORT_SYMBOL_GPL(i2c_new_ancillary_device); + +/* ------------------------------------------------------------------------- */ + +/* I2C bus adapters -- one roots each I2C or SMBUS segment */ + +static void i2c_adapter_dev_release(struct device *dev) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + complete(&adap->dev_released); +} + +unsigned int i2c_adapter_depth(struct i2c_adapter *adapter) +{ + unsigned int depth = 0; + struct device *parent; + + 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"); + + return depth; +} +EXPORT_SYMBOL_GPL(i2c_adapter_depth); + +/* + * Let users instantiate I2C devices through sysfs. This can be used when + * platform initialization code doesn't contain the proper data for + * whatever reason. Also useful for drivers that do device detection and + * detection fails, either because the device uses an unexpected address, + * or this is a compatible device with different ID register values. + * + * Parameter checking may look overzealous, but we really don't want + * the user to provide incorrect parameters. + */ +static ssize_t +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; + struct i2c_client *client; + char *blank, end; + int res; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + blank = strchr(buf, ' '); + if (!blank) { + dev_err(dev, "%s: Missing parameters\n", "new_device"); + return -EINVAL; + } + if (blank - buf > I2C_NAME_SIZE - 1) { + dev_err(dev, "%s: Invalid device name\n", "new_device"); + return -EINVAL; + } + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ + res = sscanf(++blank, "%hi%c", &info.addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "new_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "new_device"); + return -EINVAL; + } + + if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) { + info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT; + info.flags |= I2C_CLIENT_TEN; + } + + if (info.addr & I2C_ADDR_OFFSET_SLAVE) { + info.addr &= ~I2C_ADDR_OFFSET_SLAVE; + info.flags |= I2C_CLIENT_SLAVE; + } + + 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); + list_add_tail(&client->detected, &adap->userspace_clients); + mutex_unlock(&adap->userspace_clients_lock); + dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", + info.type, info.addr); + + return count; +} +static DEVICE_ATTR_WO(new_device); + +/* + * And of course let the users delete the devices they instantiated, if + * they got it wrong. This interface can only be used to delete devices + * instantiated by i2c_sysfs_new_device above. This guarantees that we + * don't delete devices to which some kernel code still has references. + * + * Parameter checking may look overzealous, but we really don't want + * the user to delete the wrong device. + */ +static ssize_t +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; + unsigned short addr; + char end; + int res; + + /* Parse parameters, reject extra parameters */ + res = sscanf(buf, "%hi%c", &addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "delete_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "delete_device"); + return -EINVAL; + } + + /* Make sure the device was added through sysfs */ + res = -ENOENT; + mutex_lock_nested(&adap->userspace_clients_lock, + i2c_adapter_depth(adap)); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + if (i2c_encode_flags_to_addr(client) == addr) { + dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", + "delete_device", client->name, client->addr); + + list_del(&client->detected); + i2c_unregister_device(client); + res = count; + break; + } + } + mutex_unlock(&adap->userspace_clients_lock); + + if (res < 0) + dev_err(dev, "%s: Can't find device in list\n", + "delete_device"); + return res; +} +static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, + delete_device_store); + +static struct attribute *i2c_adapter_attrs[] = { + &dev_attr_name.attr, + &dev_attr_new_device.attr, + &dev_attr_delete_device.attr, + NULL +}; +ATTRIBUTE_GROUPS(i2c_adapter); + +const struct device_type i2c_adapter_type = { + .groups = i2c_adapter_groups, + .release = i2c_adapter_dev_release, +}; +EXPORT_SYMBOL_GPL(i2c_adapter_type); + +/** + * i2c_verify_adapter - return parameter as i2c_adapter or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_adapter. + */ +struct i2c_adapter *i2c_verify_adapter(struct device *dev) +{ + return (dev->type == &i2c_adapter_type) + ? to_i2c_adapter(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_adapter); + +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 && + 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); + } + up_read(&__i2c_board_lock); +} + +static int i2c_do_add_adapter(struct i2c_driver *driver, + struct i2c_adapter *adap) +{ + /* Detect supported devices on that bus, and instantiate them */ + i2c_detect(adap, driver); + + return 0; +} + +static int __process_new_adapter(struct device_driver *d, void *data) +{ + return i2c_do_add_adapter(to_i2c_driver(d), data); +} + +static const struct i2c_lock_operations i2c_adapter_lock_ops = { + .lock_bus = i2c_adapter_lock_bus, + .trylock_bus = i2c_adapter_trylock_bus, + .unlock_bus = i2c_adapter_unlock_bus, +}; + +static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap) +{ + struct irq_domain *domain = adap->host_notify_domain; + irq_hw_number_t hwirq; + + if (!domain) + return; + + for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++) + irq_dispose_mapping(irq_find_mapping(domain, hwirq)); + + irq_domain_remove(domain); + adap->host_notify_domain = NULL; +} + +static int i2c_host_notify_irq_map(struct irq_domain *h, + unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq); + + return 0; +} + +static const struct irq_domain_ops i2c_host_notify_irq_ops = { + .map = i2c_host_notify_irq_map, +}; + +static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap) +{ + struct irq_domain *domain; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY)) + return 0; + + domain = irq_domain_create_linear(dev_fwnode(adap->dev.parent), + I2C_ADDR_7BITS_COUNT, + &i2c_host_notify_irq_ops, adap); + if (!domain) + return -ENOMEM; + + adap->host_notify_domain = domain; + + return 0; +} + +/** + * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct + * I2C client. + * @adap: the adapter + * @addr: the I2C address of the notifying device + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the Host Notify IRQ. + */ +int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) +{ + int irq; + + 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_safe(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + +static int i2c_allocate_adapter_id(struct i2c_adapter *adap) +{ + int id, start, end; + + if (adap->nr == -1) { + start = __i2c_first_dynamic_bus_num; + end = 0; + } else { + start = adap->nr; + end = adap->nr + 1; + } + + mutex_lock(&core_lock); + id = idr_alloc(&i2c_adapter_idr, NULL, start, end, GFP_KERNEL); + mutex_unlock(&core_lock); + if (id < 0) { + if (adap->nr != -1 && id == -ENOSPC) + id = -EBUSY; + pr_err("adapter '%s': failed to allocate id: %d\n", adap->name, id); + return id; + } + + adap->nr = id; + + return 0; +} + +static int i2c_register_adapter(struct i2c_adapter *adap) +{ + int res; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) + return -EAGAIN; + + /* Sanity checks */ + if (WARN(!adap->name[0], "i2c adapter has no name")) + return -EINVAL; + + if (!adap->algo) { + pr_err("adapter '%s': no algo supplied!\n", adap->name); + return -EINVAL; + } + + 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); + INIT_LIST_HEAD(&adap->userspace_clients); + + /* Set default timeout to 1 second if not already set */ + if (adap->timeout == 0) + adap->timeout = HZ; + + /* register soft irqs for Host Notify */ + res = i2c_setup_host_notify_irq_domain(adap); + if (res) { + pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", + adap->name, res); + return res; + } + + res = i2c_allocate_adapter_id(adap); + if (res) + goto err_remove_irq_domain; + + res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); + if (res) + goto err_free_id; + + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; + device_initialize(&adap->dev); + + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto err_put_adap; + + /* + * 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); + + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, adap, adap->nr); + mutex_unlock(&core_lock); + + res = device_add(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); + goto err_replace_id; + } + + res = i2c_setup_smbus_alert(adap); + if (res) + goto err_deregister_clients; + + dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); + + /* create pre-declared device nodes */ + of_i2c_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); + + /* Notify drivers */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + + return 0; + +err_deregister_clients: + i2c_deregister_clients(adap); + device_del(&adap->dev); +err_replace_id: + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); + debugfs_remove_recursive(adap->debugfs); + pm_runtime_disable(&adap->dev); +err_put_adap: + init_completion(&adap->dev_released); + put_device(&adap->dev); + wait_for_completion(&adap->dev_released); +err_free_id: + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); +err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); + + return res; +} + +/** + * i2c_add_adapter - declare i2c adapter, use dynamic bus number + * @adapter: the adapter to add + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * doesn't matter or when its bus number is specified by an dt alias. + * Examples of bases when the bus number doesn't matter: I2C adapters + * dynamically added by USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in adap->nr, and the specified adapter became available for clients. + * Otherwise, a negative errno value is returned. + */ +int i2c_add_adapter(struct i2c_adapter *adapter) +{ + struct device *dev = &adapter->dev; + int id; + + id = of_alias_get_id(dev->of_node, "i2c"); + if (id < 0) + id = -1; + + adapter->nr = id; + + return i2c_register_adapter(adapter); +} +EXPORT_SYMBOL(i2c_add_adapter); + +/** + * i2c_add_numbered_adapter - declare i2c adapter, use static bus number + * @adap: the adapter to register (with adap->nr initialized) + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * matters. For example, use it for I2C adapters from system-on-chip CPUs, + * or otherwise built in to the system's mainboard, and where i2c_board_info + * is used to properly configure I2C devices. + * + * If the requested bus number is set to -1, then this function will behave + * identically to i2c_add_adapter, and will dynamically assign a bus number. + * + * If no devices have pre-been declared for this bus, then be sure to + * register the adapter before any dynamically allocated ones. Otherwise + * the required bus ID may not be available. + * + * When this returns zero, the specified adapter became available for + * clients using the bus number provided in adap->nr. Also, the table + * of I2C devices pre-declared using i2c_register_board_info() is scanned, + * and the appropriate driver model device nodes are created. Otherwise, a + * negative errno value is returned. + */ +int i2c_add_numbered_adapter(struct i2c_adapter *adap) +{ + if (adap->nr == -1) /* -1 means dynamically assign bus id */ + return i2c_add_adapter(adap); + + return i2c_register_adapter(adap); +} +EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); + +static void i2c_do_del_adapter(struct i2c_driver *driver, + struct i2c_adapter *adapter) +{ + struct i2c_client *client, *_n; + + /* Remove the devices we created ourselves as the result of hardware + * probing (using a driver's detect method) */ + list_for_each_entry_safe(client, _n, &driver->clients, detected) { + if (client->adapter == adapter) { + dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", + client->name, client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + } +} + +static int __unregister_client(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + if (client && strcmp(client->name, "dummy")) + i2c_unregister_device(client); + return 0; +} + +static int __unregister_dummy(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + i2c_unregister_device(client); + return 0; +} + +static int __process_removed_adapter(struct device_driver *d, void *data) +{ + i2c_do_del_adapter(to_i2c_driver(d), data); + return 0; +} + +static void i2c_deregister_clients(struct i2c_adapter *adap) +{ + struct i2c_client *client, *next; + + /* Tell drivers about this removal */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, + __process_removed_adapter); + mutex_unlock(&core_lock); + + /* Remove devices instantiated from sysfs */ + mutex_lock_nested(&adap->userspace_clients_lock, + i2c_adapter_depth(adap)); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, + client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + mutex_unlock(&adap->userspace_clients_lock); + + /* Detach any active clients. This can't fail, thus we do not + * check the returned value. This is a two-pass process, because + * we can't remove the dummy devices during the first pass: they + * could have been instantiated by real devices wishing to clean + * them up properly, so we give them a chance to do that first. */ + device_for_each_child(&adap->dev, NULL, __unregister_client); + device_for_each_child(&adap->dev, NULL, __unregister_dummy); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + if (found == adap) + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + /* + * This drains any in-flight writers, so all + * clients will be caught by i2c_deregister_clients(). + */ + device_remove_file(&adap->dev, &dev_attr_new_device); + + i2c_acpi_remove_space_handler(adap); + + i2c_deregister_clients(adap); + + /* device name is gone after device_unregister */ + dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); + + pm_runtime_disable(&adap->dev); + + 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 + * alternative which results in decoupling the lifetime of the struct + * device from the i2c_adapter, like spi or netdev do. Any solution + * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! + */ + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); + + /* free bus id */ + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); +} +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 (device_match_fwnode(dev, data)) + return 1; + + if (dev->parent && device_match_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 (IS_ERR_OR_NULL(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 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 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) +{ + bool u = use_defaults; + u32 d; + + i2c_parse_timing(dev, "clock-frequency", &t->bus_freq_hz, + I2C_MAX_STANDARD_MODE_FREQ, u); + + 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); + + 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); + + 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 *dev, void *data)) +{ + int res; + + mutex_lock(&core_lock); + res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); + mutex_unlock(&core_lock); + + return res; +} +EXPORT_SYMBOL_GPL(i2c_for_each_dev); + +static int __process_new_driver(struct device *dev, void *data) +{ + if (dev->type != &i2c_adapter_type) + return 0; + return i2c_do_add_adapter(data, to_i2c_adapter(dev)); +} + +/* + * An i2c_driver is used with one or more i2c_client (device) nodes to access + * i2c slave chips, on a bus instance associated with some i2c_adapter. + */ + +int i2c_register_driver(struct module *owner, struct i2c_driver *driver) +{ + int res; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) + return -EAGAIN; + + /* add the driver to the list of i2c drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &i2c_bus_type; + INIT_LIST_HEAD(&driver->clients); + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound devices. + */ + res = driver_register(&driver->driver); + if (res) + return res; + + pr_debug("driver [%s] registered\n", driver->driver.name); + + /* Walk the adapters that are already present */ + i2c_for_each_dev(driver, __process_new_driver); + + return 0; +} +EXPORT_SYMBOL(i2c_register_driver); + +static int __process_removed_driver(struct device *dev, void *data) +{ + if (dev->type == &i2c_adapter_type) + i2c_do_del_adapter(data, to_i2c_adapter(dev)); + return 0; +} + +/** + * i2c_del_driver - unregister I2C driver + * @driver: the driver being unregistered + * Context: can sleep + */ +void i2c_del_driver(struct i2c_driver *driver) +{ + i2c_for_each_dev(driver, __process_removed_driver); + + driver_unregister(&driver->driver); + pr_debug("driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL(i2c_del_driver); + +/* ------------------------------------------------------------------------- */ + +struct i2c_cmd_arg { + unsigned cmd; + void *arg; +}; + +static int i2c_cmd(struct device *dev, void *_arg) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_cmd_arg *arg = _arg; + struct i2c_driver *driver; + + if (!client || !client->dev.driver) + return 0; + + driver = to_i2c_driver(client->dev.driver); + if (driver->command) + driver->command(client, arg->cmd, arg->arg); + return 0; +} + +void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) +{ + struct i2c_cmd_arg cmd_arg; + + cmd_arg.cmd = cmd; + cmd_arg.arg = arg; + device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); +} +EXPORT_SYMBOL(i2c_clients_command); + +static int __init i2c_init(void) +{ + int retval; + + retval = of_alias_get_highest_id("i2c"); + + down_write(&__i2c_board_lock); + if (retval >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = retval + 1; + up_write(&__i2c_board_lock); + + retval = bus_register(&i2c_bus_type); + if (retval) + return retval; + + is_registered = true; + + i2c_debugfs_root = debugfs_create_dir("i2c", NULL); + + retval = i2c_add_driver(&dummy_driver); + if (retval) + goto class_err; + + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); + + return 0; + +class_err: + is_registered = false; + bus_unregister(&i2c_bus_type); + return retval; +} + +static void __exit i2c_exit(void) +{ + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier)); + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier)); + i2c_del_driver(&dummy_driver); + debugfs_remove_recursive(i2c_debugfs_root); + bus_unregister(&i2c_bus_type); + tracepoint_synchronize_unregister(); +} + +/* We must initialize early, because some subsystems register i2c drivers + * in subsys_initcall() code, but are linked (and initialized) before i2c. + */ +postcore_initcall(i2c_init); +module_exit(i2c_exit); + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +/* Check if val is exceeding the quirk IFF quirk is non 0 */ +#define i2c_quirk_exceeded(val, quirk) ((quirk) && ((val) > (quirk))) + +static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *err_msg) +{ + dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n", + err_msg, msg->addr, msg->len, + str_read_write(msg->flags & I2C_M_RD)); + return -EOPNOTSUPP; +} + +static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + const struct i2c_adapter_quirks *q = adap->quirks; + int max_num = q->max_num_msgs, i; + bool do_len_check = true; + + if (q->flags & I2C_AQ_COMB) { + max_num = 2; + + /* special checks for combined messages */ + if (num == 2) { + if (q->flags & I2C_AQ_COMB_WRITE_FIRST && msgs[0].flags & I2C_M_RD) + return i2c_quirk_error(adap, &msgs[0], "1st comb msg must be write"); + + if (q->flags & I2C_AQ_COMB_READ_SECOND && !(msgs[1].flags & I2C_M_RD)) + return i2c_quirk_error(adap, &msgs[1], "2nd comb msg must be read"); + + if (q->flags & I2C_AQ_COMB_SAME_ADDR && msgs[0].addr != msgs[1].addr) + return i2c_quirk_error(adap, &msgs[0], "comb msg only to same addr"); + + if (i2c_quirk_exceeded(msgs[0].len, q->max_comb_1st_msg_len)) + return i2c_quirk_error(adap, &msgs[0], "msg too long"); + + if (i2c_quirk_exceeded(msgs[1].len, q->max_comb_2nd_msg_len)) + return i2c_quirk_error(adap, &msgs[1], "msg too long"); + + do_len_check = false; + } + } + + if (i2c_quirk_exceeded(num, max_num)) + return i2c_quirk_error(adap, &msgs[0], "too many messages"); + + for (i = 0; i < num; i++) { + u16 len = msgs[i].len; + + if (msgs[i].flags & I2C_M_RD) { + if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + + if (q->flags & I2C_AQ_NO_ZERO_LEN_READ && len == 0) + return i2c_quirk_error(adap, &msgs[i], "no zero length"); + } else { + if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + + if (q->flags & I2C_AQ_NO_ZERO_LEN_WRITE && len == 0) + return i2c_quirk_error(adap, &msgs[i], "no zero length"); + } + } + + return 0; +} + +/** + * __i2c_transfer - unlocked flavor of i2c_transfer + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * 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; + + /* + * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets + * enabled. This is an efficient way of keeping the for-loop from + * being executed when not needed. + */ + if (static_branch_unlikely(&i2c_trace_msg_key)) { + int i; + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + trace_i2c_read(adap, &msgs[i], i); + else + trace_i2c_write(adap, &msgs[i], i); + } + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + 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)) + break; + } + + if (static_branch_unlikely(&i2c_trace_msg_key)) { + int i; + for (i = 0; i < ret; i++) + if (msgs[i].flags & I2C_M_RD) + trace_i2c_reply(adap, &msgs[i], i); + trace_i2c_result(adap, num, ret); + } + + return ret; +} +EXPORT_SYMBOL(__i2c_transfer); + +/** + * i2c_transfer - execute a single or combined I2C message + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Note that there is no requirement that each message be sent to + * the same slave address, although that is the most common model. + */ +int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + int ret; + + /* REVISIT the fault reporting model here is weak: + * + * - When we get an error after receiving N bytes from a slave, + * there is no way to report "N". + * + * - When we get a NAK after transmitting N bytes to a slave, + * there is no way to report "N" ... or to let the master + * continue executing the rest of this combined message, if + * that's the appropriate response. + * + * - When for example "num" is two and we successfully complete + * the first message but get an error part way through the + * second, it's unclear whether that should be reported as + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ + ret = __i2c_lock_bus_helper(adap); + if (ret) + return ret; + + ret = __i2c_transfer(adap, msgs, num); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + return ret; +} +EXPORT_SYMBOL(i2c_transfer); + +/** + * i2c_transfer_buffer_flags - issue a single I2C message transferring data + * to/from a buffer + * @client: Handle to slave device + * @buf: Where the data is stored + * @count: How many bytes to transfer, must be less than 64k since msg.len is u16 + * @flags: The flags to be used for the message, e.g. I2C_M_RD for reads + * + * Returns negative errno, or else the number of bytes transferred. + */ +int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, + int count, u16 flags) +{ + int ret; + struct i2c_msg msg = { + .addr = client->addr, + .flags = flags | (client->flags & I2C_M_TEN), + .len = count, + .buf = buf, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + + /* + * If everything went ok (i.e. 1 msg transferred), return #bytes + * transferred, else error code. + */ + return (ret == 1) ? count : ret; +} +EXPORT_SYMBOL(i2c_transfer_buffer_flags); + +/** + * i2c_get_device_id - get manufacturer, part id and die revision of a device + * @client: The device to query + * @id: The queried information + * + * Returns negative errno on error, zero on success. + */ +int i2c_get_device_id(const struct i2c_client *client, + struct i2c_device_identity *id) +{ + struct i2c_adapter *adap = client->adapter; + union i2c_smbus_data raw_id; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + raw_id.block[0] = 3; + ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0, + I2C_SMBUS_READ, client->addr << 1, + I2C_SMBUS_I2C_BLOCK_DATA, &raw_id); + if (ret) + return ret; + + id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4); + id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3); + id->die_revision = raw_id.block[3] & 0x7; + return 0; +} +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! + * ---------------------------------------------------- + */ + +/* + * Legacy default probe function, mostly relevant for SMBus. The default + * probe method is a quick write, but it is known to corrupt the 24RF08 + * EEPROMs due to a state machine bug, and could also irreversibly + * write-protect some EEPROMs, so for address ranges 0x30-0x37 and 0x50-0x5f, + * we use a short byte read instead. Also, some bus drivers don't implement + * quick write, so we fallback to a byte read in that case too. + * On x86, there is another special case for FSC hardware monitoring chips, + * which want regular byte reads (address 0x73.) Fortunately, these are the + * only known chips using this I2C address on PC hardware. + * Returns 1 if probe succeeded, 0 if not. + */ +static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) +{ + int err; + union i2c_smbus_data dummy; + +#ifdef CONFIG_X86 + if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &dummy); + else +#endif + if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL); + else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &dummy); + else { + dev_warn(&adap->dev, "No suitable probing method supported for address 0x%02X\n", + addr); + err = -EOPNOTSUPP; + } + + return err >= 0; +} + +static int i2c_detect_address(struct i2c_client *temp_client, + struct i2c_driver *driver) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter = temp_client->adapter; + int addr = temp_client->addr; + int err; + + /* Make sure the address is valid */ + err = i2c_check_7bit_addr_validity_strict(addr); + if (err) { + dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", + addr); + return err; + } + + /* Skip if already in use (7 bit, no need to encode flags) */ + if (i2c_check_addr_busy(adapter, addr)) + return 0; + + /* Make sure there is something at this address */ + if (!i2c_default_probe(adapter, addr)) + return 0; + + /* Finally call the custom detection function */ + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr; + err = driver->detect(temp_client, &info); + if (err) { + /* -ENODEV is returned if the detection fails. We catch it + here as this isn't an error. */ + return err == -ENODEV ? 0 : err; + } + + /* Consistency check */ + if (info.type[0] == '\0') { + dev_err(&adapter->dev, + "%s detection function provided no name for 0x%x\n", + driver->driver.name, addr); + } else { + struct i2c_client *client; + + /* Detection succeeded, instantiate the device */ + if (adapter->class & I2C_CLASS_DEPRECATED) + 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.rst' for details.\n", + info.addr); + + dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", + info.type, info.addr); + 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", + info.type, info.addr); + } + return 0; +} + +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; + + address_list = driver->address_list; + if (!driver->detect || !address_list) + return 0; + + /* Warn that the adapter lost class based instantiation */ + 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.rst' for alternatives.\n", + driver->driver.name); + return 0; + } + + /* Stop here if the classes do not match */ + if (!(adapter->class & driver->class)) + return 0; + + /* Set up a temporary client to help detect callback */ + temp_client = kzalloc_obj(*temp_client); + 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", + i2c_adapter_id(adapter), address_list[i]); + temp_client->addr = address_list[i]; + err = i2c_detect_address(temp_client, driver); + if (unlikely(err)) + break; + } + + kfree(temp_client); + + return err; +} + +int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) +{ + return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_QUICK, NULL) >= 0; +} +EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); + +struct i2c_client * +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; + + if (!probe) + probe = i2c_default_probe; + + for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { + /* Check address validity */ + if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) { + dev_warn(&adap->dev, "Invalid 7-bit address 0x%02x\n", + addr_list[i]); + continue; + } + + /* Check address availability (7 bit, no need to encode flags) */ + if (i2c_check_addr_busy(adap, addr_list[i])) { + dev_dbg(&adap->dev, + "Address 0x%02x already in use, not probing\n", + addr_list[i]); + continue; + } + + /* Test address responsiveness */ + if (probe(adap, addr_list[i])) + break; + } + + if (addr_list[i] == I2C_CLIENT_END) { + dev_dbg(&adap->dev, "Probing failed, no device found\n"); + return ERR_PTR(-ENODEV); + } + + info->addr = addr_list[i]; + return i2c_new_client_device(adap, info); +} +EXPORT_SYMBOL_GPL(i2c_new_scanned_device); + +struct i2c_adapter *i2c_get_adapter(int nr) +{ + struct i2c_adapter *adapter; + + mutex_lock(&core_lock); + adapter = idr_find(&i2c_adapter_idr, nr); + if (!adapter) + goto exit; + + if (try_module_get(adapter->owner)) + get_device(&adapter->dev); + else + adapter = NULL; + + exit: + mutex_unlock(&core_lock); + return adapter; +} +EXPORT_SYMBOL(i2c_get_adapter); + +void i2c_put_adapter(struct i2c_adapter *adap) +{ + if (!adap) + return; + + 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. + * 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 + * calling i2c_put_dma_safe_msg_buf(). + * + * This function must only be called from process context! + */ +u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int 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) + return msg->buf; + + pr_debug("using bounce buffer for addr=0x%02x, len=%d\n", + msg->addr, msg->len); + + if (msg->flags & I2C_M_RD) + return kzalloc(msg->len, GFP_KERNEL); + else + return kmemdup(msg->buf, msg->len, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(i2c_get_dma_safe_msg_buf); + +/** + * i2c_put_dma_safe_msg_buf - release DMA safe buffer and sync with i2c_msg + * @buf: the buffer obtained from i2c_get_dma_safe_msg_buf(). May be NULL. + * @msg: the message which the buffer corresponds to + * @xferred: bool saying if the message was transferred + */ +void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred) +{ + if (!buf || buf == msg->buf) + return; + + if (xferred && msg->flags & I2C_M_RD) + memcpy(msg->buf, buf, msg->len); + + kfree(buf); +} +EXPORT_SYMBOL_GPL(i2c_put_dma_safe_msg_buf); + +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c new file mode 100644 index 000000000..6a82b0380 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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_obj(*ocs); + 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 new file mode 100644 index 000000000..30b48a428 --- /dev/null +++ b/drivers/i2c/i2c-core-of.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core OF support code + * + * Copyright (C) 2008 Jochen Friedrich + * based on a previous patch from Jon Smirl + * + * Copyright (C) 2013, 2018 Wolfram Sang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +int of_i2c_get_board_info(struct device *dev, struct device_node *node, + struct i2c_board_info *info) +{ + u32 addr; + int ret; + + memset(info, 0, sizeof(*info)); + + 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; + } + + ret = of_property_read_u32(node, "reg", &addr); + if (ret) { + dev_err(dev, "of_i2c: invalid reg on %pOF\n", node); + return ret; + } + + if (addr & I2C_TEN_BIT_ADDRESS) { + addr &= ~I2C_TEN_BIT_ADDRESS; + info->flags |= I2C_CLIENT_TEN; + } + + if (addr & I2C_OWN_SLAVE_ADDRESS) { + addr &= ~I2C_OWN_SLAVE_ADDRESS; + info->flags |= I2C_CLIENT_SLAVE; + } + + info->addr = addr; + info->fwnode = of_fwnode_handle(node); + + if (of_property_read_bool(node, "host-notify")) + info->flags |= I2C_CLIENT_HOST_NOTIFY; + + if (of_property_read_bool(node, "wakeup-source")) + info->flags |= I2C_CLIENT_WAKE; + + return 0; +} +EXPORT_SYMBOL_GPL(of_i2c_get_board_info); + +static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, + struct device_node *node) +{ + struct i2c_client *client; + struct i2c_board_info info; + int ret; + + dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); + + ret = of_i2c_get_board_info(&adap->dev, node, &info); + if (ret) + return ERR_PTR(ret); + + client = i2c_new_client_device(adap, &info); + if (IS_ERR(client)) + dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node); + + return client; +} + +void of_i2c_register_devices(struct i2c_adapter *adap) +{ + struct device_node *bus, *node; + struct i2c_client *client; + + /* Only register child devices if the adapter has a node pointer set */ + if (!adap->dev.of_node) + return; + + dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); + + bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); + if (!bus) + bus = of_node_get(adap->dev.of_node); + + for_each_available_child_of_node(bus, node) { + if (of_node_test_and_set_flag(node, OF_POPULATED)) + continue; + + client = of_i2c_register_device(adap, node); + if (IS_ERR(client)) { + dev_err(&adap->dev, + "Failed to create I2C device for %pOF\n", + node); + of_node_clear_flag(node, OF_POPULATED); + } + } + + of_node_put(bus); +} + +static const struct of_device_id* +i2c_of_match_device_sysfs(const struct of_device_id *matches, + struct i2c_client *client) +{ + const char *name; + + for (; matches->compatible[0]; matches++) { + /* + * Adding devices through the i2c sysfs interface provides us + * a string to match which may be compatible with the device + * tree compatible strings, however with no actual of_node the + * of_match_device() will not match + */ + if (sysfs_streq(client->name, matches->compatible)) + return matches; + + name = strchr(matches->compatible, ','); + if (!name) + name = matches->compatible; + else + name++; + + if (sysfs_streq(client->name, name)) + return matches; + } + + return NULL; +} + +const struct of_device_id +*i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client) +{ + const struct of_device_id *match; + + if (!(client && matches)) + return NULL; + + match = of_match_device(matches, &client->dev); + if (match) + return match; + + return i2c_of_match_device_sysfs(matches, client); +} + +#if IS_ENABLED(CONFIG_OF_DYNAMIC) +static int of_i2c_notify(struct notifier_block *nb, unsigned long action, + void *arg) +{ + struct of_reconfig_data *rd = arg; + struct i2c_adapter *adap; + struct i2c_client *client; + + switch (of_reconfig_get_state_change(action, rd)) { + case OF_RECONFIG_CHANGE_ADD: + adap = of_find_i2c_adapter_by_node(rd->dn->parent); + if (adap == NULL) + return NOTIFY_OK; /* not for us */ + + if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) { + put_device(&adap->dev); + return NOTIFY_OK; + } + + client = of_i2c_register_device(adap, rd->dn); + 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? */ + if (!of_node_check_flag(rd->dn, OF_POPULATED)) + return NOTIFY_OK; + + /* find our device by node */ + client = of_find_i2c_device_by_node(rd->dn); + if (client == NULL) + return NOTIFY_OK; /* no? not meant for us */ + + /* unregister takes one ref away */ + i2c_unregister_device(client); + + /* and put the reference of the find */ + put_device(&client->dev); + break; + } + + return NOTIFY_OK; +} + +struct notifier_block i2c_of_notifier = { + .notifier_call = of_i2c_notify, +}; +#endif /* CONFIG_OF_DYNAMIC */ diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c new file mode 100644 index 000000000..bebb7ba67 --- /dev/null +++ b/drivers/i2c/i2c-core-slave.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core slave support code + * + * Copyright (C) 2014 by Wolfram Sang + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include + +int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) +{ + int ret; + + 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", + __func__); + + if (!(client->flags & I2C_CLIENT_TEN)) { + /* Enforce stricter address checking */ + ret = i2c_check_7bit_addr_validity_strict(client->addr); + if (ret) { + dev_err(&client->dev, "%s: invalid address\n", __func__); + return ret; + } + } + + if (!client->adapter->algo->reg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); + return -EOPNOTSUPP; + } + + client->slave_cb = slave_cb; + + i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + ret = client->adapter->algo->reg_slave(client); + i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + + if (ret) { + client->slave_cb = NULL; + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_register); + +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; + } + + i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + ret = client->adapter->algo->unreg_slave(client); + i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + + if (ret == 0) + client->slave_cb = NULL; + else + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + + return ret; +} +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_call__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 + * + * This checks the device nodes for an I2C slave by checking the address + * used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS + * flag this means the device is configured to act as a I2C slave and it will + * be listening at that address. + * + * Returns true if an I2C own slave address is detected, otherwise returns + * false. + */ +bool i2c_detect_slave_mode(struct device *dev) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (is_of_node(fwnode)) { + u32 reg; + + fwnode_for_each_child_node_scoped(fwnode, child) { + fwnode_property_read_u32(child, "reg", ®); + if (reg & I2C_OWN_SLAVE_ADDRESS) + return true; + } + } else if (is_acpi_device_node(fwnode)) { + dev_dbg(dev, "ACPI slave is not supported yet\n"); + } + return false; +} +EXPORT_SYMBOL_GPL(i2c_detect_slave_mode); diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c new file mode 100644 index 000000000..fa63bee0b --- /dev/null +++ b/drivers/i2c/i2c-core-smbus.c @@ -0,0 +1,740 @@ +// 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 separate i2c-smbus module. + * + * All SMBus-related things are written by Frodo Looijaard + * SMBus 2.0 support by Mark Studebaker and + * Jean Delvare + */ +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include + + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/** + * 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; + + for (i = 0; i < count; i++) + 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) +{ + /* The address will be sent first */ + u8 addr = i2c_8bit_addr_from_msg(msg); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +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); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/** + * i2c_smbus_read_block_data - SMBus "block read" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most 32 bytes. + * + * This executes the SMBus "block read" protocol, returning negative errno + * else the number of data bytes in the slave's response. + * + * Note that using this function requires that the client's adapter support + * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers + * support this; its emulation through I2C messaging relies on a specific + * mechanism (I2C_M_RECV_LEN) which may not be implemented. + */ +s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 *values) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BLOCK_DATA, &data); + if (status) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_block_data); + +/** + * i2c_smbus_write_block_data - SMBus "block write" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most 32 bytes + * @values: Byte array which will be written. + * + * This executes the SMBus "block write" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val) +{ + bool is_read = msg->flags & I2C_M_RD; + unsigned char *dma_buf; + + dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL); + if (!dma_buf) + return; + + msg->buf = dma_buf; + msg->flags |= I2C_M_DMA_SAFE; + + if (init_val) + msg->buf[0] = init_val; +} + +/* + * Simulate a SMBus command using the I2C protocol. + * No checking of parameters is done! + */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* + * So we need to generate a series of msgs. In the case of writing, we + * need to use only one message; when reading, we need two. We + * initialize most things with sane defaults, to keep the code below + * somewhat simpler. + */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1; + u8 partial_pec = 0; + int status; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = flags, + .len = 1, + .buf = msgbuf0, + }, { + .addr = addr, + .flags = flags | I2C_M_RD, + .len = 0, + .buf = msgbuf1, + }, + }; + bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + + msgbuf0[0] = command; + msgbuf1[0] = 0; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + 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; + nmsgs = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_PROC_CALL: + nmsgs = 2; /* Special case */ + read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + i2c_smbus_try_get_dmabuf(&msg[0], command); + memcpy(msg[0].buf + 1, data->block, msg[0].len - 1); + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + nmsgs = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + msg[0].len = data->block[0] + 2; + i2c_smbus_try_get_dmabuf(&msg[0], command); + 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 + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, "Invalid block %s size %d\n", + str_read_write(read_write == I2C_SMBUS_READ), + data->block[0]); + return -EINVAL; + } + + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 1; + + i2c_smbus_try_get_dmabuf(&msg[0], command); + memcpy(msg[0].buf + 1, data->block + 1, data->block[0]); + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (wants_pec) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + 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[nmsgs - 1].flags & I2C_M_RD) + msg[nmsgs - 1].len++; + } + + status = __i2c_transfer(adapter, msg, nmsgs); + if (status < 0) + goto cleanup; + if (status != nmsgs) { + status = -EIO; + goto cleanup; + } + status = 0; + + /* Check PEC if last message is a read */ + 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; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(data->block + 1, msg[1].buf, data->block[0]); + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + 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; + } + +cleanup: + if (msg[0].flags & I2C_M_DMA_SAFE) + kfree(msg[0].buf); + if (msg[1].flags & I2C_M_DMA_SAFE) + kfree(msg[1].buf); + + return status; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +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) +{ + s32 res; + + 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); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); + +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; + + /* Reject invalid caller-supplied block lengths before any + * tracepoint or native smbus_xfer callback runs. + */ + if (data && + (protocol == I2C_SMBUS_I2C_BLOCK_DATA || + protocol == I2C_SMBUS_BLOCK_PROC_CALL || + (protocol == I2C_SMBUS_BLOCK_DATA && + read_write == I2C_SMBUS_WRITE)) && + (data->block[0] == 0 || + data->block[0] > I2C_SMBUS_BLOCK_MAX)) + return -EINVAL; + + /* If enabled, the following two tracepoints are conditional on + * read_write and protocol. + */ + trace_smbus_write(adapter, addr, flags, read_write, + command, protocol, data); + trace_smbus_read(adapter, addr, flags, read_write, + command, protocol); + + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; + + 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 = xfer_func(adapter, addr, flags, read_write, + command, protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } + + if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) + goto trace; + /* + * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't + * implement native support for the SMBus operation. + */ + } + + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + +trace: + /* If enabled, the reply tracepoint is conditional on read_write. */ + trace_smbus_reply(adapter, addr, flags, read_write, + command, protocol, data, res); + trace_smbus_result(adapter, addr, flags, read_write, + command, protocol, res); + + return res; +} +EXPORT_SYMBOL(__i2c_smbus_xfer); + +/** + * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most + * I2C_SMBUS_BLOCK_MAX bytes. + * + * This executes the SMBus "block read" protocol if supported by the adapter. + * If block read is not supported, it emulates it using either word or byte + * read protocols depending on availability. + * + * The addresses of the I2C slave device that are accessed with this function + * must be mapped to a linear region, so that a block read will have the same + * effect as a byte read. Before using this function you must double-check + * if the I2C slave does support exchanging a block transfer with a byte + * transfer. + */ +s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + u8 i = 0; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return i2c_smbus_read_i2c_block_data(client, command, length, values); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EOPNOTSUPP; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { + while ((i + 2) <= length) { + status = i2c_smbus_read_word_data(client, command + i); + if (status < 0) + return status; + values[i] = status & 0xff; + values[i + 1] = status >> 8; + i += 2; + } + } + + while (i < length) { + status = i2c_smbus_read_byte_data(client, command + i); + if (status < 0) + return status; + values[i] = status; + i++; + } + + return i; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); + +/** + * 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 + * + * Setup handling of the SMBus alert protocol on a given I2C bus segment. + * + * Handling can be done either through our IRQ handler, or by the + * adapter (from its handler, periodic polling, or whatever). + * + * This returns the ara client, which should be saved for later use with + * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an + * ERRPTR to indicate an error. + */ +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_client_device(adapter, &ara_board_info); +} +EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device); + +#if IS_ENABLED(CONFIG_I2C_SMBUS) +int i2c_setup_smbus_alert(struct i2c_adapter *adapter) +{ + struct device *parent = adapter->dev.parent; + int irq; + + /* Adapter instantiated without parent, skip the SMBus alert setup */ + if (!parent) + return 0; + + /* Report serious errors */ + irq = device_property_match_string(parent, "interrupt-names", "smbus_alert"); + if (irq < 0 && irq != -EINVAL && irq != -ENODATA) + return irq; + + /* Skip setup when no irq was found */ + if (irq < 0 && !device_property_present(parent, "smbalert-gpios")) + return 0; + + return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL)); +} +#endif diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h new file mode 100644 index 000000000..c519da536 --- /dev/null +++ b/drivers/i2c/i2c-core.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * i2c-core.h - interfaces internal to the I2C framework + */ + +#include +#include +#include + +struct i2c_devinfo { + struct list_head list; + int busnum; + struct i2c_board_info board_info; +}; + +/* board_lock protects board_list and first_dynamic_bus_num. + * only i2c core components are allowed to use these symbols. + */ +extern struct rw_semaphore __i2c_board_lock; +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 +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 int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable) +{ + return 0; +} +#endif /* CONFIG_ACPI */ +extern struct notifier_block i2c_acpi_notifier; + +#ifdef CONFIG_ACPI_I2C_OPREGION +int i2c_acpi_install_space_handler(struct i2c_adapter *adapter); +void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter); +#else /* CONFIG_ACPI_I2C_OPREGION */ +static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { return 0; } +static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { } +#endif /* CONFIG_ACPI_I2C_OPREGION */ + +#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 new file mode 100644 index 000000000..ccaac5e29 --- /dev/null +++ b/drivers/i2c/i2c-dev.c @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + Copyright (C) 2003 Greg Kroah-Hartman + +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* The I2C_RDWR ioctl code is written by Kolja Waschk */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a + * slave (i2c_client) with which messages will be exchanged. It's coupled + * with a character special file which is accessed by user mode drivers. + * + * The list of i2c_dev structures is parallel to the i2c_adapter lists + * maintained by the driver model, and is updated using bus notifications. + */ +struct i2c_dev { + struct list_head list; + struct i2c_adapter *adap; + struct device dev; + struct cdev cdev; +}; + +#define I2C_MINORS (MINORMASK + 1) +static LIST_HEAD(i2c_dev_list); +static DEFINE_SPINLOCK(i2c_dev_list_lock); + +static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) +{ + struct i2c_dev *i2c_dev; + + spin_lock(&i2c_dev_list_lock); + list_for_each_entry(i2c_dev, &i2c_dev_list, list) { + if (i2c_dev->adap->nr == index) + goto found; + } + i2c_dev = NULL; +found: + spin_unlock(&i2c_dev_list_lock); + return i2c_dev; +} + +static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) +{ + struct i2c_dev *i2c_dev; + + if (adap->nr >= I2C_MINORS) { + pr_err("Out of device minors (%d)\n", adap->nr); + return ERR_PTR(-ENODEV); + } + + i2c_dev = kzalloc_obj(*i2c_dev); + if (!i2c_dev) + return ERR_PTR(-ENOMEM); + i2c_dev->adap = adap; + + spin_lock(&i2c_dev_list_lock); + list_add_tail(&i2c_dev->list, &i2c_dev_list); + spin_unlock(&i2c_dev_list_lock); + return 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); + 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, + struct device_attribute *attr, char *buf) +{ + struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); + + if (!i2c_dev) + return -ENODEV; + return sysfs_emit(buf, "%s\n", i2c_dev->adap->name); +} +static DEVICE_ATTR_RO(name); + +static struct attribute *i2c_attrs[] = { + &dev_attr_name.attr, + NULL, +}; +ATTRIBUTE_GROUPS(i2c); + +/* ------------------------------------------------------------------------- */ + +/* + * After opening an instance of this character special file, a file + * descriptor starts out associated only with an i2c_adapter (and bus). + * + * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg + * traffic to any devices on the bus used by that adapter. That's because + * the i2c_msg vectors embed all the addressing information they need, and + * are submitted directly to an i2c_adapter. However, SMBus-only adapters + * don't support that interface. + * + * To use read()/write() system calls on that file descriptor, or to use + * SMBus interfaces (and work with SMBus-only hosts!), you must first issue + * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous + * (never registered) i2c_client so it holds the addressing information + * needed by those system calls and by this SMBus interface. + */ + +static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret; + + 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 = kzalloc(count, GFP_KERNEL); + if (tmp == NULL) + return -ENOMEM; + + pr_debug("i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count); + + ret = i2c_master_recv(client, tmp, count); + if (ret >= 0) + if (copy_to_user(buf, tmp, ret)) + ret = -EFAULT; + kfree(tmp); + return ret; +} + +static ssize_t i2cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + 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; + + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + pr_debug("i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count); + + ret = i2c_master_send(client, tmp, count); + kfree(tmp); + return ret; +} + +static int i2cdev_check(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + + if (!client || client->addr != *(unsigned int *)addrp) + return 0; + + return dev->driver ? -EBUSY : 0; +} + +/* walk up mux tree */ +static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); + if (!result && parent) + result = i2cdev_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2cdev_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2cdev_check_mux_children); + else + result = i2cdev_check(dev, addrp); + + return result; +} + +/* This address checking function differs from the one in i2c-core + in that it considers an address with a registered device, but no + driver bound to it, as NOT busy. */ +static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2cdev_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2cdev_check_mux_children); + + return result; +} + +static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, + unsigned nmsgs, struct i2c_msg *msgs) +{ + 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) + return -ENOMEM; + + res = 0; + for (i = 0; i < nmsgs; i++) { + /* Limit the size of the message to a sane amount */ + if (msgs[i].len > 8192) { + res = -EINVAL; + break; + } + + data_ptrs[i] = (u8 __user *)msgs[i].buf; + msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); + if (IS_ERR(msgs[i].buf)) { + res = PTR_ERR(msgs[i].buf); + break; + } + /* memdup_user allocates with GFP_KERNEL, so DMA is ok */ + msgs[i].flags |= I2C_M_DMA_SAFE; + + /* + * If the message length is received from the slave (similar + * to SMBus block read), we must ensure that the buffer will + * be large enough to cope with a message length of + * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus + * drivers allow. The first byte in the buffer must be + * pre-filled with the number of extra bytes, which must be + * at least one to hold the message length, but can be + * greater (for example to account for a checksum byte at + * the end of the message.) + */ + if (msgs[i].flags & I2C_M_RECV_LEN) { + if (!(msgs[i].flags & I2C_M_RD) || + msgs[i].len < 1 || msgs[i].buf[0] < 1 || + msgs[i].len < msgs[i].buf[0] + + I2C_SMBUS_BLOCK_MAX) { + i++; + res = -EINVAL; + break; + } + + msgs[i].len = msgs[i].buf[0]; + } + } + if (res < 0) { + int j; + for (j = 0; j < i; ++j) + kfree(msgs[j].buf); + kfree(data_ptrs); + return res; + } + + res = i2c_transfer(client->adapter, msgs, nmsgs); + while (i-- > 0) { + if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { + if (copy_to_user(data_ptrs[i], msgs[i].buf, + msgs[i].len)) + res = -EFAULT; + } + kfree(msgs[i].buf); + } + kfree(data_ptrs); + return res; +} + +static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, + u8 read_write, u8 command, u32 size, + union i2c_smbus_data __user *data) +{ + union i2c_smbus_data temp = {}; + int datasize, res; + + if ((size != I2C_SMBUS_BYTE) && + (size != I2C_SMBUS_QUICK) && + (size != I2C_SMBUS_BYTE_DATA) && + (size != I2C_SMBUS_WORD_DATA) && + (size != I2C_SMBUS_PROC_CALL) && + (size != I2C_SMBUS_BLOCK_DATA) && + (size != I2C_SMBUS_I2C_BLOCK_BROKEN) && + (size != I2C_SMBUS_I2C_BLOCK_DATA) && + (size != I2C_SMBUS_BLOCK_PROC_CALL)) { + dev_dbg(&client->adapter->dev, + "size out of range (%x) in ioctl I2C_SMBUS.\n", + size); + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((read_write != I2C_SMBUS_READ) && + (read_write != I2C_SMBUS_WRITE)) { + dev_dbg(&client->adapter->dev, + "read_write out of range (%x) in ioctl I2C_SMBUS.\n", + read_write); + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((size == I2C_SMBUS_QUICK) || + ((size == I2C_SMBUS_BYTE) && + (read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, read_write, + command, size, NULL); + + if (data == NULL) { + dev_dbg(&client->adapter->dev, + "data is NULL pointer in ioctl I2C_SMBUS.\n"); + return -EINVAL; + } + + if ((size == I2C_SMBUS_BYTE_DATA) || + (size == I2C_SMBUS_BYTE)) + datasize = sizeof(data->byte); + else if ((size == I2C_SMBUS_WORD_DATA) || + (size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data->word); + else /* size == smbus block, i2c block, or block proc. call */ + datasize = sizeof(data->block); + + if ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (size == I2C_SMBUS_I2C_BLOCK_DATA) || + (read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data, datasize)) + return -EFAULT; + } + if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) { + /* Convert old I2C block commands to the new + convention. This preserves binary compatibility. */ + size = I2C_SMBUS_I2C_BLOCK_DATA; + if (read_write == I2C_SMBUS_READ) + temp.block[0] = I2C_SMBUS_BLOCK_MAX; + } + res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + read_write, command, size, &temp); + if (!res && ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data, &temp, datasize)) + return -EFAULT; + } + return res; +} + +static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + + dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", + cmd, arg); + + switch (cmd) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) + return -EBUSY; + /* REVISIT: address could become busy later */ + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_PEC: + /* + * Setting the PEC flag here won't affect kernel drivers, + * which will be using the i2c_client node registered with + * the driver model core. Likewise, when that client has + * the PEC flag already set, the i2c-dev driver won't see + * (or use) this setting. + */ + if (arg) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (unsigned long __user *)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; + + 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_array_user(rdwr_arg.msgs, + rdwr_arg.nmsgs, sizeof(struct i2c_msg)); + if (IS_ERR(rdwr_pa)) + return PTR_ERR(rdwr_pa); + + res = i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + kfree(rdwr_pa); + return res; + } + + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data data_arg; + if (copy_from_user(&data_arg, + (struct i2c_smbus_ioctl_data __user *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data_arg.read_write, + data_arg.command, + data_arg.size, + data_arg.data); + } + case I2C_RETRIES: + if (arg > INT_MAX) + return -EINVAL; + + client->adapter->retries = arg; + break; + case I2C_TIMEOUT: + /* + * For historical reasons, user-space sets the timeout value in + * units of 10 ms. + */ + if (arg > INT_MAX / 10) + return -EINVAL; + + client->adapter->timeout = msecs_to_jiffies(arg * 10); + break; + default: + /* NOTE: returning a fault code here could cause trouble + * in buggy userspace code. Some old kernel bugs returned + * zero in this case, and userspace code might accidentally + * have depended on that bug. + */ + return -ENOTTY; + } + return 0; +} + +#ifdef CONFIG_COMPAT + +struct i2c_smbus_ioctl_data32 { + u8 read_write; + u8 command; + u32 size; + compat_caddr_t data; /* union i2c_smbus_data *data */ +}; + +struct i2c_msg32 { + u16 addr; + u16 flags; + u16 len; + compat_caddr_t buf; +}; + +struct i2c_rdwr_ioctl_data32 { + compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ + u32 nmsgs; +}; + +static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + switch (cmd) { + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (compat_ulong_t __user *)arg); + case I2C_RDWR: { + struct i2c_rdwr_ioctl_data32 rdwr_arg; + struct i2c_msg32 __user *p; + struct i2c_msg *rdwr_pa; + 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; + + rdwr_pa = kmalloc_objs(struct i2c_msg, rdwr_arg.nmsgs); + if (!rdwr_pa) + return -ENOMEM; + + p = compat_ptr(rdwr_arg.msgs); + for (i = 0; i < rdwr_arg.nmsgs; i++) { + struct i2c_msg32 umsg; + if (copy_from_user(&umsg, p + i, sizeof(umsg))) { + kfree(rdwr_pa); + return -EFAULT; + } + rdwr_pa[i] = (struct i2c_msg) { + .addr = umsg.addr, + .flags = umsg.flags, + .len = umsg.len, + .buf = (__force __u8 *)compat_ptr(umsg.buf), + }; + } + + res = i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + kfree(rdwr_pa); + return res; + } + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data32 data32; + if (copy_from_user(&data32, + (void __user *) arg, + sizeof(data32))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data32.read_write, + data32.command, + data32.size, + compat_ptr(data32.data)); + } + default: + return i2cdev_ioctl(file, cmd, arg); + } +} +#else +#define compat_i2cdev_ioctl NULL +#endif + +static int i2cdev_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct i2c_client *client; + struct i2c_adapter *adap; + + adap = i2c_get_adapter(minor); + if (!adap) + return -ENODEV; + + /* This creates an anonymous i2c_client, which may later be + * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. + * + * This client is ** NEVER REGISTERED ** with the driver model + * or I2C core code!! It just holds private copies of addressing + * information and maybe a PEC flag. + */ + client = kzalloc_obj(*client); + if (!client) { + i2c_put_adapter(adap); + return -ENOMEM; + } + snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); + + client->adapter = adap; + file->private_data = client; + + return 0; +} + +static int i2cdev_release(struct inode *inode, struct file *file) +{ + struct i2c_client *client = file->private_data; + + i2c_put_adapter(client->adapter); + kfree(client); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations i2cdev_fops = { + .owner = THIS_MODULE, + .read = i2cdev_read, + .write = i2cdev_write, + .unlocked_ioctl = i2cdev_ioctl, + .compat_ioctl = compat_i2cdev_ioctl, + .open = i2cdev_open, + .release = i2cdev_release, +}; + +/* ------------------------------------------------------------------------- */ + +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) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + int res; + + if (dev->type != &i2c_adapter_type) + return NOTIFY_DONE; + adap = to_i2c_adapter(dev); + + i2c_dev = get_free_i2c_dev(adap); + if (IS_ERR(i2c_dev)) + return NOTIFY_DONE; + + cdev_init(&i2c_dev->cdev, &i2cdev_fops); + i2c_dev->cdev.owner = THIS_MODULE; + + 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 err_put_i2c_dev; + + 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) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + + if (dev->type != &i2c_adapter_type) + 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 NOTIFY_DONE; + + put_i2c_dev(i2c_dev, true); + + pr_debug("adapter [%s] unregistered\n", adap->name); + return NOTIFY_OK; +} + +static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + return i2cdev_attach_adapter(dev); + case BUS_NOTIFY_DEL_DEVICE: + return i2cdev_detach_adapter(dev); + } + + return NOTIFY_DONE; +} + +static struct notifier_block i2cdev_notifier = { + .notifier_call = i2cdev_notifier_call, +}; + +/* ------------------------------------------------------------------------- */ + +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 + */ + +static int __init i2c_dev_init(void) +{ + int res; + + pr_info("i2c /dev entries driver\n"); + + res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); + if (res) + goto out; + + res = class_register(&i2c_dev_class); + if (res) + goto out_unreg_chrdev; + + /* Keep track of adapters which will be added or removed later */ + res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); + if (res) + goto out_unreg_class; + + /* Bind to already existing adapters right away */ + i2c_for_each_dev(NULL, i2c_dev_attach_adapter); + + return 0; + +out_unreg_class: + class_unregister(&i2c_dev_class); +out_unreg_chrdev: + unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); +out: + 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, i2c_dev_detach_adapter); + class_unregister(&i2c_dev_class); + unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); +} + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C /dev entries driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_dev_init); +module_exit(i2c_dev_exit); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c new file mode 100644 index 000000000..68a4c34b5 --- /dev/null +++ b/drivers/i2c/i2c-mux.c @@ -0,0 +1,442 @@ +/* + * Multiplexed I2C bus driver. + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * Copyright (c) 2009-2010 NSN GmbH & Co KG + * + * Simplifies access to complex multiplexed I2C bus topologies, by presenting + * each multiplexed bus segment as an additional I2C adapter. + * Supports multi-level mux'ing (mux behind a mux). + * + * Based on: + * i2c-virt.c from Kumar Gala + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. + * i2c-virtual.c from Brian Kuschak + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* multiplexer per channel data */ +struct i2c_mux_priv { + struct i2c_adapter adap; + struct i2c_algorithm algo; + struct i2c_mux_core *muxc; + u32 chan_id; +}; + +static int __i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +static int __i2c_mux_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_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Select the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +static int i2c_mux_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_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Select the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +/* Return the parent's functionality */ +static u32 i2c_mux_functionality(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + return parent->algo->functionality(parent); +} + +static void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); + if (!(flags & I2C_LOCK_ROOT_ADAPTER)) + return; + i2c_lock_bus(parent, flags); +} + +static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + if (!rt_mutex_trylock(&parent->mux_lock)) + return 0; /* mux_lock not locked, failure */ + if (!(flags & I2C_LOCK_ROOT_ADAPTER)) + return 1; /* we only want mux_lock, success */ + if (i2c_trylock_bus(parent, flags)) + return 1; /* parent locked too, success */ + rt_mutex_unlock(&parent->mux_lock); + return 0; /* parent not locked, failure */ +} + +static void i2c_mux_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + if (flags & I2C_LOCK_ROOT_ADAPTER) + i2c_unlock_bus(parent, flags); + rt_mutex_unlock(&parent->mux_lock); +} + +static void i2c_parent_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); + i2c_lock_bus(parent, flags); +} + +static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + if (!rt_mutex_trylock(&parent->mux_lock)) + return 0; /* mux_lock not locked, failure */ + if (i2c_trylock_bus(parent, flags)) + return 1; /* parent locked too, success */ + rt_mutex_unlock(&parent->mux_lock); + return 0; /* parent not locked, failure */ +} + +static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + i2c_unlock_bus(parent, flags); + rt_mutex_unlock(&parent->mux_lock); +} + +struct i2c_adapter *i2c_root_adapter(struct device *dev) +{ + struct device *i2c; + struct i2c_adapter *i2c_root; + + /* + * Walk up the device tree to find an i2c adapter, indicating + * that this is an i2c client device. Check all ancestors to + * handle mfd devices etc. + */ + for (i2c = dev; i2c; i2c = i2c->parent) { + if (i2c->type == &i2c_adapter_type) + break; + } + if (!i2c) + return NULL; + + /* Continue up the tree to find the root i2c adapter */ + i2c_root = to_i2c_adapter(i2c); + while (i2c_parent_is_i2c_adapter(i2c_root)) + i2c_root = i2c_parent_is_i2c_adapter(i2c_root); + + return i2c_root; +} +EXPORT_SYMBOL_GPL(i2c_root_adapter); + +struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + struct device *dev, int max_adapters, + int sizeof_priv, u32 flags, + int (*select)(struct i2c_mux_core *, u32), + int (*deselect)(struct i2c_mux_core *, u32)) +{ + struct i2c_mux_core *muxc; + size_t mux_size; + + 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) + muxc->priv = &muxc->adapter[max_adapters]; + + muxc->parent = parent; + muxc->dev = dev; + 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; + + return muxc; +} +EXPORT_SYMBOL_GPL(i2c_mux_alloc); + +static const struct i2c_lock_operations i2c_mux_lock_ops = { + .lock_bus = i2c_mux_lock_bus, + .trylock_bus = i2c_mux_trylock_bus, + .unlock_bus = i2c_mux_unlock_bus, +}; + +static const struct i2c_lock_operations i2c_parent_lock_ops = { + .lock_bus = i2c_parent_lock_bus, + .trylock_bus = i2c_parent_trylock_bus, + .unlock_bus = i2c_parent_unlock_bus, +}; + +int i2c_mux_add_adapter(struct i2c_mux_core *muxc, + u32 force_nr, u32 chan_id) +{ + struct i2c_adapter *parent = muxc->parent; + struct i2c_mux_priv *priv; + char symlink_name[20]; + int ret; + + if (muxc->num_adapters >= muxc->max_adapters) { + dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); + return -EINVAL; + } + + priv = kzalloc_obj(*priv); + if (!priv) + return -ENOMEM; + + /* Set up private adapter data */ + priv->muxc = muxc; + priv->chan_id = chan_id; + + /* Need to do algo dynamically because we don't know ahead + * of time what sort of physical adapter we'll be dealing with. + */ + if (parent->algo->master_xfer) { + if (muxc->mux_locked) + priv->algo.xfer = i2c_mux_master_xfer; + else + 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 */ + snprintf(priv->adap.name, sizeof(priv->adap.name), + "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id); + priv->adap.owner = THIS_MODULE; + priv->adap.algo = &priv->algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &parent->dev; + priv->adap.retries = parent->retries; + priv->adap.timeout = parent->timeout; + priv->adap.quirks = parent->quirks; + if (muxc->mux_locked) + priv->adap.lock_ops = &i2c_mux_lock_ops; + else + priv->adap.lock_ops = &i2c_parent_lock_ops; + + /* + * Try to populate the mux adapter's of_node, expands to + * nothing if !CONFIG_OF. + */ + if (muxc->dev->of_node) { + struct device_node *dev_node = muxc->dev->of_node; + struct device_node *mux_node, *child = NULL; + u32 reg; + + if (muxc->arbitrator) + mux_node = of_get_child_by_name(dev_node, "i2c-arb"); + else if (muxc->gate) + mux_node = of_get_child_by_name(dev_node, "i2c-gate"); + else + mux_node = of_get_child_by_name(dev_node, "i2c-mux"); + + if (mux_node) { + /* A "reg" property indicates an old-style DT entry */ + if (!of_property_read_u32(mux_node, "reg", ®)) { + of_node_put(mux_node); + mux_node = NULL; + } + } + + if (!mux_node) + mux_node = of_node_get(dev_node); + else if (muxc->arbitrator || muxc->gate) + child = of_node_get(mux_node); + + if (!child) { + for_each_child_of_node(mux_node, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) + break; + } + } + + priv->adap.dev.of_node = child; + of_node_put(mux_node); + } + + /* + * Associate the mux channel with an ACPI node. + */ + if (has_acpi_companion(muxc->dev)) + acpi_preset_companion(&priv->adap.dev, + ACPI_COMPANION(muxc->dev), + chan_id); + + if (force_nr) { + priv->adap.nr = force_nr; + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret < 0) { + dev_err(&parent->dev, + "failed to add mux-adapter %u as bus %u (error=%d)\n", + chan_id, force_nr, ret); + goto err_free_priv; + } + } else { + ret = i2c_add_adapter(&priv->adap); + if (ret < 0) { + dev_err(&parent->dev, + "failed to add mux-adapter %u (error=%d)\n", + chan_id, ret); + goto err_free_priv; + } + } + + WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, + "mux_device"), + "can't create symlink to mux device\n"); + + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); + WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, + symlink_name), + "can't create symlink to channel %u\n", chan_id); + dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", + i2c_adapter_id(&priv->adap)); + + muxc->adapter[muxc->num_adapters++] = &priv->adap; + return 0; + +err_free_priv: + of_node_put(priv->adap.dev.of_node); + kfree(priv); + return ret; +} +EXPORT_SYMBOL_GPL(i2c_mux_add_adapter); + +void i2c_mux_del_adapters(struct i2c_mux_core *muxc) +{ + char symlink_name[20]; + + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; + struct device_node *np = adap->dev.of_node; + + muxc->adapter[muxc->num_adapters] = NULL; + + snprintf(symlink_name, sizeof(symlink_name), + "channel-%u", priv->chan_id); + sysfs_remove_link(&muxc->dev->kobj, symlink_name); + + sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); + i2c_del_adapter(adap); + of_node_put(np); + kfree(priv); + } +} +EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c new file mode 100644 index 000000000..226d722af --- /dev/null +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C slave mode EEPROM simulator + * + * Copyright (C) 2014 by Wolfram Sang, Sang Engineering + * Copyright (C) 2014 by Renesas Electronics Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct eeprom_data { + struct bin_attribute bin; + spinlock_t buffer_lock; + 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) +{ + struct eeprom_data *eeprom = i2c_get_clientdata(client); + + switch (event) { + case I2C_SLAVE_WRITE_RECEIVED: + 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 { + 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; + case I2C_SLAVE_READ_REQUESTED: + spin_lock(&eeprom->buffer_lock); + *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 + * this byte will be actually used. Read Linux I2C slave docs + * for details. + */ + break; + + case I2C_SLAVE_STOP: + case I2C_SLAVE_WRITE_REQUESTED: + eeprom->idx_write_cnt = 0; + break; + + default: + break; + } + + return 0; +} + +static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + struct eeprom_data *eeprom; + unsigned long flags; + + eeprom = dev_get_drvdata(kobj_to_dev(kobj)); + + spin_lock_irqsave(&eeprom->buffer_lock, flags); + memcpy(buf, &eeprom->buffer[off], count); + spin_unlock_irqrestore(&eeprom->buffer_lock, flags); + + return count; +} + +static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + struct eeprom_data *eeprom; + unsigned long flags; + + eeprom = dev_get_drvdata(kobj_to_dev(kobj)); + + spin_lock_irqsave(&eeprom->buffer_lock, flags); + memcpy(&eeprom->buffer[off], buf, count); + spin_unlock_irqrestore(&eeprom->buffer_lock, flags); + + return count; +} + +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 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->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; + eeprom->bin.read = i2c_slave_eeprom_bin_read; + eeprom->bin.write = i2c_slave_eeprom_bin_write; + eeprom->bin.size = size; + + ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin); + if (ret) + return ret; + + ret = i2c_slave_register(client, i2c_slave_eeprom_slave_cb); + if (ret) { + sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin); + return ret; + } + + return 0; +}; + +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); +} + +static const struct i2c_device_id i2c_slave_eeprom_id[] = { + { .name = "slave-24c02", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, + { .name = "slave-24c02ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c32", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c32ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c64", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c64ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c512", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c512ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { } +}; +MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); + +static struct i2c_driver i2c_slave_eeprom_driver = { + .driver = { + .name = "i2c-slave-eeprom", + }, + .probe = i2c_slave_eeprom_probe, + .remove = i2c_slave_eeprom_remove, + .id_table = i2c_slave_eeprom_id, +}; +module_i2c_driver(i2c_slave_eeprom_driver); + +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("I2C slave mode EEPROM simulator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c new file mode 100644 index 000000000..4a1bf1aa5 --- /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 + * Copyright (C) 2020 by Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_dfl_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[] = { + { .name = "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 "); +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 new file mode 100644 index 000000000..069f08f68 --- /dev/null +++ b/drivers/i2c/i2c-smbus.c @@ -0,0 +1,493 @@ +// 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-2019 Jean Delvare + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct i2c_smbus_alert { + struct work_struct alert; + struct i2c_client *ara; /* Alert response address */ +}; + +struct alert_data { + unsigned short addr; + enum i2c_alert_protocol type; + unsigned int data; +}; + +/* If this is the alerting device, notify its driver */ +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; + if (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) { + /* Stop iterating after we find the device */ + driver->alert(client, data->type, data->data); + ret = -EBUSY; + } else { + dev_warn(&client->dev, "no driver alert()!\n"); + 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); + + return 0; +} + +/* + * The alert IRQ handler needs to hand work off to a task which can issue + * SMBus calls, because those sleeping calls can't be made in IRQ context. + */ +static irqreturn_t smbus_alert(int irq, void *d) +{ + struct i2c_smbus_alert *alert = d; + struct i2c_client *ara; + unsigned short prev_addr = I2C_CLIENT_END; /* Not a valid address */ + + ara = alert->ara; + + for (;;) { + s32 status; + struct alert_data data; + + /* + * Devices with pending alerts reply in address order, low + * to high, because of slave transmit arbitration. After + * responding, an SMBus device stops asserting SMBALERT#. + * + * Note that SMBus 2.0 reserves 10-bit addresses for future + * use. We neither handle them, nor try to use PEC here. + */ + status = i2c_smbus_read_byte(ara); + if (status < 0) + break; + + data.data = status & 1; + data.addr = status >> 1; + data.type = I2C_PROTOCOL_SMBUS_ALERT; + + 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 */ + 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; + } + + return IRQ_HANDLED; +} + +static void smbalert_work(struct work_struct *work) +{ + struct i2c_smbus_alert *alert; + + alert = container_of(work, struct i2c_smbus_alert, alert); + + smbus_alert(0, alert); + +} + +/* Setup SMBALERT# infrastructure */ +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), + GFP_KERNEL); + if (!alert) + return -ENOMEM; + + if (setup) { + irq = setup->irq; + } else { + 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, + irqflags, "smbus_alert", alert); + if (res) + return res; + } + + i2c_set_clientdata(ara, alert); + dev_info(&adapter->dev, "supports SMBALERT#\n"); + + return 0; +} + +/* IRQ and memory resources are managed so they are freed automatically */ +static void smbalert_remove(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + cancel_work_sync(&alert->alert); +} + +static const struct i2c_device_id smbalert_ids[] = { + { .name = "smbus_alert" }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, smbalert_ids); + +static struct i2c_driver smbalert_driver = { + .driver = { + .name = "smbus_alert", + }, + .probe = smbalert_probe, + .remove = smbalert_remove, + .id_table = smbalert_ids, +}; + +/** + * i2c_handle_smbus_alert - Handle an SMBus alert + * @ara: the ARA client on the relevant adapter + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the alert work, in turn calling the + * corresponding I2C device driver's alert function. + * + * It is assumed that ara is a valid i2c client previously returned by + * i2c_new_smbus_alert_device(). + */ +int i2c_handle_smbus_alert(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + return schedule_work(&alert->alert); +} +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_obj(struct i2c_slave_host_notify_status); + 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 "); +MODULE_DESCRIPTION("SMBus protocol extensions support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c new file mode 100644 index 000000000..04314e3ed --- /dev/null +++ b/drivers/i2c/i2c-stub.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c-stub.c - I2C/SMBus chip emulator + + Copyright (c) 2004 Mark M. Hoffman + Copyright (C) 2007-2014 Jean Delvare + +*/ + +#define pr_fmt(fmt) "i2c-stub: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHIPS 10 + +/* + * Support for I2C_FUNC_SMBUS_BLOCK_DATA is disabled by default and must + * be enabled explicitly by setting the I2C_FUNC_SMBUS_BLOCK_DATA bits + * in the 'functionality' module parameter. + */ +#define STUB_FUNC_DEFAULT \ + (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) + +#define STUB_FUNC_ALL \ + (STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA) + +static unsigned short chip_addr[MAX_CHIPS]; +module_param_array(chip_addr, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(chip_addr, + "Chip addresses (up to 10, between 0x03 and 0x77)"); + +static unsigned long functionality = STUB_FUNC_DEFAULT; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + +/* Some chips have banked register ranges */ + +static u8 bank_reg[MAX_CHIPS]; +module_param_array(bank_reg, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_reg, "Bank register"); + +static u8 bank_mask[MAX_CHIPS]; +module_param_array(bank_mask, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_mask, "Bank value mask"); + +static u8 bank_start[MAX_CHIPS]; +module_param_array(bank_start, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_start, "First banked register"); + +static u8 bank_end[MAX_CHIPS]; +module_param_array(bank_end, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_end, "Last banked register"); + +struct smbus_block_data { + struct list_head node; + u8 command; + u8 len; + u8 block[I2C_SMBUS_BLOCK_MAX]; +}; + +struct stub_chip { + u8 pointer; + u16 words[256]; /* Byte operations use the LSB as per SMBus + specification */ + struct list_head smbus_blocks; + + /* For chips with banks, extra registers are allocated dynamically */ + u8 bank_reg; + u8 bank_shift; + u8 bank_mask; + u8 bank_sel; /* Currently selected bank */ + u8 bank_start; + u8 bank_end; + u16 bank_size; + u16 *bank_words; /* Room for bank_mask * bank_size registers */ +}; + +static struct stub_chip *stub_chips; +static int stub_chips_nr; + +static struct smbus_block_data *stub_find_block(struct device *dev, + struct stub_chip *chip, + u8 command, bool create) +{ + struct smbus_block_data *b, *rb = NULL; + + list_for_each_entry(b, &chip->smbus_blocks, node) { + if (b->command == command) { + rb = b; + break; + } + } + if (rb == NULL && create) { + rb = devm_kzalloc(dev, sizeof(*rb), GFP_KERNEL); + if (rb == NULL) + return rb; + rb->command = command; + list_add(&rb->node, &chip->smbus_blocks); + } + return rb; +} + +static u16 *stub_get_wordp(struct stub_chip *chip, u8 offset) +{ + if (chip->bank_sel && + offset >= chip->bank_start && offset <= chip->bank_end) + return chip->bank_words + + (chip->bank_sel - 1) * chip->bank_size + + offset - chip->bank_start; + else + return chip->words + offset; +} + +/* Return negative errno on error. */ +static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, union i2c_smbus_data *data) +{ + s32 ret; + int i, len; + struct stub_chip *chip = NULL; + struct smbus_block_data *b; + u16 *wordp; + + /* Search for the right chip */ + for (i = 0; i < stub_chips_nr; i++) { + if (addr == chip_addr[i]) { + chip = stub_chips + i; + break; + } + } + if (!chip) + return -ENODEV; + + switch (size) { + + case I2C_SMBUS_QUICK: + dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); + ret = 0; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + chip->pointer = command; + dev_dbg(&adap->dev, + "smbus byte - addr 0x%02x, wrote 0x%02x.\n", + addr, command); + } else { + wordp = stub_get_wordp(chip, chip->pointer++); + data->byte = *wordp & 0xff; + dev_dbg(&adap->dev, + "smbus byte - addr 0x%02x, read 0x%02x.\n", + addr, data->byte); + } + + ret = 0; + break; + + case I2C_SMBUS_BYTE_DATA: + wordp = stub_get_wordp(chip, command); + if (read_write == I2C_SMBUS_WRITE) { + *wordp &= 0xff00; + *wordp |= data->byte; + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n", + addr, data->byte, command); + + /* Set the bank as needed */ + if (chip->bank_words && command == chip->bank_reg) { + chip->bank_sel = + (data->byte >> chip->bank_shift) + & chip->bank_mask; + dev_dbg(&adap->dev, + "switching to bank %u.\n", + chip->bank_sel); + } + } else { + data->byte = *wordp & 0xff; + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } + chip->pointer = command + 1; + + ret = 0; + break; + + case I2C_SMBUS_WORD_DATA: + wordp = stub_get_wordp(chip, command); + if (read_write == I2C_SMBUS_WRITE) { + *wordp = data->word; + dev_dbg(&adap->dev, + "smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n", + addr, data->word, command); + } else { + data->word = *wordp; + dev_dbg(&adap->dev, + "smbus word data - addr 0x%02x, read 0x%04x at 0x%02x.\n", + addr, data->word, command); + } + + ret = 0; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + /* + * We ignore banks here, because banked chips don't use I2C + * block transfers + */ + if (data->block[0] == 0 || + data->block[0] > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + break; + } + if (data->block[0] > 256 - command) /* Avoid overrun */ + data->block[0] = 256 - command; + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, + "i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, + "i2c block data - addr 0x%02x, read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + case I2C_SMBUS_BLOCK_DATA: + /* + * We ignore banks here, because chips typically don't use both + * banks and SMBus block transfers + */ + b = stub_find_block(&adap->dev, chip, command, false); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + break; + } + if (b == NULL) { + b = stub_find_block(&adap->dev, chip, command, + true); + if (b == NULL) { + ret = -ENOMEM; + break; + } + } + /* Largest write sets read block length */ + if (len > b->len) + b->len = len; + for (i = 0; i < len; i++) + b->block[i] = data->block[i + 1]; + /* update for byte and word commands */ + chip->words[command] = (b->block[0] << 8) | b->len; + dev_dbg(&adap->dev, + "smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + if (b == NULL) { + dev_dbg(&adap->dev, + "SMBus block read command without prior block write not supported\n"); + ret = -EOPNOTSUPP; + break; + } + len = b->len; + data->block[0] = len; + for (i = 0; i < len; i++) + data->block[i + 1] = b->block[i]; + dev_dbg(&adap->dev, + "smbus block data - addr 0x%02x, read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + default: + dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); + ret = -EOPNOTSUPP; + break; + } /* switch (size) */ + + return ret; +} + +static u32 stub_func(struct i2c_adapter *adapter) +{ + return STUB_FUNC_ALL & functionality; +} + +static const struct i2c_algorithm smbus_algorithm = { + .functionality = stub_func, + .smbus_xfer = stub_xfer, +}; + +static struct i2c_adapter stub_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo = &smbus_algorithm, + .name = "SMBus stub driver", +}; + +static int __init i2c_stub_allocate_banks(int i) +{ + struct stub_chip *chip = stub_chips + i; + + chip->bank_reg = bank_reg[i]; + chip->bank_start = bank_start[i]; + chip->bank_end = bank_end[i]; + chip->bank_size = bank_end[i] - bank_start[i] + 1; + + /* We assume that all bits in the mask are contiguous */ + chip->bank_mask = bank_mask[i]; + while (!(chip->bank_mask & 1)) { + chip->bank_shift++; + chip->bank_mask >>= 1; + } + + chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size, + sizeof(u16), + GFP_KERNEL); + if (!chip->bank_words) + return -ENOMEM; + + pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n", + chip->bank_mask, chip->bank_size, chip->bank_start, + chip->bank_end); + + return 0; +} + +static void i2c_stub_free(void) +{ + int i; + + for (i = 0; i < stub_chips_nr; i++) + kfree(stub_chips[i].bank_words); + kfree(stub_chips); +} + +static int __init i2c_stub_init(void) +{ + int i, ret; + + if (!chip_addr[0]) { + pr_err("Please specify a chip address\n"); + return -ENODEV; + } + + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { + pr_err("Invalid chip address 0x%02x\n", + chip_addr[i]); + return -EINVAL; + } + + pr_info("Virtual chip at 0x%02x\n", chip_addr[i]); + } + + /* Allocate memory for all chips at once */ + stub_chips_nr = i; + stub_chips = kzalloc_objs(struct stub_chip, stub_chips_nr); + if (!stub_chips) + return -ENOMEM; + + for (i = 0; i < stub_chips_nr; i++) { + INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); + + /* Allocate extra memory for banked register ranges */ + if (bank_mask[i]) { + ret = i2c_stub_allocate_banks(i); + if (ret) + goto fail_free; + } + } + + ret = i2c_add_adapter(&stub_adapter); + if (ret) + goto fail_free; + + return 0; + + fail_free: + i2c_stub_free(); + return ret; +} + +static void __exit i2c_stub_exit(void) +{ + i2c_del_adapter(&stub_adapter); + i2c_stub_free(); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("I2C stub driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_stub_init); +module_exit(i2c_stub_exit); diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig new file mode 100644 index 000000000..6d2f66810 --- /dev/null +++ b/drivers/i2c/muxes/Kconfig @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX + +config I2C_ARB_GPIO_CHALLENGE + tristate "GPIO-based I2C arbitration" + depends on GPIOLIB || 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 & + response mechanism where masters have to claim the bus by asserting + a GPIO. + + This driver can also be built as a module. If so, the module + will be called i2c-arb-gpio-challenge. + +config I2C_MUX_GPIO + tristate "GPIO-based I2C multiplexer" + depends on GPIOLIB + help + If you say yes to this option, support will be included for a + GPIO based I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which is controlled + through GPIO pins. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-gpio. + +config I2C_MUX_GPMUX + tristate "General Purpose I2C multiplexer" + select MULTIPLEXER + 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 + I2C busses connected through a MUX, which in turn is controlled + by a MUX-controller from the MUX subsystem. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-gpmux. + +config I2C_MUX_LTC4306 + tristate "LTC LTC4306/5 I2C multiplexer" + select GPIOLIB + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices + LTC4306 or LTC4305 I2C mux/switch devices. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-ltc4306. + +config I2C_MUX_PCA9541 + tristate "NXP PCA9541 I2C Master Selector" + help + If you say yes here you get support for the NXP PCA9541 + I2C Master Selector. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-pca9541. + +config I2C_MUX_PCA954x + 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 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. + +config I2C_MUX_PINCTRL + tristate "pinctrl-based I2C multiplexer" + depends on PINCTRL + 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. + This is useful for SoCs whose I2C module's signals can be routed to + different sets of pins at run-time. + + This driver can also be built as a module. If so, the module will be + called i2c-mux-pinctrl. + +config I2C_MUX_REG + tristate "Register-based I2C multiplexer" + depends on HAS_IOMEM + help + If you say yes to this option, support will be included for a + register based I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which is controlled + by a single register. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-reg. + +config I2C_DEMUX_PINCTRL + tristate "pinctrl-based I2C demultiplexer" + depends on PINCTRL && OF + select OF_DYNAMIC + help + If you say yes to this option, support will be included for an I2C + demultiplexer that uses the pinctrl subsystem. This is useful if you + 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. + +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 new file mode 100644 index 000000000..4b24f4951 --- /dev/null +++ b/drivers/i2c/muxes/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o + +obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o + +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 +obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o + +ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c new file mode 100644 index 000000000..d6ef91b88 --- /dev/null +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism + * + * Copyright (C) 2012 Google, Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * struct i2c_arbitrator_data - Driver data for I2C arbitrator + * + * @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 { + 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. + */ +static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan) +{ + const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); + unsigned long stop_retry, stop_time; + + /* Start a round of trying to claim the bus */ + stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1; + do { + /* Indicate that we want to claim the bus */ + 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 = gpiod_get_value(arb->their_gpio); + + if (!gpio_val) { + /* We got it, so return */ + return 0; + } + + usleep_range(50, 200); + } + + /* It didn't release, so give up, wait, and try again */ + 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 */ + 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. + */ +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 */ + gpiod_set_value(arb->our_gpio, 0); + udelay(arb->slew_delay_us); + + return 0; +} + +static int i2c_arbitrator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_mux_core *muxc; + struct i2c_arbitrator_data *arb; + struct gpio_desc *dummy; + int ret; + + /* We only support probing from device tree; no platform_data */ + if (!np) { + dev_err(dev, "Cannot find device tree node\n"); + return -ENODEV; + } + if (dev_get_platdata(dev)) { + dev_err(dev, "Platform data is not supported\n"); + return -EINVAL; + } + + muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), I2C_MUX_ARBITRATOR, + i2c_arbitrator_select, i2c_arbitrator_deselect); + if (!muxc) + return -ENOMEM; + arb = i2c_mux_priv(muxc); + + platform_set_drvdata(pdev, muxc); + + /* 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); + } + + 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) */ + 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 */ + if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us)) + arb->slew_delay_us = 10; + if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us)) + arb->wait_retry_us = 3000; + if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us)) + arb->wait_free_us = 50000; + + /* Find our parent */ + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return -EINVAL; + } + muxc->parent = of_get_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!muxc->parent) { + dev_err(dev, "Cannot find parent bus\n"); + return -EPROBE_DEFER; + } + + /* Actually add the mux adapter */ + ret = i2c_mux_add_adapter(muxc, 0, 0); + if (ret) + i2c_put_adapter(muxc->parent); + + return ret; +} + +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); +} + +static const struct of_device_id i2c_arbitrator_of_match[] = { + { .compatible = "i2c-arb-gpio-challenge", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match); + +static struct platform_driver i2c_arbitrator_driver = { + .probe = i2c_arbitrator_probe, + .remove = i2c_arbitrator_remove, + .driver = { + .name = "i2c-arb-gpio-challenge", + .of_match_table = i2c_arbitrator_of_match, + }, +}; + +module_platform_driver(i2c_arbitrator_driver); + +MODULE_DESCRIPTION("GPIO-based I2C Arbitration"); +MODULE_AUTHOR("Doug Anderson "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-arb-gpio-challenge"); diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c new file mode 100644 index 000000000..2403c0bf7 --- /dev/null +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Pinctrl based I2C DeMultiplexer + * + * Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering + * Copyright (C) 2015-16 by Renesas Electronics Corporation + * + * See the bindings doc for DTS setup and the sysfs doc for usage information. + * (look for filenames containing 'i2c-demux-pinctrl' in Documentation/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct i2c_demux_pinctrl_chan { + struct device_node *parent_np; + struct i2c_adapter *parent_adap; + struct of_changeset chgset; +}; + +struct i2c_demux_pinctrl_priv { + int cur_chan; + int num_chan; + struct device *dev; + const char *bus_name; + struct i2c_adapter cur_adap; + struct i2c_algorithm algo; + 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) +{ + struct i2c_demux_pinctrl_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap; + + return __i2c_transfer(parent, msgs, num); +} + +static u32 i2c_demux_functionality(struct i2c_adapter *adap) +{ + struct i2c_demux_pinctrl_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap; + + return parent->algo->functionality(parent); +} + +static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan) +{ + struct i2c_adapter *adap; + struct pinctrl *p; + int ret; + + ret = of_changeset_apply(&priv->chan[new_chan].chgset); + if (ret) + goto err; + + 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 can't use + * devm_pinctrl_get_select() because we need to distinguish between + * the -ENODEV from devm_pinctrl_get() and pinctrl_lookup_state(). + */ + p = devm_pinctrl_get(adap->dev.parent); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + /* continue if just no pinctrl states (e.g. i2c-gpio), otherwise exit */ + if (ret != -ENODEV) + goto err_with_put; + } else { + /* there are states. check and use them */ + struct pinctrl_state *s = pinctrl_lookup_state(p, priv->bus_name); + + if (IS_ERR(s)) { + ret = PTR_ERR(s); + goto err_with_put; + } + ret = pinctrl_select_state(p, s); + if (ret < 0) + goto err_with_put; + } + + priv->chan[new_chan].parent_adap = adap; + priv->cur_chan = new_chan; + + /* Now fill out current adapter structure. cur_chan must be up to date */ + 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), + "i2c-demux (master i2c-%d)", i2c_adapter_id(adap)); + priv->cur_adap.owner = THIS_MODULE; + priv->cur_adap.algo = &priv->algo; + priv->cur_adap.algo_data = priv; + priv->cur_adap.dev.parent = &adap->dev; + priv->cur_adap.class = adap->class; + priv->cur_adap.retries = adap->retries; + priv->cur_adap.timeout = adap->timeout; + priv->cur_adap.quirks = adap->quirks; + priv->cur_adap.dev.of_node = priv->dev->of_node; + ret = i2c_add_adapter(&priv->cur_adap); + if (ret < 0) + goto err_with_put; + + return 0; + + err_with_put: + i2c_put_adapter(adap); + err_with_revert: + of_changeset_revert(&priv->chan[new_chan].chgset); + err: + dev_err(priv->dev, "failed to setup demux-adapter %d (%d)\n", new_chan, ret); + priv->cur_chan = -EINVAL; + return ret; +} + +static int i2c_demux_deactivate_master(struct i2c_demux_pinctrl_priv *priv) +{ + int ret, cur = priv->cur_chan; + + if (cur < 0) + return 0; + + i2c_del_adapter(&priv->cur_adap); + i2c_put_adapter(priv->chan[cur].parent_adap); + + ret = of_changeset_revert(&priv->chan[cur].chgset); + + priv->chan[cur].parent_adap = NULL; + priv->cur_chan = -EINVAL; + + return ret; +} + +static int i2c_demux_change_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan) +{ + int ret; + + if (new_chan == priv->cur_chan) + return 0; + + ret = i2c_demux_deactivate_master(priv); + if (ret) + return ret; + + return i2c_demux_activate_master(priv, new_chan); +} + +static ssize_t available_masters_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); + int count = 0, i; + + for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++) + count += sysfs_emit_at(buf, count, "%d:%pOF%c", + i, priv->chan[i].parent_np, + i == priv->num_chan - 1 ? '\n' : ' '); + + return count; +} +static DEVICE_ATTR_RO(available_masters); + +static ssize_t current_master_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", priv->cur_chan); +} + +static ssize_t current_master_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + if (val >= priv->num_chan) + return -EINVAL; + + ret = i2c_demux_change_master(priv, val); + + return ret < 0 ? ret : count; +} +static DEVICE_ATTR_RW(current_master); + +static int i2c_demux_pinctrl_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct i2c_demux_pinctrl_priv *priv; + struct property *props; + int num_chan, i, j, err; + + num_chan = of_count_phandle_with_args(np, "i2c-parent", NULL); + if (num_chan < 2) { + dev_err(&pdev->dev, "Need at least two I2C masters to switch\n"); + return -EINVAL; + } + + 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; + + for (i = 0; i < num_chan; i++) { + struct device_node *adap_np; + + adap_np = of_parse_phandle(np, "i2c-parent", i); + if (!adap_np) { + dev_err(&pdev->dev, "can't get phandle for parent %d\n", i); + err = -ENOENT; + goto err_rollback; + } + priv->chan[i].parent_np = adap_np; + + 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; + of_node_put(adap_np); + 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->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + pm_runtime_no_callbacks(&pdev->dev); + + /* switch to first parent as active master */ + 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_activation; + + err = device_create_file(&pdev->dev, &dev_attr_current_master); + if (err) + goto err_rollback_available; + + return 0; + +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); + of_changeset_destroy(&priv->chan[j].chgset); + } + + return err; +} + +static void i2c_demux_pinctrl_remove(struct platform_device *pdev) +{ + struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev); + int i; + + device_remove_file(&pdev->dev, &dev_attr_current_master); + device_remove_file(&pdev->dev, &dev_attr_available_masters); + + i2c_demux_deactivate_master(priv); + + for (i = 0; i < priv->num_chan; i++) { + of_node_put(priv->chan[i].parent_np); + of_changeset_destroy(&priv->chan[i].chgset); + } +} + +static const struct of_device_id i2c_demux_pinctrl_of_match[] = { + { .compatible = "i2c-demux-pinctrl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match); + +static struct platform_driver i2c_demux_pinctrl_driver = { + .driver = { + .name = "i2c-demux-pinctrl", + .of_match_table = i2c_demux_pinctrl_of_match, + }, + .probe = i2c_demux_pinctrl_probe, + .remove = i2c_demux_pinctrl_remove, +}; +module_platform_driver(i2c_demux_pinctrl_driver); + +MODULE_DESCRIPTION("pinctrl-based I2C demux driver"); +MODULE_AUTHOR("Wolfram Sang "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-demux-pinctrl"); diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c new file mode 100644 index 000000000..9b46b84e8 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpiomux { + struct i2c_mux_gpio_platform_data data; + int ngpios; + struct gpio_desc **gpios; +}; + +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->ngpios, mux->gpios, NULL, values); +} + +static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct gpiomux *mux = i2c_mux_priv(muxc); + + i2c_mux_gpio_set(mux, chan); + + if (mux->data.settle_time) + fsleep(mux->data.settle_time); + + return 0; +} + +static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct gpiomux *mux = i2c_mux_priv(muxc); + + i2c_mux_gpio_set(mux, mux->data.idle); + + return 0; +} + +static int i2c_mux_gpio_probe_fw(struct gpiomux *mux, + struct platform_device *pdev) +{ + 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); + + } 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); + } + + if (!adapter) + return -EPROBE_DEFER; + + mux->data.parent = i2c_adapter_id(adapter); + put_device(&adapter->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(dev, "Cannot allocate values array"); + return -ENOMEM; + } + + 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 (device_property_read_u32(dev, "idle-state", &mux->data.idle)) + mux->data.idle = I2C_MUX_GPIO_NO_IDLE; + + device_property_read_u32(dev, "settle-time-us", &mux->data.settle_time); + + return 0; +} + +static int i2c_mux_gpio_probe(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc; + struct gpiomux *mux; + struct i2c_adapter *parent; + struct i2c_adapter *root; + 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_fw(mux, pdev); + if (ret < 0) + return ret; + } else { + memcpy(&mux->data, dev_get_platdata(&pdev->dev), + sizeof(mux->data)); + } + + 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, + array_size(ngpios, sizeof(*mux->gpios)), 0, + i2c_mux_gpio_select, NULL); + if (!muxc) { + ret = -ENOMEM; + goto alloc_failed; + } + mux->gpios = muxc->priv; + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + root = i2c_root_adapter(&parent->dev); + + muxc->mux_locked = true; + + if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { + initial_state = mux->data.idle; + muxc->deselect = i2c_mux_gpio_deselect; + } else { + initial_state = mux->data.values[0]; + } + + 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; + } + + mux->gpios[i] = gpiod; + + if (!muxc->mux_locked) + continue; + + gdev = gpiod_to_gpio_device(gpiod); + dev = gpio_device_to_device(gdev); + muxc->mux_locked = i2c_root_adapter(dev) == root; + } + + if (muxc->mux_locked) + dev_info(&pdev->dev, "mux-locked i2c mux\n"); + + for (i = 0; i < mux->data.n_values; i++) { + u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; + + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i]); + if (ret) + goto add_adapter_failed; + } + + dev_info(&pdev->dev, "%d port mux on %s adapter\n", + mux->data.n_values, parent->name); + + return 0; + +add_adapter_failed: + i2c_mux_del_adapters(muxc); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static void i2c_mux_gpio_remove(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); +} + +static const struct of_device_id i2c_mux_gpio_of_match[] = { + { .compatible = "i2c-mux-gpio", }, + {}, +}; +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, + .driver = { + .name = "i2c-mux-gpio", + .of_match_table = i2c_mux_gpio_of_match, + }, +}; + +module_platform_driver(i2c_mux_gpio_driver); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-mux-gpio"); diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c new file mode 100644 index 000000000..ab8e11661 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * General Purpose I2C multiplexer + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin + */ + +#include +#include +#include +#include +#include +#include + +struct mux { + struct mux_control *control; + + bool do_not_deselect; +}; + +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + int ret; + + ret = mux_control_select(mux->control, chan); + mux->do_not_deselect = ret < 0; + + return ret; +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + + if (mux->do_not_deselect) + return 0; + + return mux_control_deselect(mux->control); +} + +static struct i2c_adapter *mux_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_get_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + +static const struct of_device_id i2c_mux_of_match[] = { + { .compatible = "i2c-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_of_match); + +static int i2c_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct i2c_mux_core *muxc; + struct mux *mux; + struct i2c_adapter *parent; + int children; + int ret; + + if (!np) + return -ENODEV; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->control = devm_mux_control_get(dev, NULL); + 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)) + return dev_err_probe(dev, PTR_ERR(parent), + "failed to get i2c-parent adapter\n"); + + children = of_get_child_count(np); + + muxc = i2c_mux_alloc(parent, dev, children, 0, 0, + i2c_mux_select, i2c_mux_deselect); + if (!muxc) { + ret = -ENOMEM; + goto err_parent; + } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + muxc->mux_locked = of_property_read_bool(np, "mux-locked"); + + for_each_child_of_node(np, child) { + u32 chan; + + ret = of_property_read_u32(child, "reg", &chan); + if (ret < 0) { + dev_err(dev, "no reg property for node '%pOFn'\n", + child); + goto err_children; + } + + if (chan >= mux_control_states(mux->control)) { + dev_err(dev, "invalid reg %u\n", chan); + ret = -EINVAL; + goto err_children; + } + + ret = i2c_mux_add_adapter(muxc, 0, chan); + if (ret) + goto err_children; + } + + dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name); + + return 0; + +err_children: + of_node_put(child); + i2c_mux_del_adapters(muxc); +err_parent: + i2c_put_adapter(parent); + + return ret; +} + +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); +} + +static struct platform_driver i2c_mux_driver = { + .probe = i2c_mux_probe, + .remove = i2c_mux_remove, + .driver = { + .name = "i2c-mux-gpmux", + .of_match_table = i2c_mux_of_match, + }, +}; +module_platform_driver(i2c_mux_driver); + +MODULE_DESCRIPTION("General Purpose I2C multiplexer driver"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c new file mode 100644 index 000000000..18228f5b8 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Based on: i2c-mux-pca954x.c + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC4305_MAX_NCHANS 2 +#define LTC4306_MAX_NCHANS 4 + +#define LTC_REG_STATUS 0x0 +#define LTC_REG_CONFIG 0x1 +#define LTC_REG_MODE 0x2 +#define LTC_REG_SWITCH 0x3 + +#define LTC_DOWNSTREAM_ACCL_EN BIT(6) +#define LTC_UPSTREAM_ACCL_EN BIT(7) + +#define LTC_GPIO_ALL_INPUT 0xC0 +#define LTC_SWITCH_MASK 0xF0 + +enum ltc_type { + ltc_4305, + ltc_4306, +}; + +struct chip_desc { + u8 nchans; + u8 num_gpios; +}; + +struct ltc4306 { + struct regmap *regmap; + struct gpio_chip gpiochip; + const struct chip_desc *chip; +}; + +static const struct chip_desc chips[] = { + [ltc_4305] = { + .nchans = LTC4305_MAX_NCHANS, + }, + [ltc_4306] = { + .nchans = LTC4306_MAX_NCHANS, + .num_gpios = 2, + }, +}; + +static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == LTC_REG_CONFIG; +} + +static const struct regmap_config ltc4306_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC_REG_SWITCH, + .volatile_reg = ltc4306_is_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, LTC_REG_CONFIG, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(1 - offset)); +} + +static int ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + 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, + unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, LTC_REG_MODE, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(7 - offset)); +} + +static int ltc4306_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), BIT(7 - offset)); +} + +static int ltc4306_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + ltc4306_gpio_set(chip, offset, value); + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), 0); +} + +static int ltc4306_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, unsigned long config) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + val = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + val = BIT(4 - offset); + break; + default: + return -ENOTSUPP; + } + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(4 - offset), val); +} + +static int ltc4306_gpio_init(struct ltc4306 *data) +{ + struct device *dev = regmap_get_device(data->regmap); + + if (!data->chip->num_gpios) + return 0; + + data->gpiochip.label = dev_name(dev); + data->gpiochip.base = -1; + data->gpiochip.ngpio = data->chip->num_gpios; + data->gpiochip.parent = dev; + data->gpiochip.can_sleep = true; + data->gpiochip.get_direction = ltc4306_gpio_get_direction; + data->gpiochip.direction_input = ltc4306_gpio_direction_input; + data->gpiochip.direction_output = ltc4306_gpio_direction_output; + data->gpiochip.get = ltc4306_gpio_get; + data->gpiochip.set = ltc4306_gpio_set; + data->gpiochip.set_config = ltc4306_gpio_set_config; + data->gpiochip.owner = THIS_MODULE; + + /* gpiolib assumes all GPIOs default input */ + regmap_write(data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT); + + return devm_gpiochip_add_data(dev, &data->gpiochip, data); +} + +static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, BIT(7 - chan)); +} + +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, 0); +} + +static const struct i2c_device_id ltc4306_id[] = { + { .name = "ltc4305", .driver_data = ltc_4305 }, + { .name = "ltc4306", .driver_data = ltc_4306 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4306_id); + +static const struct of_device_id ltc4306_of_match[] = { + { .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] }, + { .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc4306_of_match); + +static int ltc4306_probe(struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + const struct chip_desc *chip; + struct i2c_mux_core *muxc; + struct ltc4306 *data; + struct gpio_desc *gpio; + bool idle_disc; + unsigned int val = 0; + int num, ret; + + chip = of_device_get_match_data(&client->dev); + + if (!chip) + chip = &chips[i2c_match_id(ltc4306_id, client)->driver_data]; + + idle_disc = device_property_read_bool(&client->dev, + "i2c-mux-idle-disconnect"); + + muxc = i2c_mux_alloc(adap, &client->dev, + chip->nchans, sizeof(*data), + I2C_MUX_LOCKED, ltc4306_select_mux, + idle_disc ? ltc4306_deselect_mux : NULL); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + data->chip = chip; + + i2c_set_clientdata(client, muxc); + + data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* Reset and enable the mux if an enable GPIO is specified. */ + gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + udelay(1); + gpiod_set_value(gpio, 1); + } + + /* + * Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if (regmap_write(data->regmap, LTC_REG_SWITCH, 0) < 0) { + dev_warn(&client->dev, "probe failed\n"); + return -ENODEV; + } + + if (device_property_read_bool(&client->dev, + "ltc,downstream-accelerators-enable")) + val |= LTC_DOWNSTREAM_ACCL_EN; + + if (device_property_read_bool(&client->dev, + "ltc,upstream-accelerators-enable")) + val |= LTC_UPSTREAM_ACCL_EN; + + if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0) + return -ENODEV; + + ret = ltc4306_gpio_init(data); + if (ret < 0) + return ret; + + /* Now create an adapter for each channel */ + for (num = 0; num < chip->nchans; num++) { + ret = i2c_mux_add_adapter(muxc, 0, num); + if (ret) { + i2c_mux_del_adapters(muxc); + return ret; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C switch %s\n", + num, client->name); + + return 0; +} + +static void ltc4306_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + + i2c_mux_del_adapters(muxc); +} + +static struct i2c_driver ltc4306_driver = { + .driver = { + .name = "ltc4306", + .of_match_table = ltc4306_of_match, + }, + .probe = ltc4306_probe, + .remove = ltc4306_remove, + .id_table = ltc4306_id, +}; + +module_i2c_driver(ltc4306_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c new file mode 100644 index 000000000..1c2debcf3 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Mellanox i2c mux driver + * + * Copyright (C) 2016-2020 Mellanox Technologies + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* mlxcpld_mux - mux control structure: + * @last_val - last selected register value or -1 if mux deselected + * @client - I2C device client + * @pdata: platform data + */ +struct mlxcpld_mux { + int last_val; + struct i2c_client *client; + struct mlxcpld_mux_plat_data pdata; +}; + +/* MUX logic description. + * Driver can support different mux control logic, according to CPLD + * implementation. + * + * Connectivity schema. + * + * i2c-mlxcpld Digital Analog + * driver + * *--------* * -> mux1 (virt bus2) -> mux -> | + * | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> | + * | bridge | bus 1 *---------* | + * | logic |---------------------> * mux reg * | + * | in CPLD| *---------* | + * *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> | + * | driver | | + * | *---------------* | Devices + * | * CPLD (i2c bus)* select | + * | * registers for *--------* + * | * mux selection * deselect + * | *---------------* + * | | + * <--------> <-----------> + * i2c cntrl Board cntrl reg + * reg space space (mux select, + * IO, LED, WD, info) + * + */ + +/* 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 mlxcpld_mux *mux, u32 val) +{ + 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 *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 (mux->last_val != regval) { + err = mlxcpld_mux_reg_write(muxc->parent, mux, regval); + mux->last_val = err < 0 ? -1 : regval; + } + + return err; +} + +static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct mlxcpld_mux *mux = i2c_mux_priv(muxc); + + /* Deselect active channel */ + mux->last_val = -1; + + return mlxcpld_mux_reg_write(muxc->parent, mux, 0); +} + +/* Probe/reomove functions */ +static int mlxcpld_mux_probe(struct platform_device *pdev) +{ + 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; + struct mlxcpld_mux *data; + int num, err; + u32 func; + + if (!pdata) + return -EINVAL; + + 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(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); + data->client = client; + memcpy(&data->pdata, pdata, sizeof(*pdata)); + data->last_val = -1; /* force the first selection */ + + /* Create an adapter for each channel. */ + 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: + i2c_mux_del_adapters(muxc); + return err; +} + +static void mlxcpld_mux_remove(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + + i2c_mux_del_adapters(muxc); +} + +static struct platform_driver mlxcpld_mux_driver = { + .driver = { + .name = "i2c-mux-mlxcpld", + }, + .probe = mlxcpld_mux_probe, + .remove = mlxcpld_mux_remove, +}; + +module_platform_driver(mlxcpld_mux_driver); + +MODULE_AUTHOR("Michael Shych "); +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 000000000..d3b32b794 --- /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 +#include +#include +#include +#include +#include +#include + +#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", ®); + 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 "); +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 new file mode 100644 index 000000000..9a59129bc --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -0,0 +1,348 @@ +/* + * I2C multiplexer driver for PCA9541 bus master selector + * + * Copyright (c) 2010 Ericsson AB. + * + * Author: Guenter Roeck + * + * Derived from: + * pca954x.c + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * The PCA9541 is a bus master selector. It supports two I2C masters connected + * to a single slave bus. + * + * Before each bus transaction, a master has to acquire bus ownership. After the + * transaction is complete, bus ownership has to be released. This fits well + * into the I2C multiplexer framework, which provides select and release + * functions for this purpose. For this reason, this driver is modeled as + * single-channel I2C bus multiplexer. + * + * This driver assumes that the two bus masters are controlled by two different + * hosts. If a single host controls both masters, platform code has to ensure + * that only one of the masters is instantiated at any given time. + */ + +#define PCA9541_CONTROL 0x01 +#define PCA9541_ISTAT 0x02 + +#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 retry delays, in us */ +#define SELECT_DELAY_SHORT 50 +#define SELECT_DELAY_LONG 1000 + +struct pca9541 { + struct i2c_client *client; + unsigned long select_timeout; + unsigned long arb_timeout; +}; + +static const struct i2c_device_id pca9541_id[] = { + { .name = "pca9541" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9541_id); + +#ifdef CONFIG_OF +static const struct of_device_id pca9541_of_match[] = { + { .compatible = "nxp,pca9541" }, + {} +}; +MODULE_DEVICE_TABLE(of, pca9541_of_match); +#endif + +/* + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock the adapter a second time. + */ +static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) +{ + struct i2c_adapter *adap = client->adapter; + union i2c_smbus_data data = { .byte = val }; + + return __i2c_smbus_xfer(adap, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} + +/* + * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock adapter a second time. + */ +static int pca9541_reg_read(struct i2c_client *client, u8 command) +{ + struct i2c_adapter *adap = client->adapter; + union i2c_smbus_data data; + int ret; + + ret = __i2c_smbus_xfer(adap, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + + return ret ?: data.byte; +} + +/* + * Arbitration management functions + */ + +/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ +static void pca9541_release_bus(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg >= 0 && !busoff(reg) && mybus(reg)) + pca9541_reg_write(client, PCA9541_CONTROL, + (reg & PCA9541_CTL_NBUSON) >> 1); +} + +/* + * Arbitration is defined as a two-step process. A bus master can only activate + * the slave bus if it owns it; otherwise it has to request ownership first. + * This multi-step process ensures that access contention is resolved + * gracefully. + * + * Bus Ownership Other master Action + * state requested access + * ---------------------------------------------------- + * off - yes wait for arbitration timeout or + * for other master to drop request + * off no no take ownership + * off yes no turn on bus + * on yes - done + * on no - wait for arbitration timeout or + * for other master to release bus + * + * The main contention point occurs if the slave bus is off and both masters + * request ownership at the same time. In this case, one master will turn on + * the slave bus, believing that it owns it. The other master will request + * bus ownership. Result is that the bus is turned on, and master which did + * _not_ own the slave bus before ends up owning it. + */ + +/* Control commands per PCA9541 datasheet */ +static const u8 pca9541_control[16] = { + 4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1 +}; + +/* + * Channel arbitration + * + * Return values: + * <0: error + * 0 : bus not acquired + * 1 : bus acquired + */ +static int pca9541_arbitrate(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg < 0) + return reg; + + if (busoff(reg)) { + int istat; + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + istat = pca9541_reg_read(client, PCA9541_ISTAT); + if (!(istat & PCA9541_ISTAT_NMYTEST) + || time_is_before_eq_jiffies(data->arb_timeout)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_NTESTON); + data->select_timeout = SELECT_DELAY_SHORT; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (mybus(reg)) { + /* + * Bus is on, and we own it. We are done with acquisition. + * Reset NTESTON and BUSINIT, then return success. + */ + if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg & ~(PCA9541_CTL_NTESTON + | PCA9541_CTL_BUSINIT)); + return 1; + } else { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + 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, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_BUSINIT + | PCA9541_CTL_NTESTON); + } else { + /* Request bus ownership if needed */ + if (!(reg & PCA9541_CTL_NTESTON)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg | PCA9541_CTL_NTESTON); + } + } + return 0; +} + +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 + (2 * client->adapter->timeout); + /* give up after this time */ + + data->arb_timeout = jiffies + client->adapter->timeout; + /* force bus ownership after this time */ + + do { + ret = pca9541_arbitrate(client); + if (ret) + return ret < 0 ? ret : 0; + + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + 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; +} + +static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + pca9541_release_bus(client); + return 0; +} + +/* + * I2C init/probing/exit functions + */ +static int pca9541_probe(struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_mux_core *muxc; + struct pca9541 *data; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* + * I2C accesses are unprotected here. + * We have to lock the I2C segment before releasing the bus. + */ + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + pca9541_release_bus(client); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + /* Create mux adapter */ + + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), + I2C_MUX_ARBITRATOR, + pca9541_select_chan, pca9541_release_chan); + if (!muxc) + return -ENOMEM; + + data = i2c_mux_priv(muxc); + data->client = client; + + i2c_set_clientdata(client, muxc); + + ret = i2c_mux_add_adapter(muxc, 0, 0); + if (ret) + return ret; + + dev_info(&client->dev, "registered master selector for I2C %s\n", + client->name); + + return 0; +} + +static void pca9541_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + + i2c_mux_del_adapters(muxc); +} + +static struct i2c_driver pca9541_driver = { + .driver = { + .name = "pca9541", + .of_match_table = of_match_ptr(pca9541_of_match), + }, + .probe = pca9541_probe, + .remove = pca9541_remove, + .id_table = pca9541_id, +}; + +module_i2c_driver(pca9541_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c new file mode 100644 index 000000000..8fca709ed --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * 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 + * are selected is determined by the chip type and register contents. A + * mux can select only one sub-bus at a time; a switch can select any + * combination simultaneously. + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, + pca_9846, + pca_9847, + pca_9848, + pca_9849, +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + u8 has_irq; + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; + struct i2c_device_identity id; +}; + +struct pca954x { + const struct chip_desc *chip; + + u8 last_chan; /* last register value */ + /* 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 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, + .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9542] = { + .nchans = 2, + .enable = 0x4, + .has_irq = 1, + .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9543] = { + .nchans = 2, + .has_irq = 1, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .has_irq = 1, + .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9545] = { + .nchans = 4, + .has_irq = 1, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9546] = { + .nchans = 4, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [pca_9846] = { + .nchans = 4, + .muxtype = pca954x_isswi, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x10b, + }, + }, + [pca_9847] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x108, + }, + }, + [pca_9848] = { + .nchans = 8, + .muxtype = pca954x_isswi, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x10a, + }, + }, + [pca_9849] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x109, + }, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + { .name = "max7356", .driver_data = max_7356 }, + { .name = "max7357", .driver_data = max_7357 }, + { .name = "max7358", .driver_data = max_7358 }, + { .name = "max7367", .driver_data = max_7367 }, + { .name = "max7368", .driver_data = max_7368 }, + { .name = "max7369", .driver_data = max_7369 }, + { .name = "pca9540", .driver_data = pca_9540 }, + { .name = "pca9542", .driver_data = pca_9542 }, + { .name = "pca9543", .driver_data = pca_9543 }, + { .name = "pca9544", .driver_data = pca_9544 }, + { .name = "pca9545", .driver_data = pca_9545 }, + { .name = "pca9546", .driver_data = pca_9546 }, + { .name = "pca9547", .driver_data = pca_9547 }, + { .name = "pca9548", .driver_data = pca_9548 }, + { .name = "pca9846", .driver_data = pca_9846 }, + { .name = "pca9847", .driver_data = pca_9847 }, + { .name = "pca9848", .driver_data = pca_9848 }, + { .name = "pca9849", .driver_data = pca_9849 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca954x_id); + +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] }, + { .compatible = "nxp,pca9544", .data = &chips[pca_9544] }, + { .compatible = "nxp,pca9545", .data = &chips[pca_9545] }, + { .compatible = "nxp,pca9546", .data = &chips[pca_9546] }, + { .compatible = "nxp,pca9547", .data = &chips[pca_9547] }, + { .compatible = "nxp,pca9548", .data = &chips[pca_9548] }, + { .compatible = "nxp,pca9846", .data = &chips[pca_9846] }, + { .compatible = "nxp,pca9847", .data = &chips[pca_9847] }, + { .compatible = "nxp,pca9848", .data = &chips[pca_9848] }, + { .compatible = "nxp,pca9849", .data = &chips[pca_9849] }, + {} +}; +MODULE_DEVICE_TABLE(of, pca954x_of_match); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + union i2c_smbus_data dummy; + + return __i2c_smbus_xfer(adap, client->addr, client->flags, + I2C_SMBUS_WRITE, val, + 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; + u8 regval; + int ret = 0; + + 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); + data->last_chan = ret < 0 ? 0 : regval; + } + + return ret; +} + +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); + } + + /* otherwise leave as-is */ + + 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 long pending; + int ret, i; + + ret = i2c_smbus_read_byte(data->client); + if (ret < 0) + return 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) +{ + if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + return 0; +} + +static struct irq_chip pca954x_irq_chip = { + .name = "i2c-mux-pca954x", + .irq_set_type = pca954x_irq_set_type, +}; + +static int pca954x_irq_setup(struct i2c_mux_core *muxc) +{ + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int c, irq; + + if (!data->chip->has_irq || client->irq <= 0) + return 0; + + raw_spin_lock_init(&data->lock); + + data->irq = irq_domain_create_linear(dev_fwnode(&client->dev), data->chip->nchans, + &irq_domain_simple_ops, data); + if (!data->irq) + return -ENODEV; + + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_create_mapping(data->irq, c); + if (!irq) { + dev_err(&client->dev, "failed irq create map\n"); + return -EINVAL; + } + irq_set_chip_data(irq, data); + irq_set_chip_and_handler(irq, &pca954x_irq_chip, + handle_simple_irq); + } + + return 0; +} + +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); + irq_dispose_mapping(irq); + } + irq_domain_remove(data->irq); + } + 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 = i2c_client_get_device_id(client); + struct i2c_adapter *adap = client->adapter; + struct device *dev = &client->dev; + struct i2c_mux_core *muxc; + struct pca954x *data; + int num; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0, + pca954x_select_chan, pca954x_deselect_mux); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + + i2c_set_clientdata(client, muxc); + data->client = client; + + 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); + pca954x_reset_deassert(data); + /* Give the chip some time to recover. */ + udelay(1); + } + + data->chip = device_get_match_data(dev); + if (!data->chip) + data->chip = &chips[id->driver_data]; + + if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) { + struct i2c_device_identity id; + + ret = i2c_get_device_id(client, &id); + if (ret && ret != -EOPNOTSUPP) + goto fail_cleanup; + + if (!ret && + (id.manufacturer_id != data->chip->id.manufacturer_id || + id.part_id != data->chip->id.part_id)) { + dev_warn(dev, "unexpected device id %03x-%03x-%x\n", + id.manufacturer_id, id.part_id, + id.die_revision); + ret = -ENODEV; + goto fail_cleanup; + } + } + + 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 a channel + * or disconnected state. + */ + ret = pca954x_init(client, data); + if (ret < 0) { + dev_warn(dev, "probe failed\n"); + ret = -ENODEV; + goto fail_cleanup; + } + + 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++) { + ret = i2c_mux_add_adapter(muxc, 0, num); + if (ret) + goto fail_cleanup; + } + + if (data->irq) { + ret = devm_request_threaded_irq(dev, data->client->irq, + NULL, pca954x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + "pca954x", data); + if (ret) + 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); + + return 0; + +fail_cleanup: + pca954x_cleanup(muxc); + return ret; +} + +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); +} + +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; + + ret = pca954x_init(client, data); + if (ret < 0) + dev_err(&client->dev, "failed to verify mux presence\n"); + + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume); + +static struct i2c_driver pca954x_driver = { + .driver = { + .name = "pca954x", + .pm = pm_sleep_ptr(&pca954x_pm), + .of_match_table = pca954x_of_match, + }, + .probe = pca954x_probe, + .remove = pca954x_remove, + .id_table = pca954x_id, +}; + +module_i2c_driver(pca954x_driver); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c new file mode 100644 index 000000000..fc686a350 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C multiplexer using pinctrl API + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../pinctrl/core.h" + +struct i2c_mux_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *states[]; +}; + +static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); + + return pinctrl_select_state(mux->pinctrl, mux->states[chan]); +} + +static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + return i2c_mux_pinctrl_select(muxc, muxc->num_adapters); +} + +static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( + struct pinctrl_state *state) +{ + struct i2c_adapter *root = NULL; + struct pinctrl_setting *setting; + struct i2c_adapter *pin_root; + + list_for_each_entry(setting, &state->settings, node) { + pin_root = i2c_root_adapter(setting->pctldev->dev); + if (!pin_root) + return NULL; + if (!root) + root = pin_root; + else if (root != pin_root) + return NULL; + } + + return root; +} + +static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_get_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + +static int i2c_mux_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct i2c_mux_core *muxc; + struct i2c_mux_pinctrl *mux; + struct i2c_adapter *parent; + struct i2c_adapter *root; + int num_names, i, ret; + const char *name; + + num_names = of_property_count_strings(np, "pinctrl-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", + num_names); + return num_names; + } + + parent = i2c_mux_pinctrl_parent_adapter(dev); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + muxc = i2c_mux_alloc(parent, dev, num_names, + 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); + + platform_set_drvdata(pdev, muxc); + + mux->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(mux->pinctrl)) { + ret = PTR_ERR(mux->pinctrl); + dev_err(dev, "Cannot get pinctrl: %d\n", ret); + goto err_put_parent; + } + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "pinctrl-names", i, + &name); + if (ret < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret); + goto err_put_parent; + } + + mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name); + if (IS_ERR(mux->states[i])) { + ret = PTR_ERR(mux->states[i]); + dev_err(dev, "Cannot look up pinctrl state %s: %d\n", + name, ret); + goto err_put_parent; + } + + if (strcmp(name, "idle")) + continue; + + if (i != num_names - 1) { + dev_err(dev, "idle state must be last\n"); + ret = -EINVAL; + goto err_put_parent; + } + muxc->deselect = i2c_mux_pinctrl_deselect; + } + + root = i2c_root_adapter(&muxc->parent->dev); + + muxc->mux_locked = true; + for (i = 0; i < num_names; i++) { + if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { + muxc->mux_locked = false; + break; + } + } + if (muxc->mux_locked) + dev_info(dev, "mux-locked i2c mux\n"); + + /* 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); + if (ret) + goto err_del_adapter; + } + + return 0; + +err_del_adapter: + i2c_mux_del_adapters(muxc); +err_put_parent: + i2c_put_adapter(parent); + + return ret; +} + +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); +} + +static const struct of_device_id i2c_mux_pinctrl_of_match[] = { + { .compatible = "i2c-mux-pinctrl", }, + {}, +}; +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 = i2c_mux_pinctrl_of_match, + }, + .probe = i2c_mux_pinctrl_probe, + .remove = i2c_mux_pinctrl_remove, +}; +module_platform_driver(i2c_mux_pinctrl_driver); + +MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); +MODULE_AUTHOR("Stephen Warren "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-mux-pinctrl"); diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c new file mode 100644 index 000000000..13da75710 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * I2C multiplexer using a single register + * + * Copyright 2015 Freescale Semiconductor + * York Sun + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct regmux { + struct i2c_mux_reg_platform_data data; +}; + +static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id) +{ + if (!mux->data.reg) + return -EINVAL; + + /* + * Write to the register, followed by a read to ensure the write is + * completed on a "posted" bus, for example PCI or write buffers. + * The endianness of reading doesn't matter and the return data + * is not used. + */ + switch (mux->data.reg_size) { + case 4: + if (mux->data.little_endian) + iowrite32(chan_id, mux->data.reg); + else + iowrite32be(chan_id, mux->data.reg); + if (!mux->data.write_only) + ioread32(mux->data.reg); + break; + case 2: + if (mux->data.little_endian) + iowrite16(chan_id, mux->data.reg); + else + iowrite16be(chan_id, mux->data.reg); + if (!mux->data.write_only) + ioread16(mux->data.reg); + break; + case 1: + iowrite8(chan_id, mux->data.reg); + if (!mux->data.write_only) + ioread8(mux->data.reg); + break; + } + + return 0; +} + +static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct regmux *mux = i2c_mux_priv(muxc); + + return i2c_mux_reg_set(mux, chan); +} + +static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct regmux *mux = i2c_mux_priv(muxc); + + if (mux->data.idle_in_use) + return i2c_mux_reg_set(mux, mux->data.idle); + + return 0; +} + +static int i2c_mux_reg_probe_fw(struct regmux *mux, struct device *dev) +{ + struct fwnode_handle *fwnode, *child; + struct i2c_adapter *adapter; + unsigned *values; + int ret, i = 0; + + if (!dev_fwnode(dev)) + return -ENODEV; + + fwnode = fwnode_find_reference(dev_fwnode(dev), "i2c-parent", 0); + if (IS_ERR(fwnode)) { + dev_err(dev, "missing 'i2c-parent' property\n"); + return -ENODEV; + } + + adapter = i2c_find_adapter_by_fwnode(fwnode); + fwnode_handle_put(fwnode); + if (!adapter) + return -EPROBE_DEFER; + + mux->data.parent = i2c_adapter_id(adapter); + put_device(&adapter->dev); + + mux->data.n_values = device_get_child_node_count(dev); + if (device_property_read_bool(dev, "little-endian")) { + mux->data.little_endian = true; + } else if (device_property_read_bool(dev, "big-endian")) { + mux->data.little_endian = false; + } else { +#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \ + defined(__LITTLE_ENDIAN) + mux->data.little_endian = true; +#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : \ + defined(__BIG_ENDIAN) + mux->data.little_endian = false; +#else +#error Endianness not defined? +#endif + } + mux->data.write_only = device_property_read_bool(dev, "write-only"); + + values = devm_kcalloc(dev, mux->data.n_values, sizeof(*mux->data.values), + GFP_KERNEL); + if (!values) + return -ENOMEM; + + device_for_each_child_node(dev, child) { + if (is_acpi_device_node(child)) { + ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), + &values[i]); + if (ret) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, + "Cannot get address\n"); + } + } else { + fwnode_property_read_u32(child, "reg", &values[i]); + } + + i++; + } + mux->data.values = values; + + if (!device_property_read_u32(dev, "idle-state", &mux->data.idle)) + mux->data.idle_in_use = true; + + return 0; +} + +static int i2c_mux_reg_probe(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc; + struct regmux *mux; + struct i2c_adapter *parent; + struct resource *res; + int i, ret, nr; + + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + if (dev_get_platdata(&pdev->dev)) { + memcpy(&mux->data, dev_get_platdata(&pdev->dev), + sizeof(mux->data)); + } else { + ret = i2c_mux_reg_probe_fw(mux, &pdev->dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Error parsing firmware description\n"); + } + + if (!mux->data.reg) { + mux->data.reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(mux->data.reg)) + return PTR_ERR(mux->data.reg); + mux->data.reg_size = resource_size(res); + } + + if (mux->data.reg_size != 4 && mux->data.reg_size != 2 && + mux->data.reg_size != 1) { + dev_err(&pdev->dev, "Invalid register size\n"); + return -EINVAL; + } + + parent = i2c_get_adapter(mux->data.parent); + if (!parent) + return -EPROBE_DEFER; + + muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0, + i2c_mux_reg_select, NULL); + if (!muxc) { + ret = -ENOMEM; + goto err_put_parent; + } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + if (mux->data.idle_in_use) + muxc->deselect = i2c_mux_reg_deselect; + + for (i = 0; i < mux->data.n_values; i++) { + nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; + + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i]); + if (ret) + goto err_del_mux_adapters; + } + + dev_dbg(&pdev->dev, "%d port mux on %s adapter\n", + mux->data.n_values, muxc->parent->name); + + return 0; + +err_del_mux_adapters: + i2c_mux_del_adapters(muxc); +err_put_parent: + i2c_put_adapter(parent); + + return ret; +} + +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); +} + +static const struct of_device_id i2c_mux_reg_of_match[] = { + { .compatible = "i2c-mux-reg", }, + {}, +}; +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, + .driver = { + .name = "i2c-mux-reg", + .of_match_table = i2c_mux_reg_of_match, + }, +}; + +module_platform_driver(i2c_mux_reg_driver); + +MODULE_DESCRIPTION("Register-based I2C multiplexer driver"); +MODULE_AUTHOR("York Sun "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-mux-reg"); -- cgit v1.3.1