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-nforce2-s4985.c (6698B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard
      4 *
      5 * Copyright (C) 2008 Jean Delvare <jdelvare@suse.de>
      6 */
      7
      8/*
      9 * We select the channels by sending commands to the Philips
     10 * PCA9556 chip at I2C address 0x18. The main adapter is used for
     11 * the non-multiplexed part of the bus, and 4 virtual adapters
     12 * are defined for the multiplexed addresses: 0x50-0x53 (memory
     13 * module EEPROM) located on channels 1-4. We define one virtual
     14 * adapter per CPU, which corresponds to one multiplexed channel:
     15 *   CPU0: virtual adapter 1, channel 1
     16 *   CPU1: virtual adapter 2, channel 2
     17 *   CPU2: virtual adapter 3, channel 3
     18 *   CPU3: virtual adapter 4, channel 4
     19 */
     20
     21#include <linux/module.h>
     22#include <linux/kernel.h>
     23#include <linux/slab.h>
     24#include <linux/init.h>
     25#include <linux/i2c.h>
     26#include <linux/mutex.h>
     27
     28extern struct i2c_adapter *nforce2_smbus;
     29
     30static struct i2c_adapter *s4985_adapter;
     31static struct i2c_algorithm *s4985_algo;
     32
     33/* Wrapper access functions for multiplexed SMBus */
     34static DEFINE_MUTEX(nforce2_lock);
     35
     36static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,
     37				unsigned short flags, char read_write,
     38				u8 command, int size,
     39				union i2c_smbus_data *data)
     40{
     41	int error;
     42
     43	/* We exclude the multiplexed addresses */
     44	if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
     45	 || addr == 0x18)
     46		return -ENXIO;
     47
     48	mutex_lock(&nforce2_lock);
     49	error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
     50						command, size, data);
     51	mutex_unlock(&nforce2_lock);
     52
     53	return error;
     54}
     55
     56/* We remember the last used channels combination so as to only switch
     57   channels when it is really needed. This greatly reduces the SMBus
     58   overhead, but also assumes that nobody will be writing to the PCA9556
     59   in our back. */
     60static u8 last_channels;
     61
     62static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr,
     63					 unsigned short flags, char read_write,
     64					 u8 command, int size,
     65					 union i2c_smbus_data *data,
     66					 u8 channels)
     67{
     68	int error;
     69
     70	/* We exclude the non-multiplexed addresses */
     71	if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
     72		return -ENXIO;
     73
     74	mutex_lock(&nforce2_lock);
     75	if (last_channels != channels) {
     76		union i2c_smbus_data mplxdata;
     77		mplxdata.byte = channels;
     78
     79		error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,
     80							I2C_SMBUS_WRITE, 0x01,
     81							I2C_SMBUS_BYTE_DATA,
     82							&mplxdata);
     83		if (error)
     84			goto UNLOCK;
     85		last_channels = channels;
     86	}
     87	error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
     88						command, size, data);
     89
     90UNLOCK:
     91	mutex_unlock(&nforce2_lock);
     92	return error;
     93}
     94
     95static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,
     96				unsigned short flags, char read_write,
     97				u8 command, int size,
     98				union i2c_smbus_data *data)
     99{
    100	/* CPU0: channel 1 enabled */
    101	return nforce2_access_channel(adap, addr, flags, read_write, command,
    102				      size, data, 0x02);
    103}
    104
    105static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,
    106				unsigned short flags, char read_write,
    107				u8 command, int size,
    108				union i2c_smbus_data *data)
    109{
    110	/* CPU1: channel 2 enabled */
    111	return nforce2_access_channel(adap, addr, flags, read_write, command,
    112				      size, data, 0x04);
    113}
    114
    115static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,
    116				unsigned short flags, char read_write,
    117				u8 command, int size,
    118				union i2c_smbus_data *data)
    119{
    120	/* CPU2: channel 3 enabled */
    121	return nforce2_access_channel(adap, addr, flags, read_write, command,
    122				      size, data, 0x08);
    123}
    124
    125static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,
    126				unsigned short flags, char read_write,
    127				u8 command, int size,
    128				union i2c_smbus_data *data)
    129{
    130	/* CPU3: channel 4 enabled */
    131	return nforce2_access_channel(adap, addr, flags, read_write, command,
    132				      size, data, 0x10);
    133}
    134
    135static int __init nforce2_s4985_init(void)
    136{
    137	int i, error;
    138	union i2c_smbus_data ioconfig;
    139
    140	if (!nforce2_smbus)
    141		return -ENODEV;
    142
    143	/* Configure the PCA9556 multiplexer */
    144	ioconfig.byte = 0x00; /* All I/O to output mode */
    145	error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
    146			       I2C_SMBUS_BYTE_DATA, &ioconfig);
    147	if (error) {
    148		dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");
    149		error = -EIO;
    150		goto ERROR0;
    151	}
    152
    153	/* Unregister physical bus */
    154	i2c_del_adapter(nforce2_smbus);
    155
    156	printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");
    157	/* Define the 5 virtual adapters and algorithms structures */
    158	s4985_adapter = kcalloc(5, sizeof(struct i2c_adapter), GFP_KERNEL);
    159	if (!s4985_adapter) {
    160		error = -ENOMEM;
    161		goto ERROR1;
    162	}
    163	s4985_algo = kcalloc(5, sizeof(struct i2c_algorithm), GFP_KERNEL);
    164	if (!s4985_algo) {
    165		error = -ENOMEM;
    166		goto ERROR2;
    167	}
    168
    169	/* Fill in the new structures */
    170	s4985_algo[0] = *(nforce2_smbus->algo);
    171	s4985_algo[0].smbus_xfer = nforce2_access_virt0;
    172	s4985_adapter[0] = *nforce2_smbus;
    173	s4985_adapter[0].algo = s4985_algo;
    174	s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;
    175	for (i = 1; i < 5; i++) {
    176		s4985_algo[i] = *(nforce2_smbus->algo);
    177		s4985_adapter[i] = *nforce2_smbus;
    178		snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),
    179			 "SMBus nForce2 adapter (CPU%d)", i - 1);
    180		s4985_adapter[i].algo = s4985_algo + i;
    181		s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;
    182	}
    183	s4985_algo[1].smbus_xfer = nforce2_access_virt1;
    184	s4985_algo[2].smbus_xfer = nforce2_access_virt2;
    185	s4985_algo[3].smbus_xfer = nforce2_access_virt3;
    186	s4985_algo[4].smbus_xfer = nforce2_access_virt4;
    187
    188	/* Register virtual adapters */
    189	for (i = 0; i < 5; i++) {
    190		error = i2c_add_adapter(s4985_adapter + i);
    191		if (error) {
    192			printk(KERN_ERR "i2c-nforce2-s4985: "
    193			       "Virtual adapter %d registration "
    194			       "failed, module not inserted\n", i);
    195			for (i--; i >= 0; i--)
    196				i2c_del_adapter(s4985_adapter + i);
    197			goto ERROR3;
    198		}
    199	}
    200
    201	return 0;
    202
    203ERROR3:
    204	kfree(s4985_algo);
    205	s4985_algo = NULL;
    206ERROR2:
    207	kfree(s4985_adapter);
    208	s4985_adapter = NULL;
    209ERROR1:
    210	/* Restore physical bus */
    211	i2c_add_adapter(nforce2_smbus);
    212ERROR0:
    213	return error;
    214}
    215
    216static void __exit nforce2_s4985_exit(void)
    217{
    218	if (s4985_adapter) {
    219		int i;
    220
    221		for (i = 0; i < 5; i++)
    222			i2c_del_adapter(s4985_adapter+i);
    223		kfree(s4985_adapter);
    224		s4985_adapter = NULL;
    225	}
    226	kfree(s4985_algo);
    227	s4985_algo = NULL;
    228
    229	/* Restore physical bus */
    230	if (i2c_add_adapter(nforce2_smbus))
    231		printk(KERN_ERR "i2c-nforce2-s4985: "
    232		       "Physical bus restoration failed\n");
    233}
    234
    235MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
    236MODULE_DESCRIPTION("S4985 SMBus multiplexing");
    237MODULE_LICENSE("GPL");
    238
    239module_init(nforce2_s4985_init);
    240module_exit(nforce2_s4985_exit);