From patchwork Fri Oct 29 08:42:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Li-hao Kuo X-Patchwork-Id: 1547890 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=jBUVynEe; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by bilbo.ozlabs.org (Postfix) with ESMTP id 4HgbXg5cP2z9sRK for ; Fri, 29 Oct 2021 19:42:43 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232554AbhJ2IpK (ORCPT ); Fri, 29 Oct 2021 04:45:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51040 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232531AbhJ2IpI (ORCPT ); Fri, 29 Oct 2021 04:45:08 -0400 Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DD66EC061714; Fri, 29 Oct 2021 01:42:39 -0700 (PDT) Received: by mail-pf1-x42b.google.com with SMTP id a26so8587938pfr.11; Fri, 29 Oct 2021 01:42:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=C3XMXzTTygfdBeK2JyqdF6vIrHQ8G5mvg/kl4VcsIbg=; b=jBUVynEeI1e9ALj2JFYhCCejMk+nXG3F+yq9APtppnBOqZzIfpQB/f8Fau7TLpHtSi oA6eu004rC95LxkQgdrgAwK656dUugniWT03RRQ17Apb8H5qh5YcONYGX0xh08cq1wan ZduDRLTd66s7mJS8KUirW06+dv4xFv5+mDlGinewdqJgrjkMXV6caztcy68f0ZIqJ9Fj E6uX1CMoYqKyMnPAGVqMm+rWWalNbrHOfQ7//1S5NZLBTuudSAeydzTlr+jQJ6dnJyiY nL/v5FrIBIa7JZM4BSjaQk9QRJDn62dc0oP9WyKDwesk21aIMdJ3KfdyP9q2H+DM6SaK hIwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=C3XMXzTTygfdBeK2JyqdF6vIrHQ8G5mvg/kl4VcsIbg=; b=6PQOBewwkmCDasKmnykxaKqQRHQd+iZd2jNpK/Mzuh1T3QdwKl7R9qR/+NeWQrIJ72 zJyFk17qlIkHnR1sqS0FbympdKqqhdoxLdY93wrsdH4+c1Av3qr7uO3FERUzX0UcTvyv dyIPzNmXnnqvUCD1APUuK/wnVYVkjTWbNDZ2ZD/iTcif8OCl5gMnFu6Vd8ieBhM75bA7 PZnVvB3f61bYoGs1ABpCPVDJmBXQOxFEttQxibIGw0ELHm7VRyioB0QE5IqkB8MAI5cW +97XimJ8Ti7YP54J/FQQYy/YxTc2df4Y95e9SypisfPOkzzz/6E1gq6wMgsg5gGtvHYW BLhA== X-Gm-Message-State: AOAM531DjaZW3tQLU+IiO9E7bmG6FWHkdlufqOJMjAssSIQ94sW1KW7V uEQGrE3qgCsFdy6s09s1xR8= X-Google-Smtp-Source: ABdhPJx/XNsgQgR2mohctCTqYvWsFEyFavL1Na2l19roF7C/7thp2MGcrQILJZB1QfNiXevypMbS7g== X-Received: by 2002:a63:9316:: with SMTP id b22mr7227075pge.463.1635496958914; Fri, 29 Oct 2021 01:42:38 -0700 (PDT) Received: from scdiu3.sunplus.com ([113.196.136.192]) by smtp.googlemail.com with ESMTPSA id ml14sm1471424pjb.43.2021.10.29.01.42.36 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 29 Oct 2021 01:42:38 -0700 (PDT) From: "LH.Kuo" X-Google-Original-From: "LH.Kuo" To: p.zabel@pengutronix.de, robh+dt@kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, devicetree@vger.kernel.org Cc: dvorkin@tibbo.com, qinjian@cqplus1.com, wells.lu@sunplus.com, "LH.Kuo" Subject: [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 Date: Fri, 29 Oct 2021 16:42:34 +0800 Message-Id: <1635496955-13985-2-git-send-email-lh.kuo@sunplus.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1635496955-13985-1-git-send-email-lh.kuo@sunplus.com> References: <1635496955-13985-1-git-send-email-lh.kuo@sunplus.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Add I2C driver for Sunplus SP7021. Signed-off-by: LH.Kuo --- MAINTAINERS | 38 +- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-sunplus.c | 1936 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1969 insertions(+), 16 deletions(-) create mode 100644 drivers/i2c/busses/i2c-sunplus.c diff --git a/MAINTAINERS b/MAINTAINERS index 80eebc1..c89a3b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3553,7 +3553,7 @@ N: kona BROADCOM BCM47XX MIPS ARCHITECTURE M: Hauke Mehrtens -M: Rafał Miłecki +M: Rafa? Mi?ecki L: linux-mips@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/mips/brcm/ @@ -3561,7 +3561,7 @@ F: arch/mips/bcm47xx/* F: arch/mips/include/asm/mach-bcm47xx/* BROADCOM BCM4908 ETHERNET DRIVER -M: Rafał Miłecki +M: Rafa? Mi?ecki M: bcm-kernel-feedback-list@broadcom.com L: netdev@vger.kernel.org S: Maintained @@ -3571,7 +3571,7 @@ F: drivers/net/ethernet/broadcom/unimac.h BROADCOM BCM5301X ARM ARCHITECTURE M: Hauke Mehrtens -M: Rafał Miłecki +M: Rafa? Mi?ecki M: bcm-kernel-feedback-list@broadcom.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -3581,7 +3581,7 @@ F: arch/arm/boot/dts/bcm953012* F: arch/arm/mach-bcm/bcm_5301x.c BROADCOM BCM53573 ARM ARCHITECTURE -M: Rafał Miłecki +M: Rafa? Mi?ecki L: bcm-kernel-feedback-list@broadcom.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -3800,7 +3800,7 @@ N: hr2 N: stingray BROADCOM IPROC GBIT ETHERNET DRIVER -M: Rafał Miłecki +M: Rafa? Mi?ecki M: bcm-kernel-feedback-list@broadcom.com L: netdev@vger.kernel.org S: Maintained @@ -3835,13 +3835,13 @@ F: drivers/infiniband/hw/bnxt_re/ F: include/uapi/rdma/bnxt_re-abi.h BROADCOM NVRAM DRIVER -M: Rafał Miłecki +M: Rafa? Mi?ecki L: linux-mips@vger.kernel.org S: Maintained F: drivers/firmware/broadcom/* BROADCOM PMB (POWER MANAGEMENT BUS) DRIVER -M: Rafał Miłecki +M: Rafa? Mi?ecki M: Florian Fainelli M: bcm-kernel-feedback-list@broadcom.com L: linux-pm@vger.kernel.org @@ -3851,7 +3851,7 @@ F: drivers/soc/bcm/bcm63xx/bcm-pmb.c F: include/dt-bindings/soc/bcm-pmb.h BROADCOM SPECIFIC AMBA DRIVER (BCMA) -M: Rafał Miłecki +M: Rafa? Mi?ecki L: linux-wireless@vger.kernel.org S: Maintained F: drivers/bcma/ @@ -6947,7 +6947,7 @@ W: http://www.broadcom.com F: drivers/scsi/elx/ ENE CB710 FLASH CARD READER DRIVER -M: Michał Mirosław +M: Micha? Miros?aw S: Maintained F: drivers/misc/cb710/ F: drivers/mmc/host/cb710-mmc.* @@ -7389,7 +7389,7 @@ F: include/uapi/video/ F: include/video/ FREESCALE CAAM (Cryptographic Acceleration and Assurance Module) DRIVER -M: Horia Geantă +M: Horia Geant? M: Pankaj Gupta L: linux-crypto@vger.kernel.org S: Maintained @@ -7926,7 +7926,7 @@ F: fs/gfs2/ F: include/uapi/linux/gfs2_ondisk.h GIGABYTE WMI DRIVER -M: Thomas Weißschuh +M: Thomas Wei?schuh L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/gigabyte-wmi.c @@ -8871,7 +8871,7 @@ S: Maintained F: drivers/i2c/i2c-stub.c I3C DRIVER FOR CADENCE I3C MASTER IP -M: Przemysław Gaj +M: Przemys?aw Gaj S: Maintained F: Documentation/devicetree/bindings/i3c/cdns,i3c-master.txt F: drivers/i3c/master/i3c-master-cdns.c @@ -14456,7 +14456,7 @@ F: drivers/pci/controller/pci-v3-semi.c PCI ENDPOINT SUBSYSTEM M: Kishon Vijay Abraham I M: Lorenzo Pieralisi -R: Krzysztof Wilczyński +R: Krzysztof Wilczy?ski L: linux-pci@vger.kernel.org S: Supported F: Documentation/PCI/endpoint/* @@ -14504,7 +14504,7 @@ F: drivers/pci/controller/pci-xgene-msi.c PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS M: Lorenzo Pieralisi R: Rob Herring -R: Krzysztof Wilczyński +R: Krzysztof Wilczy?ski L: linux-pci@vger.kernel.org S: Supported Q: http://patchwork.ozlabs.org/project/linux-pci/list/ @@ -16490,7 +16490,7 @@ F: Documentation/devicetree/bindings/rng/samsung,exynos4-rng.yaml F: drivers/crypto/exynos-rng.c SAMSUNG EXYNOS TRUE RANDOM NUMBER GENERATOR (TRNG) DRIVER -M: Łukasz Stelmach +M: ?ukasz Stelmach L: linux-samsung-soc@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.yaml @@ -16504,7 +16504,7 @@ F: drivers/video/fbdev/s3c-fb.c SAMSUNG INTERCONNECT DRIVERS M: Sylwester Nawrocki -M: Artur Świgoń +M: Artur ?wigo? L: linux-pm@vger.kernel.org L: linux-samsung-soc@vger.kernel.org S: Supported @@ -17947,6 +17947,12 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/dlink/sundance.c +SUNPLUS I2C CONTROLLER INTERFACE DRIVER +M: LH Kuo +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-sunplus.c + SUPERH M: Yoshinori Sato M: Rich Felker diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e17790f..a4d6074 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1371,6 +1371,16 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. +config I2C_SUNPLUS + tristate "Sunplus I2C driver" + depends on SOC_SP7021 || SOC_Q645 + help + Say Y here to include support for I2C controller in the + Sunplus SoCs. + + This driver can also be built as a module. If so, the module will be + called as i2c-sunplus. + config I2C_OPAL tristate "IBM OPAL I2C driver" depends on PPC_POWERNV diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1336b04..56ea06b 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -146,5 +146,6 @@ 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 +obj-$(CONFIG_I2C_SUNPLUS) += i2c-sunplus.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-sunplus.c b/drivers/i2c/busses/i2c-sunplus.c new file mode 100644 index 0000000..d7f2111 --- /dev/null +++ b/drivers/i2c/busses/i2c-sunplus.c @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Sunplus Inc. + * Author: LH Kuo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM_RUNTIME_I2C +#include +#endif + + +//#define I2C_RETEST + +//#define I2C_FUNC_DEBUG +//#define I2C_DBG_INFO +#define I2C_DBG_ERR + +#ifdef I2C_FUNC_DEBUG + #define FUNC_DEBUG() pr_info("[I2C] Debug: %s(%d)\n", __func__, __LINE__) +#else + #define FUNC_DEBUG() +#endif + +#ifdef I2C_DBG_INFO + #define DBG_INFO(fmt, args ...) pr_info("[I2C] Info (%d): " fmt"\n", __LINE__, ## args) +#else + #define DBG_INFO(fmt, args ...) +#endif + +#ifdef I2C_DBG_ERR + #define DBG_ERR(fmt, args ...) pr_info("[I2C] Error (%d): " fmt"\n", __LINE__, ## args) +#else + #define DBG_ERR(fmt, args ...) +#endif + +#define I2C_FREQ 400 +#define I2C_SLEEP_TIMEOUT 200 +#define I2C_SCL_DELAY 1 //SCl dalay xT + +#define I2C_CLK_SOURCE_FREQ 27000 // KHz(27MHz) +#define I2C_BUFFER_SIZE 1024 // Byte + + + +#define I2CM_REG_NAME "i2cm" + +#define DEVICE_NAME "sp7021-i2cm" + +#define I2CM_DMA_REG_NAME "i2cmdma" + +#define I2C_MASTER_NUM (4) + +//burst write use +#define I2C_EMPTY_THRESHOLD_VALUE 4 + +//burst read use +#define I2C_IS_READ16BYTE + +#ifdef I2C_IS_READ16BYTE +#define I2C_BURST_RDATA_BYTES 16 +#define I2C_BURST_RDATA_FLAG 0x80008000 +#else +#define I2C_BURST_RDATA_BYTES 4 +#define I2C_BURST_RDATA_FLAG 0x88888888 +#endif + +#define I2C_BURST_RDATA_ALL_FLAG 0xFFFFFFFF + + +//control0 +#define I2C_CTL0_FREQ(x) (x<<24) //bit[26:24] +#define I2C_CTL0_PREFETCH (1<<18) +#define I2C_CTL0_RESTART_EN (1<<17) //0:disable 1:enable +#define I2C_CTL0_SUBADDR_EN (1<<16) //For restart mode need to set high +#define I2C_CTL0_SW_RESET (1<<15) +#define I2C_CTL0_SLAVE_ADDR(x) (x<<1) //bit[7:1] + +//control1 +#define I2C_CTL1_ALL_CLR (0x3FF) +#define I2C_CTL1_EMPTY_CLR (1<<9) +#define I2C_CTL1_SCL_HOLD_TOO_LONG_CLR (1<<8) +#define I2C_CTL1_SCL_WAIT_CLR (1<<7) +#define I2C_CTL1_EMPTY_THRESHOLD_CLR (1<<6) +#define I2C_CTL1_DATA_NACK_CLR (1<<5) +#define I2C_CTL1_ADDRESS_NACK_CLR (1<<4) +#define I2C_CTL1_BUSY_CLR (1<<3) +#define I2C_CTL1_CLKERR_CLR (1<<2) +#define I2C_CTL1_DONE_CLR (1<<1) +#define I2C_CTL1_SIFBUSY_CLR (1<<0) + +//control2 +#define I2C_CTL2_FREQ_CUSTOM(x) (x<<0) //bit[10:0] +#define I2C_CTL2_SCL_DELAY(x) (x<<24) //bit[25:24] +#define I2C_CTL2_SDA_HALF_ENABLE (1<<31) + +//control7 +#define I2C_CTL7_RDCOUNT(x) (x<<16) //bit[31:16] +#define I2C_CTL7_WRCOUNT(x) (x<<0) //bit[15:0] + + +#define I2C_CTL0_FREQ_MASK (0x7) // 3 bit +#define I2C_CTL0_SLAVE_ADDR_MASK (0x7F) // 7 bit +#define I2C_CTL2_FREQ_CUSTOM_MASK (0x7FF) // 11 bit +#define I2C_CTL2_SCL_DELAY_MASK (0x3) // 2 bit +#define I2C_CTL7_RW_COUNT_MASK (0xFFFF) // 16 bit +#define I2C_EN0_CTL_EMPTY_THRESHOLD_MASK (0x7) // 3 bit +#define I2C_SG_DMA_LLI_INDEX_MASK (0x1F) // 5 bit + +//interrupt enable1 +#define I2C_EN1_BURST_RDATA_INT (0x80008000) //must sync with GET_BYTES_EACHTIME + +//interrupt enable2 +#define I2C_EN2_BURST_RDATA_OVERFLOW_INT (0xFFFFFFFF) + +//i2c master mode +#define I2C_MODE_DMA_MODE (1<<2) +#define I2C_MODE_MANUAL_MODE (1<<1) //0:trigger mode 1:auto mode +#define I2C_MODE_MANUAL_TRIG (1<<0) + + +//dma config +#define I2C_DMA_CFG_DMA_GO (1<<8) +#define I2C_DMA_CFG_NON_BUF_MODE (1<<2) +#define I2C_DMA_CFG_SAME_SLAVE (1<<1) +#define I2C_DMA_CFG_DMA_MODE (1<<0) + +//dma interrupt flag +#define I2C_DMA_INT_LENGTH0_FLAG (1<<6) +#define I2C_DMA_INT_THRESHOLD_FLAG (1<<5) +#define I2C_DMA_INT_IP_TIMEOUT_FLAG (1<<4) +#define I2C_DMA_INT_GDMA_TIMEOUT_FLAG (1<<3) +#define I2C_DMA_INT_WB_EN_ERROR_FLAG (1<<2) +#define I2C_DMA_INT_WCNT_ERROR_FLAG (1<<1) +#define I2C_DMA_INT_DMA_DONE_FLAG (1<<0) + +//dma interrupt enable +#define I2C_DMA_EN_LENGTH0_INT (1<<6) +#define I2C_DMA_EN_THRESHOLD_INT (1<<5) +#define I2C_DMA_EN_IP_TIMEOUT_INT (1<<4) +#define I2C_DMA_EN_GDMA_TIMEOUT_INT (1<<3) +#define I2C_DMA_EN_WB_EN_ERROR_INT (1<<2) +#define I2C_DMA_EN_WCNT_ERROR_INT (1<<1) +#define I2C_DMA_EN_DMA_DONE_INT (1<<0) + +//interrupt +#define I2C_INT_RINC_INDEX(x) (x<<18) //bit[20:18] +#define I2C_INT_WINC_INDEX(x) (x<<15) //bit[17:15] +#define I2C_INT_SCL_HOLD_TOO_LONG_FLAG (1<<11) +#define I2C_INT_WFIFO_ENABLE (1<<10) +#define I2C_INT_FULL_FLAG (1<<9) +#define I2C_INT_EMPTY_FLAG (1<<8) +#define I2C_INT_SCL_WAIT_FLAG (1<<7) +#define I2C_INT_EMPTY_THRESHOLD_FLAG (1<<6) +#define I2C_INT_DATA_NACK_FLAG (1<<5) +#define I2C_INT_ADDRESS_NACK_FLAG (1<<4) +#define I2C_INT_BUSY_FLAG (1<<3) +#define I2C_INT_CLKERR_FLAG (1<<2) +#define I2C_INT_DONE_FLAG (1<<1) +#define I2C_INT_SIFBUSY_FLAG (1<<0) + + +//interrupt enable0 +#define I2C_EN0_SCL_HOLD_TOO_LONG_INT (1<<13) +#define I2C_EN0_NACK_INT (1<<12) +#define I2C_EN0_CTL_EMPTY_THRESHOLD(x) (x<<9) //bit[11:9] +#define I2C_EN0_EMPTY_INT (1<<8) +#define I2C_EN0_SCL_WAIT_INT (1<<7) +#define I2C_EN0_EMPTY_THRESHOLD_INT (1<<6) +#define I2C_EN0_DATA_NACK_INT (1<<5) +#define I2C_EN0_ADDRESS_NACK_INT (1<<4) +#define I2C_EN0_BUSY_INT (1<<3) +#define I2C_EN0_CLKERR_INT (1<<2) +#define I2C_EN0_DONE_INT (1<<1) +#define I2C_EN0_SIFBUSY_INT (1<<0) + + +#define I2C_RESET(id, val) ((1 << (16 + id)) | (val << id)) +#define I2C_CLKEN(id, val) ((1 << (16 + id)) | (val << id)) +#define I2C_GCLKEN(id, val) ((1 << (16 + id)) | (val << id)) + + +struct regs_i2cm_s { + unsigned int control0; /* 00 */ + unsigned int control1; /* 01 */ + unsigned int control2; /* 02 */ + unsigned int control3; /* 03 */ + unsigned int control4; /* 04 */ + unsigned int control5; /* 05 */ + unsigned int i2cm_status0; /* 06 */ + unsigned int interrupt; /* 07 */ + unsigned int int_en0; /* 08 */ + unsigned int i2cm_mode; /* 09 */ + unsigned int i2cm_status1; /* 10 */ + unsigned int i2cm_status2; /* 11 */ + unsigned int control6; /* 12 */ + unsigned int int_en1; /* 13 */ + unsigned int i2cm_status3; /* 14 */ + unsigned int i2cm_status4; /* 15 */ + unsigned int int_en2; /* 16 */ + unsigned int control7; /* 17 */ + unsigned int control8; /* 18 */ + unsigned int control9; /* 19 */ + unsigned int reserved[3]; /* 20~22 */ + unsigned int version; /* 23 */ + unsigned int data00_03; /* 24 */ + unsigned int data04_07; /* 25 */ + unsigned int data08_11; /* 26 */ + unsigned int data12_15; /* 27 */ + unsigned int data16_19; /* 28 */ + unsigned int data20_23; /* 29 */ + unsigned int data24_27; /* 30 */ + unsigned int data28_31; /* 31 */ +}; + +struct regs_i2cm_dma_s { + unsigned int hw_version; /* 00 */ + unsigned int dma_config; /* 01 */ + unsigned int dma_length; /* 02 */ + unsigned int dma_addr; /* 03 */ + unsigned int port_mux; /* 04 */ + unsigned int int_flag; /* 05 */ + unsigned int int_en; /* 06 */ + unsigned int sw_reset_state; /* 07 */ + unsigned int reserved[2]; /* 08~09 */ + unsigned int sg_dma_index; /* 10 */ + unsigned int sg_dma_config; /* 11 */ + unsigned int sg_dma_length; /* 12 */ + unsigned int sg_dma_addr; /* 13 */ + unsigned int reserved2; /* 14 */ + unsigned int sg_setting; /* 15 */ + unsigned int threshold; /* 16 */ + unsigned int reserved3; /* 17 */ + unsigned int gdma_read_timeout; /* 18 */ + unsigned int gdma_write_timeout; /* 19 */ + unsigned int ip_read_timeout; /* 20 */ + unsigned int ip_write_timeout; /* 21 */ + unsigned int write_cnt_debug; /* 22 */ + unsigned int w_byte_en_debug; /* 23 */ + unsigned int sw_reset_write_cnt_debug; /* 24 */ + unsigned int reserved4[7]; /* 25~31 */ +}; + +enum I2C_Status_e_ { + I2C_SUCCESS, /* successful */ + I2C_ERR_I2C_BUSY, /* I2C is busy */ + I2C_ERR_INVALID_DEVID, /* device id is invalid */ + I2C_ERR_INVALID_CNT, /* read or write count is invalid */ + I2C_ERR_TIMEOUT_OUT, /* wait timeout */ + I2C_ERR_RECEIVE_NACK, /* receive NACK */ + I2C_ERR_FIFO_EMPTY, /* FIFO empty */ + I2C_ERR_SCL_HOLD_TOO_LONG, /* SCL hlod too long */ + I2C_ERR_RDATA_OVERFLOW, /* rdata overflow */ + I2C_ERR_INVALID_STATE, /* read write state is invalid */ + I2C_ERR_REQUESET_IRQ, /* request irq failed */ +}; + +enum I2C_State_e_ { + I2C_WRITE_STATE, /* i2c is write */ + I2C_READ_STATE, /* i2c is read */ + I2C_IDLE_STATE, /* i2c is idle */ + I2C_DMA_WRITE_STATE,/* i2c is dma write */ + I2C_DMA_READ_STATE, /* i2c is dma read */ +}; + +enum I2C_switch_e_ { + I2C_POWER_ALL_SWITCH, + I2C_POWER_NO_SWITCH, +}; + +struct I2C_Cmd_t_ { + unsigned int dDevId; + unsigned int dFreq; + unsigned int dSlaveAddr; + unsigned int dRestartEn; + unsigned int dWrDataCnt; + unsigned int dRdDataCnt; + unsigned char *pWrData; + unsigned char *pRdData; +}; + +struct I2C_Irq_Dma_Flag_t_ { + unsigned char bDmaDone; + unsigned char bWCntError; + unsigned char bWBEnError; + unsigned char bGDmaTimeout; + unsigned char bIPTimeout; + unsigned char bThreshold; + unsigned char bLength0; +}; + +struct I2C_Irq_Flag_t_ { + unsigned char bActiveDone; + unsigned char bAddrNack; + unsigned char bDataNack; + unsigned char bEmptyThreshold; + unsigned char bFiFoEmpty; + unsigned char bFiFoFull; + unsigned char bSCLHoldTooLong; + unsigned char bRdOverflow; +}; + +struct I2C_Irq_Event_t_ { + enum I2C_State_e_ eRWState; + struct I2C_Irq_Flag_t_ stIrqFlag; + struct I2C_Irq_Dma_Flag_t_ stIrqDmaFlag; + unsigned int dDevId; + unsigned int dBurstCount; + unsigned int dBurstRemainder; + unsigned int dDataIndex; + unsigned int dDataTotalLen; + unsigned int dRegDataIndex; + unsigned char bI2CBusy; + unsigned char bRet; + unsigned char *pDataBuf; +}; + + +enum I2C_DMA_RW_Mode_e_ { + I2C_DMA_WRITE_MODE, + I2C_DMA_READ_MODE, +}; + +enum I2C_RW_Mode_e_ { + I2C_WRITE_MODE, + I2C_READ_MODE, + I2C_RESTART_MODE, +}; + +enum I2C_Active_Mode_e_ { + I2C_TRIGGER, + I2C_AUTO, +}; + + +struct i2c_compatible { + int mode; /* clk source switch*/ +}; + +struct SpI2C_If_t_ { + struct i2c_msg *msgs; /* messages currently handled */ + struct i2c_adapter adap; + struct device *dev; + struct I2C_Cmd_t_ stCmdInfo; + struct I2C_Irq_Event_t_ stIrqEvent; + void __iomem *i2c_regs; + + struct clk *clk; + struct reset_control *rstc; + unsigned int i2c_clk_freq; + int irq; + wait_queue_head_t wait; + + void __iomem *i2c_dma_regs; + dma_addr_t dma_phy_base; + void *dma_vir_base; + unsigned int mode; +}; + + + +#ifdef I2C_RETEST +int test_count; +#endif + + + +void sp_i2cm_status_clear(struct regs_i2cm_s *sr, unsigned int flag) +{ + unsigned int ctl1; + + ctl1 = readl(&sr->control1); + ctl1 |= flag; + writel(ctl1, &sr->control1); + + ctl1 = readl(&sr->control1); + ctl1 &= (~flag); + writel(ctl1, &sr->control1); +} + +void sp_i2cm_dma_int_flag_clear(struct regs_i2cm_dma_s *sr_dma, unsigned int flag) +{ + unsigned int val; + + val = readl(&sr_dma->int_flag); + val |= flag; + writel(val, &sr_dma->int_flag); +} + +void sp_i2cm_reset(struct regs_i2cm_s *sr) +{ + unsigned int ctl0; + + ctl0 = readl(&sr->control0); + ctl0 |= I2C_CTL0_SW_RESET; + writel(ctl0, &sr->control0); + + udelay(2); +} + +void sp_i2cm_data0_set(struct regs_i2cm_s *sr, unsigned int *wdata) +{ + writel(*wdata, &sr->data00_03); +} + + +void sp_i2cm_int_en0_disable(struct regs_i2cm_s *sr, unsigned int int0) +{ + unsigned int val; + + val = readl(&sr->int_en0); + val &= (~int0); + writel(val, &sr->int_en0); + +} + +void sp_i2cm_rdata_flag_get(struct regs_i2cm_s *sr, unsigned int *flag) +{ + *flag = readl(&sr->i2cm_status3); +} + +void sp_i2cm_data_get(struct regs_i2cm_s *sr, unsigned int index, unsigned int *rdata) +{ + switch (index) { + case 0: + *rdata = readl(&sr->data00_03); + break; + + case 1: + *rdata = readl(&sr->data04_07); + break; + + case 2: + *rdata = readl(&sr->data08_11); + break; + + case 3: + *rdata = readl(&sr->data12_15); + break; + + case 4: + *rdata = readl(&sr->data16_19); + break; + + case 5: + *rdata = readl(&sr->data20_23); + break; + + case 6: + *rdata = readl(&sr->data24_27); + break; + + case 7: + *rdata = readl(&sr->data28_31); + break; + + default: + break; + } +} + +void sp_i2cm_rdata_flag_clear(struct regs_i2cm_s *sr, unsigned int flag) +{ + writel(flag, &sr->control6); + writel(0, &sr->control6); +} + +void sp_i2cm_clock_freq_set(struct regs_i2cm_s *sr, unsigned int freq) +{ + unsigned int div; + unsigned int ctl0, ctl2; + + div = I2C_CLK_SOURCE_FREQ / freq; + div -= 1; + if (I2C_CLK_SOURCE_FREQ % freq != 0) + div += 1; + + if (div > I2C_CTL2_FREQ_CUSTOM_MASK) + div = I2C_CTL2_FREQ_CUSTOM_MASK; + + ctl0 = readl(&sr->control0); + ctl0 &= (~I2C_CTL0_FREQ(I2C_CTL0_FREQ_MASK)); + writel(ctl0, &sr->control0); + + ctl2 = readl(&sr->control2); + ctl2 &= (~I2C_CTL2_FREQ_CUSTOM(I2C_CTL2_FREQ_CUSTOM_MASK)); + ctl2 |= I2C_CTL2_FREQ_CUSTOM(div); + writel(ctl2, &sr->control2); + +} + +void sp_i2cm_slave_addr_set(struct regs_i2cm_s *sr, unsigned int addr) +{ + unsigned int t_addr = addr & I2C_CTL0_SLAVE_ADDR_MASK; + unsigned int ctl0; + + ctl0 = readl(&sr->control0); + ctl0 &= (~I2C_CTL0_SLAVE_ADDR(I2C_CTL0_SLAVE_ADDR_MASK)); + ctl0 |= I2C_CTL0_SLAVE_ADDR(t_addr); + writel(ctl0, &sr->control0); +} + +void sp_i2cm_scl_delay_set(struct regs_i2cm_s *sr, unsigned int delay) +{ + unsigned int ctl2; + + ctl2 = readl(&sr->control2); + ctl2 &= (~I2C_CTL2_SCL_DELAY(I2C_CTL2_SCL_DELAY_MASK)); + ctl2 |= I2C_CTL2_SCL_DELAY(delay); + ctl2 &= (~(I2C_CTL2_SDA_HALF_ENABLE)); + writel(ctl2, &sr->control2); +} + +void sp_i2cm_trans_cnt_set(struct regs_i2cm_s *sr, unsigned int write_cnt, + unsigned int read_cnt) +{ + unsigned int t_write = write_cnt & I2C_CTL7_RW_COUNT_MASK; + unsigned int t_read = read_cnt & I2C_CTL7_RW_COUNT_MASK; + unsigned int ctl7; + + ctl7 = I2C_CTL7_WRCOUNT(t_write) | I2C_CTL7_RDCOUNT(t_read); + writel(ctl7, &sr->control7); +} + +void sp_i2cm_active_mode_set(struct regs_i2cm_s *sr, enum I2C_Active_Mode_e_ mode) +{ + unsigned int val; + + val = readl(&sr->i2cm_mode); + val &= (~(I2C_MODE_MANUAL_MODE | I2C_MODE_MANUAL_TRIG)); + switch (mode) { + default: + case I2C_TRIGGER: + break; + + case I2C_AUTO: + val |= I2C_MODE_MANUAL_MODE; + break; + } + writel(val, &sr->i2cm_mode); +} + +void sp_i2cm_data_set(struct regs_i2cm_s *sr, unsigned int *wdata) +{ + writel(wdata[0], &sr->data00_03); + writel(wdata[1], &sr->data04_07); + writel(wdata[2], &sr->data08_11); + writel(wdata[3], &sr->data12_15); + writel(wdata[4], &sr->data16_19); + writel(wdata[5], &sr->data20_23); + writel(wdata[6], &sr->data24_27); + writel(wdata[7], &sr->data28_31); +} + +void sp_i2cm_rw_mode_set(struct regs_i2cm_s *sr, enum I2C_RW_Mode_e_ rw_mode) +{ + unsigned int ctl0; + + ctl0 = readl(&sr->control0); + switch (rw_mode) { + default: + case I2C_WRITE_MODE: + ctl0 &= (~(I2C_CTL0_PREFETCH | I2C_CTL0_RESTART_EN | I2C_CTL0_SUBADDR_EN)); + break; + + case I2C_READ_MODE: + ctl0 &= (~(I2C_CTL0_RESTART_EN | I2C_CTL0_SUBADDR_EN)); + ctl0 |= I2C_CTL0_PREFETCH; + break; + + case I2C_RESTART_MODE: + ctl0 |= (I2C_CTL0_PREFETCH | I2C_CTL0_RESTART_EN | I2C_CTL0_SUBADDR_EN); + break; + } + writel(ctl0, &sr->control0); +} + + +void sp_i2cm_int_en0_set(struct regs_i2cm_s *sr, unsigned int int0) +{ + writel(int0, &sr->int_en0); +} + +void sp_i2cm_int_en1_set(struct regs_i2cm_s *sr, unsigned int rdata_en) +{ + writel(rdata_en, &sr->int_en1); +} + +void sp_i2cm_int_en2_set(struct regs_i2cm_s *sr, unsigned int overflow_en) +{ + writel(overflow_en, &sr->int_en2); +} + + +#define MOON0_BASE 0xF8000000 + +struct Moon_RegBase_s { + void __iomem *moon0_regs; +} Moon_RegBase; + +struct Moon_RegBase_s stMoonRegBase; + +struct regs_moon0_s { + unsigned int stamp; /* 00 */ + unsigned int clken[10]; /* 01~10 */ + unsigned int gclken[10]; /* 11~20 */ + unsigned int reset[10]; /* 21~30 */ + unsigned int sfg_cfg_mode; /* 31 */ +} regs_moon0; + + +void sp_i2cm_enable(unsigned int device_id, void __iomem *membase) +{ + struct regs_moon0_s *pMoon0Reg = (struct regs_moon0_s *)membase; + + writel(I2C_RESET(device_id, 0), &(pMoon0Reg->reset[3])); + writel(I2C_CLKEN(device_id, 1), &(pMoon0Reg->clken[3])); + writel(I2C_GCLKEN(device_id, 0), &(pMoon0Reg->gclken[3])); +} + +void sp_i2cm_manual_trigger(struct regs_i2cm_s *sr) +{ + unsigned int val; + + val = readl(&sr->i2cm_mode); + val |= I2C_MODE_MANUAL_TRIG; + writel(val, &sr->i2cm_mode); +} + +void sp_i2cm_int_en0_with_thershold_set(struct regs_i2cm_s *sr, + unsigned int int0, unsigned char threshold) +{ + unsigned int val; + + val = (int0 | I2C_EN0_CTL_EMPTY_THRESHOLD(threshold)); + writel(val, &sr->int_en0); +} + +void sp_i2cm_dma_mode_enable(struct regs_i2cm_s *sr) +{ + unsigned int val; + + val = readl(&sr->i2cm_mode); + val |= I2C_MODE_DMA_MODE; + writel(val, &sr->i2cm_mode); +} + +void sp_i2cm_dma_addr_set(struct regs_i2cm_dma_s *sr_dma, unsigned int addr) +{ + writel(addr, &sr_dma->dma_addr); +} + +void sp_i2cm_dma_length_set(struct regs_i2cm_dma_s *sr_dma, unsigned int length) +{ + length &= (0xFFFF); //only support 16 bit + writel(length, &sr_dma->dma_length); +} + +void sp_i2cm_dma_rw_mode_set(struct regs_i2cm_dma_s *sr_dma, + enum I2C_DMA_RW_Mode_e_ rw_mode) +{ + unsigned int val; + + val = readl(&sr_dma->dma_config); + switch (rw_mode) { + default: + case I2C_DMA_WRITE_MODE: + val |= I2C_DMA_CFG_DMA_MODE; + break; + + case I2C_DMA_READ_MODE: + val &= (~I2C_DMA_CFG_DMA_MODE); + break; + } + writel(val, &sr_dma->dma_config); + +} + +void sp_i2cm_dma_int_en_set(struct regs_i2cm_dma_s *sr_dma, unsigned int dma_int) +{ + writel(dma_int, &sr_dma->int_en); +} + +void sp_i2cm_dma_go_set(struct regs_i2cm_dma_s *sr_dma) +{ + unsigned int val; + + val = readl(&sr_dma->dma_config); + val |= I2C_DMA_CFG_DMA_GO; + writel(val, &sr_dma->dma_config); +} + + +static void _sp_i2cm_intflag_check(struct SpI2C_If_t_ *pstSpI2CInfo, + struct I2C_Irq_Event_t_ *pstIrqEvent) +{ + + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + unsigned int int_flag = 0; + unsigned int overflow_flag = 0; + #ifdef I2C_RETEST + unsigned int scl_belay = 0; + #endif + + + int_flag = readl(&sr->interrupt); + + if (int_flag & I2C_INT_DONE_FLAG) { + DBG_INFO("I2C is done !!\n"); + pstIrqEvent->stIrqFlag.bActiveDone = 1; + } else { + pstIrqEvent->stIrqFlag.bActiveDone = 0; + } + + if (int_flag & I2C_INT_ADDRESS_NACK_FLAG) { + DBG_INFO("I2C slave address NACK !!\n"); + #ifdef I2C_RETEST + + scl_belay = readl(&sr->control2); + scl_belay &= I2C_CTL2_SCL_DELAY(I2C_CTL2_SCL_DELAY_MASK); + + if (scl_belay == 0x00) + test_count++; + else if (test_count > 9) + test_count = 1; + #endif + pstIrqEvent->stIrqFlag.bAddrNack = 1; + } else { + pstIrqEvent->stIrqFlag.bAddrNack = 0; + } + + if (int_flag & I2C_INT_DATA_NACK_FLAG) { + DBG_INFO("I2C slave data NACK !!\n"); + pstIrqEvent->stIrqFlag.bDataNack = 1; + } else { + pstIrqEvent->stIrqFlag.bDataNack = 0; + } + + // write use + if (int_flag & I2C_INT_EMPTY_THRESHOLD_FLAG) { + DBG_INFO("I2C empty threshold occur !!\n"); + pstIrqEvent->stIrqFlag.bEmptyThreshold = 1; + } else { + pstIrqEvent->stIrqFlag.bEmptyThreshold = 0; + } + + // write use + if (int_flag & I2C_INT_EMPTY_FLAG) { + DBG_INFO("I2C FIFO empty occur !!\n"); + pstIrqEvent->stIrqFlag.bFiFoEmpty = 1; + } else { + pstIrqEvent->stIrqFlag.bFiFoEmpty = 0; + } + + // write use (for debug) + if (int_flag & I2C_INT_FULL_FLAG) { + DBG_INFO("I2C FIFO full occur !!\n"); + pstIrqEvent->stIrqFlag.bFiFoFull = 1; + } else { + pstIrqEvent->stIrqFlag.bFiFoFull = 0; + } + + if (int_flag & I2C_INT_SCL_HOLD_TOO_LONG_FLAG) { + DBG_INFO("I2C SCL hold too long occur !!\n"); + pstIrqEvent->stIrqFlag.bSCLHoldTooLong = 1; + } else { + pstIrqEvent->stIrqFlag.bSCLHoldTooLong = 0; + } + sp_i2cm_status_clear(sr, I2C_CTL1_ALL_CLR); + + // read use + overflow_flag = readl(&sr->i2cm_status4); + + if (overflow_flag) { + DBG_ERR("I2C burst read data overflow !! overflow_flag = 0x%x\n", overflow_flag); + pstIrqEvent->stIrqFlag.bRdOverflow = 1; + } else { + pstIrqEvent->stIrqFlag.bRdOverflow = 0; + } +} + +static void _sp_i2cm_dma_intflag_check(struct SpI2C_If_t_ *pstSpI2CInfo, + struct I2C_Irq_Event_t_ *pstIrqEvent) +{ + struct regs_i2cm_dma_s *sr_dma = (struct regs_i2cm_dma_s *)pstSpI2CInfo->i2c_dma_regs; + unsigned int int_flag = 0; + + int_flag = readl(&sr_dma->int_flag); + + if (int_flag & I2C_DMA_INT_DMA_DONE_FLAG) { + DBG_INFO("I2C DMA is done !!\n"); + pstIrqEvent->stIrqDmaFlag.bDmaDone = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bDmaDone = 0; + } + + if (int_flag & I2C_DMA_INT_WCNT_ERROR_FLAG) { + DBG_INFO("I2C DMA WCNT ERR !!\n"); + pstIrqEvent->stIrqDmaFlag.bWCntError = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bWCntError = 0; + } + if (int_flag & I2C_DMA_INT_WB_EN_ERROR_FLAG) { + DBG_INFO("I2C DMA WB EN ERR !!\n"); + pstIrqEvent->stIrqDmaFlag.bWBEnError = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bWBEnError = 0; + } + if (int_flag & I2C_DMA_INT_GDMA_TIMEOUT_FLAG) { + DBG_INFO("I2C DMA timeout !!\n"); + pstIrqEvent->stIrqDmaFlag.bGDmaTimeout = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bGDmaTimeout = 0; + } + if (int_flag & I2C_DMA_INT_IP_TIMEOUT_FLAG) { + DBG_INFO("I2C IP timeout !!\n"); + pstIrqEvent->stIrqDmaFlag.bIPTimeout = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bIPTimeout = 0; + } + if (int_flag & I2C_DMA_INT_LENGTH0_FLAG) { + DBG_INFO("I2C Length is zero !!\n"); + pstIrqEvent->stIrqDmaFlag.bLength0 = 1; + } else { + pstIrqEvent->stIrqDmaFlag.bLength0 = 0; + } + + sp_i2cm_dma_int_flag_clear(sr_dma, 0x7F); //write 1 to clear + +} + +static irqreturn_t _sp_i2cm_irqevent_handler(int irq, void *args) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = args; + struct I2C_Irq_Event_t_ *pstIrqEvent = &(pstSpI2CInfo->stIrqEvent); + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + unsigned char w_data[32] = {0}; + unsigned char r_data[I2C_BURST_RDATA_BYTES] = {0}; + unsigned int rdata_flag = 0; + unsigned int bit_index = 0; + int i = 0, j = 0, k = 0; + + _sp_i2cm_intflag_check(pstSpI2CInfo, pstIrqEvent); + +switch (pstIrqEvent->eRWState) { +case I2C_WRITE_STATE: +case I2C_DMA_WRITE_STATE: + if (pstIrqEvent->stIrqFlag.bActiveDone) { + DBG_INFO("I2C write success !!\n"); + pstIrqEvent->bRet = I2C_SUCCESS; + wake_up(&pstSpI2CInfo->wait); + } else if (pstIrqEvent->stIrqFlag.bAddrNack || pstIrqEvent->stIrqFlag.bDataNack) { + + if (pstIrqEvent->eRWState == I2C_DMA_WRITE_STATE) + DBG_ERR("DMA wtire NACK!!\n"); + else + DBG_ERR("wtire NACK!!\n"); + + pstIrqEvent->bRet = I2C_ERR_RECEIVE_NACK; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); + } else if (pstIrqEvent->stIrqFlag.bSCLHoldTooLong) { + DBG_ERR("I2C SCL hold too long !!\n"); + pstIrqEvent->bRet = I2C_ERR_SCL_HOLD_TOO_LONG; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); + } else if (pstIrqEvent->stIrqFlag.bFiFoEmpty) { + DBG_ERR("I2C FIFO empty !!\n"); + pstIrqEvent->bRet = I2C_ERR_FIFO_EMPTY; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); + } else if ((pstIrqEvent->dBurstCount > 0) && + (pstIrqEvent->eRWState == I2C_WRITE_STATE)) { + if (pstIrqEvent->stIrqFlag.bEmptyThreshold) { + for (i = 0; i < I2C_EMPTY_THRESHOLD_VALUE; i++) { + for (j = 0; j < 4; j++) { + + if (pstIrqEvent->dDataIndex >= pstIrqEvent->dDataTotalLen) + w_data[j] = 0; + else + w_data[j] = + pstIrqEvent->pDataBuf[pstIrqEvent->dDataIndex]; + + pstIrqEvent->dDataIndex++; + } + sp_i2cm_data0_set(sr, (unsigned int *)w_data); + pstIrqEvent->dBurstCount--; + if (pstIrqEvent->dBurstCount == 0) { + sp_i2cm_int_en0_disable(sr, + (I2C_EN0_EMPTY_THRESHOLD_INT | I2C_EN0_EMPTY_INT)); + break; + } + } + sp_i2cm_status_clear(sr, I2C_CTL1_EMPTY_THRESHOLD_CLR); + } + } + break; + +case I2C_READ_STATE: +case I2C_DMA_READ_STATE: + if (pstIrqEvent->stIrqFlag.bAddrNack || pstIrqEvent->stIrqFlag.bDataNack) { + + if (pstIrqEvent->eRWState == I2C_DMA_READ_STATE) + DBG_ERR("DMA read NACK!!\n"); + else + DBG_ERR("read NACK!!\n"); + + pstIrqEvent->bRet = I2C_ERR_RECEIVE_NACK; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); + } else if (pstIrqEvent->stIrqFlag.bSCLHoldTooLong) { + DBG_ERR("I2C SCL hold too long !!\n"); + pstIrqEvent->bRet = I2C_ERR_SCL_HOLD_TOO_LONG; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); + } else if (pstIrqEvent->stIrqFlag.bRdOverflow) { + DBG_ERR("I2C read data overflow !!\n"); + pstIrqEvent->bRet = I2C_ERR_RDATA_OVERFLOW; + pstIrqEvent->stIrqFlag.bActiveDone = 1; + wake_up(&pstSpI2CInfo->wait); + sp_i2cm_reset(sr); +} else { + if ((pstIrqEvent->dBurstCount > 0) && (pstIrqEvent->eRWState == I2C_READ_STATE)) { + sp_i2cm_rdata_flag_get(sr, &rdata_flag); + for (i = 0; i < (32 / I2C_BURST_RDATA_BYTES); i++) { + bit_index = (I2C_BURST_RDATA_BYTES - 1) + (I2C_BURST_RDATA_BYTES * i); + if (rdata_flag & (1 << bit_index)) { + for (j = 0; j < (I2C_BURST_RDATA_BYTES/4); j++) { + k = pstIrqEvent->dRegDataIndex + j; + if (k >= 8) + k -= 8; + + sp_i2cm_data_get(sr, + k, (unsigned int *)(&pstIrqEvent->pDataBuf[pstIrqEvent->dDataIndex])); + pstIrqEvent->dDataIndex += 4; + } + sp_i2cm_rdata_flag_clear(sr, + (((1 << I2C_BURST_RDATA_BYTES) - 1) << (I2C_BURST_RDATA_BYTES * i))); + pstIrqEvent->dRegDataIndex += (I2C_BURST_RDATA_BYTES / 4); + if (pstIrqEvent->dRegDataIndex >= 8) + pstIrqEvent->dBurstCount--; + } + } + } + if (pstIrqEvent->stIrqFlag.bActiveDone) { + if ((pstIrqEvent->dBurstRemainder) && + (pstIrqEvent->eRWState == I2C_READ_STATE)) { + j = 0; + for (i = 0; i < (I2C_BURST_RDATA_BYTES/4); i++) { + k = pstIrqEvent->dRegDataIndex + i; + if (k >= 8) + k -= 8; + + sp_i2cm_data_get(sr, k, (unsigned int *)(&r_data[j])); + j += 4; + } + + for (i = 0; i < pstIrqEvent->dBurstRemainder; i++) + pstIrqEvent->pDataBuf[pstIrqEvent->dDataIndex + i] + = r_data[i]; + } + + DBG_INFO("I2C read success !!\n"); + pstIrqEvent->bRet = I2C_SUCCESS; + wake_up(&pstSpI2CInfo->wait); + } + } + break; + +default: + break; +} + //switch case + + _sp_i2cm_dma_intflag_check(pstSpI2CInfo, pstIrqEvent); + + switch (pstIrqEvent->eRWState) { + case I2C_DMA_WRITE_STATE: + DBG_INFO("I2C_DMA_WRITE_STATE !!\n"); + if (pstIrqEvent->stIrqDmaFlag.bDmaDone) { + DBG_INFO("I2C dma write success !!\n"); + pstIrqEvent->bRet = I2C_SUCCESS; + wake_up(&pstSpI2CInfo->wait); + } + break; + + case I2C_DMA_READ_STATE: + DBG_INFO("I2C_DMA_READ_STATE !!\n"); + if (pstIrqEvent->stIrqDmaFlag.bDmaDone) { + DBG_INFO("I2C dma read success !!\n"); + pstIrqEvent->bRet = I2C_SUCCESS; + wake_up(&pstSpI2CInfo->wait); + } + break; + + default: + break; + } + + return IRQ_HANDLED; +} + + +static int _sp_i2cm_init(unsigned int device_id, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + + FUNC_DEBUG(); + + if (device_id >= I2C_MASTER_NUM) { + DBG_ERR("I2C device id is not correct !! device_id=%d\n", device_id); + return I2C_ERR_INVALID_DEVID; + } + + DBG_INFO("[I2C adapter] i2c_regs= 0x%x\n", (unsigned int)pstSpI2CInfo->i2c_regs); + DBG_INFO("[I2C adapter] i2c_dma_regs= 0x%x\n", (unsigned int)pstSpI2CInfo->i2c_dma_regs); + + sp_i2cm_reset(sr); + + return I2C_SUCCESS; +} + +static int _sp_i2cm_get_irq(struct platform_device *pdev, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + int irq; + + FUNC_DEBUG(); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + DBG_ERR("[I2C adapter] get irq number fail, irq = %d\n", irq); + return -ENODEV; + } + + pstSpI2CInfo->irq = irq; + return I2C_SUCCESS; +} + +static int _sp_i2cm_get_resources(struct platform_device *pdev, + struct SpI2C_If_t_ *pstSpI2CInfo) +{ + int ret; + struct resource *res; + + FUNC_DEBUG(); + + /* find and map our resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, I2CM_REG_NAME); + if (res) { + pstSpI2CInfo->i2c_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pstSpI2CInfo->i2c_regs)) + DBG_INFO("[I2C adapter] platform_get_resource_byname fail\n"); + } else { + DBG_ERR("[I2C adapter] %s (%d)\n", __func__, __LINE__); + return -ENODEV; + } + + + /* find DMA and map our resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, I2CM_DMA_REG_NAME); + if (res) { + pstSpI2CInfo->i2c_dma_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pstSpI2CInfo->i2c_dma_regs)) + DBG_INFO("[I2C adapter] platform_get_resource_byname fail\n"); + } else { + DBG_ERR("[I2C adapter] %s (%d)\n", __func__, __LINE__); + return -ENODEV; + } + + ret = _sp_i2cm_get_irq(pdev, pstSpI2CInfo); + if (ret) { + DBG_ERR("[I2C adapter] %s (%d) ret = %d\n", __func__, __LINE__, ret); + return ret; + } + + return I2C_SUCCESS; +} + +int sp_i2cm_read(struct I2C_Cmd_t_ *pstCmdInfo, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + struct I2C_Irq_Event_t_ *pstIrqEvent = &(pstSpI2CInfo->stIrqEvent); + unsigned char w_data[32] = {0}; + unsigned int read_cnt = 0; + unsigned int write_cnt = 0; + unsigned int burst_cnt = 0, burst_r = 0; + unsigned int int0 = 0, int1 = 0, int2 = 0; + int ret = I2C_SUCCESS; + int i = 0; + + FUNC_DEBUG(); + + if (pstCmdInfo->dDevId > I2C_MASTER_NUM) + return I2C_ERR_INVALID_DEVID; + + if (pstIrqEvent->bI2CBusy) { + DBG_ERR("I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(pstIrqEvent, 0, sizeof(*pstIrqEvent)); + pstIrqEvent->bI2CBusy = 1; + + write_cnt = pstCmdInfo->dWrDataCnt; + read_cnt = pstCmdInfo->dRdDataCnt; + + if (pstCmdInfo->dRestartEn) { + //if ((write_cnt > 32) || (write_cnt == 0)) { + if (write_cnt > 32) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C write count is invalid !! write count=%d\n", write_cnt); + return I2C_ERR_INVALID_CNT; + } + } + + if ((read_cnt > 0xFFFF) || (read_cnt == 0)) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C read count is invalid !! read count=%d\n", read_cnt); + return I2C_ERR_INVALID_CNT; + } + + burst_cnt = read_cnt / I2C_BURST_RDATA_BYTES; + burst_r = read_cnt % I2C_BURST_RDATA_BYTES; + DBG_INFO("write_cnt = %d, read_cnt = %d, burst_cnt = %d, burst_r = %d\n", + write_cnt, read_cnt, burst_cnt, burst_r); + + int0 = (I2C_EN0_SCL_HOLD_TOO_LONG_INT | I2C_EN0_EMPTY_INT | I2C_EN0_DATA_NACK_INT + | I2C_EN0_ADDRESS_NACK_INT | I2C_EN0_DONE_INT); + if (burst_cnt) { + int1 = I2C_BURST_RDATA_FLAG; + int2 = I2C_BURST_RDATA_ALL_FLAG; + } + + pstIrqEvent->eRWState = I2C_READ_STATE; + pstIrqEvent->dBurstCount = burst_cnt; + pstIrqEvent->dBurstRemainder = burst_r; + pstIrqEvent->dDataIndex = 0; + pstIrqEvent->dRegDataIndex = 0; + pstIrqEvent->dDataTotalLen = read_cnt; + pstIrqEvent->pDataBuf = pstCmdInfo->pRdData; + + //hal_i2cm_reset(pstCmdInfo->dDevId); + sp_i2cm_reset(sr); + sp_i2cm_clock_freq_set(sr, pstCmdInfo->dFreq); + sp_i2cm_slave_addr_set(sr, pstCmdInfo->dSlaveAddr); + #ifdef I2C_RETEST + if ((test_count > 1) && (test_count%3 == 0)) { + sp_i2cm_scl_delay_set(sr, 0x01); + DBG_INFO("test_count = %d", test_count); + } else { + sp_i2cm_scl_delay_set(sr, I2C_SCL_DELAY); + } + #endif + sp_i2cm_trans_cnt_set(sr, write_cnt, read_cnt); + sp_i2cm_active_mode_set(sr, I2C_TRIGGER); + + if (pstCmdInfo->dRestartEn) { + DBG_INFO("I2C_RESTART_MODE\n"); + for (i = 0; i < write_cnt; i++) + w_data[i] = pstCmdInfo->pWrData[i]; + + sp_i2cm_data_set(sr, (unsigned int *)w_data); + sp_i2cm_rw_mode_set(sr, I2C_RESTART_MODE); + } else { + DBG_INFO("I2C_READ_MODE\n"); + sp_i2cm_rw_mode_set(sr, I2C_READ_MODE); + } + + sp_i2cm_int_en0_set(sr, int0); + sp_i2cm_int_en1_set(sr, int1); + sp_i2cm_int_en2_set(sr, int2); + sp_i2cm_manual_trigger(sr); //start send data + + ret = wait_event_timeout(pstSpI2CInfo->wait, + pstIrqEvent->stIrqFlag.bActiveDone, (I2C_SLEEP_TIMEOUT * HZ) / 500); + if (ret == 0) { + DBG_ERR("I2C read timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = pstIrqEvent->bRet; + } + sp_i2cm_reset(sr); + pstIrqEvent->eRWState = I2C_IDLE_STATE; + pstIrqEvent->bI2CBusy = 0; + + return ret; +} + +int sp_i2cm_write(struct I2C_Cmd_t_ *pstCmdInfo, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + struct I2C_Irq_Event_t_ *pstIrqEvent = &(pstSpI2CInfo->stIrqEvent); + unsigned char w_data[32] = {0}; + unsigned int write_cnt = 0; + unsigned int burst_cnt = 0; + unsigned int int0 = 0; + int ret = I2C_SUCCESS; + int i = 0; + + FUNC_DEBUG(); + + if (pstCmdInfo->dDevId > I2C_MASTER_NUM) + return I2C_ERR_INVALID_DEVID; + + if (pstIrqEvent->bI2CBusy) { + DBG_ERR("I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(pstIrqEvent, 0, sizeof(*pstIrqEvent)); + pstIrqEvent->bI2CBusy = 1; + + write_cnt = pstCmdInfo->dWrDataCnt; + + if (write_cnt > 0xFFFF) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C write count is invalid !! write count=%d\n", write_cnt); + return I2C_ERR_INVALID_CNT; + } + + if (write_cnt > 32) { + burst_cnt = (write_cnt - 32) / 4; + if ((write_cnt - 32) % 4) + burst_cnt += 1; + for (i = 0; i < 32; i++) + w_data[i] = pstCmdInfo->pWrData[i]; + } else { + for (i = 0; i < write_cnt; i++) + w_data[i] = pstCmdInfo->pWrData[i]; + } + DBG_INFO("write_cnt = %d, burst_cnt = %d\n", write_cnt, burst_cnt); + + int0 = (I2C_EN0_SCL_HOLD_TOO_LONG_INT | I2C_EN0_EMPTY_INT | I2C_EN0_DATA_NACK_INT + | I2C_EN0_ADDRESS_NACK_INT | I2C_EN0_DONE_INT); + + if (burst_cnt) + int0 |= I2C_EN0_EMPTY_THRESHOLD_INT; + + pstIrqEvent->eRWState = I2C_WRITE_STATE; + pstIrqEvent->dBurstCount = burst_cnt; + pstIrqEvent->dDataIndex = i; + pstIrqEvent->dDataTotalLen = write_cnt; + pstIrqEvent->pDataBuf = pstCmdInfo->pWrData; + + sp_i2cm_reset(sr); + sp_i2cm_clock_freq_set(sr, pstCmdInfo->dFreq); + sp_i2cm_slave_addr_set(sr, pstCmdInfo->dSlaveAddr); + #ifdef I2C_RETEST + if ((test_count > 1) && (test_count%3 == 0)) { + DBG_INFO("test_count = %d", test_count); + sp_i2cm_scl_delay_set(sr, 0x01); + } else { + sp_i2cm_scl_delay_set(sr, I2C_SCL_DELAY); + } + #endif + sp_i2cm_trans_cnt_set(sr, write_cnt, 0); + sp_i2cm_active_mode_set(sr, I2C_TRIGGER); + sp_i2cm_rw_mode_set(sr, I2C_WRITE_MODE); + sp_i2cm_data_set(sr, (unsigned int *)w_data); + + if (burst_cnt) + sp_i2cm_int_en0_with_thershold_set(sr, int0, I2C_EMPTY_THRESHOLD_VALUE); + else + sp_i2cm_int_en0_set(sr, int0); + + sp_i2cm_manual_trigger(sr); //start send data + + ret = wait_event_timeout(pstSpI2CInfo->wait, + pstIrqEvent->stIrqFlag.bActiveDone, (I2C_SLEEP_TIMEOUT * HZ) / 500); + if (ret == 0) { + DBG_ERR("I2C write timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = pstIrqEvent->bRet; + } + sp_i2cm_reset(sr); + pstIrqEvent->eRWState = I2C_IDLE_STATE; + pstIrqEvent->bI2CBusy = 0; + + return ret; +} + + +int sp_i2cm_dma_write(struct I2C_Cmd_t_ *pstCmdInfo, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + struct regs_i2cm_dma_s *sr_dma = (struct regs_i2cm_dma_s *)pstSpI2CInfo->i2c_dma_regs; + struct I2C_Irq_Event_t_ *pstIrqEvent = &(pstSpI2CInfo->stIrqEvent); + struct Moon_RegBase_s *pstMoonRegBase = &stMoonRegBase; + unsigned int int0 = 0; + int ret = I2C_SUCCESS; + unsigned int dma_int = 0; + dma_addr_t dma_w_addr = 0; + + FUNC_DEBUG(); + + + if (pstCmdInfo->dDevId > I2C_MASTER_NUM) + return I2C_ERR_INVALID_DEVID; + + if (pstSpI2CInfo->mode == I2C_POWER_ALL_SWITCH) { + pstMoonRegBase->moon0_regs = (void __iomem *)MOON0_BASE; + sp_i2cm_enable(0, pstMoonRegBase->moon0_regs); + } + + if (pstIrqEvent->bI2CBusy) { + DBG_ERR("I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(pstIrqEvent, 0, sizeof(*pstIrqEvent)); + pstIrqEvent->bI2CBusy = 1; + + if (pstCmdInfo->dWrDataCnt > 0xFFFF) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C write count is invalid !! write count=%d\n", pstCmdInfo->dWrDataCnt); + return I2C_ERR_INVALID_CNT; + } + + + DBG_INFO(" DMA DataCnt = %d\n", pstCmdInfo->dWrDataCnt); + + pstIrqEvent->eRWState = I2C_DMA_WRITE_STATE; + + dma_w_addr = dma_map_single(pstSpI2CInfo->dev, pstCmdInfo->pWrData, + pstCmdInfo->dWrDataCnt, DMA_TO_DEVICE); + + if (dma_mapping_error(pstSpI2CInfo->dev, dma_w_addr)) { + DBG_ERR("I2C dma_w_addr fail\n"); + dma_w_addr = pstSpI2CInfo->dma_phy_base; + memcpy(pstSpI2CInfo->dma_vir_base, pstCmdInfo->pWrData, pstCmdInfo->dWrDataCnt); + } + + int0 = (I2C_EN0_SCL_HOLD_TOO_LONG_INT | I2C_EN0_EMPTY_INT + | I2C_EN0_DATA_NACK_INT | I2C_EN0_ADDRESS_NACK_INT | I2C_EN0_DONE_INT); + + + dma_int = I2C_DMA_EN_DMA_DONE_INT; + + + sp_i2cm_reset(sr); + sp_i2cm_dma_mode_enable(sr); + sp_i2cm_clock_freq_set(sr, pstCmdInfo->dFreq); + sp_i2cm_slave_addr_set(sr, pstCmdInfo->dSlaveAddr); + + #ifdef I2C_RETEST + if ((test_count > 1) && (test_count%3 == 0)) { + DBG_INFO("test_count = %d", test_count); + sp_i2cm_scl_delay_set(sr, 0x01); + } else { + sp_i2cm_scl_delay_set(sr, I2C_SCL_DELAY); + } + #endif + sp_i2cm_active_mode_set(sr, I2C_AUTO); + sp_i2cm_rw_mode_set(sr, I2C_WRITE_MODE); + sp_i2cm_int_en0_set(sr, int0); + + sp_i2cm_dma_addr_set(sr_dma, (unsigned int)dma_w_addr); + sp_i2cm_dma_length_set(sr_dma, pstCmdInfo->dWrDataCnt); + sp_i2cm_dma_rw_mode_set(sr_dma, I2C_DMA_READ_MODE); + sp_i2cm_dma_int_en_set(sr_dma, dma_int); + sp_i2cm_dma_go_set(sr_dma); + + + ret = wait_event_timeout(pstSpI2CInfo->wait, + pstIrqEvent->stIrqDmaFlag.bDmaDone, (I2C_SLEEP_TIMEOUT * HZ) / 200); + if (ret == 0) { + DBG_ERR("I2C DMA write timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = pstIrqEvent->bRet; + } + sp_i2cm_status_clear(sr, 0xFFFFFFFF); + + if (dma_w_addr != pstSpI2CInfo->dma_phy_base) + dma_unmap_single(pstSpI2CInfo->dev, dma_w_addr, + pstCmdInfo->dWrDataCnt, DMA_TO_DEVICE); + + + + pstIrqEvent->eRWState = I2C_IDLE_STATE; + pstIrqEvent->bI2CBusy = 0; + + sp_i2cm_reset(sr); + + return ret; +} + +int sp_i2cm_dma_read(struct I2C_Cmd_t_ *pstCmdInfo, struct SpI2C_If_t_ *pstSpI2CInfo) +{ + struct regs_i2cm_s *sr = (struct regs_i2cm_s *)pstSpI2CInfo->i2c_regs; + struct regs_i2cm_dma_s *sr_dma = (struct regs_i2cm_dma_s *)pstSpI2CInfo->i2c_dma_regs; + struct I2C_Irq_Event_t_ *pstIrqEvent = &(pstSpI2CInfo->stIrqEvent); + + struct Moon_RegBase_s *pstMoonRegBase = &stMoonRegBase; + unsigned char w_data[32] = {0}; + unsigned int read_cnt = 0; + unsigned int write_cnt = 0; + unsigned int int0 = 0, int1 = 0, int2 = 0; + unsigned int dma_int = 0; + int ret = I2C_SUCCESS; + int i = 0; + dma_addr_t dma_r_addr = 0; + + FUNC_DEBUG(); + + if (pstCmdInfo->dDevId > I2C_MASTER_NUM) + return I2C_ERR_INVALID_DEVID; + + if (pstSpI2CInfo->mode == I2C_POWER_ALL_SWITCH) { + pstMoonRegBase->moon0_regs = (void __iomem *)MOON0_BASE; + sp_i2cm_enable(0, pstMoonRegBase->moon0_regs); + } + + + if (pstIrqEvent->bI2CBusy) { + DBG_ERR("I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(pstIrqEvent, 0, sizeof(*pstIrqEvent)); + pstIrqEvent->bI2CBusy = 1; + + write_cnt = pstCmdInfo->dWrDataCnt; + read_cnt = pstCmdInfo->dRdDataCnt; + + if (pstCmdInfo->dRestartEn) { + if (write_cnt > 32) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C write count is invalid !! write count=%d\n", write_cnt); + return I2C_ERR_INVALID_CNT; + } + } + + if ((read_cnt > 0xFFFF) || (read_cnt == 0)) { + pstIrqEvent->bI2CBusy = 0; + DBG_ERR("I2C read count is invalid !! read count=%d\n", read_cnt); + return I2C_ERR_INVALID_CNT; + } + + DBG_INFO("write_cnt = %d, DMA read_cnt = %d\n", + write_cnt, read_cnt); + + dma_r_addr = dma_map_single(pstSpI2CInfo->dev, pstCmdInfo->pRdData, + pstCmdInfo->dRdDataCnt, DMA_FROM_DEVICE); + + + if (dma_mapping_error(pstSpI2CInfo->dev, dma_r_addr)) { + DBG_ERR("I2C dma_r_addr fail\n"); + dma_r_addr = pstSpI2CInfo->dma_phy_base; + } + + + int0 = (I2C_EN0_SCL_HOLD_TOO_LONG_INT | I2C_EN0_EMPTY_INT | I2C_EN0_DATA_NACK_INT + | I2C_EN0_ADDRESS_NACK_INT | I2C_EN0_DONE_INT); + + dma_int = I2C_DMA_EN_DMA_DONE_INT; + + pstIrqEvent->eRWState = I2C_DMA_READ_STATE; + + pstIrqEvent->dDataIndex = 0; + pstIrqEvent->dRegDataIndex = 0; + pstIrqEvent->dDataTotalLen = read_cnt; + + sp_i2cm_reset(sr); + sp_i2cm_dma_mode_enable(sr); + sp_i2cm_clock_freq_set(sr, pstCmdInfo->dFreq); + sp_i2cm_slave_addr_set(sr, pstCmdInfo->dSlaveAddr); + + #ifdef I2C_RETEST + if ((test_count > 1) && (test_count%3 == 0)) { + DBG_INFO("test_count = %d", test_count); + sp_i2cm_scl_delay_set(sr, 0x01); + } else { + sp_i2cm_scl_delay_set(sr, I2C_SCL_DELAY); + } + #endif + + if (pstCmdInfo->dRestartEn) { + DBG_INFO("I2C_RESTART_MODE\n"); + sp_i2cm_active_mode_set(sr, I2C_TRIGGER); + sp_i2cm_rw_mode_set(sr, I2C_RESTART_MODE); + sp_i2cm_trans_cnt_set(sr, write_cnt, read_cnt); + for (i = 0; i < write_cnt; i++) + w_data[i] = pstCmdInfo->pWrData[i]; + + sp_i2cm_data_set(sr, (unsigned int *)w_data); + } else { + DBG_INFO("I2C_READ_MODE\n"); + sp_i2cm_active_mode_set(sr, I2C_AUTO); + sp_i2cm_rw_mode_set(sr, I2C_READ_MODE); + } + + sp_i2cm_int_en0_set(sr, int0); + sp_i2cm_int_en1_set(sr, int1); + sp_i2cm_int_en2_set(sr, int2); + + sp_i2cm_dma_addr_set(sr_dma, (unsigned int)dma_r_addr); + sp_i2cm_dma_length_set(sr_dma, pstCmdInfo->dRdDataCnt); + sp_i2cm_dma_rw_mode_set(sr_dma, I2C_DMA_WRITE_MODE); + sp_i2cm_dma_int_en_set(sr_dma, dma_int); + sp_i2cm_dma_go_set(sr_dma); + + + if (pstCmdInfo->dRestartEn) + sp_i2cm_manual_trigger(sr); //start send data + + + ret = wait_event_timeout(pstSpI2CInfo->wait, + pstIrqEvent->stIrqDmaFlag.bDmaDone, (I2C_SLEEP_TIMEOUT * HZ) / 200); + + if (ret == 0) { + DBG_ERR("I2C DMA read timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = pstIrqEvent->bRet; + } + sp_i2cm_status_clear(sr, 0xFFFFFFFF); + + //copy data from virtual addr to pstCmdInfo->pRdData + + if (dma_r_addr == pstSpI2CInfo->dma_phy_base) + memcpy(pstCmdInfo->pRdData, pstSpI2CInfo->dma_vir_base, pstCmdInfo->dRdDataCnt); + else + dma_unmap_single(pstSpI2CInfo->dev, + dma_r_addr, pstCmdInfo->dRdDataCnt, DMA_FROM_DEVICE); + + pstIrqEvent->eRWState = I2C_IDLE_STATE; + pstIrqEvent->bI2CBusy = 0; + + + sp_i2cm_reset(sr); + return ret; +} + + + +static int sp_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = adap->algo_data; + struct I2C_Cmd_t_ *pstCmdInfo = &(pstSpI2CInfo->stCmdInfo); + int ret = I2C_SUCCESS; + int i = 0; + unsigned char restart_w_data[32] = {0}; + unsigned int restart_write_cnt = 0; + unsigned int restart_en = 0; + + FUNC_DEBUG(); + +#ifdef CONFIG_PM_RUNTIME_I2C + ret = pm_runtime_get_sync(&adap->dev); + if (ret < 0) + goto out; +#endif + if (num == 0) + return -EINVAL; + + memset(pstCmdInfo, 0, sizeof(*pstCmdInfo)); + pstCmdInfo->dDevId = adap->nr; + + if (pstCmdInfo->dFreq > I2C_FREQ) + pstCmdInfo->dFreq = I2C_FREQ; + else + pstCmdInfo->dFreq = pstSpI2CInfo->i2c_clk_freq/1000; + + DBG_INFO("[I2C] set freq : %d\n", pstCmdInfo->dFreq); + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_TEN) + return -EINVAL; + + pstCmdInfo->dSlaveAddr = msgs[i].addr; + + if (msgs[i].flags & I2C_M_NOSTART) { + + restart_write_cnt = msgs[i].len; + for (i = 0; i < restart_write_cnt; i++) + restart_w_data[i] = msgs[i].buf[i]; + + restart_en = 1; + continue; + } + + if (msgs[i].flags & I2C_M_RD) { + if (restart_en == 1) { + pstCmdInfo->dWrDataCnt = restart_write_cnt; + pstCmdInfo->pWrData = restart_w_data; + DBG_INFO("I2C_M_RD dWrDataCnt =%d ", pstCmdInfo->dWrDataCnt); + DBG_INFO("I2C_M_RD pWrData[0] =%x ", pstCmdInfo->pWrData[0]); + DBG_INFO("I2C_M_RD pWrData[1] =%x ", pstCmdInfo->pWrData[1]); + restart_en = 0; + pstCmdInfo->dRestartEn = 1; + } + pstCmdInfo->dRdDataCnt = msgs[i].len; + pstCmdInfo->pRdData = i2c_get_dma_safe_msg_buf(&msgs[i], 4); + + if ((pstCmdInfo->dRdDataCnt < 4) || (!pstCmdInfo->pRdData)) { + pstCmdInfo->pRdData = msgs[i].buf; + ret = sp_i2cm_read(pstCmdInfo, pstSpI2CInfo); + } else { + ret = sp_i2cm_dma_read(pstCmdInfo, pstSpI2CInfo); + i2c_put_dma_safe_msg_buf(pstCmdInfo->pRdData, &msgs[i], true); + } + + } else { + pstCmdInfo->dWrDataCnt = msgs[i].len; + pstCmdInfo->pWrData = i2c_get_dma_safe_msg_buf(&msgs[i], 4); + if ((pstCmdInfo->dWrDataCnt < 4) || (!pstCmdInfo->pWrData)) { + pstCmdInfo->pWrData = msgs[i].buf; + ret = sp_i2cm_write(pstCmdInfo, pstSpI2CInfo); + } else { + ret = sp_i2cm_dma_write(pstCmdInfo, pstSpI2CInfo); + i2c_put_dma_safe_msg_buf(pstCmdInfo->pWrData, + &msgs[i], true); + } + } + + if (ret != I2C_SUCCESS) + return -EIO; + } + +#ifdef CONFIG_PM_RUNTIME_I2C + pm_runtime_put(&adap->dev); + +#endif + + return num; + +#ifdef CONFIG_PM_RUNTIME_I2C +out: + pm_runtime_mark_last_busy(&adap->dev); + pm_runtime_put_autosuspend(&adap->dev); + return num; +#endif + +} + +static u32 sp_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm sp_algorithm = { + .master_xfer = sp_master_xfer, + .functionality = sp_functionality, +}; + +static int sp_i2c_probe(struct platform_device *pdev) +{ + struct SpI2C_If_t_ *pstSpI2CInfo; + struct i2c_adapter *p_adap; + unsigned int i2c_clk_freq; + int device_id = 0; + int ret = I2C_SUCCESS; + struct device *dev = &pdev->dev; + const struct i2c_compatible *dev_mode; + + FUNC_DEBUG(); + + if (pdev->dev.of_node) { + pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); + DBG_INFO("[I2C adapter] pdev->id=%d\n", pdev->id); + device_id = pdev->id; + } + + pstSpI2CInfo = devm_kzalloc(&pdev->dev, sizeof(*pstSpI2CInfo), GFP_KERNEL); + if (!pstSpI2CInfo) + return -ENOMEM; + + if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_clk_freq)) { + dev_dbg(&pdev->dev, "clk_freq %d\n", i2c_clk_freq); + pstSpI2CInfo->i2c_clk_freq = i2c_clk_freq; + } else + pstSpI2CInfo->i2c_clk_freq = I2C_FREQ*1000; + + DBG_INFO("[I2C adapter] get freq : %d\n", pstSpI2CInfo->i2c_clk_freq); + + pstSpI2CInfo->dev = &pdev->dev; + + ret = _sp_i2cm_get_resources(pdev, pstSpI2CInfo); + if (ret != I2C_SUCCESS) { + DBG_ERR("[I2C adapter] get resources fail !\n"); + return ret; + } + + pstSpI2CInfo->clk = devm_clk_get(dev, NULL); + + if (IS_ERR(pstSpI2CInfo->clk)) { + ret = PTR_ERR(pstSpI2CInfo->clk); + dev_err(dev, "failed to retrieve clk: %d\n", ret); + goto err_clk_disable; + } + ret = clk_prepare_enable(pstSpI2CInfo->clk); + + if (ret) { + dev_err(dev, "failed to enable clk: %d\n", ret); + goto err_clk_disable; + } + + pstSpI2CInfo->rstc = devm_reset_control_get(dev, NULL); + + if (IS_ERR(pstSpI2CInfo->rstc)) { + ret = PTR_ERR(pstSpI2CInfo->rstc); + dev_err(dev, "failed to retrieve reset controller: %d\n", ret); + goto err_reset_assert; + } + ret = reset_control_deassert(pstSpI2CInfo->rstc); + + if (ret) { + dev_err(dev, "failed to deassert reset line: %d\n", ret); + goto err_reset_assert; + } + + /* dma alloc*/ + pstSpI2CInfo->dma_vir_base = dma_alloc_coherent(&pdev->dev, I2C_BUFFER_SIZE, + &pstSpI2CInfo->dma_phy_base, GFP_ATOMIC); + if (!pstSpI2CInfo->dma_vir_base) + goto free_dma; + + ret = _sp_i2cm_init(device_id, pstSpI2CInfo); + if (ret != 0) { + DBG_ERR("[I2C adapter] i2c master %d init error\n", device_id); + return ret; + } + + init_waitqueue_head(&pstSpI2CInfo->wait); + + dev_mode = of_device_get_match_data(&pdev->dev); + pstSpI2CInfo->mode = dev_mode->mode; + p_adap = &pstSpI2CInfo->adap; + sprintf(p_adap->name, "%s%d", DEVICE_NAME, device_id); + p_adap->algo = &sp_algorithm; + p_adap->algo_data = pstSpI2CInfo; + p_adap->nr = device_id; + p_adap->class = 0; + p_adap->retries = 5; + p_adap->dev.parent = &pdev->dev; + p_adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_numbered_adapter(p_adap); + if (ret < 0) { + DBG_ERR("[I2C adapter] error add adapter %s\n", p_adap->name); + goto free_dma; + } else { + DBG_INFO("[I2C adapter] add adapter %s success\n", p_adap->name); + platform_set_drvdata(pdev, pstSpI2CInfo); + } + + ret = request_irq(pstSpI2CInfo->irq, _sp_i2cm_irqevent_handler, IRQF_TRIGGER_HIGH, + p_adap->name, pstSpI2CInfo); + if (ret) { + DBG_ERR("request irq fail !!\n"); + return I2C_ERR_REQUESET_IRQ; + } + + +#ifdef CONFIG_PM_RUNTIME_I2C + pm_runtime_set_autosuspend_delay(&pdev->dev, 5000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); +#endif + + return ret; + +free_dma: + dma_free_coherent(&pdev->dev, I2C_BUFFER_SIZE, + pstSpI2CInfo->dma_vir_base, pstSpI2CInfo->dma_phy_base); + +err_reset_assert: + reset_control_assert(pstSpI2CInfo->rstc); + +err_clk_disable: + clk_disable_unprepare(pstSpI2CInfo->clk); + + return ret; +} + +static int sp_i2c_remove(struct platform_device *pdev) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = platform_get_drvdata(pdev); + struct i2c_adapter *p_adap = &pstSpI2CInfo->adap; + + FUNC_DEBUG(); + +#ifdef CONFIG_PM_RUNTIME_I2C + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); +#endif + + dma_free_coherent(&pdev->dev, I2C_BUFFER_SIZE, + pstSpI2CInfo->dma_vir_base, pstSpI2CInfo->dma_phy_base); + + i2c_del_adapter(p_adap); + if (p_adap->nr < I2C_MASTER_NUM) { + clk_disable_unprepare(pstSpI2CInfo->clk); + reset_control_assert(pstSpI2CInfo->rstc); + free_irq(pstSpI2CInfo->irq, NULL); + } + + return 0; +} + +static int sp_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = platform_get_drvdata(pdev); + struct i2c_adapter *p_adap = &pstSpI2CInfo->adap; + + FUNC_DEBUG(); + + if (p_adap->nr < I2C_MASTER_NUM) + reset_control_assert(pstSpI2CInfo->rstc); + + return 0; +} + +static int sp_i2c_resume(struct platform_device *pdev) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = platform_get_drvdata(pdev); + struct i2c_adapter *p_adap = &pstSpI2CInfo->adap; + + FUNC_DEBUG(); + + if (p_adap->nr < I2C_MASTER_NUM) { + reset_control_deassert(pstSpI2CInfo->rstc); //release reset + clk_prepare_enable(pstSpI2CInfo->clk); //enable clken and disable gclken + } + + return 0; +} + +static const struct i2c_compatible i2c_7021_compat = { + .mode = I2C_POWER_ALL_SWITCH, +}; + +static const struct i2c_compatible i2c_645_compat = { + .mode = I2C_POWER_NO_SWITCH, +}; + + +static const struct of_device_id sp_i2c_of_match[] = { + { .compatible = "sunplus,sp7021-i2cm", + .data = &i2c_7021_compat, }, + { .compatible = "sunplus,q645-i2cm", + .data = &i2c_645_compat, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sp_i2c_of_match); + +#ifdef CONFIG_PM_RUNTIME_I2C +static int sp_i2c_runtime_suspend(struct device *dev) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &pstSpI2CInfo->adap; + + FUNC_DEBUG(); + + if (p_adap->nr < I2C_MASTER_NUM) + reset_control_assert(pstSpI2CInfo->rstc); + + return 0; +} + +static int sp_i2c_runtime_resume(struct device *dev) +{ + struct SpI2C_If_t_ *pstSpI2CInfo = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &pstSpI2CInfo->adap; + + FUNC_DEBUG(); + + if (p_adap->nr < I2C_MASTER_NUM) { + reset_control_deassert(pstSpI2CInfo->rstc); //release reset + clk_prepare_enable(pstSpI2CInfo->clk); //enable clken and disable gclken + } + + return 0; +} +static const struct dev_pm_ops sp7021_i2c_pm_ops = { + .runtime_suspend = sp_i2c_runtime_suspend, + .runtime_resume = sp_i2c_runtime_resume, +}; + +#define sp_i2c_pm_ops (&sp7021_i2c_pm_ops) +#endif + +static struct platform_driver sp_i2c_driver = { + .probe = sp_i2c_probe, + .remove = sp_i2c_remove, + .suspend = sp_i2c_suspend, + .resume = sp_i2c_resume, + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_NAME, + .of_match_table = sp_i2c_of_match, +#ifdef CONFIG_PM_RUNTIME_I2C + .pm = sp_i2c_pm_ops, +#endif + }, +}; + +static int __init sp_i2c_adap_init(void) +{ + return platform_driver_register(&sp_i2c_driver); +} +module_init(sp_i2c_adap_init); + +static void __exit sp_i2c_adap_exit(void) +{ + platform_driver_unregister(&sp_i2c_driver); +} +module_exit(sp_i2c_adap_exit); + +MODULE_AUTHOR("lH Kuo "); +MODULE_DESCRIPTION("Sunplus I2c controller driver"); +MODULE_LICENSE("GPL v2"); From patchwork Fri Oct 29 08:42:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Li-hao Kuo X-Patchwork-Id: 1547892 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=QRPETtFs; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by bilbo.ozlabs.org (Postfix) with ESMTP id 4HgbXk1jpPz9sX3 for ; Fri, 29 Oct 2021 19:42:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232543AbhJ2IpM (ORCPT ); Fri, 29 Oct 2021 04:45:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51054 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232539AbhJ2IpJ (ORCPT ); Fri, 29 Oct 2021 04:45:09 -0400 Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4971FC061767; Fri, 29 Oct 2021 01:42:41 -0700 (PDT) Received: by mail-pf1-x42e.google.com with SMTP id x7so3622247pfh.7; Fri, 29 Oct 2021 01:42:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=uDTCJZU7DhCB0VVCsGKEbP1g8fUFF2GRuEeusaByhjo=; b=QRPETtFsr7C7ddN4PLleARPQ9JjjNNE4rpkXwO1R+h8hs5IWz3EkmWPyqDnEBYVGNm c98Gt0yXhGYojoaJSe84FnVgi5TLI0fg4ZovyemdewSqcjJQaEyDE3cMoNaChCbLErpQ ohAhaHmoIkMQHQ9XPdWM/SqN9hwE/GwjwZN6zJjC4Lis2Fq+TP374+ZJRGGrgMSV3blc Dcmp9McU5rwmCaUG4wNvPeqNllIJmZkt7NC75P/BrIEBXiBm/Jlk3mS2Fr0/4AczJQIV Ye5VWapUpZXcPWvzaGziChdWim9FsrEiCygEqkBaTEi3Aev435yjwiDRKwewJoKf3x/J uZyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=uDTCJZU7DhCB0VVCsGKEbP1g8fUFF2GRuEeusaByhjo=; b=vW9SDTKFg6pmMSOm4eI2m1TZm9KKNDXxoyd/FHznKUzSGfICAwTQFzhqPQhbCn/u31 6cWyzDRj7LGfsZ/w+xCdCks297wbBbO5j0v0bQKw7NW9EGwGSrrcgCoBMWYzOKy159F7 Si5FGgtA1PsBMX9F/MHfQylBQ3IEh0j9pudRoDPuJUau3G1XMXN6ZBfpVRAfWx012jpK Kk5+t4awF7T4WeORT9RXrXueox4PA6gTG3Ue06yUZofM7S5dDORJoASWxUUj3jYvRXDQ 2GF4YRXbq4Kytwku01uMYvn9yAlCbO8mPxUDMlE8w2rA/OKzmd8aarJ2gnHcXXShshLA /nZA== X-Gm-Message-State: AOAM5334feKqB7oPx+VpiMLfypbLQnyAa7A0xqqDTF3ICHhoYMXK/3Q1 hcxP85KWwGZC+XSHR/GSO4Q= X-Google-Smtp-Source: ABdhPJzRfUOPUVKf5rmn9tT+CBtylw25DCwfQvB2MDmDAshtyWtJAhnSNO5w3+PsxSB8pPpFTvTJYw== X-Received: by 2002:a05:6a00:1309:b0:44d:4d1e:c930 with SMTP id j9-20020a056a00130900b0044d4d1ec930mr9486642pfu.65.1635496960821; Fri, 29 Oct 2021 01:42:40 -0700 (PDT) Received: from scdiu3.sunplus.com ([113.196.136.192]) by smtp.googlemail.com with ESMTPSA id ml14sm1471424pjb.43.2021.10.29.01.42.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 29 Oct 2021 01:42:40 -0700 (PDT) From: "LH.Kuo" X-Google-Original-From: "LH.Kuo" To: p.zabel@pengutronix.de, robh+dt@kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, devicetree@vger.kernel.org Cc: dvorkin@tibbo.com, qinjian@cqplus1.com, wells.lu@sunplus.com, "LH.Kuo" Subject: [PATCH 2/2] devicetree bindings I2C Add bindings doc for Sunplus SP7021 Date: Fri, 29 Oct 2021 16:42:35 +0800 Message-Id: <1635496955-13985-3-git-send-email-lh.kuo@sunplus.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1635496955-13985-1-git-send-email-lh.kuo@sunplus.com> References: <1635496955-13985-1-git-send-email-lh.kuo@sunplus.com> Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Add devicetree bindings I2C Add bindings doc for Sunplus SP7021 Signed-off-by: LH.Kuo --- .../devicetree/bindings/i2c/i2c-sunplus.yaml | 82 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml diff --git a/Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml b/Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml new file mode 100644 index 0000000..7e2f827 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (C) Sunplus Co., Ltd. 2021 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-sunplus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sunplus's I2C controller + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +maintainers: + - lh.kuo + +properties: + compatible: + enum: + - sunplus,sp7021-i2cm + - sunplus,q645-i2cm + + reg: + items: + - description: Base address and length of the I2C registers + - description: Base address and length of the I2C DMA registers + + reg-names: + items: + - const: i2cm + - const: i2cmdma + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + clock-frequency: + enum: [ 100000, 400000 ] + + pinctrl-names: + description: + A pinctrl state named "default" must be defined. + const: default + + pinctrl-0: + description: + A phandle to the default pinctrl state. + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + - resets + - pinctrl-names + - pinctrl-0 + +additionalProperties: false + +examples: + - | + #include + #include + #include + i2cm0: i2c@9C004600 { + compatible = "sunplus,sp7021-i2cm"; + reg = <0x9c004600 0x80>, <0x9c004680 0x80>; + reg-names = "i2cm", "i2cmdma"; + interrupt-parent = <&intc>; + interrupts = <174 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc I2CM0>; + resets = <&rstc RST_I2CM0>; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2cm0_pins>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index c89a3b1..7dc9bea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17951,6 +17951,7 @@ SUNPLUS I2C CONTROLLER INTERFACE DRIVER M: LH Kuo L: linux-i2c@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml F: drivers/i2c/busses/i2c-sunplus.c SUPERH