summaryrefslogtreecommitdiffstats
path: root/doc/driver-model/UDM-design.txt
blob: 185f477b06362be3f4224787b61c1249a440f088 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
The U-Boot Driver Model Project
===============================
Design document
===============
Marek Vasut <marek.vasut@gmail.com>
Pavel Herrmann <morpheus.ibis@gmail.com>
2012-05-17

I) The modular concept
----------------------

The driver core design is done with modularity in mind. The long-term plan is to
extend this modularity to allow loading not only drivers, but various other
objects into U-Boot at runtime -- like commands, support for other boards etc.

II) Driver core initialization stages
-------------------------------------

The drivers have to be initialized in two stages, since the U-Boot bootloader
runs in two stages itself. The first stage is the one which is executed before
the bootloader itself is relocated. The second stage then happens after
relocation.

  1) First stage
  --------------

  The first stage runs after the bootloader did very basic hardware init. This
  means the stack pointer was configured, caches disabled and that's about it.
  The problem with this part is the memory management isn't running at all. To
  make things even worse, at this point, the RAM is still likely uninitialized
  and therefore unavailable.

  2) Second stage
  ---------------

  At this stage, the bootloader has initialized RAM and is running from it's
  final location. Dynamic memory allocations are working at this point. Most of
  the driver initialization is executed here.

