PL050 PS/2 Controller
PL050 provides a keyboard or mouse interface(KMI) that is IBM PS/2 or AT-compatible and is licensed by ARM.
There are several registers to communicate with the controller, some of them are described in details further.
Address | Type | Description |
---|---|---|
Base + 0x00 | rw | Control register. |
Base + 0x04 | r | Status register. |
Base + 0x08 | rw | Received data (read)/ Data to be transmitted (write). |
Base + 0x0c | rw | Clock divisor register. |
Base + 0x10 | r | Interrupt status register. |
The table above could be represented as a C struct:
// PL050(KMI) bases under Integrator/CP compatible board.
#define KMI_KB_BASE 0x18000000 // keyboard
#define KMI_MS_BASE 0x19000000 // mouse
typedef struct _KMI_MMIO {
uint32 cr; // control register (rw)
uint32 stat; // status register (r)
uint32 data; // data register (rw)
uint32 clk; // clock divisor register (rw)
uint32 ir; // interrupt control register (r)
} KMI_MMIO;
// Note: set a valid pointer before initialising it.
KMI_MMIO volatile *mmio;
The above uses memory mapped input/output (MMIO), but other architectures may use I/O ports instead or a combination of I/O ports and MMIO for example the X84/64. So understanding your platform is very important to understand how to proceed in talking to the devices.
So first, configure the PL050 by enabling it. Let's take a look at control register. It contains five different bit fields:
Bits | Name | Description |
---|---|---|
0 | FKMIC | The force KMI clock LOW bit field is used to force the PrimeCell KMI clock pad LOW regardless of the state of the KMI FSM. |
1 | FKMID | The force KMI data LOW bit field is used to force the PrimeCell KMI data pad LOW regardless of the state of the KMI finite state machine (FSM). |
2 | KmiEn | The enable PrimeCell KMI bit field is used to enable the KMI. |
3 | KMITXINTREn | Enable transmitter interrupt. This bit field is used to enable the PrimeCell KMI transmitter interrupt. |
4 | KMIRXINTREn | Enable receiver interrupt. This bit field is used to enable the PrimeCell KMI receiver interrupt. |
5 | KMITYPE | 0 = PS2/AT mode (default), 1 = No line control bit mode. |
6-7 | - | Reserved. Read unpredictable. |
Setting the KmiEn will enable KMI. After that it's needed to setup a receiver interrupt, all it's needed is to set KMIRXINTREn and configure system's interrupt controller to call the callback which processes the input data.
void kmi_init(device_t* dev)
{
mmio->cr = (1 << 2) | (1 << 4);
}
The core function to implement is a one to send commands to PS/2 devices, like keyboard. For that the data register is used. To send a command, set a command id to data register and wait for an answer. The informations which helps to understand when is located in a status register. Data register does not contain any fields, just used to forward data, while it's interesting to take a detailed look at the status register:
Bits | Name | Description |
---|---|---|
0 | KMID | This bit reflects the status of the KMIDATAIN line after synchronizing. |
1 | KMIC | This bit reflects the status of the KMICLKIN line after synchronizing and sampling. |
2 | RXPARITY | This bit reflects the parity bit for the last received data byte (odd parity). |
3 | RXBUSY | This bit indicates that the PrimeCell KMI is currently receiving data. 0 = idle, 1 = receiving data. |
4 | RXFULL | This bit indicates that the receiver register is full and ready to be read. 0 = receive register empty, 1 = receive register full, ready to be read. |
5 | TXBUSY | This bit indicates that the PrimeCell KMI is currently sending data. 0 = idle, 1 = currently sending data. |
6 | TXEMPTY | This bit indicates that the transmit register is empty and ready to transmit. 0 = transmit register full, 1 = transmit register empty, ready to be written. |
7 | - | Reserved. Read unpredictable. |
void kmi_send(uint8 cmd)
{
kmi->data = cmd;
while ((registers->stat) & (1 << 5)); // wait while data is sending (TXBUSY)
ASSERT(registers->data == 0xfa);
}
These functions are base ones to communicate with PS/2 devices. Using this functions you can enable mouse by writing 0xF4, more information about commands could be found at PS/2 Keyboard and PS/2 Mouse
int kmi_setup_mouse()
{
kmi_send(0xF4);
}
Note that, due to the complexity of the Raspberry Pi's USB interface and Qemu's versatilepb being the go-to Pi emulator before raspi2 was developed, some older Pi programs will use the PL050 to easily enable debugging in Qemu. No Pi device so far has PS/2 compatibility hardware.