* [PATCH 0/2] This is a patch series for I2C driver for Sunplus SP7021 SoC. @ 2021-10-29 8:42 LH.Kuo 2021-10-29 8:42 ` [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo ` (2 more replies) 0 siblings, 3 replies; 10+ messages in thread From: LH.Kuo @ 2021-10-29 8:42 UTC (permalink / raw) To: p.zabel, robh+dt, linux-kernel, linux-i2c, devicetree Cc: dvorkin, qinjian, wells.lu, LH.Kuo Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a single chip. It is designed for industrial control. Refer to: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview https://tibbo.com/store/plus1.html LH.Kuo (2): I2C: Add I2C driver for Sunplus SP7021 devicetree bindings I2C Add bindings doc for Sunplus SP7021 .../devicetree/bindings/i2c/i2c-sunplus.yaml | 82 + MAINTAINERS | 39 +- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-sunplus.c | 1936 ++++++++++++++++++++ 5 files changed, 2052 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml create mode 100644 drivers/i2c/busses/i2c-sunplus.c -- 2.7.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 2021-10-29 8:42 [PATCH 0/2] This is a patch series for I2C driver for Sunplus SP7021 SoC LH.Kuo @ 2021-10-29 8:42 ` LH.Kuo 2021-10-29 8:42 ` [PATCH 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo 2021-11-09 6:59 ` [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC LH.Kuo 2 siblings, 0 replies; 10+ messages in thread From: LH.Kuo @ 2021-10-29 8:42 UTC (permalink / raw) To: p.zabel, robh+dt, linux-kernel, linux-i2c, devicetree Cc: dvorkin, qinjian, wells.lu, LH.Kuo Add I2C driver for Sunplus SP7021. Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> --- 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 <hauke@hauke-m.de> -M: Rafał Miłecki <zajec5@gmail.com> +M: Rafa? Mi?ecki <zajec5@gmail.com> 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 <rafal@milecki.pl> +M: Rafa? Mi?ecki <rafal@milecki.pl> 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 <hauke@hauke-m.de> -M: Rafał Miłecki <zajec5@gmail.com> +M: Rafa? Mi?ecki <zajec5@gmail.com> 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 <rafal@milecki.pl> +M: Rafa? Mi?ecki <rafal@milecki.pl> 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 <rafal@milecki.pl> +M: Rafa? Mi?ecki <rafal@milecki.pl> 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 <zajec5@gmail.com> +M: Rafa? Mi?ecki <zajec5@gmail.com> L: linux-mips@vger.kernel.org S: Maintained F: drivers/firmware/broadcom/* BROADCOM PMB (POWER MANAGEMENT BUS) DRIVER -M: Rafał Miłecki <rafal@milecki.pl> +M: Rafa? Mi?ecki <rafal@milecki.pl> M: Florian Fainelli <f.fainelli@gmail.com> 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 <zajec5@gmail.com> +M: Rafa? Mi?ecki <zajec5@gmail.com> 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 <mirq-linux@rere.qmqm.pl> +M: Micha? Miros?aw <mirq-linux@rere.qmqm.pl> 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ă <horia.geanta@nxp.com> +M: Horia Geant? <horia.geanta@nxp.com> M: Pankaj Gupta <pankaj.gupta@nxp.com> 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 <thomas@weissschuh.net> +M: Thomas Wei?schuh <thomas@weissschuh.net> 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 <pgaj@cadence.com> +M: Przemys?aw Gaj <pgaj@cadence.com> 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 <kishon@ti.com> M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> -R: Krzysztof Wilczyński <kw@linux.com> +R: Krzysztof Wilczy?ski <kw@linux.com> 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 <lorenzo.pieralisi@arm.com> R: Rob Herring <robh@kernel.org> -R: Krzysztof Wilczyński <kw@linux.com> +R: Krzysztof Wilczy?ski <kw@linux.com> 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 <l.stelmach@samsung.com> +M: ?ukasz Stelmach <l.stelmach@samsung.com> 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 <s.nawrocki@samsung.com> -M: Artur Świgoń <a.swigon@samsung.com> +M: Artur ?wigo? <a.swigon@samsung.com> 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 <lh.kuo@sunplus.com> +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-sunplus.c + SUPERH M: Yoshinori Sato <ysato@users.sourceforge.jp> M: Rich Felker <dalias@libc.org> 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 <lh.kuo@sunplus.com> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/rtc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include <linux/dma-mapping.h> + +#ifdef CONFIG_PM_RUNTIME_I2C +#include <linux/pm_runtime.h> +#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 <lh.kuo@sunplus.com>"); +MODULE_DESCRIPTION("Sunplus I2c controller driver"); +MODULE_LICENSE("GPL v2"); -- 2.7.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] devicetree bindings I2C Add bindings doc for Sunplus SP7021 2021-10-29 8:42 [PATCH 0/2] This is a patch series for I2C driver for Sunplus SP7021 SoC LH.Kuo 2021-10-29 8:42 ` [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo @ 2021-10-29 8:42 ` LH.Kuo 2021-11-08 19:23 ` Rob Herring 2021-11-09 6:59 ` [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC LH.Kuo 2 siblings, 1 reply; 10+ messages in thread From: LH.Kuo @ 2021-10-29 8:42 UTC (permalink / raw) To: p.zabel, robh+dt, linux-kernel, linux-i2c, devicetree Cc: dvorkin, qinjian, wells.lu, LH.Kuo Add devicetree bindings I2C Add bindings doc for Sunplus SP7021 Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> --- .../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 <lh.kuo@sunplus.com> + +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 <dt-bindings/clock/sp-sp7021.h> + #include <dt-bindings/reset/sp-sp7021.h> + #include <dt-bindings/interrupt-controller/irq.h> + 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 <lh.kuo@sunplus.com> L: linux-i2c@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml F: drivers/i2c/busses/i2c-sunplus.c SUPERH -- 2.7.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] devicetree bindings I2C Add bindings doc for Sunplus SP7021 2021-10-29 8:42 ` [PATCH 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo @ 2021-11-08 19:23 ` Rob Herring 0 siblings, 0 replies; 10+ messages in thread From: Rob Herring @ 2021-11-08 19:23 UTC (permalink / raw) To: LH.Kuo Cc: p.zabel, linux-kernel, linux-i2c, devicetree, dvorkin, qinjian, wells.lu, LH.Kuo On Fri, Oct 29, 2021 at 04:42:35PM +0800, LH.Kuo wrote: > Add devicetree bindings I2C Add bindings doc for Sunplus SP7021 Please follow the subject convention used by the subsystem. This will be evident running 'git log --oneline'. For this one: 'dt-bindings: i2c: ...' > > Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> > --- > .../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 <lh.kuo@sunplus.com> > + > +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 This means you can't have any child nodes which I'd assume you want. You need 'unevaluatedProperties: false' instead. > + > +examples: > + - | > + #include <dt-bindings/clock/sp-sp7021.h> > + #include <dt-bindings/reset/sp-sp7021.h> > + #include <dt-bindings/interrupt-controller/irq.h> > + i2cm0: i2c@9C004600 { Drop unused labels. > + 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 <lh.kuo@sunplus.com> > L: linux-i2c@vger.kernel.org > S: Maintained > +F: Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml > F: drivers/i2c/busses/i2c-sunplus.c > > SUPERH > -- > 2.7.4 > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC 2021-10-29 8:42 [PATCH 0/2] This is a patch series for I2C driver for Sunplus SP7021 SoC LH.Kuo 2021-10-29 8:42 ` [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo 2021-10-29 8:42 ` [PATCH 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo @ 2021-11-09 6:59 ` LH.Kuo 2021-11-09 6:59 ` [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo 2021-11-09 6:59 ` [PATCH v2 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo 2 siblings, 2 replies; 10+ messages in thread From: LH.Kuo @ 2021-11-09 6:59 UTC (permalink / raw) To: p.zabel, daniel.thompson, lee.jones, u.kleine-koenig, robh+dt, linux-kernel, linux-i2c, devicetree Cc: qinjian, wells.lu, LH.Kuo This is a patch series for I2C driver for Sunplus SP7021 SoC. Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a single chip. It is designed for industrial control. Refer to: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview https://tibbo.com/store/plus1.html LH.Kuo (2): I2C: Add I2C driver for Sunplus SP7021 devicetree bindings I2C Add bindings doc for Sunplus SP7021 .../devicetree/bindings/i2c/i2c-sunplus.yaml | 82 + MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-sunplus.c | 1700 ++++++++++++++++++++ 5 files changed, 1800 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml create mode 100644 drivers/i2c/busses/i2c-sunplus.c -- 2.7.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 2021-11-09 6:59 ` [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC LH.Kuo @ 2021-11-09 6:59 ` LH.Kuo 2021-11-09 9:47 ` Philipp Zabel 2021-11-09 6:59 ` [PATCH v2 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo 1 sibling, 1 reply; 10+ messages in thread From: LH.Kuo @ 2021-11-09 6:59 UTC (permalink / raw) To: p.zabel, daniel.thompson, lee.jones, u.kleine-koenig, robh+dt, linux-kernel, linux-i2c, devicetree Cc: qinjian, wells.lu, LH.Kuo Add I2C driver for Sunplus SP7021. Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> --- Changes in v2: - Addressed all comments from Mr. Rob Herring. - Modified the structure and register access method. - Modifiedthe path about MAINTAINERS. ( wrong messages PATH in v1). - Modifiedthe the YAML file. MAINTAINERS | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-sunplus.c | 1700 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1717 insertions(+) create mode 100644 drivers/i2c/busses/i2c-sunplus.c diff --git a/MAINTAINERS b/MAINTAINERS index 170bbbe..5b7a8a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18189,6 +18189,12 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/dlink/sundance.c +SUNPLUS I2C CONTROLLER INTERFACE DRIVER +M: LH Kuo <lh.kuo@sunplus.com> +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-sunplus.c + SUPERH M: Yoshinori Sato <ysato@users.sourceforge.jp> M: Rich Felker <dalias@libc.org> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index dce3928..d9d6d21 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1061,6 +1061,16 @@ config I2C_SUN6I_P2WI This interface is used to connect to specific PMIC devices (like the AXP221). +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_SYNQUACER tristate "Socionext SynQuacer I2C controller" depends on ARCH_SYNQUACER || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index d85899f..8b44c70 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o i2c-stm32f7-drv-objs := i2c-stm32f7.o i2c-stm32.o obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.o obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o +obj-$(CONFIG_I2C_SUNPLUS) += i2c-sunplus.o obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o diff --git a/drivers/i2c/busses/i2c-sunplus.c b/drivers/i2c/busses/i2c-sunplus.c new file mode 100644 index 0000000..e617535 --- /dev/null +++ b/drivers/i2c/busses/i2c-sunplus.c @@ -0,0 +1,1700 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Sunplus Inc. + * Author: LH Kuo <lh.kuo@sunplus.com> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/rtc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> + +#define SP_I2C_FREQ 400 +#define SP_I2C_SLEEP_TIMEOUT 200 +#define SP_I2C_SCL_DELAY 1 //SCl dalay xT + +#define SP_CLK_SOURCE_FREQ 27000 // KHz(27MHz) +#define SP_BUFFER_SIZE 1024 // Byte + +#define SP_I2C_REG_NAME "i2cm" +#define SP_DEVICE_NAME "sunplus-i2cm" +#define SP_I2C_DMA_REG_NAME "i2cmdma" + +//burst write use +#define SP_I2C_EMPTY_THRESHOLD 4 + +#define SP_I2C_BURST_RDATA_BYTES 16 +#define SP_I2C_BURST_RDATA_FLAG 0x80008000 +#define SP_I2C_BURST_RDATA_ALL_FLAG 0xFFFFFFFF + +#define SP_I2C_CTL0_REG 0x0000 +//control0 +#define SP_I2C_CTL0_FREQ(x) (x<<24) //bit[26:24] +#define SP_I2C_CTL0_PREFETCH (1<<18) +#define SP_I2C_CTL0_RESTART_EN (1<<17) //0:disable 1:enable +#define SP_I2C_CTL0_SUBADDR_EN (1<<16) //For restart mode need to set high +#define SP_I2C_CTL0_SW_RESET (1<<15) +#define SP_I2C_CTL0_SLAVE_ADDR(x) (x<<1) //bit[7:1] + +#define SP_I2C_CTL1_REG 0x0004 +//control1 +#define SP_I2C_CTL1_ALL_CLR (0x3FF) +#define SP_I2C_CTL1_EMPTY_CLR (1<<9) +#define SP_I2C_CTL1_SCL_HOLD_TOO_LONG_CLR (1<<8) +#define SP_I2C_CTL1_SCL_WAIT_CLR (1<<7) +#define SP_I2C_CTL1_EMPTY_THRESHOLD_CLR (1<<6) +#define SP_I2C_CTL1_DATA_NACK_CLR (1<<5) +#define SP_I2C_CTL1_ADDRESS_NACK_CLR (1<<4) +#define SP_I2C_CTL1_BUSY_CLR (1<<3) +#define SP_I2C_CTL1_CLKERR_CLR (1<<2) +#define SP_I2C_CTL1_DONE_CLR (1<<1) +#define SPI2C_CTL1_SIFBUSY_CLR (1<<0) + +#define SP_I2C_CTL2_REG 0x0008 +//control2 +#define SP_I2C_CTL2_FREQ_CUSTOM(x) (x<<0) //bit[10:0] +#define SP_I2C_CTL2_SCL_DELAY(x) (x<<24) //bit[25:24] +#define SP_I2C_CTL2_SDA_HALF_ENABLE (1<<31) + +#define SP_I2C_INT_REG 0x001c +#define SP_I2C_INT_EN0_REG 0x0020 +#define SP_I2C_MOD_REG 0x0024 +#define SP_I2C_CTL6_REG 0x0030 +#define SP_I2C_INT_EN1_REG 0x0034 +#define SP_I2C_STATUS3_REG 0x0038 +#define SP_I2C_STATUS4_REG 0x0040 +#define SP_I2C_INT_EN2_REG 0x0040 +#define SP_I2C_CTL7_REG 0x0044 +//control7 +#define SP_I2C_CTL7_RDCOUNT(x) (x<<16) //bit[31:16] +#define SP_I2C_CTL7_WRCOUNT(x) (x<<0) //bit[15:0] + + +#define SP_I2C_CTL0_FREQ_MASK (0x7) // 3 bit +#define SP_I2C_CTL0_SLAVE_ADDR_MASK (0x7F) // 7 bit +#define SP_I2C_CTL2_FREQ_CUSTOM_MASK (0x7FF) // 11 bit +#define SP_I2C_CTL2_SCL_DELAY_MASK (0x3) // 2 bit +#define SP_I2C_CTL7_RW_COUNT_MASK (0xFFFF) // 16 bit +#define SP_I2C_EN0_CTL_EMPTY_THRESHOLD_MASK (0x7) // 3 bit +#define SP_I2C_SG_DMA_LLI_INDEX_MASK (0x1F) // 5 bit + +//interrupt enable1 +#define SP_I2C_EN1_BURST_RDATA_INT (0x80008000) //must sync with GET_BYTES_EACHTIME + +//interrupt enable2 +#define SP_I2C_EN2_BURST_RDATA_OVERFLOW_INT (0xFFFFFFFF) + +//i2c master mode +#define SP_I2C_MODE_DMA_MODE (1<<2) +#define SP_I2C_MODE_MANUAL_MODE (1<<1) //0:trigger mode 1:auto mode +#define SP_I2C_MODE_MANUAL_TRIG (1<<0) + +#define SP_I2C_DATA0_REG 0x0060 +#define SP_I2C_DATA4_REG 0x0064 +#define SP_I2C_DATA8_REG 0x0068 +#define SP_I2C_DATA12_REG 0x006c +#define SP_I2C_DATA16_REG 0x0070 +#define SP_I2C_DATA20_REG 0x0074 +#define SP_I2C_DATA24_REG 0x0078 +#define SP_I2C_DATA28_REG 0x007c + +#define SP_I2C_DMA_CONF_REG 0x0004 +#define SP_I2C_DMA_LEN_REG 0x0008 +#define SP_I2C_DMA_ADDR_REG 0x000c + +//dma config +#define SP_I2C_DMA_CFG_DMA_GO (1<<8) +#define SP_I2C_DMA_CFG_NON_BUF_MODE (1<<2) +#define SP_I2C_DMA_CFG_SAME_SLAVE (1<<1) +#define SP_I2C_DMA_CFG_DMA_MODE (1<<0) + +#define SP_I2C_DMA_FLAG_REG 0x0014 +//dma interrupt flag +#define SP_I2C_DMA_INT_LENGTH0_FLAG (1<<6) +#define SP_I2C_DMA_INT_THRESHOLD_FLAG (1<<5) +#define SP_I2C_DMA_INT_IP_TIMEOUT_FLAG (1<<4) +#define SP_I2C_DMA_INT_GDMA_TIMEOUT_FLAG (1<<3) +#define SP_I2C_DMA_INT_WB_EN_ERROR_FLAG (1<<2) +#define SP_I2C_DMA_INT_WCNT_ERROR_FLAG (1<<1) +#define SP_I2C_DMA_INT_DMA_DONE_FLAG (1<<0) + +#define SP_I2C_DMA_INT_EN_REG 0x0018 +//dma interrupt enable +#define SP_I2C_DMA_EN_LENGTH0_INT (1<<6) +#define SP_I2C_DMA_EN_THRESHOLD_INT (1<<5) +#define SP_I2C_DMA_EN_IP_TIMEOUT_INT (1<<4) +#define SP_I2C_DMA_EN_GDMA_TIMEOUT_INT (1<<3) +#define SP_I2C_DMA_EN_WB_EN_ERROR_INT (1<<2) +#define SP_I2C_DMA_EN_WCNT_ERROR_INT (1<<1) +#define SP_I2C_DMA_EN_DMA_DONE_INT (1<<0) + +//interrupt +#define SP_I2C_INT_RINC_INDEX(x) (x<<18) //bit[20:18] +#define SP_I2C_INT_WINC_INDEX(x) (x<<15) //bit[17:15] +#define SP_I2C_INT_SCL_HOLD_TOO_LONG_FLAG (1<<11) +#define SP_I2C_INT_WFIFO_ENABLE (1<<10) +#define SP_I2C_INT_FULL_FLAG (1<<9) +#define SP_I2C_INT_EMPTY_FLAG (1<<8) +#define SP_I2C_INT_SCL_WAIT_FLAG (1<<7) +#define SP_I2C_INT_EMPTY_THRESHOLD_FLAG (1<<6) +#define SP_I2C_INT_DATA_NACK_FLAG (1<<5) +#define SP_I2C_INT_ADDRESS_NACK_FLAG (1<<4) +#define SP_I2C_INT_BUSY_FLAG (1<<3) +#define SP_I2C_INT_CLKERR_FLAG (1<<2) +#define SP_I2C_INT_DONE_FLAG (1<<1) +#define SP_I2C_INT_SIFBUSY_FLAG (1<<0) + + +//interrupt enable0 +#define SP_I2C_EN0_SCL_HOLD_TOO_LONG_INT (1<<13) +#define SP_I2C_EN0_NACK_INT (1<<12) +#define SP_I2C_EN0_CTL_EMPTY_THRESHOLD(x) (x<<9) //bit[11:9] +#define SP_I2C_EN0_EMPTY_INT (1<<8) +#define SP_I2C_EN0_SCL_WAIT_INT (1<<7) +#define SP_I2C_EN0_EMPTY_THRESHOLD_INT (1<<6) +#define SP_I2C_EN0_DATA_NACK_INT (1<<5) +#define SP_I2C_EN0_ADDRESS_NACK_INT (1<<4) +#define SP_I2C_EN0_BUSY_INT (1<<3) +#define SP_I2C_EN0_CLKERR_INT (1<<2) +#define SP_I2C_EN0_DONE_INT (1<<1) +#define SP_I2C_EN0_SIFBUSY_INT (1<<0) + + +#define SP_I2C_RESET(id, val) ((1 << (16 + id)) | (val << id)) +#define SP_I2C_CLKEN(id, val) ((1 << (16 + id)) | (val << id)) +#define SP_I2C_GCLKEN(id, val) ((1 << (16 + id)) | (val << id)) + +#define SP_I2C_POWER_BASE 0xF8000000 +#define SP_I2C_POWER_CLKEN3 0x0010 +#define SP_I2C_POWER_GCLKEN3 0x0038 +#define SP_I2C_POWER_RESET3 0x0060 + +enum sp_ststus_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 sp_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 sp_i2c_switch_e_ { + I2C_POWER_ALL_SWITCH, + I2C_POWER_NO_SWITCH, +}; + +struct sp_i2c_cmd { + unsigned int dev_id; + unsigned int freq; + unsigned int slave_addr; + unsigned int restart_en; + unsigned int write_cnt; + unsigned int read_cnt; + unsigned char *write_data; + unsigned char *read_data; +}; + +struct sp_i2c_irq_dma_flag { + unsigned char dma_done; + unsigned char write_cnt_err; + unsigned char wb_en_err; + unsigned char gdma_timeout; + unsigned char ipt_timerout; + unsigned char threshold; + unsigned char dma_ligth; +}; + +struct sp_i2c_irq_flag { + unsigned char active_done; + unsigned char addr_nack; + unsigned char data_nack; + unsigned char empty_threshold; + unsigned char fifo_empty; + unsigned char fifo_full; + unsigned char scl_hold_too_long; + unsigned char read_over_flow; +}; + +struct sp_i2c_irq_event { + enum sp_state_e_ rw_state; + struct sp_i2c_irq_flag irq_flag; + struct sp_i2c_irq_dma_flag irq_dma_flag; + unsigned int dev_id; + unsigned int burst_cnt; + unsigned int burst_remainder; + unsigned int data_index; + unsigned int data_total_len; + unsigned int reg_data_index; + unsigned char busy; + unsigned char ret; + unsigned char *data_buf; +}; + + +enum sp_i2c_dma_mode { + I2C_DMA_WRITE_MODE, + I2C_DMA_READ_MODE, +}; + +enum sp_i2c_mode { + I2C_WRITE_MODE, + I2C_READ_MODE, + I2C_RESTART_MODE, +}; + +enum sp_i2c_active_mode { + I2C_TRIGGER, + I2C_AUTO, +}; + + +struct i2c_compatible { + int mode; /* clk source switch*/ + int total_port; /* clk source switch*/ +}; + +struct sp_i2c_dev { + struct i2c_msg *msgs; /* messages currently handled */ + struct i2c_adapter adap; + struct device *dev; + struct sp_i2c_cmd sp_i2c_cmd_info; + struct sp_i2c_irq_event sp_i2c_irq_info; + 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; + unsigned int total_port; +}; + +static unsigned int sp_i2cm_get_int_flag(void __iomem *sr) +{ + return readl(sr + SP_I2C_INT_REG); +} + +static void sp_i2cm_status_clear(void __iomem *sr, unsigned int flag) +{ + unsigned int ctl1; + + ctl1 = readl(sr + SP_I2C_CTL1_REG); + ctl1 |= flag; + writel(ctl1, sr + SP_I2C_CTL1_REG); + + ctl1 = readl(sr + SP_I2C_CTL1_REG); + ctl1 &= (~flag); + writel(ctl1, sr + SP_I2C_CTL1_REG); +} + + +static void sp_i2cm_reset(void __iomem *sr) +{ + unsigned int ctl0; + + ctl0 = readl(sr + SP_I2C_CTL0_REG); + ctl0 |= SP_I2C_CTL0_SW_RESET; + writel(ctl0, sr + SP_I2C_CTL0_REG); + + udelay(2); +} + +static void sp_i2cm_data0_set(void __iomem *sr, unsigned int *wdata) +{ + writel(*wdata, sr + SP_I2C_DATA0_REG); +} + + +static void sp_i2cm_int_en0_disable(void __iomem *sr, unsigned int int0) +{ + unsigned int val; + + val = readl(sr + SP_I2C_INT_EN0_REG); + val &= (~int0); + writel(val, sr + SP_I2C_INT_EN0_REG); + +} + +static void sp_i2cm_rdata_flag_get(void __iomem *sr, unsigned int *flag) +{ + *flag = readl(sr + SP_I2C_STATUS3_REG); +} + +static unsigned int sp_i2cm_over_flag_get(void __iomem *sr) +{ + return readl(sr + SP_I2C_STATUS4_REG); +} + +static void sp_i2cm_data_get(void __iomem *sr, unsigned int index, unsigned int *rdata) +{ + switch (index) { + case 0: + *rdata = readl(sr + SP_I2C_DATA0_REG); + break; + + case 1: + *rdata = readl(sr + SP_I2C_DATA4_REG); + break; + + case 2: + *rdata = readl(sr + SP_I2C_DATA8_REG); + break; + + case 3: + *rdata = readl(sr + SP_I2C_DATA12_REG); + break; + + case 4: + *rdata = readl(sr + SP_I2C_DATA16_REG); + break; + + case 5: + *rdata = readl(sr + SP_I2C_DATA20_REG); + break; + + case 6: + *rdata = readl(sr + SP_I2C_DATA24_REG); + break; + + case 7: + *rdata = readl(sr + SP_I2C_DATA28_REG); + break; + + default: + break; + } +} + +static void sp_i2cm_rdata_flag_clear(void __iomem *sr, unsigned int flag) +{ + writel(flag, sr + SP_I2C_CTL6_REG); + writel(0, sr + SP_I2C_CTL6_REG); +} + +static void sp_i2cm_clock_freq_set(void __iomem *sr, unsigned int freq) +{ + unsigned int div; + unsigned int ctl0, ctl2; + + div = SP_CLK_SOURCE_FREQ / freq; + div -= 1; + if (SP_CLK_SOURCE_FREQ % freq != 0) + div += 1; + + if (div > SP_I2C_CTL2_FREQ_CUSTOM_MASK) + div = SP_I2C_CTL2_FREQ_CUSTOM_MASK; + + ctl0 = readl(sr + SP_I2C_CTL0_REG); + ctl0 &= (~SP_I2C_CTL0_FREQ(SP_I2C_CTL0_FREQ_MASK)); + writel(ctl0, sr + SP_I2C_CTL0_REG); + + ctl2 = readl(sr + SP_I2C_CTL2_REG); + ctl2 &= (~SP_I2C_CTL2_FREQ_CUSTOM(SP_I2C_CTL2_FREQ_CUSTOM_MASK)); + ctl2 |= SP_I2C_CTL2_FREQ_CUSTOM(div); + writel(ctl2, sr + SP_I2C_CTL2_REG); + +} + +static void sp_i2cm_slave_addr_set(void __iomem *sr, unsigned int addr) +{ + unsigned int t_addr = addr & SP_I2C_CTL0_SLAVE_ADDR_MASK; + unsigned int ctl0; + + ctl0 = readl(sr + SP_I2C_CTL0_REG); + ctl0 &= (~SP_I2C_CTL0_SLAVE_ADDR(SP_I2C_CTL0_SLAVE_ADDR_MASK)); + ctl0 |= SP_I2C_CTL0_SLAVE_ADDR(t_addr); + writel(ctl0, sr + SP_I2C_CTL0_REG); +} + +static void sp_i2cm_scl_delay_set(void __iomem *sr, unsigned int delay) +{ + unsigned int ctl2; + + ctl2 = readl(sr + SP_I2C_CTL2_REG); + ctl2 &= (~SP_I2C_CTL2_SCL_DELAY(SP_I2C_CTL2_SCL_DELAY_MASK)); + ctl2 |= SP_I2C_CTL2_SCL_DELAY(delay); + ctl2 &= (~(SP_I2C_CTL2_SDA_HALF_ENABLE)); + writel(ctl2, sr + SP_I2C_CTL2_REG); +} + +static void sp_i2cm_trans_cnt_set(void __iomem *sr, unsigned int write_cnt, + unsigned int read_cnt) +{ + unsigned int t_write = write_cnt & SP_I2C_CTL7_RW_COUNT_MASK; + unsigned int t_read = read_cnt & SP_I2C_CTL7_RW_COUNT_MASK; + unsigned int ctl7; + + ctl7 = SP_I2C_CTL7_WRCOUNT(t_write) | SP_I2C_CTL7_RDCOUNT(t_read); + writel(ctl7, sr + SP_I2C_CTL7_REG); +} + +static void sp_i2cm_active_mode_set(void __iomem *sr, enum sp_i2c_active_mode mode) +{ + unsigned int val; + + val = readl(sr + SP_I2C_MOD_REG); + val &= (~(SP_I2C_MODE_MANUAL_MODE | SP_I2C_MODE_MANUAL_TRIG)); + switch (mode) { + default: + case I2C_TRIGGER: + break; + + case I2C_AUTO: + val |= SP_I2C_MODE_MANUAL_MODE; + break; + } + writel(val, sr + SP_I2C_MOD_REG); +} + +static void sp_i2cm_data_set(void __iomem *sr, unsigned int *wdata) +{ + writel(wdata[0], sr + SP_I2C_DATA0_REG); + writel(wdata[1], sr + SP_I2C_DATA4_REG); + writel(wdata[2], sr + SP_I2C_DATA8_REG); + writel(wdata[3], sr + SP_I2C_DATA12_REG); + writel(wdata[4], sr + SP_I2C_DATA16_REG); + writel(wdata[5], sr + SP_I2C_DATA20_REG); + writel(wdata[6], sr + SP_I2C_DATA24_REG); + writel(wdata[7], sr + SP_I2C_DATA28_REG); +} + +static void sp_i2cm_rw_mode_set(void __iomem *sr, enum sp_i2c_mode rw_mode) +{ + unsigned int ctl0; + + ctl0 = readl(sr + SP_I2C_CTL0_REG); + switch (rw_mode) { + default: + case I2C_WRITE_MODE: + ctl0 &= (~(SP_I2C_CTL0_PREFETCH | + SP_I2C_CTL0_RESTART_EN | SP_I2C_CTL0_SUBADDR_EN)); + break; + + case I2C_READ_MODE: + ctl0 &= (~(SP_I2C_CTL0_RESTART_EN | SP_I2C_CTL0_SUBADDR_EN)); + ctl0 |= SP_I2C_CTL0_PREFETCH; + break; + + case I2C_RESTART_MODE: + ctl0 |= (SP_I2C_CTL0_PREFETCH | + SP_I2C_CTL0_RESTART_EN | SP_I2C_CTL0_SUBADDR_EN); + break; + } + writel(ctl0, sr + SP_I2C_CTL0_REG); +} + + +static void sp_i2cm_int_en0_set(void __iomem *sr, unsigned int int0) +{ + writel(int0, sr + SP_I2C_INT_EN0_REG); +} + +static void sp_i2cm_int_en1_set(void __iomem *sr, unsigned int rdata_en) +{ + writel(rdata_en, sr + SP_I2C_INT_EN1_REG); +} + +static void sp_i2cm_int_en2_set(void __iomem *sr, unsigned int overflow_en) +{ + writel(overflow_en, sr + SP_I2C_INT_EN2_REG); +} + +static void sp_i2cm_enable(unsigned int device_id, void __iomem *membase) +{ + writel(SP_I2C_RESET(device_id, 0), membase + SP_I2C_POWER_CLKEN3); + writel(SP_I2C_CLKEN(device_id, 1), membase + SP_I2C_POWER_GCLKEN3); + writel(SP_I2C_GCLKEN(device_id, 0), membase + SP_I2C_POWER_RESET3); +} + +static void sp_i2cm_manual_trigger(void __iomem *sr) +{ + unsigned int val; + + val = readl(sr + SP_I2C_MOD_REG); + val |= SP_I2C_MODE_MANUAL_TRIG; + writel(val, sr + SP_I2C_MOD_REG); +} + +static void sp_i2cm_int_en0_with_thershold_set(void __iomem *sr, + unsigned int int0, unsigned char threshold) +{ + unsigned int val; + + val = (int0 | SP_I2C_EN0_CTL_EMPTY_THRESHOLD(threshold)); + writel(val, sr + SP_I2C_INT_EN0_REG); +} + +static void sp_i2cm_dma_mode_enable(void __iomem *sr) +{ + unsigned int val; + + val = readl(sr + SP_I2C_MOD_REG); + val |= SP_I2C_MODE_DMA_MODE; + writel(val, sr + SP_I2C_MOD_REG); +} + +static unsigned int sp_i2cm_get_dma_int_flag(void __iomem *sr_dma) +{ + return readl(sr_dma + SP_I2C_INT_REG); +} + + +static void sp_i2cm_dma_int_flag_clear(void __iomem *sr_dma, unsigned int flag) +{ + unsigned int val; + + val = readl(sr_dma + SP_I2C_DMA_FLAG_REG); + val |= flag; + writel(val, sr_dma + SP_I2C_DMA_FLAG_REG); +} + + +static void sp_i2cm_dma_addr_set(void __iomem *sr_dma, unsigned int addr) +{ + writel(addr, sr_dma + SP_I2C_DMA_ADDR_REG); +} + +static void sp_i2cm_dma_length_set(void __iomem *sr_dma, unsigned int length) +{ + length &= (0xFFFF); //only support 16 bit + writel(length, sr_dma + SP_I2C_DMA_LEN_REG); +} + +static void sp_i2cm_dma_rw_mode_set(void __iomem *sr_dma, + enum sp_i2c_dma_mode rw_mode) +{ + unsigned int val; + + val = readl(sr_dma + SP_I2C_DMA_CONF_REG); + switch (rw_mode) { + default: + case I2C_DMA_WRITE_MODE: + val |= SP_I2C_DMA_CFG_DMA_MODE; + break; + + case I2C_DMA_READ_MODE: + val &= (~SP_I2C_DMA_CFG_DMA_MODE); + break; + } + writel(val, sr_dma + SP_I2C_DMA_CONF_REG); +} + +static void sp_i2cm_dma_int_en_set(void __iomem *sr_dma, unsigned int dma_int) +{ + writel(dma_int, sr_dma + SP_I2C_DMA_INT_EN_REG); +} + +static void sp_i2cm_dma_go_set(void __iomem *sr_dma) +{ + unsigned int val; + + val = readl(sr_dma + SP_I2C_DMA_CONF_REG); + val |= SP_I2C_DMA_CFG_DMA_GO; + writel(val, sr_dma + SP_I2C_DMA_CONF_REG); +} + + +static void _sp_i2cm_intflag_check(struct sp_i2c_dev *sp_i2c_dev_info, + struct sp_i2c_irq_event *sp_i2c_irq_event) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + unsigned int int_flag = 0; + unsigned int overflow_flag = 0; + + int_flag = sp_i2cm_get_int_flag(sr); + + if (int_flag & SP_I2C_INT_DONE_FLAG) + sp_i2c_irq_event->irq_flag.active_done = 1; + else + sp_i2c_irq_event->irq_flag.active_done = 0; + + if (int_flag & SP_I2C_INT_ADDRESS_NACK_FLAG) + sp_i2c_irq_event->irq_flag.addr_nack = 1; + else + sp_i2c_irq_event->irq_flag.addr_nack = 0; + + if (int_flag & SP_I2C_INT_DATA_NACK_FLAG) + sp_i2c_irq_event->irq_flag.data_nack = 1; + else + sp_i2c_irq_event->irq_flag.data_nack = 0; + + // write use + if (int_flag & SP_I2C_INT_EMPTY_THRESHOLD_FLAG) + sp_i2c_irq_event->irq_flag.empty_threshold = 1; + else + sp_i2c_irq_event->irq_flag.empty_threshold = 0; + + // write use + if (int_flag & SP_I2C_INT_EMPTY_FLAG) + sp_i2c_irq_event->irq_flag.fifo_empty = 1; + else + sp_i2c_irq_event->irq_flag.fifo_empty = 0; + + // write use (for debug) + if (int_flag & SP_I2C_INT_FULL_FLAG) + sp_i2c_irq_event->irq_flag.fifo_full = 1; + else + sp_i2c_irq_event->irq_flag.fifo_full = 0; + + if (int_flag & SP_I2C_INT_SCL_HOLD_TOO_LONG_FLAG) + sp_i2c_irq_event->irq_flag.scl_hold_too_long = 1; + else + sp_i2c_irq_event->irq_flag.scl_hold_too_long = 0; + + sp_i2cm_status_clear(sr, SP_I2C_CTL1_ALL_CLR); + + overflow_flag = sp_i2cm_over_flag_get(sr); + + if (overflow_flag) + sp_i2c_irq_event->irq_flag.read_over_flow = 1; + else + sp_i2c_irq_event->irq_flag.read_over_flow = 0; + +} + +static void _sp_i2cm_dma_intflag_check(struct sp_i2c_dev *sp_i2c_dev_info, + struct sp_i2c_irq_event *sp_i2c_irq_event) +{ + void __iomem *sr_dma = sp_i2c_dev_info->i2c_dma_regs; + unsigned int int_flag = 0; + + int_flag = sp_i2cm_get_dma_int_flag(sr_dma); + + if (int_flag & SP_I2C_DMA_INT_DMA_DONE_FLAG) + sp_i2c_irq_event->irq_dma_flag.dma_done = 1; + else + sp_i2c_irq_event->irq_dma_flag.dma_done = 0; + + if (int_flag & SP_I2C_DMA_INT_WCNT_ERROR_FLAG) + sp_i2c_irq_event->irq_dma_flag.write_cnt_err = 1; + else + sp_i2c_irq_event->irq_dma_flag.write_cnt_err = 0; + + if (int_flag & SP_I2C_DMA_INT_WB_EN_ERROR_FLAG) + sp_i2c_irq_event->irq_dma_flag.wb_en_err = 1; + else + sp_i2c_irq_event->irq_dma_flag.wb_en_err = 0; + + if (int_flag & SP_I2C_DMA_INT_GDMA_TIMEOUT_FLAG) + sp_i2c_irq_event->irq_dma_flag.gdma_timeout = 1; + else + sp_i2c_irq_event->irq_dma_flag.gdma_timeout = 0; + + if (int_flag & SP_I2C_DMA_INT_IP_TIMEOUT_FLAG) + sp_i2c_irq_event->irq_dma_flag.ipt_timerout = 1; + else + sp_i2c_irq_event->irq_dma_flag.ipt_timerout = 0; + + if (int_flag & SP_I2C_DMA_INT_LENGTH0_FLAG) + sp_i2c_irq_event->irq_dma_flag.dma_ligth = 1; + else + sp_i2c_irq_event->irq_dma_flag.dma_ligth = 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 sp_i2c_dev *sp_i2c_dev_info = args; + struct sp_i2c_irq_event *sp_i2c_irq_event = &(sp_i2c_dev_info->sp_i2c_irq_info); + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + unsigned char w_data[32] = {0}; + unsigned char r_data[SP_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(sp_i2c_dev_info, sp_i2c_irq_event); + +switch (sp_i2c_irq_event->rw_state) { +case I2C_WRITE_STATE: +case I2C_DMA_WRITE_STATE: + if (sp_i2c_irq_event->irq_flag.active_done) { + sp_i2c_irq_event->ret = I2C_SUCCESS; + wake_up(&sp_i2c_dev_info->wait); + } else if (sp_i2c_irq_event->irq_flag.addr_nack || sp_i2c_irq_event->irq_flag.data_nack) { + + if (sp_i2c_irq_event->rw_state == I2C_DMA_WRITE_STATE) + dev_err(sp_i2c_dev_info->dev, "DMA wtire NACK!!\n"); + else + dev_err(sp_i2c_dev_info->dev, "wtire NACK!!\n"); + + sp_i2c_irq_event->ret = I2C_ERR_RECEIVE_NACK; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); + } else if (sp_i2c_irq_event->irq_flag.scl_hold_too_long) { + sp_i2c_irq_event->ret = I2C_ERR_SCL_HOLD_TOO_LONG; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); + } else if (sp_i2c_irq_event->irq_flag.fifo_empty) { + sp_i2c_irq_event->ret = I2C_ERR_FIFO_EMPTY; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); + } else if ((sp_i2c_irq_event->burst_cnt > 0) && + (sp_i2c_irq_event->rw_state == I2C_WRITE_STATE)) { + if (sp_i2c_irq_event->irq_flag.empty_threshold) { + for (i = 0; i < SP_I2C_EMPTY_THRESHOLD; i++) { + for (j = 0; j < 4; j++) { + + if (sp_i2c_irq_event->data_index >= + sp_i2c_irq_event->data_total_len) + w_data[j] = 0; + else + w_data[j] = + sp_i2c_irq_event->data_buf[sp_i2c_irq_event->data_index]; + + sp_i2c_irq_event->data_index++; + } + sp_i2cm_data0_set(sr, (unsigned int *)w_data); + sp_i2c_irq_event->burst_cnt--; + if (sp_i2c_irq_event->burst_cnt == 0) { + sp_i2cm_int_en0_disable(sr, + (SP_I2C_EN0_EMPTY_THRESHOLD_INT | SP_I2C_EN0_EMPTY_INT)); + break; + } + } + sp_i2cm_status_clear(sr, SP_I2C_CTL1_EMPTY_THRESHOLD_CLR); + } + } + break; + +case I2C_READ_STATE: +case I2C_DMA_READ_STATE: + if (sp_i2c_irq_event->irq_flag.addr_nack || sp_i2c_irq_event->irq_flag.data_nack) { + + if (sp_i2c_irq_event->rw_state == I2C_DMA_READ_STATE) + dev_err(sp_i2c_dev_info->dev, "DMA read NACK!!\n"); + else + dev_err(sp_i2c_dev_info->dev, "read NACK!!\n"); + + sp_i2c_irq_event->ret = I2C_ERR_RECEIVE_NACK; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); + } else if (sp_i2c_irq_event->irq_flag.scl_hold_too_long) { + sp_i2c_irq_event->ret = I2C_ERR_SCL_HOLD_TOO_LONG; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); + } else if (sp_i2c_irq_event->irq_flag.read_over_flow) { + sp_i2c_irq_event->ret = I2C_ERR_RDATA_OVERFLOW; + sp_i2c_irq_event->irq_flag.active_done = 1; + wake_up(&sp_i2c_dev_info->wait); + sp_i2cm_reset(sr); +} else { + if ((sp_i2c_irq_event->burst_cnt > 0) && (sp_i2c_irq_event->rw_state == I2C_READ_STATE)) { + sp_i2cm_rdata_flag_get(sr, &rdata_flag); + for (i = 0; i < (32 / SP_I2C_BURST_RDATA_BYTES); i++) { + bit_index = (SP_I2C_BURST_RDATA_BYTES - 1) + (SP_I2C_BURST_RDATA_BYTES * i); + if (rdata_flag & (1 << bit_index)) { + for (j = 0; j < (SP_I2C_BURST_RDATA_BYTES/4); j++) { + k = sp_i2c_irq_event->reg_data_index + j; + if (k >= 8) + k -= 8; + + sp_i2cm_data_get(sr, k, +(unsigned int *)(&sp_i2c_irq_event->data_buf[sp_i2c_irq_event->data_index])); + sp_i2c_irq_event->data_index += 4; + } + sp_i2cm_rdata_flag_clear(sr, + (((1 << SP_I2C_BURST_RDATA_BYTES) - 1) << (SP_I2C_BURST_RDATA_BYTES * i))); + sp_i2c_irq_event->reg_data_index += (SP_I2C_BURST_RDATA_BYTES / 4); + if (sp_i2c_irq_event->reg_data_index >= 8) + sp_i2c_irq_event->burst_cnt--; + } + } + } + if (sp_i2c_irq_event->irq_flag.active_done) { + if ((sp_i2c_irq_event->burst_remainder) && + (sp_i2c_irq_event->rw_state == I2C_READ_STATE)) { + j = 0; + for (i = 0; i < (SP_I2C_BURST_RDATA_BYTES/4); i++) { + k = sp_i2c_irq_event->reg_data_index + i; + if (k >= 8) + k -= 8; + + sp_i2cm_data_get(sr, k, (unsigned int *)(&r_data[j])); + j += 4; + } + + for (i = 0; i < sp_i2c_irq_event->burst_remainder; i++) + sp_i2c_irq_event->data_buf[sp_i2c_irq_event->data_index + i] + = r_data[i]; + } + sp_i2c_irq_event->ret = I2C_SUCCESS; + wake_up(&sp_i2c_dev_info->wait); + } + } + break; + +default: + break; +} + //switch case + + _sp_i2cm_dma_intflag_check(sp_i2c_dev_info, sp_i2c_irq_event); + + switch (sp_i2c_irq_event->rw_state) { + case I2C_DMA_WRITE_STATE: + if (sp_i2c_irq_event->irq_dma_flag.dma_done) { + sp_i2c_irq_event->ret = I2C_SUCCESS; + wake_up(&sp_i2c_dev_info->wait); + } + break; + + case I2C_DMA_READ_STATE: + if (sp_i2c_irq_event->irq_dma_flag.dma_done) { + sp_i2c_irq_event->ret = I2C_SUCCESS; + wake_up(&sp_i2c_dev_info->wait); + } + break; + + default: + break; + } + + return IRQ_HANDLED; +} + + +static int _sp_i2cm_init(unsigned int device_id, struct sp_i2c_dev *sp_i2c_dev_info) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + + if (device_id >= sp_i2c_dev_info->total_port) + return I2C_ERR_INVALID_DEVID; + + sp_i2cm_reset(sr); + + return I2C_SUCCESS; +} + +static int _sp_i2cm_get_irq(struct platform_device *pdev, struct sp_i2c_dev *sp_i2c_dev_info) +{ + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(sp_i2c_dev_info->dev, " get irq number fail, irq = %d\n", irq); + return -ENODEV; + } + + sp_i2c_dev_info->irq = irq; + return I2C_SUCCESS; +} + +static int _sp_i2cm_get_resources(struct platform_device *pdev, + struct sp_i2c_dev *sp_i2c_dev_info) +{ + int ret; + struct resource *res; + + /* find and map our resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, SP_I2C_REG_NAME); + if (res) { + sp_i2c_dev_info->i2c_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sp_i2c_dev_info->i2c_regs)) + dev_err(&pdev->dev, " platform_get_resource_byname fail\n"); + } else { + dev_err(sp_i2c_dev_info->dev, "adapter %s (%d)\n", __func__, __LINE__); + return -ENODEV; + } + + + /* find DMA and map our resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, SP_I2C_DMA_REG_NAME); + if (res) { + sp_i2c_dev_info->i2c_dma_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sp_i2c_dev_info->i2c_dma_regs)) + dev_err(&pdev->dev, " platform_get_resource_byname fail\n"); + } else { + dev_err(sp_i2c_dev_info->dev, "adapter %s (%d)\n", __func__, __LINE__); + return -ENODEV; + } + + ret = _sp_i2cm_get_irq(pdev, sp_i2c_dev_info); + if (ret) { + dev_err(sp_i2c_dev_info->dev, " line (%d) ret = %d\n", __LINE__, ret); + return ret; + } + + return I2C_SUCCESS; +} + +static int sp_i2cm_read(struct sp_i2c_cmd *sp_i2c_cmd_info, struct sp_i2c_dev *sp_i2c_dev_info) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + struct sp_i2c_irq_event *sp_i2c_irq_event = &(sp_i2c_dev_info->sp_i2c_irq_info); + 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; + + if (sp_i2c_cmd_info->dev_id > sp_i2c_dev_info->total_port) + return I2C_ERR_INVALID_DEVID; + + if (sp_i2c_irq_event->busy) { + dev_err(sp_i2c_dev_info->dev, "I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(sp_i2c_irq_event, 0, sizeof(*sp_i2c_irq_event)); + sp_i2c_irq_event->busy = 1; + + write_cnt = sp_i2c_cmd_info->write_cnt; + read_cnt = sp_i2c_cmd_info->read_cnt; + + if (sp_i2c_cmd_info->restart_en) { + //if ((write_cnt > 32) || (write_cnt == 0)) { + if (write_cnt > 32) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, + "I2C write count is invalid !! write count=%d\n", write_cnt); + return I2C_ERR_INVALID_CNT; + } + } + + if ((read_cnt > 0xFFFF) || (read_cnt == 0)) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, + "I2C read count is invalid !! read count=%d\n", read_cnt); + return I2C_ERR_INVALID_CNT; + } + + burst_cnt = read_cnt / SP_I2C_BURST_RDATA_BYTES; + burst_r = read_cnt % SP_I2C_BURST_RDATA_BYTES; + + int0 = (SP_I2C_EN0_SCL_HOLD_TOO_LONG_INT | SP_I2C_EN0_EMPTY_INT | SP_I2C_EN0_DATA_NACK_INT + | SP_I2C_EN0_ADDRESS_NACK_INT | SP_I2C_EN0_DONE_INT); + if (burst_cnt) { + int1 = SP_I2C_BURST_RDATA_FLAG; + int2 = SP_I2C_BURST_RDATA_ALL_FLAG; + } + + sp_i2c_irq_event->rw_state = I2C_READ_STATE; + sp_i2c_irq_event->burst_cnt = burst_cnt; + sp_i2c_irq_event->burst_remainder = burst_r; + sp_i2c_irq_event->data_index = 0; + sp_i2c_irq_event->reg_data_index = 0; + sp_i2c_irq_event->data_total_len = read_cnt; + sp_i2c_irq_event->data_buf = sp_i2c_cmd_info->read_data; + + //hal_i2cm_reset(sp_i2c_cmd_info->dev_id); + sp_i2cm_reset(sr); + sp_i2cm_clock_freq_set(sr, sp_i2c_cmd_info->freq); + sp_i2cm_slave_addr_set(sr, sp_i2c_cmd_info->slave_addr); + sp_i2cm_scl_delay_set(sr, SP_I2C_SCL_DELAY); + sp_i2cm_trans_cnt_set(sr, write_cnt, read_cnt); + sp_i2cm_active_mode_set(sr, I2C_TRIGGER); + + if (sp_i2c_cmd_info->restart_en) { + for (i = 0; i < write_cnt; i++) + w_data[i] = sp_i2c_cmd_info->write_data[i]; + + sp_i2cm_data_set(sr, (unsigned int *)w_data); + sp_i2cm_rw_mode_set(sr, I2C_RESTART_MODE); + } else { + 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(sp_i2c_dev_info->wait, + sp_i2c_irq_event->irq_flag.active_done, (SP_I2C_SLEEP_TIMEOUT * HZ) / 500); + if (ret == 0) { + dev_err(sp_i2c_dev_info->dev, "I2C read timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = sp_i2c_irq_event->ret; + } + sp_i2cm_reset(sr); + sp_i2c_irq_event->rw_state = I2C_IDLE_STATE; + sp_i2c_irq_event->busy = 0; + + return ret; +} + +static int sp_i2cm_write(struct sp_i2c_cmd *sp_i2c_cmd_info, struct sp_i2c_dev *sp_i2c_dev_info) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + struct sp_i2c_irq_event *sp_i2c_irq_event = &(sp_i2c_dev_info->sp_i2c_irq_info); + 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; + + if (sp_i2c_cmd_info->dev_id > sp_i2c_dev_info->total_port) + return I2C_ERR_INVALID_DEVID; + + if (sp_i2c_irq_event->busy) { + dev_err(sp_i2c_dev_info->dev, "I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(sp_i2c_irq_event, 0, sizeof(*sp_i2c_irq_event)); + sp_i2c_irq_event->busy = 1; + + write_cnt = sp_i2c_cmd_info->write_cnt; + + if (write_cnt > 0xFFFF) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, + "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] = sp_i2c_cmd_info->write_data[i]; + } else { + for (i = 0; i < write_cnt; i++) + w_data[i] = sp_i2c_cmd_info->write_data[i]; + } + + int0 = (SP_I2C_EN0_SCL_HOLD_TOO_LONG_INT | SP_I2C_EN0_EMPTY_INT | SP_I2C_EN0_DATA_NACK_INT + | SP_I2C_EN0_ADDRESS_NACK_INT | SP_I2C_EN0_DONE_INT); + + if (burst_cnt) + int0 |= SP_I2C_EN0_EMPTY_THRESHOLD_INT; + + sp_i2c_irq_event->rw_state = I2C_WRITE_STATE; + sp_i2c_irq_event->burst_cnt = burst_cnt; + sp_i2c_irq_event->data_index = i; + sp_i2c_irq_event->data_total_len = write_cnt; + sp_i2c_irq_event->data_buf = sp_i2c_cmd_info->write_data; + + sp_i2cm_reset(sr); + sp_i2cm_clock_freq_set(sr, sp_i2c_cmd_info->freq); + sp_i2cm_slave_addr_set(sr, sp_i2c_cmd_info->slave_addr); + sp_i2cm_scl_delay_set(sr, SP_I2C_SCL_DELAY); + 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, SP_I2C_EMPTY_THRESHOLD); + else + sp_i2cm_int_en0_set(sr, int0); + + sp_i2cm_manual_trigger(sr); //start send data + + ret = wait_event_timeout(sp_i2c_dev_info->wait, + sp_i2c_irq_event->irq_flag.active_done, (SP_I2C_SLEEP_TIMEOUT * HZ) / 500); + if (ret == 0) { + dev_err(sp_i2c_dev_info->dev, "I2C write timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = sp_i2c_irq_event->ret; + } + sp_i2cm_reset(sr); + sp_i2c_irq_event->rw_state = I2C_IDLE_STATE; + sp_i2c_irq_event->busy = 0; + + return ret; +} + + +static int sp_i2cm_dma_write(struct sp_i2c_cmd *sp_i2c_cmd_info, struct sp_i2c_dev *sp_i2c_dev_info) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + void __iomem *sr_dma = (struct regs_i2cm_dma_s *)sp_i2c_dev_info->i2c_dma_regs; + struct sp_i2c_irq_event *sp_i2c_irq_event = &(sp_i2c_dev_info->sp_i2c_irq_info); + unsigned int int0 = 0; + int ret = I2C_SUCCESS; + unsigned int dma_int = 0; + dma_addr_t dma_w_addr = 0; + + if (sp_i2c_cmd_info->dev_id > sp_i2c_dev_info->total_port) + return I2C_ERR_INVALID_DEVID; + + if (sp_i2c_dev_info->mode == I2C_POWER_ALL_SWITCH) + sp_i2cm_enable(0, (void __iomem *)SP_I2C_POWER_BASE); + + if (sp_i2c_irq_event->busy) { + dev_err(sp_i2c_dev_info->dev, "I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(sp_i2c_irq_event, 0, sizeof(*sp_i2c_irq_event)); + sp_i2c_irq_event->busy = 1; + + if (sp_i2c_cmd_info->write_cnt > 0xFFFF) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, " I2C write count is invalid !! write count=%d\n", + sp_i2c_cmd_info->write_cnt); + return I2C_ERR_INVALID_CNT; + } + + sp_i2c_irq_event->rw_state = I2C_DMA_WRITE_STATE; + + dma_w_addr = dma_map_single(sp_i2c_dev_info->dev, sp_i2c_cmd_info->write_data, + sp_i2c_cmd_info->write_cnt, DMA_TO_DEVICE); + + if (dma_mapping_error(sp_i2c_dev_info->dev, dma_w_addr)) { + dev_err(sp_i2c_dev_info->dev, "I2C dma_w_addr fail\n"); + dma_w_addr = sp_i2c_dev_info->dma_phy_base; + memcpy(sp_i2c_dev_info->dma_vir_base, + sp_i2c_cmd_info->write_data, sp_i2c_cmd_info->write_cnt); + } + + int0 = (SP_I2C_EN0_SCL_HOLD_TOO_LONG_INT | SP_I2C_EN0_EMPTY_INT + | SP_I2C_EN0_DATA_NACK_INT | SP_I2C_EN0_ADDRESS_NACK_INT | SP_I2C_EN0_DONE_INT); + + + dma_int = SP_I2C_DMA_EN_DMA_DONE_INT; + + + sp_i2cm_reset(sr); + sp_i2cm_dma_mode_enable(sr); + sp_i2cm_clock_freq_set(sr, sp_i2c_cmd_info->freq); + sp_i2cm_slave_addr_set(sr, sp_i2c_cmd_info->slave_addr); + sp_i2cm_scl_delay_set(sr, SP_I2C_SCL_DELAY); + 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, sp_i2c_cmd_info->write_cnt); + 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(sp_i2c_dev_info->wait, + sp_i2c_irq_event->irq_dma_flag.dma_done, (SP_I2C_SLEEP_TIMEOUT * HZ) / 200); + if (ret == 0) { + dev_err(sp_i2c_dev_info->dev, "I2C DMA write timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = sp_i2c_irq_event->ret; + } + sp_i2cm_status_clear(sr, 0xFFFFFFFF); + + if (dma_w_addr != sp_i2c_dev_info->dma_phy_base) + dma_unmap_single(sp_i2c_dev_info->dev, dma_w_addr, + sp_i2c_cmd_info->write_cnt, DMA_TO_DEVICE); + + + + sp_i2c_irq_event->rw_state = I2C_IDLE_STATE; + sp_i2c_irq_event->busy = 0; + + sp_i2cm_reset(sr); + + return ret; +} + +static int sp_i2cm_dma_read(struct sp_i2c_cmd *sp_i2c_cmd_info, struct sp_i2c_dev *sp_i2c_dev_info) +{ + void __iomem *sr = sp_i2c_dev_info->i2c_regs; + void __iomem *sr_dma = (struct regs_i2cm_dma_s *)sp_i2c_dev_info->i2c_dma_regs; + struct sp_i2c_irq_event *sp_i2c_irq_event = &(sp_i2c_dev_info->sp_i2c_irq_info); + 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; + + if (sp_i2c_cmd_info->dev_id > sp_i2c_dev_info->total_port) + return I2C_ERR_INVALID_DEVID; + + if (sp_i2c_dev_info->mode == I2C_POWER_ALL_SWITCH) + sp_i2cm_enable(0, (void __iomem *)SP_I2C_POWER_BASE); + + if (sp_i2c_irq_event->busy) { + dev_err(sp_i2c_dev_info->dev, "I2C is busy !!\n"); + return I2C_ERR_I2C_BUSY; + } + + memset(sp_i2c_irq_event, 0, sizeof(*sp_i2c_irq_event)); + sp_i2c_irq_event->busy = 1; + + write_cnt = sp_i2c_cmd_info->write_cnt; + read_cnt = sp_i2c_cmd_info->read_cnt; + + if (sp_i2c_cmd_info->restart_en) { + if (write_cnt > 32) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, + "I2C write count is invalid !! write count=%d\n", write_cnt); + return I2C_ERR_INVALID_CNT; + } + } + + if ((read_cnt > 0xFFFF) || (read_cnt == 0)) { + sp_i2c_irq_event->busy = 0; + dev_err(sp_i2c_dev_info->dev, + "I2C read count is invalid !! read count=%d\n", read_cnt); + return I2C_ERR_INVALID_CNT; + } + + dma_r_addr = dma_map_single(sp_i2c_dev_info->dev, sp_i2c_cmd_info->read_data, + sp_i2c_cmd_info->read_cnt, DMA_FROM_DEVICE); + + + if (dma_mapping_error(sp_i2c_dev_info->dev, dma_r_addr)) { + dev_err(sp_i2c_dev_info->dev, "I2C dma_r_addr fail\n"); + dma_r_addr = sp_i2c_dev_info->dma_phy_base; + } + + + int0 = (SP_I2C_EN0_SCL_HOLD_TOO_LONG_INT | SP_I2C_EN0_EMPTY_INT | SP_I2C_EN0_DATA_NACK_INT + | SP_I2C_EN0_ADDRESS_NACK_INT | SP_I2C_EN0_DONE_INT); + + dma_int = SP_I2C_DMA_EN_DMA_DONE_INT; + + sp_i2c_irq_event->rw_state = I2C_DMA_READ_STATE; + + sp_i2c_irq_event->data_index = 0; + sp_i2c_irq_event->reg_data_index = 0; + sp_i2c_irq_event->data_total_len = read_cnt; + + sp_i2cm_reset(sr); + sp_i2cm_dma_mode_enable(sr); + sp_i2cm_clock_freq_set(sr, sp_i2c_cmd_info->freq); + sp_i2cm_slave_addr_set(sr, sp_i2c_cmd_info->slave_addr); + sp_i2cm_scl_delay_set(sr, SP_I2C_SCL_DELAY); + + if (sp_i2c_cmd_info->restart_en) { + 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] = sp_i2c_cmd_info->write_data[i]; + + sp_i2cm_data_set(sr, (unsigned int *)w_data); + } else { + 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, sp_i2c_cmd_info->read_cnt); + 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 (sp_i2c_cmd_info->restart_en) + sp_i2cm_manual_trigger(sr); //start send data + + + ret = wait_event_timeout(sp_i2c_dev_info->wait, + sp_i2c_irq_event->irq_dma_flag.dma_done, (SP_I2C_SLEEP_TIMEOUT * HZ) / 200); + if (ret == 0) { + dev_err(sp_i2c_dev_info->dev, "I2C DMA read timeout !!\n"); + ret = I2C_ERR_TIMEOUT_OUT; + } else { + ret = sp_i2c_irq_event->ret; + } + sp_i2cm_status_clear(sr, 0xFFFFFFFF); + + //copy data from virtual addr to sp_i2c_cmd_info->read_data + + if (dma_r_addr == sp_i2c_dev_info->dma_phy_base) + memcpy(sp_i2c_cmd_info->read_data, + sp_i2c_dev_info->dma_vir_base, sp_i2c_cmd_info->read_cnt); + else + dma_unmap_single(sp_i2c_dev_info->dev, dma_r_addr, + sp_i2c_cmd_info->read_cnt, DMA_FROM_DEVICE); + + sp_i2c_irq_event->rw_state = I2C_IDLE_STATE; + sp_i2c_irq_event->busy = 0; + + + sp_i2cm_reset(sr); + return ret; +} + +static int sp_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct sp_i2c_dev *sp_i2c_dev_info = adap->algo_data; + struct sp_i2c_cmd *sp_i2c_cmd_info = &(sp_i2c_dev_info->sp_i2c_cmd_info); + 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; + + ret = pm_runtime_get_sync(sp_i2c_dev_info->dev); + + if (num == 0) + return -EINVAL; + + memset(sp_i2c_cmd_info, 0, sizeof(*sp_i2c_cmd_info)); + sp_i2c_cmd_info->dev_id = adap->nr; + + if (sp_i2c_cmd_info->freq > SP_I2C_FREQ) + sp_i2c_cmd_info->freq = SP_I2C_FREQ; + else + sp_i2c_cmd_info->freq = sp_i2c_dev_info->i2c_clk_freq/1000; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_TEN) + return -EINVAL; + + sp_i2c_cmd_info->slave_addr = 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) { + sp_i2c_cmd_info->write_cnt = restart_write_cnt; + sp_i2c_cmd_info->write_data = restart_w_data; + restart_en = 0; + sp_i2c_cmd_info->restart_en = 1; + } + sp_i2c_cmd_info->read_cnt = msgs[i].len; + sp_i2c_cmd_info->read_data = i2c_get_dma_safe_msg_buf(&msgs[i], 4); + + if ((sp_i2c_cmd_info->read_cnt < 4) || (!sp_i2c_cmd_info->read_data)) { + sp_i2c_cmd_info->read_data = msgs[i].buf; + ret = sp_i2cm_read(sp_i2c_cmd_info, sp_i2c_dev_info); + } else { + ret = sp_i2cm_dma_read(sp_i2c_cmd_info, sp_i2c_dev_info); + i2c_put_dma_safe_msg_buf(sp_i2c_cmd_info->read_data, + &msgs[i], true); + } + + } else { + sp_i2c_cmd_info->write_cnt = msgs[i].len; + sp_i2c_cmd_info->write_data = i2c_get_dma_safe_msg_buf(&msgs[i], 4); + if ((sp_i2c_cmd_info->write_cnt < 4) || + (!sp_i2c_cmd_info->write_data)) { + sp_i2c_cmd_info->write_data = msgs[i].buf; + ret = sp_i2cm_write(sp_i2c_cmd_info, sp_i2c_dev_info); + } else { + ret = sp_i2cm_dma_write(sp_i2c_cmd_info, sp_i2c_dev_info); + i2c_put_dma_safe_msg_buf(sp_i2c_cmd_info->write_data, + &msgs[i], true); + } + } + + if (ret != I2C_SUCCESS) + return -EIO; + } + + pm_runtime_put(sp_i2c_dev_info->dev); + return num; + +} + +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 const struct i2c_compatible i2c_7021_compat = { + .mode = I2C_POWER_ALL_SWITCH, + .total_port = 4, +}; + +static const struct i2c_compatible i2c_645_compat = { + .mode = I2C_POWER_NO_SWITCH, + .total_port = 6, + +}; + +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); + +static int sp_i2c_probe(struct platform_device *pdev) +{ + struct sp_i2c_dev *sp_i2c_dev_info; + 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; + + if (pdev->dev.of_node) { + pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); + dev_err(&pdev->dev, "pdev->id=%d\n", pdev->id); + device_id = pdev->id; + } + + sp_i2c_dev_info = devm_kzalloc(&pdev->dev, sizeof(*sp_i2c_dev_info), GFP_KERNEL); + if (!sp_i2c_dev_info) + return -ENOMEM; + + if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_clk_freq)) { + dev_err(&pdev->dev, "clk_freq %d\n", i2c_clk_freq); + sp_i2c_dev_info->i2c_clk_freq = i2c_clk_freq; + } else { + sp_i2c_dev_info->i2c_clk_freq = SP_I2C_FREQ*1000; + } + + sp_i2c_dev_info->dev = &pdev->dev; + + ret = _sp_i2cm_get_resources(pdev, sp_i2c_dev_info); + if (ret != I2C_SUCCESS) { + dev_err(&pdev->dev, " get resources fail !\n"); + return ret; + } + + /* dma alloc*/ + sp_i2c_dev_info->dma_vir_base = dma_alloc_coherent(&pdev->dev, SP_BUFFER_SIZE, + &sp_i2c_dev_info->dma_phy_base, GFP_ATOMIC); + if (!sp_i2c_dev_info->dma_vir_base) + goto free_dma; + + sp_i2c_dev_info->clk = devm_clk_get(dev, NULL); + + if (IS_ERR(sp_i2c_dev_info->clk)) { + ret = PTR_ERR(sp_i2c_dev_info->clk); + dev_err(&pdev->dev, "failed to retrieve clk: %d\n", ret); + goto err_clk_disable; + } + + sp_i2c_dev_info->rstc = devm_reset_control_get_exclusive(dev, NULL); + + if (IS_ERR(sp_i2c_dev_info->rstc)) { + ret = PTR_ERR(sp_i2c_dev_info->rstc); + dev_err(&pdev->dev, "failed to retrieve reset controller: %d\n", ret); + goto err_clk_disable; + } + + ret = clk_prepare_enable(sp_i2c_dev_info->clk); + + if (ret) { + dev_err(&pdev->dev, "failed to enable clk: %d\n", ret); + goto err_clk_disable; + } + + ret = reset_control_deassert(sp_i2c_dev_info->rstc); + + if (ret) { + dev_err(&pdev->dev, "failed to deassert reset line: %d\n", ret); + goto err_reset_assert; + } + + init_waitqueue_head(&sp_i2c_dev_info->wait); + + dev_mode = of_device_get_match_data(&pdev->dev); + sp_i2c_dev_info->mode = dev_mode->mode; + sp_i2c_dev_info->total_port = dev_mode->total_port; + p_adap = &sp_i2c_dev_info->adap; + sprintf(p_adap->name, "%s%d", SP_DEVICE_NAME, device_id); + p_adap->algo = &sp_algorithm; + p_adap->algo_data = sp_i2c_dev_info; + 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); + + ret = _sp_i2cm_init(device_id, sp_i2c_dev_info); + if (ret != 0) { + dev_err(&pdev->dev, "i2c master %d init error\n", device_id); + goto err_reset_assert; + } + + if (ret < 0) + goto err_reset_assert; + else + platform_set_drvdata(pdev, sp_i2c_dev_info); + + ret = request_irq(sp_i2c_dev_info->irq, _sp_i2cm_irqevent_handler, IRQF_TRIGGER_HIGH, + p_adap->name, sp_i2c_dev_info); + if (ret) { + dev_err(&pdev->dev, "request irq fail !!\n"); + return I2C_ERR_REQUESET_IRQ; + } + + 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); + + return ret; + +err_reset_assert: + reset_control_assert(sp_i2c_dev_info->rstc); +err_clk_disable: + clk_disable_unprepare(sp_i2c_dev_info->clk); +free_dma: + dma_free_coherent(&pdev->dev, SP_BUFFER_SIZE, + sp_i2c_dev_info->dma_vir_base, sp_i2c_dev_info->dma_phy_base); + + return ret; +} + +static int sp_i2c_remove(struct platform_device *pdev) +{ + struct sp_i2c_dev *sp_i2c_dev_info = platform_get_drvdata(pdev); + struct i2c_adapter *p_adap = &sp_i2c_dev_info->adap; + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + + dma_free_coherent(&pdev->dev, SP_BUFFER_SIZE, + sp_i2c_dev_info->dma_vir_base, sp_i2c_dev_info->dma_phy_base); + + i2c_del_adapter(p_adap); + if (p_adap->nr < sp_i2c_dev_info->total_port) { + clk_disable_unprepare(sp_i2c_dev_info->clk); + reset_control_assert(sp_i2c_dev_info->rstc); + free_irq(sp_i2c_dev_info->irq, NULL); + } + + return 0; +} + +static int __maybe_unused sp_i2c_suspend(struct device *dev) +{ + struct sp_i2c_dev *sp_i2c_dev_info = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &sp_i2c_dev_info->adap; + + if (p_adap->nr < sp_i2c_dev_info->total_port) + reset_control_assert(sp_i2c_dev_info->rstc); + + return 0; +} + +static int __maybe_unused sp_i2c_resume(struct device *dev) +{ + struct sp_i2c_dev *sp_i2c_dev_info = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &sp_i2c_dev_info->adap; + + if (p_adap->nr < sp_i2c_dev_info->total_port) { + reset_control_deassert(sp_i2c_dev_info->rstc); //release reset + clk_prepare_enable(sp_i2c_dev_info->clk); //enable clken and disable gclken + } + + return 0; +} + +static int sp_i2c_runtime_suspend(struct device *dev) +{ + struct sp_i2c_dev *sp_i2c_dev_info = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &sp_i2c_dev_info->adap; + + if (p_adap->nr < sp_i2c_dev_info->total_port) + reset_control_assert(sp_i2c_dev_info->rstc); + + return 0; +} + +static int sp_i2c_runtime_resume(struct device *dev) +{ + struct sp_i2c_dev *sp_i2c_dev_info = dev_get_drvdata(dev); + struct i2c_adapter *p_adap = &sp_i2c_dev_info->adap; + + if (p_adap->nr < sp_i2c_dev_info->total_port) { + reset_control_deassert(sp_i2c_dev_info->rstc); //release reset + clk_prepare_enable(sp_i2c_dev_info->clk); //enable clken and disable gclken + } + + return 0; +} +static const struct dev_pm_ops sp7021_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(sp_i2c_runtime_suspend, + sp_i2c_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(sp_i2c_suspend, sp_i2c_resume) + +}; + +#define sp_i2c_pm_ops (&sp7021_i2c_pm_ops) + +static struct platform_driver sp_i2c_driver = { + .probe = sp_i2c_probe, + .remove = sp_i2c_remove, + .driver = { + .owner = THIS_MODULE, + .name = SP_DEVICE_NAME, + .of_match_table = sp_i2c_of_match, + .pm = sp_i2c_pm_ops, + }, +}; + +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_LICENSE("GPL"); +MODULE_AUTHOR("Sunplus Technology"); +MODULE_DESCRIPTION("Sunplus I2C Master Driver"); -- 2.7.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 2021-11-09 6:59 ` [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo @ 2021-11-09 9:47 ` Philipp Zabel [not found] ` <af65896bb3d94afa9e296a428dcbd0e1@sphcmbx02.sunplus.com.tw> 0 siblings, 1 reply; 10+ messages in thread From: Philipp Zabel @ 2021-11-09 9:47 UTC (permalink / raw) To: LH.Kuo, daniel.thompson, lee.jones, u.kleine-koenig, robh+dt, linux-kernel, linux-i2c, devicetree Cc: qinjian, wells.lu, LH.Kuo On Tue, 2021-11-09 at 14:59 +0800, LH.Kuo wrote: [...] > +static int sp_i2c_probe(struct platform_device *pdev) > +{ > + struct sp_i2c_dev *sp_i2c_dev_info; > + 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; > + > + if (pdev->dev.of_node) { > + pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); > + dev_err(&pdev->dev, "pdev->id=%d\n", pdev->id); > + device_id = pdev->id; > + } > + > + sp_i2c_dev_info = devm_kzalloc(&pdev->dev, sizeof(*sp_i2c_dev_info), GFP_KERNEL); > + if (!sp_i2c_dev_info) > + return -ENOMEM; > + > + if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_clk_freq)) { > + dev_err(&pdev->dev, "clk_freq %d\n", i2c_clk_freq); > + sp_i2c_dev_info->i2c_clk_freq = i2c_clk_freq; > + } else { > + sp_i2c_dev_info->i2c_clk_freq = SP_I2C_FREQ*1000; > + } > + > + sp_i2c_dev_info->dev = &pdev->dev; > + > + ret = _sp_i2cm_get_resources(pdev, sp_i2c_dev_info); > + if (ret != I2C_SUCCESS) { > + dev_err(&pdev->dev, " get resources fail !\n"); > + return ret; > + } > + > + /* dma alloc*/ > + sp_i2c_dev_info->dma_vir_base = dma_alloc_coherent(&pdev->dev, SP_BUFFER_SIZE, > + &sp_i2c_dev_info->dma_phy_base, GFP_ATOMIC); > + if (!sp_i2c_dev_info->dma_vir_base) > + goto free_dma; Please fix your error paths, the driver shouldn't try to revert the action that just failed. Here you can use dmam_alloc_coherent() and just return -ENOMEM on error. > + > + sp_i2c_dev_info->clk = devm_clk_get(dev, NULL); > + > + if (IS_ERR(sp_i2c_dev_info->clk)) { > + ret = PTR_ERR(sp_i2c_dev_info->clk); > + dev_err(&pdev->dev, "failed to retrieve clk: %d\n", ret); > + goto err_clk_disable; Then this could return ret; Better, use return dev_err_probe(). > + } > + > + sp_i2c_dev_info->rstc = devm_reset_control_get_exclusive(dev, NULL); > + > + if (IS_ERR(sp_i2c_dev_info->rstc)) { > + ret = PTR_ERR(sp_i2c_dev_info->rstc); > + dev_err(&pdev->dev, "failed to retrieve reset controller: %d\n", ret); > + goto err_clk_disable; Same as above. > + } > + > + ret = clk_prepare_enable(sp_i2c_dev_info->clk); > + > + if (ret) { > + dev_err(&pdev->dev, "failed to enable clk: %d\n", ret); Consider using "%pe" and ERR_PTR(ret) to print the error name instead of a number [1]. [1] https://www.kernel.org/doc/html/latest/core-api/printk-formats.html?#error-pointers > + goto err_clk_disable; return ret; > + } > + > + ret = reset_control_deassert(sp_i2c_dev_info->rstc); > + > + if (ret) { > + dev_err(&pdev->dev, "failed to deassert reset line: %d\n", ret); > + goto err_reset_assert; goto err_clk_disable; > + } > + > + init_waitqueue_head(&sp_i2c_dev_info->wait); > + > + dev_mode = of_device_get_match_data(&pdev->dev); > + sp_i2c_dev_info->mode = dev_mode->mode; > + sp_i2c_dev_info->total_port = dev_mode->total_port; > + p_adap = &sp_i2c_dev_info->adap; > + sprintf(p_adap->name, "%s%d", SP_DEVICE_NAME, device_id); > + p_adap->algo = &sp_algorithm; > + p_adap->algo_data = sp_i2c_dev_info; > + 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); > + > + ret = _sp_i2cm_init(device_id, sp_i2c_dev_info); > + if (ret != 0) { > + dev_err(&pdev->dev, "i2c master %d init error\n", device_id); > + goto err_reset_assert; This one is correct, but I'd also print ret. > + } > + > + if (ret < 0) > + goto err_reset_assert; > + else > + platform_set_drvdata(pdev, sp_i2c_dev_info); > + > + ret = request_irq(sp_i2c_dev_info->irq, _sp_i2cm_irqevent_handler, IRQF_TRIGGER_HIGH, > + p_adap->name, sp_i2c_dev_info); > + if (ret) { > + dev_err(&pdev->dev, "request irq fail !!\n"); > + return I2C_ERR_REQUESET_IRQ; Don't return non-standard error codes. This should return ret instead, but not before going through the error cleanup path below. Also, consider using devm_request_irq(). > + } > + > + 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); > + > + return ret; > + > +err_reset_assert: > + reset_control_assert(sp_i2c_dev_info->rstc); > +err_clk_disable: > + clk_disable_unprepare(sp_i2c_dev_info->clk); > +free_dma: > + dma_free_coherent(&pdev->dev, SP_BUFFER_SIZE, > + sp_i2c_dev_info->dma_vir_base, sp_i2c_dev_info->dma_phy_base); > + > + return ret; > +} regards Philipp ^ permalink raw reply [flat|nested] 10+ messages in thread
[parent not found: <af65896bb3d94afa9e296a428dcbd0e1@sphcmbx02.sunplus.com.tw>]
* Re: [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 [not found] ` <af65896bb3d94afa9e296a428dcbd0e1@sphcmbx02.sunplus.com.tw> @ 2021-11-10 9:16 ` Philipp Zabel 0 siblings, 0 replies; 10+ messages in thread From: Philipp Zabel @ 2021-11-10 9:16 UTC (permalink / raw) To: Lh Kuo 郭力豪, LH.Kuo, daniel.thompson, lee.jones, u.kleine-koenig, robh+dt, linux-kernel, linux-i2c, devicetree Cc: qinjian, Wells Lu 呂芳騰 Hi, On Wed, 2021-11-10 at 05:37 +0000, Lh Kuo 郭力豪 wrote: [...] > > > + /* dma alloc*/ > > > + sp_i2c_dev_info->dma_vir_base = dma_alloc_coherent(&pdev->dev, > > SP_BUFFER_SIZE, > > > + &sp_i2c_dev_info->dma_phy_base, GFP_ATOMIC); > > > + if (!sp_i2c_dev_info->dma_vir_base) > > > + goto free_dma; > > > > Please fix your error paths, the driver shouldn't try to revert the action that > > just failed. > > > > Here you can use dmam_alloc_coherent() and just return -ENOMEM on error. > > > I will make change as below is it OK ? > > /* dma alloc*/ > sp_i2c_dev_info->dma_vir_base = dmam_alloc_coherent(&pdev->dev, SP_BUFFER_SIZE, > &sp_i2c_dev_info->dma_phy_base, GFP_ATOMIC); > if (!sp_i2c_dev_info->dma_vir_base) > return -ENOMEM; Yes, this looks good to me. With this change, you can remove the dma_free_coherent() calls below. > > > + > > > + sp_i2c_dev_info->clk = devm_clk_get(dev, NULL); > > > + > > > + if (IS_ERR(sp_i2c_dev_info->clk)) { > > > + ret = PTR_ERR(sp_i2c_dev_info->clk); > > > + dev_err(&pdev->dev, "failed to retrieve clk: %d\n", ret); > > > + goto err_clk_disable; > > > > Then this could > > > > return ret; > > > > Better, use return dev_err_probe(). > > > > I will make change as below is it OK ? > > sp_i2c_dev_info->clk = devm_clk_get(dev, NULL); > > if (IS_ERR(sp_i2c_dev_info->clk)) { > return dev_err_probe(&pdev->dev, PTR_ERR(sp_i2c_dev_info->clk), > "Could not get clock\n"); > } Yes. > > > + } > > > + > > > + sp_i2c_dev_info->rstc = devm_reset_control_get_exclusive(dev, NULL); > > > + > > > + if (IS_ERR(sp_i2c_dev_info->rstc)) { > > > + ret = PTR_ERR(sp_i2c_dev_info->rstc); > > > + dev_err(&pdev->dev, "failed to retrieve reset controller: %d\n", ret); > > > + goto err_clk_disable; > > > > Same as above. > > > > I will make change as below is it OK ? > > sp_i2c_dev_info->rstc = devm_reset_control_get_exclusive(dev, NULL); > > if (IS_ERR(sp_i2c_dev_info->rstc)) { > return dev_err_probe(&pdev->dev, PTR_ERR(sp_i2c_dev_info->rstc), > "Could not get clock\n"); > } The error message should be "Could not get reset\n" instead. > > > + } > > > + > > > + ret = clk_prepare_enable(sp_i2c_dev_info->clk); > > > + > > > + if (ret) { > > > + dev_err(&pdev->dev, "failed to enable clk: %d\n", ret); > > > > Consider using "%pe" and ERR_PTR(ret) to print the error name instead of a > > number [1]. > > > > [1] > > https://www.kernel.org/doc/html/latest/core-api/printk-formats.html?#error-p > > ointers > > > > > + goto err_clk_disable; > > > > return ret; > > > > I will make change as below is it OK ? > > ret = clk_prepare_enable(sp_i2c_dev_info->clk); > > if (ret) { > dev_err(&pdev->dev, "failed to enable clk: %pe\n", ERR_PTR(ret)); Ok. Alternatively, you could also use dev_err_probe() as above. > goto err_clk_disable; Not ok. If clk_prepare_enable() did not succeed, do not call clk_disable_unprepare(). return ret instead. > } > > > > + } > > > + > > > + ret = reset_control_deassert(sp_i2c_dev_info->rstc); > > > + > > > + if (ret) { > > > + dev_err(&pdev->dev, "failed to deassert reset line: %d\n", ret); Consider either changing this to %pe or use dev_err_probe(), for consistency with the above error messages. > > > + goto err_reset_assert; > > > > goto err_clk_disable; This is required as well. > > > > > + } > > > + > > > + init_waitqueue_head(&sp_i2c_dev_info->wait); > > > + > > > + dev_mode = of_device_get_match_data(&pdev->dev); > > > + sp_i2c_dev_info->mode = dev_mode->mode; > > > + sp_i2c_dev_info->total_port = dev_mode->total_port; > > > + p_adap = &sp_i2c_dev_info->adap; > > > + sprintf(p_adap->name, "%s%d", SP_DEVICE_NAME, device_id); > > > + p_adap->algo = &sp_algorithm; > > > + p_adap->algo_data = sp_i2c_dev_info; > > > + 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); > > > + > > > + ret = _sp_i2cm_init(device_id, sp_i2c_dev_info); > > > + if (ret != 0) { > > > + dev_err(&pdev->dev, "i2c master %d init error\n", device_id); > > > + goto err_reset_assert; > > > > This one is correct, but I'd also print ret. > > > > I will make change as below is it OK ? > > ret = _sp_i2cm_init(device_id, sp_i2c_dev_info); > if (ret != 0) { > dev_err(&pdev->dev, "i2c master %d init error ret %d\n", device_id, ret); > goto err_reset_assert; > } Yes, although I'd prefer a consistent style with the error messages above. For example: dev_err(&pdev->dev, "i2c master %d init error: %pe\n", device_id, ERR_PTR(ret)); or dev_err_probe(&pdev->dev, ret, "i2c master %d init error", device_id); > > > + } > > > + > > > + if (ret < 0) > > > + goto err_reset_assert; > > > + else > > > + platform_set_drvdata(pdev, sp_i2c_dev_info); > > > + > > > + ret = request_irq(sp_i2c_dev_info->irq, _sp_i2cm_irqevent_handler, > > IRQF_TRIGGER_HIGH, > > > + p_adap->name, sp_i2c_dev_info); > > > + if (ret) { > > > + dev_err(&pdev->dev, "request irq fail !!\n"); > > > + return I2C_ERR_REQUESET_IRQ; > > > > Don't return non-standard error codes. This should return ret instead, but not > > before going through the error cleanup path below. Also, consider using > > devm_request_irq(). > > > > I will make change as below is it OK ? > ret = devm_request_irq(&pdev->dev, sp_i2c_dev_info->irq, _sp_i2cm_irqevent_handler, > IRQF_TRIGGER_HIGH, p_adap->name, sp_i2c_dev_info); > if (ret) { > dev_err(&pdev->dev, "request irq fail !!\n"); > return ret; > } Yes. With this, you can remove the free_irq() call below. regards Philipp ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] devicetree bindings I2C Add bindings doc for Sunplus SP7021 2021-11-09 6:59 ` [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC LH.Kuo 2021-11-09 6:59 ` [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo @ 2021-11-09 6:59 ` LH.Kuo 2021-11-29 20:23 ` Rob Herring 1 sibling, 1 reply; 10+ messages in thread From: LH.Kuo @ 2021-11-09 6:59 UTC (permalink / raw) To: p.zabel, daniel.thompson, lee.jones, u.kleine-koenig, robh+dt, linux-kernel, linux-i2c, devicetree Cc: qinjian, wells.lu, LH.Kuo Add devicetree bindings I2C Add bindings doc for Sunplus SP7021 Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> --- Changes in v2: - Addressed all comments from Mr. Rob Herring. - Modified the structure and register access method. - Modifiedthe path about MAINTAINERS. ( wrong messages PATH in v1). - Modifiedthe the YAML file. .../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..af860ee --- /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 <lh.kuo@sunplus.com> + +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 + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/sp-sp7021.h> + #include <dt-bindings/reset/sp-sp7021.h> + #include <dt-bindings/interrupt-controller/irq.h> + 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 5b7a8a2..575a8e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18193,6 +18193,7 @@ SUNPLUS I2C CONTROLLER INTERFACE DRIVER M: LH Kuo <lh.kuo@sunplus.com> L: linux-i2c@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml F: drivers/i2c/busses/i2c-sunplus.c SUPERH -- 2.7.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] devicetree bindings I2C Add bindings doc for Sunplus SP7021 2021-11-09 6:59 ` [PATCH v2 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo @ 2021-11-29 20:23 ` Rob Herring 0 siblings, 0 replies; 10+ messages in thread From: Rob Herring @ 2021-11-29 20:23 UTC (permalink / raw) To: LH.Kuo Cc: p.zabel, daniel.thompson, lee.jones, u.kleine-koenig, linux-kernel, linux-i2c, devicetree, qinjian, wells.lu, LH.Kuo On Tue, Nov 09, 2021 at 02:59:26PM +0800, LH.Kuo wrote: > Add devicetree bindings I2C Add bindings doc for Sunplus SP7021 > > Signed-off-by: LH.Kuo <lh.kuo@sunplus.com> As with other SunPlus patches, author and S-o-b must match. > --- > Changes in v2: > - Addressed all comments from Mr. Rob Herring. > - Modified the structure and register access method. > - Modifiedthe path about MAINTAINERS. ( wrong messages PATH in v1). > - Modifiedthe the YAML file. > > .../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..af860ee > --- /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 <lh.kuo@sunplus.com> Full name here please. > + > +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 Drop 'Base address and length of the '. > + > + reg-names: > + items: > + - const: i2cm > + - const: i2cmdma > + > + interrupts: > + maxItems: 1 > + > + clocks: > + maxItems: 1 > + > + resets: > + maxItems: 1 > + > + clock-frequency: > + enum: [ 100000, 400000 ] You can't support other frequencies? > + > + pinctrl-names: > + description: > + A pinctrl state named "default" must be defined. > + const: default > + > + pinctrl-0: > + description: > + A phandle to the default pinctrl state. You don't have to define pinctrl properties when there's only 'default'. > + > +required: > + - compatible > + - reg > + - reg-names > + - interrupts > + - clocks > + - resets > + - pinctrl-names > + - pinctrl-0 > + > +unevaluatedProperties: false > + > +examples: > + - | > + #include <dt-bindings/clock/sp-sp7021.h> > + #include <dt-bindings/reset/sp-sp7021.h> > + #include <dt-bindings/interrupt-controller/irq.h> > + 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 5b7a8a2..575a8e0 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -18193,6 +18193,7 @@ SUNPLUS I2C CONTROLLER INTERFACE DRIVER > M: LH Kuo <lh.kuo@sunplus.com> > L: linux-i2c@vger.kernel.org > S: Maintained > +F: Documentation/devicetree/bindings/i2c/i2c-sunplus.yaml > F: drivers/i2c/busses/i2c-sunplus.c > > SUPERH > -- > 2.7.4 > > ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2021-11-29 20:26 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-10-29 8:42 [PATCH 0/2] This is a patch series for I2C driver for Sunplus SP7021 SoC LH.Kuo 2021-10-29 8:42 ` [PATCH 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo 2021-10-29 8:42 ` [PATCH 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo 2021-11-08 19:23 ` Rob Herring 2021-11-09 6:59 ` [PATCH v2 0/2] Add I2C control driver for Sunplus SP7021 SoC LH.Kuo 2021-11-09 6:59 ` [PATCH v2 1/2] I2C: Add I2C driver for Sunplus SP7021 LH.Kuo 2021-11-09 9:47 ` Philipp Zabel [not found] ` <af65896bb3d94afa9e296a428dcbd0e1@sphcmbx02.sunplus.com.tw> 2021-11-10 9:16 ` Philipp Zabel 2021-11-09 6:59 ` [PATCH v2 2/2] devicetree bindings I2C Add bindings doc " LH.Kuo 2021-11-29 20:23 ` Rob Herring
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.