III) The drivers
----------------

  1) The structure of a driver
  ----------------------------

  The driver will contain a structure located in a separate section, which
  will allow linker to create a list of compiled-in drivers at compile time.
  Let's call this list "driver_list".

  struct driver __attribute__((section(driver_list))) {
    /* The name of the driver */
    char		name[STATIC_CONFIG_DRIVER_NAME_LENGTH];

    /*
     * This function should connect this driver with cores it depends on and
     * with other drivers, likely bus drivers
     */
    int			(*bind)(struct instance *i);

    /* This function actually initializes the hardware. */
    int			(*probe)(struct instance *i);

    /*
     * The function of the driver called when U-Boot finished relocation.
     * This is particularly important to eg. move pointers to DMA buffers
     * and such from the location before relocation to their final location.
     */
    int			(*reloc)(struct instance *i);

    /*
     * This is called when the driver is shuting down, to deinitialize the
     * hardware.
     */
    int			(*remove)(struct instance *i);

    /* This is called to remove the driver from the driver tree */
    int			(*unbind)(struct instance *i);

    /* This is a list of cores this driver depends on */
    struct driver	*cores[];
  };

  The cores[] array in here is very important. It allows u-boot to figure out,
  in compile-time, which possible cores can be activated at runtime. Therefore
  if there are cores that won't be ever activated, GCC LTO might remove them
  from the final binary. Actually, this information might be used to drive build
  of the cores.

  FIXME: Should *cores[] be really struct driver, pointing to drivers that
         represent the cores? Shouldn't it be core instance pointer?

  2) Instantiation of a driver
  ----------------------------

  The driver is instantiated by calling:

    driver_bind(struct instance *bus, const struct driver_info *di)

  The "struct instance *bus" is a pointer to a bus with which this driver should
  be registered with. The "root" bus pointer is supplied to the board init
  functions.

  FIXME: We need some functions that will return list of busses of certain type
         registered with the system so the user can find proper instance even if
	 he has no bus pointer (this will come handy if the user isn't
	 registering the driver from board init function, but somewhere else).

  The "const struct driver_info *di" pointer points to a structure defining the
  driver to be registered. The structure is defined as follows:

  struct driver_info {
	char			name[STATIC_CONFIG_DRIVER_NAME_LENGTH];
	void			*platform_data;
  }

  The instantiation of a driver by calling driver_bind() creates an instance
  of the driver by allocating "struct driver_instance". Note that only struct
  instance is passed to the driver. The wrapping struct driver_instance is there
  for purposes of the driver core:

  struct driver_instance {
    uint32_t          flags;
    struct instance   i;
  };

  struct instance {
	/* Pointer to a driver information passed by driver_register() */
	const struct driver_info	*info;
	/* Pointer to a bus this driver is bound with */
	struct instance			*bus;
	/* Pointer to this driver's own private data */
	void				*private_data;
	/* Pointer to the first block of successor nodes (optional) */
	struct successor_block 		*succ;
  }

  The instantiation of a driver does not mean the hardware is initialized. The
  driver_bind() call only creates the instance of the driver, fills in the "bus"
  pointer and calls the drivers' .bind() function. The .bind() function of the
  driver should hook the driver with the remaining cores and/or drivers it
  depends on.

  It's important to note here, that in case the driver instance has multiple
  parents, such parent can be connected with this instance by calling:

    driver_link(struct instance *parent, struct instance *dev);

  This will connect the other parent driver with the newly instantiated driver.
  Note that this must be called after driver_bind() and before driver_acticate()
  (driver_activate() will be explained below). To allow struct instance to have
  multiple parent pointer, the struct instance *bus will utilize it's last bit
  to indicate if this is a pointer to struct instance or to an array if
  instances, struct successor block. The approach is similar as the approach to
  *succ in struct instance, described in the following paragraph.

  The last pointer of the struct instance, the pointer to successor nodes, is
  used only in case of a bus driver. Otherwise the pointer contains NULL value.
  The last bit of this field indicates if this is a bus having a single child
  node (so the last bit is 0) or if this bus has multiple child nodes (the last
  bit is 1). In the former case, the driver core should clear the last bit and
  this pointer points directly to the child node. In the later case of a bus
  driver, the pointer points to an instance of structure:

  struct successor_block {
    /* Array of pointers to instances of devices attached to this bus */
    struct instance                     *dev[BLOCKING_FACTOR];
    /* Pointer to next block of successors */
    struct successor_block              *next;
  }

  Some of the *dev[] array members might be NULL in case there are no more
  devices attached. The *next is NULL in case the list of attached devices
  doesn't continue anymore. The BLOCKING_FACTOR is used to allocate multiple
  slots for successor devices at once to avoid fragmentation of memory.

  3) The bind() function of a driver
  ----------------------------------

  The bind function of a driver connects the driver with various cores the
  driver provides functions for. The driver model related part will look like
  the following example for a bus driver:

  int driver_bind(struct instance *in)
  {
	...
        core_bind(&core_i2c_static_instance, in, i2c_bus_funcs);
        ...
  }

  FIXME: What if we need to run-time determine, depending on some hardware
         register, what kind of i2c_bus_funcs to pass?

  This makes the i2c core aware of a new bus. The i2c_bus_funcs is a constant
  structure of functions any i2c bus driver must provide to work. This will
  allow the i2c command operate with the bus. The core_i2c_static_instance is
  the pointer to the instance of a core this driver provides function to.

  FIXME: Maybe replace "core-i2c" with CORE_I2C global pointer to an instance of
         the core?

  4) The instantiation of a core driver
  -------------------------------------

  The core driver is special in the way that it's single-instance driver. It is
  always present in the system, though it might not be activated. The fact that
  it's single instance allows it to be instantiated at compile time.

  Therefore, all possible structures of this driver can be in read-only memory,
  especially struct driver and struct driver_instance. But the successor list,
  which needs special treatment.

  To solve the problem with a successor list and the core driver flags, a new
  entry in struct gd (global data) will be introduced. This entry will point to
  runtime allocated array of struct driver_instance. It will be possible to
  allocate the exact amount of struct driver_instance necessary, as the number
  of cores that might be activated will be known at compile time. The cores will
  then behave like any usual driver.

  Pointers to the struct instance of cores can be computed at compile time,
  therefore allowing the resulting u-boot binary to save some overhead.

  5) The probe() function of a driver
  -----------------------------------

  The probe function of a driver allocates necessary resources and does required
  initialization of the hardware itself. This is usually called only when the
  driver is needed, as a part of the defered probe mechanism.

  The driver core should implement a function called

    int driver_activate(struct instance *in);

  which should call the .probe() function of the driver and then configure the
  state of the driver instance to "ACTIVATED". This state of a driver instance
  should be stored in a wrap-around structure for the structure instance, the
  struct driver_instance.

  6) The command side interface to a driver
  -----------------------------------------

  The U-Boot command shall communicate only with the specific driver core. The
  driver core in turn exports necessary API towards the command.

  7) Demonstration imaginary board
  --------------------------------

  Consider the following computer:

  *
  |
  +-- System power management logic
  |
  +-- CPU clock controlling logc
  |
  +-- NAND controller
  |   |
  |   +-- NAND flash chip
  |
  +-- 128MB of DDR DRAM
  |
  +-- I2C bus #0
  |   |
  |   +-- RTC
  |   |
  |   +-- EEPROM #0
  |   |
  |   +-- EEPROM #1
  |
  +-- USB host-only IP core
  |   |
  |   +-- USB storage device
  |
  +-- USB OTG-capable IP core
  |   |
  |   +-- connection to the host PC
  |
  +-- GPIO
  |   |
  |   +-- User LED #0
  |   |
  |   +-- User LED #1
  |
  +-- UART0
  |
  +-- UART1
  |
  +-- Ethernet controller #0
  |
  +-- Ethernet controller #1
  |
  +-- Audio codec
  |
  +-- PCI bridge
  |   |
  |   +-- Ethernet controller #2
  |   |
  |   +-- SPI host card
  |   |   |
  |   |   +-- Audio amplifier (must be operational before codec)
  |   |
  |   +-- GPIO host card
  |       |
  |       +-- User LED #2
  |
  +-- LCD controller
  |
  +-- PWM controller (must be enabled after LCD controller)
  |
  +-- SPI host controller
  |   |
  |   +-- SD/MMC connected via SPI
  |   |
  |   +-- SPI flash
  |
  +-- CPLD/FPGA with stored configuration of the board
OpenPOWER on IntegriCloud