# HG changeset patch # Parent 2f64dca46fe64c8331b9a0b5c4d7755355e90c9d Attachment for i.MX6 AHCI SATA controller. This controller is mostly standard, but needs some minor initialization for its PLL and clocks. In addition, we have to set the PORTS that are supported as well as enabling SSS and setup the 1ms timer (which happens every 1 / 1000th the ahb frequency). This driver is totally untested. Sponsored by: Netflix diff -r 2f64dca46fe6 sys/arm/conf/IMX6 --- a/sys/arm/conf/IMX6 +++ b/sys/arm/conf/IMX6 @@ -109,6 +109,7 @@ device mmc # SD/MMC protocol device mmcsd # SDCard disk device # SCSI peripherals +device ahci device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device cd # CD diff -r 2f64dca46fe6 sys/arm/freescale/imx/files.imx53 --- a/sys/arm/freescale/imx/files.imx53 +++ b/sys/arm/freescale/imx/files.imx53 @@ -33,8 +33,8 @@ arm/freescale/imx/imx_gpt.c standard # Clock Configuration Manager arm/freescale/imx/imx51_ccm.c standard -# i.MX5xx PATA controller -dev/ata/chipsets/ata-fsl.c optional imxata +# i.MX53x AHCI controller +arm/freescale/imx/imx_ahci.c optional ahci # SDHCI/MMC arm/freescale/imx/imx_sdhci.c optional sdhci diff -r 2f64dca46fe6 sys/arm/freescale/imx/files.imx6 --- a/sys/arm/freescale/imx/files.imx6 +++ b/sys/arm/freescale/imx/files.imx6 @@ -32,6 +32,7 @@ arm/freescale/imx/imx_i2c.c optional fs # # Optional devices. # +arm/freescale/imx/imx_ahci.c optional ahci arm/freescale/imx/imx_sdhci.c optional sdhci arm/freescale/imx/imx_wdog.c optional imxwdt diff -r 2f64dca46fe6 sys/arm/freescale/imx/imx_ahci.c --- /dev/null +++ b/sys/arm/freescale/imx/imx_ahci.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2014 M. Warner Losh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define IMX6_TIMER1MS 0xe0 + +static int +ahci_imx6_ctlr_reset(device_t dev) +{ + + return ahci_ctlr_reset(dev); +} + +static void +ahci_imx6_init(device_t dev) +{ + struct ahci_controller *ctlr = device_get_softc(dev); + + /* + * Need to setup the PHY parameters. This must be done in two + * writes to GPR13. First to write the parmaeters and second + * to enable the mpll clock. + */ +#if 0 + /* XXX vvvvv this code is cut and pasted directly from Linux vvvvv XXX */ + imxpriv->gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + ret = clk_prepare_enable(imxpriv->sata_ref_clk); + + /* + * set PHY Paremeters, two steps to configure the GPR13, + * one write for rest of parameters, mask of first write + * is 0x07fffffd, and the other one write for setting + * the mpll_clk_en. + */ + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK + | IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK + | IMX6Q_GPR13_SATA_SPD_MODE_MASK + | IMX6Q_GPR13_SATA_MPLL_SS_EN + | IMX6Q_GPR13_SATA_TX_ATTEN_MASK + | IMX6Q_GPR13_SATA_TX_BOOST_MASK + | IMX6Q_GPR13_SATA_TX_LVL_MASK + | IMX6Q_GPR13_SATA_TX_EDGE_RATE + , IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB + | IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F + | IMX6Q_GPR13_SATA_SPD_MODE_3P0G + | IMX6Q_GPR13_SATA_MPLL_SS_EN + | IMX6Q_GPR13_SATA_TX_ATTEN_9_16 + | IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB + | IMX6Q_GPR13_SATA_TX_LVL_1_025_V); + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); + usleep_range(100, 200); + /* XXX ^^^^^ this code is cut and pasted directly from Linux ^^^^^ XXX */ +#endif + /* + * Need to configure the staggered startup in host caps, + * mark that only one port is enabled in ports implelemented + * mask, and frob the imx6-specific TIMER1MS register to + * be the clock frequency of the ahb clock in kilo-Hertz. + */ + ATA_OUTL(ctlr->r_mem, AHCI_CAP, + ATA_INL(ctlr->r_mem, AHCI_CAP) | AHCI_CAP_SSS); + ATA_OUTL(ctlr->r_mem, AHCI_PI, + ATA_INL(ctlr->r_mem, AHCI_PI) | 0x1); + ATA_OUTL(ctlr->r_mem, IMX6_TIMER1MS, imx_ccm_ahb_hz() / 1000); +} + +static int +ahci_imx6_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,imx6q-ahci") && + !ofw_bus_is_compatible(dev, "fsl,imx53-ahci")) + return (ENXIO); + + device_set_desc(dev, "Freescale i.MX6 AHCI SATA"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ahci_imx6_attach(device_t dev) +{ + struct ahci_controller *ctlr = device_get_softc(dev); + int error; + + ctlr->quirks = 0; /* Be optimistic -- Linux turns off PMP */ + ctlr->vendorid = 0; + ctlr->deviceid = 0; + ctlr->subvendorid = 0; + ctlr->subdeviceid = 0; + ctlr->r_rid = 0; + if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE))) + return ENXIO; + + /* Init PLLs, and initialize SoC specific regs */ + ahci_imx6_init(dev); + + /* Reset controller */ + if ((error = ahci_imx6_ctlr_reset(dev)) != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + return (error); + }; + + /* No MSI registers on this platform */ + ctlr->msi = 0; + + /* Note: ahci_attach will release ctlr->r_mem on errors automatically */ + return ahci_attach(dev); +} + +static int +ahci_imx6_detach(device_t dev) +{ + // XXX what to do + + return ahci_detach(dev); +} + +static int +ahci_imx6_suspend(device_t dev) +{ + + return ENXIO; +} + +static int +ahci_imx6_resume(device_t dev) +{ + + return ENXIO; +} + +devclass_t ahci_devclass; +static device_method_t ahci_ata_methods[] = { + DEVMETHOD(device_probe, ahci_imx6_probe), + DEVMETHOD(device_attach, ahci_imx6_attach), + DEVMETHOD(device_detach, ahci_imx6_detach), + DEVMETHOD(device_suspend, ahci_imx6_suspend), + DEVMETHOD(device_resume, ahci_imx6_resume), + DEVMETHOD(bus_print_child, ahci_print_child), + DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), + DEVMETHOD(bus_release_resource, ahci_release_resource), + DEVMETHOD(bus_setup_intr, ahci_setup_intr), + DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), + DEVMETHOD(bus_child_location_str, ahci_child_location_str), + { 0, 0 } +}; +static driver_t ahci_ata_driver = { + "ahci", + ahci_ata_methods, + sizeof(struct ahci_controller) +}; +DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0);