1df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/*
2df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * I2C bus driver for the Cadence I2C controller.
3df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
4df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Copyright (C) 2009 - 2014 Xilinx, Inc.
5df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
6df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This program is free software; you can redistribute it
7df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * and/or modify it under the terms of the GNU General Public
8df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * License as published by the Free Software Foundation;
9df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * either version 2 of the License, or (at your option) any
10df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * later version.
11df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
12df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
13df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/clk.h>
14df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/delay.h>
15df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/i2c.h>
16df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/interrupt.h>
17df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/io.h>
18df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/module.h>
19df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#include <linux/platform_device.h>
20df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
21df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* Register offsets for the I2C device. */
22df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_OFFSET		0x00 /* Control Register, RW */
23df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_SR_OFFSET		0x04 /* Status Register, RO */
24df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_ADDR_OFFSET		0x08 /* I2C Address Register, RW */
25df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_DATA_OFFSET		0x0C /* I2C Data Register, RW */
26df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_ISR_OFFSET		0x10 /* IRQ Status Register, RW */
27df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_XFER_SIZE_OFFSET	0x14 /* Transfer Size Register, RW */
28df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_TIME_OUT_OFFSET	0x1C /* Time Out Register, RW */
29df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IER_OFFSET		0x24 /* IRQ Enable Register, WO */
30df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IDR_OFFSET		0x28 /* IRQ Disable Register, WO */
31df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
32df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* Control Register Bit mask definitions */
33df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_HOLD		BIT(4) /* Hold Bus bit */
34df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_ACK_EN		BIT(3)
35df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_NEA			BIT(2)
36df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_MS			BIT(1)
37df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */
38df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_RW			BIT(0)
39df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* 1 = Auto init FIFO to zeroes */
40df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_CLR_FIFO		BIT(6)
41df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_DIVA_SHIFT		14
42df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_DIVA_MASK		(3 << CDNS_I2C_CR_DIVA_SHIFT)
43df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_DIVB_SHIFT		8
44df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_CR_DIVB_MASK		(0x3f << CDNS_I2C_CR_DIVB_SHIFT)
45df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
46df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* Status Register Bit mask definitions */
47df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_SR_BA		BIT(8)
48df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_SR_RXDV	BIT(5)
49df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
50df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/*
51df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * I2C Address Register Bit mask definitions
52df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0]
53df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * bits. A write access to this register always initiates a transfer if the I2C
54df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * is in master mode.
55df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
56df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_ADDR_MASK	0x000003FF /* I2C Address Mask */
57df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
58df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/*
59df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * I2C Interrupt Registers Bit mask definitions
60df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
61df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * bit definitions.
62df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
63df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_ARB_LOST		BIT(9)
64df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_RX_UNF		BIT(7)
65df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_TX_OVF		BIT(6)
66df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_RX_OVF		BIT(5)
67df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_SLV_RDY		BIT(4)
68df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_TO			BIT(3)
69df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_NACK		BIT(2)
70df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_DATA		BIT(1)
71df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_COMP		BIT(0)
72df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
73df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_ALL_INTR_MASK	(CDNS_I2C_IXR_ARB_LOST | \
74df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_UNF | \
75df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_TX_OVF | \
76df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_OVF | \
77df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_SLV_RDY | \
78df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_TO | \
79df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_NACK | \
80df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_DATA | \
81df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_COMP)
82df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
83df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_IXR_ERR_INTR_MASK	(CDNS_I2C_IXR_ARB_LOST | \
84df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_UNF | \
85df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_TX_OVF | \
86df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_OVF | \
87df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_NACK)
88df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
89df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_ENABLED_INTR_MASK	(CDNS_I2C_IXR_ARB_LOST | \
90df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_UNF | \
91df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_TX_OVF | \
92df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_RX_OVF | \
93df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_NACK | \
94df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_DATA | \
95df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					 CDNS_I2C_IXR_COMP)
96df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
97df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_TIMEOUT		msecs_to_jiffies(1000)
98df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
99df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_FIFO_DEPTH		16
100df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* FIFO depth at which the DATA interrupt occurs */
101df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_DATA_INTR_DEPTH	(CDNS_I2C_FIFO_DEPTH - 2)
102df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_MAX_TRANSFER_SIZE	255
103df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/* Transfer size in multiples of data interrupt depth */
104df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_TRANSFER_SIZE	(CDNS_I2C_MAX_TRANSFER_SIZE - 3)
105df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
106df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define DRIVER_NAME		"cdns-i2c"
107df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
108df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_SPEED_MAX	400000
109df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_SPEED_DEFAULT	100000
110df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
111df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_DIVA_MAX	4
112df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define CDNS_I2C_DIVB_MAX	64
113df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
114681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare#define CDNS_I2C_TIMEOUT_MAX	0xFF
115681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare
116df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define cdns_i2c_readreg(offset)       readl_relaxed(id->membase + offset)
117df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
118df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
119df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
120df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * struct cdns_i2c - I2C device private data structure
121df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @membase:		Base address of the I2C device
122df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @adap:		I2C adapter instance
123df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @p_msg:		Message pointer
124df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @err_status:		Error status in Interrupt Status Register
125df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @xfer_done:		Transfer complete status
126df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @p_send_buf:		Pointer to transmit buffer
127df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @p_recv_buf:		Pointer to receive buffer
128df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @suspended:		Flag holding the device's PM status
129df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @send_count:		Number of bytes still expected to send
130df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @recv_count:		Number of bytes still expected to receive
131df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @irq:		IRQ number
132df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @input_clk:		Input clock to I2C controller
133df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @i2c_clk:		Maximum I2C clock speed
134df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @bus_hold_flag:	Flag used in repeated start for clearing HOLD bit
135df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @clk:		Pointer to struct clk
136df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @clk_rate_change_nb:	Notifier block for clock rate changes
137df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
138df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstruct cdns_i2c {
139df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	void __iomem *membase;
140df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct i2c_adapter adap;
141df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct i2c_msg *p_msg;
142df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int err_status;
143df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct completion xfer_done;
144df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned char *p_send_buf;
145df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned char *p_recv_buf;
146df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	u8 suspended;
147df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int send_count;
148df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int recv_count;
149df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int irq;
150df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned long input_clk;
151df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int i2c_clk;
152df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int bus_hold_flag;
153df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct clk *clk;
154df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct notifier_block clk_rate_change_nb;
155df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann};
156df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
157df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann#define to_cdns_i2c(_nb)	container_of(_nb, struct cdns_i2c, \
158df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					     clk_rate_change_nb)
159df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
160df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
161df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_clear_bus_hold() - Clear bus hold bit
162df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @id:	Pointer to driver data struct
163df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
164df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Helper to clear the controller's bus hold bit.
165df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
166df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
167df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
168df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
169df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (reg & CDNS_I2C_CR_HOLD)
170df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
171df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
172df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
173df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
174df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_isr - Interrupt handler for the I2C device
175df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @irq:	irq number for the I2C device
176df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @ptr:	void pointer to cdns_i2c structure
177df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
178df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This function handles the data interrupt, transfer complete interrupt and
179df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * the error interrupts of the I2C device.
180df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
181df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: IRQ_HANDLED always
182df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
183df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic irqreturn_t cdns_i2c_isr(int irq, void *ptr)
184df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
185df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int isr_status, avail_bytes;
186df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int bytes_to_recv, bytes_to_send;
187df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id = ptr;
188df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Signal completion only after everything is updated */
189df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int done_flag = 0;
190df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	irqreturn_t status = IRQ_NONE;
191df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
192df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
193df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
194df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Handling nack and arbitration lost interrupt */
195df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
196df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		done_flag = 1;
197df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		status = IRQ_HANDLED;
198df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
199df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
200df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Handling Data interrupt */
201df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if ((isr_status & CDNS_I2C_IXR_DATA) &&
202df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			(id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) {
203df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* Always read data interrupt threshold bytes */
204df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH;
205df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH;
206df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
207df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
208df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/*
209df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 * if the tranfer size register value is zero, then
210df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 * check for the remaining bytes and update the
211df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 * transfer size register.
212df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 */
213df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (!avail_bytes) {
214df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
215df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
216df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						CDNS_I2C_XFER_SIZE_OFFSET);
217df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			else
218df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_writereg(id->recv_count,
219df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						CDNS_I2C_XFER_SIZE_OFFSET);
220df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		}
221df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
222df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* Process the data received */
223df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		while (bytes_to_recv--)
224df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			*(id->p_recv_buf)++ =
225df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
226df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
227df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (!id->bus_hold_flag &&
228df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
229df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_clear_bus_hold(id);
230df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
231df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		status = IRQ_HANDLED;
232df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
233df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
234df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Handling Transfer Complete interrupt */
235df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (isr_status & CDNS_I2C_IXR_COMP) {
236df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (!id->p_recv_buf) {
237df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			/*
238df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * If the device is sending data If there is further
239df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * data to be sent. Calculate the available space
240df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * in FIFO and fill the FIFO with that many bytes.
241df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 */
242df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			if (id->send_count) {
243df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				avail_bytes = CDNS_I2C_FIFO_DEPTH -
244df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				    cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
245df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				if (id->send_count > avail_bytes)
246df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					bytes_to_send = avail_bytes;
247df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				else
248df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					bytes_to_send = id->send_count;
249df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
250df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				while (bytes_to_send--) {
251df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					cdns_i2c_writereg(
252df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						(*(id->p_send_buf)++),
253df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						 CDNS_I2C_DATA_OFFSET);
254df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					id->send_count--;
255df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				}
256df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			} else {
257df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				/*
258df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				 * Signal the completion of transaction and
259df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				 * clear the hold bus bit if there are no
260df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				 * further messages to be processed.
261df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				 */
262df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				done_flag = 1;
263df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			}
264df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			if (!id->send_count && !id->bus_hold_flag)
265df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_clear_bus_hold(id);
266df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		} else {
267df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			if (!id->bus_hold_flag)
268df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_clear_bus_hold(id);
269df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			/*
270df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * If the device is receiving data, then signal
271df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * the completion of transaction and read the data
272df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * present in the FIFO. Signal the completion of
273df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 * transaction.
274df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 */
275df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
276df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					CDNS_I2C_SR_RXDV) {
277df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				*(id->p_recv_buf)++ =
278df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
279df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				id->recv_count--;
280df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			}
281df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			done_flag = 1;
282df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		}
283df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
284df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		status = IRQ_HANDLED;
285df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
286df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
287df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Update the status for errors */
288df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
289df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->err_status)
290df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		status = IRQ_HANDLED;
291df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
292df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
293df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
294df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (done_flag)
295df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		complete(&id->xfer_done);
296df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
297df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return status;
298df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
299df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
300df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
301df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_mrecv - Prepare and start a master receive operation
302df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @id:		pointer to the i2c device structure
303df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
304df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic void cdns_i2c_mrecv(struct cdns_i2c *id)
305df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
306df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int ctrl_reg;
307df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int isr_status;
308df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
309df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->p_recv_buf = id->p_msg->buf;
310df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->recv_count = id->p_msg->len;
311df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
312df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Put the controller in master receive mode and clear the FIFO */
313df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
314df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
315df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
316df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->p_msg->flags & I2C_M_RECV_LEN)
317df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
318df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
319df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
320df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Check for the message size against FIFO depth and set the
321df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * 'hold bus' bit if it is greater than FIFO depth.
322df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
323df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
324df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		ctrl_reg |= CDNS_I2C_CR_HOLD;
325df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
326df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
327df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
328df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the interrupts in interrupt status register */
329df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
330df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
331df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
332df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
333df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * The no. of bytes to receive is checked against the limit of
334df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * max transfer size. Set transfer size register with no of bytes
335df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * receive if it is less than transfer size and transfer size if
336df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * it is more. Enable the interrupts.
337df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
338df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
339df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
340df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				  CDNS_I2C_XFER_SIZE_OFFSET);
341df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	else
342df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
343df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the bus hold flag if bytes to receive is less than FIFO size */
344df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (!id->bus_hold_flag &&
345df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
346df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
347df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_clear_bus_hold(id);
348df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Set the slave address in address register - triggers operation */
349df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
350df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						CDNS_I2C_ADDR_OFFSET);
351df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
352df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
353df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
354df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
355df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_msend - Prepare and start a master send operation
356df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @id:		pointer to the i2c device
357df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
358df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic void cdns_i2c_msend(struct cdns_i2c *id)
359df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
360df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int avail_bytes;
361df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int bytes_to_send;
362df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int ctrl_reg;
363df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int isr_status;
364df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
365df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->p_recv_buf = NULL;
366df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->p_send_buf = id->p_msg->buf;
367df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->send_count = id->p_msg->len;
368df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
369df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Set the controller in Master transmit mode and clear the FIFO. */
370df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
371df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg &= ~CDNS_I2C_CR_RW;
372df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg |= CDNS_I2C_CR_CLR_FIFO;
373df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
374df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
375df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Check for the message size against FIFO depth and set the
376df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * 'hold bus' bit if it is greater than FIFO depth.
377df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
378df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->send_count > CDNS_I2C_FIFO_DEPTH)
379df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		ctrl_reg |= CDNS_I2C_CR_HOLD;
380df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
381df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
382df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the interrupts in interrupt status register. */
383df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
384df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
385df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
386df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
387df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Calculate the space available in FIFO. Check the message length
388df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * against the space available, and fill the FIFO accordingly.
389df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Enable the interrupts.
390df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
391df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	avail_bytes = CDNS_I2C_FIFO_DEPTH -
392df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
393df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
394df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->send_count > avail_bytes)
395df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		bytes_to_send = avail_bytes;
396df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	else
397df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		bytes_to_send = id->send_count;
398df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
399df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	while (bytes_to_send--) {
400df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_writereg((*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET);
401df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->send_count--;
402df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
403df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
404df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
405df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Clear the bus hold flag if there is no more data
406df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * and if it is the last message.
407df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
408df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (!id->bus_hold_flag && !id->send_count)
409df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_clear_bus_hold(id);
410df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Set the slave address in address register - triggers operation. */
411df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
412df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann						CDNS_I2C_ADDR_OFFSET);
413df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
414df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
415df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
416df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
417df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
418df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_master_reset - Reset the interface
419df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @adap:	pointer to the i2c adapter driver instance
420df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
421df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This function cleanup the fifos, clear the hold bit and status
422df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * and disable the interrupts.
423df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
424df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic void cdns_i2c_master_reset(struct i2c_adapter *adap)
425df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
426df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id = adap->algo_data;
427df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	u32 regval;
428df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
429df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Disable the interrupts */
430df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
431df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the hold bit and fifos */
432df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
433df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	regval &= ~CDNS_I2C_CR_HOLD;
434df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	regval |= CDNS_I2C_CR_CLR_FIFO;
435df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
436df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Update the transfercount register to zero */
437df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
438df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the interupt status register */
439df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
440df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET);
441df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Clear the status register */
442df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	regval = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
443df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(regval, CDNS_I2C_SR_OFFSET);
444df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
445df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
446df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
447df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		struct i2c_adapter *adap)
448df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
449df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int ret;
450df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	u32 reg;
451df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
452df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->p_msg = msg;
453df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->err_status = 0;
454df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	reinit_completion(&id->xfer_done);
455df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
456df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Check for the TEN Bit mode on each msg */
457df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
458df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (msg->flags & I2C_M_TEN) {
459df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (reg & CDNS_I2C_CR_NEA)
460df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA,
461df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					CDNS_I2C_CR_OFFSET);
462df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	} else {
463df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (!(reg & CDNS_I2C_CR_NEA))
464df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,
465df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					CDNS_I2C_CR_OFFSET);
466df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
467df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
468df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Check for the R/W flag on each msg */
469df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (msg->flags & I2C_M_RD)
470df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_mrecv(id);
471df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	else
472df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_msend(id);
473df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
474df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Wait for the signal of completion */
475df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = wait_for_completion_timeout(&id->xfer_done, adap->timeout);
476df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (!ret) {
477df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_master_reset(adap);
478df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(id->adap.dev.parent,
479df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				"timeout waiting on completion\n");
480df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return -ETIMEDOUT;
481df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
482df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
483df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK,
484df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			  CDNS_I2C_IDR_OFFSET);
485df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
486df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* If it is bus arbitration error, try again */
487df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->err_status & CDNS_I2C_IXR_ARB_LOST)
488df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return -EAGAIN;
489df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
490df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
491df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
492df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
493df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
494df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_master_xfer - The main i2c transfer function
495df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @adap:	pointer to the i2c adapter driver instance
496df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @msgs:	pointer to the i2c message structure
497df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @num:	the number of messages to transfer
498df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
499df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Initiates the send/recv activity based on the transfer message received.
500df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
501df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: number of msgs processed on success, negative error otherwise
502df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
503df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
504df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				int num)
505df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
506df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int ret, count;
507df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	u32 reg;
508df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id = adap->algo_data;
509df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
510df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Check if the bus is free */
511df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
512df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return -EAGAIN;
513df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
514df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
515df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * Set the flag to one when multiple messages are to be
516df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * processed with a repeated start.
517df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
518df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (num > 1) {
519df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->bus_hold_flag = 1;
520df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
521df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		reg |= CDNS_I2C_CR_HOLD;
522df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
523df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	} else {
524df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->bus_hold_flag = 0;
525df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
526df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
527df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* Process the msg one by one */
528df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	for (count = 0; count < num; count++, msgs++) {
529df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (count == (num - 1))
530df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			id->bus_hold_flag = 0;
531df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
532df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		ret = cdns_i2c_process_msg(id, msgs, adap);
533df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (ret)
534df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			return ret;
535df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
536df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* Report the other error interrupts to application */
537df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (id->err_status) {
538df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_master_reset(adap);
539df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
540df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			if (id->err_status & CDNS_I2C_IXR_NACK)
541df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				return -ENXIO;
542df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
543df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			return -EIO;
544df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		}
545df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
546df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
547df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return num;
548df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
549df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
550df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
551df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_func - Returns the supported features of the I2C driver
552df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @adap:	pointer to the i2c adapter structure
553df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
554df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 32 bit value, each bit corresponding to a feature
555df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
556df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic u32 cdns_i2c_func(struct i2c_adapter *adap)
557df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
558df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
559df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
560df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		I2C_FUNC_SMBUS_BLOCK_DATA;
561df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
562df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
563df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic const struct i2c_algorithm cdns_i2c_algo = {
564df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	.master_xfer	= cdns_i2c_master_xfer,
565df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	.functionality	= cdns_i2c_func,
566df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann};
567df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
568df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
569df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_calc_divs - Calculate clock dividers
570df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @f:		I2C clock frequency
571df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @input_clk:	Input clock frequency
572df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @a:		First divider (return value)
573df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @b:		Second divider (return value)
574df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
575df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * f is used as input and output variable. As input it is used as target I2C
576df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * frequency. On function exit f holds the actually resulting I2C frequency.
577df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
578df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 on success, negative errno otherwise.
579df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
580df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
581df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		unsigned int *a, unsigned int *b)
582df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
583df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
584df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
585df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int last_error, current_error;
586df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
587df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/* calculate (divisor_a+1) x (divisor_b+1) */
588df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	temp = input_clk / (22 * fscl);
589df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
590df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	/*
591df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * If the calculated value is negative or 0, the fscl input is out of
592df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 * range. Return error.
593df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	 */
594df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
595df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return -EINVAL;
596df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
597df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	last_error = -1;
598df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
599df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
600df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
601df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
602df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			continue;
603df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		div_b--;
604df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
605df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
606df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
607df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (actual_fscl > fscl)
608df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			continue;
609df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
610df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
611df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann							(fscl - actual_fscl));
612df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
613df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (last_error > current_error) {
614df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			calc_div_a = div_a;
615df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			calc_div_b = div_b;
616df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			best_fscl = actual_fscl;
617df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			last_error = current_error;
618df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		}
619df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
620df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
621df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	*a = calc_div_a;
622df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	*b = calc_div_b;
623df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	*f = best_fscl;
624df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
625df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
626df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
627df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
628df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
629df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_setclk - This function sets the serial clock rate for the I2C device
630df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @clk_in:	I2C clock input frequency in Hz
631df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @id:		Pointer to the I2C device structure
632df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
633df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * The device must be idle rather than busy transferring data before setting
634df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * these device options.
635df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * The data rate is set by values in the control register.
636df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * The formula for determining the correct register values is
637df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *	Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
638df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * See the hardware data sheet for a full explanation of setting the serial
639df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * clock rate. The clock can not be faster than the input clock divide by 22.
640df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * The two most common clock rates are 100KHz and 400KHz.
641df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
642df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 on success, negative error otherwise
643df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
644df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
645df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
646df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int div_a, div_b;
647df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned int ctrl_reg;
648df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int ret = 0;
649df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	unsigned long fscl = id->i2c_clk;
650df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
651df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b);
652df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret)
653df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return ret;
654df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
655df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
656df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
657df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
658df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			(div_b << CDNS_I2C_CR_DIVB_SHIFT));
659df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
660df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
661df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
662df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
663df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
664df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
665df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_clk_notifier_cb - Clock rate change callback
666df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @nb:		Pointer to notifier block
667df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @event:	Notification reason
668df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @data:	Pointer to notification data object
669df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
670df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This function is called when the cdns_i2c input clock frequency changes.
671df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * The callback checks whether a valid bus frequency can be generated after the
672df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * change. If so, the change is acknowledged, otherwise the change is aborted.
673df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * New dividers are written to the HW in the pre- or post change notification
674df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * depending on the scaling direction.
675df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
676df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return:	NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK
677df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *		to acknowedge the change, NOTIFY_DONE if the notification is
678df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *		considered irrelevant.
679df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
680df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
681df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		event, void *data)
682df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
683df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct clk_notifier_data *ndata = data;
684df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id = to_cdns_i2c(nb);
685df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
686df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (id->suspended)
687df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return NOTIFY_OK;
688df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
689df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	switch (event) {
690df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	case PRE_RATE_CHANGE:
691df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	{
692df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		unsigned long input_clk = ndata->new_rate;
693df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		unsigned long fscl = id->i2c_clk;
694df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		unsigned int div_a, div_b;
695df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		int ret;
696df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
697df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		ret = cdns_i2c_calc_divs(&fscl, input_clk, &div_a, &div_b);
698df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (ret) {
699df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			dev_warn(id->adap.dev.parent,
700df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann					"clock rate change rejected\n");
701df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			return NOTIFY_STOP;
702df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		}
703df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
704df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* scale up */
705df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (ndata->new_rate > ndata->old_rate)
706df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_setclk(ndata->new_rate, id);
707df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
708df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return NOTIFY_OK;
709df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
710df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	case POST_RATE_CHANGE:
711df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->input_clk = ndata->new_rate;
712df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* scale down */
713df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (ndata->new_rate < ndata->old_rate)
714df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_setclk(ndata->new_rate, id);
715df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return NOTIFY_OK;
716df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	case ABORT_RATE_CHANGE:
717df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		/* scale up */
718df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		if (ndata->new_rate > ndata->old_rate)
719df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			cdns_i2c_setclk(ndata->old_rate, id);
720df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return NOTIFY_OK;
721df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	default:
722df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return NOTIFY_DONE;
723df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
724df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
725df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
726df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
727df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_suspend - Suspend method for the driver
728df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @_dev:	Address of the platform_device structure
729df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
730df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Put the driver into low power mode.
731df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
732df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 always
733df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
734df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int __maybe_unused cdns_i2c_suspend(struct device *_dev)
735df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
736df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct platform_device *pdev = container_of(_dev,
737df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			struct platform_device, dev);
738df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
739df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
740df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	clk_disable(xi2c->clk);
741df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	xi2c->suspended = 1;
742df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
743df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
744df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
745df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
746df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
747df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_resume - Resume from suspend
748df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @_dev:	Address of the platform_device structure
749df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
750df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Resume operation after suspend.
751df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
752df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 on success and error value on error
753df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
754df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int __maybe_unused cdns_i2c_resume(struct device *_dev)
755df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
756df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct platform_device *pdev = container_of(_dev,
757df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			struct platform_device, dev);
758df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
759df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int ret;
760df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
761df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = clk_enable(xi2c->clk);
762df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret) {
763df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(_dev, "Cannot enable clock.\n");
764df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return ret;
765df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
766df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
767df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	xi2c->suspended = 0;
768df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
769df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
770df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
771df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
772df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
773df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			 cdns_i2c_resume);
774df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
775df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
776df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_probe - Platform registration call
777df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @pdev:	Handle to the platform device structure
778df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
779df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This function does all the memory allocation and registration for the i2c
780df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * device. User can modify the address mode to 10 bit address mode using the
781df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * ioctl call with option I2C_TENBIT.
782df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
783df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 on success, negative error otherwise
784df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
785df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_probe(struct platform_device *pdev)
786df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
787df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct resource *r_mem;
788df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id;
789df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	int ret;
790df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
791df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
792df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (!id)
793df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return -ENOMEM;
794df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
795df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	platform_set_drvdata(pdev, id);
796df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
797df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
798df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
799df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (IS_ERR(id->membase))
800df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return PTR_ERR(id->membase);
801df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
802df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->irq = platform_get_irq(pdev, 0);
803df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
804df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.dev.of_node = pdev->dev.of_node;
805df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.algo = &cdns_i2c_algo;
806df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.timeout = CDNS_I2C_TIMEOUT;
807df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.retries = 3;		/* Default retry value. */
808df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.algo_data = id;
809df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->adap.dev.parent = &pdev->dev;
810df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	init_completion(&id->xfer_done);
811df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	snprintf(id->adap.name, sizeof(id->adap.name),
812df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 "Cadence I2C at %08lx", (unsigned long)r_mem->start);
813df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
814df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->clk = devm_clk_get(&pdev->dev, NULL);
815df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (IS_ERR(id->clk)) {
816df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(&pdev->dev, "input clock not found.\n");
817df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return PTR_ERR(id->clk);
818df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
819df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = clk_prepare_enable(id->clk);
820df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret) {
821df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(&pdev->dev, "Unable to enable clock.\n");
822df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		return ret;
823df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
824df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
825df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
826df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
827df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	id->input_clk = clk_get_rate(id->clk);
828df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
829df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
830df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			&id->i2c_clk);
831df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))
832df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
833df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
834df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	cdns_i2c_writereg(CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS,
835df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann			  CDNS_I2C_CR_OFFSET);
836df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
837df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = cdns_i2c_setclk(id->input_clk, id);
838df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret) {
839df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
840df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		ret = -EINVAL;
841df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		goto err_clk_dis;
842df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
843df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
844df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
845df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann				 DRIVER_NAME, id);
846df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret) {
847df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
848df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		goto err_clk_dis;
849df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
850df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
851df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	ret = i2c_add_adapter(&id->adap);
852df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	if (ret < 0) {
853df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		dev_err(&pdev->dev, "reg adap failed: %d\n", ret);
854df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		goto err_clk_dis;
855df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	}
856df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
857681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	/*
858681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 * Cadence I2C controller has a bug wherein it generates
859681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 * invalid read transaction after HW timeout in master receiver mode.
860681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 * HW timeout is not used by this driver and the interrupt is disabled.
861681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 * But the feature itself cannot be disabled. Hence maximum value
862681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 * is written to this register to reduce the chances of error.
863681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	 */
864681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare	cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
865681d15a0f527af7ab3a783e1037de86fbcb136acVishnu Motghare
866df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
867df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		 id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
868df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
869df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
870df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
871df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannerr_clk_dis:
872df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	clk_disable_unprepare(id->clk);
873df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return ret;
874df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
875df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
876df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann/**
877df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * cdns_i2c_remove - Unregister the device after releasing the resources
878df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * @pdev:	Handle to the platform device structure
879df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
880df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * This function frees all the resources allocated to the device.
881df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann *
882df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann * Return: 0 always
883df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann */
884df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic int cdns_i2c_remove(struct platform_device *pdev)
885df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann{
886df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	struct cdns_i2c *id = platform_get_drvdata(pdev);
887df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
888df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	i2c_del_adapter(&id->adap);
889df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
890df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	clk_disable_unprepare(id->clk);
891df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
892df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	return 0;
893df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann}
894df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
895df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic const struct of_device_id cdns_i2c_of_match[] = {
896df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	{ .compatible = "cdns,i2c-r1p10", },
897df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	{ /* end of table */ }
898df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann};
899df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren BrinkmannMODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
900df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
901df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannstatic struct platform_driver cdns_i2c_drv = {
902df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	.driver = {
903df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		.name  = DRIVER_NAME,
904df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		.owner = THIS_MODULE,
905df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		.of_match_table = cdns_i2c_of_match,
906df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann		.pm = &cdns_i2c_dev_pm_ops,
907df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	},
908df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	.probe  = cdns_i2c_probe,
909df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann	.remove = cdns_i2c_remove,
910df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann};
911df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
912df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmannmodule_platform_driver(cdns_i2c_drv);
913df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren Brinkmann
914df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren BrinkmannMODULE_AUTHOR("Xilinx Inc.");
915df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren BrinkmannMODULE_DESCRIPTION("Cadence I2C bus driver");
916df8eb5691c48d3b03b918bdf065caa59c4281cdcSoren BrinkmannMODULE_LICENSE("GPL");
917