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-pca-isa.c (4690B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  i2c-pca-isa.c driver for PCA9564 on ISA boards
      4 *    Copyright (C) 2004 Arcom Control Systems
      5 *    Copyright (C) 2008 Pengutronix
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/ioport.h>
     10#include <linux/module.h>
     11#include <linux/moduleparam.h>
     12#include <linux/delay.h>
     13#include <linux/jiffies.h>
     14#include <linux/init.h>
     15#include <linux/interrupt.h>
     16#include <linux/wait.h>
     17#include <linux/isa.h>
     18#include <linux/i2c.h>
     19#include <linux/i2c-algo-pca.h>
     20#include <linux/io.h>
     21
     22#include <asm/irq.h>
     23
     24#define DRIVER "i2c-pca-isa"
     25#define IO_SIZE 4
     26
     27static unsigned long base;
     28static int irq = -1;
     29
     30/* Data sheet recommends 59kHz for 100kHz operation due to variation
     31 * in the actual clock rate */
     32static int clock  = 59000;
     33
     34static struct i2c_adapter pca_isa_ops;
     35static wait_queue_head_t pca_wait;
     36
     37static void pca_isa_writebyte(void *pd, int reg, int val)
     38{
     39#ifdef DEBUG_IO
     40	static char *names[] = { "T/O", "DAT", "ADR", "CON" };
     41	printk(KERN_DEBUG "*** write %s at %#lx <= %#04x\n", names[reg],
     42	       base+reg, val);
     43#endif
     44	outb(val, base+reg);
     45}
     46
     47static int pca_isa_readbyte(void *pd, int reg)
     48{
     49	int res = inb(base+reg);
     50#ifdef DEBUG_IO
     51	{
     52		static char *names[] = { "STA", "DAT", "ADR", "CON" };
     53		printk(KERN_DEBUG "*** read  %s => %#04x\n", names[reg], res);
     54	}
     55#endif
     56	return res;
     57}
     58
     59static int pca_isa_waitforcompletion(void *pd)
     60{
     61	unsigned long timeout;
     62	long ret;
     63
     64	if (irq > -1) {
     65		ret = wait_event_timeout(pca_wait,
     66				pca_isa_readbyte(pd, I2C_PCA_CON)
     67				& I2C_PCA_CON_SI, pca_isa_ops.timeout);
     68	} else {
     69		/* Do polling */
     70		timeout = jiffies + pca_isa_ops.timeout;
     71		do {
     72			ret = time_before(jiffies, timeout);
     73			if (pca_isa_readbyte(pd, I2C_PCA_CON)
     74					& I2C_PCA_CON_SI)
     75				break;
     76			udelay(100);
     77		} while (ret);
     78	}
     79
     80	return ret > 0;
     81}
     82
     83static void pca_isa_resetchip(void *pd)
     84{
     85	/* apparently only an external reset will do it. not a lot can be done */
     86	printk(KERN_WARNING DRIVER ": Haven't figured out how to do a reset yet\n");
     87}
     88
     89static irqreturn_t pca_handler(int this_irq, void *dev_id) {
     90	wake_up(&pca_wait);
     91	return IRQ_HANDLED;
     92}
     93
     94static struct i2c_algo_pca_data pca_isa_data = {
     95	/* .data intentionally left NULL, not needed with ISA */
     96	.write_byte		= pca_isa_writebyte,
     97	.read_byte		= pca_isa_readbyte,
     98	.wait_for_completion	= pca_isa_waitforcompletion,
     99	.reset_chip		= pca_isa_resetchip,
    100};
    101
    102static struct i2c_adapter pca_isa_ops = {
    103	.owner          = THIS_MODULE,
    104	.algo_data	= &pca_isa_data,
    105	.name		= "PCA9564/PCA9665 ISA Adapter",
    106	.timeout	= HZ,
    107};
    108
    109static int pca_isa_match(struct device *dev, unsigned int id)
    110{
    111	int match = base != 0;
    112
    113	if (match) {
    114		if (irq <= -1)
    115			dev_warn(dev, "Using polling mode (specify irq)\n");
    116	} else
    117		dev_err(dev, "Please specify I/O base\n");
    118
    119	return match;
    120}
    121
    122static int pca_isa_probe(struct device *dev, unsigned int id)
    123{
    124	init_waitqueue_head(&pca_wait);
    125
    126	dev_info(dev, "i/o base %#08lx. irq %d\n", base, irq);
    127
    128#ifdef CONFIG_PPC
    129	if (check_legacy_ioport(base)) {
    130		dev_err(dev, "I/O address %#08lx is not available\n", base);
    131		goto out;
    132	}
    133#endif
    134
    135	if (!request_region(base, IO_SIZE, "i2c-pca-isa")) {
    136		dev_err(dev, "I/O address %#08lx is in use\n", base);
    137		goto out;
    138	}
    139
    140	if (irq > -1) {
    141		if (request_irq(irq, pca_handler, 0, "i2c-pca-isa", &pca_isa_ops) < 0) {
    142			dev_err(dev, "Request irq%d failed\n", irq);
    143			goto out_region;
    144		}
    145	}
    146
    147	pca_isa_data.i2c_clock = clock;
    148	if (i2c_pca_add_bus(&pca_isa_ops) < 0) {
    149		dev_err(dev, "Failed to add i2c bus\n");
    150		goto out_irq;
    151	}
    152
    153	return 0;
    154
    155 out_irq:
    156	if (irq > -1)
    157		free_irq(irq, &pca_isa_ops);
    158 out_region:
    159	release_region(base, IO_SIZE);
    160 out:
    161	return -ENODEV;
    162}
    163
    164static void pca_isa_remove(struct device *dev, unsigned int id)
    165{
    166	i2c_del_adapter(&pca_isa_ops);
    167
    168	if (irq > -1) {
    169		disable_irq(irq);
    170		free_irq(irq, &pca_isa_ops);
    171	}
    172	release_region(base, IO_SIZE);
    173}
    174
    175static struct isa_driver pca_isa_driver = {
    176	.match		= pca_isa_match,
    177	.probe		= pca_isa_probe,
    178	.remove		= pca_isa_remove,
    179	.driver = {
    180		.owner	= THIS_MODULE,
    181		.name	= DRIVER,
    182	}
    183};
    184
    185MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
    186MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver");
    187MODULE_LICENSE("GPL");
    188
    189module_param_hw(base, ulong, ioport, 0);
    190MODULE_PARM_DESC(base, "I/O base address");
    191module_param_hw(irq, int, irq, 0);
    192MODULE_PARM_DESC(irq, "IRQ");
    193module_param(clock, int, 0);
    194MODULE_PARM_DESC(clock, "Clock rate in hertz.\n\t\t"
    195		"For PCA9564: 330000,288000,217000,146000,"
    196		"88000,59000,44000,36000\n"
    197		"\t\tFor PCA9665:\tStandard: 60300 - 100099\n"
    198		"\t\t\t\tFast: 100100 - 400099\n"
    199		"\t\t\t\tFast+: 400100 - 10000099\n"
    200		"\t\t\t\tTurbo: Up to 1265800");
    201module_isa_driver(pca_isa_driver, 1);