diff --git a/sys/arm/nvidia/tegra_ahci.c b/sys/arm/nvidia/tegra_ahci.c index c5d4dbff977c..30e28dd33235 100644 --- a/sys/arm/nvidia/tegra_ahci.c +++ b/sys/arm/nvidia/tegra_ahci.c @@ -1,781 +1,781 @@ /*- * Copyright (c) 2016 Michal Meloun * 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 /* * AHCI driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SATA_CONFIGURATION 0x180 #define SATA_CONFIGURATION_CLK_OVERRIDE (1U << 31) #define SATA_CONFIGURATION_EN_FPCI (1 << 0) #define SATA_FPCI_BAR5 0x94 #define SATA_FPCI_BAR_START(x) (((x) & 0xFFFFFFF) << 4) #define SATA_FPCI_BAR_ACCESS_TYPE (1 << 0) #define SATA_INTR_MASK 0x188 #define SATA_INTR_MASK_IP_INT_MASK (1 << 16) #define SCFG_OFFSET 0x1000 #define T_SATA0_CFG_1 0x04 #define T_SATA0_CFG_1_IO_SPACE (1 << 0) #define T_SATA0_CFG_1_MEMORY_SPACE (1 << 1) #define T_SATA0_CFG_1_BUS_MASTER (1 << 2) #define T_SATA0_CFG_1_SERR (1 << 8) #define T_SATA0_CFG_9 0x24 #define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13 #define T_SATA0_CFG_35 0x94 #define T_SATA0_CFG_35_IDP_INDEX_MASK (0x7ff << 2) #define T_SATA0_CFG_35_IDP_INDEX (0x2a << 2) #define T_SATA0_AHCI_IDP1 0x98 #define T_SATA0_AHCI_IDP1_DATA 0x400040 #define T_SATA0_CFG_PHY_1 0x12c #define T_SATA0_CFG_PHY_1_PADS_IDDQ_EN (1 << 23) #define T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN (1 << 22) #define T_SATA0_NVOOB 0x114 #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK (0x3 << 26) #define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH (0x3 << 26) #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK (0x3 << 24) #define T_SATA0_NVOOB_SQUELCH_FILTER_MODE (0x1 << 24) #define T_SATA0_NVOOB_COMMA_CNT_MASK (0xff << 16) #define T_SATA0_NVOOB_COMMA_CNT (0x07 << 16) #define T_SATA0_CFG_PHY 0x120 #define T_SATA0_CFG_PHY_MASK_SQUELCH (1 << 24) #define T_SATA0_CFG_PHY_USE_7BIT_ALIGN_DET_FOR_SPD (1 << 11) #define T_SATA0_CFG2NVOOB_2 0x134 #define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK (0x1ff << 18) #define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW (0xc << 18) #define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 #define T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30) #define T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17) #define T_SATA0_AHCI_HBA_CAP_BKDR_SALP (1 << 26) #define T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14) #define T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13) #define T_SATA0_BKDOOR_CC 0x4a4 #define T_SATA0_BKDOOR_CC_CLASS_CODE_MASK (0xffff << 16) #define T_SATA0_BKDOOR_CC_CLASS_CODE (0x0106 << 16) #define T_SATA0_BKDOOR_CC_PROG_IF_MASK (0xff << 8) #define T_SATA0_BKDOOR_CC_PROG_IF (0x01 << 8) #define T_SATA0_CFG_SATA 0x54c #define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN (1 << 12) #define T_SATA0_CFG_MISC 0x550 #define T_SATA0_INDEX 0x680 #define T_SATA0_CHX_PHY_CTRL1_GEN1 0x690 #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK 0xff #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8 #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK 0xff #define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0 #define T_SATA0_CHX_PHY_CTRL1_GEN2 0x694 #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK 0xff #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12 #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK 0xff #define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0 #define T_SATA0_CHX_PHY_CTRL2 0x69c #define T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1 0x23 #define T_SATA0_CHX_PHY_CTRL11 0x6d0 #define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16) #define T_SATA0_CHX_PHY_CTRL17 0x6e8 #define T_SATA0_CHX_PHY_CTRL18 0x6ec #define T_SATA0_CHX_PHY_CTRL20 0x6f4 #define T_SATA0_CHX_PHY_CTRL21 0x6f8 #define FUSE_SATA_CALIB 0x124 #define FUSE_SATA_CALIB_MASK 0x3 #define SATA_AUX_MISC_CNTL 0x1108 #define SATA_AUX_PAD_PLL_CTRL_0 0x1120 #define SATA_AUX_PAD_PLL_CTRL_1 0x1124 #define SATA_AUX_PAD_PLL_CTRL_2 0x1128 #define SATA_AUX_PAD_PLL_CTRL_3 0x112c #define T_AHCI_HBA_CCC_PORTS 0x0018 #define T_AHCI_HBA_CAP_BKDR 0x00A0 #define T_AHCI_HBA_CAP_BKDR_S64A (1 << 31) #define T_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30) #define T_AHCI_HBA_CAP_BKDR_SSNTF (1 << 29) #define T_AHCI_HBA_CAP_BKDR_SMPS (1 << 28) #define T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP (1 << 27) #define T_AHCI_HBA_CAP_BKDR_SALP (1 << 26) #define T_AHCI_HBA_CAP_BKDR_SAL (1 << 25) #define T_AHCI_HBA_CAP_BKDR_SUPP_CLO (1 << 24) #define T_AHCI_HBA_CAP_BKDR_INTF_SPD_SUPP(x) (((x) & 0xF) << 20) #define T_AHCI_HBA_CAP_BKDR_SUPP_NONZERO_OFFSET (1 << 19) #define T_AHCI_HBA_CAP_BKDR_SUPP_AHCI_ONLY (1 << 18) #define T_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17) #define T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING (1 << 16) #define T_AHCI_HBA_CAP_BKDR_PIO_MULT_DRQ_BLK (1 << 15) #define T_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14) #define T_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13) #define T_AHCI_HBA_CAP_BKDR_NUM_CMD_SLOTS(x) (((x) & 0x1F) << 8) #define T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING (1 << 7) #define T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP (1 << 6) #define T_AHCI_HBA_CAP_BKDR_EXT_SATA (1 << 5) #define T_AHCI_HBA_CAP_BKDR_NUM_PORTS(x) (((x) & 0xF) << 0) #define T_AHCI_PORT_BKDR 0x0170 #define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE_VAL(x) (((x) & 0xFF) << 24) #define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE_VAL(x) (((x) & 0x1F) << 16) #define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE (1 << 15) #define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE (1 << 14) #define T_AHCI_PORT_BKDR_PXDEVSLP_DM(x) (((x) & 0xF) << 10) #define T_AHCI_PORT_BKDR_PORT_UNCONNECTED (1 << 9) #define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_CLAMP_THIS_CH (1 << 8) #define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_UNCLAMP (1 << 7) #define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_CLAMP (1 << 6) #define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_UNCLAMP (1 << 5) #define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_CLAMP (1 << 4) #define T_AHCI_PORT_BKDR_HOTPLUG_CAP (1 << 3) #define T_AHCI_PORT_BKDR_MECH_SWITCH (1 << 2) #define T_AHCI_PORT_BKDR_COLD_PRSN_DET (1 << 1) #define T_AHCI_PORT_BKDR_EXT_SATA_SUPP (1 << 0) /* AUX registers */ #define SATA_AUX_MISC_CNTL_1 0x008 #define SATA_AUX_MISC_CNTL_1_DEVSLP_OVERRIDE (1 << 17) #define SATA_AUX_MISC_CNTL_1_SDS_SUPPORT (1 << 13) #define SATA_AUX_MISC_CNTL_1_DESO_SUPPORT (1 << 15) #define AHCI_WR4(_sc, _r, _v) bus_write_4((_sc)->ctlr.r_mem, (_r), (_v)) #define AHCI_RD4(_sc, _r) bus_read_4((_sc)->ctlr.r_mem, (_r)) #define SATA_WR4(_sc, _r, _v) bus_write_4((_sc)->sata_mem, (_r), (_v)) #define SATA_RD4(_sc, _r) bus_read_4((_sc)->sata_mem, (_r)) struct sata_pad_calibration { uint32_t gen1_tx_amp; uint32_t gen1_tx_peak; uint32_t gen2_tx_amp; uint32_t gen2_tx_peak; }; static const struct sata_pad_calibration tegra124_pad_calibration[] = { {0x18, 0x04, 0x18, 0x0a}, {0x0e, 0x04, 0x14, 0x0a}, {0x0e, 0x07, 0x1a, 0x0e}, {0x14, 0x0e, 0x1a, 0x0e}, }; struct ahci_soc; struct tegra_ahci_sc { struct ahci_controller ctlr; /* Must be first */ device_t dev; struct ahci_soc *soc; struct resource *sata_mem; struct resource *aux_mem; clk_t clk_sata; clk_t clk_sata_oob; clk_t clk_pll_e; clk_t clk_cml; hwreset_t hwreset_sata; hwreset_t hwreset_sata_oob; hwreset_t hwreset_sata_cold; regulator_t regulators[16]; /* Safe maximum */ phy_t phy; }; struct ahci_soc { char **regulator_names; int (*init)(struct tegra_ahci_sc *sc); }; /* Tegra 124 config. */ static char *tegra124_reg_names[] = { "hvdd-supply", "vddio-supply", "avdd-supply", "target-5v-supply", "target-12v-supply", NULL }; static int tegra124_ahci_init(struct tegra_ahci_sc *sc); static struct ahci_soc tegra124_soc = { .regulator_names = tegra124_reg_names, .init = tegra124_ahci_init, }; /* Tegra 210 config. */ static char *tegra210_reg_names[] = { NULL }; static struct ahci_soc tegra210_soc = { .regulator_names = tegra210_reg_names, }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-ahci", (uintptr_t)&tegra124_soc}, {"nvidia,tegra210-ahci", (uintptr_t)&tegra210_soc}, {NULL, 0} }; static int get_fdt_resources(struct tegra_ahci_sc *sc, phandle_t node) { int i, rv; /* Regulators. */ for (i = 0; sc->soc->regulator_names[i] != NULL; i++) { if (i >= nitems(sc->regulators)) { device_printf(sc->dev, "Too many regulators present in DT.\n"); return (EOVERFLOW); } rv = regulator_get_by_ofw_property(sc->dev, 0, sc->soc->regulator_names[i], sc->regulators + i); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' regulator\n", sc->soc->regulator_names[i]); return (ENXIO); } } /* Resets. */ rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata", &sc->hwreset_sata ); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-oob", &sc->hwreset_sata_oob); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata oob' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-cold", &sc->hwreset_sata_cold); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata cold' reset\n"); return (ENXIO); } /* Phy */ rv = phy_get_by_ofw_name(sc->dev, 0, "sata-0", &sc->phy); if (rv != 0) { rv = phy_get_by_ofw_idx(sc->dev, 0, 0, &sc->phy); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata' phy\n"); return (ENXIO); } } /* Clocks. */ rv = clk_get_by_ofw_name(sc->dev, 0, "sata", &sc->clk_sata); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "sata-oob", &sc->clk_sata_oob); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sata oob' clock\n"); return (ENXIO); } /* These are optional */ rv = clk_get_by_ofw_name(sc->dev, 0, "cml1", &sc->clk_cml); if (rv != 0) sc->clk_cml = NULL; rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e); if (rv != 0) sc->clk_pll_e = NULL; return (0); } static int enable_fdt_resources(struct tegra_ahci_sc *sc) { int i, rv; /* Enable regulators. */ for (i = 0; i < nitems(sc->regulators); i++) { if (sc->regulators[i] == NULL) continue; rv = regulator_enable(sc->regulators[i]); if (rv != 0) { device_printf(sc->dev, "Cannot enable '%s' regulator\n", sc->soc->regulator_names[i]); return (rv); } } /* Stop clocks */ clk_stop(sc->clk_sata); clk_stop(sc->clk_sata_oob); tegra_powergate_power_off(TEGRA_POWERGATE_SAX); rv = hwreset_assert(sc->hwreset_sata); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'sata' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_sata_oob); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'sata oob' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_sata_cold); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'sata cold' reset\n"); return (rv); } rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SAX, sc->clk_sata, sc->hwreset_sata); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'SAX' powergate\n"); return (rv); } rv = clk_enable(sc->clk_sata_oob); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'sata oob' clock\n"); return (rv); } if (sc->clk_cml != NULL) { rv = clk_enable(sc->clk_cml); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'cml' clock\n"); return (rv); } } if (sc->clk_pll_e != NULL) { rv = clk_enable(sc->clk_pll_e); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pll e' clock\n"); return (rv); } } rv = hwreset_deassert(sc->hwreset_sata_cold); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'sata cold' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_sata_oob); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'sata oob' reset\n"); return (rv); } rv = phy_enable(sc->phy); if (rv != 0) { device_printf(sc->dev, "Cannot enable SATA phy\n"); return (rv); } return (0); } static int tegra124_ahci_init(struct tegra_ahci_sc *sc) { uint32_t val; const struct sata_pad_calibration *calib; /* Pad calibration. */ val = tegra_fuse_read_4(FUSE_SATA_CALIB); calib = tegra124_pad_calibration + (val & FUSE_SATA_CALIB_MASK); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 1); val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT); val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT); val |= calib->gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; val |= calib->gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1, val); val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT); val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT); val |= calib->gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT; val |= calib->gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2, val); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11, T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2, T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 0); return (0); } static int tegra_ahci_ctrl_init(struct tegra_ahci_sc *sc) { uint32_t val; int rv; /* Enable SATA MMIO. */ val = SATA_RD4(sc, SATA_FPCI_BAR5); val &= ~SATA_FPCI_BAR_START(~0); val |= SATA_FPCI_BAR_START(0x10000); val |= SATA_FPCI_BAR_ACCESS_TYPE; SATA_WR4(sc, SATA_FPCI_BAR5, val); /* Enable FPCI access */ val = SATA_RD4(sc, SATA_CONFIGURATION); val |= SATA_CONFIGURATION_EN_FPCI; SATA_WR4(sc, SATA_CONFIGURATION, val); /* Recommended electrical settings for phy */ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL17, 0x55010000); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL18, 0x55010000); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL20, 0x1); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL21, 0x1); /* SQUELCH and Gen3 */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY); val |= T_SATA0_CFG_PHY_MASK_SQUELCH; val &= ~T_SATA0_CFG_PHY_USE_7BIT_ALIGN_DET_FOR_SPD; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY, val); val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_NVOOB); val &= ~T_SATA0_NVOOB_COMMA_CNT_MASK; val &= ~T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK; val &= ~T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK; val |= T_SATA0_NVOOB_COMMA_CNT; val |= T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH; val |= T_SATA0_NVOOB_SQUELCH_FILTER_MODE; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_NVOOB, val); /* Setup COMWAKE_IDLE_CNT */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG2NVOOB_2); val &= ~T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK; val |= T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG2NVOOB_2, val); if (sc->soc->init != NULL) { rv = sc->soc->init(sc); if (rv != 0) { device_printf(sc->dev, "SOC specific intialization failed: %d\n", rv); return (rv); } } /* Enable backdoor programming. */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); /* Set device class and interface */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC); val &= ~T_SATA0_BKDOOR_CC_CLASS_CODE_MASK; val &= ~T_SATA0_BKDOOR_CC_PROG_IF_MASK; val |= T_SATA0_BKDOOR_CC_CLASS_CODE; val |= T_SATA0_BKDOOR_CC_PROG_IF; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC, val); /* Enable LPM capabilities */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR); val |= T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP; val |= T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP; val |= T_SATA0_AHCI_HBA_CAP_BKDR_SALP; val |= T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR, val); /* Disable backdoor programming. */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); /* SATA Second Level Clock Gating */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_35); val &= ~T_SATA0_CFG_35_IDP_INDEX_MASK; val |= T_SATA0_CFG_35_IDP_INDEX; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_35, val); SATA_WR4(sc, SCFG_OFFSET + T_SATA0_AHCI_IDP1, 0x400040); val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY_1); val |= T_SATA0_CFG_PHY_1_PADS_IDDQ_EN; val |= T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_PHY_1, val); /* * Indicate Sata only has the capability to enter DevSleep * from slumber link. */ if (sc->aux_mem != NULL) { val = bus_read_4(sc->aux_mem, SATA_AUX_MISC_CNTL_1); val |= SATA_AUX_MISC_CNTL_1_DESO_SUPPORT; bus_write_4(sc->aux_mem, SATA_AUX_MISC_CNTL_1, val); } /* Enable IPFS Clock Gating */ val = SATA_RD4(sc, SCFG_OFFSET + SATA_CONFIGURATION); val &= ~SATA_CONFIGURATION_CLK_OVERRIDE; SATA_WR4(sc, SCFG_OFFSET + SATA_CONFIGURATION, val); /* Enable IO & memory access, bus master mode */ val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_1); val |= T_SATA0_CFG_1_IO_SPACE; val |= T_SATA0_CFG_1_MEMORY_SPACE; val |= T_SATA0_CFG_1_BUS_MASTER; val |= T_SATA0_CFG_1_SERR; SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_1, val); /* AHCI bar */ SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_9, 0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT); /* Unmask interrupts. */ val = SATA_RD4(sc, SATA_INTR_MASK); val |= SATA_INTR_MASK_IP_INT_MASK; SATA_WR4(sc, SATA_INTR_MASK, val); return (0); } static int tegra_ahci_ctlr_reset(device_t dev) { struct tegra_ahci_sc *sc; int rv; uint32_t reg; sc = device_get_softc(dev); rv = ahci_ctlr_reset(dev); if (rv != 0) return (0); AHCI_WR4(sc, T_AHCI_HBA_CCC_PORTS, 1); /* Overwrite AHCI capabilites. */ reg = AHCI_RD4(sc, T_AHCI_HBA_CAP_BKDR); reg &= ~T_AHCI_HBA_CAP_BKDR_NUM_PORTS(~0); reg |= T_AHCI_HBA_CAP_BKDR_NUM_PORTS(0); reg |= T_AHCI_HBA_CAP_BKDR_EXT_SATA; reg |= T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING; reg |= T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING; reg |= T_AHCI_HBA_CAP_BKDR_SUPP_PM; reg |= T_AHCI_HBA_CAP_BKDR_SUPP_CLO; reg |= T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP; AHCI_WR4(sc, T_AHCI_HBA_CAP_BKDR, reg); /* Overwrite AHCI portcapabilites. */ reg = AHCI_RD4(sc, T_AHCI_PORT_BKDR); reg |= T_AHCI_PORT_BKDR_COLD_PRSN_DET; reg |= T_AHCI_PORT_BKDR_HOTPLUG_CAP; reg |= T_AHCI_PORT_BKDR_EXT_SATA_SUPP; AHCI_WR4(sc, T_AHCI_PORT_BKDR, reg); return (0); } static int tegra_ahci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); - device_set_desc_copy(dev, "AHCI SATA controller"); + device_set_desc(dev, "AHCI SATA controller"); return (BUS_PROBE_DEFAULT); } static int tegra_ahci_attach(device_t dev) { struct tegra_ahci_sc *sc; struct ahci_controller *ctlr; phandle_t node; int rv, rid; sc = device_get_softc(dev); sc->dev = dev; ctlr = &sc->ctlr; node = ofw_bus_get_node(dev); sc->soc = (struct ahci_soc *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; ctlr->r_rid = 0; ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE); if (ctlr->r_mem == NULL) return (ENXIO); rid = 1; sc->sata_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sata_mem == NULL) { rv = ENXIO; goto fail; } /* Aux is optionall */ rid = 2; sc->aux_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); rv = get_fdt_resources(sc, node); if (rv != 0) { device_printf(sc->dev, "Failed to allocate FDT resource(s)\n"); goto fail; } rv = enable_fdt_resources(sc); if (rv != 0) { device_printf(sc->dev, "Failed to enable FDT resource(s)\n"); goto fail; } rv = tegra_ahci_ctrl_init(sc); if (rv != 0) { device_printf(sc->dev, "Failed to initialize controller)\n"); goto fail; } /* Setup controller defaults. */ ctlr->msi = 0; ctlr->numirqs = 1; ctlr->ccc = 0; /* Reset controller. */ rv = tegra_ahci_ctlr_reset(dev); if (rv != 0) goto fail; rv = ahci_attach(dev); return (rv); fail: /* XXX FDT stuff */ if (sc->sata_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sata_mem); if (ctlr->r_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (rv); } static int tegra_ahci_detach(device_t dev) { ahci_detach(dev); return (0); } static int tegra_ahci_suspend(device_t dev) { struct tegra_ahci_sc *sc = device_get_softc(dev); bus_generic_suspend(dev); /* Disable interupts, so the state change(s) doesn't trigger. */ ATA_OUTL(sc->ctlr.r_mem, AHCI_GHC, ATA_INL(sc->ctlr.r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); return (0); } static int tegra_ahci_resume(device_t dev) { int res; if ((res = tegra_ahci_ctlr_reset(dev)) != 0) return (res); ahci_ctlr_setup(dev); return (bus_generic_resume(dev)); } static device_method_t tegra_ahci_methods[] = { DEVMETHOD(device_probe, tegra_ahci_probe), DEVMETHOD(device_attach, tegra_ahci_attach), DEVMETHOD(device_detach, tegra_ahci_detach), DEVMETHOD(device_suspend, tegra_ahci_suspend), DEVMETHOD(device_resume, tegra_ahci_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, ahci_child_location), DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag), DEVMETHOD_END }; static DEFINE_CLASS_0(ahci, tegra_ahci_driver, tegra_ahci_methods, sizeof(struct tegra_ahci_sc)); DRIVER_MODULE(tegra_ahci, simplebus, tegra_ahci_driver, NULL, NULL); diff --git a/sys/arm/ti/am335x/am335x_dmtimer.c b/sys/arm/ti/am335x/am335x_dmtimer.c index a4ca188bd83b..fde5f04875cc 100644 --- a/sys/arm/ti/am335x/am335x_dmtimer.c +++ b/sys/arm/ti/am335x/am335x_dmtimer.c @@ -1,403 +1,401 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 Damjan Marion * 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 #include #include #include #include #include #include #include #include #include #include /* For arm_set_delay */ #include #include #include #include #include #include "am335x_dmtreg.h" struct am335x_dmtimer_softc { device_t dev; int tmr_mem_rid; struct resource * tmr_mem_res; int tmr_irq_rid; struct resource * tmr_irq_res; void *tmr_irq_handler; clk_t clk_fck; uint64_t sysclk_freq; uint32_t tclr; /* Cached TCLR register. */ union { struct timecounter tc; struct eventtimer et; } func; int tmr_num; /* Hardware unit number. */ char tmr_name[12]; /* "DMTimerN", N = tmr_num */ }; static struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL; static struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL; static void am335x_dmtimer_delay(int, void *); /* * We use dmtimer2 for eventtimer and dmtimer3 for timecounter. */ #define ET_TMR_NUM 2 #define TC_TMR_NUM 3 /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am335x-timer", 1}, {"ti,am335x-timer-1ms", 1}, {NULL, 0}, }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->tmr_mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->tmr_mem_res, (reg), (val)) static int am335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct am335x_dmtimer_softc *sc; uint32_t initial_count, reload_count; sc = et->et_priv; /* * Stop the timer before changing it. This routine will often be called * while the timer is still running, to either lengthen or shorten the * current event time. We need to ensure the timer doesn't expire while * we're working with it. * * Also clear any pending interrupt status, because it's at least * theoretically possible that we're running in a primary interrupt * context now, and a timer interrupt could be pending even before we * stopped the timer. The more likely case is that we're being called * from the et_event_cb() routine dispatched from our own handler, but * it's not clear to me that that's the only case possible. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (period != 0) { reload_count = ((uint32_t)et->et_frequency * period) >> 32; sc->tclr |= DMT_TCLR_AUTOLOAD; } else { reload_count = 0; } if (first != 0) initial_count = ((uint32_t)et->et_frequency * first) >> 32; else initial_count = reload_count; /* * Set auto-reload and current-count values. This timer hardware counts * up from the initial/reload value and interrupts on the zero rollover. */ DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); /* Enable overflow interrupt, and start the timer. */ DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); sc->tclr |= DMT_TCLR_START; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); return (0); } static int am335x_dmtimer_et_stop(struct eventtimer *et) { struct am335x_dmtimer_softc *sc; sc = et->et_priv; /* Stop timer, disable and clear interrupt. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); return (0); } static int am335x_dmtimer_et_intr(void *arg) { struct am335x_dmtimer_softc *sc; sc = arg; /* Ack the interrupt, and invoke the callback if it's still enabled. */ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (sc->func.et.et_active) sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg); return (FILTER_HANDLED); } static int am335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer")); /* * Setup eventtimer interrupt handling. Panic if anything goes wrong, * because the system just isn't going to run without an eventtimer. */ sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->tmr_irq_rid, RF_ACTIVE); if (sc->tmr_irq_res == NULL) panic("am335x_dmtimer: could not allocate irq resources"); if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK, am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0) panic("am335x_dmtimer: count not setup irq handler"); sc->func.et.et_name = sc->tmr_name; sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->func.et.et_quality = 500; sc->func.et.et_frequency = sc->sysclk_freq; sc->func.et.et_min_period = ((0x00000005LLU << 32) / sc->func.et.et_frequency); sc->func.et.et_max_period = (0xfffffffeLLU << 32) / sc->func.et.et_frequency; sc->func.et.et_start = am335x_dmtimer_et_start; sc->func.et.et_stop = am335x_dmtimer_et_stop; sc->func.et.et_priv = sc; am335x_dmtimer_et_sc = sc; et_register(&sc->func.et); return (0); } static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static int am335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter")); /* Set up timecounter, start it, register it. */ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; DMTIMER_WRITE4(sc, DMT_TLDR, 0); DMTIMER_WRITE4(sc, DMT_TCRR, 0); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); sc->func.tc.tc_name = sc->tmr_name; sc->func.tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; sc->func.tc.tc_counter_mask = ~0u; sc->func.tc.tc_frequency = sc->sysclk_freq; sc->func.tc.tc_quality = 500; sc->func.tc.tc_priv = sc; am335x_dmtimer_tc_sc = sc; tc_init(&sc->func.tc); arm_set_delay(am335x_dmtimer_delay, sc); return (0); } static int am335x_dmtimer_probe(device_t dev) { - char strbuf[32]; int tmr_num; uint64_t rev_address; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* * Get the hardware unit number from address of rev register. * If this isn't the hardware unit we're going to use for either the * eventtimer or the timecounter, no point in instantiating the device. */ rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); switch (rev_address) { case DMTIMER2_REV: tmr_num = 2; break; case DMTIMER3_REV: tmr_num = 3; break; default: /* Not DMTIMER2 or DMTIMER3 */ return (ENXIO); } - snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num); - device_set_desc_copy(dev, strbuf); + device_set_descf("AM335x DMTimer%d", tmr_num); return(BUS_PROBE_DEFAULT); } static int am335x_dmtimer_attach(device_t dev) { struct am335x_dmtimer_softc *sc; int err; uint64_t rev_address; clk_t sys_clkin; sc = device_get_softc(dev); sc->dev = dev; /* expect one clock */ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck); if (err != 0) { device_printf(dev, "Cant find clock index 0. err: %d\n", err); return (ENXIO); } err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin); if (err != 0) { device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err); return (ENXIO); } /* Select M_OSC as DPLL parent */ err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin); if (err != 0) { device_printf(dev, "Cant set mux to CLK_M_OSC\n"); return (ENXIO); } /* Enable clocks and power on the device. */ err = ti_sysc_clock_enable(device_get_parent(dev)); if (err != 0) { device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err); return (ENXIO); } /* Get the base clock frequency. */ err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq); if (err != 0) { device_printf(dev, "Cant get sysclk frequency, err %d\n", err); return (ENXIO); } /* Request the memory resources. */ sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->tmr_mem_rid, RF_ACTIVE); if (sc->tmr_mem_res == NULL) { return (ENXIO); } rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); switch (rev_address) { case DMTIMER2_REV: sc->tmr_num = 2; break; case DMTIMER3_REV: sc->tmr_num = 3; break; default: device_printf(dev, "Not timer 2 or 3! %#jx\n", rev_address); return (ENXIO); } snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); /* * Go set up either a timecounter or eventtimer. We wouldn't have * attached if we weren't one or the other. */ if (sc->tmr_num == ET_TMR_NUM) am335x_dmtimer_et_init(sc); else if (sc->tmr_num == TC_TMR_NUM) am335x_dmtimer_tc_init(sc); else panic("am335x_dmtimer: bad timer number %d", sc->tmr_num); return (0); } static device_method_t am335x_dmtimer_methods[] = { DEVMETHOD(device_probe, am335x_dmtimer_probe), DEVMETHOD(device_attach, am335x_dmtimer_attach), { 0, 0 } }; static driver_t am335x_dmtimer_driver = { "am335x_dmtimer", am335x_dmtimer_methods, sizeof(struct am335x_dmtimer_softc), }; DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, 0, 0); MODULE_DEPEND(am335x_dmtimer, ti_sysc, 1, 1, 1); static void am335x_dmtimer_delay(int usec, void *arg) { struct am335x_dmtimer_softc *sc = arg; int32_t counts; uint32_t first, last; /* Get the number of times to count */ counts = (usec + 1) * (sc->sysclk_freq / 1000000); first = DMTIMER_READ4(sc, DMT_TCRR); while (counts > 0) { last = DMTIMER_READ4(sc, DMT_TCRR); if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } diff --git a/sys/arm/ti/am335x/am335x_dmtpps.c b/sys/arm/ti/am335x/am335x_dmtpps.c index f3e4386e4837..5a19d3ad0dc3 100644 --- a/sys/arm/ti/am335x/am335x_dmtpps.c +++ b/sys/arm/ti/am335x/am335x_dmtpps.c @@ -1,617 +1,614 @@ /*- * Copyright (c) 2015 Ian lepore * 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. */ /* * AM335x PPS driver using DMTimer capture. * * Note that this PPS driver does not use an interrupt. Instead it uses the * hardware's ability to latch the timer's count register in response to a * signal on an IO pin. Each of timers 4-7 have an associated pin, and this * code allows any one of those to be used. * * The timecounter routines in kern_tc.c call the pps poll routine periodically * to see if a new counter value has been latched. When a new value has been * latched, the only processing done in the poll routine is to capture the * current set of timecounter timehands (done with pps_capture()) and the * latched value from the timer. The remaining work (done by pps_event() while * holding a mutex) is scheduled to be done later in a non-interrupt context. */ #include #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "am335x_dmtreg.h" #define PPS_CDEV_NAME "dmtpps" struct dmtpps_softc { device_t dev; int mem_rid; struct resource * mem_res; int tmr_num; /* N from hwmod str "timerN" */ char tmr_name[12]; /* "DMTimerN" */ uint32_t tclr; /* Cached TCLR register. */ struct timecounter tc; int pps_curmode; /* Edge mode now set in hw. */ struct cdev * pps_cdev; struct pps_state pps_state; struct mtx pps_mtx; clk_t clk_fck; uint64_t sysclk_freq; }; static int dmtpps_tmr_num; /* Set by probe() */ /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am335x-timer", 1}, {"ti,am335x-timer-1ms", 1}, {NULL, 0}, }; SIMPLEBUS_PNP_INFO(compat_data); /* * A table relating pad names to the hardware timer number they can be mux'd to. */ struct padinfo { char * ballname; int tmr_num; }; static struct padinfo dmtpps_padinfo[] = { {"GPMC_ADVn_ALE", 4}, {"I2C0_SDA", 4}, {"MII1_TX_EN", 4}, {"XDMA_EVENT_INTR0", 4}, {"GPMC_BEn0_CLE", 5}, {"MDC", 5}, {"MMC0_DAT3", 5}, {"UART1_RTSn", 5}, {"GPMC_WEn", 6}, {"MDIO", 6}, {"MMC0_DAT2", 6}, {"UART1_CTSn", 6}, {"GPMC_OEn_REn", 7}, {"I2C0_SCL", 7}, {"UART0_CTSn", 7}, {"XDMA_EVENT_INTR1", 7}, {NULL, 0} }; /* * This is either brilliantly user-friendly, or utterly lame... * * The am335x chip is used on the popular Beaglebone boards. Those boards have * pins for all four capture-capable timers available on the P8 header. Allow * users to configure the input pin by giving the name of the header pin. */ struct nicknames { const char * nick; const char * name; }; static struct nicknames dmtpps_pin_nicks[] = { {"P8-7", "GPMC_ADVn_ALE"}, {"P8-9", "GPMC_BEn0_CLE"}, {"P8-10", "GPMC_WEn"}, {"P8-8", "GPMC_OEn_REn",}, {NULL, NULL} }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) /* * Translate a short friendly case-insensitive name to its canonical name. */ static const char * dmtpps_translate_nickname(const char *nick) { struct nicknames *nn; for (nn = dmtpps_pin_nicks; nn->nick != NULL; nn++) if (strcasecmp(nick, nn->nick) == 0) return nn->name; return (nick); } /* * See if our tunable is set to the name of the input pin. If not, that's NOT * an error, return 0. If so, try to configure that pin as a timer capture * input pin, and if that works, then we have our timer unit number and if it * fails that IS an error, return -1. */ static int dmtpps_find_tmr_num_by_tunable(void) { struct padinfo *pi; char iname[20]; char muxmode[12]; const char * ballname; int err; if (!TUNABLE_STR_FETCH("hw.am335x_dmtpps.input", iname, sizeof(iname))) return (0); ballname = dmtpps_translate_nickname(iname); for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { if (strcmp(ballname, pi->ballname) != 0) continue; snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); err = ti_pinmux_padconf_set(pi->ballname, muxmode, PADCONF_INPUT); if (err != 0) { printf("am335x_dmtpps: unable to configure capture pin " "for %s to input mode\n", muxmode); return (-1); } else if (bootverbose) { printf("am335x_dmtpps: configured pin %s as input " "for %s\n", iname, muxmode); } return (pi->tmr_num); } /* Invalid name in the tunable, that's an error. */ printf("am335x_dmtpps: unknown pin name '%s'\n", iname); return (-1); } /* * Ask the pinmux driver whether any pin has been configured as a TIMER4..TIMER7 * input pin. If so, return the timer number, if not return 0. */ static int dmtpps_find_tmr_num_by_padconf(void) { int err; unsigned int padstate; const char * padmux; struct padinfo *pi; char muxmode[12]; for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { err = ti_pinmux_padconf_get(pi->ballname, &padmux, &padstate); snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); if (err == 0 && (padstate & RXACTIVE) != 0 && strcmp(muxmode, padmux) == 0) return (pi->tmr_num); } /* Nothing found, not an error. */ return (0); } /* * Figure out which hardware timer number to use based on input pin * configuration. This is done just once, the first time probe() runs. */ static int dmtpps_find_tmr_num(void) { int tmr_num; if ((tmr_num = dmtpps_find_tmr_num_by_tunable()) == 0) tmr_num = dmtpps_find_tmr_num_by_padconf(); if (tmr_num <= 0) { printf("am335x_dmtpps: PPS driver not enabled: unable to find " "or configure a capture input pin\n"); tmr_num = -1; /* Must return non-zero to prevent re-probing. */ } return (tmr_num); } static void dmtpps_set_hw_capture(struct dmtpps_softc *sc, bool force_off) { int newmode; if (force_off) newmode = 0; else newmode = sc->pps_state.ppsparam.mode & PPS_CAPTUREASSERT; if (newmode == sc->pps_curmode) return; sc->pps_curmode = newmode; if (newmode == PPS_CAPTUREASSERT) sc->tclr |= DMT_TCLR_CAPTRAN_LOHI; else sc->tclr &= ~DMT_TCLR_CAPTRAN_MASK; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); } static unsigned dmtpps_get_timecount(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static void dmtpps_poll(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; /* * If a new value has been latched we've got a PPS event. Capture the * timecounter data, then override the capcount field (pps_capture() * populates it from the current DMT_TCRR register) with the latched * value from the TCAR1 register. * * Note that we don't have the TCAR interrupt enabled, but the hardware * still provides the status bits in the "RAW" status register even when * they're masked from generating an irq. However, when clearing the * TCAR status to re-arm the capture for the next second, we have to * write to the IRQ status register, not the RAW register. Quirky. * * We do not need to hold a lock while capturing the pps data, because * it is captured into an area of the pps_state struct which is read * only by pps_event(). We do need to hold a lock while calling * pps_event(), because it manipulates data which is also accessed from * the ioctl(2) context by userland processes. */ if (DMTIMER_READ4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) { pps_capture(&sc->pps_state); sc->pps_state.capcount = DMTIMER_READ4(sc, DMT_TCAR1); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR); mtx_lock_spin(&sc->pps_mtx); pps_event(&sc->pps_state, PPS_CAPTUREASSERT); mtx_unlock_spin(&sc->pps_mtx); } } static int dmtpps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Begin polling for pps and enable capture in the hardware whenever the * device is open. Doing this stuff again is harmless if this isn't the * first open. */ sc->tc.tc_poll_pps = dmtpps_poll; dmtpps_set_hw_capture(sc, false); return 0; } static int dmtpps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Stop polling and disable capture on last close. Use the force-off * flag to override the configured mode and turn off the hardware. */ sc->tc.tc_poll_pps = NULL; dmtpps_set_hw_capture(sc, true); return 0; } static int dmtpps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct dmtpps_softc *sc; int err; sc = dev->si_drv1; /* Let the kernel do the heavy lifting for ioctl. */ mtx_lock_spin(&sc->pps_mtx); err = pps_ioctl(cmd, data, &sc->pps_state); mtx_unlock_spin(&sc->pps_mtx); if (err != 0) return (err); /* * The capture mode could have changed, set the hardware to whatever * mode is now current. Effectively a no-op if nothing changed. */ dmtpps_set_hw_capture(sc, false); return (err); } static struct cdevsw dmtpps_cdevsw = { .d_version = D_VERSION, .d_open = dmtpps_open, .d_close = dmtpps_close, .d_ioctl = dmtpps_ioctl, .d_name = PPS_CDEV_NAME, }; static int dmtpps_probe(device_t dev) { - char strbuf[64]; int tmr_num; uint64_t rev_address; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* * If we haven't chosen which hardware timer to use yet, go do that now. * We need to know that to decide whether to return success for this * hardware timer instance or not. */ if (dmtpps_tmr_num == 0) dmtpps_tmr_num = dmtpps_find_tmr_num(); /* * Figure out which hardware timer is being probed and see if it matches * the configured timer number determined earlier. */ rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); switch (rev_address) { case DMTIMER1_1MS_REV: tmr_num = 1; break; case DMTIMER2_REV: tmr_num = 2; break; case DMTIMER3_REV: tmr_num = 3; break; case DMTIMER4_REV: tmr_num = 4; break; case DMTIMER5_REV: tmr_num = 5; break; case DMTIMER6_REV: tmr_num = 6; break; case DMTIMER7_REV: tmr_num = 7; break; default: return (ENXIO); } if (dmtpps_tmr_num != tmr_num) return (ENXIO); - snprintf(strbuf, sizeof(strbuf), "AM335x PPS-Capture DMTimer%d", - tmr_num); - device_set_desc_copy(dev, strbuf); + device_set_descf("AM335x PPS-Capture DMTimer%d", tmr_num); return(BUS_PROBE_DEFAULT); } static int dmtpps_attach(device_t dev) { struct dmtpps_softc *sc; struct make_dev_args mda; int err; clk_t sys_clkin; uint64_t rev_address; sc = device_get_softc(dev); sc->dev = dev; /* Figure out which hardware timer this is and set the name string. */ rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); switch (rev_address) { case DMTIMER1_1MS_REV: sc->tmr_num = 1; break; case DMTIMER2_REV: sc->tmr_num = 2; break; case DMTIMER3_REV: sc->tmr_num = 3; break; case DMTIMER4_REV: sc->tmr_num = 4; break; case DMTIMER5_REV: sc->tmr_num = 5; break; case DMTIMER6_REV: sc->tmr_num = 6; break; case DMTIMER7_REV: sc->tmr_num = 7; break; } snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); /* expect one clock */ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck); if (err != 0) { device_printf(dev, "Cant find clock index 0. err: %d\n", err); return (ENXIO); } err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin); if (err != 0) { device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err); return (ENXIO); } /* Select M_OSC as DPLL parent */ err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin); if (err != 0) { device_printf(dev, "Cant set mux to CLK_M_OSC\n"); return (ENXIO); } /* Enable clocks and power on the device. */ err = ti_sysc_clock_enable(device_get_parent(dev)); if (err != 0) { device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err); return (ENXIO); } /* Get the base clock frequency. */ err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq); if (err != 0) { device_printf(dev, "Cant get sysclk frequency, err %d\n", err); return (ENXIO); } /* Request the memory resources. */ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) { return (ENXIO); } /* * Configure the timer pulse/capture pin to input/capture mode. This is * required in addition to configuring the pin as input with the pinmux * controller (which was done via fdt data or tunable at probe time). */ sc->tclr = DMT_TCLR_GPO_CFG; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); /* Set up timecounter hardware, start it. */ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; DMTIMER_WRITE4(sc, DMT_TLDR, 0); DMTIMER_WRITE4(sc, DMT_TCRR, 0); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); /* Register the timecounter. */ sc->tc.tc_name = sc->tmr_name; sc->tc.tc_get_timecount = dmtpps_get_timecount; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* * Indicate our PPS capabilities. Have the kernel init its part of the * pps_state struct and add its capabilities. * * While the hardware has a mode to capture each edge, it's not clear we * can use it that way, because there's only a single interrupt/status * bit to say something was captured, but not which edge it was. For * now, just say we can only capture assert events (the positive-going * edge of the pulse). */ mtx_init(&sc->pps_mtx, "dmtpps", NULL, MTX_SPIN); sc->pps_state.flags = PPSFLAG_MTX_SPIN; sc->pps_state.ppscap = PPS_CAPTUREASSERT; sc->pps_state.driver_abi = PPS_ABI_VERSION; sc->pps_state.driver_mtx = &sc->pps_mtx; pps_init_abi(&sc->pps_state); /* Create the PPS cdev. */ make_dev_args_init(&mda); mda.mda_flags = MAKEDEV_WAITOK; mda.mda_devsw = &dmtpps_cdevsw; mda.mda_cr = NULL; mda.mda_uid = UID_ROOT; mda.mda_gid = GID_WHEEL; mda.mda_mode = 0600; mda.mda_unit = device_get_unit(dev); mda.mda_si_drv1 = sc; if ((err = make_dev_s(&mda, &sc->pps_cdev, PPS_CDEV_NAME)) != 0) { device_printf(dev, "Failed to create cdev %s\n", PPS_CDEV_NAME); return (err); } if (bootverbose) device_printf(sc->dev, "Using %s for PPS device /dev/%s\n", sc->tmr_name, PPS_CDEV_NAME); return (0); } static int dmtpps_detach(device_t dev) { /* * There is no way to remove a timecounter once it has been registered, * even if it's not in use, so we can never detach. If we were * dynamically loaded as a module this will prevent unloading. */ return (EBUSY); } static device_method_t dmtpps_methods[] = { DEVMETHOD(device_probe, dmtpps_probe), DEVMETHOD(device_attach, dmtpps_attach), DEVMETHOD(device_detach, dmtpps_detach), { 0, 0 } }; static driver_t dmtpps_driver = { "am335x_dmtpps", dmtpps_methods, sizeof(struct dmtpps_softc), }; DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, 0, 0); MODULE_DEPEND(am335x_dmtpps, ti_sysc, 1, 1, 1);