18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Juergen Fitschen <me@jue.yt> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "i2c-at91.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci struct at91_twi_dev *dev = dev_id; 188c2ecf20Sopenharmony_ci const unsigned status = at91_twi_read(dev, AT91_TWI_SR); 198c2ecf20Sopenharmony_ci const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR); 208c2ecf20Sopenharmony_ci u8 value; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci if (!irqstatus) 238c2ecf20Sopenharmony_ci return IRQ_NONE; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* slave address has been detected on I2C bus */ 268c2ecf20Sopenharmony_ci if (irqstatus & AT91_TWI_SVACC) { 278c2ecf20Sopenharmony_ci if (status & AT91_TWI_SVREAD) { 288c2ecf20Sopenharmony_ci i2c_slave_event(dev->slave, 298c2ecf20Sopenharmony_ci I2C_SLAVE_READ_REQUESTED, &value); 308c2ecf20Sopenharmony_ci writeb_relaxed(value, dev->base + AT91_TWI_THR); 318c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IER, 328c2ecf20Sopenharmony_ci AT91_TWI_TXRDY | AT91_TWI_EOSACC); 338c2ecf20Sopenharmony_ci } else { 348c2ecf20Sopenharmony_ci i2c_slave_event(dev->slave, 358c2ecf20Sopenharmony_ci I2C_SLAVE_WRITE_REQUESTED, &value); 368c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IER, 378c2ecf20Sopenharmony_ci AT91_TWI_RXRDY | AT91_TWI_EOSACC); 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* byte transmitted to remote master */ 438c2ecf20Sopenharmony_ci if (irqstatus & AT91_TWI_TXRDY) { 448c2ecf20Sopenharmony_ci i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value); 458c2ecf20Sopenharmony_ci writeb_relaxed(value, dev->base + AT91_TWI_THR); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* byte received from remote master */ 498c2ecf20Sopenharmony_ci if (irqstatus & AT91_TWI_RXRDY) { 508c2ecf20Sopenharmony_ci value = readb_relaxed(dev->base + AT91_TWI_RHR); 518c2ecf20Sopenharmony_ci i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* master sent stop */ 558c2ecf20Sopenharmony_ci if (irqstatus & AT91_TWI_EOSACC) { 568c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IDR, 578c2ecf20Sopenharmony_ci AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC); 588c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC); 598c2ecf20Sopenharmony_ci i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int at91_reg_slave(struct i2c_client *slave) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (dev->slave) 708c2ecf20Sopenharmony_ci return -EBUSY; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (slave->flags & I2C_CLIENT_TEN) 738c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Make sure twi_clk doesn't get turned off! */ 768c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev->dev); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci dev->slave = slave; 798c2ecf20Sopenharmony_ci dev->smr = AT91_TWI_SMR_SADR(slave->addr); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci at91_init_twi_bus(dev); 828c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int at91_unreg_slave(struct i2c_client *slave) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci WARN_ON(!dev->slave); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci dev_info(dev->dev, "leaving slave mode\n"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci dev->slave = NULL; 988c2ecf20Sopenharmony_ci dev->smr = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci at91_init_twi_bus(dev); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci pm_runtime_put(dev->dev); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic u32 at91_twi_func(struct i2c_adapter *adapter) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL 1108c2ecf20Sopenharmony_ci | I2C_FUNC_SMBUS_READ_BLOCK_DATA; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct i2c_algorithm at91_twi_algorithm_slave = { 1148c2ecf20Sopenharmony_ci .reg_slave = at91_reg_slave, 1158c2ecf20Sopenharmony_ci .unreg_slave = at91_unreg_slave, 1168c2ecf20Sopenharmony_ci .functionality = at91_twi_func, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciint at91_twi_probe_slave(struct platform_device *pdev, 1208c2ecf20Sopenharmony_ci u32 phy_addr, struct at91_twi_dev *dev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int rc; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave, 1258c2ecf20Sopenharmony_ci 0, dev_name(dev->dev), dev); 1268c2ecf20Sopenharmony_ci if (rc) { 1278c2ecf20Sopenharmony_ci dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc); 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev->adapter.algo = &at91_twi_algorithm_slave; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_civoid at91_init_twi_bus_slave(struct at91_twi_dev *dev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS); 1398c2ecf20Sopenharmony_ci if (dev->slave_detected && dev->smr) { 1408c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_SMR, dev->smr); 1418c2ecf20Sopenharmony_ci at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 144