Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/allwinner/a10_ahci.c
39507 views
1
/*-
2
* Copyright (c) 2015 Luiz Otavio O Souza <[email protected]> All rights reserved.
3
* Copyright (c) 2014-2015 M. Warner Losh <[email protected]>
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*
26
* The magic-bit-bang sequence used in this code may be based on a linux
27
* platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd.
28
* www.allwinnertech.com, by Daniel Wang <[email protected]>
29
* though none of the original code was copied.
30
*/
31
32
#include "opt_bus.h"
33
34
#include <sys/param.h>
35
#include <sys/systm.h>
36
#include <sys/bus.h>
37
#include <sys/rman.h>
38
#include <sys/kernel.h>
39
#include <sys/module.h>
40
41
#include <machine/bus.h>
42
#include <dev/ofw/ofw_bus.h>
43
#include <dev/ofw/ofw_bus_subr.h>
44
45
#include <dev/ahci/ahci.h>
46
#include <dev/clk/clk.h>
47
#include <dev/regulator/regulator.h>
48
49
/*
50
* Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register
51
* set with a few extra implementation-specific registers that need to
52
* be accounted for. There's only one PHY in the system, and it needs
53
* to be trained to bring the link up. In addition, there's some DMA
54
* specific things that need to be done as well. These things are also
55
* just about completely undocumented, except in ugly code in the Linux
56
* SDK Allwinner releases.
57
*/
58
59
/* BITx -- Unknown bit that needs to be set/cleared at position x */
60
/* UFx -- Uknown multi-bit field frobbed during init */
61
#define AHCI_BISTAFR 0x00A0
62
#define AHCI_BISTCR 0x00A4
63
#define AHCI_BISTFCTR 0x00A8
64
#define AHCI_BISTSR 0x00AC
65
#define AHCI_BISTDECR 0x00B0
66
#define AHCI_DIAGNR 0x00B4
67
#define AHCI_DIAGNR1 0x00B8
68
#define AHCI_OOBR 0x00BC
69
#define AHCI_PHYCS0R 0x00C0
70
/* Bits 0..17 are a mystery */
71
#define PHYCS0R_BIT18 (1 << 18)
72
#define PHYCS0R_POWER_ENABLE (1 << 19)
73
#define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */
74
#define PHYCS0R_UF1_INIT (3 << 20)
75
#define PHYCS0R_BIT23 (1 << 23)
76
#define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */
77
#define PHYCS0R_UF2_INIT (5 << 24)
78
/* Bit 27 mystery */
79
#define PHYCS0R_POWER_STATUS_MASK (7 << 28)
80
#define PHYCS0R_PS_GOOD (2 << 28)
81
/* Bit 31 mystery */
82
#define AHCI_PHYCS1R 0x00C4
83
/* Bits 0..5 are a mystery */
84
#define PHYCS1R_UF1_MASK (3 << 6)
85
#define PHYCS1R_UF1_INIT (2 << 6)
86
#define PHYCS1R_UF2_MASK (0x1f << 8)
87
#define PHYCS1R_UF2_INIT (6 << 8)
88
/* Bits 13..14 are a mystery */
89
#define PHYCS1R_BIT15 (1 << 15)
90
#define PHYCS1R_UF3_MASK (3 << 16)
91
#define PHYCS1R_UF3_INIT (2 << 16)
92
/* Bit 18 mystery */
93
#define PHYCS1R_HIGHZ (1 << 19)
94
/* Bits 20..27 mystery */
95
#define PHYCS1R_BIT28 (1 << 28)
96
/* Bits 29..31 mystery */
97
#define AHCI_PHYCS2R 0x00C8
98
/* bits 0..4 mystery */
99
#define PHYCS2R_UF1_MASK (0x1f << 5)
100
#define PHYCS2R_UF1_INIT (0x19 << 5)
101
/* Bits 10..23 mystery */
102
#define PHYCS2R_CALIBRATE (1 << 24)
103
/* Bits 25..31 mystery */
104
#define AHCI_TIMER1MS 0x00E0
105
#define AHCI_GPARAM1R 0x00E8
106
#define AHCI_GPARAM2R 0x00EC
107
#define AHCI_PPARAMR 0x00F0
108
#define AHCI_TESTR 0x00F4
109
#define AHCI_VERSIONR 0x00F8
110
#define AHCI_IDR 0x00FC
111
#define AHCI_RWCR 0x00FC
112
113
#define AHCI_P0DMACR 0x0070
114
#define AHCI_P0PHYCR 0x0078
115
#define AHCI_P0PHYSR 0x007C
116
117
#define PLL_FREQ 100000000
118
119
struct ahci_a10_softc {
120
struct ahci_controller ahci_ctlr;
121
regulator_t ahci_reg;
122
clk_t clk_pll;
123
clk_t clk_gate;
124
};
125
126
static void inline
127
ahci_set(struct resource *m, bus_size_t off, uint32_t set)
128
{
129
uint32_t val = ATA_INL(m, off);
130
131
val |= set;
132
ATA_OUTL(m, off, val);
133
}
134
135
static void inline
136
ahci_clr(struct resource *m, bus_size_t off, uint32_t clr)
137
{
138
uint32_t val = ATA_INL(m, off);
139
140
val &= ~clr;
141
ATA_OUTL(m, off, val);
142
}
143
144
static void inline
145
ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set)
146
{
147
uint32_t val = ATA_INL(m, off);
148
149
val &= mask;
150
val |= set;
151
ATA_OUTL(m, off, val);
152
}
153
154
/*
155
* Should this be phy_reset or phy_init
156
*/
157
#define PHY_RESET_TIMEOUT 1000
158
static void
159
ahci_a10_phy_reset(device_t dev)
160
{
161
uint32_t to, val;
162
struct ahci_controller *ctlr = device_get_softc(dev);
163
164
/*
165
* Here starts the magic -- most of the comments are based
166
* on guesswork, names of routines and printf error
167
* messages. The code works, but it will do that even if the
168
* comments are 100% BS.
169
*/
170
171
/*
172
* Lock out other access while we initialize. Or at least that
173
* seems to be the case based on Linux SDK #defines. Maybe this
174
* put things into reset?
175
*/
176
ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0);
177
DELAY(100);
178
179
/*
180
* Set bit 19 in PHYCS1R. Guessing this disables driving the PHY
181
* port for a bit while we reset things.
182
*/
183
ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
184
185
/*
186
* Frob PHYCS0R...
187
*/
188
ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
189
~PHYCS0R_UF2_MASK,
190
PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18);
191
192
/*
193
* Set three fields in PHYCS1R
194
*/
195
ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R,
196
~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK),
197
PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT);
198
199
/*
200
* Two more mystery bits in PHYCS1R. -- can these be combined above?
201
*/
202
ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28);
203
204
/*
205
* Now clear that first mysery bit. Perhaps this starts
206
* driving the PHY again so we can power it up and start
207
* talking to the SATA drive, if any below.
208
*/
209
ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
210
211
/*
212
* Frob PHYCS0R again...
213
*/
214
ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
215
~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT);
216
217
/*
218
* Frob PHYCS2R, because 25 means something?
219
*/
220
ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK,
221
PHYCS2R_UF1_INIT);
222
223
DELAY(100); /* WAG */
224
225
/*
226
* Turn on the power to the PHY and wait for it to report back
227
* good?
228
*/
229
ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE);
230
for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
231
val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R);
232
if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD)
233
break;
234
DELAY(10);
235
}
236
if (to == 0 && bootverbose)
237
device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val);
238
239
/*
240
* Calibrate the clocks between the device and the host. This appears
241
* to be an automated process that clears the bit when it is done.
242
*/
243
ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE);
244
for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
245
val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R);
246
if ((val & PHYCS2R_CALIBRATE) == 0)
247
break;
248
DELAY(10);
249
}
250
if (to == 0 && bootverbose)
251
device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val);
252
253
/*
254
* OK, let things settle down a bit.
255
*/
256
DELAY(1000);
257
258
/*
259
* Go back into normal mode now that we've calibrated the PHY.
260
*/
261
ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7);
262
}
263
264
static void
265
ahci_a10_ch_start(struct ahci_channel *ch)
266
{
267
uint32_t reg;
268
269
/*
270
* Magical values from Allwinner SDK, setup the DMA before start
271
* operations on this channel.
272
*/
273
reg = ATA_INL(ch->r_mem, AHCI_P0DMACR);
274
reg &= ~0xff00;
275
reg |= 0x4400;
276
ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg);
277
}
278
279
static int
280
ahci_a10_ctlr_reset(device_t dev)
281
{
282
283
ahci_a10_phy_reset(dev);
284
285
return (ahci_ctlr_reset(dev));
286
}
287
288
static int
289
ahci_a10_probe(device_t dev)
290
{
291
292
if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci"))
293
return (ENXIO);
294
device_set_desc(dev, "Allwinner Integrated AHCI controller");
295
296
return (BUS_PROBE_DEFAULT);
297
}
298
299
static int
300
ahci_a10_attach(device_t dev)
301
{
302
int error;
303
struct ahci_a10_softc *sc;
304
struct ahci_controller *ctlr;
305
306
sc = device_get_softc(dev);
307
ctlr = &sc->ahci_ctlr;
308
309
ctlr->quirks = AHCI_Q_NOPMP;
310
ctlr->vendorid = 0;
311
ctlr->deviceid = 0;
312
ctlr->subvendorid = 0;
313
ctlr->subdeviceid = 0;
314
ctlr->r_rid = 0;
315
if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
316
&ctlr->r_rid, RF_ACTIVE)))
317
return (ENXIO);
318
319
/* Enable the (optional) regulator */
320
if (regulator_get_by_ofw_property(dev, 0, "target-supply",
321
&sc->ahci_reg) == 0) {
322
error = regulator_enable(sc->ahci_reg);
323
if (error != 0) {
324
device_printf(dev, "Could not enable regulator\n");
325
goto fail;
326
}
327
}
328
329
/* Enable clocks */
330
error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_gate);
331
if (error != 0) {
332
device_printf(dev, "Cannot get gate clock\n");
333
goto fail;
334
}
335
error = clk_get_by_ofw_index(dev, 0, 1, &sc->clk_pll);
336
if (error != 0) {
337
device_printf(dev, "Cannot get PLL clock\n");
338
goto fail;
339
}
340
error = clk_set_freq(sc->clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN);
341
if (error != 0) {
342
device_printf(dev, "Cannot set PLL frequency\n");
343
goto fail;
344
}
345
error = clk_enable(sc->clk_pll);
346
if (error != 0) {
347
device_printf(dev, "Cannot enable PLL\n");
348
goto fail;
349
}
350
error = clk_enable(sc->clk_gate);
351
if (error != 0) {
352
device_printf(dev, "Cannot enable clk gate\n");
353
goto fail;
354
}
355
356
/* Reset controller */
357
if ((error = ahci_a10_ctlr_reset(dev)) != 0)
358
goto fail;
359
360
/*
361
* No MSI registers on this platform.
362
*/
363
ctlr->msi = 0;
364
ctlr->numirqs = 1;
365
366
/* Channel start callback(). */
367
ctlr->ch_start = ahci_a10_ch_start;
368
369
/*
370
* Note: ahci_attach will release ctlr->r_mem on errors automatically
371
*/
372
return (ahci_attach(dev));
373
374
fail:
375
if (sc->ahci_reg != NULL)
376
regulator_disable(sc->ahci_reg);
377
if (sc->clk_gate != NULL)
378
clk_release(sc->clk_gate);
379
if (sc->clk_pll != NULL)
380
clk_release(sc->clk_pll);
381
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
382
return (error);
383
}
384
385
static int
386
ahci_a10_detach(device_t dev)
387
{
388
struct ahci_a10_softc *sc;
389
struct ahci_controller *ctlr;
390
391
sc = device_get_softc(dev);
392
ctlr = &sc->ahci_ctlr;
393
394
if (sc->ahci_reg != NULL)
395
regulator_disable(sc->ahci_reg);
396
if (sc->clk_gate != NULL)
397
clk_release(sc->clk_gate);
398
if (sc->clk_pll != NULL)
399
clk_release(sc->clk_pll);
400
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
401
return (ahci_detach(dev));
402
}
403
404
static device_method_t ahci_ata_methods[] = {
405
DEVMETHOD(device_probe, ahci_a10_probe),
406
DEVMETHOD(device_attach, ahci_a10_attach),
407
DEVMETHOD(device_detach, ahci_a10_detach),
408
DEVMETHOD(bus_print_child, ahci_print_child),
409
DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
410
DEVMETHOD(bus_release_resource, ahci_release_resource),
411
DEVMETHOD(bus_setup_intr, ahci_setup_intr),
412
DEVMETHOD(bus_teardown_intr,ahci_teardown_intr),
413
DEVMETHOD(bus_child_location, ahci_child_location),
414
DEVMETHOD_END
415
};
416
417
static driver_t ahci_ata_driver = {
418
"ahci",
419
ahci_ata_methods,
420
sizeof(struct ahci_a10_softc)
421
};
422
423
DRIVER_MODULE(a10_ahci, simplebus, ahci_ata_driver, 0, 0);
424
425