cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2c-kempld.c (9544B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * I2C bus driver for Kontron COM modules
      4 *
      5 * Copyright (c) 2010-2013 Kontron Europe GmbH
      6 * Author: Michael Brunner <michael.brunner@kontron.com>
      7 *
      8 * The driver is based on the i2c-ocores driver by Peter Korsgaard.
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/i2c.h>
     14#include <linux/delay.h>
     15#include <linux/mfd/kempld.h>
     16
     17#define KEMPLD_I2C_PRELOW	0x0b
     18#define KEMPLD_I2C_PREHIGH	0x0c
     19#define KEMPLD_I2C_DATA		0x0e
     20
     21#define KEMPLD_I2C_CTRL		0x0d
     22#define I2C_CTRL_IEN		0x40
     23#define I2C_CTRL_EN		0x80
     24
     25#define KEMPLD_I2C_STAT		0x0f
     26#define I2C_STAT_IF		0x01
     27#define I2C_STAT_TIP		0x02
     28#define I2C_STAT_ARBLOST	0x20
     29#define I2C_STAT_BUSY		0x40
     30#define I2C_STAT_NACK		0x80
     31
     32#define KEMPLD_I2C_CMD		0x0f
     33#define I2C_CMD_START		0x91
     34#define I2C_CMD_STOP		0x41
     35#define I2C_CMD_READ		0x21
     36#define I2C_CMD_WRITE		0x11
     37#define I2C_CMD_READ_ACK	0x21
     38#define I2C_CMD_READ_NACK	0x29
     39#define I2C_CMD_IACK		0x01
     40
     41#define KEMPLD_I2C_FREQ_MAX	2700	/* 2.7 mHz */
     42#define KEMPLD_I2C_FREQ_STD	100	/* 100 kHz */
     43
     44enum {
     45	STATE_DONE = 0,
     46	STATE_INIT,
     47	STATE_ADDR,
     48	STATE_ADDR10,
     49	STATE_START,
     50	STATE_WRITE,
     51	STATE_READ,
     52	STATE_ERROR,
     53};
     54
     55struct kempld_i2c_data {
     56	struct device			*dev;
     57	struct kempld_device_data	*pld;
     58	struct i2c_adapter		adap;
     59	struct i2c_msg			*msg;
     60	int				pos;
     61	int				nmsgs;
     62	int				state;
     63	bool				was_active;
     64};
     65
     66static unsigned int bus_frequency = KEMPLD_I2C_FREQ_STD;
     67module_param(bus_frequency, uint, 0);
     68MODULE_PARM_DESC(bus_frequency, "Set I2C bus frequency in kHz (default="
     69				__MODULE_STRING(KEMPLD_I2C_FREQ_STD)")");
     70
     71static int i2c_bus = -1;
     72module_param(i2c_bus, int, 0);
     73MODULE_PARM_DESC(i2c_bus, "Set I2C bus number (default=-1 for dynamic assignment)");
     74
     75static bool i2c_gpio_mux;
     76module_param(i2c_gpio_mux, bool, 0);
     77MODULE_PARM_DESC(i2c_gpio_mux, "Enable I2C port on GPIO out (default=false)");
     78
     79/*
     80 * kempld_get_mutex must be called prior to calling this function.
     81 */
     82static int kempld_i2c_process(struct kempld_i2c_data *i2c)
     83{
     84	struct kempld_device_data *pld = i2c->pld;
     85	u8 stat = kempld_read8(pld, KEMPLD_I2C_STAT);
     86	struct i2c_msg *msg = i2c->msg;
     87	u8 addr;
     88
     89	/* Ready? */
     90	if (stat & I2C_STAT_TIP)
     91		return -EBUSY;
     92
     93	if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) {
     94		/* Stop has been sent */
     95		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK);
     96		if (i2c->state == STATE_ERROR)
     97			return -EIO;
     98		return 0;
     99	}
    100
    101	/* Error? */
    102	if (stat & I2C_STAT_ARBLOST) {
    103		i2c->state = STATE_ERROR;
    104		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP);
    105		return -EAGAIN;
    106	}
    107
    108	if (i2c->state == STATE_INIT) {
    109		if (stat & I2C_STAT_BUSY)
    110			return -EBUSY;
    111
    112		i2c->state = STATE_ADDR;
    113	}
    114
    115	if (i2c->state == STATE_ADDR) {
    116		/* 10 bit address? */
    117		if (i2c->msg->flags & I2C_M_TEN) {
    118			addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6);
    119			/* Set read bit if necessary */
    120			addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0;
    121			i2c->state = STATE_ADDR10;
    122		} else {
    123			addr = i2c_8bit_addr_from_msg(i2c->msg);
    124			i2c->state = STATE_START;
    125		}
    126
    127		kempld_write8(pld, KEMPLD_I2C_DATA, addr);
    128		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START);
    129
    130		return 0;
    131	}
    132
    133	/* Second part of 10 bit addressing */
    134	if (i2c->state == STATE_ADDR10) {
    135		kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff);
    136		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE);
    137
    138		i2c->state = STATE_START;
    139		return 0;
    140	}
    141
    142	if (i2c->state == STATE_START || i2c->state == STATE_WRITE) {
    143		i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
    144
    145		if (stat & I2C_STAT_NACK) {
    146			i2c->state = STATE_ERROR;
    147			kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP);
    148			return -ENXIO;
    149		}
    150	} else {
    151		msg->buf[i2c->pos++] = kempld_read8(pld, KEMPLD_I2C_DATA);
    152	}
    153
    154	if (i2c->pos >= msg->len) {
    155		i2c->nmsgs--;
    156		i2c->msg++;
    157		i2c->pos = 0;
    158		msg = i2c->msg;
    159
    160		if (i2c->nmsgs) {
    161			if (!(msg->flags & I2C_M_NOSTART)) {
    162				i2c->state = STATE_ADDR;
    163				return 0;
    164			} else {
    165				i2c->state = (msg->flags & I2C_M_RD)
    166					? STATE_READ : STATE_WRITE;
    167			}
    168		} else {
    169			i2c->state = STATE_DONE;
    170			kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP);
    171			return 0;
    172		}
    173	}
    174
    175	if (i2c->state == STATE_READ) {
    176		kempld_write8(pld, KEMPLD_I2C_CMD, i2c->pos == (msg->len - 1) ?
    177			      I2C_CMD_READ_NACK : I2C_CMD_READ_ACK);
    178	} else {
    179		kempld_write8(pld, KEMPLD_I2C_DATA, msg->buf[i2c->pos++]);
    180		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE);
    181	}
    182
    183	return 0;
    184}
    185
    186static int kempld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
    187				int num)
    188{
    189	struct kempld_i2c_data *i2c = i2c_get_adapdata(adap);
    190	struct kempld_device_data *pld = i2c->pld;
    191	unsigned long timeout = jiffies + HZ;
    192	int ret;
    193
    194	i2c->msg = msgs;
    195	i2c->pos = 0;
    196	i2c->nmsgs = num;
    197	i2c->state = STATE_INIT;
    198
    199	/* Handle the transfer */
    200	while (time_before(jiffies, timeout)) {
    201		kempld_get_mutex(pld);
    202		ret = kempld_i2c_process(i2c);
    203		kempld_release_mutex(pld);
    204
    205		if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR)
    206			return (i2c->state == STATE_DONE) ? num : ret;
    207
    208		if (ret == 0)
    209			timeout = jiffies + HZ;
    210
    211		usleep_range(5, 15);
    212	}
    213
    214	i2c->state = STATE_ERROR;
    215
    216	return -ETIMEDOUT;
    217}
    218
    219/*
    220 * kempld_get_mutex must be called prior to calling this function.
    221 */
    222static void kempld_i2c_device_init(struct kempld_i2c_data *i2c)
    223{
    224	struct kempld_device_data *pld = i2c->pld;
    225	u16 prescale_corr;
    226	long prescale;
    227	u8 ctrl;
    228	u8 stat;
    229	u8 cfg;
    230
    231	/* Make sure the device is disabled */
    232	ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL);
    233	ctrl &= ~(I2C_CTRL_EN | I2C_CTRL_IEN);
    234	kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl);
    235
    236	if (bus_frequency > KEMPLD_I2C_FREQ_MAX)
    237		bus_frequency = KEMPLD_I2C_FREQ_MAX;
    238
    239	if (pld->info.spec_major == 1)
    240		prescale = pld->pld_clock / (bus_frequency * 5) - 1000;
    241	else
    242		prescale = pld->pld_clock / (bus_frequency * 4) - 3000;
    243
    244	if (prescale < 0)
    245		prescale = 0;
    246
    247	/* Round to the best matching value */
    248	prescale_corr = prescale / 1000;
    249	if (prescale % 1000 >= 500)
    250		prescale_corr++;
    251
    252	kempld_write8(pld, KEMPLD_I2C_PRELOW, prescale_corr & 0xff);
    253	kempld_write8(pld, KEMPLD_I2C_PREHIGH, prescale_corr >> 8);
    254
    255	/* Activate I2C bus output on GPIO pins */
    256	cfg = kempld_read8(pld, KEMPLD_CFG);
    257	if (i2c_gpio_mux)
    258		cfg |= KEMPLD_CFG_GPIO_I2C_MUX;
    259	else
    260		cfg &= ~KEMPLD_CFG_GPIO_I2C_MUX;
    261	kempld_write8(pld, KEMPLD_CFG, cfg);
    262
    263	/* Enable the device */
    264	kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK);
    265	ctrl |= I2C_CTRL_EN;
    266	kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl);
    267
    268	stat = kempld_read8(pld, KEMPLD_I2C_STAT);
    269	if (stat & I2C_STAT_BUSY)
    270		kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP);
    271}
    272
    273static u32 kempld_i2c_func(struct i2c_adapter *adap)
    274{
    275	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
    276}
    277
    278static const struct i2c_algorithm kempld_i2c_algorithm = {
    279	.master_xfer	= kempld_i2c_xfer,
    280	.functionality	= kempld_i2c_func,
    281};
    282
    283static const struct i2c_adapter kempld_i2c_adapter = {
    284	.owner		= THIS_MODULE,
    285	.name		= "i2c-kempld",
    286	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD |
    287			  I2C_CLASS_DEPRECATED,
    288	.algo		= &kempld_i2c_algorithm,
    289};
    290
    291static int kempld_i2c_probe(struct platform_device *pdev)
    292{
    293	struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
    294	struct kempld_i2c_data *i2c;
    295	int ret;
    296	u8 ctrl;
    297
    298	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
    299	if (!i2c)
    300		return -ENOMEM;
    301
    302	i2c->pld = pld;
    303	i2c->dev = &pdev->dev;
    304	i2c->adap = kempld_i2c_adapter;
    305	i2c->adap.dev.parent = i2c->dev;
    306	i2c_set_adapdata(&i2c->adap, i2c);
    307	platform_set_drvdata(pdev, i2c);
    308
    309	kempld_get_mutex(pld);
    310	ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL);
    311
    312	if (ctrl & I2C_CTRL_EN)
    313		i2c->was_active = true;
    314
    315	kempld_i2c_device_init(i2c);
    316	kempld_release_mutex(pld);
    317
    318	/* Add I2C adapter to I2C tree */
    319	if (i2c_bus >= -1)
    320		i2c->adap.nr = i2c_bus;
    321	ret = i2c_add_numbered_adapter(&i2c->adap);
    322	if (ret)
    323		return ret;
    324
    325	dev_info(i2c->dev, "I2C bus initialized at %dkHz\n",
    326		 bus_frequency);
    327
    328	return 0;
    329}
    330
    331static int kempld_i2c_remove(struct platform_device *pdev)
    332{
    333	struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
    334	struct kempld_device_data *pld = i2c->pld;
    335	u8 ctrl;
    336
    337	kempld_get_mutex(pld);
    338	/*
    339	 * Disable I2C logic if it was not activated before the
    340	 * driver loaded
    341	 */
    342	if (!i2c->was_active) {
    343		ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL);
    344		ctrl &= ~I2C_CTRL_EN;
    345		kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl);
    346	}
    347	kempld_release_mutex(pld);
    348
    349	i2c_del_adapter(&i2c->adap);
    350
    351	return 0;
    352}
    353
    354#ifdef CONFIG_PM
    355static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state)
    356{
    357	struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
    358	struct kempld_device_data *pld = i2c->pld;
    359	u8 ctrl;
    360
    361	kempld_get_mutex(pld);
    362	ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL);
    363	ctrl &= ~I2C_CTRL_EN;
    364	kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl);
    365	kempld_release_mutex(pld);
    366
    367	return 0;
    368}
    369
    370static int kempld_i2c_resume(struct platform_device *pdev)
    371{
    372	struct kempld_i2c_data *i2c = platform_get_drvdata(pdev);
    373	struct kempld_device_data *pld = i2c->pld;
    374
    375	kempld_get_mutex(pld);
    376	kempld_i2c_device_init(i2c);
    377	kempld_release_mutex(pld);
    378
    379	return 0;
    380}
    381#else
    382#define kempld_i2c_suspend	NULL
    383#define kempld_i2c_resume	NULL
    384#endif
    385
    386static struct platform_driver kempld_i2c_driver = {
    387	.driver = {
    388		.name = "kempld-i2c",
    389	},
    390	.probe		= kempld_i2c_probe,
    391	.remove		= kempld_i2c_remove,
    392	.suspend	= kempld_i2c_suspend,
    393	.resume		= kempld_i2c_resume,
    394};
    395
    396module_platform_driver(kempld_i2c_driver);
    397
    398MODULE_DESCRIPTION("KEM PLD I2C Driver");
    399MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
    400MODULE_LICENSE("GPL");
    401MODULE_ALIAS("platform:kempld_i2c");