PL050 PS/2 Controller

From OSDev Wiki
Jump to navigation Jump to search

This article is a stub! This page or section is a stub. You can help the wiki by accurately contributing to it.

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.

See Also

External Links