162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microchip CoreI2C I2C controller driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2018-2022 Microchip Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Daire McNamara <daire.mcnamara@microchip.com> 862306a36Sopenharmony_ci * Author: Conor Dooley <conor.dooley@microchip.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/clkdev.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define CORE_I2C_CTRL (0x00) 2162306a36Sopenharmony_ci#define CTRL_CR0 BIT(0) 2262306a36Sopenharmony_ci#define CTRL_CR1 BIT(1) 2362306a36Sopenharmony_ci#define CTRL_AA BIT(2) 2462306a36Sopenharmony_ci#define CTRL_SI BIT(3) 2562306a36Sopenharmony_ci#define CTRL_STO BIT(4) 2662306a36Sopenharmony_ci#define CTRL_STA BIT(5) 2762306a36Sopenharmony_ci#define CTRL_ENS1 BIT(6) 2862306a36Sopenharmony_ci#define CTRL_CR2 BIT(7) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define STATUS_BUS_ERROR (0x00) 3162306a36Sopenharmony_ci#define STATUS_M_START_SENT (0x08) 3262306a36Sopenharmony_ci#define STATUS_M_REPEATED_START_SENT (0x10) 3362306a36Sopenharmony_ci#define STATUS_M_SLAW_ACK (0x18) 3462306a36Sopenharmony_ci#define STATUS_M_SLAW_NACK (0x20) 3562306a36Sopenharmony_ci#define STATUS_M_TX_DATA_ACK (0x28) 3662306a36Sopenharmony_ci#define STATUS_M_TX_DATA_NACK (0x30) 3762306a36Sopenharmony_ci#define STATUS_M_ARB_LOST (0x38) 3862306a36Sopenharmony_ci#define STATUS_M_SLAR_ACK (0x40) 3962306a36Sopenharmony_ci#define STATUS_M_SLAR_NACK (0x48) 4062306a36Sopenharmony_ci#define STATUS_M_RX_DATA_ACKED (0x50) 4162306a36Sopenharmony_ci#define STATUS_M_RX_DATA_NACKED (0x58) 4262306a36Sopenharmony_ci#define STATUS_S_SLAW_ACKED (0x60) 4362306a36Sopenharmony_ci#define STATUS_S_ARB_LOST_SLAW_ACKED (0x68) 4462306a36Sopenharmony_ci#define STATUS_S_GENERAL_CALL_ACKED (0x70) 4562306a36Sopenharmony_ci#define STATUS_S_ARB_LOST_GENERAL_CALL_ACKED (0x78) 4662306a36Sopenharmony_ci#define STATUS_S_RX_DATA_ACKED (0x80) 4762306a36Sopenharmony_ci#define STATUS_S_RX_DATA_NACKED (0x88) 4862306a36Sopenharmony_ci#define STATUS_S_GENERAL_CALL_RX_DATA_ACKED (0x90) 4962306a36Sopenharmony_ci#define STATUS_S_GENERAL_CALL_RX_DATA_NACKED (0x98) 5062306a36Sopenharmony_ci#define STATUS_S_RX_STOP (0xA0) 5162306a36Sopenharmony_ci#define STATUS_S_SLAR_ACKED (0xA8) 5262306a36Sopenharmony_ci#define STATUS_S_ARB_LOST_SLAR_ACKED (0xB0) 5362306a36Sopenharmony_ci#define STATUS_S_TX_DATA_ACK (0xB8) 5462306a36Sopenharmony_ci#define STATUS_S_TX_DATA_NACK (0xC0) 5562306a36Sopenharmony_ci#define STATUS_LAST_DATA_ACK (0xC8) 5662306a36Sopenharmony_ci#define STATUS_M_SMB_MASTER_RESET (0xD0) 5762306a36Sopenharmony_ci#define STATUS_S_SCL_LOW_TIMEOUT (0xD8) /* 25 ms */ 5862306a36Sopenharmony_ci#define STATUS_NO_STATE_INFO (0xF8) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define CORE_I2C_STATUS (0x04) 6162306a36Sopenharmony_ci#define CORE_I2C_DATA (0x08) 6262306a36Sopenharmony_ci#define WRITE_BIT (0x0) 6362306a36Sopenharmony_ci#define READ_BIT (0x1) 6462306a36Sopenharmony_ci#define SLAVE_ADDR_SHIFT (1) 6562306a36Sopenharmony_ci#define CORE_I2C_SLAVE0_ADDR (0x0c) 6662306a36Sopenharmony_ci#define GENERAL_CALL_BIT (0x0) 6762306a36Sopenharmony_ci#define CORE_I2C_SMBUS (0x10) 6862306a36Sopenharmony_ci#define SMBALERT_INT_ENB (0x0) 6962306a36Sopenharmony_ci#define SMBSUS_INT_ENB (0x1) 7062306a36Sopenharmony_ci#define SMBUS_ENB (0x2) 7162306a36Sopenharmony_ci#define SMBALERT_NI_STATUS (0x3) 7262306a36Sopenharmony_ci#define SMBALERT_NO_CTRL (0x4) 7362306a36Sopenharmony_ci#define SMBSUS_NI_STATUS (0x5) 7462306a36Sopenharmony_ci#define SMBSUS_NO_CTRL (0x6) 7562306a36Sopenharmony_ci#define SMBUS_RESET (0x7) 7662306a36Sopenharmony_ci#define CORE_I2C_FREQ (0x14) 7762306a36Sopenharmony_ci#define CORE_I2C_GLITCHREG (0x18) 7862306a36Sopenharmony_ci#define CORE_I2C_SLAVE1_ADDR (0x1c) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define PCLK_DIV_960 (CTRL_CR2) 8162306a36Sopenharmony_ci#define PCLK_DIV_256 (0) 8262306a36Sopenharmony_ci#define PCLK_DIV_224 (CTRL_CR0) 8362306a36Sopenharmony_ci#define PCLK_DIV_192 (CTRL_CR1) 8462306a36Sopenharmony_ci#define PCLK_DIV_160 (CTRL_CR0 | CTRL_CR1) 8562306a36Sopenharmony_ci#define PCLK_DIV_120 (CTRL_CR0 | CTRL_CR2) 8662306a36Sopenharmony_ci#define PCLK_DIV_60 (CTRL_CR1 | CTRL_CR2) 8762306a36Sopenharmony_ci#define BCLK_DIV_8 (CTRL_CR0 | CTRL_CR1 | CTRL_CR2) 8862306a36Sopenharmony_ci#define CLK_MASK (CTRL_CR0 | CTRL_CR1 | CTRL_CR2) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * struct mchp_corei2c_dev - Microchip CoreI2C device private data 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * @base: pointer to register struct 9462306a36Sopenharmony_ci * @dev: device reference 9562306a36Sopenharmony_ci * @i2c_clk: clock reference for i2c input clock 9662306a36Sopenharmony_ci * @buf: pointer to msg buffer for easier use 9762306a36Sopenharmony_ci * @msg_complete: xfer completion object 9862306a36Sopenharmony_ci * @adapter: core i2c abstraction 9962306a36Sopenharmony_ci * @msg_err: error code for completed message 10062306a36Sopenharmony_ci * @bus_clk_rate: current i2c bus clock rate 10162306a36Sopenharmony_ci * @isr_status: cached copy of local ISR status 10262306a36Sopenharmony_ci * @msg_len: number of bytes transferred in msg 10362306a36Sopenharmony_ci * @addr: address of the current slave 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct mchp_corei2c_dev { 10662306a36Sopenharmony_ci void __iomem *base; 10762306a36Sopenharmony_ci struct device *dev; 10862306a36Sopenharmony_ci struct clk *i2c_clk; 10962306a36Sopenharmony_ci u8 *buf; 11062306a36Sopenharmony_ci struct completion msg_complete; 11162306a36Sopenharmony_ci struct i2c_adapter adapter; 11262306a36Sopenharmony_ci int msg_err; 11362306a36Sopenharmony_ci u32 bus_clk_rate; 11462306a36Sopenharmony_ci u32 isr_status; 11562306a36Sopenharmony_ci u16 msg_len; 11662306a36Sopenharmony_ci u8 addr; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u8 ctrl = readb(idev->base + CORE_I2C_CTRL); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ctrl &= ~CTRL_ENS1; 12462306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void mchp_corei2c_core_enable(struct mchp_corei2c_dev *idev) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u8 ctrl = readb(idev->base + CORE_I2C_CTRL); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ctrl |= CTRL_ENS1; 13262306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void mchp_corei2c_reset(struct mchp_corei2c_dev *idev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci mchp_corei2c_core_disable(idev); 13862306a36Sopenharmony_ci mchp_corei2c_core_enable(idev); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void mchp_corei2c_stop(struct mchp_corei2c_dev *idev) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u8 ctrl = readb(idev->base + CORE_I2C_CTRL); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci ctrl |= CTRL_STO; 14662306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic inline int mchp_corei2c_set_divisor(u32 rate, 15062306a36Sopenharmony_ci struct mchp_corei2c_dev *idev) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u8 clkval, ctrl; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (rate >= 960) 15562306a36Sopenharmony_ci clkval = PCLK_DIV_960; 15662306a36Sopenharmony_ci else if (rate >= 256) 15762306a36Sopenharmony_ci clkval = PCLK_DIV_256; 15862306a36Sopenharmony_ci else if (rate >= 224) 15962306a36Sopenharmony_ci clkval = PCLK_DIV_224; 16062306a36Sopenharmony_ci else if (rate >= 192) 16162306a36Sopenharmony_ci clkval = PCLK_DIV_192; 16262306a36Sopenharmony_ci else if (rate >= 160) 16362306a36Sopenharmony_ci clkval = PCLK_DIV_160; 16462306a36Sopenharmony_ci else if (rate >= 120) 16562306a36Sopenharmony_ci clkval = PCLK_DIV_120; 16662306a36Sopenharmony_ci else if (rate >= 60) 16762306a36Sopenharmony_ci clkval = PCLK_DIV_60; 16862306a36Sopenharmony_ci else if (rate >= 8) 16962306a36Sopenharmony_ci clkval = BCLK_DIV_8; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 17462306a36Sopenharmony_ci ctrl &= ~CLK_MASK; 17562306a36Sopenharmony_ci ctrl |= clkval; 17662306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 17962306a36Sopenharmony_ci if ((ctrl & CLK_MASK) != clkval) 18062306a36Sopenharmony_ci return -EIO; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int mchp_corei2c_init(struct mchp_corei2c_dev *idev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci u32 clk_rate = clk_get_rate(idev->i2c_clk); 18862306a36Sopenharmony_ci u32 divisor = clk_rate / idev->bus_clk_rate; 18962306a36Sopenharmony_ci int ret; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = mchp_corei2c_set_divisor(divisor, idev); 19262306a36Sopenharmony_ci if (ret) 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci mchp_corei2c_reset(idev); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void mchp_corei2c_empty_rx(struct mchp_corei2c_dev *idev) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u8 ctrl; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (idev->msg_len > 0) { 20562306a36Sopenharmony_ci *idev->buf++ = readb(idev->base + CORE_I2C_DATA); 20662306a36Sopenharmony_ci idev->msg_len--; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (idev->msg_len <= 1) { 21062306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 21162306a36Sopenharmony_ci ctrl &= ~CTRL_AA; 21262306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if (idev->msg_len > 0) 21962306a36Sopenharmony_ci writeb(*idev->buf++, idev->base + CORE_I2C_DATA); 22062306a36Sopenharmony_ci idev->msg_len--; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u32 status = idev->isr_status; 22862306a36Sopenharmony_ci u8 ctrl; 22962306a36Sopenharmony_ci bool last_byte = false, finished = false; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!idev->buf) 23262306a36Sopenharmony_ci return IRQ_NONE; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (status) { 23562306a36Sopenharmony_ci case STATUS_M_START_SENT: 23662306a36Sopenharmony_ci case STATUS_M_REPEATED_START_SENT: 23762306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 23862306a36Sopenharmony_ci ctrl &= ~CTRL_STA; 23962306a36Sopenharmony_ci writeb(idev->addr, idev->base + CORE_I2C_DATA); 24062306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 24162306a36Sopenharmony_ci if (idev->msg_len == 0) 24262306a36Sopenharmony_ci finished = true; 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case STATUS_M_ARB_LOST: 24562306a36Sopenharmony_ci idev->msg_err = -EAGAIN; 24662306a36Sopenharmony_ci finished = true; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case STATUS_M_SLAW_ACK: 24962306a36Sopenharmony_ci case STATUS_M_TX_DATA_ACK: 25062306a36Sopenharmony_ci if (idev->msg_len > 0) 25162306a36Sopenharmony_ci mchp_corei2c_fill_tx(idev); 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci last_byte = true; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case STATUS_M_TX_DATA_NACK: 25662306a36Sopenharmony_ci case STATUS_M_SLAR_NACK: 25762306a36Sopenharmony_ci case STATUS_M_SLAW_NACK: 25862306a36Sopenharmony_ci idev->msg_err = -ENXIO; 25962306a36Sopenharmony_ci last_byte = true; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case STATUS_M_SLAR_ACK: 26262306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 26362306a36Sopenharmony_ci if (idev->msg_len == 1u) { 26462306a36Sopenharmony_ci ctrl &= ~CTRL_AA; 26562306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci ctrl |= CTRL_AA; 26862306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci if (idev->msg_len < 1u) 27162306a36Sopenharmony_ci last_byte = true; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case STATUS_M_RX_DATA_ACKED: 27462306a36Sopenharmony_ci mchp_corei2c_empty_rx(idev); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case STATUS_M_RX_DATA_NACKED: 27762306a36Sopenharmony_ci mchp_corei2c_empty_rx(idev); 27862306a36Sopenharmony_ci if (idev->msg_len == 0) 27962306a36Sopenharmony_ci last_byte = true; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* On the last byte to be transmitted, send STOP */ 28662306a36Sopenharmony_ci if (last_byte) 28762306a36Sopenharmony_ci mchp_corei2c_stop(idev); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (last_byte || finished) 29062306a36Sopenharmony_ci complete(&idev->msg_complete); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return IRQ_HANDLED; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic irqreturn_t mchp_corei2c_isr(int irq, void *_dev) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct mchp_corei2c_dev *idev = _dev; 29862306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 29962306a36Sopenharmony_ci u8 ctrl; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 30262306a36Sopenharmony_ci if (ctrl & CTRL_SI) { 30362306a36Sopenharmony_ci idev->isr_status = readb(idev->base + CORE_I2C_STATUS); 30462306a36Sopenharmony_ci ret = mchp_corei2c_handle_isr(idev); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 30862306a36Sopenharmony_ci ctrl &= ~CTRL_SI; 30962306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev, 31562306a36Sopenharmony_ci struct i2c_msg *msg) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci u8 ctrl; 31862306a36Sopenharmony_ci unsigned long time_left; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci idev->addr = i2c_8bit_addr_from_msg(msg); 32162306a36Sopenharmony_ci idev->msg_len = msg->len; 32262306a36Sopenharmony_ci idev->buf = msg->buf; 32362306a36Sopenharmony_ci idev->msg_err = 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci reinit_completion(&idev->msg_complete); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci mchp_corei2c_core_enable(idev); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ctrl = readb(idev->base + CORE_I2C_CTRL); 33062306a36Sopenharmony_ci ctrl |= CTRL_STA; 33162306a36Sopenharmony_ci writeb(ctrl, idev->base + CORE_I2C_CTRL); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&idev->msg_complete, 33462306a36Sopenharmony_ci idev->adapter.timeout); 33562306a36Sopenharmony_ci if (!time_left) 33662306a36Sopenharmony_ci return -ETIMEDOUT; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return idev->msg_err; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 34262306a36Sopenharmony_ci int num) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap); 34562306a36Sopenharmony_ci int i, ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci for (i = 0; i < num; i++) { 34862306a36Sopenharmony_ci ret = mchp_corei2c_xfer_msg(idev, msgs++); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return num; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic u32 mchp_corei2c_func(struct i2c_adapter *adap) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic const struct i2c_algorithm mchp_corei2c_algo = { 36262306a36Sopenharmony_ci .master_xfer = mchp_corei2c_xfer, 36362306a36Sopenharmony_ci .functionality = mchp_corei2c_func, 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int mchp_corei2c_probe(struct platform_device *pdev) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct mchp_corei2c_dev *idev; 36962306a36Sopenharmony_ci struct resource *res; 37062306a36Sopenharmony_ci int irq, ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); 37362306a36Sopenharmony_ci if (!idev) 37462306a36Sopenharmony_ci return -ENOMEM; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci idev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 37762306a36Sopenharmony_ci if (IS_ERR(idev->base)) 37862306a36Sopenharmony_ci return PTR_ERR(idev->base); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 38162306a36Sopenharmony_ci if (irq < 0) 38262306a36Sopenharmony_ci return irq; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); 38562306a36Sopenharmony_ci if (IS_ERR(idev->i2c_clk)) 38662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(idev->i2c_clk), 38762306a36Sopenharmony_ci "missing clock\n"); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci idev->dev = &pdev->dev; 39062306a36Sopenharmony_ci init_completion(&idev->msg_complete); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = device_property_read_u32(idev->dev, "clock-frequency", 39362306a36Sopenharmony_ci &idev->bus_clk_rate); 39462306a36Sopenharmony_ci if (ret || !idev->bus_clk_rate) { 39562306a36Sopenharmony_ci dev_info(&pdev->dev, "default to 100kHz\n"); 39662306a36Sopenharmony_ci idev->bus_clk_rate = 100000; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (idev->bus_clk_rate > 400000) 40062306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -EINVAL, 40162306a36Sopenharmony_ci "clock-frequency too high: %d\n", 40262306a36Sopenharmony_ci idev->bus_clk_rate); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * This driver supports both the hard peripherals & soft FPGA cores. 40662306a36Sopenharmony_ci * The hard peripherals do not have shared IRQs, but we don't have 40762306a36Sopenharmony_ci * control over what way the interrupts are wired for the soft cores. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, mchp_corei2c_isr, IRQF_SHARED, 41062306a36Sopenharmony_ci pdev->name, idev); 41162306a36Sopenharmony_ci if (ret) 41262306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, 41362306a36Sopenharmony_ci "failed to claim irq %d\n", irq); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = clk_prepare_enable(idev->i2c_clk); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, 41862306a36Sopenharmony_ci "failed to enable clock\n"); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = mchp_corei2c_init(idev); 42162306a36Sopenharmony_ci if (ret) { 42262306a36Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 42362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "failed to program clock divider\n"); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci i2c_set_adapdata(&idev->adapter, idev); 42762306a36Sopenharmony_ci snprintf(idev->adapter.name, sizeof(idev->adapter.name), 42862306a36Sopenharmony_ci "Microchip I2C hw bus at %08lx", (unsigned long)res->start); 42962306a36Sopenharmony_ci idev->adapter.owner = THIS_MODULE; 43062306a36Sopenharmony_ci idev->adapter.algo = &mchp_corei2c_algo; 43162306a36Sopenharmony_ci idev->adapter.dev.parent = &pdev->dev; 43262306a36Sopenharmony_ci idev->adapter.dev.of_node = pdev->dev.of_node; 43362306a36Sopenharmony_ci idev->adapter.timeout = HZ; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci platform_set_drvdata(pdev, idev); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = i2c_add_adapter(&idev->adapter); 43862306a36Sopenharmony_ci if (ret) { 43962306a36Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci dev_info(&pdev->dev, "registered CoreI2C bus driver\n"); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void mchp_corei2c_remove(struct platform_device *pdev) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct mchp_corei2c_dev *idev = platform_get_drvdata(pdev); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 45362306a36Sopenharmony_ci i2c_del_adapter(&idev->adapter); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic const struct of_device_id mchp_corei2c_of_match[] = { 45762306a36Sopenharmony_ci { .compatible = "microchip,mpfs-i2c" }, 45862306a36Sopenharmony_ci { .compatible = "microchip,corei2c-rtl-v7" }, 45962306a36Sopenharmony_ci {}, 46062306a36Sopenharmony_ci}; 46162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mchp_corei2c_of_match); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct platform_driver mchp_corei2c_driver = { 46462306a36Sopenharmony_ci .probe = mchp_corei2c_probe, 46562306a36Sopenharmony_ci .remove_new = mchp_corei2c_remove, 46662306a36Sopenharmony_ci .driver = { 46762306a36Sopenharmony_ci .name = "microchip-corei2c", 46862306a36Sopenharmony_ci .of_match_table = mchp_corei2c_of_match, 46962306a36Sopenharmony_ci }, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cimodule_platform_driver(mchp_corei2c_driver); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip CoreI2C bus driver"); 47562306a36Sopenharmony_ciMODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); 47662306a36Sopenharmony_ciMODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 47762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 478