brintos

brintos / linux-shallow public Read only

0
0
Text · 3.4 KiB · 131a67d Raw
143 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)4 *5 *  Copyright (C) 2017 Juergen Fitschen <me@jue.yt>6 */7 8#include <linux/err.h>9#include <linux/i2c.h>10#include <linux/interrupt.h>11#include <linux/pm_runtime.h>12 13#include "i2c-at91.h"14 15static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)16{17	struct at91_twi_dev *dev = dev_id;18	const unsigned status = at91_twi_read(dev, AT91_TWI_SR);19	const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);20	u8 value;21 22	if (!irqstatus)23		return IRQ_NONE;24 25	/* slave address has been detected on I2C bus */26	if (irqstatus & AT91_TWI_SVACC) {27		if (status & AT91_TWI_SVREAD) {28			i2c_slave_event(dev->slave,29					I2C_SLAVE_READ_REQUESTED, &value);30			writeb_relaxed(value, dev->base + AT91_TWI_THR);31			at91_twi_write(dev, AT91_TWI_IER,32				       AT91_TWI_TXRDY | AT91_TWI_EOSACC);33		} else {34			i2c_slave_event(dev->slave,35					I2C_SLAVE_WRITE_REQUESTED, &value);36			at91_twi_write(dev, AT91_TWI_IER,37				       AT91_TWI_RXRDY | AT91_TWI_EOSACC);38		}39		at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);40	}41 42	/* byte transmitted to remote master */43	if (irqstatus & AT91_TWI_TXRDY) {44		i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);45		writeb_relaxed(value, dev->base + AT91_TWI_THR);46	}47 48	/* byte received from remote master */49	if (irqstatus & AT91_TWI_RXRDY) {50		value = readb_relaxed(dev->base + AT91_TWI_RHR);51		i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);52	}53 54	/* master sent stop */55	if (irqstatus & AT91_TWI_EOSACC) {56		at91_twi_write(dev, AT91_TWI_IDR,57			       AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);58		at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);59		i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);60	}61 62	return IRQ_HANDLED;63}64 65static int at91_reg_slave(struct i2c_client *slave)66{67	struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);68 69	if (dev->slave)70		return -EBUSY;71 72	if (slave->flags & I2C_CLIENT_TEN)73		return -EAFNOSUPPORT;74 75	/* Make sure twi_clk doesn't get turned off! */76	pm_runtime_get_sync(dev->dev);77 78	dev->slave = slave;79	dev->smr = AT91_TWI_SMR_SADR(slave->addr);80 81	at91_init_twi_bus(dev);82	at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);83 84	dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);85 86	return 0;87}88 89static int at91_unreg_slave(struct i2c_client *slave)90{91	struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);92 93	WARN_ON(!dev->slave);94 95	dev_info(dev->dev, "leaving slave mode\n");96 97	dev->slave = NULL;98	dev->smr = 0;99 100	at91_init_twi_bus(dev);101 102	pm_runtime_put(dev->dev);103 104	return 0;105}106 107static u32 at91_twi_func(struct i2c_adapter *adapter)108{109	return I2C_FUNC_SLAVE;110}111 112static const struct i2c_algorithm at91_twi_algorithm_slave = {113	.reg_slave	= at91_reg_slave,114	.unreg_slave	= at91_unreg_slave,115	.functionality	= at91_twi_func,116};117 118int at91_twi_probe_slave(struct platform_device *pdev,119			 u32 phy_addr, struct at91_twi_dev *dev)120{121	int rc;122 123	rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,124			      0, dev_name(dev->dev), dev);125	if (rc) {126		dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);127		return rc;128	}129 130	dev->adapter.algo = &at91_twi_algorithm_slave;131 132	return 0;133}134 135void at91_init_twi_bus_slave(struct at91_twi_dev *dev)136{137	at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);138	if (dev->slave_detected && dev->smr) {139		at91_twi_write(dev, AT91_TWI_SMR, dev->smr);140		at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);141	}142}143