164a6f1c4SConor Dooley // SPDX-License-Identifier: GPL-2.0
264a6f1c4SConor Dooley /*
364a6f1c4SConor Dooley * Microchip CoreI2C I2C controller driver
464a6f1c4SConor Dooley *
564a6f1c4SConor Dooley * Copyright (c) 2018-2022 Microchip Corporation. All rights reserved.
664a6f1c4SConor Dooley *
764a6f1c4SConor Dooley * Author: Daire McNamara <daire.mcnamara@microchip.com>
864a6f1c4SConor Dooley * Author: Conor Dooley <conor.dooley@microchip.com>
964a6f1c4SConor Dooley */
1064a6f1c4SConor Dooley #include <linux/clk.h>
1164a6f1c4SConor Dooley #include <linux/clkdev.h>
1264a6f1c4SConor Dooley #include <linux/err.h>
1364a6f1c4SConor Dooley #include <linux/i2c.h>
1464a6f1c4SConor Dooley #include <linux/interrupt.h>
1564a6f1c4SConor Dooley #include <linux/io.h>
1664a6f1c4SConor Dooley #include <linux/kernel.h>
1764a6f1c4SConor Dooley #include <linux/module.h>
1864a6f1c4SConor Dooley #include <linux/platform_device.h>
1964a6f1c4SConor Dooley
2064a6f1c4SConor Dooley #define CORE_I2C_CTRL (0x00)
2164a6f1c4SConor Dooley #define CTRL_CR0 BIT(0)
2264a6f1c4SConor Dooley #define CTRL_CR1 BIT(1)
2364a6f1c4SConor Dooley #define CTRL_AA BIT(2)
2464a6f1c4SConor Dooley #define CTRL_SI BIT(3)
2564a6f1c4SConor Dooley #define CTRL_STO BIT(4)
2664a6f1c4SConor Dooley #define CTRL_STA BIT(5)
2764a6f1c4SConor Dooley #define CTRL_ENS1 BIT(6)
2864a6f1c4SConor Dooley #define CTRL_CR2 BIT(7)
2964a6f1c4SConor Dooley
3064a6f1c4SConor Dooley #define STATUS_BUS_ERROR (0x00)
3164a6f1c4SConor Dooley #define STATUS_M_START_SENT (0x08)
3264a6f1c4SConor Dooley #define STATUS_M_REPEATED_START_SENT (0x10)
3364a6f1c4SConor Dooley #define STATUS_M_SLAW_ACK (0x18)
3464a6f1c4SConor Dooley #define STATUS_M_SLAW_NACK (0x20)
3564a6f1c4SConor Dooley #define STATUS_M_TX_DATA_ACK (0x28)
3664a6f1c4SConor Dooley #define STATUS_M_TX_DATA_NACK (0x30)
3764a6f1c4SConor Dooley #define STATUS_M_ARB_LOST (0x38)
3864a6f1c4SConor Dooley #define STATUS_M_SLAR_ACK (0x40)
3964a6f1c4SConor Dooley #define STATUS_M_SLAR_NACK (0x48)
4064a6f1c4SConor Dooley #define STATUS_M_RX_DATA_ACKED (0x50)
4164a6f1c4SConor Dooley #define STATUS_M_RX_DATA_NACKED (0x58)
4264a6f1c4SConor Dooley #define STATUS_S_SLAW_ACKED (0x60)
4364a6f1c4SConor Dooley #define STATUS_S_ARB_LOST_SLAW_ACKED (0x68)
4464a6f1c4SConor Dooley #define STATUS_S_GENERAL_CALL_ACKED (0x70)
4564a6f1c4SConor Dooley #define STATUS_S_ARB_LOST_GENERAL_CALL_ACKED (0x78)
4664a6f1c4SConor Dooley #define STATUS_S_RX_DATA_ACKED (0x80)
4764a6f1c4SConor Dooley #define STATUS_S_RX_DATA_NACKED (0x88)
4864a6f1c4SConor Dooley #define STATUS_S_GENERAL_CALL_RX_DATA_ACKED (0x90)
4964a6f1c4SConor Dooley #define STATUS_S_GENERAL_CALL_RX_DATA_NACKED (0x98)
5064a6f1c4SConor Dooley #define STATUS_S_RX_STOP (0xA0)
5164a6f1c4SConor Dooley #define STATUS_S_SLAR_ACKED (0xA8)
5264a6f1c4SConor Dooley #define STATUS_S_ARB_LOST_SLAR_ACKED (0xB0)
5364a6f1c4SConor Dooley #define STATUS_S_TX_DATA_ACK (0xB8)
5464a6f1c4SConor Dooley #define STATUS_S_TX_DATA_NACK (0xC0)
5564a6f1c4SConor Dooley #define STATUS_LAST_DATA_ACK (0xC8)
5664a6f1c4SConor Dooley #define STATUS_M_SMB_MASTER_RESET (0xD0)
5764a6f1c4SConor Dooley #define STATUS_S_SCL_LOW_TIMEOUT (0xD8) /* 25 ms */
5864a6f1c4SConor Dooley #define STATUS_NO_STATE_INFO (0xF8)
5964a6f1c4SConor Dooley
6064a6f1c4SConor Dooley #define CORE_I2C_STATUS (0x04)
6164a6f1c4SConor Dooley #define CORE_I2C_DATA (0x08)
6264a6f1c4SConor Dooley #define WRITE_BIT (0x0)
6364a6f1c4SConor Dooley #define READ_BIT (0x1)
6464a6f1c4SConor Dooley #define SLAVE_ADDR_SHIFT (1)
6564a6f1c4SConor Dooley #define CORE_I2C_SLAVE0_ADDR (0x0c)
6664a6f1c4SConor Dooley #define GENERAL_CALL_BIT (0x0)
6764a6f1c4SConor Dooley #define CORE_I2C_SMBUS (0x10)
6864a6f1c4SConor Dooley #define SMBALERT_INT_ENB (0x0)
6964a6f1c4SConor Dooley #define SMBSUS_INT_ENB (0x1)
7064a6f1c4SConor Dooley #define SMBUS_ENB (0x2)
7164a6f1c4SConor Dooley #define SMBALERT_NI_STATUS (0x3)
7264a6f1c4SConor Dooley #define SMBALERT_NO_CTRL (0x4)
7364a6f1c4SConor Dooley #define SMBSUS_NI_STATUS (0x5)
7464a6f1c4SConor Dooley #define SMBSUS_NO_CTRL (0x6)
7564a6f1c4SConor Dooley #define SMBUS_RESET (0x7)
7664a6f1c4SConor Dooley #define CORE_I2C_FREQ (0x14)
7764a6f1c4SConor Dooley #define CORE_I2C_GLITCHREG (0x18)
7864a6f1c4SConor Dooley #define CORE_I2C_SLAVE1_ADDR (0x1c)
7964a6f1c4SConor Dooley
8064a6f1c4SConor Dooley #define PCLK_DIV_960 (CTRL_CR2)
8164a6f1c4SConor Dooley #define PCLK_DIV_256 (0)
8264a6f1c4SConor Dooley #define PCLK_DIV_224 (CTRL_CR0)
8364a6f1c4SConor Dooley #define PCLK_DIV_192 (CTRL_CR1)
8464a6f1c4SConor Dooley #define PCLK_DIV_160 (CTRL_CR0 | CTRL_CR1)
8564a6f1c4SConor Dooley #define PCLK_DIV_120 (CTRL_CR0 | CTRL_CR2)
8664a6f1c4SConor Dooley #define PCLK_DIV_60 (CTRL_CR1 | CTRL_CR2)
8764a6f1c4SConor Dooley #define BCLK_DIV_8 (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
8864a6f1c4SConor Dooley #define CLK_MASK (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
8964a6f1c4SConor Dooley
9064a6f1c4SConor Dooley /**
9164a6f1c4SConor Dooley * struct mchp_corei2c_dev - Microchip CoreI2C device private data
9264a6f1c4SConor Dooley *
9364a6f1c4SConor Dooley * @base: pointer to register struct
9464a6f1c4SConor Dooley * @dev: device reference
9564a6f1c4SConor Dooley * @i2c_clk: clock reference for i2c input clock
96*3f66c65fSConor Dooley * @msg_queue: pointer to the messages requiring sending
9764a6f1c4SConor Dooley * @buf: pointer to msg buffer for easier use
9864a6f1c4SConor Dooley * @msg_complete: xfer completion object
9964a6f1c4SConor Dooley * @adapter: core i2c abstraction
10064a6f1c4SConor Dooley * @msg_err: error code for completed message
10164a6f1c4SConor Dooley * @bus_clk_rate: current i2c bus clock rate
10264a6f1c4SConor Dooley * @isr_status: cached copy of local ISR status
103*3f66c65fSConor Dooley * @total_num: total number of messages to be sent/received
104*3f66c65fSConor Dooley * @current_num: index of the current message being sent/received
10564a6f1c4SConor Dooley * @msg_len: number of bytes transferred in msg
10664a6f1c4SConor Dooley * @addr: address of the current slave
107*3f66c65fSConor Dooley * @restart_needed: whether or not a repeated start is required after current message
10864a6f1c4SConor Dooley */
10964a6f1c4SConor Dooley struct mchp_corei2c_dev {
11064a6f1c4SConor Dooley void __iomem *base;
11164a6f1c4SConor Dooley struct device *dev;
11264a6f1c4SConor Dooley struct clk *i2c_clk;
113*3f66c65fSConor Dooley struct i2c_msg *msg_queue;
11464a6f1c4SConor Dooley u8 *buf;
11564a6f1c4SConor Dooley struct completion msg_complete;
11664a6f1c4SConor Dooley struct i2c_adapter adapter;
11764a6f1c4SConor Dooley int msg_err;
118*3f66c65fSConor Dooley int total_num;
119*3f66c65fSConor Dooley int current_num;
12064a6f1c4SConor Dooley u32 bus_clk_rate;
12164a6f1c4SConor Dooley u32 isr_status;
12264a6f1c4SConor Dooley u16 msg_len;
12364a6f1c4SConor Dooley u8 addr;
124*3f66c65fSConor Dooley bool restart_needed;
12564a6f1c4SConor Dooley };
12664a6f1c4SConor Dooley
mchp_corei2c_core_disable(struct mchp_corei2c_dev * idev)12764a6f1c4SConor Dooley static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev)
12864a6f1c4SConor Dooley {
12964a6f1c4SConor Dooley u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
13064a6f1c4SConor Dooley
13164a6f1c4SConor Dooley ctrl &= ~CTRL_ENS1;
13264a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
13364a6f1c4SConor Dooley }
13464a6f1c4SConor Dooley
mchp_corei2c_core_enable(struct mchp_corei2c_dev * idev)13564a6f1c4SConor Dooley static void mchp_corei2c_core_enable(struct mchp_corei2c_dev *idev)
13664a6f1c4SConor Dooley {
13764a6f1c4SConor Dooley u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
13864a6f1c4SConor Dooley
13964a6f1c4SConor Dooley ctrl |= CTRL_ENS1;
14064a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
14164a6f1c4SConor Dooley }
14264a6f1c4SConor Dooley
mchp_corei2c_reset(struct mchp_corei2c_dev * idev)14364a6f1c4SConor Dooley static void mchp_corei2c_reset(struct mchp_corei2c_dev *idev)
14464a6f1c4SConor Dooley {
14564a6f1c4SConor Dooley mchp_corei2c_core_disable(idev);
14664a6f1c4SConor Dooley mchp_corei2c_core_enable(idev);
14764a6f1c4SConor Dooley }
14864a6f1c4SConor Dooley
mchp_corei2c_stop(struct mchp_corei2c_dev * idev)14964a6f1c4SConor Dooley static inline void mchp_corei2c_stop(struct mchp_corei2c_dev *idev)
15064a6f1c4SConor Dooley {
15164a6f1c4SConor Dooley u8 ctrl = readb(idev->base + CORE_I2C_CTRL);
15264a6f1c4SConor Dooley
15364a6f1c4SConor Dooley ctrl |= CTRL_STO;
15464a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
15564a6f1c4SConor Dooley }
15664a6f1c4SConor Dooley
mchp_corei2c_set_divisor(u32 rate,struct mchp_corei2c_dev * idev)15764a6f1c4SConor Dooley static inline int mchp_corei2c_set_divisor(u32 rate,
15864a6f1c4SConor Dooley struct mchp_corei2c_dev *idev)
15964a6f1c4SConor Dooley {
16064a6f1c4SConor Dooley u8 clkval, ctrl;
16164a6f1c4SConor Dooley
16264a6f1c4SConor Dooley if (rate >= 960)
16364a6f1c4SConor Dooley clkval = PCLK_DIV_960;
16464a6f1c4SConor Dooley else if (rate >= 256)
16564a6f1c4SConor Dooley clkval = PCLK_DIV_256;
16664a6f1c4SConor Dooley else if (rate >= 224)
16764a6f1c4SConor Dooley clkval = PCLK_DIV_224;
16864a6f1c4SConor Dooley else if (rate >= 192)
16964a6f1c4SConor Dooley clkval = PCLK_DIV_192;
17064a6f1c4SConor Dooley else if (rate >= 160)
17164a6f1c4SConor Dooley clkval = PCLK_DIV_160;
17264a6f1c4SConor Dooley else if (rate >= 120)
17364a6f1c4SConor Dooley clkval = PCLK_DIV_120;
17464a6f1c4SConor Dooley else if (rate >= 60)
17564a6f1c4SConor Dooley clkval = PCLK_DIV_60;
17664a6f1c4SConor Dooley else if (rate >= 8)
17764a6f1c4SConor Dooley clkval = BCLK_DIV_8;
17864a6f1c4SConor Dooley else
17964a6f1c4SConor Dooley return -EINVAL;
18064a6f1c4SConor Dooley
18164a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
18264a6f1c4SConor Dooley ctrl &= ~CLK_MASK;
18364a6f1c4SConor Dooley ctrl |= clkval;
18464a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
18564a6f1c4SConor Dooley
18664a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
18764a6f1c4SConor Dooley if ((ctrl & CLK_MASK) != clkval)
18864a6f1c4SConor Dooley return -EIO;
18964a6f1c4SConor Dooley
19064a6f1c4SConor Dooley return 0;
19164a6f1c4SConor Dooley }
19264a6f1c4SConor Dooley
mchp_corei2c_init(struct mchp_corei2c_dev * idev)19364a6f1c4SConor Dooley static int mchp_corei2c_init(struct mchp_corei2c_dev *idev)
19464a6f1c4SConor Dooley {
19564a6f1c4SConor Dooley u32 clk_rate = clk_get_rate(idev->i2c_clk);
19664a6f1c4SConor Dooley u32 divisor = clk_rate / idev->bus_clk_rate;
19764a6f1c4SConor Dooley int ret;
19864a6f1c4SConor Dooley
19964a6f1c4SConor Dooley ret = mchp_corei2c_set_divisor(divisor, idev);
20064a6f1c4SConor Dooley if (ret)
20164a6f1c4SConor Dooley return ret;
20264a6f1c4SConor Dooley
20364a6f1c4SConor Dooley mchp_corei2c_reset(idev);
20464a6f1c4SConor Dooley
20564a6f1c4SConor Dooley return 0;
20664a6f1c4SConor Dooley }
20764a6f1c4SConor Dooley
mchp_corei2c_empty_rx(struct mchp_corei2c_dev * idev)20864a6f1c4SConor Dooley static void mchp_corei2c_empty_rx(struct mchp_corei2c_dev *idev)
20964a6f1c4SConor Dooley {
21064a6f1c4SConor Dooley u8 ctrl;
21164a6f1c4SConor Dooley
21264a6f1c4SConor Dooley if (idev->msg_len > 0) {
21364a6f1c4SConor Dooley *idev->buf++ = readb(idev->base + CORE_I2C_DATA);
21464a6f1c4SConor Dooley idev->msg_len--;
21564a6f1c4SConor Dooley }
21664a6f1c4SConor Dooley
217dde61c48SConor Dooley if (idev->msg_len <= 1) {
21864a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
21964a6f1c4SConor Dooley ctrl &= ~CTRL_AA;
22064a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
22164a6f1c4SConor Dooley }
22264a6f1c4SConor Dooley }
22364a6f1c4SConor Dooley
mchp_corei2c_fill_tx(struct mchp_corei2c_dev * idev)22464a6f1c4SConor Dooley static int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev)
22564a6f1c4SConor Dooley {
22664a6f1c4SConor Dooley if (idev->msg_len > 0)
22764a6f1c4SConor Dooley writeb(*idev->buf++, idev->base + CORE_I2C_DATA);
22864a6f1c4SConor Dooley idev->msg_len--;
22964a6f1c4SConor Dooley
23064a6f1c4SConor Dooley return 0;
23164a6f1c4SConor Dooley }
23264a6f1c4SConor Dooley
mchp_corei2c_next_msg(struct mchp_corei2c_dev * idev)233*3f66c65fSConor Dooley static void mchp_corei2c_next_msg(struct mchp_corei2c_dev *idev)
234*3f66c65fSConor Dooley {
235*3f66c65fSConor Dooley struct i2c_msg *this_msg;
236*3f66c65fSConor Dooley u8 ctrl;
237*3f66c65fSConor Dooley
238*3f66c65fSConor Dooley if (idev->current_num >= idev->total_num) {
239*3f66c65fSConor Dooley complete(&idev->msg_complete);
240*3f66c65fSConor Dooley return;
241*3f66c65fSConor Dooley }
242*3f66c65fSConor Dooley
243*3f66c65fSConor Dooley /*
244*3f66c65fSConor Dooley * If there's been an error, the isr needs to return control
245*3f66c65fSConor Dooley * to the "main" part of the driver, so as not to keep sending
246*3f66c65fSConor Dooley * messages once it completes and clears the SI bit.
247*3f66c65fSConor Dooley */
248*3f66c65fSConor Dooley if (idev->msg_err) {
249*3f66c65fSConor Dooley complete(&idev->msg_complete);
250*3f66c65fSConor Dooley return;
251*3f66c65fSConor Dooley }
252*3f66c65fSConor Dooley
253*3f66c65fSConor Dooley this_msg = idev->msg_queue++;
254*3f66c65fSConor Dooley
255*3f66c65fSConor Dooley if (idev->current_num < (idev->total_num - 1)) {
256*3f66c65fSConor Dooley struct i2c_msg *next_msg = idev->msg_queue;
257*3f66c65fSConor Dooley
258*3f66c65fSConor Dooley idev->restart_needed = next_msg->flags & I2C_M_RD;
259*3f66c65fSConor Dooley } else {
260*3f66c65fSConor Dooley idev->restart_needed = false;
261*3f66c65fSConor Dooley }
262*3f66c65fSConor Dooley
263*3f66c65fSConor Dooley idev->addr = i2c_8bit_addr_from_msg(this_msg);
264*3f66c65fSConor Dooley idev->msg_len = this_msg->len;
265*3f66c65fSConor Dooley idev->buf = this_msg->buf;
266*3f66c65fSConor Dooley
267*3f66c65fSConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
268*3f66c65fSConor Dooley ctrl |= CTRL_STA;
269*3f66c65fSConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
270*3f66c65fSConor Dooley
271*3f66c65fSConor Dooley idev->current_num++;
272*3f66c65fSConor Dooley }
273*3f66c65fSConor Dooley
mchp_corei2c_handle_isr(struct mchp_corei2c_dev * idev)27464a6f1c4SConor Dooley static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
27564a6f1c4SConor Dooley {
27664a6f1c4SConor Dooley u32 status = idev->isr_status;
27764a6f1c4SConor Dooley u8 ctrl;
27864a6f1c4SConor Dooley bool last_byte = false, finished = false;
27964a6f1c4SConor Dooley
28064a6f1c4SConor Dooley if (!idev->buf)
28164a6f1c4SConor Dooley return IRQ_NONE;
28264a6f1c4SConor Dooley
28364a6f1c4SConor Dooley switch (status) {
28464a6f1c4SConor Dooley case STATUS_M_START_SENT:
28564a6f1c4SConor Dooley case STATUS_M_REPEATED_START_SENT:
28664a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
28764a6f1c4SConor Dooley ctrl &= ~CTRL_STA;
28864a6f1c4SConor Dooley writeb(idev->addr, idev->base + CORE_I2C_DATA);
28964a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
29064a6f1c4SConor Dooley break;
29164a6f1c4SConor Dooley case STATUS_M_ARB_LOST:
29264a6f1c4SConor Dooley idev->msg_err = -EAGAIN;
29364a6f1c4SConor Dooley finished = true;
29464a6f1c4SConor Dooley break;
29564a6f1c4SConor Dooley case STATUS_M_SLAW_ACK:
29664a6f1c4SConor Dooley case STATUS_M_TX_DATA_ACK:
297*3f66c65fSConor Dooley if (idev->msg_len > 0) {
29864a6f1c4SConor Dooley mchp_corei2c_fill_tx(idev);
299*3f66c65fSConor Dooley } else {
300*3f66c65fSConor Dooley if (idev->restart_needed)
301*3f66c65fSConor Dooley finished = true;
30264a6f1c4SConor Dooley else
30364a6f1c4SConor Dooley last_byte = true;
304*3f66c65fSConor Dooley }
30564a6f1c4SConor Dooley break;
30664a6f1c4SConor Dooley case STATUS_M_TX_DATA_NACK:
30764a6f1c4SConor Dooley case STATUS_M_SLAR_NACK:
30864a6f1c4SConor Dooley case STATUS_M_SLAW_NACK:
30964a6f1c4SConor Dooley idev->msg_err = -ENXIO;
31064a6f1c4SConor Dooley last_byte = true;
31164a6f1c4SConor Dooley break;
31264a6f1c4SConor Dooley case STATUS_M_SLAR_ACK:
31364a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
31464a6f1c4SConor Dooley if (idev->msg_len == 1u) {
31564a6f1c4SConor Dooley ctrl &= ~CTRL_AA;
31664a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
31764a6f1c4SConor Dooley } else {
31864a6f1c4SConor Dooley ctrl |= CTRL_AA;
31964a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
32064a6f1c4SConor Dooley }
32164a6f1c4SConor Dooley if (idev->msg_len < 1u)
32264a6f1c4SConor Dooley last_byte = true;
32364a6f1c4SConor Dooley break;
32464a6f1c4SConor Dooley case STATUS_M_RX_DATA_ACKED:
32564a6f1c4SConor Dooley mchp_corei2c_empty_rx(idev);
32664a6f1c4SConor Dooley break;
32764a6f1c4SConor Dooley case STATUS_M_RX_DATA_NACKED:
32864a6f1c4SConor Dooley mchp_corei2c_empty_rx(idev);
32964a6f1c4SConor Dooley if (idev->msg_len == 0)
33064a6f1c4SConor Dooley last_byte = true;
33164a6f1c4SConor Dooley break;
33264a6f1c4SConor Dooley default:
33364a6f1c4SConor Dooley break;
33464a6f1c4SConor Dooley }
33564a6f1c4SConor Dooley
33664a6f1c4SConor Dooley /* On the last byte to be transmitted, send STOP */
33764a6f1c4SConor Dooley if (last_byte)
33864a6f1c4SConor Dooley mchp_corei2c_stop(idev);
33964a6f1c4SConor Dooley
34064a6f1c4SConor Dooley if (last_byte || finished)
341*3f66c65fSConor Dooley mchp_corei2c_next_msg(idev);
34264a6f1c4SConor Dooley
34364a6f1c4SConor Dooley return IRQ_HANDLED;
34464a6f1c4SConor Dooley }
34564a6f1c4SConor Dooley
mchp_corei2c_isr(int irq,void * _dev)34664a6f1c4SConor Dooley static irqreturn_t mchp_corei2c_isr(int irq, void *_dev)
34764a6f1c4SConor Dooley {
34864a6f1c4SConor Dooley struct mchp_corei2c_dev *idev = _dev;
34964a6f1c4SConor Dooley irqreturn_t ret = IRQ_NONE;
35064a6f1c4SConor Dooley u8 ctrl;
35164a6f1c4SConor Dooley
35264a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
35364a6f1c4SConor Dooley if (ctrl & CTRL_SI) {
35464a6f1c4SConor Dooley idev->isr_status = readb(idev->base + CORE_I2C_STATUS);
35564a6f1c4SConor Dooley ret = mchp_corei2c_handle_isr(idev);
35664a6f1c4SConor Dooley }
35764a6f1c4SConor Dooley
35864a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
35964a6f1c4SConor Dooley ctrl &= ~CTRL_SI;
36064a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
36164a6f1c4SConor Dooley
36264a6f1c4SConor Dooley return ret;
36364a6f1c4SConor Dooley }
36464a6f1c4SConor Dooley
mchp_corei2c_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)365*3f66c65fSConor Dooley static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
366*3f66c65fSConor Dooley int num)
36764a6f1c4SConor Dooley {
368*3f66c65fSConor Dooley struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
369*3f66c65fSConor Dooley struct i2c_msg *this_msg = msgs;
37064a6f1c4SConor Dooley unsigned long time_left;
371*3f66c65fSConor Dooley u8 ctrl;
37264a6f1c4SConor Dooley
37364a6f1c4SConor Dooley mchp_corei2c_core_enable(idev);
37464a6f1c4SConor Dooley
375*3f66c65fSConor Dooley /*
376*3f66c65fSConor Dooley * The isr controls the flow of a transfer, this info needs to be saved
377*3f66c65fSConor Dooley * to a location that it can access the queue information from.
378*3f66c65fSConor Dooley */
379*3f66c65fSConor Dooley idev->restart_needed = false;
380*3f66c65fSConor Dooley idev->msg_queue = msgs;
381*3f66c65fSConor Dooley idev->total_num = num;
382*3f66c65fSConor Dooley idev->current_num = 0;
383*3f66c65fSConor Dooley
384*3f66c65fSConor Dooley /*
385*3f66c65fSConor Dooley * But the first entry to the isr is triggered by the start in this
386*3f66c65fSConor Dooley * function, so the first message needs to be "dequeued".
387*3f66c65fSConor Dooley */
388*3f66c65fSConor Dooley idev->addr = i2c_8bit_addr_from_msg(this_msg);
389*3f66c65fSConor Dooley idev->msg_len = this_msg->len;
390*3f66c65fSConor Dooley idev->buf = this_msg->buf;
391*3f66c65fSConor Dooley idev->msg_err = 0;
392*3f66c65fSConor Dooley
393*3f66c65fSConor Dooley if (idev->total_num > 1) {
394*3f66c65fSConor Dooley struct i2c_msg *next_msg = msgs + 1;
395*3f66c65fSConor Dooley
396*3f66c65fSConor Dooley idev->restart_needed = next_msg->flags & I2C_M_RD;
397*3f66c65fSConor Dooley }
398*3f66c65fSConor Dooley
399*3f66c65fSConor Dooley idev->current_num++;
400*3f66c65fSConor Dooley idev->msg_queue++;
401*3f66c65fSConor Dooley
402*3f66c65fSConor Dooley reinit_completion(&idev->msg_complete);
403*3f66c65fSConor Dooley
404*3f66c65fSConor Dooley /*
405*3f66c65fSConor Dooley * Send the first start to pass control to the isr
406*3f66c65fSConor Dooley */
40764a6f1c4SConor Dooley ctrl = readb(idev->base + CORE_I2C_CTRL);
40864a6f1c4SConor Dooley ctrl |= CTRL_STA;
40964a6f1c4SConor Dooley writeb(ctrl, idev->base + CORE_I2C_CTRL);
41064a6f1c4SConor Dooley
41164a6f1c4SConor Dooley time_left = wait_for_completion_timeout(&idev->msg_complete,
41264a6f1c4SConor Dooley idev->adapter.timeout);
41364a6f1c4SConor Dooley if (!time_left)
41464a6f1c4SConor Dooley return -ETIMEDOUT;
41564a6f1c4SConor Dooley
416*3f66c65fSConor Dooley if (idev->msg_err)
41764a6f1c4SConor Dooley return idev->msg_err;
41864a6f1c4SConor Dooley
41964a6f1c4SConor Dooley return num;
42064a6f1c4SConor Dooley }
42164a6f1c4SConor Dooley
mchp_corei2c_func(struct i2c_adapter * adap)42264a6f1c4SConor Dooley static u32 mchp_corei2c_func(struct i2c_adapter *adap)
42364a6f1c4SConor Dooley {
42464a6f1c4SConor Dooley return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
42564a6f1c4SConor Dooley }
42664a6f1c4SConor Dooley
42764a6f1c4SConor Dooley static const struct i2c_algorithm mchp_corei2c_algo = {
42864a6f1c4SConor Dooley .master_xfer = mchp_corei2c_xfer,
42964a6f1c4SConor Dooley .functionality = mchp_corei2c_func,
43064a6f1c4SConor Dooley };
43164a6f1c4SConor Dooley
mchp_corei2c_probe(struct platform_device * pdev)43264a6f1c4SConor Dooley static int mchp_corei2c_probe(struct platform_device *pdev)
43364a6f1c4SConor Dooley {
43464a6f1c4SConor Dooley struct mchp_corei2c_dev *idev;
43564a6f1c4SConor Dooley struct resource *res;
43664a6f1c4SConor Dooley int irq, ret;
43764a6f1c4SConor Dooley
43864a6f1c4SConor Dooley idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
43964a6f1c4SConor Dooley if (!idev)
44064a6f1c4SConor Dooley return -ENOMEM;
44164a6f1c4SConor Dooley
44264a6f1c4SConor Dooley idev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
44364a6f1c4SConor Dooley if (IS_ERR(idev->base))
44464a6f1c4SConor Dooley return PTR_ERR(idev->base);
44564a6f1c4SConor Dooley
44664a6f1c4SConor Dooley irq = platform_get_irq(pdev, 0);
447adcf6eaeSZhu Wang if (irq < 0)
448adcf6eaeSZhu Wang return irq;
44964a6f1c4SConor Dooley
45064a6f1c4SConor Dooley idev->i2c_clk = devm_clk_get(&pdev->dev, NULL);
45164a6f1c4SConor Dooley if (IS_ERR(idev->i2c_clk))
45264a6f1c4SConor Dooley return dev_err_probe(&pdev->dev, PTR_ERR(idev->i2c_clk),
45364a6f1c4SConor Dooley "missing clock\n");
45464a6f1c4SConor Dooley
45564a6f1c4SConor Dooley idev->dev = &pdev->dev;
45664a6f1c4SConor Dooley init_completion(&idev->msg_complete);
45764a6f1c4SConor Dooley
45864a6f1c4SConor Dooley ret = device_property_read_u32(idev->dev, "clock-frequency",
45964a6f1c4SConor Dooley &idev->bus_clk_rate);
46064a6f1c4SConor Dooley if (ret || !idev->bus_clk_rate) {
46164a6f1c4SConor Dooley dev_info(&pdev->dev, "default to 100kHz\n");
46264a6f1c4SConor Dooley idev->bus_clk_rate = 100000;
46364a6f1c4SConor Dooley }
46464a6f1c4SConor Dooley
46564a6f1c4SConor Dooley if (idev->bus_clk_rate > 400000)
46664a6f1c4SConor Dooley return dev_err_probe(&pdev->dev, -EINVAL,
46764a6f1c4SConor Dooley "clock-frequency too high: %d\n",
46864a6f1c4SConor Dooley idev->bus_clk_rate);
46964a6f1c4SConor Dooley
47064a6f1c4SConor Dooley /*
47164a6f1c4SConor Dooley * This driver supports both the hard peripherals & soft FPGA cores.
47264a6f1c4SConor Dooley * The hard peripherals do not have shared IRQs, but we don't have
47364a6f1c4SConor Dooley * control over what way the interrupts are wired for the soft cores.
47464a6f1c4SConor Dooley */
47564a6f1c4SConor Dooley ret = devm_request_irq(&pdev->dev, irq, mchp_corei2c_isr, IRQF_SHARED,
47664a6f1c4SConor Dooley pdev->name, idev);
47764a6f1c4SConor Dooley if (ret)
47864a6f1c4SConor Dooley return dev_err_probe(&pdev->dev, ret,
47964a6f1c4SConor Dooley "failed to claim irq %d\n", irq);
48064a6f1c4SConor Dooley
48164a6f1c4SConor Dooley ret = clk_prepare_enable(idev->i2c_clk);
48264a6f1c4SConor Dooley if (ret)
48364a6f1c4SConor Dooley return dev_err_probe(&pdev->dev, ret,
48464a6f1c4SConor Dooley "failed to enable clock\n");
48564a6f1c4SConor Dooley
48664a6f1c4SConor Dooley ret = mchp_corei2c_init(idev);
48764a6f1c4SConor Dooley if (ret) {
48864a6f1c4SConor Dooley clk_disable_unprepare(idev->i2c_clk);
48964a6f1c4SConor Dooley return dev_err_probe(&pdev->dev, ret, "failed to program clock divider\n");
49064a6f1c4SConor Dooley }
49164a6f1c4SConor Dooley
49264a6f1c4SConor Dooley i2c_set_adapdata(&idev->adapter, idev);
49364a6f1c4SConor Dooley snprintf(idev->adapter.name, sizeof(idev->adapter.name),
49464a6f1c4SConor Dooley "Microchip I2C hw bus at %08lx", (unsigned long)res->start);
49564a6f1c4SConor Dooley idev->adapter.owner = THIS_MODULE;
49664a6f1c4SConor Dooley idev->adapter.algo = &mchp_corei2c_algo;
49764a6f1c4SConor Dooley idev->adapter.dev.parent = &pdev->dev;
49864a6f1c4SConor Dooley idev->adapter.dev.of_node = pdev->dev.of_node;
49964a6f1c4SConor Dooley idev->adapter.timeout = HZ;
50064a6f1c4SConor Dooley
50164a6f1c4SConor Dooley platform_set_drvdata(pdev, idev);
50264a6f1c4SConor Dooley
50364a6f1c4SConor Dooley ret = i2c_add_adapter(&idev->adapter);
50464a6f1c4SConor Dooley if (ret) {
50564a6f1c4SConor Dooley clk_disable_unprepare(idev->i2c_clk);
50664a6f1c4SConor Dooley return ret;
50764a6f1c4SConor Dooley }
50864a6f1c4SConor Dooley
50964a6f1c4SConor Dooley dev_info(&pdev->dev, "registered CoreI2C bus driver\n");
51064a6f1c4SConor Dooley
51164a6f1c4SConor Dooley return 0;
51264a6f1c4SConor Dooley }
51364a6f1c4SConor Dooley
mchp_corei2c_remove(struct platform_device * pdev)514e190a0c3SUwe Kleine-König static void mchp_corei2c_remove(struct platform_device *pdev)
51564a6f1c4SConor Dooley {
51664a6f1c4SConor Dooley struct mchp_corei2c_dev *idev = platform_get_drvdata(pdev);
51764a6f1c4SConor Dooley
51864a6f1c4SConor Dooley clk_disable_unprepare(idev->i2c_clk);
51964a6f1c4SConor Dooley i2c_del_adapter(&idev->adapter);
52064a6f1c4SConor Dooley }
52164a6f1c4SConor Dooley
52264a6f1c4SConor Dooley static const struct of_device_id mchp_corei2c_of_match[] = {
52364a6f1c4SConor Dooley { .compatible = "microchip,mpfs-i2c" },
52464a6f1c4SConor Dooley { .compatible = "microchip,corei2c-rtl-v7" },
52564a6f1c4SConor Dooley {},
52664a6f1c4SConor Dooley };
52764a6f1c4SConor Dooley MODULE_DEVICE_TABLE(of, mchp_corei2c_of_match);
52864a6f1c4SConor Dooley
52964a6f1c4SConor Dooley static struct platform_driver mchp_corei2c_driver = {
53064a6f1c4SConor Dooley .probe = mchp_corei2c_probe,
531e190a0c3SUwe Kleine-König .remove_new = mchp_corei2c_remove,
53264a6f1c4SConor Dooley .driver = {
53364a6f1c4SConor Dooley .name = "microchip-corei2c",
53464a6f1c4SConor Dooley .of_match_table = mchp_corei2c_of_match,
53564a6f1c4SConor Dooley },
53664a6f1c4SConor Dooley };
53764a6f1c4SConor Dooley
53864a6f1c4SConor Dooley module_platform_driver(mchp_corei2c_driver);
53964a6f1c4SConor Dooley
54064a6f1c4SConor Dooley MODULE_DESCRIPTION("Microchip CoreI2C bus driver");
54164a6f1c4SConor Dooley MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
54264a6f1c4SConor Dooley MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
54364a6f1c4SConor Dooley MODULE_LICENSE("GPL");
544