brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 69a71bc Raw
241 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard4 *5 * Copyright (C) 2008 Jean Delvare <jdelvare@suse.de>6 */7 8/*9 * We select the channels by sending commands to the Philips10 * PCA9556 chip at I2C address 0x18. The main adapter is used for11 * the non-multiplexed part of the bus, and 4 virtual adapters12 * are defined for the multiplexed addresses: 0x50-0x53 (memory13 * module EEPROM) located on channels 1-4. We define one virtual14 * adapter per CPU, which corresponds to one multiplexed channel:15 *   CPU0: virtual adapter 1, channel 116 *   CPU1: virtual adapter 2, channel 217 *   CPU2: virtual adapter 3, channel 318 *   CPU3: virtual adapter 4, channel 419 */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) == 0x3045	 || 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 switch57   channels when it is really needed. This greatly reduces the SMBus58   overhead, but also assumes that nobody will be writing to the PCA955659   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);241