Changeset View
Changeset View
Standalone View
Standalone View
head/sys/arm/allwinner/a10_ahci.c
Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/ahci/ahci.h> | #include <dev/ahci/ahci.h> | ||||
#include <arm/allwinner/a10_clk.h> | #include <dev/extres/clk/clk.h> | ||||
/* | /* | ||||
* Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register | * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register | ||||
* set with a few extra implementation-specific registers that need to | * set with a few extra implementation-specific registers that need to | ||||
* be accounted for. There's only one PHY in the system, and it needs | * be accounted for. There's only one PHY in the system, and it needs | ||||
* to be trained to bring the link up. In addition, there's some DMA | * to be trained to bring the link up. In addition, there's some DMA | ||||
* specific things that need to be done as well. These things are also | * specific things that need to be done as well. These things are also | ||||
* just about completely undocumented, except in ugly code in the Linux | * just about completely undocumented, except in ugly code in the Linux | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#define AHCI_VERSIONR 0x00F8 | #define AHCI_VERSIONR 0x00F8 | ||||
#define AHCI_IDR 0x00FC | #define AHCI_IDR 0x00FC | ||||
#define AHCI_RWCR 0x00FC | #define AHCI_RWCR 0x00FC | ||||
#define AHCI_P0DMACR 0x0070 | #define AHCI_P0DMACR 0x0070 | ||||
#define AHCI_P0PHYCR 0x0078 | #define AHCI_P0PHYCR 0x0078 | ||||
#define AHCI_P0PHYSR 0x007C | #define AHCI_P0PHYSR 0x007C | ||||
#define PLL_FREQ 100000000 | |||||
static void inline | static void inline | ||||
ahci_set(struct resource *m, bus_size_t off, uint32_t set) | ahci_set(struct resource *m, bus_size_t off, uint32_t set) | ||||
{ | { | ||||
uint32_t val = ATA_INL(m, off); | uint32_t val = ATA_INL(m, off); | ||||
val |= set; | val |= set; | ||||
ATA_OUTL(m, off, val); | ATA_OUTL(m, off, val); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | ahci_a10_probe(device_t dev) | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static int | static int | ||||
ahci_a10_attach(device_t dev) | ahci_a10_attach(device_t dev) | ||||
{ | { | ||||
int error; | int error; | ||||
struct ahci_controller *ctlr; | struct ahci_controller *ctlr; | ||||
clk_t clk_pll, clk_gate; | |||||
ctlr = device_get_softc(dev); | ctlr = device_get_softc(dev); | ||||
clk_pll = clk_gate = NULL; | |||||
ctlr->quirks = AHCI_Q_NOPMP; | ctlr->quirks = AHCI_Q_NOPMP; | ||||
ctlr->vendorid = 0; | ctlr->vendorid = 0; | ||||
ctlr->deviceid = 0; | ctlr->deviceid = 0; | ||||
ctlr->subvendorid = 0; | ctlr->subvendorid = 0; | ||||
ctlr->subdeviceid = 0; | ctlr->subdeviceid = 0; | ||||
ctlr->r_rid = 0; | ctlr->r_rid = 0; | ||||
if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
&ctlr->r_rid, RF_ACTIVE))) | &ctlr->r_rid, RF_ACTIVE))) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* Turn on the PLL for SATA */ | /* Enable clocks */ | ||||
a10_clk_ahci_activate(); | error = clk_get_by_ofw_index(dev, 0, &clk_pll); | ||||
if (error != 0) { | |||||
device_printf(dev, "Cannot get PLL clock\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_get_by_ofw_index(dev, 1, &clk_gate); | |||||
if (error != 0) { | |||||
device_printf(dev, "Cannot get gate clock\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_set_freq(clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN); | |||||
if (error != 0) { | |||||
device_printf(dev, "Cannot set PLL frequency\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_enable(clk_pll); | |||||
if (error != 0) { | |||||
device_printf(dev, "Cannot enable PLL\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_enable(clk_gate); | |||||
if (error != 0) { | |||||
device_printf(dev, "Cannot enable clk gate\n"); | |||||
goto fail; | |||||
} | |||||
/* Reset controller */ | /* Reset controller */ | ||||
if ((error = ahci_a10_ctlr_reset(dev)) != 0) { | if ((error = ahci_a10_ctlr_reset(dev)) != 0) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, | goto fail; | ||||
ctlr->r_mem); | |||||
return (error); | |||||
}; | |||||
/* | /* | ||||
* No MSI registers on this platform. | * No MSI registers on this platform. | ||||
*/ | */ | ||||
ctlr->msi = 0; | ctlr->msi = 0; | ||||
ctlr->numirqs = 1; | ctlr->numirqs = 1; | ||||
/* Channel start callback(). */ | /* Channel start callback(). */ | ||||
ctlr->ch_start = ahci_a10_ch_start; | ctlr->ch_start = ahci_a10_ch_start; | ||||
/* | /* | ||||
* Note: ahci_attach will release ctlr->r_mem on errors automatically | * Note: ahci_attach will release ctlr->r_mem on errors automatically | ||||
*/ | */ | ||||
return (ahci_attach(dev)); | return (ahci_attach(dev)); | ||||
fail: | |||||
if (clk_gate != NULL) | |||||
clk_release(clk_gate); | |||||
if (clk_pll != NULL) | |||||
clk_release(clk_pll); | |||||
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); | |||||
return (error); | |||||
} | } | ||||
static int | static int | ||||
ahci_a10_detach(device_t dev) | ahci_a10_detach(device_t dev) | ||||
{ | { | ||||
return (ahci_detach(dev)); | return (ahci_detach(dev)); | ||||
} | } | ||||
Show All 23 Lines |