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
|
@page page_device_ata ATA / AHCI
# AHCI host abstraction (`ahci.h`)
This page documents **`struct rt_ahci_host`**, **`struct rt_ahci_ops`**, and per-port **`struct rt_ahci_port`** in **`drivers/ahci.h`**, as driven by **`rt_ahci_host_register` / `rt_ahci_host_unregister`** in `components/drivers/ata/ahci.c`.
## `struct rt_ahci_host` (caller fills before `rt_ahci_host_register`)
```368:383:components/drivers/include/drivers/ahci.h
struct rt_ahci_host
{
struct rt_scsi_host parent;
int irq;
void *regs;
rt_size_t ports_nr;
rt_uint32_t ports_map;
struct rt_ahci_port ports[32];
rt_uint32_t cap;
rt_uint32_t max_blocks;
const struct rt_ahci_ops *ops;
};
```
| Member | Owner | Meaning |
| --- | --- | --- |
| `parent` | **Caller** | Embedded **`rt_scsi_host`**; **`parent.dev` must be non-NULL** (used for `rt_dma_alloc_coherent`, etc.). |
| `irq` | **Caller** | Global host IRQ; the core installs the ISR with `struct rt_ahci_host *` as parameter. |
| `regs` | **Caller** | Virtual base of the HBA (AHCI 1.0) register block. |
| `ports_nr` | **Core** | Filled from **`CAP.NP`**; do not rely on the initial value. |
| `ports_map` | **Core (DT may override)** | Read from **`PI`**; if the node has **`ports-implemented`**, that property wins. |
| `ports[]` | **Core + ops** | See **Per-port state** below. |
| `cap` | **Core** | Read from **`RT_AHCI_HBA_CAP`** (masked); used with SG paths for **64-bit DMA** (**`RT_AHCI_CAP_64`**, etc.). |
| `max_blocks` | **Caller (may be 0)** | Max blocks per SCSI split; **0** is replaced with **`0x80`** at register time. |
| `ops` | **Caller** | **Required**; every callback is optional (**`NULL`** = core default), but failures in **`host_init` / `port_init` / `port_dma_init`** skip the port or log DMA errors. |
## `struct rt_ahci_port` (fields visible to `ops`)
```346:366:components/drivers/include/drivers/ahci.h
struct rt_ahci_port
{
void *regs;
void *dma;
rt_ubase_t dma_handle;
struct rt_ahci_cmd_hdr *cmd_slot;
struct rt_ahci_sg *cmd_tbl_sg;
void *cmd_tbl;
rt_ubase_t cmd_tbl_dma;
void *rx_fis;
rt_uint32_t int_enabled;
rt_size_t block_size;
rt_uint16_t *ataid;
rt_bool_t link;
struct rt_completion done;
};
```
| Member | Owner | Meaning |
| --- | --- | --- |
| `regs` | **Core** | `host->regs + 0x100 + port_index * 0x80`; valid before **`port_init`**. |
| `dma`, `dma_handle`, `cmd_slot`, `rx_fis`, `cmd_tbl`, `cmd_tbl_dma`, `cmd_tbl_sg` | **Core** | After **`link == RT_TRUE`**, **`RT_AHCI_DMA_SIZE`** coherent memory is allocated and laid out; **`CLB`/`CLBU`**, **`FB`/`FBU`** programmed. |
| `int_enabled` | **Core** | **`RT_AHCI_PORT_INTE_*`** mask written to **`PORT_INTE`** after DMA engines start. |
| `block_size` | **Core** | **512** or **2048** from Identify / Inquiry and **SIG** / **ATAPI**. |
| `ataid` | **Core** | Identify buffer; used with **`rt_ahci_ata_id_*`** helpers. |
| `link` | **Core** | **`RT_TRUE`** when link is up; ISR calls **`port_isr`** and **`rt_completion_done`** only if **`link`**. |
| `done` | **Core** | **`rt_completion_init`**; command path **`rt_completion_wait(&port->done, …)`** after **`PORT_CI`**; **`port_isr`** must stay consistent with **`done`**. |
In **`port_init` / `port_dma_init`** you may touch **`port->regs`** and other HW state not yet filled by the core. **Do not assume `dma` exists inside `port_init`** (DMA is allocated only after a successful link).
## `struct rt_ahci_ops` (order inside `rt_ahci_host_register`)
```385:392:components/drivers/include/drivers/ahci.h
struct rt_ahci_ops
{
rt_err_t (*host_init)(struct rt_ahci_host *host);
rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr);
};
```
| Callback | When | Role |
| --- | --- | --- |
| **`host_init`** | After HBA soft reset, **`GHC.AHCI_EN`**, **CAP** read/mask, **`PI` write**, before port enumeration. | Optional. HBA-level, clocks, PHY, vendor regs; non-**`RT_EOK`** fails **`rt_ahci_host_register`**. |
| **`port_init`** | After **`PORT_CMD`** has **LIST_ON/FIS_ON/START/FIS_RX** cleared and the port has quiesced, before **SPIN_UP**. | Optional. Port reset, **`SCTL`**, non-standard **SIG**; **`port->regs`** valid. Failure **`continue`**s the port. |
| **`port_link_up`** | After **`port_init`** and the core has set **SPIN_UP**. | Optional. If **`NULL`**, core polls **`SSTS.DET`** for **`PHYRDY`**. Must return **`RT_EOK`** when the link is usable. |
| **`port_dma_init`** | After **`CLB`/`FB`** and DMA buffers are programmed, before the core writes **`PORT_CMD`** (ACTIVE, FIS_RX, POWER_ON, SPIN_UP, START). | Optional. Extra SoC DMA/port setup; errors are logged but the core still asserts **`PORT_CMD`**. |
| **`port_isr`** | Global ISR: bit set in **`HBA_INTS`**, **`port->link`**, **`PORT_INTS`** read into **`isr`**. | Optional. SoC-specific IRQ handling; core always **`rt_completion_done(&port->done)`** and clears **`PORT_INTS`**. |
### Common integration pitfalls
- **`port_init` vs DMA**: **`port->dma` is not allocated yet** inside **`port_init`**—only touch **`port->regs`** and PHY-level setup there.
- **`port_dma_init` errors are logged only**: the core still enables **`PORT_CMD`**—if your SoC cannot run without extra setup, fail earlier in **`port_link_up`** instead.
- **`port_isr`**: if you override, ensure you still cooperate with **`rt_completion_done`** semantics expected by **`ahci.c`** command path, or commands hang.
- **64-bit DMA**: honor **`RT_AHCI_CAP_64`** when building PRDT—SG list addresses must match HBA capability.
## Registration API
| API | Role |
| --- | --- |
| **`rt_ahci_host_register(host)`** | Validates **`host`**, **`host->parent.dev`**, **`host->ops`**; HBA reset, ports, link, DMA, **`rt_scsi_host_register`**, IRQ. |
| **`rt_ahci_host_unregister(host)`** | Masks IRQ, frees per-port DMA, unregisters the SCSI host. |
## See also
- `components/drivers/include/drivers/ahci.h` (**`RT_AHCI_*`**, **`struct rt_ahci_cmd_hdr`**, **`struct rt_ahci_sg`**)
- `components/drivers/ata/ahci.c`
**Same documentation pattern** (fill structs + `ops` + register order): `documentation/6.components/device-driver/ufs/ufs.md`, `nvme/nvme.md`, `scsi/scsi.md` (section 1), `block/disk.md`, `dma/dma.md` (section 1).
|