Hello,
i have small status update.
First most important information:
On AMD is trackpoint connected to ASF bus (similar to SMBus, but with
other control registers).
This bus has interrupt. After boot always generates interrupts for each
transaction. After playing it stops generating interrupts for
transaction, i don't know exactly why. More important is, that it can
generate interrupts when receives message from slave to broadcast
address (0x08).
To enable interrupts we should set:
ListenAddr to 0x08 and ListenAddrEn to 1:
outb_p((0x08 << 1) | 0x01, 0x09 + piix4_smba);
Then SlaveIntrListenEn of SlaveEn should be set to 1:
outb_p(inb_p(0x15 + piix4_smba) | 0x02, 0x15 + piix4_smba);
Now funny part, how to read address of slave device
(0x2c for synaptics):
Important register is ASFx13 DataBankSel register. If interrupt is
generated from slave device, then DataBankxFull is set. Address can be
retrieved using block read from 0x07 (DataIndex) register. Before
reading register SetReadHostDataBank should be 0. After setting
DataBankSel i am reading HostControl (dummy read), then reading form
DataIndex value 0x08 (broadcast address) and 0x2c (device, that wanted
attention). I am using following code, but it don't work good. Sometimes
there is bad value, sometimes it's reversed, first read gives 0x2c,
second 0x10. Don't know why. I have looked on decompiled windows driver
from synaptics and it looks, that there is special handling for
different DataBankSel values. I don't know exactly why, but problems are
rare, for now i am ignoring problems.
static u8 read_asf_data_bank(unsigned short piix4_smba, u8 bank_number)
{
outb_p(bank_number << 4, 0x13 + piix4_smba);
inb_p(0x02 + piix4_smba); // reset DataIndex
inb_p(0x07 + piix4_smba); // read SMBus broadcast address
return inb_p(0x07 + piix4_smba);
}
static irqreturn_t piix4_isr(int irq, void *dev_id)
{
//struct i2c_adapter *piix4_adapter = (struct i2c_adapter *)dev_id;
//struct i2c_piix4_adapdata *adapdata =
i2c_get_adapdata(piix4_adapter);
unsigned short piix4_smba = 0xb20;
u8 bank_sel;
u8 address[2] = {0x00, 0x00};
u8 *current_address;
current_address = &address[0];
bank_sel = inb_p(0x13 + piix4_smba); // DataBankSel
if ((bank_sel & 0x0c) == 0x00) { // bits DataBankxFull not set
return IRQ_HANDLED;
}
printk(KERN_INFO "Bank=%02x\n", bank_sel);
if ((bank_sel & 0x01) == 0) { // Last touched bank is 0
if (bank_sel & 0x08) {
*current_address = read_asf_data_bank(piix4_smba, 1);
current_address++;
}
if (bank_sel & 0x04) {
*current_address = read_asf_data_bank(piix4_smba, 0);
}
}
else { // Last touched bank is 1
if (bank_sel & 0x04) {
*current_address = read_asf_data_bank(piix4_smba, 0);
current_address++;
}
if (bank_sel & 0x08) {
*current_address = read_asf_data_bank(piix4_smba, 1);
}
}
outb_p(bank_sel & 0x0c, 0x13 + piix4_smba); // Clear DataBankxFull
printk(KERN_INFO "Address=%02x %02x\n", address[0] >> 1, address[1]
>> 1);
if (address[0] != 0x00) {
i2c_handle_smbus_host_notify(piix4_aux_adapter, address[0] >> 1);
}
if (address[1] != 0x00 && address[1] != address[0]) {
i2c_handle_smbus_host_notify(piix4_aux_adapter, address[1] >> 1);
}
return IRQ_HANDLED;
}
Now, when i load module i see this in log:
i2c i2c-11: Error: no response!
rmi4_f12 rmi4-00.fn12: Failed to read object data. Code: -6.
Bank=8d
Address=2c 2c
input input92: rmi_2d_sensor_abs_report: obj[0]: type: 0x01 X: 323 Y:
301 Z: 79 WX: 9 WY: 2
Bank=8d
Address=2c 2c
input input92: rmi_2d_sensor_abs_report: obj[0]: type: 0x01 X: 271 Y:
378 Z: 73 WX: 8 WY: 4
Bank=8d
Address=2c 2c
input input92: rmi_2d_sensor_abs_report: obj[0]: type: 0x01 X: 468 Y:
378 Z: 72 WX: 7 WY: 3
Bank=8d
Address=2c 2c
i2c i2c-11: Bus collision! SMBus may be locked until next hard reset.
(sorry!)
rmi4_f12 rmi4-00.fn12: Failed to read object data. Code: -5.
Bank=8d
Address=2c 2c
input input92: rmi_2d_sensor_abs_report: obj[0]: type: 0x01 X: 563 Y:
373 Z: 73 WX: 7 WY: 2
Bank=8d
Address=2c 2c
i2c i2c-11: Bus collision! SMBus may be locked until next hard reset.
(sorry!)
rmi4_f12 rmi4-00.fn12: Failed to read object data. Code: -5.
There are too many bus collisions a no responses. This is my
implementation of piix4_access_asf
static s32 piix4_access_asf(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
unsigned short piix4_smba = adapdata->smba;
int timeout = 0;
int retval;
u8 temp;
// Acquire IMC semaphore
outb_p(0x01, 0x14 + piix4_smba);
while ((++timeout < MAX_TIMEOUT) && (!((temp = inb_p(0x14 +
piix4_smba)) & 0x01))) {
usleep_range(250, 500);
}
if ((temp & 0x01) == 0) {
printk(KERN_INFO "lock not acquired\n");
return -EBUSY;
}
outb_p(0x80, 0x13 + piix4_smba); // Set DataBankSel to host bank
retval = piix4_access(adap, addr, flags, read_write, command, size,
data);
// Release semphore
outb_p(0x02, 0x14 + piix4_smba);
return retval;
}
Before transaction i am requesting HostSemaphore. Semaphore is correctly
acquired. Always. I don't know how exactly i should avoid bus conflicts.
Interrupts are sometimes generated with low frequency, sometimes with
high frequency. I don't know exactly why. Frequency changes after
restart of psmouse driver. Frequency changes with touch activity too, if
starts generating with high frequency and then i don't touch anything,
then frequency goes down. After touchpad activity frequency again goes
up.
Here is video with current state:
https://youtu.be/tf850B7UTWA