ths
authored
18 years ago
1
/*
2
* Arm PrimeCell PL050 Keyboard / Mouse Interface
3
*
4
* Copyright ( c ) 2006 - 2007 CodeSourcery .
5
6
7
8
9
* Written by Paul Brook
*
* This code is licenced under the GPL .
*/
10
11
12
# include "hw.h"
# include "primecell.h"
# include "ps2.h"
13
14
15
16
17
18
19
20
typedef struct {
void * dev ;
uint32_t base ;
uint32_t cr ;
uint32_t clk ;
uint32_t last ;
int pending ;
21
qemu_irq irq ;
22
23
24
int is_mouse ;
} pl050_state ;
25
26
27
28
29
30
31
32
# define PL050_TXEMPTY ( 1 << 6 )
# define PL050_TXBUSY ( 1 << 5 )
# define PL050_RXFULL ( 1 << 4 )
# define PL050_RXBUSY ( 1 << 3 )
# define PL050_RXPARITY ( 1 << 2 )
# define PL050_KMIC ( 1 << 1 )
# define PL050_KMID ( 1 << 0 )
33
34
35
36
37
38
39
40
41
42
43
static const unsigned char pl050_id [] =
{ 0x50 , 0x10 , 0x04 , 0x00 , 0x0d , 0xf0 , 0x05 , 0xb1 };
static void pl050_update ( void * opaque , int level )
{
pl050_state * s = ( pl050_state * ) opaque ;
int raise ;
s -> pending = level ;
raise = ( s -> pending && ( s -> cr & 0x10 ) != 0 )
|| ( s -> cr & 0x08 ) != 0 ;
44
qemu_set_irq ( s -> irq , raise );
45
46
47
48
49
50
51
52
53
54
55
56
57
}
static uint32_t pl050_read ( void * opaque , target_phys_addr_t offset )
{
pl050_state * s = ( pl050_state * ) opaque ;
offset -= s -> base ;
if ( offset >= 0xfe0 && offset < 0x1000 )
return pl050_id [( offset - 0xfe0 ) >> 2 ];
switch ( offset >> 2 ) {
case 0 : /* KMICR */
return s -> cr ;
case 1 : /* KMISTAT */
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
uint8_t val ;
uint32_t stat ;
val = s -> last ;
val = val ^ ( val >> 4 );
val = val ^ ( val >> 2 );
val = ( val ^ ( val >> 1 )) & 1 ;
stat = PL050_TXEMPTY ;
if ( val )
stat |= PL050_RXPARITY ;
if ( s -> pending )
stat |= PL050_RXFULL ;
return stat ;
74
75
76
77
78
79
80
81
82
83
}
case 2 : /* KMIDATA */
if ( s -> pending )
s -> last = ps2_read_data ( s -> dev );
return s -> last ;
case 3 : /* KMICLKDIV */
return s -> clk ;
case 4 : /* KMIIR */
return s -> pending | 2 ;
default :
84
cpu_abort ( cpu_single_env , "pl050_read: Bad offset %x \n " , ( int ) offset );
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
return 0 ;
}
}
static void pl050_write ( void * opaque , target_phys_addr_t offset ,
uint32_t value )
{
pl050_state * s = ( pl050_state * ) opaque ;
offset -= s -> base ;
switch ( offset >> 2 ) {
case 0 : /* KMICR */
s -> cr = value ;
pl050_update ( s , s -> pending );
/* ??? Need to implement the enable/disable bit. */
break ;
case 2 : /* KMIDATA */
/* ??? This should toggle the TX interrupt line. */
/* ??? This means kbd/mouse can block each other. */
if ( s -> is_mouse ) {
ps2_write_mouse ( s -> dev , value );
} else {
ps2_write_keyboard ( s -> dev , value );
}
break ;
case 3 : /* KMICLKDIV */
s -> clk = value ;
return ;
default :
113
cpu_abort ( cpu_single_env , "pl050_write: Bad offset %x \n " , ( int ) offset );
114
115
116
117
118
119
120
121
122
123
124
125
126
127
}
}
static CPUReadMemoryFunc * pl050_readfn [] = {
pl050_read ,
pl050_read ,
pl050_read
};
static CPUWriteMemoryFunc * pl050_writefn [] = {
pl050_write ,
pl050_write ,
pl050_write
};
128
void pl050_init ( uint32_t base , qemu_irq irq , int is_mouse )
129
130
131
132
133
134
135
{
int iomemtype ;
pl050_state * s ;
s = ( pl050_state * ) qemu_mallocz ( sizeof ( pl050_state ));
iomemtype = cpu_register_io_memory ( 0 , pl050_readfn ,
pl050_writefn , s );
136
cpu_register_physical_memory ( base , 0x00001000 , iomemtype );
137
138
139
140
141
142
143
144
145
146
s -> base = base ;
s -> irq = irq ;
s -> is_mouse = is_mouse ;
if ( is_mouse )
s -> dev = ps2_mouse_init ( pl050_update , s );
else
s -> dev = ps2_kbd_init ( pl050_update , s );
/* ??? Save/restore. */
}