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 | |
28 | extern struct i2c_adapter *nforce2_smbus; |
29 | |
30 | static struct i2c_adapter *s4985_adapter; |
31 | static struct i2c_algorithm *s4985_algo; |
32 | |
33 | /* Wrapper access functions for multiplexed SMBus */ |
34 | static DEFINE_MUTEX(nforce2_lock); |
35 | |
36 | static 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(lock: &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. */ |
60 | static u8 last_channels; |
61 | |
62 | static 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 | |
90 | UNLOCK: |
91 | mutex_unlock(lock: &nforce2_lock); |
92 | return error; |
93 | } |
94 | |
95 | static 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, channels: 0x02); |
103 | } |
104 | |
105 | static 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, channels: 0x04); |
113 | } |
114 | |
115 | static 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, channels: 0x08); |
123 | } |
124 | |
125 | static 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, channels: 0x10); |
133 | } |
134 | |
135 | static 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(adapter: nforce2_smbus, addr: 0x18, flags: 0, I2C_SMBUS_WRITE, command: 0x03, |
146 | I2C_SMBUS_BYTE_DATA, 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(adap: 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(n: 5, size: sizeof(struct i2c_adapter), GFP_KERNEL); |
159 | if (!s4985_adapter) { |
160 | error = -ENOMEM; |
161 | goto ERROR1; |
162 | } |
163 | s4985_algo = kcalloc(n: 5, size: 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(buf: s4985_adapter[i].name, size: sizeof(s4985_adapter[i].name), |
179 | fmt: "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(adap: 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(adap: s4985_adapter + i); |
197 | goto ERROR3; |
198 | } |
199 | } |
200 | |
201 | return 0; |
202 | |
203 | ERROR3: |
204 | kfree(objp: s4985_algo); |
205 | s4985_algo = NULL; |
206 | ERROR2: |
207 | kfree(objp: s4985_adapter); |
208 | s4985_adapter = NULL; |
209 | ERROR1: |
210 | /* Restore physical bus */ |
211 | i2c_add_adapter(adap: nforce2_smbus); |
212 | ERROR0: |
213 | return error; |
214 | } |
215 | |
216 | static 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(adap: s4985_adapter+i); |
223 | kfree(objp: s4985_adapter); |
224 | s4985_adapter = NULL; |
225 | } |
226 | kfree(objp: s4985_algo); |
227 | s4985_algo = NULL; |
228 | |
229 | /* Restore physical bus */ |
230 | if (i2c_add_adapter(adap: nforce2_smbus)) |
231 | printk(KERN_ERR "i2c-nforce2-s4985: " |
232 | "Physical bus restoration failed\n" ); |
233 | } |
234 | |
235 | MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>" ); |
236 | MODULE_DESCRIPTION("S4985 SMBus multiplexing" ); |
237 | MODULE_LICENSE("GPL" ); |
238 | |
239 | module_init(nforce2_s4985_init); |
240 | module_exit(nforce2_s4985_exit); |
241 | |