brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · dac694a Raw
387 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006-2007 PA Semi, Inc4 *5 * SMBus host driver for PA Semi PWRficient6 */7 8#include <linux/module.h>9#include <linux/pci.h>10#include <linux/kernel.h>11#include <linux/stddef.h>12#include <linux/sched.h>13#include <linux/i2c.h>14#include <linux/delay.h>15#include <linux/slab.h>16#include <linux/io.h>17 18#include "i2c-pasemi-core.h"19 20/* Register offsets */21#define REG_MTXFIFO	0x0022#define REG_MRXFIFO	0x0423#define REG_SMSTA	0x1424#define REG_IMASK	0x1825#define REG_CTL		0x1c26#define REG_REV		0x2827 28/* Register defs */29#define MTXFIFO_READ	0x0000040030#define MTXFIFO_STOP	0x0000020031#define MTXFIFO_START	0x0000010032#define MTXFIFO_DATA_M	0x000000ff33 34#define MRXFIFO_EMPTY	0x0000010035#define MRXFIFO_DATA_M	0x000000ff36 37#define SMSTA_XEN	0x0800000038#define SMSTA_MTN	0x0020000039 40#define CTL_MRR		0x0000040041#define CTL_MTR		0x0000020042#define CTL_EN		0x0000080043#define CTL_CLK_M	0x000000ff44 45static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)46{47	dev_dbg(smbus->dev, "smbus write reg %x val %08x\n", reg, val);48	iowrite32(val, smbus->ioaddr + reg);49}50 51static inline int reg_read(struct pasemi_smbus *smbus, int reg)52{53	int ret;54	ret = ioread32(smbus->ioaddr + reg);55	dev_dbg(smbus->dev, "smbus read reg %x val %08x\n", reg, ret);56	return ret;57}58 59#define TXFIFO_WR(smbus, reg)	reg_write((smbus), REG_MTXFIFO, (reg))60#define RXFIFO_RD(smbus)	reg_read((smbus), REG_MRXFIFO)61 62static void pasemi_reset(struct pasemi_smbus *smbus)63{64	u32 val = (CTL_MTR | CTL_MRR | (smbus->clk_div & CTL_CLK_M));65 66	if (smbus->hw_rev >= 6)67		val |= CTL_EN;68 69	reg_write(smbus, REG_CTL, val);70	reinit_completion(&smbus->irq_completion);71}72 73static void pasemi_smb_clear(struct pasemi_smbus *smbus)74{75	unsigned int status;76 77	status = reg_read(smbus, REG_SMSTA);78	reg_write(smbus, REG_SMSTA, status);79}80 81static int pasemi_smb_waitready(struct pasemi_smbus *smbus)82{83	int timeout = 100;84	unsigned int status;85 86	if (smbus->use_irq) {87		reinit_completion(&smbus->irq_completion);88		reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);89		wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100));90		reg_write(smbus, REG_IMASK, 0);91		status = reg_read(smbus, REG_SMSTA);92	} else {93		status = reg_read(smbus, REG_SMSTA);94		while (!(status & SMSTA_XEN) && timeout--) {95			msleep(1);96			status = reg_read(smbus, REG_SMSTA);97		}98	}99 100	/* Got NACK? */101	if (status & SMSTA_MTN)102		return -ENXIO;103 104	if (timeout < 0) {105		dev_warn(smbus->dev, "Timeout, status 0x%08x\n", status);106		reg_write(smbus, REG_SMSTA, status);107		return -ETIME;108	}109 110	/* Clear XEN */111	reg_write(smbus, REG_SMSTA, SMSTA_XEN);112 113	return 0;114}115 116static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,117			       struct i2c_msg *msg, int stop)118{119	struct pasemi_smbus *smbus = adapter->algo_data;120	int read, i, err;121	u32 rd;122 123	read = msg->flags & I2C_M_RD ? 1 : 0;124 125	TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg));126 127	if (read) {128		TXFIFO_WR(smbus, msg->len | MTXFIFO_READ |129				 (stop ? MTXFIFO_STOP : 0));130 131		err = pasemi_smb_waitready(smbus);132		if (err)133			goto reset_out;134 135		for (i = 0; i < msg->len; i++) {136			rd = RXFIFO_RD(smbus);137			if (rd & MRXFIFO_EMPTY) {138				err = -ENODATA;139				goto reset_out;140			}141			msg->buf[i] = rd & MRXFIFO_DATA_M;142		}143	} else {144		for (i = 0; i < msg->len - 1; i++)145			TXFIFO_WR(smbus, msg->buf[i]);146 147		TXFIFO_WR(smbus, msg->buf[msg->len-1] |148			  (stop ? MTXFIFO_STOP : 0));149 150		if (stop) {151			err = pasemi_smb_waitready(smbus);152			if (err)153				goto reset_out;154		}155	}156 157	return 0;158 159 reset_out:160	pasemi_reset(smbus);161	return err;162}163 164static int pasemi_i2c_xfer(struct i2c_adapter *adapter,165			   struct i2c_msg *msgs, int num)166{167	struct pasemi_smbus *smbus = adapter->algo_data;168	int ret, i;169 170	pasemi_smb_clear(smbus);171 172	ret = 0;173 174	for (i = 0; i < num && !ret; i++)175		ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));176 177	return ret ? ret : num;178}179 180static int pasemi_smb_xfer(struct i2c_adapter *adapter,181		u16 addr, unsigned short flags, char read_write, u8 command,182		int size, union i2c_smbus_data *data)183{184	struct pasemi_smbus *smbus = adapter->algo_data;185	unsigned int rd;186	int read_flag, err;187	int len = 0, i;188 189	/* All our ops take 8-bit shifted addresses */190	addr <<= 1;191	read_flag = read_write == I2C_SMBUS_READ;192 193	pasemi_smb_clear(smbus);194 195	switch (size) {196	case I2C_SMBUS_QUICK:197		TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START |198			  MTXFIFO_STOP);199		break;200	case I2C_SMBUS_BYTE:201		TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START);202		if (read_write)203			TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ);204		else205			TXFIFO_WR(smbus, MTXFIFO_STOP | command);206		break;207	case I2C_SMBUS_BYTE_DATA:208		TXFIFO_WR(smbus, addr | MTXFIFO_START);209		TXFIFO_WR(smbus, command);210		if (read_write) {211			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);212			TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP);213		} else {214			TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte);215		}216		break;217	case I2C_SMBUS_WORD_DATA:218		TXFIFO_WR(smbus, addr | MTXFIFO_START);219		TXFIFO_WR(smbus, command);220		if (read_write) {221			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);222			TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP);223		} else {224			TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);225			TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8));226		}227		break;228	case I2C_SMBUS_BLOCK_DATA:229		TXFIFO_WR(smbus, addr | MTXFIFO_START);230		TXFIFO_WR(smbus, command);231		if (read_write) {232			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);233			TXFIFO_WR(smbus, 1 | MTXFIFO_READ);234			rd = RXFIFO_RD(smbus);235			len = min_t(u8, (rd & MRXFIFO_DATA_M),236				    I2C_SMBUS_BLOCK_MAX);237			TXFIFO_WR(smbus, len | MTXFIFO_READ |238					 MTXFIFO_STOP);239		} else {240			len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);241			TXFIFO_WR(smbus, len);242			for (i = 1; i < len; i++)243				TXFIFO_WR(smbus, data->block[i]);244			TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP);245		}246		break;247	case I2C_SMBUS_PROC_CALL:248		read_write = I2C_SMBUS_READ;249		TXFIFO_WR(smbus, addr | MTXFIFO_START);250		TXFIFO_WR(smbus, command);251		TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);252		TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M);253		TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);254		TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ);255		break;256	case I2C_SMBUS_BLOCK_PROC_CALL:257		len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1);258		read_write = I2C_SMBUS_READ;259		TXFIFO_WR(smbus, addr | MTXFIFO_START);260		TXFIFO_WR(smbus, command);261		TXFIFO_WR(smbus, len);262		for (i = 1; i <= len; i++)263			TXFIFO_WR(smbus, data->block[i]);264		TXFIFO_WR(smbus, addr | I2C_SMBUS_READ);265		TXFIFO_WR(smbus, MTXFIFO_READ | 1);266		rd = RXFIFO_RD(smbus);267		len = min_t(u8, (rd & MRXFIFO_DATA_M),268			    I2C_SMBUS_BLOCK_MAX - len);269		TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP);270		break;271 272	default:273		dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);274		return -EINVAL;275	}276 277	err = pasemi_smb_waitready(smbus);278	if (err)279		goto reset_out;280 281	if (read_write == I2C_SMBUS_WRITE)282		return 0;283 284	switch (size) {285	case I2C_SMBUS_BYTE:286	case I2C_SMBUS_BYTE_DATA:287		rd = RXFIFO_RD(smbus);288		if (rd & MRXFIFO_EMPTY) {289			err = -ENODATA;290			goto reset_out;291		}292		data->byte = rd & MRXFIFO_DATA_M;293		break;294	case I2C_SMBUS_WORD_DATA:295	case I2C_SMBUS_PROC_CALL:296		rd = RXFIFO_RD(smbus);297		if (rd & MRXFIFO_EMPTY) {298			err = -ENODATA;299			goto reset_out;300		}301		data->word = rd & MRXFIFO_DATA_M;302		rd = RXFIFO_RD(smbus);303		if (rd & MRXFIFO_EMPTY) {304			err = -ENODATA;305			goto reset_out;306		}307		data->word |= (rd & MRXFIFO_DATA_M) << 8;308		break;309	case I2C_SMBUS_BLOCK_DATA:310	case I2C_SMBUS_BLOCK_PROC_CALL:311		data->block[0] = len;312		for (i = 1; i <= len; i ++) {313			rd = RXFIFO_RD(smbus);314			if (rd & MRXFIFO_EMPTY) {315				err = -ENODATA;316				goto reset_out;317			}318			data->block[i] = rd & MRXFIFO_DATA_M;319		}320		break;321	}322 323	return 0;324 325 reset_out:326	pasemi_reset(smbus);327	return err;328}329 330static u32 pasemi_smb_func(struct i2c_adapter *adapter)331{332	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |333	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |334	       I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |335	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C;336}337 338static const struct i2c_algorithm smbus_algorithm = {339	.xfer = pasemi_i2c_xfer,340	.smbus_xfer = pasemi_smb_xfer,341	.functionality = pasemi_smb_func,342};343 344int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)345{346	int error;347 348	smbus->adapter.owner = THIS_MODULE;349	snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),350		 "PA Semi SMBus adapter (%s)", dev_name(smbus->dev));351	smbus->adapter.algo = &smbus_algorithm;352	smbus->adapter.algo_data = smbus;353 354	/* set up the sysfs linkage to our parent device */355	smbus->adapter.dev.parent = smbus->dev;356	smbus->use_irq = 0;357	init_completion(&smbus->irq_completion);358 359	if (smbus->hw_rev != PASEMI_HW_REV_PCI)360		smbus->hw_rev = reg_read(smbus, REG_REV);361 362	reg_write(smbus, REG_IMASK, 0);363 364	pasemi_reset(smbus);365 366	error = devm_i2c_add_adapter(smbus->dev, &smbus->adapter);367	if (error)368		return error;369 370	return 0;371}372EXPORT_SYMBOL_GPL(pasemi_i2c_common_probe);373 374irqreturn_t pasemi_irq_handler(int irq, void *dev_id)375{376	struct pasemi_smbus *smbus = dev_id;377 378	reg_write(smbus, REG_IMASK, 0);379	complete(&smbus->irq_completion);380	return IRQ_HANDLED;381}382EXPORT_SYMBOL_GPL(pasemi_irq_handler);383 384MODULE_LICENSE("GPL");385MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");386MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");387