diff --git a/sys/arm/allwinner/a10_codec.c b/sys/arm/allwinner/a10_codec.c index fc4937351f3b..421d1c015691 100644 --- a/sys/arm/allwinner/a10_codec.c +++ b/sys/arm/allwinner/a10_codec.c @@ -1,1206 +1,1206 @@ /*- * Copyright (c) 2014-2016 Jared D. McNeill * 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 ``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 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. */ /* * Allwinner A10/A20 and H3 Audio Codec */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "sunxi_dma_if.h" #include "mixer_if.h" struct a10codec_info; struct a10codec_config { /* mixer class */ struct kobj_class *mixer_class; /* toggle DAC/ADC mute */ void (*mute)(struct a10codec_info *, int, int); /* DRQ types */ u_int drqtype_codec; u_int drqtype_sdram; /* register map */ bus_size_t DPC, DAC_FIFOC, DAC_FIFOS, DAC_TXDATA, ADC_FIFOC, ADC_FIFOS, ADC_RXDATA, DAC_CNT, ADC_CNT; }; #define TX_TRIG_LEVEL 0xf #define RX_TRIG_LEVEL 0x7 #define DRQ_CLR_CNT 0x3 #define AC_DAC_DPC(_sc) ((_sc)->cfg->DPC) #define DAC_DPC_EN_DA 0x80000000 #define AC_DAC_FIFOC(_sc) ((_sc)->cfg->DAC_FIFOC) #define DAC_FIFOC_FS_SHIFT 29 #define DAC_FIFOC_FS_MASK (7U << DAC_FIFOC_FS_SHIFT) #define DAC_FS_48KHZ 0 #define DAC_FS_32KHZ 1 #define DAC_FS_24KHZ 2 #define DAC_FS_16KHZ 3 #define DAC_FS_12KHZ 4 #define DAC_FS_8KHZ 5 #define DAC_FS_192KHZ 6 #define DAC_FS_96KHZ 7 #define DAC_FIFOC_FIFO_MODE_SHIFT 24 #define DAC_FIFOC_FIFO_MODE_MASK (3U << DAC_FIFOC_FIFO_MODE_SHIFT) #define FIFO_MODE_24_31_8 0 #define FIFO_MODE_16_31_16 0 #define FIFO_MODE_16_15_0 1 #define DAC_FIFOC_DRQ_CLR_CNT_SHIFT 21 #define DAC_FIFOC_DRQ_CLR_CNT_MASK (3U << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) #define DAC_FIFOC_TX_TRIG_LEVEL_SHIFT 8 #define DAC_FIFOC_TX_TRIG_LEVEL_MASK (0x7f << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT) #define DAC_FIFOC_MONO_EN (1U << 6) #define DAC_FIFOC_TX_BITS (1U << 5) #define DAC_FIFOC_DRQ_EN (1U << 4) #define DAC_FIFOC_FIFO_FLUSH (1U << 0) #define AC_DAC_FIFOS(_sc) ((_sc)->cfg->DAC_FIFOS) #define AC_DAC_TXDATA(_sc) ((_sc)->cfg->DAC_TXDATA) #define AC_ADC_FIFOC(_sc) ((_sc)->cfg->ADC_FIFOC) #define ADC_FIFOC_FS_SHIFT 29 #define ADC_FIFOC_FS_MASK (7U << ADC_FIFOC_FS_SHIFT) #define ADC_FS_48KHZ 0 #define ADC_FIFOC_EN_AD (1U << 28) #define ADC_FIFOC_RX_FIFO_MODE (1U << 24) #define ADC_FIFOC_RX_TRIG_LEVEL_SHIFT 8 #define ADC_FIFOC_RX_TRIG_LEVEL_MASK (0x1f << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT) #define ADC_FIFOC_MONO_EN (1U << 7) #define ADC_FIFOC_RX_BITS (1U << 6) #define ADC_FIFOC_DRQ_EN (1U << 4) #define ADC_FIFOC_FIFO_FLUSH (1U << 1) #define AC_ADC_FIFOS(_sc) ((_sc)->cfg->ADC_FIFOS) #define AC_ADC_RXDATA(_sc) ((_sc)->cfg->ADC_RXDATA) #define AC_DAC_CNT(_sc) ((_sc)->cfg->DAC_CNT) #define AC_ADC_CNT(_sc) ((_sc)->cfg->ADC_CNT) static uint32_t a10codec_fmt[] = { SND_FORMAT(AFMT_S16_LE, 1, 0), SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps a10codec_pcaps = { 8000, 192000, a10codec_fmt, 0 }; static struct pcmchan_caps a10codec_rcaps = { 8000, 48000, a10codec_fmt, 0 }; struct a10codec_info; struct a10codec_chinfo { struct snd_dbuf *buffer; struct pcm_channel *channel; struct a10codec_info *parent; bus_dmamap_t dmamap; void *dmaaddr; bus_addr_t physaddr; bus_size_t fifo; device_t dmac; void *dmachan; int dir; int run; uint32_t pos; uint32_t format; uint32_t blocksize; uint32_t speed; }; struct a10codec_info { device_t dev; struct resource *res[2]; struct mtx *lock; bus_dma_tag_t dmat; unsigned dmasize; void *ih; struct a10codec_config *cfg; struct a10codec_chinfo play; struct a10codec_chinfo rec; }; static struct resource_spec a10codec_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define CODEC_ANALOG_READ(sc, reg) bus_read_4((sc)->res[1], (reg)) #define CODEC_ANALOG_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) #define CODEC_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) #define CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) /* * A10/A20 mixer interface */ #define A10_DAC_ACTL 0x10 #define A10_DACAREN (1U << 31) #define A10_DACALEN (1U << 30) #define A10_MIXEN (1U << 29) #define A10_DACPAS (1U << 8) #define A10_PAMUTE (1U << 6) #define A10_PAVOL_SHIFT 0 #define A10_PAVOL_MASK (0x3f << A10_PAVOL_SHIFT) #define A10_ADC_ACTL 0x28 #define A10_ADCREN (1U << 31) #define A10_ADCLEN (1U << 30) #define A10_PREG1EN (1U << 29) #define A10_PREG2EN (1U << 28) #define A10_VMICEN (1U << 27) #define A10_ADCG_SHIFT 20 #define A10_ADCG_MASK (7U << A10_ADCG_SHIFT) #define A10_ADCIS_SHIFT 17 #define A10_ADCIS_MASK (7U << A10_ADCIS_SHIFT) #define A10_ADC_IS_LINEIN 0 #define A10_ADC_IS_FMIN 1 #define A10_ADC_IS_MIC1 2 #define A10_ADC_IS_MIC2 3 #define A10_ADC_IS_MIC1_L_MIC2_R 4 #define A10_ADC_IS_MIC1_LR_MIC2_LR 5 #define A10_ADC_IS_OMIX 6 #define A10_ADC_IS_LINEIN_L_MIC1_R 7 #define A10_LNRDF (1U << 16) #define A10_LNPREG_SHIFT 13 #define A10_LNPREG_MASK (7U << A10_LNPREG_SHIFT) #define A10_PA_EN (1U << 4) #define A10_DDE (1U << 3) static int a10_mixer_init(struct snd_mixer *m) { struct a10codec_info *sc = mix_getdevinfo(m); uint32_t val; mix_setdevs(m, SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_RECLEV); mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC); /* Unmute input source to PA */ val = CODEC_READ(sc, A10_DAC_ACTL); val |= A10_PAMUTE; CODEC_WRITE(sc, A10_DAC_ACTL, val); /* Enable PA */ val = CODEC_READ(sc, A10_ADC_ACTL); val |= A10_PA_EN; CODEC_WRITE(sc, A10_ADC_ACTL, val); return (0); } static const struct a10_mixer { unsigned reg; unsigned mask; unsigned shift; } a10_mixers[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = { A10_DAC_ACTL, A10_PAVOL_MASK, A10_PAVOL_SHIFT }, [SOUND_MIXER_LINE] = { A10_ADC_ACTL, A10_LNPREG_MASK, A10_LNPREG_SHIFT }, [SOUND_MIXER_RECLEV] = { A10_ADC_ACTL, A10_ADCG_MASK, A10_ADCG_SHIFT }, }; static int a10_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct a10codec_info *sc = mix_getdevinfo(m); uint32_t val; unsigned nvol, max; max = a10_mixers[dev].mask >> a10_mixers[dev].shift; nvol = (left * max) / 100; val = CODEC_READ(sc, a10_mixers[dev].reg); val &= ~a10_mixers[dev].mask; val |= (nvol << a10_mixers[dev].shift); CODEC_WRITE(sc, a10_mixers[dev].reg, val); left = right = (left * 100) / max; return (left | (right << 8)); } static uint32_t a10_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) { struct a10codec_info *sc = mix_getdevinfo(m); uint32_t val; val = CODEC_READ(sc, A10_ADC_ACTL); switch (src) { case SOUND_MASK_LINE: /* line-in */ val &= ~A10_ADCIS_MASK; val |= (A10_ADC_IS_LINEIN << A10_ADCIS_SHIFT); break; case SOUND_MASK_MIC: /* MIC1 */ val &= ~A10_ADCIS_MASK; val |= (A10_ADC_IS_MIC1 << A10_ADCIS_SHIFT); break; case SOUND_MASK_LINE1: /* MIC2 */ val &= ~A10_ADCIS_MASK; val |= (A10_ADC_IS_MIC2 << A10_ADCIS_SHIFT); break; default: break; } CODEC_WRITE(sc, A10_ADC_ACTL, val); switch ((val & A10_ADCIS_MASK) >> A10_ADCIS_SHIFT) { case A10_ADC_IS_LINEIN: return (SOUND_MASK_LINE); case A10_ADC_IS_MIC1: return (SOUND_MASK_MIC); case A10_ADC_IS_MIC2: return (SOUND_MASK_LINE1); default: return (0); } } static void a10_mute(struct a10codec_info *sc, int mute, int dir) { uint32_t val; if (dir == PCMDIR_PLAY) { val = CODEC_READ(sc, A10_DAC_ACTL); if (mute) { /* Disable DAC analog l/r channels and output mixer */ val &= ~A10_DACAREN; val &= ~A10_DACALEN; val &= ~A10_DACPAS; } else { /* Enable DAC analog l/r channels and output mixer */ val |= A10_DACAREN; val |= A10_DACALEN; val |= A10_DACPAS; } CODEC_WRITE(sc, A10_DAC_ACTL, val); } else { val = CODEC_READ(sc, A10_ADC_ACTL); if (mute) { /* Disable ADC analog l/r channels, MIC1 preamp, * and VMIC pin voltage */ val &= ~A10_ADCREN; val &= ~A10_ADCLEN; val &= ~A10_PREG1EN; val &= ~A10_VMICEN; } else { /* Enable ADC analog l/r channels, MIC1 preamp, * and VMIC pin voltage */ val |= A10_ADCREN; val |= A10_ADCLEN; val |= A10_PREG1EN; val |= A10_VMICEN; } CODEC_WRITE(sc, A10_ADC_ACTL, val); } } static kobj_method_t a10_mixer_methods[] = { KOBJMETHOD(mixer_init, a10_mixer_init), KOBJMETHOD(mixer_set, a10_mixer_set), KOBJMETHOD(mixer_setrecsrc, a10_mixer_setrecsrc), KOBJMETHOD_END }; MIXER_DECLARE(a10_mixer); /* * H3 mixer interface */ #define H3_PR_CFG 0x00 #define H3_AC_PR_RST (1 << 28) #define H3_AC_PR_RW (1 << 24) #define H3_AC_PR_ADDR_SHIFT 16 #define H3_AC_PR_ADDR_MASK (0x1f << H3_AC_PR_ADDR_SHIFT) #define H3_ACDA_PR_WDAT_SHIFT 8 #define H3_ACDA_PR_WDAT_MASK (0xff << H3_ACDA_PR_WDAT_SHIFT) #define H3_ACDA_PR_RDAT_SHIFT 0 #define H3_ACDA_PR_RDAT_MASK (0xff << H3_ACDA_PR_RDAT_SHIFT) #define H3_LOMIXSC 0x01 #define H3_LOMIXSC_LDAC (1 << 1) #define H3_ROMIXSC 0x02 #define H3_ROMIXSC_RDAC (1 << 1) #define H3_DAC_PA_SRC 0x03 #define H3_DACAREN (1 << 7) #define H3_DACALEN (1 << 6) #define H3_RMIXEN (1 << 5) #define H3_LMIXEN (1 << 4) #define H3_LINEIN_GCTR 0x05 #define H3_LINEING_SHIFT 4 #define H3_LINEING_MASK (0x7 << H3_LINEING_SHIFT) #define H3_MIC_GCTR 0x06 #define H3_MIC1_GAIN_SHIFT 4 #define H3_MIC1_GAIN_MASK (0x7 << H3_MIC1_GAIN_SHIFT) #define H3_MIC2_GAIN_SHIFT 0 #define H3_MIC2_GAIN_MASK (0x7 << H3_MIC2_GAIN_SHIFT) #define H3_PAEN_CTR 0x07 #define H3_LINEOUTEN (1 << 7) #define H3_LINEOUT_VOLC 0x09 #define H3_LINEOUTVOL_SHIFT 3 #define H3_LINEOUTVOL_MASK (0x1f << H3_LINEOUTVOL_SHIFT) #define H3_MIC2G_LINEOUT_CTR 0x0a #define H3_LINEOUT_LSEL (1 << 3) #define H3_LINEOUT_RSEL (1 << 2) #define H3_LADCMIXSC 0x0c #define H3_RADCMIXSC 0x0d #define H3_ADCMIXSC_MIC1 (1 << 6) #define H3_ADCMIXSC_MIC2 (1 << 5) #define H3_ADCMIXSC_LINEIN (1 << 2) #define H3_ADCMIXSC_OMIXER (3 << 0) #define H3_ADC_AP_EN 0x0f #define H3_ADCREN (1 << 7) #define H3_ADCLEN (1 << 6) #define H3_ADCG_SHIFT 0 #define H3_ADCG_MASK (0x7 << H3_ADCG_SHIFT) static u_int h3_pr_read(struct a10codec_info *sc, u_int addr) { uint32_t val; /* Read current value */ val = CODEC_ANALOG_READ(sc, H3_PR_CFG); /* De-assert reset */ val |= H3_AC_PR_RST; CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Read mode */ val &= ~H3_AC_PR_RW; CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Set address */ val &= ~H3_AC_PR_ADDR_MASK; val |= (addr << H3_AC_PR_ADDR_SHIFT); CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Read data */ return (CODEC_ANALOG_READ(sc , H3_PR_CFG) & H3_ACDA_PR_RDAT_MASK); } static void h3_pr_write(struct a10codec_info *sc, u_int addr, u_int data) { uint32_t val; /* Read current value */ val = CODEC_ANALOG_READ(sc, H3_PR_CFG); /* De-assert reset */ val |= H3_AC_PR_RST; CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Set address */ val &= ~H3_AC_PR_ADDR_MASK; val |= (addr << H3_AC_PR_ADDR_SHIFT); CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Write data */ val &= ~H3_ACDA_PR_WDAT_MASK; val |= (data << H3_ACDA_PR_WDAT_SHIFT); CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); /* Write mode */ val |= H3_AC_PR_RW; CODEC_ANALOG_WRITE(sc, H3_PR_CFG, val); } static void h3_pr_set_clear(struct a10codec_info *sc, u_int addr, u_int set, u_int clr) { u_int old, new; old = h3_pr_read(sc, addr); new = set | (old & ~clr); h3_pr_write(sc, addr, new); } static int h3_mixer_init(struct snd_mixer *m) { int rid=1; pcell_t reg[2]; phandle_t analogref; struct a10codec_info *sc = mix_getdevinfo(m); if (OF_getencprop(ofw_bus_get_node(sc->dev), "allwinner,codec-analog-controls", &analogref, sizeof(analogref)) <= 0) { return (ENXIO); } if (OF_getencprop(OF_node_from_xref(analogref), "reg", reg, sizeof(reg)) <= 0) { return (ENXIO); } sc->res[1] = bus_alloc_resource(sc->dev, SYS_RES_MEMORY, &rid, reg[0], reg[0]+reg[1], reg[1], RF_ACTIVE ); if (sc->res[1] == NULL) { return (ENXIO); } mix_setdevs(m, SOUND_MASK_PCM | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1); mix_setrecdevs(m, SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_IMIX); pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); /* Right & Left LINEOUT enable */ h3_pr_set_clear(sc, H3_PAEN_CTR, H3_LINEOUTEN, 0); h3_pr_set_clear(sc, H3_MIC2G_LINEOUT_CTR, H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0); return (0); } static const struct h3_mixer { unsigned reg; unsigned mask; unsigned shift; } h3_mixers[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = { H3_LINEOUT_VOLC, H3_LINEOUTVOL_MASK, H3_LINEOUTVOL_SHIFT }, [SOUND_MIXER_RECLEV] = { H3_ADC_AP_EN, H3_ADCG_MASK, H3_ADCG_SHIFT }, [SOUND_MIXER_LINE] = { H3_LINEIN_GCTR, H3_LINEING_MASK, H3_LINEING_SHIFT }, [SOUND_MIXER_MIC] = { H3_MIC_GCTR, H3_MIC1_GAIN_MASK, H3_MIC1_GAIN_SHIFT }, [SOUND_MIXER_LINE1] = { H3_MIC_GCTR, H3_MIC2_GAIN_MASK, H3_MIC2_GAIN_SHIFT }, }; static int h3_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct a10codec_info *sc = mix_getdevinfo(m); unsigned nvol, max; max = h3_mixers[dev].mask >> h3_mixers[dev].shift; nvol = (left * max) / 100; h3_pr_set_clear(sc, h3_mixers[dev].reg, nvol << h3_mixers[dev].shift, h3_mixers[dev].mask); left = right = (left * 100) / max; return (left | (right << 8)); } static uint32_t h3_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) { struct a10codec_info *sc = mix_getdevinfo(m); uint32_t val; val = 0; src &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_LINE1 | SOUND_MASK_IMIX); if ((src & SOUND_MASK_LINE) != 0) /* line-in */ val |= H3_ADCMIXSC_LINEIN; if ((src & SOUND_MASK_MIC) != 0) /* MIC1 */ val |= H3_ADCMIXSC_MIC1; if ((src & SOUND_MASK_LINE1) != 0) /* MIC2 */ val |= H3_ADCMIXSC_MIC2; if ((src & SOUND_MASK_IMIX) != 0) /* l/r output mixer */ val |= H3_ADCMIXSC_OMIXER; h3_pr_write(sc, H3_LADCMIXSC, val); h3_pr_write(sc, H3_RADCMIXSC, val); return (src); } static void h3_mute(struct a10codec_info *sc, int mute, int dir) { if (dir == PCMDIR_PLAY) { if (mute) { /* Mute DAC l/r channels to output mixer */ h3_pr_set_clear(sc, H3_LOMIXSC, 0, H3_LOMIXSC_LDAC); h3_pr_set_clear(sc, H3_ROMIXSC, 0, H3_ROMIXSC_RDAC); /* Disable DAC analog l/r channels and output mixer */ h3_pr_set_clear(sc, H3_DAC_PA_SRC, 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN); } else { /* Enable DAC analog l/r channels and output mixer */ h3_pr_set_clear(sc, H3_DAC_PA_SRC, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0); /* Unmute DAC l/r channels to output mixer */ h3_pr_set_clear(sc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0); h3_pr_set_clear(sc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0); } } else { if (mute) { /* Disable ADC analog l/r channels */ h3_pr_set_clear(sc, H3_ADC_AP_EN, 0, H3_ADCREN | H3_ADCLEN); } else { /* Enable ADC analog l/r channels */ h3_pr_set_clear(sc, H3_ADC_AP_EN, H3_ADCREN | H3_ADCLEN, 0); } } } static kobj_method_t h3_mixer_methods[] = { KOBJMETHOD(mixer_init, h3_mixer_init), KOBJMETHOD(mixer_set, h3_mixer_set), KOBJMETHOD(mixer_setrecsrc, h3_mixer_setrecsrc), KOBJMETHOD_END }; MIXER_DECLARE(h3_mixer); /* * Channel interface */ static void a10codec_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct a10codec_chinfo *ch = arg; if (error != 0) return; ch->physaddr = segs[0].ds_addr; } static void a10codec_transfer(struct a10codec_chinfo *ch) { bus_addr_t src, dst; int error; if (ch->dir == PCMDIR_PLAY) { src = ch->physaddr + ch->pos; dst = ch->fifo; } else { src = ch->fifo; dst = ch->physaddr + ch->pos; } error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, src, dst, ch->blocksize); if (error) { ch->run = 0; device_printf(ch->parent->dev, "DMA transfer failed: %d\n", error); } } static void a10codec_dmaconfig(struct a10codec_chinfo *ch) { struct a10codec_info *sc = ch->parent; struct sunxi_dma_config conf; memset(&conf, 0, sizeof(conf)); conf.src_width = conf.dst_width = 16; conf.src_burst_len = conf.dst_burst_len = 4; if (ch->dir == PCMDIR_PLAY) { conf.dst_noincr = true; conf.src_drqtype = sc->cfg->drqtype_sdram; conf.dst_drqtype = sc->cfg->drqtype_codec; } else { conf.src_noincr = true; conf.src_drqtype = sc->cfg->drqtype_codec; conf.dst_drqtype = sc->cfg->drqtype_sdram; } SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); } static void a10codec_dmaintr(void *priv) { struct a10codec_chinfo *ch = priv; unsigned bufsize; bufsize = sndbuf_getsize(ch->buffer); ch->pos += ch->blocksize; if (ch->pos >= bufsize) ch->pos -= bufsize; if (ch->run) { chn_intr(ch->channel); a10codec_transfer(ch); } } static unsigned a10codec_fs(struct a10codec_chinfo *ch) { switch (ch->speed) { case 48000: return (DAC_FS_48KHZ); case 24000: return (DAC_FS_24KHZ); case 12000: return (DAC_FS_12KHZ); case 192000: return (DAC_FS_192KHZ); case 32000: return (DAC_FS_32KHZ); case 16000: return (DAC_FS_16KHZ); case 8000: return (DAC_FS_8KHZ); case 96000: return (DAC_FS_96KHZ); default: return (DAC_FS_48KHZ); } } static void a10codec_start(struct a10codec_chinfo *ch) { struct a10codec_info *sc = ch->parent; uint32_t val; ch->pos = 0; if (ch->dir == PCMDIR_PLAY) { /* Flush DAC FIFO */ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), DAC_FIFOC_FIFO_FLUSH); /* Clear DAC FIFO status */ CODEC_WRITE(sc, AC_DAC_FIFOS(sc), CODEC_READ(sc, AC_DAC_FIFOS(sc))); /* Unmute output */ sc->cfg->mute(sc, 0, ch->dir); /* Configure DAC DMA channel */ a10codec_dmaconfig(ch); /* Configure DAC FIFO */ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), (AFMT_CHANNEL(ch->format) == 1 ? DAC_FIFOC_MONO_EN : 0) | (a10codec_fs(ch) << DAC_FIFOC_FS_SHIFT) | (FIFO_MODE_16_15_0 << DAC_FIFOC_FIFO_MODE_SHIFT) | (DRQ_CLR_CNT << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) | (TX_TRIG_LEVEL << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT)); /* Enable DAC DRQ */ val = CODEC_READ(sc, AC_DAC_FIFOC(sc)); val |= DAC_FIFOC_DRQ_EN; CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val); } else { /* Flush ADC FIFO */ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), ADC_FIFOC_FIFO_FLUSH); /* Clear ADC FIFO status */ CODEC_WRITE(sc, AC_ADC_FIFOS(sc), CODEC_READ(sc, AC_ADC_FIFOS(sc))); /* Unmute input */ sc->cfg->mute(sc, 0, ch->dir); /* Configure ADC DMA channel */ a10codec_dmaconfig(ch); /* Configure ADC FIFO */ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), ADC_FIFOC_EN_AD | ADC_FIFOC_RX_FIFO_MODE | (AFMT_CHANNEL(ch->format) == 1 ? ADC_FIFOC_MONO_EN : 0) | (a10codec_fs(ch) << ADC_FIFOC_FS_SHIFT) | (RX_TRIG_LEVEL << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT)); /* Enable ADC DRQ */ val = CODEC_READ(sc, AC_ADC_FIFOC(sc)); val |= ADC_FIFOC_DRQ_EN; CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val); } /* Start DMA transfer */ a10codec_transfer(ch); } static void a10codec_stop(struct a10codec_chinfo *ch) { struct a10codec_info *sc = ch->parent; /* Disable DMA channel */ SUNXI_DMA_HALT(ch->dmac, ch->dmachan); sc->cfg->mute(sc, 1, ch->dir); if (ch->dir == PCMDIR_PLAY) { /* Disable DAC DRQ */ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), 0); } else { /* Disable ADC DRQ */ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), 0); } } static void * a10codec_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct a10codec_info *sc = devinfo; struct a10codec_chinfo *ch = dir == PCMDIR_PLAY ? &sc->play : &sc->rec; phandle_t xref; pcell_t *cells; int ncells, error; error = ofw_bus_parse_xref_list_alloc(ofw_bus_get_node(sc->dev), "dmas", "#dma-cells", dir == PCMDIR_PLAY ? 1 : 0, &xref, &ncells, &cells); if (error != 0) { device_printf(sc->dev, "cannot parse 'dmas' property\n"); return (NULL); } OF_prop_free(cells); ch->parent = sc; ch->channel = c; ch->buffer = b; ch->dir = dir; ch->fifo = rman_get_start(sc->res[0]) + (dir == PCMDIR_REC ? AC_ADC_RXDATA(sc) : AC_DAC_TXDATA(sc)); ch->dmac = OF_device_from_xref(xref); if (ch->dmac == NULL) { device_printf(sc->dev, "cannot find DMA controller\n"); device_printf(sc->dev, "xref = 0x%x\n", (u_int)xref); return (NULL); } ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, false, a10codec_dmaintr, ch); if (ch->dmachan == NULL) { device_printf(sc->dev, "cannot allocate DMA channel\n"); return (NULL); } error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); if (error != 0) { device_printf(sc->dev, "cannot allocate channel buffer\n"); return (NULL); } error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, sc->dmasize, a10codec_dmamap_cb, ch, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->dev, "cannot load DMA map\n"); return (NULL); } memset(ch->dmaaddr, 0, sc->dmasize); if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { device_printf(sc->dev, "cannot setup sndbuf\n"); return (NULL); } return (ch); } static int a10codec_chan_free(kobj_t obj, void *data) { struct a10codec_chinfo *ch = data; struct a10codec_info *sc = ch->parent; SUNXI_DMA_FREE(ch->dmac, ch->dmachan); bus_dmamap_unload(sc->dmat, ch->dmamap); bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); return (0); } static int a10codec_chan_setformat(kobj_t obj, void *data, uint32_t format) { struct a10codec_chinfo *ch = data; ch->format = format; return (0); } static uint32_t a10codec_chan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct a10codec_chinfo *ch = data; /* * The codec supports full duplex operation but both DAC and ADC * use the same source clock (PLL2). Limit the available speeds to * those supported by a 24576000 Hz input. */ switch (speed) { case 8000: case 12000: case 16000: case 24000: case 32000: case 48000: ch->speed = speed; break; case 96000: case 192000: /* 96 KHz / 192 KHz mode only supported for playback */ if (ch->dir == PCMDIR_PLAY) { ch->speed = speed; } else { ch->speed = 48000; } break; case 44100: ch->speed = 48000; break; case 22050: ch->speed = 24000; break; case 11025: ch->speed = 12000; break; default: ch->speed = 48000; break; } return (ch->speed); } static uint32_t a10codec_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct a10codec_chinfo *ch = data; ch->blocksize = blocksize & ~3; return (ch->blocksize); } static int a10codec_chan_trigger(kobj_t obj, void *data, int go) { struct a10codec_chinfo *ch = data; struct a10codec_info *sc = ch->parent; if (!PCMTRIG_COMMON(go)) return (0); snd_mtxlock(sc->lock); switch (go) { case PCMTRIG_START: ch->run = 1; a10codec_stop(ch); a10codec_start(ch); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: ch->run = 0; a10codec_stop(ch); break; default: break; } snd_mtxunlock(sc->lock); return (0); } static uint32_t a10codec_chan_getptr(kobj_t obj, void *data) { struct a10codec_chinfo *ch = data; return (ch->pos); } static struct pcmchan_caps * a10codec_chan_getcaps(kobj_t obj, void *data) { struct a10codec_chinfo *ch = data; if (ch->dir == PCMDIR_PLAY) { return (&a10codec_pcaps); } else { return (&a10codec_rcaps); } } static kobj_method_t a10codec_chan_methods[] = { KOBJMETHOD(channel_init, a10codec_chan_init), KOBJMETHOD(channel_free, a10codec_chan_free), KOBJMETHOD(channel_setformat, a10codec_chan_setformat), KOBJMETHOD(channel_setspeed, a10codec_chan_setspeed), KOBJMETHOD(channel_setblocksize, a10codec_chan_setblocksize), KOBJMETHOD(channel_trigger, a10codec_chan_trigger), KOBJMETHOD(channel_getptr, a10codec_chan_getptr), KOBJMETHOD(channel_getcaps, a10codec_chan_getcaps), KOBJMETHOD_END }; CHANNEL_DECLARE(a10codec_chan); /* * Device interface */ static const struct a10codec_config a10_config = { .mixer_class = &a10_mixer_class, .mute = a10_mute, .drqtype_codec = 19, .drqtype_sdram = 22, .DPC = 0x00, .DAC_FIFOC = 0x04, .DAC_FIFOS = 0x08, .DAC_TXDATA = 0x0c, .ADC_FIFOC = 0x1c, .ADC_FIFOS = 0x20, .ADC_RXDATA = 0x24, .DAC_CNT = 0x30, .ADC_CNT = 0x34, }; static const struct a10codec_config h3_config = { .mixer_class = &h3_mixer_class, .mute = h3_mute, .drqtype_codec = 15, .drqtype_sdram = 1, .DPC = 0x00, .DAC_FIFOC = 0x04, .DAC_FIFOS = 0x08, .DAC_TXDATA = 0x20, .ADC_FIFOC = 0x10, .ADC_FIFOS = 0x14, .ADC_RXDATA = 0x18, .DAC_CNT = 0x40, .ADC_CNT = 0x44, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-codec", (uintptr_t)&a10_config }, { "allwinner,sun7i-a20-codec", (uintptr_t)&a10_config }, { "allwinner,sun8i-h3-codec", (uintptr_t)&h3_config }, { NULL, 0 } }; static int a10codec_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Audio Codec"); return (BUS_PROBE_DEFAULT); } static int a10codec_attach(device_t dev) { struct a10codec_info *sc; char status[SND_STATUSLEN]; struct gpiobus_pin *pa_pin; phandle_t node; clk_t clk_bus, clk_codec; hwreset_t rst; uint32_t val; int error; node = ofw_bus_get_node(dev); sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->cfg = (void *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; sc->dev = dev; sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10codec softc"); if (bus_alloc_resources(dev, a10codec_spec, sc->res)) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } sc->dmasize = 131072; error = bus_dma_tag_create( bus_get_dma_tag(dev), 4, sc->dmasize, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sc->dmasize, 1, /* maxsize, nsegs */ sc->dmasize, 0, /* maxsegsize, flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dmat); if (error != 0) { device_printf(dev, "cannot create DMA tag\n"); goto fail; } /* Get clocks */ if (clk_get_by_ofw_name(dev, 0, "apb", &clk_bus) != 0 && clk_get_by_ofw_name(dev, 0, "ahb", &clk_bus) != 0) { device_printf(dev, "cannot find bus clock\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "codec", &clk_codec) != 0) { device_printf(dev, "cannot find codec clock\n"); goto fail; } /* Gating bus clock for codec */ if (clk_enable(clk_bus) != 0) { device_printf(dev, "cannot enable bus clock\n"); goto fail; } /* Activate audio codec clock. According to the A10 and A20 user * manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most * audio sampling rates require an 24.576MHz input clock with the * exception of 44.1kHz, 22.05kHz, and 11.025kHz. Unfortunately, * both capture and playback use the same clock source so to * safely support independent full duplex operation, we use a fixed * 24.576MHz clock source and don't advertise native support for * the three sampling rates that require a 22.5792MHz input. */ error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(dev, "cannot set codec clock frequency\n"); goto fail; } /* Enable audio codec clock */ error = clk_enable(clk_codec); if (error != 0) { device_printf(dev, "cannot enable codec clock\n"); goto fail; } /* De-assert hwreset */ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } /* Enable DAC */ val = CODEC_READ(sc, AC_DAC_DPC(sc)); val |= DAC_DPC_EN_DA; CODEC_WRITE(sc, AC_DAC_DPC(sc), val); if (mixer_init(dev, sc->cfg->mixer_class, sc)) { device_printf(dev, "mixer_init failed\n"); goto fail; } /* Unmute PA */ if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios", &pa_pin) == 0) { error = gpio_pin_set_active(pa_pin, 1); if (error != 0) device_printf(dev, "failed to unmute PA\n"); } pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); if (pcm_register(dev, sc, 1, 1)) { device_printf(dev, "pcm_register failed\n"); goto fail; } pcm_addchan(dev, PCMDIR_PLAY, &a10codec_chan_class, sc); pcm_addchan(dev, PCMDIR_REC, &a10codec_chan_class, sc); snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); pcm_setstatus(dev, status); return (0); fail: bus_release_resources(dev, a10codec_spec, sc->res); snd_mtxfree(sc->lock); free(sc, M_DEVBUF); return (ENXIO); } static device_method_t a10codec_pcm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10codec_probe), DEVMETHOD(device_attach, a10codec_attach), DEVMETHOD_END }; static driver_t a10codec_pcm_driver = { "pcm", a10codec_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(a10codec, simplebus, a10codec_pcm_driver, 0, 0); MODULE_DEPEND(a10codec, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(a10codec, 1); diff --git a/sys/arm/allwinner/a31_dmac.c b/sys/arm/allwinner/a31_dmac.c index 988444c4b1c5..9dc98342a159 100644 --- a/sys/arm/allwinner/a31_dmac.c +++ b/sys/arm/allwinner/a31_dmac.c @@ -1,554 +1,554 @@ /*- * Copyright (c) 2016 Jared D. McNeill * 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 ``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 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. * */ /* * Allwinner DMA controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "sunxi_dma_if.h" #define DMA_IRQ_EN_REG0 0x00 #define DMA_IRQ_EN_REG1 0x04 #define DMA_IRQ_EN_REG(ch) (DMA_IRQ_EN_REG0 + ((ch) / 8) * 4) #define DMA_PKG_IRQ_EN(ch) (1 << (((ch) % 8) * 4 + 1)) #define DMA_PKG_IRQ_MASK 0x2222222222222222ULL #define DMA_IRQ_PEND_REG0 0x10 #define DMA_IRQ_PEND_REG1 0x14 #define DMA_IRQ_PEND_REG(ch) (DMA_IRQ_PEND_REG0 + ((ch) / 8) * 4) #define DMA_STA_REG 0x30 #define DMA_EN_REG(n) (0x100 + (n) * 0x40 + 0x00) #define DMA_EN (1 << 0) #define DMA_PAU_REG(n) (0x100 + (n) * 0x40 + 0x04) #define DMA_STAR_ADDR_REG(n) (0x100 + (n) * 0x40 + 0x08) #define DMA_CFG_REG(n) (0x100 + (n) * 0x40 + 0x0c) #define DMA_DEST_DATA_WIDTH (0x3 << 25) #define DMA_DEST_DATA_WIDTH_SHIFT 25 #define DMA_DEST_BST_LEN (0x3 << 22) #define DMA_DEST_BST_LEN_SHIFT 22 #define DMA_DEST_ADDR_MODE (0x1 << 21) #define DMA_DEST_ADDR_MODE_SHIFT 21 #define DMA_DEST_DRQ_TYPE (0x1f << 16) #define DMA_DEST_DRQ_TYPE_SHIFT 16 #define DMA_SRC_DATA_WIDTH (0x3 << 9) #define DMA_SRC_DATA_WIDTH_SHIFT 9 #define DMA_SRC_BST_LEN (0x3 << 6) #define DMA_SRC_BST_LEN_SHIFT 6 #define DMA_SRC_ADDR_MODE (0x1 << 5) #define DMA_SRC_ADDR_MODE_SHIFT 5 #define DMA_SRC_DRQ_TYPE (0x1f << 0) #define DMA_SRC_DRQ_TYPE_SHIFT 0 #define DMA_DATA_WIDTH_8BIT 0 #define DMA_DATA_WIDTH_16BIT 1 #define DMA_DATA_WIDTH_32BIT 2 #define DMA_DATA_WIDTH_64BIT 3 #define DMA_ADDR_MODE_LINEAR 0 #define DMA_ADDR_MODE_IO 1 #define DMA_BST_LEN_1 0 #define DMA_BST_LEN_4 1 #define DMA_BST_LEN_8 2 #define DMA_BST_LEN_16 3 #define DMA_CUR_SRC_REG(n) (0x100 + (n) * 0x40 + 0x10) #define DMA_CUR_DEST_REG(n) (0x100 + (n) * 0x40 + 0x14) #define DMA_BCNT_LEFT_REG(n) (0x100 + (n) * 0x40 + 0x18) #define DMA_PARA_REG(n) (0x100 + (n) * 0x40 + 0x1c) #define WAIT_CYC (0xff << 0) #define WAIT_CYC_SHIFT 0 struct a31dmac_desc { uint32_t config; uint32_t srcaddr; uint32_t dstaddr; uint32_t bcnt; uint32_t para; uint32_t next; #define DMA_NULL 0xfffff800 }; #define DESC_ALIGN 4 #define DESC_SIZE sizeof(struct a31dmac_desc) struct a31dmac_config { u_int nchans; }; static const struct a31dmac_config a31_config = { .nchans = 16 }; static const struct a31dmac_config h3_config = { .nchans = 12 }; static const struct a31dmac_config a83t_config = { .nchans = 8 }; static const struct a31dmac_config a64_config = { .nchans = 8 }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun6i-a31-dma", (uintptr_t)&a31_config }, { "allwinner,sun8i-a83t-dma", (uintptr_t)&a83t_config }, { "allwinner,sun8i-h3-dma", (uintptr_t)&h3_config }, { "allwinner,sun50i-a64-dma", (uintptr_t)&a64_config }, { NULL, (uintptr_t)NULL } }; struct a31dmac_softc; struct a31dmac_channel { struct a31dmac_softc * sc; uint8_t index; void (*callback)(void *); void * callbackarg; bus_dmamap_t dmamap; struct a31dmac_desc *desc; bus_addr_t physaddr; }; struct a31dmac_softc { struct resource * res[2]; struct mtx mtx; void * ih; bus_dma_tag_t dmat; u_int nchans; struct a31dmac_channel * chans; }; static struct resource_spec a31dmac_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; #define DMA_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) #define DMA_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static void a31dmac_intr(void *); static void a31dmac_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int a31dmac_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(dev, "Allwinner DMA controller"); return (BUS_PROBE_DEFAULT); } static int a31dmac_attach(device_t dev) { struct a31dmac_softc *sc; struct a31dmac_config *conf; u_int index; hwreset_t rst; clk_t clk; int error; sc = device_get_softc(dev); conf = (void *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; clk = NULL; rst = NULL; if (bus_alloc_resources(dev, a31dmac_spec, sc->res)) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, "a31 dmac", NULL, MTX_SPIN); /* Clock and reset setup */ if (clk_get_by_ofw_index(dev, 0, 0, &clk) != 0) { device_printf(dev, "cannot get clock\n"); goto fail; } if (clk_enable(clk) != 0) { device_printf(dev, "cannot enable clock\n"); goto fail; } if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) != 0) { device_printf(dev, "cannot get hwreset\n"); goto fail; } if (hwreset_deassert(rst) != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } /* Descriptor DMA */ error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent tag */ DESC_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ DESC_SIZE, 1, /* maxsize, nsegs */ DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dmat); if (error != 0) { device_printf(dev, "cannot create dma tag\n"); goto fail; } /* Disable all interrupts and clear pending status */ DMA_WRITE(sc, DMA_IRQ_EN_REG0, 0); DMA_WRITE(sc, DMA_IRQ_EN_REG1, 0); DMA_WRITE(sc, DMA_IRQ_PEND_REG0, ~0); DMA_WRITE(sc, DMA_IRQ_PEND_REG1, ~0); /* Initialize channels */ sc->nchans = conf->nchans; sc->chans = malloc(sizeof(*sc->chans) * sc->nchans, M_DEVBUF, M_WAITOK | M_ZERO); for (index = 0; index < sc->nchans; index++) { sc->chans[index].sc = sc; sc->chans[index].index = index; sc->chans[index].callback = NULL; sc->chans[index].callbackarg = NULL; error = bus_dmamem_alloc(sc->dmat, (void **)&sc->chans[index].desc, BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->chans[index].dmamap); if (error != 0) { device_printf(dev, "cannot allocate dma mem\n"); goto fail; } error = bus_dmamap_load(sc->dmat, sc->chans[index].dmamap, sc->chans[index].desc, sizeof(*sc->chans[index].desc), a31dmac_dmamap_cb, &sc->chans[index], BUS_DMA_WAITOK); if (error != 0) { device_printf(dev, "cannot load dma map\n"); goto fail; } DMA_WRITE(sc, DMA_EN_REG(index), 0); } error = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_MISC, NULL, a31dmac_intr, sc, &sc->ih); if (error != 0) { device_printf(dev, "could not setup interrupt handler\n"); bus_release_resources(dev, a31dmac_spec, sc->res); mtx_destroy(&sc->mtx); return (ENXIO); } OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); return (0); fail: for (index = 0; index < sc->nchans; index++) if (sc->chans[index].desc != NULL) { bus_dmamap_unload(sc->dmat, sc->chans[index].dmamap); bus_dmamem_free(sc->dmat, sc->chans[index].desc, sc->chans[index].dmamap); } if (sc->chans != NULL) free(sc->chans, M_DEVBUF); if (sc->ih != NULL) bus_teardown_intr(dev, sc->res[1], sc->ih); if (rst != NULL) hwreset_release(rst); if (clk != NULL) clk_release(clk); bus_release_resources(dev, a31dmac_spec, sc->res); return (ENXIO); } static void a31dmac_dmamap_cb(void *priv, bus_dma_segment_t *segs, int nsegs, int error) { struct a31dmac_channel *ch; if (error != 0) return; ch = priv; ch->physaddr = segs[0].ds_addr; } static void a31dmac_intr(void *priv) { struct a31dmac_softc *sc; uint32_t pend0, pend1, bit; uint64_t pend, mask; u_int index; sc = priv; pend0 = DMA_READ(sc, DMA_IRQ_PEND_REG0); pend1 = sc->nchans > 8 ? DMA_READ(sc, DMA_IRQ_PEND_REG1) : 0; if (pend0 == 0 && pend1 == 0) return; if (pend0 != 0) DMA_WRITE(sc, DMA_IRQ_PEND_REG0, pend0); if (pend1 != 0) DMA_WRITE(sc, DMA_IRQ_PEND_REG1, pend1); pend = pend0 | ((uint64_t)pend1 << 32); while ((bit = ffsll(pend & DMA_PKG_IRQ_MASK)) != 0) { mask = (1U << (bit - 1)); pend &= ~mask; index = (bit - 1) / 4; if (index >= sc->nchans) continue; if (sc->chans[index].callback == NULL) continue; sc->chans[index].callback(sc->chans[index].callbackarg); } } static int a31dmac_set_config(device_t dev, void *priv, const struct sunxi_dma_config *cfg) { struct a31dmac_channel *ch; uint32_t config, para; unsigned int dst_dw, dst_bl, dst_wc, dst_am; unsigned int src_dw, src_bl, src_wc, src_am; ch = priv; switch (cfg->dst_width) { case 8: dst_dw = DMA_DATA_WIDTH_8BIT; break; case 16: dst_dw = DMA_DATA_WIDTH_16BIT; break; case 32: dst_dw = DMA_DATA_WIDTH_32BIT; break; case 64: dst_dw = DMA_DATA_WIDTH_64BIT; break; default: return (EINVAL); } switch (cfg->dst_burst_len) { case 1: dst_bl = DMA_BST_LEN_1; break; case 4: dst_bl = DMA_BST_LEN_4; break; case 8: dst_bl = DMA_BST_LEN_8; break; case 16: dst_bl = DMA_BST_LEN_16; break; default: return (EINVAL); } switch (cfg->src_width) { case 8: src_dw = DMA_DATA_WIDTH_8BIT; break; case 16: src_dw = DMA_DATA_WIDTH_16BIT; break; case 32: src_dw = DMA_DATA_WIDTH_32BIT; break; case 64: src_dw = DMA_DATA_WIDTH_64BIT; default: return (EINVAL); } switch (cfg->src_burst_len) { case 1: src_bl = DMA_BST_LEN_1; break; case 4: src_bl = DMA_BST_LEN_4; break; case 8: src_bl = DMA_BST_LEN_8; break; case 16: src_bl = DMA_BST_LEN_16; break; default: return (EINVAL); } dst_am = cfg->dst_noincr ? DMA_ADDR_MODE_IO : DMA_ADDR_MODE_LINEAR; src_am = cfg->src_noincr ? DMA_ADDR_MODE_IO : DMA_ADDR_MODE_LINEAR; dst_wc = cfg->dst_wait_cyc; src_wc = cfg->src_wait_cyc; if (dst_wc != src_wc) return (EINVAL); config = (dst_dw << DMA_DEST_DATA_WIDTH_SHIFT) | (dst_bl << DMA_DEST_BST_LEN_SHIFT) | (dst_am << DMA_DEST_ADDR_MODE_SHIFT) | (cfg->dst_drqtype << DMA_DEST_DRQ_TYPE_SHIFT) | (src_dw << DMA_SRC_DATA_WIDTH_SHIFT) | (src_bl << DMA_SRC_BST_LEN_SHIFT) | (src_am << DMA_SRC_ADDR_MODE_SHIFT) | (cfg->src_drqtype << DMA_SRC_DRQ_TYPE_SHIFT); para = (dst_wc << WAIT_CYC_SHIFT); ch->desc->config = htole32(config); ch->desc->para = htole32(para); return (0); } static void * a31dmac_alloc(device_t dev, bool dedicated, void (*cb)(void *), void *cbarg) { struct a31dmac_softc *sc; struct a31dmac_channel *ch; uint32_t irqen; u_int index; sc = device_get_softc(dev); ch = NULL; mtx_lock_spin(&sc->mtx); for (index = 0; index < sc->nchans; index++) { if (sc->chans[index].callback == NULL) { ch = &sc->chans[index]; ch->callback = cb; ch->callbackarg = cbarg; irqen = DMA_READ(sc, DMA_IRQ_EN_REG(index)); irqen |= DMA_PKG_IRQ_EN(index); DMA_WRITE(sc, DMA_IRQ_EN_REG(index), irqen); break; } } mtx_unlock_spin(&sc->mtx); return (ch); } static void a31dmac_free(device_t dev, void *priv) { struct a31dmac_channel *ch; struct a31dmac_softc *sc; uint32_t irqen; u_int index; ch = priv; sc = ch->sc; index = ch->index; mtx_lock_spin(&sc->mtx); irqen = DMA_READ(sc, DMA_IRQ_EN_REG(index)); irqen &= ~DMA_PKG_IRQ_EN(index); DMA_WRITE(sc, DMA_IRQ_EN_REG(index), irqen); DMA_WRITE(sc, DMA_IRQ_PEND_REG(index), DMA_PKG_IRQ_EN(index)); ch->callback = NULL; ch->callbackarg = NULL; mtx_unlock_spin(&sc->mtx); } static int a31dmac_transfer(device_t dev, void *priv, bus_addr_t src, bus_addr_t dst, size_t nbytes) { struct a31dmac_channel *ch; struct a31dmac_softc *sc; ch = priv; sc = ch->sc; ch->desc->srcaddr = htole32((uint32_t)src); ch->desc->dstaddr = htole32((uint32_t)dst); ch->desc->bcnt = htole32(nbytes); ch->desc->next = htole32(DMA_NULL); DMA_WRITE(sc, DMA_STAR_ADDR_REG(ch->index), (uint32_t)ch->physaddr); DMA_WRITE(sc, DMA_EN_REG(ch->index), DMA_EN); return (0); } static void a31dmac_halt(device_t dev, void *priv) { struct a31dmac_channel *ch; struct a31dmac_softc *sc; ch = priv; sc = ch->sc; DMA_WRITE(sc, DMA_EN_REG(ch->index), 0); } static device_method_t a31dmac_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a31dmac_probe), DEVMETHOD(device_attach, a31dmac_attach), /* sunxi DMA interface */ DEVMETHOD(sunxi_dma_alloc, a31dmac_alloc), DEVMETHOD(sunxi_dma_free, a31dmac_free), DEVMETHOD(sunxi_dma_set_config, a31dmac_set_config), DEVMETHOD(sunxi_dma_transfer, a31dmac_transfer), DEVMETHOD(sunxi_dma_halt, a31dmac_halt), DEVMETHOD_END }; static driver_t a31dmac_driver = { "a31dmac", a31dmac_methods, sizeof(struct a31dmac_softc) }; DRIVER_MODULE(a31dmac, simplebus, a31dmac_driver, 0, 0); diff --git a/sys/arm/allwinner/a33_codec.c b/sys/arm/allwinner/a33_codec.c index d71562b08c9b..4bf2863b74ef 100644 --- a/sys/arm/allwinner/a33_codec.c +++ b/sys/arm/allwinner/a33_codec.c @@ -1,407 +1,407 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Oleksandr Tymoshenko * Copyright (c) 2018 Jared McNeill * * 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 ``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 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 #include #include #include -#include +#include #include #include "opt_snd.h" #include #include #include "audio_dai_if.h" #define SYSCLK_CTL 0x00c #define AIF1CLK_ENA (1 << 11) #define AIF1CLK_SRC_MASK (3 << 8) #define AIF1CLK_SRC_PLL (2 << 8) #define SYSCLK_ENA (1 << 3) #define SYSCLK_SRC (1 << 0) #define MOD_CLK_ENA 0x010 #define MOD_RST_CTL 0x014 #define MOD_AIF1 (1 << 15) #define MOD_ADC (1 << 3) #define MOD_DAC (1 << 2) #define SYS_SR_CTRL 0x018 #define AIF1_FS_MASK (0xf << 12) #define AIF_FS_48KHZ (8 << 12) #define AIF1CLK_CTRL 0x040 #define AIF1_MSTR_MOD (1 << 15) #define AIF1_BCLK_INV (1 << 14) #define AIF1_LRCK_INV (1 << 13) #define AIF1_BCLK_DIV_MASK (0xf << 9) #define AIF1_BCLK_DIV_16 (6 << 9) #define AIF1_LRCK_DIV_MASK (7 << 6) #define AIF1_LRCK_DIV_16 (0 << 6) #define AIF1_LRCK_DIV_64 (2 << 6) #define AIF1_WORD_SIZ_MASK (3 << 4) #define AIF1_WORD_SIZ_16 (1 << 4) #define AIF1_DATA_FMT_MASK (3 << 2) #define AIF1_DATA_FMT_I2S (0 << 2) #define AIF1_DATA_FMT_LJ (1 << 2) #define AIF1_DATA_FMT_RJ (2 << 2) #define AIF1_DATA_FMT_DSP (3 << 2) #define AIF1_ADCDAT_CTRL 0x044 #define AIF1_ADC0L_ENA (1 << 15) #define AIF1_ADC0R_ENA (1 << 14) #define AIF1_DACDAT_CTRL 0x048 #define AIF1_DAC0L_ENA (1 << 15) #define AIF1_DAC0R_ENA (1 << 14) #define AIF1_MXR_SRC 0x04c #define AIF1L_MXR_SRC_MASK (0xf << 12) #define AIF1L_MXR_SRC_AIF1 (0x8 << 12) #define AIF1L_MXR_SRC_ADC (0x2 << 12) #define AIF1R_MXR_SRC_MASK (0xf << 8) #define AIF1R_MXR_SRC_AIF1 (0x8 << 8) #define AIF1R_MXR_SRC_ADC (0x2 << 8) #define ADC_DIG_CTRL 0x100 #define ADC_DIG_CTRL_ENAD (1 << 15) #define HMIC_CTRL1 0x110 #define HMIC_CTRL1_N_MASK (0xf << 8) #define HMIC_CTRL1_N(n) (((n) & 0xf) << 8) #define HMIC_CTRL1_JACK_IN_IRQ_EN (1 << 4) #define HMIC_CTRL1_JACK_OUT_IRQ_EN (1 << 3) #define HMIC_CTRL1_MIC_DET_IRQ_EN (1 << 0) #define HMIC_CTRL2 0x114 #define HMIC_CTRL2_MDATA_THRES __BITS(12,8) #define HMIC_STS 0x118 #define HMIC_STS_MIC_PRESENT (1 << 6) #define HMIC_STS_JACK_DET_OIRQ (1 << 4) #define HMIC_STS_JACK_DET_IIRQ (1 << 3) #define HMIC_STS_MIC_DET_ST (1 << 0) #define DAC_DIG_CTRL 0x120 #define DAC_DIG_CTRL_ENDA (1 << 15) #define DAC_MXR_SRC 0x130 #define DACL_MXR_SRC_MASK (0xf << 12) #define DACL_MXR_SRC_AIF1_DAC0L (0x8 << 12) #define DACR_MXR_SRC_MASK (0xf << 8) #define DACR_MXR_SRC_AIF1_DAC0R (0x8 << 8) static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-a33-codec", 1}, { NULL, 0 } }; static struct resource_spec sun8i_codec_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct sun8i_codec_softc { device_t dev; struct resource *res[2]; struct mtx mtx; clk_t clk_gate; clk_t clk_mod; void * intrhand; }; #define CODEC_LOCK(sc) mtx_lock(&(sc)->mtx) #define CODEC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define CODEC_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) #define CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int sun8i_codec_probe(device_t dev); static int sun8i_codec_attach(device_t dev); static int sun8i_codec_detach(device_t dev); static int sun8i_codec_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(dev, "Allwinner Codec"); return (BUS_PROBE_DEFAULT); } static int sun8i_codec_attach(device_t dev) { struct sun8i_codec_softc *sc; int error; uint32_t val; struct gpiobus_pin *pa_pin; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, sun8i_codec_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_mod); if (error != 0) { device_printf(dev, "cannot get \"mod\" clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_gate); if (error != 0) { device_printf(dev, "cannot get \"bus\" clock\n"); goto fail; } error = clk_enable(sc->clk_gate); if (error != 0) { device_printf(dev, "cannot enable \"bus\" clock\n"); goto fail; } /* Enable clocks */ val = CODEC_READ(sc, SYSCLK_CTL); val |= AIF1CLK_ENA; val &= ~AIF1CLK_SRC_MASK; val |= AIF1CLK_SRC_PLL; val |= SYSCLK_ENA; val &= ~SYSCLK_SRC; CODEC_WRITE(sc, SYSCLK_CTL, val); CODEC_WRITE(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC); CODEC_WRITE(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC); /* Enable digital parts */ CODEC_WRITE(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA); CODEC_WRITE(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD); /* Set AIF1 to 48 kHz */ val = CODEC_READ(sc, SYS_SR_CTRL); val &= ~AIF1_FS_MASK; val |= AIF_FS_48KHZ; CODEC_WRITE(sc, SYS_SR_CTRL, val); /* Set AIF1 to 16-bit */ val = CODEC_READ(sc, AIF1CLK_CTRL); val &= ~AIF1_WORD_SIZ_MASK; val |= AIF1_WORD_SIZ_16; CODEC_WRITE(sc, AIF1CLK_CTRL, val); /* Enable AIF1 DAC timelot 0 */ val = CODEC_READ(sc, AIF1_DACDAT_CTRL); val |= AIF1_DAC0L_ENA; val |= AIF1_DAC0R_ENA; CODEC_WRITE(sc, AIF1_DACDAT_CTRL, val); /* Enable AIF1 ADC timelot 0 */ val = CODEC_READ(sc, AIF1_ADCDAT_CTRL); val |= AIF1_ADC0L_ENA; val |= AIF1_ADC0R_ENA; CODEC_WRITE(sc, AIF1_ADCDAT_CTRL, val); /* DAC mixer source select */ val = CODEC_READ(sc, DAC_MXR_SRC); val &= ~DACL_MXR_SRC_MASK; val |= DACL_MXR_SRC_AIF1_DAC0L; val &= ~DACR_MXR_SRC_MASK; val |= DACR_MXR_SRC_AIF1_DAC0R; CODEC_WRITE(sc, DAC_MXR_SRC, val); /* ADC mixer source select */ val = CODEC_READ(sc, AIF1_MXR_SRC); val &= ~AIF1L_MXR_SRC_MASK; val |= AIF1L_MXR_SRC_ADC; val &= ~AIF1R_MXR_SRC_MASK; val |= AIF1R_MXR_SRC_ADC; CODEC_WRITE(sc, AIF1_MXR_SRC, val); /* Enable PA power */ /* Unmute PA */ if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios", &pa_pin) == 0) { error = gpio_pin_set_active(pa_pin, 1); if (error != 0) device_printf(dev, "failed to unmute PA\n"); } OF_device_register_xref(OF_xref_from_node(node), dev); return (0); fail: sun8i_codec_detach(dev); return (error); } static int sun8i_codec_detach(device_t dev) { struct sun8i_codec_softc *sc; sc = device_get_softc(dev); if (sc->clk_gate) clk_release(sc->clk_gate); if (sc->clk_mod) clk_release(sc->clk_mod); if (sc->intrhand != NULL) bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); bus_release_resources(dev, sun8i_codec_spec, sc->res); mtx_destroy(&sc->mtx); return (0); } static int sun8i_codec_dai_init(device_t dev, uint32_t format) { struct sun8i_codec_softc *sc; int fmt, pol, clk; uint32_t val; sc = device_get_softc(dev); fmt = AUDIO_DAI_FORMAT_FORMAT(format); pol = AUDIO_DAI_FORMAT_POLARITY(format); clk = AUDIO_DAI_FORMAT_CLOCK(format); val = CODEC_READ(sc, AIF1CLK_CTRL); val &= ~AIF1_DATA_FMT_MASK; switch (fmt) { case AUDIO_DAI_FORMAT_I2S: val |= AIF1_DATA_FMT_I2S; break; case AUDIO_DAI_FORMAT_RJ: val |= AIF1_DATA_FMT_RJ; break; case AUDIO_DAI_FORMAT_LJ: val |= AIF1_DATA_FMT_LJ; break; case AUDIO_DAI_FORMAT_DSPA: case AUDIO_DAI_FORMAT_DSPB: val |= AIF1_DATA_FMT_DSP; break; default: return EINVAL; } val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV); /* Codec LRCK polarity is inverted (datasheet is wrong) */ if (!AUDIO_DAI_POLARITY_INVERTED_FRAME(pol)) val |= AIF1_LRCK_INV; if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol)) val |= AIF1_BCLK_INV; switch (clk) { case AUDIO_DAI_CLOCK_CBM_CFM: val &= ~AIF1_MSTR_MOD; /* codec is master */ break; case AUDIO_DAI_CLOCK_CBS_CFS: val |= AIF1_MSTR_MOD; /* codec is slave */ break; default: return EINVAL; } val &= ~AIF1_LRCK_DIV_MASK; val |= AIF1_LRCK_DIV_64; val &= ~AIF1_BCLK_DIV_MASK; val |= AIF1_BCLK_DIV_16; CODEC_WRITE(sc, AIF1CLK_CTRL, val); return (0); } static int sun8i_codec_dai_trigger(device_t dev, int go, int pcm_dir) { return (0); } static int sun8i_codec_dai_setup_mixer(device_t dev, device_t pcmdev) { /* Do nothing for now */ return (0); } static device_method_t sun8i_codec_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sun8i_codec_probe), DEVMETHOD(device_attach, sun8i_codec_attach), DEVMETHOD(device_detach, sun8i_codec_detach), DEVMETHOD(audio_dai_init, sun8i_codec_dai_init), DEVMETHOD(audio_dai_setup_mixer, sun8i_codec_dai_setup_mixer), DEVMETHOD(audio_dai_trigger, sun8i_codec_dai_trigger), DEVMETHOD_END }; static driver_t sun8i_codec_driver = { "sun8icodec", sun8i_codec_methods, sizeof(struct sun8i_codec_softc), }; DRIVER_MODULE(sun8i_codec, simplebus, sun8i_codec_driver, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/a64/sun50i_a64_acodec.c b/sys/arm/allwinner/a64/sun50i_a64_acodec.c index f99d5385d897..f9ada20ec554 100644 --- a/sys/arm/allwinner/a64/sun50i_a64_acodec.c +++ b/sys/arm/allwinner/a64/sun50i_a64_acodec.c @@ -1,481 +1,481 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Oleksandr Tymoshenko * Copyright (c) 2018 Jared McNeill * * 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 ``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 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 #include #include -#include +#include #include #include "syscon_if.h" #include "opt_snd.h" #include #include #include "audio_dai_if.h" #include "mixer_if.h" #define A64_PR_CFG 0x00 #define A64_AC_PR_RST (1 << 28) #define A64_AC_PR_RW (1 << 24) #define A64_AC_PR_ADDR_MASK (0x1f << 16) #define A64_AC_PR_ADDR(n) (((n) & 0x1f) << 16) #define A64_ACDA_PR_WDAT_MASK (0xff << 8) #define A64_ACDA_PR_WDAT(n) (((n) & 0xff) << 8) #define A64_ACDA_PR_RDAT(n) ((n) & 0xff) #define A64_HP_CTRL 0x00 #define A64_HPPA_EN (1 << 6) #define A64_HPVOL_MASK 0x3f #define A64_HPVOL(n) ((n) & 0x3f) #define A64_OL_MIX_CTRL 0x01 #define A64_LMIXMUTE_LDAC (1 << 1) #define A64_OR_MIX_CTRL 0x02 #define A64_RMIXMUTE_RDAC (1 << 1) #define A64_LINEOUT_CTRL0 0x05 #define A64_LINEOUT_LEFT_EN (1 << 7) #define A64_LINEOUT_RIGHT_EN (1 << 6) #define A64_LINEOUT_EN (A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN) #define A64_LINEOUT_CTRL1 0x06 #define A64_LINEOUT_VOL __BITS(4,0) #define A64_MIC1_CTRL 0x07 #define A64_MIC1G __BITS(6,4) #define A64_MIC1AMPEN (1 << 3) #define A64_MIC1BOOST __BITS(2,0) #define A64_MIC2_CTRL 0x08 #define A64_MIC2_SEL (1 << 7) #define A64_MIC2G_MASK (7 << 4) #define A64_MIC2G(n) (((n) & 7) << 4) #define A64_MIC2AMPEN (1 << 3) #define A64_MIC2BOOST_MASK (7 << 0) #define A64_MIC2BOOST(n) (((n) & 7) << 0) #define A64_LINEIN_CTRL 0x09 #define A64_LINEING __BITS(6,4) #define A64_MIX_DAC_CTRL 0x0a #define A64_DACAREN (1 << 7) #define A64_DACALEN (1 << 6) #define A64_RMIXEN (1 << 5) #define A64_LMIXEN (1 << 4) #define A64_RHPPAMUTE (1 << 3) #define A64_LHPPAMUTE (1 << 2) #define A64_RHPIS (1 << 1) #define A64_LHPIS (1 << 0) #define A64_L_ADCMIX_SRC 0x0b #define A64_R_ADCMIX_SRC 0x0c #define A64_ADCMIX_SRC_MIC1 (1 << 6) #define A64_ADCMIX_SRC_MIC2 (1 << 5) #define A64_ADCMIX_SRC_LINEIN (1 << 2) #define A64_ADCMIX_SRC_OMIXER (1 << 1) #define A64_ADC_CTRL 0x0d #define A64_ADCREN (1 << 7) #define A64_ADCLEN (1 << 6) #define A64_ADCG __BITS(2,0) #define A64_JACK_MIC_CTRL 0x1d #define A64_JACKDETEN (1 << 7) #define A64_INNERRESEN (1 << 6) #define A64_HMICBIASEN (1 << 5) #define A64_AUTOPLEN (1 << 1) #define A64CODEC_MIXER_DEVS ((1 << SOUND_MIXER_VOLUME) | \ (1 << SOUND_MIXER_MIC)) static struct ofw_compat_data compat_data[] = { { "allwinner,sun50i-a64-codec-analog", 1}, { NULL, 0 } }; struct a64codec_softc { device_t dev; struct resource *res; struct mtx mtx; u_int regaddr; /* address for the sysctl */ }; #define A64CODEC_LOCK(sc) mtx_lock(&(sc)->mtx) #define A64CODEC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define A64CODEC_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define A64CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static int a64codec_probe(device_t dev); static int a64codec_attach(device_t dev); static int a64codec_detach(device_t dev); static u_int a64_acodec_pr_read(struct a64codec_softc *sc, u_int addr) { uint32_t val; /* Read current value */ val = A64CODEC_READ(sc, A64_PR_CFG); /* De-assert reset */ val |= A64_AC_PR_RST; A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Read mode */ val &= ~A64_AC_PR_RW; A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Set address */ val &= ~A64_AC_PR_ADDR_MASK; val |= A64_AC_PR_ADDR(addr); A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Read data */ val = A64CODEC_READ(sc, A64_PR_CFG); return A64_ACDA_PR_RDAT(val); } static void a64_acodec_pr_write(struct a64codec_softc *sc, u_int addr, u_int data) { uint32_t val; /* Read current value */ val = A64CODEC_READ(sc, A64_PR_CFG); /* De-assert reset */ val |= A64_AC_PR_RST; A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Set address */ val &= ~A64_AC_PR_ADDR_MASK; val |= A64_AC_PR_ADDR(addr); A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Write data */ val &= ~A64_ACDA_PR_WDAT_MASK; val |= A64_ACDA_PR_WDAT(data); A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Write mode */ val |= A64_AC_PR_RW; A64CODEC_WRITE(sc, A64_PR_CFG, val); /* Clear write mode */ val &= ~A64_AC_PR_RW; A64CODEC_WRITE(sc, A64_PR_CFG, val); } static void a64_acodec_pr_set_clear(struct a64codec_softc *sc, u_int addr, u_int set, u_int clr) { u_int old, new; old = a64_acodec_pr_read(sc, addr); new = set | (old & ~clr); a64_acodec_pr_write(sc, addr, new); } static int a64codec_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(dev, "Allwinner A64 Analog Codec"); return (BUS_PROBE_DEFAULT); } static int a64codec_attach(device_t dev) { struct a64codec_softc *sc; int error, rid; phandle_t node; regulator_t reg; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "cannot allocate resource for device\n"); error = ENXIO; goto fail; } if (regulator_get_by_ofw_property(dev, 0, "cpvdd-supply", ®) == 0) { error = regulator_enable(reg); if (error != 0) { device_printf(dev, "cannot enable PHY regulator\n"); goto fail; } } /* Right & Left Headphone PA enable */ a64_acodec_pr_set_clear(sc, A64_HP_CTRL, A64_HPPA_EN, 0); /* Microphone BIAS enable */ a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL, A64_HMICBIASEN | A64_INNERRESEN, 0); /* Unmute DAC to output mixer */ a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL, A64_LMIXMUTE_LDAC, 0); a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL, A64_RMIXMUTE_RDAC, 0); /* For now we work only with headphones */ a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, 0, A64_LINEOUT_EN); a64_acodec_pr_set_clear(sc, A64_HP_CTRL, A64_HPPA_EN, 0); u_int val = a64_acodec_pr_read(sc, A64_HP_CTRL); val &= ~(0x3f); val |= 0x25; a64_acodec_pr_write(sc, A64_HP_CTRL, val); a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, A64_MIC2AMPEN | A64_MIC2_SEL | A64_MIC2G(0x3) | A64_MIC2BOOST(0x4), A64_MIC2G_MASK | A64_MIC2BOOST_MASK); a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC, A64_ADCMIX_SRC_MIC2); a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC, A64_ADCMIX_SRC_MIC2); /* Max out MIC2 gain */ val = a64_acodec_pr_read(sc, A64_MIC2_CTRL); val &= ~(0x7); val |= (0x7); val &= ~(7 << 4); val |= (7 << 4); a64_acodec_pr_write(sc, A64_MIC2_CTRL, val); node = ofw_bus_get_node(dev); OF_device_register_xref(OF_xref_from_node(node), dev); return (0); fail: a64codec_detach(dev); return (error); } static int a64codec_detach(device_t dev) { struct a64codec_softc *sc; sc = device_get_softc(dev); if (sc->res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res); mtx_destroy(&sc->mtx); return (0); } static int a64codec_mixer_init(struct snd_mixer *m) { mix_setdevs(m, A64CODEC_MIXER_DEVS); return (0); } static int a64codec_mixer_uninit(struct snd_mixer *m) { return (0); } static int a64codec_mixer_reinit(struct snd_mixer *m) { return (0); } static int a64codec_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct a64codec_softc *sc; struct mtx *mixer_lock; uint8_t do_unlock; u_int val; sc = device_get_softc(mix_getdevinfo(m)); mixer_lock = mixer_get_lock(m); if (mtx_owned(mixer_lock)) { do_unlock = 0; } else { do_unlock = 1; mtx_lock(mixer_lock); } right = left; A64CODEC_LOCK(sc); switch(dev) { case SOUND_MIXER_VOLUME: val = a64_acodec_pr_read(sc, A64_HP_CTRL); val &= ~(A64_HPVOL_MASK); val |= A64_HPVOL(left * 63 / 100); a64_acodec_pr_write(sc, A64_HP_CTRL, val); break; case SOUND_MIXER_MIC: val = a64_acodec_pr_read(sc, A64_MIC2_CTRL); val &= ~(A64_MIC2BOOST_MASK); val |= A64_MIC2BOOST(left * 7 / 100); a64_acodec_pr_write(sc, A64_MIC2_CTRL, val); break; default: break; } A64CODEC_UNLOCK(sc); if (do_unlock) { mtx_unlock(mixer_lock); } return (left | (right << 8)); } static unsigned a64codec_mixer_setrecsrc(struct snd_mixer *m, unsigned src) { return (0); } static kobj_method_t a64codec_mixer_methods[] = { KOBJMETHOD(mixer_init, a64codec_mixer_init), KOBJMETHOD(mixer_uninit, a64codec_mixer_uninit), KOBJMETHOD(mixer_reinit, a64codec_mixer_reinit), KOBJMETHOD(mixer_set, a64codec_mixer_set), KOBJMETHOD(mixer_setrecsrc, a64codec_mixer_setrecsrc), KOBJMETHOD_END }; MIXER_DECLARE(a64codec_mixer); static int a64codec_dai_init(device_t dev, uint32_t format) { return (0); } static int a64codec_dai_trigger(device_t dev, int go, int pcm_dir) { struct a64codec_softc *sc = device_get_softc(dev); if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC)) return (EINVAL); switch (go) { case PCMTRIG_START: if (pcm_dir == PCMDIR_PLAY) { /* Enable DAC analog l/r channels, HP PA, and output mixer */ a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | A64_RHPPAMUTE | A64_LHPPAMUTE, 0); } else if (pcm_dir == PCMDIR_REC) { /* Enable ADC analog l/r channels */ a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, A64_ADCREN | A64_ADCLEN, 0); } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: if (pcm_dir == PCMDIR_PLAY) { /* Disable DAC analog l/r channels, HP PA, and output mixer */ a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL, 0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN | A64_RHPPAMUTE | A64_LHPPAMUTE); } else if (pcm_dir == PCMDIR_REC) { /* Disable ADC analog l/r channels */ a64_acodec_pr_set_clear(sc, A64_ADC_CTRL, 0, A64_ADCREN | A64_ADCLEN); } break; } return (0); } static int a64codec_dai_setup_mixer(device_t dev, device_t pcmdev) { mixer_init(pcmdev, &a64codec_mixer_class, dev); return (0); } static device_method_t a64codec_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a64codec_probe), DEVMETHOD(device_attach, a64codec_attach), DEVMETHOD(device_detach, a64codec_detach), DEVMETHOD(audio_dai_init, a64codec_dai_init), DEVMETHOD(audio_dai_setup_mixer, a64codec_dai_setup_mixer), DEVMETHOD(audio_dai_trigger, a64codec_dai_trigger), DEVMETHOD_END }; static driver_t a64codec_driver = { "a64codec", a64codec_methods, sizeof(struct a64codec_softc), }; DRIVER_MODULE(a64codec, simplebus, a64codec_driver, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/aw_cir.c b/sys/arm/allwinner/aw_cir.c index 7e9fdfca80bf..5ebb57f48040 100644 --- a/sys/arm/allwinner/aw_cir.c +++ b/sys/arm/allwinner/aw_cir.c @@ -1,558 +1,558 @@ /*- * Copyright (c) 2016 Ganbold Tsagaankhuu * 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. */ /* * Allwinner Consumer IR controller */ #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #define READ(_sc, _r) bus_read_4((_sc)->res[0], (_r)) #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res[0], (_r), (_v)) /* IR Control */ #define AW_IR_CTL 0x00 /* Global Enable */ #define AW_IR_CTL_GEN (1 << 0) /* RX enable */ #define AW_IR_CTL_RXEN (1 << 1) /* CIR mode enable */ #define AW_IR_CTL_MD (1 << 4) | (1 << 5) /* RX Config Reg */ #define AW_IR_RXCTL 0x10 /* Pulse Polarity Invert flag */ #define AW_IR_RXCTL_RPPI (1 << 2) /* RX Data */ #define AW_IR_RXFIFO 0x20 /* RX Interrupt Control */ #define AW_IR_RXINT 0x2C /* RX FIFO Overflow */ #define AW_IR_RXINT_ROI_EN (1 << 0) /* RX Packet End */ #define AW_IR_RXINT_RPEI_EN (1 << 1) /* RX FIFO Data Available */ #define AW_IR_RXINT_RAI_EN (1 << 4) /* RX FIFO available byte level */ #define AW_IR_RXINT_RAL(val) ((val) << 8) /* RX Interrupt Status Reg */ #define AW_IR_RXSTA 0x30 /* RX FIFO Get Available Counter */ #define AW_IR_RXSTA_COUNTER(val) (((val) >> 8) & (sc->fifo_size * 2 - 1)) /* Clear all interrupt status */ #define AW_IR_RXSTA_CLEARALL 0xff /* IR Sample Configure Reg */ #define AW_IR_CIR 0x34 /* * Frequency sample: 23437.5Hz (Cycle: 42.7us) * Pulse of NEC Remote > 560us */ /* Filter Threshold = 8 * 42.7 = ~341us < 500us */ #define AW_IR_RXFILT_VAL (((8) & 0x3f) << 2) /* Idle Threshold = (2 + 1) * 128 * 42.7 = ~16.4ms > 9ms */ #define AW_IR_RXIDLE_VAL (((2) & 0xff) << 8) /* Bit 15 - value (pulse/space) */ #define VAL_MASK 0x80 /* Bits 0:14 - sample duration */ #define PERIOD_MASK 0x7f /* Clock rate for IR0 or IR1 clock in CIR mode */ #define AW_IR_BASE_CLK 3000000 /* Frequency sample 3MHz/64 = 46875Hz (21.3us) */ #define AW_IR_SAMPLE_64 (0 << 0) /* Frequency sample 3MHz/128 = 23437.5Hz (42.7us) */ #define AW_IR_SAMPLE_128 (1 << 0) #define AW_IR_ERROR_CODE 0xffffffff #define AW_IR_REPEAT_CODE 0x0 /* 80 * 42.7 = ~3.4ms, Lead1(4.5ms) > AW_IR_L1_MIN */ #define AW_IR_L1_MIN 80 /* 40 * 42.7 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms) > AW_IR_L0_MIN */ #define AW_IR_L0_MIN 40 /* 26 * 42.7 = ~1109us ~= 561 * 2, Pulse < AW_IR_PMAX */ #define AW_IR_PMAX 26 /* 26 * 42.7 = ~1109us ~= 561 * 2, D1 > AW_IR_DMID, D0 <= AW_IR_DMID */ #define AW_IR_DMID 26 /* 53 * 42.7 = ~2263us ~= 561 * 4, D < AW_IR_DMAX */ #define AW_IR_DMAX 53 /* Active Thresholds */ #define AW_IR_ACTIVE_T_VAL AW_IR_L1_MIN #define AW_IR_ACTIVE_T (((AW_IR_ACTIVE_T_VAL - 1) & 0xff) << 16) #define AW_IR_ACTIVE_T_C_VAL 0 #define AW_IR_ACTIVE_T_C ((AW_IR_ACTIVE_T_C_VAL & 0xff) << 23) /* Code masks */ #define CODE_MASK 0x00ff00ff #define INV_CODE_MASK 0xff00ff00 #define VALID_CODE_MASK 0x00ff0000 enum { A10_IR = 1, A13_IR, A31_IR, }; #define AW_IR_RAW_BUF_SIZE 128 SYSCTL_NODE(_hw, OID_AUTO, aw_cir, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "aw_cir driver"); static int aw_cir_debug = 0; SYSCTL_INT(_hw_aw_cir, OID_AUTO, debug, CTLFLAG_RWTUN, &aw_cir_debug, 0, "Debug 1=on 0=off"); struct aw_ir_softc { device_t dev; struct resource *res[2]; void * intrhand; int fifo_size; int dcnt; /* Packet Count */ unsigned char buf[AW_IR_RAW_BUF_SIZE]; struct evdev_dev *sc_evdev; }; static struct resource_spec aw_ir_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-ir", A10_IR }, { "allwinner,sun5i-a13-ir", A13_IR }, { "allwinner,sun6i-a31-ir", A31_IR }, { NULL, 0 } }; static void aw_ir_buf_reset(struct aw_ir_softc *sc) { sc->dcnt = 0; } static void aw_ir_buf_write(struct aw_ir_softc *sc, unsigned char data) { if (sc->dcnt < AW_IR_RAW_BUF_SIZE) sc->buf[sc->dcnt++] = data; else if (bootverbose) device_printf(sc->dev, "IR RX Buffer Full!\n"); } static int aw_ir_buf_full(struct aw_ir_softc *sc) { return (sc->dcnt >= AW_IR_RAW_BUF_SIZE); } static unsigned char aw_ir_read_data(struct aw_ir_softc *sc) { return (unsigned char)(READ(sc, AW_IR_RXFIFO) & 0xff); } static unsigned long aw_ir_decode_packets(struct aw_ir_softc *sc) { unsigned int len, code; unsigned int active_delay; unsigned char val, last; int i, bitcount; if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "sc->dcnt = %d\n", sc->dcnt); /* Find Lead 1 (bit separator) */ active_delay = AW_IR_ACTIVE_T_VAL * (AW_IR_ACTIVE_T_C_VAL != 0 ? 128 : 1); len = active_delay; if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "Initial len: %d\n", len); for (i = 0; i < sc->dcnt; i++) { val = sc->buf[i]; if (val & VAL_MASK) len += (val & PERIOD_MASK) + 1; else { if (len > AW_IR_L1_MIN) break; len = 0; } } if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "len = %d\n", len); if ((val & VAL_MASK) || (len <= AW_IR_L1_MIN)) { if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "Bit separator error\n"); goto error_code; } /* Find Lead 0 (bit length) */ len = 0; for (; i < sc->dcnt; i++) { val = sc->buf[i]; if (val & VAL_MASK) { if(len > AW_IR_L0_MIN) break; len = 0; } else len += (val & PERIOD_MASK) + 1; } if ((!(val & VAL_MASK)) || (len <= AW_IR_L0_MIN)) { if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "Bit length error\n"); goto error_code; } /* Start decoding */ code = 0; bitcount = 0; last = 1; len = 0; for (; i < sc->dcnt; i++) { val = sc->buf[i]; if (last) { if (val & VAL_MASK) len += (val & PERIOD_MASK) + 1; else { if (len > AW_IR_PMAX) { if (bootverbose) device_printf(sc->dev, "Pulse error, len=%d\n", len); goto error_code; } last = 0; len = (val & PERIOD_MASK) + 1; } } else { if (val & VAL_MASK) { if (len > AW_IR_DMAX) { if (bootverbose) device_printf(sc->dev, "Distance error, len=%d\n", len); goto error_code; } else { if (len > AW_IR_DMID) { /* Decode */ code |= 1 << bitcount; } bitcount++; if (bitcount == 32) break; /* Finish decoding */ } last = 1; len = (val & PERIOD_MASK) + 1; } else len += (val & PERIOD_MASK) + 1; } } return (code); error_code: return (AW_IR_ERROR_CODE); } static int aw_ir_validate_code(unsigned long code) { unsigned long v1, v2; /* Don't check address */ v1 = code & CODE_MASK; v2 = (code & INV_CODE_MASK) >> 8; if (((v1 ^ v2) & VALID_CODE_MASK) == VALID_CODE_MASK) return (0); /* valid */ else return (1); /* invalid */ } static void aw_ir_intr(void *arg) { struct aw_ir_softc *sc; uint32_t val; int i, dcnt; unsigned long ir_code; int stat; sc = (struct aw_ir_softc *)arg; /* Read RX interrupt status */ val = READ(sc, AW_IR_RXSTA); if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "RX interrupt status: %x\n", val); /* Clean all pending interrupt statuses */ WRITE(sc, AW_IR_RXSTA, val | AW_IR_RXSTA_CLEARALL); /* When Rx FIFO Data available or Packet end */ if (val & (AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RPEI_EN)) { if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "RX FIFO Data available or Packet end\n"); /* Get available message count in RX FIFO */ dcnt = AW_IR_RXSTA_COUNTER(val); /* Read FIFO */ for (i = 0; i < dcnt; i++) { if (aw_ir_buf_full(sc)) { if (bootverbose) device_printf(sc->dev, "raw buffer full\n"); break; } else aw_ir_buf_write(sc, aw_ir_read_data(sc)); } } if (val & AW_IR_RXINT_RPEI_EN) { /* RX Packet end */ if (bootverbose && __predict_false(aw_cir_debug) != 0) device_printf(sc->dev, "RX Packet end\n"); ir_code = aw_ir_decode_packets(sc); stat = aw_ir_validate_code(ir_code); if (stat == 0) { evdev_push_event(sc->sc_evdev, EV_MSC, MSC_SCAN, ir_code); evdev_sync(sc->sc_evdev); } if (bootverbose && __predict_false(aw_cir_debug) != 0) { device_printf(sc->dev, "Final IR code: %lx\n", ir_code); device_printf(sc->dev, "IR code status: %d\n", stat); } aw_ir_buf_reset(sc); } if (val & AW_IR_RXINT_ROI_EN) { /* RX FIFO overflow */ if (bootverbose) device_printf(sc->dev, "RX FIFO overflow\n"); /* Flush raw buffer */ aw_ir_buf_reset(sc); } } static int aw_ir_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner CIR controller"); return (BUS_PROBE_DEFAULT); } static int aw_ir_attach(device_t dev) { struct aw_ir_softc *sc; hwreset_t rst_apb; clk_t clk_ir, clk_gate; int err; uint32_t val = 0; clk_ir = clk_gate = NULL; rst_apb = NULL; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, aw_ir_spec, sc->res) != 0) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A10_IR: sc->fifo_size = 16; break; case A13_IR: case A31_IR: sc->fifo_size = 64; break; } /* De-assert reset */ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst_apb) == 0) { err = hwreset_deassert(rst_apb); if (err != 0) { device_printf(dev, "cannot de-assert reset\n"); goto error; } } /* Reset buffer */ aw_ir_buf_reset(sc); /* Get clocks and enable them */ err = clk_get_by_ofw_name(dev, 0, "apb", &clk_gate); if (err != 0) { device_printf(dev, "Cannot get gate clock\n"); goto error; } err = clk_get_by_ofw_name(dev, 0, "ir", &clk_ir); if (err != 0) { device_printf(dev, "Cannot get IR clock\n"); goto error; } /* Set clock rate */ err = clk_set_freq(clk_ir, AW_IR_BASE_CLK, 0); if (err != 0) { device_printf(dev, "cannot set IR clock rate\n"); goto error; } /* Enable clocks */ err = clk_enable(clk_gate); if (err != 0) { device_printf(dev, "Cannot enable clk gate\n"); goto error; } err = clk_enable(clk_ir); if (err != 0) { device_printf(dev, "Cannot enable IR clock\n"); goto error; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_ir_intr, sc, &sc->intrhand)) { bus_release_resources(dev, aw_ir_spec, sc->res); device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto error; } /* Enable CIR Mode */ WRITE(sc, AW_IR_CTL, AW_IR_CTL_MD); /* * Set clock sample, filter, idle thresholds. * Frequency sample = 3MHz/128 = 23437.5Hz (42.7us) */ val = AW_IR_SAMPLE_128; val |= (AW_IR_RXFILT_VAL | AW_IR_RXIDLE_VAL); val |= (AW_IR_ACTIVE_T | AW_IR_ACTIVE_T_C); WRITE(sc, AW_IR_CIR, val); /* Invert Input Signal */ WRITE(sc, AW_IR_RXCTL, AW_IR_RXCTL_RPPI); /* Clear All RX Interrupt Status */ WRITE(sc, AW_IR_RXSTA, AW_IR_RXSTA_CLEARALL); /* * Enable RX interrupt in case of overflow, packet end * and FIFO available. * RX FIFO Threshold = FIFO size / 2 */ WRITE(sc, AW_IR_RXINT, AW_IR_RXINT_ROI_EN | AW_IR_RXINT_RPEI_EN | AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RAL((sc->fifo_size >> 1) - 1)); /* Enable IR Module */ val = READ(sc, AW_IR_CTL); WRITE(sc, AW_IR_CTL, val | AW_IR_CTL_GEN | AW_IR_CTL_RXEN); sc->sc_evdev = evdev_alloc(); evdev_set_name(sc->sc_evdev, device_get_desc(sc->dev)); evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->dev)); evdev_set_id(sc->sc_evdev, BUS_HOST, 0, 0, 0); evdev_support_event(sc->sc_evdev, EV_SYN); evdev_support_event(sc->sc_evdev, EV_MSC); evdev_support_msc(sc->sc_evdev, MSC_SCAN); err = evdev_register(sc->sc_evdev); if (err) { device_printf(dev, "failed to register evdev: error=%d\n", err); goto error; } return (0); error: if (clk_gate != NULL) clk_release(clk_gate); if (clk_ir != NULL) clk_release(clk_ir); if (rst_apb != NULL) hwreset_release(rst_apb); evdev_free(sc->sc_evdev); sc->sc_evdev = NULL; /* Avoid double free */ bus_release_resources(dev, aw_ir_spec, sc->res); return (ENXIO); } static device_method_t aw_ir_methods[] = { DEVMETHOD(device_probe, aw_ir_probe), DEVMETHOD(device_attach, aw_ir_attach), DEVMETHOD_END }; static driver_t aw_ir_driver = { "aw_ir", aw_ir_methods, sizeof(struct aw_ir_softc), }; DRIVER_MODULE(aw_ir, simplebus, aw_ir_driver, 0, 0); MODULE_DEPEND(aw_ir, evdev, 1, 1, 1); diff --git a/sys/arm/allwinner/aw_gpio.c b/sys/arm/allwinner/aw_gpio.c index b77972ac0187..f72dbf88099f 100644 --- a/sys/arm/allwinner/aw_gpio.c +++ b/sys/arm/allwinner/aw_gpio.c @@ -1,1483 +1,1483 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Ganbold Tsagaankhuu * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. * 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 #include #include #include #include #include #include #include #include #include -#include +#include #include #if defined(__aarch64__) #include "opt_soc.h" #endif #include "pic_if.h" #include "gpio_if.h" #define AW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); #define AW_GPIO_INTR_CAPS (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \ GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) #define AW_GPIO_NONE 0 #define AW_GPIO_PULLUP 1 #define AW_GPIO_PULLDOWN 2 #define AW_GPIO_INPUT 0 #define AW_GPIO_OUTPUT 1 #define AW_GPIO_DRV_MASK 0x3 #define AW_GPIO_PUD_MASK 0x3 #define AW_PINCTRL 1 #define AW_R_PINCTRL 2 struct aw_gpio_conf { struct allwinner_padconf *padconf; const char *banks; }; /* Defined in aw_padconf.c */ #ifdef SOC_ALLWINNER_A10 extern struct allwinner_padconf a10_padconf; struct aw_gpio_conf a10_gpio_conf = { .padconf = &a10_padconf, .banks = "abcdefghi", }; #endif /* Defined in a13_padconf.c */ #ifdef SOC_ALLWINNER_A13 extern struct allwinner_padconf a13_padconf; struct aw_gpio_conf a13_gpio_conf = { .padconf = &a13_padconf, .banks = "bcdefg", }; #endif /* Defined in a20_padconf.c */ #ifdef SOC_ALLWINNER_A20 extern struct allwinner_padconf a20_padconf; struct aw_gpio_conf a20_gpio_conf = { .padconf = &a20_padconf, .banks = "abcdefghi", }; #endif /* Defined in a31_padconf.c */ #ifdef SOC_ALLWINNER_A31 extern struct allwinner_padconf a31_padconf; struct aw_gpio_conf a31_gpio_conf = { .padconf = &a31_padconf, .banks = "abcdefgh", }; #endif /* Defined in a31s_padconf.c */ #ifdef SOC_ALLWINNER_A31S extern struct allwinner_padconf a31s_padconf; struct aw_gpio_conf a31s_gpio_conf = { .padconf = &a31s_padconf, .banks = "abcdefgh", }; #endif #if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) extern struct allwinner_padconf a31_r_padconf; struct aw_gpio_conf a31_r_gpio_conf = { .padconf = &a31_r_padconf, .banks = "lm", }; #endif /* Defined in a33_padconf.c */ #ifdef SOC_ALLWINNER_A33 extern struct allwinner_padconf a33_padconf; struct aw_gpio_conf a33_gpio_conf = { .padconf = &a33_padconf, .banks = "bcdefgh", }; #endif /* Defined in h3_padconf.c */ #if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5) extern struct allwinner_padconf h3_padconf; extern struct allwinner_padconf h3_r_padconf; struct aw_gpio_conf h3_gpio_conf = { .padconf = &h3_padconf, .banks = "acdefg", }; struct aw_gpio_conf h3_r_gpio_conf = { .padconf = &h3_r_padconf, .banks = "l", }; #endif /* Defined in a83t_padconf.c */ #ifdef SOC_ALLWINNER_A83T extern struct allwinner_padconf a83t_padconf; extern struct allwinner_padconf a83t_r_padconf; struct aw_gpio_conf a83t_gpio_conf = { .padconf = &a83t_padconf, .banks = "bcdefgh" }; struct aw_gpio_conf a83t_r_gpio_conf = { .padconf = &a83t_r_padconf, .banks = "l", }; #endif /* Defined in a64_padconf.c */ #ifdef SOC_ALLWINNER_A64 extern struct allwinner_padconf a64_padconf; extern struct allwinner_padconf a64_r_padconf; struct aw_gpio_conf a64_gpio_conf = { .padconf = &a64_padconf, .banks = "bcdefgh", }; struct aw_gpio_conf a64_r_gpio_conf = { .padconf = &a64_r_padconf, .banks = "l", }; #endif /* Defined in h6_padconf.c */ #ifdef SOC_ALLWINNER_H6 extern struct allwinner_padconf h6_padconf; extern struct allwinner_padconf h6_r_padconf; struct aw_gpio_conf h6_gpio_conf = { .padconf = &h6_padconf, .banks = "cdfgh", }; struct aw_gpio_conf h6_r_gpio_conf = { .padconf = &h6_r_padconf, .banks = "lm", }; #endif static struct ofw_compat_data compat_data[] = { #ifdef SOC_ALLWINNER_A10 {"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A13 {"allwinner,sun5i-a13-pinctrl", (uintptr_t)&a13_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A20 {"allwinner,sun7i-a20-pinctrl", (uintptr_t)&a20_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A31 {"allwinner,sun6i-a31-pinctrl", (uintptr_t)&a31_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A31S {"allwinner,sun6i-a31s-pinctrl", (uintptr_t)&a31s_gpio_conf}, #endif #if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) {"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A33 {"allwinner,sun6i-a33-pinctrl", (uintptr_t)&a33_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A83T {"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_gpio_conf}, {"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_gpio_conf}, #endif #if defined(SOC_ALLWINNER_H3) || defined(SOC_ALLWINNER_H5) {"allwinner,sun8i-h3-pinctrl", (uintptr_t)&h3_gpio_conf}, {"allwinner,sun50i-h5-pinctrl", (uintptr_t)&h3_gpio_conf}, {"allwinner,sun8i-h3-r-pinctrl", (uintptr_t)&h3_r_gpio_conf}, #endif #ifdef SOC_ALLWINNER_A64 {"allwinner,sun50i-a64-pinctrl", (uintptr_t)&a64_gpio_conf}, {"allwinner,sun50i-a64-r-pinctrl", (uintptr_t)&a64_r_gpio_conf}, #endif #ifdef SOC_ALLWINNER_H6 {"allwinner,sun50i-h6-pinctrl", (uintptr_t)&h6_gpio_conf}, {"allwinner,sun50i-h6-r-pinctrl", (uintptr_t)&h6_r_gpio_conf}, #endif {NULL, 0} }; struct clk_list { TAILQ_ENTRY(clk_list) next; clk_t clk; }; struct gpio_irqsrc { struct intr_irqsrc isrc; u_int irq; uint32_t mode; uint32_t pin; uint32_t bank; uint32_t intnum; uint32_t intfunc; uint32_t oldfunc; bool enabled; }; #define AW_GPIO_MEMRES 0 #define AW_GPIO_IRQRES 1 #define AW_GPIO_RESSZ 2 struct aw_gpio_softc { device_t sc_dev; device_t sc_busdev; struct resource * sc_res[AW_GPIO_RESSZ]; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; void * sc_intrhand; struct aw_gpio_conf *conf; TAILQ_HEAD(, clk_list) clk_list; struct gpio_irqsrc *gpio_pic_irqsrc; int nirqs; }; static struct resource_spec aw_gpio_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; #define AW_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define AW_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define AW_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define AW_GPIO_GP_CFG(_bank, _idx) 0x00 + ((_bank) * 0x24) + ((_idx) << 2) #define AW_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24) #define AW_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2) #define AW_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 2) #define AW_GPIO_GP_INT_BASE(_bank) (0x200 + 0x20 * _bank) #define AW_GPIO_GP_INT_CFG(_bank, _pin) (AW_GPIO_GP_INT_BASE(_bank) + (0x4 * ((_pin) / 8))) #define AW_GPIO_GP_INT_CTL(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x10) #define AW_GPIO_GP_INT_STA(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x14) #define AW_GPIO_GP_INT_DEB(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x18) #define AW_GPIO_INT_EDGE_POSITIVE 0x0 #define AW_GPIO_INT_EDGE_NEGATIVE 0x1 #define AW_GPIO_INT_LEVEL_HIGH 0x2 #define AW_GPIO_INT_LEVEL_LOW 0x3 #define AW_GPIO_INT_EDGE_BOTH 0x4 static char *aw_gpio_parse_function(phandle_t node); static const char **aw_gpio_parse_pins(phandle_t node, int *pins_nb); static uint32_t aw_gpio_parse_bias(phandle_t node); static int aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive); static int aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value); static int aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); static int aw_gpio_pin_get_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int *value); static int aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int value); static void aw_gpio_intr(void *arg); static void aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc); static void aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc); static void aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc); static int aw_gpio_register_isrcs(struct aw_gpio_softc *sc); #define AW_GPIO_WRITE(_sc, _off, _val) \ bus_write_4((_sc)->sc_res[AW_GPIO_MEMRES], _off, _val) #define AW_GPIO_READ(_sc, _off) \ bus_read_4((_sc)->sc_res[AW_GPIO_MEMRES], _off) static uint32_t aw_gpio_get_function(struct aw_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); if (pin > sc->conf->padconf->npins) return (0); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); func = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3)); return ((func >> offset) & 0x7); } static int aw_gpio_set_function(struct aw_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Check if the function exists in the padconf data */ if (sc->conf->padconf->pins[pin].functions[f] == NULL) return (EINVAL); /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); data = AW_GPIO_READ(sc, AW_GPIO_GP_CFG(bank, pin >> 3)); data &= ~(7 << offset); data |= (f << offset); AW_GPIO_WRITE(sc, AW_GPIO_GP_CFG(bank, pin >> 3), data); return (0); } static uint32_t aw_gpio_get_pud(struct aw_gpio_softc *sc, uint32_t pin) { uint32_t bank, offset, val; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4)); return ((val >> offset) & AW_GPIO_PUD_MASK); } static void aw_gpio_set_pud(struct aw_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset, val; if (aw_gpio_get_pud(sc, pin) == state) return; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = AW_GPIO_READ(sc, AW_GPIO_GP_PUL(bank, pin >> 4)); val &= ~(AW_GPIO_PUD_MASK << offset); val |= (state << offset); AW_GPIO_WRITE(sc, AW_GPIO_GP_PUL(bank, pin >> 4), val); } static uint32_t aw_gpio_get_drv(struct aw_gpio_softc *sc, uint32_t pin) { uint32_t bank, offset, val; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4)); return ((val >> offset) & AW_GPIO_DRV_MASK); } static void aw_gpio_set_drv(struct aw_gpio_softc *sc, uint32_t pin, uint32_t drive) { uint32_t bank, offset, val; if (aw_gpio_get_drv(sc, pin) == drive) return; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = AW_GPIO_READ(sc, AW_GPIO_GP_DRV(bank, pin >> 4)); val &= ~(AW_GPIO_DRV_MASK << offset); val |= (drive << offset); AW_GPIO_WRITE(sc, AW_GPIO_GP_DRV(bank, pin >> 4), val); } static int aw_gpio_pin_configure(struct aw_gpio_softc *sc, uint32_t pin, uint32_t flags) { u_int val; int err = 0; /* Must be called with lock held. */ AW_GPIO_LOCK_ASSERT(sc); if (pin > sc->conf->padconf->npins) return (EINVAL); /* Manage input/output. */ if (flags & GPIO_PIN_INPUT) { err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT); } else if ((flags & GPIO_PIN_OUTPUT) && aw_gpio_get_function(sc, pin) != AW_GPIO_OUTPUT) { if (flags & GPIO_PIN_PRESET_LOW) { aw_gpio_pin_set_locked(sc, pin, 0); } else if (flags & GPIO_PIN_PRESET_HIGH) { aw_gpio_pin_set_locked(sc, pin, 1); } else { /* Read the pin and preset output to current state. */ err = aw_gpio_set_function(sc, pin, AW_GPIO_INPUT); if (err == 0) { aw_gpio_pin_get_locked(sc, pin, &val); aw_gpio_pin_set_locked(sc, pin, val); } } if (err == 0) err = aw_gpio_set_function(sc, pin, AW_GPIO_OUTPUT); } if (err) return (err); /* Manage Pull-up/pull-down. */ if (flags & GPIO_PIN_PULLUP) aw_gpio_set_pud(sc, pin, AW_GPIO_PULLUP); else if (flags & GPIO_PIN_PULLDOWN) aw_gpio_set_pud(sc, pin, AW_GPIO_PULLDOWN); else aw_gpio_set_pud(sc, pin, AW_GPIO_NONE); return (0); } static device_t aw_gpio_get_bus(device_t dev) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int aw_gpio_pin_max(device_t dev, int *maxpin) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); *maxpin = sc->conf->padconf->npins - 1; return (0); } static int aw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->conf->padconf->npins) return (EINVAL); *caps = AW_GPIO_DEFAULT_CAPS; if (sc->conf->padconf->pins[pin].eint_func != 0) *caps |= AW_GPIO_INTR_CAPS; return (0); } static int aw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct aw_gpio_softc *sc; uint32_t func; uint32_t pud; sc = device_get_softc(dev); if (pin >= sc->conf->padconf->npins) return (EINVAL); AW_GPIO_LOCK(sc); func = aw_gpio_get_function(sc, pin); switch (func) { case AW_GPIO_INPUT: *flags = GPIO_PIN_INPUT; break; case AW_GPIO_OUTPUT: *flags = GPIO_PIN_OUTPUT; break; default: *flags = 0; break; } pud = aw_gpio_get_pud(sc, pin); switch (pud) { case AW_GPIO_PULLDOWN: *flags |= GPIO_PIN_PULLDOWN; break; case AW_GPIO_PULLUP: *flags |= GPIO_PIN_PULLUP; break; default: break; } AW_GPIO_UNLOCK(sc); return (0); } static int aw_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->conf->padconf->npins) return (EINVAL); snprintf(name, GPIOMAXNAME - 1, "%s", sc->conf->padconf->pins[pin].name); name[GPIOMAXNAME - 1] = '\0'; return (0); } static int aw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct aw_gpio_softc *sc; int err; sc = device_get_softc(dev); if (pin > sc->conf->padconf->npins) return (EINVAL); AW_GPIO_LOCK(sc); err = aw_gpio_pin_configure(sc, pin, flags); AW_GPIO_UNLOCK(sc); return (err); } static int aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int value) { uint32_t bank, data; AW_GPIO_LOCK_ASSERT(sc); if (pin > sc->conf->padconf->npins) return (EINVAL); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank)); if (value) data |= (1 << pin); else data &= ~(1 << pin); AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data); return (0); } static int aw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct aw_gpio_softc *sc; int ret; sc = device_get_softc(dev); AW_GPIO_LOCK(sc); ret = aw_gpio_pin_set_locked(sc, pin, value); AW_GPIO_UNLOCK(sc); return (ret); } static int aw_gpio_pin_get_locked(struct aw_gpio_softc *sc,uint32_t pin, unsigned int *val) { uint32_t bank, reg_data; AW_GPIO_LOCK_ASSERT(sc); if (pin > sc->conf->padconf->npins) return (EINVAL); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; reg_data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank)); *val = (reg_data & (1 << pin)) ? 1 : 0; return (0); } static char * aw_gpio_parse_function(phandle_t node) { char *function; if (OF_getprop_alloc(node, "function", (void **)&function) != -1) return (function); if (OF_getprop_alloc(node, "allwinner,function", (void **)&function) != -1) return (function); return (NULL); } static const char ** aw_gpio_parse_pins(phandle_t node, int *pins_nb) { const char **pinlist; *pins_nb = ofw_bus_string_list_to_array(node, "pins", &pinlist); if (*pins_nb > 0) return (pinlist); *pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins", &pinlist); if (*pins_nb > 0) return (pinlist); return (NULL); } static uint32_t aw_gpio_parse_bias(phandle_t node) { uint32_t bias; if (OF_getencprop(node, "pull", &bias, sizeof(bias)) != -1) return (bias); if (OF_getencprop(node, "allwinner,pull", &bias, sizeof(bias)) != -1) return (bias); if (OF_hasprop(node, "bias-disable")) return (AW_GPIO_NONE); if (OF_hasprop(node, "bias-pull-up")) return (AW_GPIO_PULLUP); if (OF_hasprop(node, "bias-pull-down")) return (AW_GPIO_PULLDOWN); return (AW_GPIO_NONE); } static int aw_gpio_parse_drive_strength(phandle_t node, uint32_t *drive) { uint32_t drive_str; if (OF_getencprop(node, "drive", drive, sizeof(*drive)) != -1) return (0); if (OF_getencprop(node, "allwinner,drive", drive, sizeof(*drive)) != -1) return (0); if (OF_getencprop(node, "drive-strength", &drive_str, sizeof(drive_str)) != -1) { *drive = (drive_str / 10) - 1; return (0); } return (1); } static int aw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct aw_gpio_softc *sc; int ret; sc = device_get_softc(dev); AW_GPIO_LOCK(sc); ret = aw_gpio_pin_get_locked(sc, pin, val); AW_GPIO_UNLOCK(sc); return (ret); } static int aw_gpio_pin_toggle(device_t dev, uint32_t pin) { struct aw_gpio_softc *sc; uint32_t bank, data; sc = device_get_softc(dev); if (pin > sc->conf->padconf->npins) return (EINVAL); bank = sc->conf->padconf->pins[pin].port; pin = sc->conf->padconf->pins[pin].pin; AW_GPIO_LOCK(sc); data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank)); if (data & (1 << pin)) data &= ~(1 << pin); else data |= (1 << pin); AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), data); AW_GPIO_UNLOCK(sc); return (0); } static int aw_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, uint32_t change_pins, uint32_t *orig_pins) { struct aw_gpio_softc *sc; uint32_t bank, data, pin; sc = device_get_softc(dev); if (first_pin > sc->conf->padconf->npins) return (EINVAL); /* * We require that first_pin refers to the first pin in a bank, because * this API is not about convenience, it's for making a set of pins * change simultaneously (required) with reasonably high performance * (desired); we need to do a read-modify-write on a single register. */ bank = sc->conf->padconf->pins[first_pin].port; pin = sc->conf->padconf->pins[first_pin].pin; if (pin != 0) return (EINVAL); AW_GPIO_LOCK(sc); data = AW_GPIO_READ(sc, AW_GPIO_GP_DAT(bank)); if ((clear_pins | change_pins) != 0) AW_GPIO_WRITE(sc, AW_GPIO_GP_DAT(bank), (data & ~clear_pins) ^ change_pins); AW_GPIO_UNLOCK(sc); if (orig_pins != NULL) *orig_pins = data; return (0); } static int aw_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, uint32_t *pin_flags) { struct aw_gpio_softc *sc; uint32_t pin; int err; sc = device_get_softc(dev); if (first_pin > sc->conf->padconf->npins) return (EINVAL); if (sc->conf->padconf->pins[first_pin].pin != 0) return (EINVAL); /* * The configuration for a bank of pins is scattered among several * registers; we cannot g'tee to simultaneously change the state of all * the pins in the flags array. So just loop through the array * configuring each pin for now. If there was a strong need, it might * be possible to support some limited simultaneous config, such as * adjacent groups of 8 pins that line up the same as the config regs. */ for (err = 0, pin = first_pin; err == 0 && pin < num_pins; ++pin) { if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) err = aw_gpio_pin_configure(sc, pin, pin_flags[pin]); } return (err); } static int aw_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { struct aw_gpio_softc *sc; int i; sc = device_get_softc(bus); /* The GPIO pins are mapped as: . */ for (i = 0; i < sc->conf->padconf->npins; i++) if (sc->conf->padconf->pins[i].port == gpios[0] && sc->conf->padconf->pins[i].pin == gpios[1]) { *pin = i; break; } *flags = gpios[gcells - 1]; return (0); } static int aw_find_pinnum_by_name(struct aw_gpio_softc *sc, const char *pinname) { int i; for (i = 0; i < sc->conf->padconf->npins; i++) if (!strcmp(pinname, sc->conf->padconf->pins[i].name)) return i; return (-1); } static int aw_find_pin_func(struct aw_gpio_softc *sc, int pin, const char *func) { int i; for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++) if (sc->conf->padconf->pins[pin].functions[i] && !strcmp(func, sc->conf->padconf->pins[pin].functions[i])) return (i); return (-1); } static int aw_fdt_configure_pins(device_t dev, phandle_t cfgxref) { struct aw_gpio_softc *sc; phandle_t node; const char **pinlist = NULL; char *pin_function = NULL; uint32_t pin_drive, pin_pull; int pins_nb, pin_num, pin_func, i, ret; bool set_drive; sc = device_get_softc(dev); node = OF_node_from_xref(cfgxref); ret = 0; set_drive = false; /* Getting all prop for configuring pins */ pinlist = aw_gpio_parse_pins(node, &pins_nb); if (pinlist == NULL) return (ENOENT); pin_function = aw_gpio_parse_function(node); if (pin_function == NULL) { ret = ENOENT; goto out; } if (aw_gpio_parse_drive_strength(node, &pin_drive) == 0) set_drive = true; pin_pull = aw_gpio_parse_bias(node); /* Configure each pin to the correct function, drive and pull */ for (i = 0; i < pins_nb; i++) { pin_num = aw_find_pinnum_by_name(sc, pinlist[i]); if (pin_num == -1) { ret = ENOENT; goto out; } pin_func = aw_find_pin_func(sc, pin_num, pin_function); if (pin_func == -1) { ret = ENOENT; goto out; } AW_GPIO_LOCK(sc); if (aw_gpio_get_function(sc, pin_num) != pin_func) aw_gpio_set_function(sc, pin_num, pin_func); if (set_drive) aw_gpio_set_drv(sc, pin_num, pin_drive); if (pin_pull != AW_GPIO_NONE) aw_gpio_set_pud(sc, pin_num, pin_pull); AW_GPIO_UNLOCK(sc); } out: OF_prop_free(pinlist); OF_prop_free(pin_function); return (ret); } static void aw_gpio_enable_bank_supply(void *arg) { struct aw_gpio_softc *sc = arg; regulator_t vcc_supply; char bank_reg_name[16]; int i, nbanks; nbanks = strlen(sc->conf->banks); for (i = 0; i < nbanks; i++) { snprintf(bank_reg_name, sizeof(bank_reg_name), "vcc-p%c-supply", sc->conf->banks[i]); if (regulator_get_by_ofw_property(sc->sc_dev, 0, bank_reg_name, &vcc_supply) == 0) { if (bootverbose) device_printf(sc->sc_dev, "Enabling regulator for gpio bank %c\n", sc->conf->banks[i]); if (regulator_enable(vcc_supply) != 0) { device_printf(sc->sc_dev, "Cannot enable regulator for bank %c\n", sc->conf->banks[i]); } } } } static int aw_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner GPIO/Pinmux controller"); return (BUS_PROBE_DEFAULT); } static int aw_gpio_attach(device_t dev) { int error; phandle_t gpio; struct aw_gpio_softc *sc; struct clk_list *clkp, *clkp_tmp; clk_t clk; hwreset_t rst = NULL; int off, err, clkret; sc = device_get_softc(dev); sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "aw gpio", "gpio", MTX_SPIN); if (bus_alloc_resources(dev, aw_gpio_res_spec, sc->sc_res) != 0) { device_printf(dev, "cannot allocate device resources\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_res[AW_GPIO_IRQRES], INTR_TYPE_CLK | INTR_MPSAFE, NULL, aw_gpio_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; /* Use the right pin data for the current SoC */ sc->conf = (struct aw_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } TAILQ_INIT(&sc->clk_list); for (off = 0, clkret = 0; clkret == 0; off++) { clkret = clk_get_by_ofw_index(dev, 0, off, &clk); if (clkret != 0) break; err = clk_enable(clk); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(clk)); goto fail; } clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); clkp->clk = clk; TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); } if (clkret != 0 && clkret != ENOENT) { device_printf(dev, "Could not find clock at offset %d (%d)\n", off, clkret); goto fail; } aw_gpio_register_isrcs(sc); intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; /* * Register as a pinctrl device */ fdt_pinctrl_register(dev, "pins"); fdt_pinctrl_configure_tree(dev); fdt_pinctrl_register(dev, "allwinner,pins"); fdt_pinctrl_configure_tree(dev); config_intrhook_oneshot(aw_gpio_enable_bank_supply, sc); return (0); fail: if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); mtx_destroy(&sc->sc_mtx); /* Disable clock */ TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { err = clk_disable(clkp->clk); if (err != 0) device_printf(dev, "Could not disable clock %s\n", clk_get_name(clkp->clk)); err = clk_release(clkp->clk); if (err != 0) device_printf(dev, "Could not release clock %s\n", clk_get_name(clkp->clk)); TAILQ_REMOVE(&sc->clk_list, clkp, next); free(clkp, M_DEVBUF); } /* Assert resets */ if (rst) { hwreset_assert(rst); hwreset_release(rst); } return (ENXIO); } static int aw_gpio_detach(device_t dev) { return (EBUSY); } static void aw_gpio_intr(void *arg) { struct aw_gpio_softc *sc; struct intr_irqsrc *isrc; uint32_t reg; int irq; sc = (struct aw_gpio_softc *)arg; AW_GPIO_LOCK(sc); for (irq = 0; irq < sc->nirqs; irq++) { if (!sc->gpio_pic_irqsrc[irq].enabled) continue; reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_STA(sc->gpio_pic_irqsrc[irq].bank)); if (!(reg & (1 << sc->gpio_pic_irqsrc[irq].intnum))) continue; isrc = &sc->gpio_pic_irqsrc[irq].isrc; if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { aw_gpio_pic_disable_intr_locked(sc, isrc); aw_gpio_pic_post_filter(sc->sc_dev, isrc); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } } AW_GPIO_UNLOCK(sc); } /* * Interrupts support */ static int aw_gpio_register_isrcs(struct aw_gpio_softc *sc) { const char *name; int nirqs; int pin; int err; name = device_get_nameunit(sc->sc_dev); for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) { if (sc->conf->padconf->pins[pin].eint_func == 0) continue; nirqs++; } sc->gpio_pic_irqsrc = malloc(sizeof(*sc->gpio_pic_irqsrc) * nirqs, M_DEVBUF, M_WAITOK | M_ZERO); for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) { if (sc->conf->padconf->pins[pin].eint_func == 0) continue; sc->gpio_pic_irqsrc[nirqs].pin = pin; sc->gpio_pic_irqsrc[nirqs].bank = sc->conf->padconf->pins[pin].eint_bank; sc->gpio_pic_irqsrc[nirqs].intnum = sc->conf->padconf->pins[pin].eint_num; sc->gpio_pic_irqsrc[nirqs].intfunc = sc->conf->padconf->pins[pin].eint_func; sc->gpio_pic_irqsrc[nirqs].irq = nirqs; sc->gpio_pic_irqsrc[nirqs].mode = GPIO_INTR_CONFORM; err = intr_isrc_register(&sc->gpio_pic_irqsrc[nirqs].isrc, sc->sc_dev, 0, "%s,%s", name, sc->conf->padconf->pins[pin].functions[sc->conf->padconf->pins[pin].eint_func]); if (err) { device_printf(sc->sc_dev, "intr_isrs_register failed for irq %d\n", nirqs); } nirqs++; } sc->nirqs = nirqs; return (0); } static void aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc) { u_int irq; uint32_t reg; AW_GPIO_LOCK_ASSERT(sc); irq = ((struct gpio_irqsrc *)isrc)->irq; reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank)); reg &= ~(1 << sc->gpio_pic_irqsrc[irq].intnum); AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg); sc->gpio_pic_irqsrc[irq].enabled = false; } static void aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); AW_GPIO_LOCK(sc); aw_gpio_pic_disable_intr_locked(sc, isrc); AW_GPIO_UNLOCK(sc); } static void aw_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct aw_gpio_softc *sc; u_int irq; uint32_t reg; sc = device_get_softc(dev); irq = ((struct gpio_irqsrc *)isrc)->irq; AW_GPIO_LOCK(sc); reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank)); reg |= 1 << sc->gpio_pic_irqsrc[irq].intnum; AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg); AW_GPIO_UNLOCK(sc); sc->gpio_pic_irqsrc[irq].enabled = true; } static int aw_gpio_pic_map_gpio(struct aw_gpio_softc *sc, struct intr_map_data_gpio *dag, u_int *irqp, u_int *mode) { u_int irq; int pin; irq = dag->gpio_pin_num; for (pin = 0; pin < sc->nirqs; pin++) if (sc->gpio_pic_irqsrc[pin].pin == irq) break; if (pin == sc->nirqs) { device_printf(sc->sc_dev, "Invalid interrupt number %u\n", irq); return (EINVAL); } switch (dag->gpio_intr_mode) { case GPIO_INTR_LEVEL_LOW: case GPIO_INTR_LEVEL_HIGH: case GPIO_INTR_EDGE_RISING: case GPIO_INTR_EDGE_FALLING: case GPIO_INTR_EDGE_BOTH: break; default: device_printf(sc->sc_dev, "Unsupported interrupt mode 0x%8x\n", dag->gpio_intr_mode); return (EINVAL); } *irqp = pin; if (mode != NULL) *mode = dag->gpio_intr_mode; return (0); } static int aw_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct aw_gpio_softc *sc; u_int irq; int err; sc = device_get_softc(dev); switch (data->type) { case INTR_MAP_DATA_GPIO: err = aw_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, &irq, NULL); break; default: return (ENOTSUP); }; if (err == 0) *isrcp = &sc->gpio_pic_irqsrc[irq].isrc; return (0); } static int aw_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct aw_gpio_softc *sc; uint32_t irqcfg; uint32_t pinidx, reg; u_int irq, mode; int err; sc = device_get_softc(dev); err = 0; switch (data->type) { case INTR_MAP_DATA_GPIO: err = aw_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, &irq, &mode); if (err != 0) return (err); break; default: return (ENOTSUP); }; pinidx = (sc->gpio_pic_irqsrc[irq].intnum % 8) * 4; AW_GPIO_LOCK(sc); switch (mode) { case GPIO_INTR_LEVEL_LOW: irqcfg = AW_GPIO_INT_LEVEL_LOW << pinidx; break; case GPIO_INTR_LEVEL_HIGH: irqcfg = AW_GPIO_INT_LEVEL_HIGH << pinidx; break; case GPIO_INTR_EDGE_RISING: irqcfg = AW_GPIO_INT_EDGE_POSITIVE << pinidx; break; case GPIO_INTR_EDGE_FALLING: irqcfg = AW_GPIO_INT_EDGE_NEGATIVE << pinidx; break; case GPIO_INTR_EDGE_BOTH: irqcfg = AW_GPIO_INT_EDGE_BOTH << pinidx; break; } /* Switch the pin to interrupt mode */ sc->gpio_pic_irqsrc[irq].oldfunc = aw_gpio_get_function(sc, sc->gpio_pic_irqsrc[irq].pin); aw_gpio_set_function(sc, sc->gpio_pic_irqsrc[irq].pin, sc->gpio_pic_irqsrc[irq].intfunc); /* Write interrupt mode */ reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank, sc->gpio_pic_irqsrc[irq].intnum)); reg &= ~(0xF << pinidx); reg |= irqcfg; AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank, sc->gpio_pic_irqsrc[irq].intnum), reg); AW_GPIO_UNLOCK(sc); return (0); } static int aw_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct aw_gpio_softc *sc; struct gpio_irqsrc *gi; sc = device_get_softc(dev); gi = (struct gpio_irqsrc *)isrc; /* Switch back the pin to it's original function */ AW_GPIO_LOCK(sc); aw_gpio_set_function(sc, gi->pin, gi->oldfunc); AW_GPIO_UNLOCK(sc); return (0); } static void aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct aw_gpio_softc *sc; struct gpio_irqsrc *gi; sc = device_get_softc(dev); gi = (struct gpio_irqsrc *)isrc; arm_irq_memory_barrier(0); AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum); } static void aw_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct aw_gpio_softc *sc; struct gpio_irqsrc *gi; sc = device_get_softc(dev); gi = (struct gpio_irqsrc *)isrc; arm_irq_memory_barrier(0); AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum); aw_gpio_pic_enable_intr(dev, isrc); } static void aw_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct aw_gpio_softc *sc; sc = device_get_softc(dev); aw_gpio_pic_disable_intr_locked(sc, isrc); } /* * OFWBUS Interface */ static phandle_t aw_gpio_get_node(device_t dev, device_t bus) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(dev)); } static device_method_t aw_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_gpio_probe), DEVMETHOD(device_attach, aw_gpio_attach), DEVMETHOD(device_detach, aw_gpio_detach), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, aw_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, aw_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, aw_gpio_pic_map_intr), DEVMETHOD(pic_setup_intr, aw_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, aw_gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, aw_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, aw_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, aw_gpio_pic_pre_ithread), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, aw_gpio_get_bus), DEVMETHOD(gpio_pin_max, aw_gpio_pin_max), DEVMETHOD(gpio_pin_getname, aw_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, aw_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, aw_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, aw_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, aw_gpio_pin_get), DEVMETHOD(gpio_pin_set, aw_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, aw_gpio_pin_toggle), DEVMETHOD(gpio_pin_access_32, aw_gpio_pin_access_32), DEVMETHOD(gpio_pin_config_32, aw_gpio_pin_config_32), DEVMETHOD(gpio_map_gpios, aw_gpio_map_gpios), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, aw_gpio_get_node), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins), DEVMETHOD_END }; static driver_t aw_gpio_driver = { "gpio", aw_gpio_methods, sizeof(struct aw_gpio_softc), }; EARLY_DRIVER_MODULE(aw_gpio, simplebus, aw_gpio_driver, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/arm/allwinner/aw_i2s.c b/sys/arm/allwinner/aw_i2s.c index 376405056f51..87dfb109363f 100644 --- a/sys/arm/allwinner/aw_i2s.c +++ b/sys/arm/allwinner/aw_i2s.c @@ -1,805 +1,805 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Oleksandr Tymoshenko * Copyright (c) 2018 Jared McNeill * * 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 ``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 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 #include #include -#include +#include #include "syscon_if.h" #include "opt_snd.h" #include #include #include "audio_dai_if.h" #define FIFO_LEVEL 0x40 #define DA_CTL 0x00 #define DA_CTL_BCLK_OUT (1 << 18) /* sun8i */ #define DA_CLK_LRCK_OUT (1 << 17) /* sun8i */ #define DA_CTL_SDO_EN (1 << 8) #define DA_CTL_MS (1 << 5) /* sun4i */ #define DA_CTL_PCM (1 << 4) /* sun4i */ #define DA_CTL_MODE_SEL_MASK (3 << 4) /* sun8i */ #define DA_CTL_MODE_SEL_PCM (0 << 4) /* sun8i */ #define DA_CTL_MODE_SEL_LJ (1 << 4) /* sun8i */ #define DA_CTL_MODE_SEL_RJ (2 << 4) /* sun8i */ #define DA_CTL_TXEN (1 << 2) #define DA_CTL_RXEN (1 << 1) #define DA_CTL_GEN (1 << 0) #define DA_FAT0 0x04 #define DA_FAT0_LRCK_PERIOD_MASK (0x3ff << 8) /* sun8i */ #define DA_FAT0_LRCK_PERIOD(n) (((n) & 0x3fff) << 8) /* sun8i */ #define DA_FAT0_LRCP_MASK (1 << 7) #define DA_LRCP_NORMAL (0 << 7) #define DA_LRCP_INVERTED (1 << 7) #define DA_FAT0_BCP_MASK (1 << 6) #define DA_BCP_NORMAL (0 << 6) #define DA_BCP_INVERTED (1 << 6) #define DA_FAT0_SR __BITS(5,4) #define DA_FAT0_WSS __BITS(3,2) #define DA_FAT0_FMT_MASK (3 << 0) #define DA_FMT_I2S 0 #define DA_FMT_LJ 1 #define DA_FMT_RJ 2 #define DA_FAT1 0x08 #define DA_ISTA 0x0c #define DA_ISTA_TXUI_INT (1 << 6) #define DA_ISTA_TXEI_INT (1 << 4) #define DA_ISTA_RXAI_INT (1 << 0) #define DA_RXFIFO 0x10 #define DA_FCTL 0x14 #define DA_FCTL_HUB_EN (1 << 31) #define DA_FCTL_FTX (1 << 25) #define DA_FCTL_FRX (1 << 24) #define DA_FCTL_TXTL_MASK (0x7f << 12) #define DA_FCTL_TXTL(v) (((v) & 0x7f) << 12) #define DA_FCTL_TXIM (1 << 2) #define DA_FSTA 0x18 #define DA_FSTA_TXE_CNT(v) (((v) >> 16) & 0xff) #define DA_FSTA_RXA_CNT(v) ((v) & 0x3f) #define DA_INT 0x1c #define DA_INT_TX_DRQ (1 << 7) #define DA_INT_TXUI_EN (1 << 6) #define DA_INT_TXEI_EN (1 << 4) #define DA_INT_RX_DRQ (1 << 3) #define DA_INT_RXAI_EN (1 << 0) #define DA_TXFIFO 0x20 #define DA_CLKD 0x24 #define DA_CLKD_MCLKO_EN_SUN8I (1 << 8) #define DA_CLKD_MCLKO_EN_SUN4I (1 << 7) #define DA_CLKD_BCLKDIV_SUN8I(n) (((n) & 0xf) << 4) #define DA_CLKD_BCLKDIV_SUN8I_MASK (0xf << 4) #define DA_CLKD_BCLKDIV_SUN4I(n) (((n) & 7) << 4) #define DA_CLKD_BCLKDIV_SUN4I_MASK (7 << 4) #define DA_CLKD_BCLKDIV_8 3 #define DA_CLKD_BCLKDIV_16 5 #define DA_CLKD_MCLKDIV(n) (((n) & 0xff) << 0) #define DA_CLKD_MCLKDIV_MASK (0xf << 0) #define DA_CLKD_MCLKDIV_1 0 #define DA_TXCNT 0x28 #define DA_RXCNT 0x2c #define DA_CHCFG 0x30 /* sun8i */ #define DA_CHCFG_TX_SLOT_HIZ (1 << 9) #define DA_CHCFG_TXN_STATE (1 << 8) #define DA_CHCFG_RX_SLOT_NUM_MASK (7 << 4) #define DA_CHCFG_RX_SLOT_NUM(n) (((n) & 7) << 4) #define DA_CHCFG_TX_SLOT_NUM_MASK (7 << 0) #define DA_CHCFG_TX_SLOT_NUM(n) (((n) & 7) << 0) #define DA_CHSEL_OFFSET(n) (((n) & 3) << 12) /* sun8i */ #define DA_CHSEL_OFFSET_MASK (3 << 12) /* sun8i */ #define DA_CHSEL_EN(n) (((n) & 0xff) << 4) #define DA_CHSEL_EN_MASK (0xff << 4) #define DA_CHSEL_SEL(n) (((n) & 7) << 0) #define DA_CHSEL_SEL_MASK (7 << 0) #define AUDIO_BUFFER_SIZE 48000 * 4 #define AW_I2S_SAMPLE_RATE 48000 #define AW_I2S_CLK_RATE 24576000 enum sunxi_i2s_type { SUNXI_I2S_SUN4I, SUNXI_I2S_SUN8I, }; struct sunxi_i2s_config { const char *name; enum sunxi_i2s_type type; bus_size_t txchsel; bus_size_t txchmap; bus_size_t rxchsel; bus_size_t rxchmap; }; static const struct sunxi_i2s_config sun50i_a64_codec_config = { .name = "Audio Codec (digital part)", .type = SUNXI_I2S_SUN4I, .txchsel = 0x30, .txchmap = 0x34, .rxchsel = 0x38, .rxchmap = 0x3c, }; static const struct sunxi_i2s_config sun8i_h3_config = { .name = "I2S/PCM controller", .type = SUNXI_I2S_SUN8I, .txchsel = 0x34, .txchmap = 0x44, .rxchsel = 0x54, .rxchmap = 0x58, }; static const u_int sun4i_i2s_bclk_divmap[] = { [0] = 2, [1] = 4, [2] = 6, [3] = 8, [4] = 12, [5] = 16, }; static const u_int sun4i_i2s_mclk_divmap[] = { [0] = 1, [1] = 2, [2] = 4, [3] = 6, [4] = 8, [5] = 12, [6] = 16, [7] = 24, }; static const u_int sun8i_i2s_divmap[] = { [1] = 1, [2] = 2, [3] = 4, [4] = 6, [5] = 8, [6] = 12, [7] = 16, [8] = 24, [9] = 32, [10] = 48, [11] = 64, [12] = 96, [13] = 128, [14] = 176, [15] = 192, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun50i-a64-codec-i2s", (uintptr_t)&sun50i_a64_codec_config }, { "allwinner,sun8i-h3-i2s", (uintptr_t)&sun8i_h3_config }, { NULL, 0 } }; static struct resource_spec aw_i2s_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct aw_i2s_softc { device_t dev; struct resource *res[2]; struct mtx mtx; clk_t clk; struct sunxi_i2s_config *cfg; void * intrhand; /* pointers to playback/capture buffers */ uint32_t play_ptr; uint32_t rec_ptr; }; #define I2S_LOCK(sc) mtx_lock(&(sc)->mtx) #define I2S_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define I2S_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) #define I2S_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) #define I2S_TYPE(sc) ((sc)->cfg->type) static int aw_i2s_probe(device_t dev); static int aw_i2s_attach(device_t dev); static int aw_i2s_detach(device_t dev); static u_int sunxi_i2s_div_to_regval(const u_int *divmap, u_int divmaplen, u_int div) { u_int n; for (n = 0; n < divmaplen; n++) if (divmap[n] == div) return n; return -1; } static uint32_t sc_fmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps aw_i2s_caps = {AW_I2S_SAMPLE_RATE, AW_I2S_SAMPLE_RATE, sc_fmt, 0}; static int aw_i2s_init(struct aw_i2s_softc *sc) { uint32_t val; int error; error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "cannot enable mod clock\n"); return (ENXIO); } /* Reset */ val = I2S_READ(sc, DA_CTL); val &= ~(DA_CTL_TXEN|DA_CTL_RXEN|DA_CTL_GEN); I2S_WRITE(sc, DA_CTL, val); val = I2S_READ(sc, DA_FCTL); val &= ~(DA_FCTL_FTX|DA_FCTL_FRX); val &= ~(DA_FCTL_TXTL_MASK); val |= DA_FCTL_TXTL(FIFO_LEVEL); I2S_WRITE(sc, DA_FCTL, val); I2S_WRITE(sc, DA_TXCNT, 0); I2S_WRITE(sc, DA_RXCNT, 0); /* Enable */ val = I2S_READ(sc, DA_CTL); val |= DA_CTL_GEN; I2S_WRITE(sc, DA_CTL, val); val |= DA_CTL_SDO_EN; I2S_WRITE(sc, DA_CTL, val); /* Setup channels */ I2S_WRITE(sc, sc->cfg->txchmap, 0x76543210); val = I2S_READ(sc, sc->cfg->txchsel); val &= ~DA_CHSEL_EN_MASK; val |= DA_CHSEL_EN(3); val &= ~DA_CHSEL_SEL_MASK; val |= DA_CHSEL_SEL(1); I2S_WRITE(sc, sc->cfg->txchsel, val); I2S_WRITE(sc, sc->cfg->rxchmap, 0x76543210); val = I2S_READ(sc, sc->cfg->rxchsel); val &= ~DA_CHSEL_EN_MASK; val |= DA_CHSEL_EN(3); val &= ~DA_CHSEL_SEL_MASK; val |= DA_CHSEL_SEL(1); I2S_WRITE(sc, sc->cfg->rxchsel, val); if (I2S_TYPE(sc) == SUNXI_I2S_SUN8I) { val = I2S_READ(sc, DA_CHCFG); val &= ~DA_CHCFG_TX_SLOT_NUM_MASK; val |= DA_CHCFG_TX_SLOT_NUM(1); val &= ~DA_CHCFG_RX_SLOT_NUM_MASK; val |= DA_CHCFG_RX_SLOT_NUM(1); I2S_WRITE(sc, DA_CHCFG, val); } return (0); } static int aw_i2s_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(dev, "Allwinner I2S"); return (BUS_PROBE_DEFAULT); } static int aw_i2s_attach(device_t dev) { struct aw_i2s_softc *sc; int error; phandle_t node; hwreset_t rst; clk_t clk; sc = device_get_softc(dev); sc->dev = dev; sc->cfg = (void*)ofw_bus_search_compatible(dev, compat_data)->ocd_data; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, aw_i2s_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk); if (error != 0) { device_printf(dev, "cannot get i2s_clk clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "apb", &clk); if (error != 0) { device_printf(dev, "cannot get APB clock\n"); goto fail; } error = clk_enable(clk); if (error != 0) { device_printf(dev, "cannot enable APB clock\n"); goto fail; } if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } aw_i2s_init(sc); node = ofw_bus_get_node(dev); OF_device_register_xref(OF_xref_from_node(node), dev); return (0); fail: aw_i2s_detach(dev); return (error); } static int aw_i2s_detach(device_t dev) { struct aw_i2s_softc *i2s; i2s = device_get_softc(dev); if (i2s->clk) clk_release(i2s->clk); if (i2s->intrhand != NULL) bus_teardown_intr(i2s->dev, i2s->res[1], i2s->intrhand); bus_release_resources(dev, aw_i2s_spec, i2s->res); mtx_destroy(&i2s->mtx); return (0); } static int aw_i2s_dai_init(device_t dev, uint32_t format) { struct aw_i2s_softc *sc; int fmt, pol; uint32_t ctl, fat0, chsel; u_int offset; sc = device_get_softc(dev); fmt = AUDIO_DAI_FORMAT_FORMAT(format); pol = AUDIO_DAI_FORMAT_POLARITY(format); ctl = I2S_READ(sc, DA_CTL); fat0 = I2S_READ(sc, DA_FAT0); if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { fat0 &= ~DA_FAT0_FMT_MASK; switch (fmt) { case AUDIO_DAI_FORMAT_I2S: fat0 |= DA_FMT_I2S; break; case AUDIO_DAI_FORMAT_RJ: fat0 |= DA_FMT_RJ; break; case AUDIO_DAI_FORMAT_LJ: fat0 |= DA_FMT_LJ; break; default: return EINVAL; } ctl &= ~DA_CTL_PCM; } else { ctl &= ~DA_CTL_MODE_SEL_MASK; switch (fmt) { case AUDIO_DAI_FORMAT_I2S: ctl |= DA_CTL_MODE_SEL_LJ; offset = 1; break; case AUDIO_DAI_FORMAT_LJ: ctl |= DA_CTL_MODE_SEL_LJ; offset = 0; break; case AUDIO_DAI_FORMAT_RJ: ctl |= DA_CTL_MODE_SEL_RJ; offset = 0; break; case AUDIO_DAI_FORMAT_DSPA: ctl |= DA_CTL_MODE_SEL_PCM; offset = 1; break; case AUDIO_DAI_FORMAT_DSPB: ctl |= DA_CTL_MODE_SEL_PCM; offset = 0; break; default: return EINVAL; } chsel = I2S_READ(sc, sc->cfg->txchsel); chsel &= ~DA_CHSEL_OFFSET_MASK; chsel |= DA_CHSEL_OFFSET(offset); I2S_WRITE(sc, sc->cfg->txchsel, chsel); chsel = I2S_READ(sc, sc->cfg->rxchsel); chsel &= ~DA_CHSEL_OFFSET_MASK; chsel |= DA_CHSEL_OFFSET(offset); I2S_WRITE(sc, sc->cfg->rxchsel, chsel); } fat0 &= ~(DA_FAT0_LRCP_MASK|DA_FAT0_BCP_MASK); if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol)) fat0 |= DA_BCP_INVERTED; if (AUDIO_DAI_POLARITY_INVERTED_FRAME(pol)) fat0 |= DA_LRCP_INVERTED; } else { if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol)) fat0 |= DA_BCP_INVERTED; if (!AUDIO_DAI_POLARITY_INVERTED_FRAME(pol)) fat0 |= DA_LRCP_INVERTED; fat0 &= ~DA_FAT0_LRCK_PERIOD_MASK; fat0 |= DA_FAT0_LRCK_PERIOD(32 - 1); } I2S_WRITE(sc, DA_CTL, ctl); I2S_WRITE(sc, DA_FAT0, fat0); return (0); } static int aw_i2s_dai_intr(device_t dev, struct snd_dbuf *play_buf, struct snd_dbuf *rec_buf) { struct aw_i2s_softc *sc; int ret = 0; uint32_t val, status; sc = device_get_softc(dev); I2S_LOCK(sc); status = I2S_READ(sc, DA_ISTA); /* Clear interrupts */ // device_printf(sc->dev, "status: %08x\n", status); I2S_WRITE(sc, DA_ISTA, status); if (status & DA_ISTA_TXEI_INT) { uint8_t *samples; uint32_t count, size, readyptr, written, empty; val = I2S_READ(sc, DA_FSTA); empty = DA_FSTA_TXE_CNT(val); count = sndbuf_getready(play_buf); size = sndbuf_getsize(play_buf); readyptr = sndbuf_getreadyptr(play_buf); samples = (uint8_t*)sndbuf_getbuf(play_buf); written = 0; if (empty > count / 2) empty = count / 2; for (; empty > 0; empty--) { val = (samples[readyptr++ % size] << 16); val |= (samples[readyptr++ % size] << 24); written += 2; I2S_WRITE(sc, DA_TXFIFO, val); } sc->play_ptr += written; sc->play_ptr %= size; ret |= AUDIO_DAI_PLAY_INTR; } if (status & DA_ISTA_RXAI_INT) { uint8_t *samples; uint32_t count, size, freeptr, recorded, available; val = I2S_READ(sc, DA_FSTA); available = DA_FSTA_RXA_CNT(val); count = sndbuf_getfree(rec_buf); size = sndbuf_getsize(rec_buf); freeptr = sndbuf_getfreeptr(rec_buf); samples = (uint8_t*)sndbuf_getbuf(rec_buf); recorded = 0; if (available > count / 2) available = count / 2; for (; available > 0; available--) { val = I2S_READ(sc, DA_RXFIFO); samples[freeptr++ % size] = (val >> 16) & 0xff; samples[freeptr++ % size] = (val >> 24) & 0xff; recorded += 2; } sc->rec_ptr += recorded; sc->rec_ptr %= size; ret |= AUDIO_DAI_REC_INTR; } I2S_UNLOCK(sc); return (ret); } static struct pcmchan_caps * aw_i2s_dai_get_caps(device_t dev) { return (&aw_i2s_caps); } static int aw_i2s_dai_trigger(device_t dev, int go, int pcm_dir) { struct aw_i2s_softc *sc = device_get_softc(dev); uint32_t val; if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC)) return (EINVAL); switch (go) { case PCMTRIG_START: if (pcm_dir == PCMDIR_PLAY) { /* Flush FIFO */ val = I2S_READ(sc, DA_FCTL); I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FTX); I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FTX); /* Reset TX sample counter */ I2S_WRITE(sc, DA_TXCNT, 0); /* Enable TX block */ val = I2S_READ(sc, DA_CTL); I2S_WRITE(sc, DA_CTL, val | DA_CTL_TXEN); /* Enable TX underrun interrupt */ val = I2S_READ(sc, DA_INT); I2S_WRITE(sc, DA_INT, val | DA_INT_TXEI_EN); } if (pcm_dir == PCMDIR_REC) { /* Flush FIFO */ val = I2S_READ(sc, DA_FCTL); I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FRX); I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FRX); /* Reset RX sample counter */ I2S_WRITE(sc, DA_RXCNT, 0); /* Enable RX block */ val = I2S_READ(sc, DA_CTL); I2S_WRITE(sc, DA_CTL, val | DA_CTL_RXEN); /* Enable RX data available interrupt */ val = I2S_READ(sc, DA_INT); I2S_WRITE(sc, DA_INT, val | DA_INT_RXAI_EN); } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) { /* Disable TX block */ val = I2S_READ(sc, DA_CTL); I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_TXEN); /* Enable TX underrun interrupt */ val = I2S_READ(sc, DA_INT); I2S_WRITE(sc, DA_INT, val & ~DA_INT_TXEI_EN); sc->play_ptr = 0; } else { /* Disable RX block */ val = I2S_READ(sc, DA_CTL); I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_RXEN); /* Disable RX data available interrupt */ val = I2S_READ(sc, DA_INT); I2S_WRITE(sc, DA_INT, val & ~DA_INT_RXAI_EN); sc->rec_ptr = 0; } I2S_UNLOCK(sc); break; } return (0); } static uint32_t aw_i2s_dai_get_ptr(device_t dev, int pcm_dir) { struct aw_i2s_softc *sc; uint32_t ptr; sc = device_get_softc(dev); I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) ptr = sc->play_ptr; else ptr = sc->rec_ptr; I2S_UNLOCK(sc); return ptr; } static int aw_i2s_dai_setup_intr(device_t dev, driver_intr_t intr_handler, void *intr_arg) { struct aw_i2s_softc *sc = device_get_softc(dev); if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, intr_handler, intr_arg, &sc->intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } return (0); } static uint32_t aw_i2s_dai_set_chanformat(device_t dev, uint32_t format) { return (0); } static int aw_i2s_dai_set_sysclk(device_t dev, unsigned int rate, int dai_dir) { struct aw_i2s_softc *sc; int bclk_val, mclk_val; uint32_t val; int error; sc = device_get_softc(dev); error = clk_set_freq(sc->clk, AW_I2S_CLK_RATE, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(sc->dev, "couldn't set mod clock rate to %u Hz: %d\n", AW_I2S_CLK_RATE, error); return error; } error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "couldn't enable mod clock: %d\n", error); return error; } const u_int bclk_prate = I2S_TYPE(sc) == SUNXI_I2S_SUN4I ? rate : AW_I2S_CLK_RATE; const u_int bclk_div = bclk_prate / (2 * 32 * AW_I2S_SAMPLE_RATE); const u_int mclk_div = AW_I2S_CLK_RATE / rate; if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { bclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_bclk_divmap, nitems(sun4i_i2s_bclk_divmap), bclk_div); mclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_mclk_divmap, nitems(sun4i_i2s_mclk_divmap), mclk_div); } else { bclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap, nitems(sun8i_i2s_divmap), bclk_div); mclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap, nitems(sun8i_i2s_divmap), mclk_div); } if (bclk_val == -1 || mclk_val == -1) { device_printf(sc->dev, "couldn't configure bclk/mclk dividers\n"); return EIO; } val = I2S_READ(sc, DA_CLKD); if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { val |= DA_CLKD_MCLKO_EN_SUN4I; val &= ~DA_CLKD_BCLKDIV_SUN4I_MASK; val |= DA_CLKD_BCLKDIV_SUN4I(bclk_val); } else { val |= DA_CLKD_MCLKO_EN_SUN8I; val &= ~DA_CLKD_BCLKDIV_SUN8I_MASK; val |= DA_CLKD_BCLKDIV_SUN8I(bclk_val); } val &= ~DA_CLKD_MCLKDIV_MASK; val |= DA_CLKD_MCLKDIV(mclk_val); I2S_WRITE(sc, DA_CLKD, val); return (0); } static uint32_t aw_i2s_dai_set_chanspeed(device_t dev, uint32_t speed) { return (speed); } static device_method_t aw_i2s_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_i2s_probe), DEVMETHOD(device_attach, aw_i2s_attach), DEVMETHOD(device_detach, aw_i2s_detach), DEVMETHOD(audio_dai_init, aw_i2s_dai_init), DEVMETHOD(audio_dai_setup_intr, aw_i2s_dai_setup_intr), DEVMETHOD(audio_dai_set_sysclk, aw_i2s_dai_set_sysclk), DEVMETHOD(audio_dai_set_chanspeed, aw_i2s_dai_set_chanspeed), DEVMETHOD(audio_dai_set_chanformat, aw_i2s_dai_set_chanformat), DEVMETHOD(audio_dai_intr, aw_i2s_dai_intr), DEVMETHOD(audio_dai_get_caps, aw_i2s_dai_get_caps), DEVMETHOD(audio_dai_trigger, aw_i2s_dai_trigger), DEVMETHOD(audio_dai_get_ptr, aw_i2s_dai_get_ptr), DEVMETHOD_END }; static driver_t aw_i2s_driver = { "i2s", aw_i2s_methods, sizeof(struct aw_i2s_softc), }; DRIVER_MODULE(aw_i2s, simplebus, aw_i2s_driver, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/aw_mmc.c b/sys/arm/allwinner/aw_mmc.c index 7c783f69880c..d229fba1e07d 100644 --- a/sys/arm/allwinner/aw_mmc.c +++ b/sys/arm/allwinner/aw_mmc.c @@ -1,1520 +1,1520 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 Emmanuel Vadot * Copyright (c) 2013 Alexander Fedorov * 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 #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include "opt_mmccam.h" #ifdef MMCCAM #include #include #include #include #include #include #include "mmc_sim_if.h" #endif #include "mmc_pwrseq_if.h" #define AW_MMC_MEMRES 0 #define AW_MMC_IRQRES 1 #define AW_MMC_RESSZ 2 #define AW_MMC_DMA_SEGS (PAGE_SIZE / sizeof(struct aw_mmc_dma_desc)) #define AW_MMC_DMA_DESC_SIZE (sizeof(struct aw_mmc_dma_desc) * AW_MMC_DMA_SEGS) #define AW_MMC_DMA_FTRGLEVEL 0x20070008 #define AW_MMC_RESET_RETRY 1000 #define CARD_ID_FREQUENCY 400000 struct aw_mmc_conf { uint32_t dma_xferlen; bool mask_data0; bool can_calibrate; bool new_timing; }; static const struct aw_mmc_conf a10_mmc_conf = { .dma_xferlen = 0x2000, }; static const struct aw_mmc_conf a13_mmc_conf = { .dma_xferlen = 0x10000, }; static const struct aw_mmc_conf a64_mmc_conf = { .dma_xferlen = 0x10000, .mask_data0 = true, .can_calibrate = true, .new_timing = true, }; static const struct aw_mmc_conf a64_emmc_conf = { .dma_xferlen = 0x2000, .can_calibrate = true, }; static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-mmc", (uintptr_t)&a10_mmc_conf}, {"allwinner,sun5i-a13-mmc", (uintptr_t)&a13_mmc_conf}, {"allwinner,sun7i-a20-mmc", (uintptr_t)&a13_mmc_conf}, {"allwinner,sun50i-a64-mmc", (uintptr_t)&a64_mmc_conf}, {"allwinner,sun50i-a64-emmc", (uintptr_t)&a64_emmc_conf}, {NULL, 0} }; struct aw_mmc_softc { device_t aw_dev; clk_t aw_clk_ahb; clk_t aw_clk_mmc; hwreset_t aw_rst_ahb; int aw_bus_busy; int aw_resid; int aw_timeout; struct callout aw_timeoutc; struct mmc_host aw_host; struct mmc_helper mmc_helper; #ifdef MMCCAM union ccb * ccb; struct mmc_sim mmc_sim; #else struct mmc_request * aw_req; #endif struct mtx aw_mtx; struct resource * aw_res[AW_MMC_RESSZ]; struct aw_mmc_conf * aw_mmc_conf; uint32_t aw_intr; uint32_t aw_intr_wait; void * aw_intrhand; unsigned int aw_clock; device_t child; /* Fields required for DMA access. */ bus_addr_t aw_dma_desc_phys; bus_dmamap_t aw_dma_map; bus_dma_tag_t aw_dma_tag; void * aw_dma_desc; bus_dmamap_t aw_dma_buf_map; bus_dma_tag_t aw_dma_buf_tag; int aw_dma_map_err; }; static struct resource_spec aw_mmc_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; static int aw_mmc_probe(device_t); static int aw_mmc_attach(device_t); static int aw_mmc_detach(device_t); static int aw_mmc_setup_dma(struct aw_mmc_softc *); static void aw_mmc_teardown_dma(struct aw_mmc_softc *sc); static int aw_mmc_reset(struct aw_mmc_softc *); static int aw_mmc_init(struct aw_mmc_softc *); static void aw_mmc_intr(void *); static int aw_mmc_update_clock(struct aw_mmc_softc *, uint32_t); static void aw_mmc_helper_cd_handler(device_t, bool); static void aw_mmc_print_error(uint32_t); static int aw_mmc_update_ios(device_t, device_t); static int aw_mmc_request(device_t, device_t, struct mmc_request *); #ifndef MMCCAM static int aw_mmc_get_ro(device_t, device_t); static int aw_mmc_acquire_host(device_t, device_t); static int aw_mmc_release_host(device_t, device_t); #endif #define AW_MMC_LOCK(_sc) mtx_lock(&(_sc)->aw_mtx) #define AW_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->aw_mtx) #define AW_MMC_READ_4(_sc, _reg) \ bus_read_4((_sc)->aw_res[AW_MMC_MEMRES], _reg) #define AW_MMC_WRITE_4(_sc, _reg, _value) \ bus_write_4((_sc)->aw_res[AW_MMC_MEMRES], _reg, _value) SYSCTL_NODE(_hw, OID_AUTO, aw_mmc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "aw_mmc driver"); static int aw_mmc_debug = 0; SYSCTL_INT(_hw_aw_mmc, OID_AUTO, debug, CTLFLAG_RWTUN, &aw_mmc_debug, 0, "Debug level bit0=card changes bit1=ios changes, bit2=interrupts, bit3=commands"); #define AW_MMC_DEBUG_CARD 0x1 #define AW_MMC_DEBUG_IOS 0x2 #define AW_MMC_DEBUG_INT 0x4 #define AW_MMC_DEBUG_CMD 0x8 #ifdef MMCCAM static int aw_mmc_get_tran_settings(device_t dev, struct ccb_trans_settings_mmc *cts) { struct aw_mmc_softc *sc; sc = device_get_softc(dev); cts->host_ocr = sc->aw_host.host_ocr; cts->host_f_min = sc->aw_host.f_min; cts->host_f_max = sc->aw_host.f_max; cts->host_caps = sc->aw_host.caps; cts->host_max_data = (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS) / MMC_SECTOR_SIZE; memcpy(&cts->ios, &sc->aw_host.ios, sizeof(struct mmc_ios)); return (0); } static int aw_mmc_set_tran_settings(device_t dev, struct ccb_trans_settings_mmc *cts) { struct aw_mmc_softc *sc; struct mmc_ios *ios; struct mmc_ios *new_ios; sc = device_get_softc(dev); ios = &sc->aw_host.ios; new_ios = &cts->ios; /* Update only requested fields */ if (cts->ios_valid & MMC_CLK) { ios->clock = new_ios->clock; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "Clock => %d\n", ios->clock); } if (cts->ios_valid & MMC_VDD) { ios->vdd = new_ios->vdd; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "VDD => %d\n", ios->vdd); } if (cts->ios_valid & MMC_CS) { ios->chip_select = new_ios->chip_select; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "CS => %d\n", ios->chip_select); } if (cts->ios_valid & MMC_BW) { ios->bus_width = new_ios->bus_width; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "Bus width => %d\n", ios->bus_width); } if (cts->ios_valid & MMC_PM) { ios->power_mode = new_ios->power_mode; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "Power mode => %d\n", ios->power_mode); } if (cts->ios_valid & MMC_BT) { ios->timing = new_ios->timing; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "Timing => %d\n", ios->timing); } if (cts->ios_valid & MMC_BM) { ios->bus_mode = new_ios->bus_mode; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_IOS)) device_printf(sc->aw_dev, "Bus mode => %d\n", ios->bus_mode); } return (aw_mmc_update_ios(sc->aw_dev, NULL)); } static int aw_mmc_cam_request(device_t dev, union ccb *ccb) { struct aw_mmc_softc *sc; struct ccb_mmcio *mmcio; sc = device_get_softc(dev); mmcio = &ccb->mmcio; AW_MMC_LOCK(sc); if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) { device_printf(sc->aw_dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags, mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0, mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0); } if (mmcio->cmd.data != NULL) { if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0) panic("data->len = %d, data->flags = %d -- something is b0rked", (int)mmcio->cmd.data->len, mmcio->cmd.data->flags); } if (sc->ccb != NULL) { device_printf(sc->aw_dev, "Controller still has an active command\n"); return (EBUSY); } sc->ccb = ccb; /* aw_mmc_request locks again */ AW_MMC_UNLOCK(sc); aw_mmc_request(sc->aw_dev, NULL, NULL); return (0); } static void aw_mmc_cam_poll(device_t dev) { struct aw_mmc_softc *sc; sc = device_get_softc(dev); aw_mmc_intr(sc); } #endif /* MMCCAM */ static void aw_mmc_helper_cd_handler(device_t dev, bool present) { struct aw_mmc_softc *sc; sc = device_get_softc(dev); #ifdef MMCCAM mmc_cam_sim_discover(&sc->mmc_sim); #else AW_MMC_LOCK(sc); if (present) { if (sc->child == NULL) { if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD)) device_printf(sc->aw_dev, "Card inserted\n"); sc->child = device_add_child(sc->aw_dev, "mmc", -1); AW_MMC_UNLOCK(sc); if (sc->child) { device_set_ivars(sc->child, sc); (void)device_probe_and_attach(sc->child); } } else AW_MMC_UNLOCK(sc); } else { /* Card isn't present, detach if necessary */ if (sc->child != NULL) { if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD)) device_printf(sc->aw_dev, "Card removed\n"); AW_MMC_UNLOCK(sc); device_delete_child(sc->aw_dev, sc->child); sc->child = NULL; } else AW_MMC_UNLOCK(sc); } #endif /* MMCCAM */ } static int aw_mmc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Integrated MMC/SD controller"); return (BUS_PROBE_DEFAULT); } static int aw_mmc_attach(device_t dev) { struct aw_mmc_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; int error; sc = device_get_softc(dev); sc->aw_dev = dev; sc->aw_mmc_conf = (struct aw_mmc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; #ifndef MMCCAM sc->aw_req = NULL; #endif if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) { device_printf(dev, "cannot allocate device resources\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->aw_res[AW_MMC_IRQRES], INTR_TYPE_NET | INTR_MPSAFE, NULL, aw_mmc_intr, sc, &sc->aw_intrhand)) { bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } mtx_init(&sc->aw_mtx, device_get_nameunit(sc->aw_dev), "aw_mmc", MTX_DEF); callout_init_mtx(&sc->aw_timeoutc, &sc->aw_mtx, 0); /* De-assert reset */ if (hwreset_get_by_ofw_name(dev, 0, "ahb", &sc->aw_rst_ahb) == 0) { error = hwreset_deassert(sc->aw_rst_ahb); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } /* Activate the module clock. */ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->aw_clk_ahb); if (error != 0) { device_printf(dev, "cannot get ahb clock\n"); goto fail; } error = clk_enable(sc->aw_clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "mmc", &sc->aw_clk_mmc); if (error != 0) { device_printf(dev, "cannot get mmc clock\n"); goto fail; } error = clk_set_freq(sc->aw_clk_mmc, CARD_ID_FREQUENCY, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(dev, "cannot init mmc clock\n"); goto fail; } error = clk_enable(sc->aw_clk_mmc); if (error != 0) { device_printf(dev, "cannot enable mmc clock\n"); goto fail; } sc->aw_timeout = 10; ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, &sc->aw_timeout, 0, "Request timeout in seconds"); /* Soft Reset controller. */ if (aw_mmc_reset(sc) != 0) { device_printf(dev, "cannot reset the controller\n"); goto fail; } if (aw_mmc_setup_dma(sc) != 0) { device_printf(sc->aw_dev, "Couldn't setup DMA!\n"); goto fail; } /* Set some defaults for freq and supported mode */ sc->aw_host.f_min = 400000; sc->aw_host.f_max = 52000000; sc->aw_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; sc->aw_host.caps |= MMC_CAP_HSPEED | MMC_CAP_SIGNALING_330; mmc_fdt_parse(dev, 0, &sc->mmc_helper, &sc->aw_host); mmc_fdt_gpio_setup(dev, 0, &sc->mmc_helper, aw_mmc_helper_cd_handler); #ifdef MMCCAM sc->ccb = NULL; if (mmc_cam_sim_alloc(dev, "aw_mmc", &sc->mmc_sim) != 0) { device_printf(dev, "cannot alloc cam sim\n"); goto fail; } #endif /* MMCCAM */ return (0); fail: callout_drain(&sc->aw_timeoutc); mtx_destroy(&sc->aw_mtx); bus_teardown_intr(dev, sc->aw_res[AW_MMC_IRQRES], sc->aw_intrhand); bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res); return (ENXIO); } static int aw_mmc_detach(device_t dev) { struct aw_mmc_softc *sc; device_t d; sc = device_get_softc(dev); clk_disable(sc->aw_clk_mmc); clk_disable(sc->aw_clk_ahb); hwreset_assert(sc->aw_rst_ahb); mmc_fdt_gpio_teardown(&sc->mmc_helper); callout_drain(&sc->aw_timeoutc); AW_MMC_LOCK(sc); d = sc->child; sc->child = NULL; AW_MMC_UNLOCK(sc); if (d != NULL) device_delete_child(sc->aw_dev, d); aw_mmc_teardown_dma(sc); mtx_destroy(&sc->aw_mtx); bus_teardown_intr(dev, sc->aw_res[AW_MMC_IRQRES], sc->aw_intrhand); bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res); #ifdef MMCCAM mmc_cam_sim_free(&sc->mmc_sim); #endif return (0); } static void aw_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { struct aw_mmc_softc *sc; sc = (struct aw_mmc_softc *)arg; if (err) { sc->aw_dma_map_err = err; return; } sc->aw_dma_desc_phys = segs[0].ds_addr; } static int aw_mmc_setup_dma(struct aw_mmc_softc *sc) { int error; /* Allocate the DMA descriptor memory. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->aw_dev), /* parent */ AW_MMC_DMA_ALIGN, 0, /* align, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg*/ AW_MMC_DMA_DESC_SIZE, 1, /* maxsize, nsegment */ AW_MMC_DMA_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lock, lockarg*/ &sc->aw_dma_tag); if (error) return (error); error = bus_dmamem_alloc(sc->aw_dma_tag, &sc->aw_dma_desc, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->aw_dma_map); if (error) return (error); error = bus_dmamap_load(sc->aw_dma_tag, sc->aw_dma_map, sc->aw_dma_desc, AW_MMC_DMA_DESC_SIZE, aw_dma_desc_cb, sc, 0); if (error) return (error); if (sc->aw_dma_map_err) return (sc->aw_dma_map_err); /* Create the DMA map for data transfers. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->aw_dev), /* parent */ AW_MMC_DMA_ALIGN, 0, /* align, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg*/ sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS, /* maxsize, nsegments */ sc->aw_mmc_conf->dma_xferlen, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lock, lockarg*/ &sc->aw_dma_buf_tag); if (error) return (error); error = bus_dmamap_create(sc->aw_dma_buf_tag, 0, &sc->aw_dma_buf_map); if (error) return (error); return (0); } static void aw_mmc_teardown_dma(struct aw_mmc_softc *sc) { bus_dmamap_unload(sc->aw_dma_tag, sc->aw_dma_map); bus_dmamem_free(sc->aw_dma_tag, sc->aw_dma_desc, sc->aw_dma_map); if (bus_dma_tag_destroy(sc->aw_dma_tag) != 0) device_printf(sc->aw_dev, "Cannot destroy the dma tag\n"); bus_dmamap_unload(sc->aw_dma_buf_tag, sc->aw_dma_buf_map); bus_dmamap_destroy(sc->aw_dma_buf_tag, sc->aw_dma_buf_map); if (bus_dma_tag_destroy(sc->aw_dma_buf_tag) != 0) device_printf(sc->aw_dev, "Cannot destroy the dma buf tag\n"); } static void aw_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { int i; struct aw_mmc_dma_desc *dma_desc; struct aw_mmc_softc *sc; sc = (struct aw_mmc_softc *)arg; sc->aw_dma_map_err = err; if (err) return; dma_desc = sc->aw_dma_desc; for (i = 0; i < nsegs; i++) { if (segs[i].ds_len == sc->aw_mmc_conf->dma_xferlen) dma_desc[i].buf_size = 0; /* Size of 0 indicate max len */ else dma_desc[i].buf_size = segs[i].ds_len; dma_desc[i].buf_addr = segs[i].ds_addr; dma_desc[i].config = AW_MMC_DMA_CONFIG_CH | AW_MMC_DMA_CONFIG_OWN | AW_MMC_DMA_CONFIG_DIC; dma_desc[i].next = sc->aw_dma_desc_phys + ((i + 1) * sizeof(struct aw_mmc_dma_desc)); } dma_desc[0].config |= AW_MMC_DMA_CONFIG_FD; dma_desc[nsegs - 1].config |= AW_MMC_DMA_CONFIG_LD | AW_MMC_DMA_CONFIG_ER; dma_desc[nsegs - 1].config &= ~AW_MMC_DMA_CONFIG_DIC; dma_desc[nsegs - 1].next = 0; } static int aw_mmc_prepare_dma(struct aw_mmc_softc *sc) { bus_dmasync_op_t sync_op; int error; struct mmc_command *cmd; uint32_t val; #ifdef MMCCAM cmd = &sc->ccb->mmcio.cmd; #else cmd = sc->aw_req->cmd; #endif if (cmd->data->len > (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS)) return (EFBIG); error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, cmd->data->data, cmd->data->len, aw_dma_cb, sc, 0); if (error) return (error); if (sc->aw_dma_map_err) return (sc->aw_dma_map_err); if (cmd->data->flags & MMC_DATA_WRITE) sync_op = BUS_DMASYNC_PREWRITE; else sync_op = BUS_DMASYNC_PREREAD; bus_dmamap_sync(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, sync_op); bus_dmamap_sync(sc->aw_dma_tag, sc->aw_dma_map, BUS_DMASYNC_PREWRITE); /* Enable DMA */ val = AW_MMC_READ_4(sc, AW_MMC_GCTL); val &= ~AW_MMC_GCTL_FIFO_AC_MOD; val |= AW_MMC_GCTL_DMA_ENB; AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val); /* Reset DMA */ val |= AW_MMC_GCTL_DMA_RST; AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val); AW_MMC_WRITE_4(sc, AW_MMC_DMAC, AW_MMC_DMAC_IDMAC_SOFT_RST); AW_MMC_WRITE_4(sc, AW_MMC_DMAC, AW_MMC_DMAC_IDMAC_IDMA_ON | AW_MMC_DMAC_IDMAC_FIX_BURST); /* Enable RX or TX DMA interrupt */ val = AW_MMC_READ_4(sc, AW_MMC_IDIE); if (cmd->data->flags & MMC_DATA_WRITE) val |= AW_MMC_IDST_TX_INT; else val |= AW_MMC_IDST_RX_INT; AW_MMC_WRITE_4(sc, AW_MMC_IDIE, val); /* Set DMA descritptor list address */ AW_MMC_WRITE_4(sc, AW_MMC_DLBA, sc->aw_dma_desc_phys); /* FIFO trigger level */ AW_MMC_WRITE_4(sc, AW_MMC_FWLR, AW_MMC_DMA_FTRGLEVEL); return (0); } static int aw_mmc_reset(struct aw_mmc_softc *sc) { uint32_t reg; int timeout; reg = AW_MMC_READ_4(sc, AW_MMC_GCTL); reg |= AW_MMC_GCTL_RESET; AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg); timeout = AW_MMC_RESET_RETRY; while (--timeout > 0) { if ((AW_MMC_READ_4(sc, AW_MMC_GCTL) & AW_MMC_GCTL_RESET) == 0) break; DELAY(100); } if (timeout == 0) return (ETIMEDOUT); return (0); } static int aw_mmc_init(struct aw_mmc_softc *sc) { uint32_t reg; int ret; ret = aw_mmc_reset(sc); if (ret != 0) return (ret); /* Set the timeout. */ AW_MMC_WRITE_4(sc, AW_MMC_TMOR, AW_MMC_TMOR_DTO_LMT_SHIFT(AW_MMC_TMOR_DTO_LMT_MASK) | AW_MMC_TMOR_RTO_LMT_SHIFT(AW_MMC_TMOR_RTO_LMT_MASK)); /* Unmask interrupts. */ AW_MMC_WRITE_4(sc, AW_MMC_IMKR, 0); /* Clear pending interrupts. */ AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff); /* Debug register, undocumented */ AW_MMC_WRITE_4(sc, AW_MMC_DBGC, 0xdeb); /* Function select register */ AW_MMC_WRITE_4(sc, AW_MMC_FUNS, 0xceaa0000); AW_MMC_WRITE_4(sc, AW_MMC_IDST, 0xffffffff); /* Enable interrupts and disable AHB access. */ reg = AW_MMC_READ_4(sc, AW_MMC_GCTL); reg |= AW_MMC_GCTL_INT_ENB; reg &= ~AW_MMC_GCTL_FIFO_AC_MOD; reg &= ~AW_MMC_GCTL_WAIT_MEM_ACCESS; AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg); return (0); } static void aw_mmc_req_done(struct aw_mmc_softc *sc) { struct mmc_command *cmd; #ifdef MMCCAM union ccb *ccb; #else struct mmc_request *req; #endif uint32_t val, mask; int retry; #ifdef MMCCAM ccb = sc->ccb; cmd = &ccb->mmcio.cmd; #else cmd = sc->aw_req->cmd; #endif if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) { device_printf(sc->aw_dev, "%s: cmd %d err %d\n", __func__, cmd->opcode, cmd->error); } if (cmd->error != MMC_ERR_NONE) { /* Reset the FIFO and DMA engines. */ mask = AW_MMC_GCTL_FIFO_RST | AW_MMC_GCTL_DMA_RST; val = AW_MMC_READ_4(sc, AW_MMC_GCTL); AW_MMC_WRITE_4(sc, AW_MMC_GCTL, val | mask); retry = AW_MMC_RESET_RETRY; while (--retry > 0) { if ((AW_MMC_READ_4(sc, AW_MMC_GCTL) & AW_MMC_GCTL_RESET) == 0) break; DELAY(100); } if (retry == 0) device_printf(sc->aw_dev, "timeout resetting DMA/FIFO\n"); aw_mmc_update_clock(sc, 1); } if (!dumping) callout_stop(&sc->aw_timeoutc); sc->aw_intr = 0; sc->aw_resid = 0; sc->aw_dma_map_err = 0; sc->aw_intr_wait = 0; #ifdef MMCCAM sc->ccb = NULL; ccb->ccb_h.status = (ccb->mmcio.cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR); xpt_done(ccb); #else req = sc->aw_req; sc->aw_req = NULL; req->done(req); #endif } static void aw_mmc_req_ok(struct aw_mmc_softc *sc) { int timeout; struct mmc_command *cmd; uint32_t status; timeout = 1000; while (--timeout > 0) { status = AW_MMC_READ_4(sc, AW_MMC_STAR); if ((status & AW_MMC_STAR_CARD_BUSY) == 0) break; DELAY(1000); } #ifdef MMCCAM cmd = &sc->ccb->mmcio.cmd; #else cmd = sc->aw_req->cmd; #endif if (timeout == 0) { cmd->error = MMC_ERR_FAILED; aw_mmc_req_done(sc); return; } if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { cmd->resp[0] = AW_MMC_READ_4(sc, AW_MMC_RESP3); cmd->resp[1] = AW_MMC_READ_4(sc, AW_MMC_RESP2); cmd->resp[2] = AW_MMC_READ_4(sc, AW_MMC_RESP1); cmd->resp[3] = AW_MMC_READ_4(sc, AW_MMC_RESP0); } else cmd->resp[0] = AW_MMC_READ_4(sc, AW_MMC_RESP0); } /* All data has been transferred ? */ if (cmd->data != NULL && (sc->aw_resid << 2) < cmd->data->len) cmd->error = MMC_ERR_FAILED; aw_mmc_req_done(sc); } static inline void set_mmc_error(struct aw_mmc_softc *sc, int error_code) { #ifdef MMCCAM sc->ccb->mmcio.cmd.error = error_code; #else sc->aw_req->cmd->error = error_code; #endif } static void aw_mmc_timeout(void *arg) { struct aw_mmc_softc *sc; sc = (struct aw_mmc_softc *)arg; #ifdef MMCCAM if (sc->ccb != NULL) { #else if (sc->aw_req != NULL) { #endif device_printf(sc->aw_dev, "controller timeout\n"); set_mmc_error(sc, MMC_ERR_TIMEOUT); aw_mmc_req_done(sc); } else device_printf(sc->aw_dev, "Spurious timeout - no active request\n"); } static void aw_mmc_print_error(uint32_t err) { if(err & AW_MMC_INT_RESP_ERR) printf("AW_MMC_INT_RESP_ERR "); if (err & AW_MMC_INT_RESP_CRC_ERR) printf("AW_MMC_INT_RESP_CRC_ERR "); if (err & AW_MMC_INT_DATA_CRC_ERR) printf("AW_MMC_INT_DATA_CRC_ERR "); if (err & AW_MMC_INT_RESP_TIMEOUT) printf("AW_MMC_INT_RESP_TIMEOUT "); if (err & AW_MMC_INT_FIFO_RUN_ERR) printf("AW_MMC_INT_FIFO_RUN_ERR "); if (err & AW_MMC_INT_CMD_BUSY) printf("AW_MMC_INT_CMD_BUSY "); if (err & AW_MMC_INT_DATA_START_ERR) printf("AW_MMC_INT_DATA_START_ERR "); if (err & AW_MMC_INT_DATA_END_BIT_ERR) printf("AW_MMC_INT_DATA_END_BIT_ERR"); printf("\n"); } static void aw_mmc_intr(void *arg) { bus_dmasync_op_t sync_op; struct aw_mmc_softc *sc; struct mmc_data *data; uint32_t idst, imask, rint; sc = (struct aw_mmc_softc *)arg; AW_MMC_LOCK(sc); rint = AW_MMC_READ_4(sc, AW_MMC_RISR); idst = AW_MMC_READ_4(sc, AW_MMC_IDST); imask = AW_MMC_READ_4(sc, AW_MMC_IMKR); if (idst == 0 && imask == 0 && rint == 0) { AW_MMC_UNLOCK(sc); return; } if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT)) { device_printf(sc->aw_dev, "idst: %#x, imask: %#x, rint: %#x\n", idst, imask, rint); } #ifdef MMCCAM if (sc->ccb == NULL) { #else if (sc->aw_req == NULL) { #endif device_printf(sc->aw_dev, "Spurious interrupt - no active request, rint: 0x%08X\n", rint); aw_mmc_print_error(rint); goto end; } if (rint & AW_MMC_INT_ERR_BIT) { if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT)) { device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint); aw_mmc_print_error(rint); } if (rint & AW_MMC_INT_RESP_TIMEOUT) set_mmc_error(sc, MMC_ERR_TIMEOUT); else set_mmc_error(sc, MMC_ERR_FAILED); aw_mmc_req_done(sc); goto end; } if (idst & AW_MMC_IDST_ERROR) { if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_INT)) device_printf(sc->aw_dev, "error idst: 0x%08x\n", idst); set_mmc_error(sc, MMC_ERR_FAILED); aw_mmc_req_done(sc); goto end; } sc->aw_intr |= rint; #ifdef MMCCAM data = sc->ccb->mmcio.cmd.data; #else data = sc->aw_req->cmd->data; #endif if (data != NULL && (idst & AW_MMC_IDST_COMPLETE) != 0) { if (data->flags & MMC_DATA_WRITE) sync_op = BUS_DMASYNC_POSTWRITE; else sync_op = BUS_DMASYNC_POSTREAD; bus_dmamap_sync(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, sync_op); bus_dmamap_sync(sc->aw_dma_tag, sc->aw_dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->aw_dma_buf_tag, sc->aw_dma_buf_map); sc->aw_resid = data->len >> 2; } if ((sc->aw_intr & sc->aw_intr_wait) == sc->aw_intr_wait) aw_mmc_req_ok(sc); end: AW_MMC_WRITE_4(sc, AW_MMC_IDST, idst); AW_MMC_WRITE_4(sc, AW_MMC_RISR, rint); AW_MMC_UNLOCK(sc); } static int aw_mmc_request(device_t bus, device_t child, struct mmc_request *req) { int blksz; struct aw_mmc_softc *sc; struct mmc_command *cmd; uint32_t cmdreg, imask; int err; sc = device_get_softc(bus); AW_MMC_LOCK(sc); #ifdef MMCCAM KASSERT(req == NULL, ("req should be NULL in MMCCAM case!")); /* * For MMCCAM, sc->ccb has been NULL-checked and populated * by aw_mmc_cam_request() already. */ cmd = &sc->ccb->mmcio.cmd; #else if (sc->aw_req) { AW_MMC_UNLOCK(sc); return (EBUSY); } sc->aw_req = req; cmd = req->cmd; if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CMD)) { device_printf(sc->aw_dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", cmd->opcode, cmd->arg, cmd->flags, cmd->data != NULL ? (unsigned int)cmd->data->len : 0, cmd->data != NULL ? cmd->data->flags: 0); } #endif cmdreg = AW_MMC_CMDR_LOAD; imask = AW_MMC_INT_ERR_BIT; sc->aw_intr_wait = 0; sc->aw_intr = 0; sc->aw_resid = 0; cmd->error = MMC_ERR_NONE; if (cmd->opcode == MMC_GO_IDLE_STATE) cmdreg |= AW_MMC_CMDR_SEND_INIT_SEQ; if (cmd->flags & MMC_RSP_PRESENT) cmdreg |= AW_MMC_CMDR_RESP_RCV; if (cmd->flags & MMC_RSP_136) cmdreg |= AW_MMC_CMDR_LONG_RESP; if (cmd->flags & MMC_RSP_CRC) cmdreg |= AW_MMC_CMDR_CHK_RESP_CRC; if (cmd->data) { cmdreg |= AW_MMC_CMDR_DATA_TRANS | AW_MMC_CMDR_WAIT_PRE_OVER; if (cmd->data->flags & MMC_DATA_MULTI) { cmdreg |= AW_MMC_CMDR_STOP_CMD_FLAG; imask |= AW_MMC_INT_AUTO_STOP_DONE; sc->aw_intr_wait |= AW_MMC_INT_AUTO_STOP_DONE; } else { sc->aw_intr_wait |= AW_MMC_INT_DATA_OVER; imask |= AW_MMC_INT_DATA_OVER; } if (cmd->data->flags & MMC_DATA_WRITE) cmdreg |= AW_MMC_CMDR_DIR_WRITE; #ifdef MMCCAM if (cmd->data->flags & MMC_DATA_BLOCK_SIZE) { AW_MMC_WRITE_4(sc, AW_MMC_BKSR, cmd->data->block_size); AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len); } else #endif { blksz = min(cmd->data->len, MMC_SECTOR_SIZE); AW_MMC_WRITE_4(sc, AW_MMC_BKSR, blksz); AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len); } } else { imask |= AW_MMC_INT_CMD_DONE; } /* Enable the interrupts we are interested in */ AW_MMC_WRITE_4(sc, AW_MMC_IMKR, imask); AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff); /* Enable auto stop if needed */ AW_MMC_WRITE_4(sc, AW_MMC_A12A, cmdreg & AW_MMC_CMDR_STOP_CMD_FLAG ? 0 : 0xffff); /* Write the command argument */ AW_MMC_WRITE_4(sc, AW_MMC_CAGR, cmd->arg); /* * If we don't have data start the request * if we do prepare the dma request and start the request */ if (cmd->data == NULL) { AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg | cmd->opcode); } else { err = aw_mmc_prepare_dma(sc); if (err != 0) device_printf(sc->aw_dev, "prepare_dma failed: %d\n", err); AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg | cmd->opcode); } if (!dumping) { callout_reset(&sc->aw_timeoutc, sc->aw_timeout * hz, aw_mmc_timeout, sc); } AW_MMC_UNLOCK(sc); return (0); } static int aw_mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct aw_mmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: *(int *)result = sc->aw_host.ios.bus_mode; break; case MMCBR_IVAR_BUS_WIDTH: *(int *)result = sc->aw_host.ios.bus_width; break; case MMCBR_IVAR_CHIP_SELECT: *(int *)result = sc->aw_host.ios.chip_select; break; case MMCBR_IVAR_CLOCK: *(int *)result = sc->aw_host.ios.clock; break; case MMCBR_IVAR_F_MIN: *(int *)result = sc->aw_host.f_min; break; case MMCBR_IVAR_F_MAX: *(int *)result = sc->aw_host.f_max; break; case MMCBR_IVAR_HOST_OCR: *(int *)result = sc->aw_host.host_ocr; break; case MMCBR_IVAR_MODE: *(int *)result = sc->aw_host.mode; break; case MMCBR_IVAR_OCR: *(int *)result = sc->aw_host.ocr; break; case MMCBR_IVAR_POWER_MODE: *(int *)result = sc->aw_host.ios.power_mode; break; case MMCBR_IVAR_VDD: *(int *)result = sc->aw_host.ios.vdd; break; case MMCBR_IVAR_VCCQ: *(int *)result = sc->aw_host.ios.vccq; break; case MMCBR_IVAR_CAPS: *(int *)result = sc->aw_host.caps; break; case MMCBR_IVAR_TIMING: *(int *)result = sc->aw_host.ios.timing; break; case MMCBR_IVAR_MAX_DATA: *(int *)result = (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS) / MMC_SECTOR_SIZE; break; case MMCBR_IVAR_RETUNE_REQ: *(int *)result = retune_req_none; break; } return (0); } static int aw_mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct aw_mmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: sc->aw_host.ios.bus_mode = value; break; case MMCBR_IVAR_BUS_WIDTH: sc->aw_host.ios.bus_width = value; break; case MMCBR_IVAR_CHIP_SELECT: sc->aw_host.ios.chip_select = value; break; case MMCBR_IVAR_CLOCK: sc->aw_host.ios.clock = value; break; case MMCBR_IVAR_MODE: sc->aw_host.mode = value; break; case MMCBR_IVAR_OCR: sc->aw_host.ocr = value; break; case MMCBR_IVAR_POWER_MODE: sc->aw_host.ios.power_mode = value; break; case MMCBR_IVAR_VDD: sc->aw_host.ios.vdd = value; break; case MMCBR_IVAR_VCCQ: sc->aw_host.ios.vccq = value; break; case MMCBR_IVAR_TIMING: sc->aw_host.ios.timing = value; break; /* These are read-only */ case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: case MMCBR_IVAR_F_MAX: case MMCBR_IVAR_MAX_DATA: return (EINVAL); } return (0); } static int aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon) { uint32_t reg; int retry; reg = AW_MMC_READ_4(sc, AW_MMC_CKCR); reg &= ~(AW_MMC_CKCR_ENB | AW_MMC_CKCR_LOW_POWER | AW_MMC_CKCR_MASK_DATA0); if (clkon) reg |= AW_MMC_CKCR_ENB; if (sc->aw_mmc_conf->mask_data0) reg |= AW_MMC_CKCR_MASK_DATA0; AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg); reg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK | AW_MMC_CMDR_WAIT_PRE_OVER; AW_MMC_WRITE_4(sc, AW_MMC_CMDR, reg); retry = 0xfffff; while (reg & AW_MMC_CMDR_LOAD && --retry > 0) { reg = AW_MMC_READ_4(sc, AW_MMC_CMDR); DELAY(10); } AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff); if (reg & AW_MMC_CMDR_LOAD) { device_printf(sc->aw_dev, "timeout updating clock\n"); return (ETIMEDOUT); } if (sc->aw_mmc_conf->mask_data0) { reg = AW_MMC_READ_4(sc, AW_MMC_CKCR); reg &= ~AW_MMC_CKCR_MASK_DATA0; AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg); } return (0); } #ifndef MMCCAM static int aw_mmc_switch_vccq(device_t bus, device_t child) { struct aw_mmc_softc *sc; int uvolt, err; sc = device_get_softc(bus); if (sc->mmc_helper.vqmmc_supply == NULL) return EOPNOTSUPP; switch (sc->aw_host.ios.vccq) { case vccq_180: uvolt = 1800000; break; case vccq_330: uvolt = 3300000; break; default: return EINVAL; } err = regulator_set_voltage(sc->mmc_helper.vqmmc_supply, uvolt, uvolt); if (err != 0) { device_printf(sc->aw_dev, "Cannot set vqmmc to %d<->%d\n", uvolt, uvolt); return (err); } return (0); } #endif static int aw_mmc_update_ios(device_t bus, device_t child) { int error; struct aw_mmc_softc *sc; struct mmc_ios *ios; unsigned int clock; uint32_t reg, div = 1; int reg_status; int rv; sc = device_get_softc(bus); ios = &sc->aw_host.ios; /* Set the bus width. */ switch (ios->bus_width) { case bus_width_1: AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR1); break; case bus_width_4: AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR4); break; case bus_width_8: AW_MMC_WRITE_4(sc, AW_MMC_BWDR, AW_MMC_BWDR8); break; } switch (ios->power_mode) { case power_on: break; case power_off: if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD)) device_printf(sc->aw_dev, "Powering down sd/mmc\n"); if (sc->mmc_helper.vmmc_supply) { rv = regulator_status(sc->mmc_helper.vmmc_supply, ®_status); if (rv == 0 && reg_status == REGULATOR_STATUS_ENABLED) regulator_disable(sc->mmc_helper.vmmc_supply); } if (sc->mmc_helper.vqmmc_supply) { rv = regulator_status(sc->mmc_helper.vqmmc_supply, ®_status); if (rv == 0 && reg_status == REGULATOR_STATUS_ENABLED) regulator_disable(sc->mmc_helper.vqmmc_supply); } if (sc->mmc_helper.mmc_pwrseq) MMC_PWRSEQ_SET_POWER(sc->mmc_helper.mmc_pwrseq, false); aw_mmc_reset(sc); break; case power_up: if (__predict_false(aw_mmc_debug & AW_MMC_DEBUG_CARD)) device_printf(sc->aw_dev, "Powering up sd/mmc\n"); if (sc->mmc_helper.vmmc_supply) { rv = regulator_status(sc->mmc_helper.vmmc_supply, ®_status); if (rv == 0 && reg_status != REGULATOR_STATUS_ENABLED) regulator_enable(sc->mmc_helper.vmmc_supply); } if (sc->mmc_helper.vqmmc_supply) { rv = regulator_status(sc->mmc_helper.vqmmc_supply, ®_status); if (rv == 0 && reg_status != REGULATOR_STATUS_ENABLED) regulator_enable(sc->mmc_helper.vqmmc_supply); } if (sc->mmc_helper.mmc_pwrseq) MMC_PWRSEQ_SET_POWER(sc->mmc_helper.mmc_pwrseq, true); aw_mmc_init(sc); break; }; /* Enable ddr mode if needed */ reg = AW_MMC_READ_4(sc, AW_MMC_GCTL); if (ios->timing == bus_timing_uhs_ddr50 || ios->timing == bus_timing_mmc_ddr52) reg |= AW_MMC_GCTL_DDR_MOD_SEL; else reg &= ~AW_MMC_GCTL_DDR_MOD_SEL; AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg); if (ios->clock && ios->clock != sc->aw_clock) { sc->aw_clock = clock = ios->clock; /* Disable clock */ error = aw_mmc_update_clock(sc, 0); if (error != 0) return (error); if (ios->timing == bus_timing_mmc_ddr52 && (sc->aw_mmc_conf->new_timing || ios->bus_width == bus_width_8)) { div = 2; clock <<= 1; } /* Reset the divider. */ reg = AW_MMC_READ_4(sc, AW_MMC_CKCR); reg &= ~AW_MMC_CKCR_DIV; reg |= div - 1; AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg); /* New timing mode if needed */ if (sc->aw_mmc_conf->new_timing) { reg = AW_MMC_READ_4(sc, AW_MMC_NTSR); reg |= AW_MMC_NTSR_MODE_SELECT; AW_MMC_WRITE_4(sc, AW_MMC_NTSR, reg); } /* Set the MMC clock. */ error = clk_disable(sc->aw_clk_mmc); if (error != 0 && bootverbose) device_printf(sc->aw_dev, "failed to disable mmc clock: %d\n", error); error = clk_set_freq(sc->aw_clk_mmc, clock, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(sc->aw_dev, "failed to set frequency to %u Hz: %d\n", clock, error); return (error); } error = clk_enable(sc->aw_clk_mmc); if (error != 0 && bootverbose) device_printf(sc->aw_dev, "failed to re-enable mmc clock: %d\n", error); if (sc->aw_mmc_conf->can_calibrate) AW_MMC_WRITE_4(sc, AW_MMC_SAMP_DL, AW_MMC_SAMP_DL_SW_EN); /* Enable clock. */ error = aw_mmc_update_clock(sc, 1); if (error != 0) return (error); } return (0); } #ifndef MMCCAM static int aw_mmc_get_ro(device_t bus, device_t child) { struct aw_mmc_softc *sc; sc = device_get_softc(bus); return (mmc_fdt_gpio_get_readonly(&sc->mmc_helper)); } static int aw_mmc_acquire_host(device_t bus, device_t child) { struct aw_mmc_softc *sc; int error; sc = device_get_softc(bus); AW_MMC_LOCK(sc); while (sc->aw_bus_busy) { error = msleep(sc, &sc->aw_mtx, PCATCH, "mmchw", 0); if (error != 0) { AW_MMC_UNLOCK(sc); return (error); } } sc->aw_bus_busy++; AW_MMC_UNLOCK(sc); return (0); } static int aw_mmc_release_host(device_t bus, device_t child) { struct aw_mmc_softc *sc; sc = device_get_softc(bus); AW_MMC_LOCK(sc); sc->aw_bus_busy--; wakeup(sc); AW_MMC_UNLOCK(sc); return (0); } #endif static device_method_t aw_mmc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_mmc_probe), DEVMETHOD(device_attach, aw_mmc_attach), DEVMETHOD(device_detach, aw_mmc_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, aw_mmc_read_ivar), DEVMETHOD(bus_write_ivar, aw_mmc_write_ivar), DEVMETHOD(bus_add_child, bus_generic_add_child), #ifndef MMCCAM /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, aw_mmc_update_ios), DEVMETHOD(mmcbr_request, aw_mmc_request), DEVMETHOD(mmcbr_get_ro, aw_mmc_get_ro), DEVMETHOD(mmcbr_switch_vccq, aw_mmc_switch_vccq), DEVMETHOD(mmcbr_acquire_host, aw_mmc_acquire_host), DEVMETHOD(mmcbr_release_host, aw_mmc_release_host), #endif #ifdef MMCCAM /* MMCCAM interface */ DEVMETHOD(mmc_sim_get_tran_settings, aw_mmc_get_tran_settings), DEVMETHOD(mmc_sim_set_tran_settings, aw_mmc_set_tran_settings), DEVMETHOD(mmc_sim_cam_request, aw_mmc_cam_request), DEVMETHOD(mmc_sim_cam_poll, aw_mmc_cam_poll), #endif DEVMETHOD_END }; static driver_t aw_mmc_driver = { "aw_mmc", aw_mmc_methods, sizeof(struct aw_mmc_softc), }; DRIVER_MODULE(aw_mmc, simplebus, aw_mmc_driver, NULL, NULL); #ifndef MMCCAM MMC_DECLARE_BRIDGE(aw_mmc); #endif SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/aw_reset.c b/sys/arm/allwinner/aw_reset.c index da2879ce20c8..4fadaf2bfef4 100644 --- a/sys/arm/allwinner/aw_reset.c +++ b/sys/arm/allwinner/aw_reset.c @@ -1,158 +1,158 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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. */ /* * Allwinner module software reset registers */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "hwreset_if.h" #define RESET_OFFSET(index) ((index / 32) * 4) #define RESET_SHIFT(index) (index % 32) static struct ofw_compat_data compat_data[] = { { "allwinner,sun6i-a31-ahb1-reset", 1 }, { "allwinner,sun6i-a31-clock-reset", 1 }, { NULL, 0 } }; struct aw_reset_softc { struct resource *res; struct mtx mtx; }; static struct resource_spec aw_reset_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define RESET_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define RESET_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static int aw_reset_assert(device_t dev, intptr_t id, bool reset) { struct aw_reset_softc *sc; uint32_t reg_value; sc = device_get_softc(dev); mtx_lock(&sc->mtx); reg_value = RESET_READ(sc, RESET_OFFSET(id)); if (reset) reg_value &= ~(1 << RESET_SHIFT(id)); else reg_value |= (1 << RESET_SHIFT(id)); RESET_WRITE(sc, RESET_OFFSET(id), reg_value); mtx_unlock(&sc->mtx); return (0); } static int aw_reset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct aw_reset_softc *sc; uint32_t reg_value; sc = device_get_softc(dev); mtx_lock(&sc->mtx); reg_value = RESET_READ(sc, RESET_OFFSET(id)); mtx_unlock(&sc->mtx); *reset = (reg_value & (1 << RESET_SHIFT(id))) != 0 ? false : true; return (0); } static int aw_reset_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Module Resets"); return (BUS_PROBE_DEFAULT); } static int aw_reset_attach(device_t dev) { struct aw_reset_softc *sc; sc = device_get_softc(dev); if (bus_alloc_resources(dev, aw_reset_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); hwreset_register_ofw_provider(dev); return (0); } static device_method_t aw_reset_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_reset_probe), DEVMETHOD(device_attach, aw_reset_attach), /* Reset interface */ DEVMETHOD(hwreset_assert, aw_reset_assert), DEVMETHOD(hwreset_is_asserted, aw_reset_is_asserted), DEVMETHOD_END }; static driver_t aw_reset_driver = { "aw_reset", aw_reset_methods, sizeof(struct aw_reset_softc), }; EARLY_DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(aw_reset, 1); diff --git a/sys/arm/allwinner/aw_rsb.c b/sys/arm/allwinner/aw_rsb.c index 08522caff725..fa599ddcb3dd 100644 --- a/sys/arm/allwinner/aw_rsb.c +++ b/sys/arm/allwinner/aw_rsb.c @@ -1,505 +1,505 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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. */ /* * Allwinner RSB (Reduced Serial Bus) and P2WI (Push-Pull Two Wire Interface) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "iicbus_if.h" #define RSB_CTRL 0x00 #define START_TRANS (1 << 7) #define GLOBAL_INT_ENB (1 << 1) #define SOFT_RESET (1 << 0) #define RSB_CCR 0x04 #define RSB_INTE 0x08 #define RSB_INTS 0x0c #define INT_TRANS_ERR_ID(x) (((x) >> 8) & 0xf) #define INT_LOAD_BSY (1 << 2) #define INT_TRANS_ERR (1 << 1) #define INT_TRANS_OVER (1 << 0) #define INT_MASK (INT_LOAD_BSY|INT_TRANS_ERR|INT_TRANS_OVER) #define RSB_DADDR0 0x10 #define RSB_DADDR1 0x14 #define RSB_DLEN 0x18 #define DLEN_READ (1 << 4) #define RSB_DATA0 0x1c #define RSB_DATA1 0x20 #define RSB_PMCR 0x28 #define RSB_PMCR_START (1 << 31) #define RSB_PMCR_DATA(x) (x << 16) #define RSB_PMCR_REG(x) (x << 8) #define RSB_CMD 0x2c #define CMD_SRTA 0xe8 #define CMD_RD8 0x8b #define CMD_RD16 0x9c #define CMD_RD32 0xa6 #define CMD_WR8 0x4e #define CMD_WR16 0x59 #define CMD_WR32 0x63 #define RSB_DAR 0x30 #define DAR_RTA (0xff << 16) #define DAR_RTA_SHIFT 16 #define DAR_DA (0xffff << 0) #define DAR_DA_SHIFT 0 #define RSB_MAXLEN 8 #define RSB_RESET_RETRY 100 #define RSB_I2C_TIMEOUT hz #define RSB_ADDR_PMIC_PRIMARY 0x3a3 #define RSB_ADDR_PMIC_SECONDARY 0x745 #define RSB_ADDR_PERIPH_IC 0xe89 #define PMIC_MODE_REG 0x3e #define PMIC_MODE_I2C 0x00 #define PMIC_MODE_RSB 0x7c #define A31_P2WI 1 #define A23_RSB 2 static struct ofw_compat_data compat_data[] = { { "allwinner,sun6i-a31-p2wi", A31_P2WI }, { "allwinner,sun8i-a23-rsb", A23_RSB }, { NULL, 0 } }; static struct resource_spec rsb_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; /* * Device address to Run-time address mappings. * * Run-time address (RTA) is an 8-bit value used to address the device during * a read or write transaction. The following are valid RTAs: * 0x17 0x2d 0x3a 0x4e 0x59 0x63 0x74 0x8b 0x9c 0xa6 0xb1 0xc5 0xd2 0xe8 0xff * * Allwinner uses RTA 0x2d for the primary PMIC, 0x3a for the secondary PMIC, * and 0x4e for the peripheral IC (where applicable). */ static const struct { uint16_t addr; uint8_t rta; } rsb_rtamap[] = { { .addr = RSB_ADDR_PMIC_PRIMARY, .rta = 0x2d }, { .addr = RSB_ADDR_PMIC_SECONDARY, .rta = 0x3a }, { .addr = RSB_ADDR_PERIPH_IC, .rta = 0x4e }, { .addr = 0, .rta = 0 } }; struct rsb_softc { struct resource *res; struct mtx mtx; clk_t clk; hwreset_t rst; device_t iicbus; int busy; uint32_t status; uint16_t cur_addr; int type; struct iic_msg *msg; }; #define RSB_LOCK(sc) mtx_lock(&(sc)->mtx) #define RSB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RSB_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) #define RSB_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define RSB_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static phandle_t rsb_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static int rsb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct rsb_softc *sc; int retry; sc = device_get_softc(dev); RSB_LOCK(sc); /* Write soft-reset bit and wait for it to self-clear. */ RSB_WRITE(sc, RSB_CTRL, SOFT_RESET); for (retry = RSB_RESET_RETRY; retry > 0; retry--) if ((RSB_READ(sc, RSB_CTRL) & SOFT_RESET) == 0) break; RSB_UNLOCK(sc); if (retry == 0) { device_printf(dev, "soft reset timeout\n"); return (ETIMEDOUT); } return (IIC_ENOADDR); } static uint32_t rsb_encode(const uint8_t *buf, u_int len, u_int off) { uint32_t val; u_int n; val = 0; for (n = off; n < MIN(len, 4 + off); n++) val |= ((uint32_t)buf[n] << ((n - off) * NBBY)); return val; } static void rsb_decode(const uint32_t val, uint8_t *buf, u_int len, u_int off) { u_int n; for (n = off; n < MIN(len, 4 + off); n++) buf[n] = (val >> ((n - off) * NBBY)) & 0xff; } static int rsb_start(device_t dev) { struct rsb_softc *sc; int error, retry; sc = device_get_softc(dev); RSB_ASSERT_LOCKED(sc); /* Start the transfer */ RSB_WRITE(sc, RSB_CTRL, GLOBAL_INT_ENB | START_TRANS); /* Wait for transfer to complete */ error = ETIMEDOUT; for (retry = RSB_I2C_TIMEOUT; retry > 0; retry--) { sc->status |= RSB_READ(sc, RSB_INTS); if ((sc->status & INT_TRANS_OVER) != 0) { error = 0; break; } DELAY((1000 * hz) / RSB_I2C_TIMEOUT); } if (error == 0 && (sc->status & INT_TRANS_OVER) == 0) { device_printf(dev, "transfer error, status 0x%08x\n", sc->status); error = EIO; } return (error); } static int rsb_set_rta(device_t dev, uint16_t addr) { struct rsb_softc *sc; uint8_t rta; int i; sc = device_get_softc(dev); RSB_ASSERT_LOCKED(sc); /* Lookup run-time address for given device address */ for (rta = 0, i = 0; rsb_rtamap[i].rta != 0; i++) if (rsb_rtamap[i].addr == addr) { rta = rsb_rtamap[i].rta; break; } if (rta == 0) { device_printf(dev, "RTA not known for address %#x\n", addr); return (ENXIO); } /* Set run-time address */ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); RSB_WRITE(sc, RSB_DAR, (addr << DAR_DA_SHIFT) | (rta << DAR_RTA_SHIFT)); RSB_WRITE(sc, RSB_CMD, CMD_SRTA); return (rsb_start(dev)); } static int rsb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct rsb_softc *sc; uint32_t daddr[2], data[2], dlen; uint16_t device_addr; uint8_t cmd; int error; sc = device_get_softc(dev); /* * P2WI and RSB are not really I2C or SMBus controllers, so there are * some restrictions imposed by the driver. * * Transfers must contain exactly two messages. The first is always * a write, containing a single data byte offset. Data will either * be read from or written to the corresponding data byte in the * second message. The slave address in both messages must be the * same. */ if (nmsgs != 2 || (msgs[0].flags & IIC_M_RD) == IIC_M_RD || (msgs[0].slave >> 1) != (msgs[1].slave >> 1) || msgs[0].len != 1 || msgs[1].len > RSB_MAXLEN) return (EINVAL); /* The RSB controller can read or write 1, 2, or 4 bytes at a time. */ if (sc->type == A23_RSB) { if ((msgs[1].flags & IIC_M_RD) != 0) { switch (msgs[1].len) { case 1: cmd = CMD_RD8; break; case 2: cmd = CMD_RD16; break; case 4: cmd = CMD_RD32; break; default: return (EINVAL); } } else { switch (msgs[1].len) { case 1: cmd = CMD_WR8; break; case 2: cmd = CMD_WR16; break; case 4: cmd = CMD_WR32; break; default: return (EINVAL); } } } RSB_LOCK(sc); while (sc->busy) mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0); sc->busy = 1; sc->status = 0; /* Select current run-time address if necessary */ if (sc->type == A23_RSB) { device_addr = msgs[0].slave >> 1; if (sc->cur_addr != device_addr) { error = rsb_set_rta(dev, device_addr); if (error != 0) goto done; sc->cur_addr = device_addr; sc->status = 0; } } /* Clear interrupt status */ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); /* Program data access address registers */ daddr[0] = rsb_encode(msgs[0].buf, msgs[0].len, 0); RSB_WRITE(sc, RSB_DADDR0, daddr[0]); /* Write data */ if ((msgs[1].flags & IIC_M_RD) == 0) { data[0] = rsb_encode(msgs[1].buf, msgs[1].len, 0); RSB_WRITE(sc, RSB_DATA0, data[0]); } /* Set command type for RSB */ if (sc->type == A23_RSB) RSB_WRITE(sc, RSB_CMD, cmd); /* Program data length register and transfer direction */ dlen = msgs[0].len - 1; if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) dlen |= DLEN_READ; RSB_WRITE(sc, RSB_DLEN, dlen); /* Start transfer */ error = rsb_start(dev); if (error != 0) goto done; /* Read data */ if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) { data[0] = RSB_READ(sc, RSB_DATA0); rsb_decode(data[0], msgs[1].buf, msgs[1].len, 0); } done: sc->msg = NULL; sc->busy = 0; wakeup(sc); RSB_UNLOCK(sc); return (error); } static int rsb_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A23_RSB: device_set_desc(dev, "Allwinner RSB"); break; case A31_P2WI: device_set_desc(dev, "Allwinner P2WI"); break; default: return (ENXIO); } return (BUS_PROBE_DEFAULT); } static int rsb_attach(device_t dev) { struct rsb_softc *sc; int error; sc = device_get_softc(dev); mtx_init(&sc->mtx, device_get_nameunit(dev), "rsb", MTX_DEF); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) == 0) { error = clk_enable(sc->clk); if (error != 0) { device_printf(dev, "cannot enable clock\n"); goto fail; } } if (hwreset_get_by_ofw_idx(dev, 0, 0, &sc->rst) == 0) { error = hwreset_deassert(sc->rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } if (bus_alloc_resources(dev, rsb_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } /* Set the PMIC into RSB mode as ATF might have leave it in I2C mode */ RSB_WRITE(sc, RSB_PMCR, RSB_PMCR_REG(PMIC_MODE_REG) | RSB_PMCR_DATA(PMIC_MODE_RSB) | RSB_PMCR_START); sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "cannot add iicbus child device\n"); error = ENXIO; goto fail; } bus_generic_attach(dev); return (0); fail: bus_release_resources(dev, rsb_spec, &sc->res); if (sc->rst != NULL) hwreset_release(sc->rst); if (sc->clk != NULL) clk_release(sc->clk); mtx_destroy(&sc->mtx); return (error); } static device_method_t rsb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rsb_probe), DEVMETHOD(device_attach, rsb_attach), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, rsb_get_node), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, rsb_reset), DEVMETHOD(iicbus_transfer, rsb_transfer), DEVMETHOD_END }; static driver_t rsb_driver = { "iichb", rsb_methods, sizeof(struct rsb_softc), }; EARLY_DRIVER_MODULE(iicbus, rsb, iicbus_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(rsb, simplebus, rsb_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(rsb, 1); MODULE_DEPEND(rsb, iicbus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/aw_thermal.c b/sys/arm/allwinner/aw_thermal.c index 4f1e02612347..d9293b5cb171 100644 --- a/sys/arm/allwinner/aw_thermal.c +++ b/sys/arm/allwinner/aw_thermal.c @@ -1,724 +1,724 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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. */ /* * Allwinner thermal sensor controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "cpufreq_if.h" #include "nvmem_if.h" #define THS_CTRL0 0x00 #define THS_CTRL1 0x04 #define ADC_CALI_EN (1 << 17) #define THS_CTRL2 0x40 #define SENSOR_ACQ1_SHIFT 16 #define SENSOR2_EN (1 << 2) #define SENSOR1_EN (1 << 1) #define SENSOR0_EN (1 << 0) #define THS_INTC 0x44 #define THS_THERMAL_PER_SHIFT 12 #define THS_INTS 0x48 #define THS2_DATA_IRQ_STS (1 << 10) #define THS1_DATA_IRQ_STS (1 << 9) #define THS0_DATA_IRQ_STS (1 << 8) #define SHUT_INT2_STS (1 << 6) #define SHUT_INT1_STS (1 << 5) #define SHUT_INT0_STS (1 << 4) #define ALARM_INT2_STS (1 << 2) #define ALARM_INT1_STS (1 << 1) #define ALARM_INT0_STS (1 << 0) #define THS_ALARM0_CTRL 0x50 #define ALARM_T_HOT_MASK 0xfff #define ALARM_T_HOT_SHIFT 16 #define ALARM_T_HYST_MASK 0xfff #define ALARM_T_HYST_SHIFT 0 #define THS_SHUTDOWN0_CTRL 0x60 #define SHUT_T_HOT_MASK 0xfff #define SHUT_T_HOT_SHIFT 16 #define THS_FILTER 0x70 #define THS_CALIB0 0x74 #define THS_CALIB1 0x78 #define THS_DATA0 0x80 #define THS_DATA1 0x84 #define THS_DATA2 0x88 #define DATA_MASK 0xfff #define A83T_CLK_RATE 24000000 #define A83T_ADC_ACQUIRE_TIME 23 /* 24Mhz/(23 + 1) = 1us */ #define A83T_THERMAL_PER 1 /* 4096 * (1 + 1) / 24Mhz = 341 us */ #define A83T_FILTER 0x5 /* Filter enabled, avg of 4 */ #define A83T_TEMP_BASE 2719000 #define A83T_TEMP_MUL 1000 #define A83T_TEMP_DIV 14186 #define A64_CLK_RATE 4000000 #define A64_ADC_ACQUIRE_TIME 400 /* 4Mhz/(400 + 1) = 100 us */ #define A64_THERMAL_PER 24 /* 4096 * (24 + 1) / 4Mhz = 25.6 ms */ #define A64_FILTER 0x6 /* Filter enabled, avg of 8 */ #define A64_TEMP_BASE 2170000 #define A64_TEMP_MUL 1000 #define A64_TEMP_DIV 8560 #define H3_CLK_RATE 4000000 #define H3_ADC_ACQUIRE_TIME 0x3f #define H3_THERMAL_PER 401 #define H3_FILTER 0x6 /* Filter enabled, avg of 8 */ #define H3_TEMP_BASE 217 #define H3_TEMP_MUL 1000 #define H3_TEMP_DIV 8253 #define H3_TEMP_MINUS 1794000 #define H3_INIT_ALARM 90 /* degC */ #define H3_INIT_SHUT 105 /* degC */ #define H5_CLK_RATE 24000000 #define H5_ADC_ACQUIRE_TIME 479 /* 24Mhz/479 = 20us */ #define H5_THERMAL_PER 58 /* 4096 * (58 + 1) / 24Mhz = 10ms */ #define H5_FILTER 0x6 /* Filter enabled, avg of 8 */ #define H5_TEMP_BASE 233832448 #define H5_TEMP_MUL 124885 #define H5_TEMP_DIV 20 #define H5_TEMP_BASE_CPU 271581184 #define H5_TEMP_MUL_CPU 152253 #define H5_TEMP_BASE_GPU 289406976 #define H5_TEMP_MUL_GPU 166724 #define H5_INIT_CPU_ALARM 80 /* degC */ #define H5_INIT_CPU_SHUT 96 /* degC */ #define H5_INIT_GPU_ALARM 84 /* degC */ #define H5_INIT_GPU_SHUT 100 /* degC */ #define TEMP_C_TO_K 273 #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) #define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS) #define ALARM_INT_ALL (ALARM_INT0_STS) #define MAX_SENSORS 3 #define MAX_CF_LEVELS 64 #define THROTTLE_ENABLE_DEFAULT 1 /* Enable thermal throttling */ static int aw_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT; TUNABLE_INT("hw.aw_thermal.throttle_enable", &aw_thermal_throttle_enable); struct aw_thermal_sensor { const char *name; const char *desc; int init_alarm; int init_shut; }; struct aw_thermal_config { struct aw_thermal_sensor sensors[MAX_SENSORS]; int nsensors; uint64_t clk_rate; uint32_t adc_acquire_time; int adc_cali_en; uint32_t filter; uint32_t thermal_per; int (*to_temp)(uint32_t, int); uint32_t (*to_reg)(int, int); int temp_base; int temp_mul; int temp_div; int calib0, calib1; uint32_t calib0_mask, calib1_mask; }; static int a83t_to_temp(uint32_t val, int sensor) { return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV); } static const struct aw_thermal_config a83t_config = { .nsensors = 3, .sensors = { [0] = { .name = "cluster0", .desc = "CPU cluster 0 temperature", }, [1] = { .name = "cluster1", .desc = "CPU cluster 1 temperature", }, [2] = { .name = "gpu", .desc = "GPU temperature", }, }, .clk_rate = A83T_CLK_RATE, .adc_acquire_time = A83T_ADC_ACQUIRE_TIME, .adc_cali_en = 1, .filter = A83T_FILTER, .thermal_per = A83T_THERMAL_PER, .to_temp = a83t_to_temp, .calib0_mask = 0xffffffff, .calib1_mask = 0xffff, }; static int a64_to_temp(uint32_t val, int sensor) { return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV); } static const struct aw_thermal_config a64_config = { .nsensors = 3, .sensors = { [0] = { .name = "cpu", .desc = "CPU temperature", }, [1] = { .name = "gpu1", .desc = "GPU temperature 1", }, [2] = { .name = "gpu2", .desc = "GPU temperature 2", }, }, .clk_rate = A64_CLK_RATE, .adc_acquire_time = A64_ADC_ACQUIRE_TIME, .adc_cali_en = 1, .filter = A64_FILTER, .thermal_per = A64_THERMAL_PER, .to_temp = a64_to_temp, .calib0_mask = 0xffffffff, .calib1_mask = 0xffff, }; static int h3_to_temp(uint32_t val, int sensor) { return (H3_TEMP_BASE - ((val * H3_TEMP_MUL) / H3_TEMP_DIV)); } static uint32_t h3_to_reg(int val, int sensor) { return ((H3_TEMP_MINUS - (val * H3_TEMP_DIV)) / H3_TEMP_MUL); } static const struct aw_thermal_config h3_config = { .nsensors = 1, .sensors = { [0] = { .name = "cpu", .desc = "CPU temperature", .init_alarm = H3_INIT_ALARM, .init_shut = H3_INIT_SHUT, }, }, .clk_rate = H3_CLK_RATE, .adc_acquire_time = H3_ADC_ACQUIRE_TIME, .adc_cali_en = 1, .filter = H3_FILTER, .thermal_per = H3_THERMAL_PER, .to_temp = h3_to_temp, .to_reg = h3_to_reg, .calib0_mask = 0xffffffff, }; static int h5_to_temp(uint32_t val, int sensor) { int tmp; /* Temp is lower than 70 degrees */ if (val > 0x500) { tmp = H5_TEMP_BASE - (val * H5_TEMP_MUL); tmp >>= H5_TEMP_DIV; return (tmp); } if (sensor == 0) tmp = H5_TEMP_BASE_CPU - (val * H5_TEMP_MUL_CPU); else if (sensor == 1) tmp = H5_TEMP_BASE_GPU - (val * H5_TEMP_MUL_GPU); else { printf("Unknown sensor %d\n", sensor); return (val); } tmp >>= H5_TEMP_DIV; return (tmp); } static uint32_t h5_to_reg(int val, int sensor) { int tmp; if (val < 70) { tmp = H5_TEMP_BASE - (val << H5_TEMP_DIV); tmp /= H5_TEMP_MUL; } else { if (sensor == 0) { tmp = H5_TEMP_BASE_CPU - (val << H5_TEMP_DIV); tmp /= H5_TEMP_MUL_CPU; } else if (sensor == 1) { tmp = H5_TEMP_BASE_GPU - (val << H5_TEMP_DIV); tmp /= H5_TEMP_MUL_GPU; } else { printf("Unknown sensor %d\n", sensor); return (val); } } return ((uint32_t)tmp); } static const struct aw_thermal_config h5_config = { .nsensors = 2, .sensors = { [0] = { .name = "cpu", .desc = "CPU temperature", .init_alarm = H5_INIT_CPU_ALARM, .init_shut = H5_INIT_CPU_SHUT, }, [1] = { .name = "gpu", .desc = "GPU temperature", .init_alarm = H5_INIT_GPU_ALARM, .init_shut = H5_INIT_GPU_SHUT, }, }, .clk_rate = H5_CLK_RATE, .adc_acquire_time = H5_ADC_ACQUIRE_TIME, .filter = H5_FILTER, .thermal_per = H5_THERMAL_PER, .to_temp = h5_to_temp, .to_reg = h5_to_reg, .calib0_mask = 0xffffffff, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-a83t-ths", (uintptr_t)&a83t_config }, { "allwinner,sun8i-h3-ths", (uintptr_t)&h3_config }, { "allwinner,sun50i-a64-ths", (uintptr_t)&a64_config }, { "allwinner,sun50i-h5-ths", (uintptr_t)&h5_config }, { NULL, (uintptr_t)NULL } }; #define THS_CONF(d) \ (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data struct aw_thermal_softc { device_t dev; struct resource *res[2]; struct aw_thermal_config *conf; struct task cf_task; int throttle; int min_freq; struct cf_level levels[MAX_CF_LEVELS]; eventhandler_tag cf_pre_tag; clk_t clk_apb; clk_t clk_ths; }; static struct resource_spec aw_thermal_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; #define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int aw_thermal_init(struct aw_thermal_softc *sc) { phandle_t node; uint32_t calib[2]; int error; node = ofw_bus_get_node(sc->dev); if (nvmem_get_cell_len(node, "calibration") > sizeof(calib)) { device_printf(sc->dev, "calibration nvmem cell is too large\n"); return (ENXIO); } error = nvmem_read_cell_by_name(node, "calibration", (void *)&calib, nvmem_get_cell_len(node, "calibration")); /* Read calibration settings from EFUSE */ if (error != 0) { device_printf(sc->dev, "Cannot read THS efuse\n"); return (error); } calib[0] &= sc->conf->calib0_mask; calib[1] &= sc->conf->calib1_mask; /* Write calibration settings to thermal controller */ if (calib[0] != 0) WR4(sc, THS_CALIB0, calib[0]); if (calib[1] != 0) WR4(sc, THS_CALIB1, calib[1]); /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ WR4(sc, THS_CTRL1, ADC_CALI_EN); WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time); WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT); /* Set thermal period */ WR4(sc, THS_INTC, sc->conf->thermal_per << THS_THERMAL_PER_SHIFT); /* Enable average filter */ WR4(sc, THS_FILTER, sc->conf->filter); /* Enable interrupts */ WR4(sc, THS_INTS, RD4(sc, THS_INTS)); WR4(sc, THS_INTC, RD4(sc, THS_INTC) | SHUT_INT_ALL | ALARM_INT_ALL); /* Enable sensors */ WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL); return (0); } static int aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) { uint32_t val; val = RD4(sc, THS_DATA0 + (sensor * 4)); return (sc->conf->to_temp(val, sensor)); } static int aw_thermal_getshut(struct aw_thermal_softc *sc, int sensor) { uint32_t val; val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4)); val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK; return (sc->conf->to_temp(val, sensor)); } static void aw_thermal_setshut(struct aw_thermal_softc *sc, int sensor, int temp) { uint32_t val; val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4)); val &= ~(SHUT_T_HOT_MASK << SHUT_T_HOT_SHIFT); val |= (sc->conf->to_reg(temp, sensor) << SHUT_T_HOT_SHIFT); WR4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4), val); } static int aw_thermal_gethyst(struct aw_thermal_softc *sc, int sensor) { uint32_t val; val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK; return (sc->conf->to_temp(val, sensor)); } static int aw_thermal_getalarm(struct aw_thermal_softc *sc, int sensor) { uint32_t val; val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK; return (sc->conf->to_temp(val, sensor)); } static void aw_thermal_setalarm(struct aw_thermal_softc *sc, int sensor, int temp) { uint32_t val; val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); val &= ~(ALARM_T_HOT_MASK << ALARM_T_HOT_SHIFT); val |= (sc->conf->to_reg(temp, sensor) << ALARM_T_HOT_SHIFT); WR4(sc, THS_ALARM0_CTRL + (sensor * 4), val); } static int aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) { struct aw_thermal_softc *sc; int sensor, val; sc = arg1; sensor = arg2; val = aw_thermal_gettemp(sc, sensor) + TEMP_C_TO_K; return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } static void aw_thermal_throttle(struct aw_thermal_softc *sc, int enable) { device_t cf_dev; int count, error; if (enable == sc->throttle) return; if (enable != 0) { /* Set the lowest available frequency */ cf_dev = devclass_get_device(devclass_find("cpufreq"), 0); if (cf_dev == NULL) return; count = MAX_CF_LEVELS; error = CPUFREQ_LEVELS(cf_dev, sc->levels, &count); if (error != 0 || count == 0) return; sc->min_freq = sc->levels[count - 1].total_set.freq; error = CPUFREQ_SET(cf_dev, &sc->levels[count - 1], CPUFREQ_PRIO_USER); if (error != 0) return; } sc->throttle = enable; } static void aw_thermal_cf_task(void *arg, int pending) { struct aw_thermal_softc *sc; sc = arg; aw_thermal_throttle(sc, 1); } static void aw_thermal_cf_pre_change(void *arg, const struct cf_level *level, int *status) { struct aw_thermal_softc *sc; int temp_cur, temp_alarm; sc = arg; if (aw_thermal_throttle_enable == 0 || sc->throttle == 0 || level->total_set.freq == sc->min_freq) return; temp_cur = aw_thermal_gettemp(sc, 0); temp_alarm = aw_thermal_getalarm(sc, 0); if (temp_cur < temp_alarm) aw_thermal_throttle(sc, 0); else *status = ENXIO; } static void aw_thermal_intr(void *arg) { struct aw_thermal_softc *sc; device_t dev; uint32_t ints; dev = arg; sc = device_get_softc(dev); ints = RD4(sc, THS_INTS); WR4(sc, THS_INTS, ints); if ((ints & SHUT_INT_ALL) != 0) { device_printf(dev, "WARNING - current temperature exceeds safe limits\n"); shutdown_nice(RB_POWEROFF); } if ((ints & ALARM_INT_ALL) != 0) taskqueue_enqueue(taskqueue_thread, &sc->cf_task); } static int aw_thermal_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (THS_CONF(dev) == NULL) return (ENXIO); device_set_desc(dev, "Allwinner Thermal Sensor Controller"); return (BUS_PROBE_DEFAULT); } static int aw_thermal_attach(device_t dev) { struct aw_thermal_softc *sc; hwreset_t rst; int i, error; void *ih; sc = device_get_softc(dev); sc->dev = dev; rst = NULL; ih = NULL; sc->conf = THS_CONF(dev); TASK_INIT(&sc->cf_task, 0, aw_thermal_cf_task, sc); if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_apb) == 0) { error = clk_enable(sc->clk_apb); if (error != 0) { device_printf(dev, "cannot enable apb clock\n"); goto fail; } } if (clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_ths) == 0) { error = clk_set_freq(sc->clk_ths, sc->conf->clk_rate, 0); if (error != 0) { device_printf(dev, "cannot set ths clock rate\n"); goto fail; } error = clk_enable(sc->clk_ths); if (error != 0) { device_printf(dev, "cannot enable ths clock\n"); goto fail; } } if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_thermal_intr, dev, &ih); if (error != 0) { device_printf(dev, "cannot setup interrupt handler\n"); goto fail; } for (i = 0; i < sc->conf->nsensors; i++) { if (sc->conf->sensors[i].init_alarm > 0) aw_thermal_setalarm(sc, i, sc->conf->sensors[i].init_alarm); if (sc->conf->sensors[i].init_shut > 0) aw_thermal_setshut(sc, i, sc->conf->sensors[i].init_shut); } if (aw_thermal_init(sc) != 0) goto fail; for (i = 0; i < sc->conf->nsensors; i++) SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, sc->conf->sensors[i].name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i, aw_thermal_sysctl, "IK0", sc->conf->sensors[i].desc); if (bootverbose) for (i = 0; i < sc->conf->nsensors; i++) { device_printf(dev, "%s: alarm %dC hyst %dC shut %dC\n", sc->conf->sensors[i].name, aw_thermal_getalarm(sc, i), aw_thermal_gethyst(sc, i), aw_thermal_getshut(sc, i)); } sc->cf_pre_tag = EVENTHANDLER_REGISTER(cpufreq_pre_change, aw_thermal_cf_pre_change, sc, EVENTHANDLER_PRI_FIRST); return (0); fail: if (ih != NULL) bus_teardown_intr(dev, sc->res[1], ih); if (rst != NULL) hwreset_release(rst); if (sc->clk_apb != NULL) clk_release(sc->clk_apb); if (sc->clk_ths != NULL) clk_release(sc->clk_ths); bus_release_resources(dev, aw_thermal_spec, sc->res); return (ENXIO); } static device_method_t aw_thermal_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_thermal_probe), DEVMETHOD(device_attach, aw_thermal_attach), DEVMETHOD_END }; static driver_t aw_thermal_driver = { "aw_thermal", aw_thermal_methods, sizeof(struct aw_thermal_softc), }; DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, 0, 0); MODULE_VERSION(aw_thermal, 1); MODULE_DEPEND(aw_thermal, aw_sid, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/allwinner/aw_usb3phy.c b/sys/arm/allwinner/aw_usb3phy.c index 058fce6061a7..b49a9a86b6e9 100644 --- a/sys/arm/allwinner/aw_usb3phy.c +++ b/sys/arm/allwinner/aw_usb3phy.c @@ -1,294 +1,294 @@ /* $NetBSD: sunxi_usb3phy.c,v 1.1 2018/05/01 23:59:42 jmcneill Exp $ */ /*- * Copyright (c) 2018 Jared McNeill * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Allwinner USB3PHY */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "phynode_if.h" #define USB3PHY_APP 0x00 #define APP_FORCE_VBUS (0x3 << 12) #define USB3PHY_PIPE_CLOCK_CONTROL 0x14 #define PCC_PIPE_CLK_OPEN (1 << 6) #define USB3PHY_PHY_TUNE_LOW 0x18 #define PTL_MAGIC 0x0047fc87 #define USB3PHY_PHY_TUNE_HIGH 0x1c #define PTH_TX_DEEMPH_3P5DB (0x1F << 19) #define PTH_TX_DEEMPH_6DB (0x3F << 13) #define PTH_TX_SWING_FULL (0x7F << 6) #define PTH_LOS_BIAS (0x7 << 3) #define PTH_TX_BOOST_LVL (0x7 << 0) #define USB3PHY_PHY_EXTERNAL_CONTROL 0x20 #define PEC_REF_SSP_EN (1 << 26) #define PEC_SSC_EN (1 << 24) #define PEC_EXTERN_VBUS (0x3 << 1) #define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) #define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) static struct ofw_compat_data compat_data[] = { { "allwinner,sun50i-h6-usb3-phy", 1 }, { NULL, 0 } }; static struct resource_spec aw_usb3phy_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; struct awusb3phy_softc { struct resource * res; regulator_t reg; int mode; }; /* Phy class and methods. */ static int awusb3phy_phy_enable(struct phynode *phy, bool enable); static int awusb3phy_get_mode(struct phynode *phy, int *mode); static int awusb3phy_set_mode(struct phynode *phy, int mode); static phynode_usb_method_t awusb3phy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, awusb3phy_phy_enable), PHYNODEMETHOD(phynode_usb_get_mode, awusb3phy_get_mode), PHYNODEMETHOD(phynode_usb_set_mode, awusb3phy_set_mode), PHYNODEMETHOD_END }; DEFINE_CLASS_1(awusb3phy_phynode, awusb3phy_phynode_class, awusb3phy_phynode_methods, sizeof(struct phynode_usb_sc), phynode_usb_class); #define RD4(res, o) bus_read_4(res, (o)) #define WR4(res, o, v) bus_write_4(res, (o), (v)) static int awusb3phy_phy_enable(struct phynode *phynode, bool enable) { struct awusb3phy_softc *sc; device_t dev; uint32_t val; int error = 0; dev = phynode_get_device(phynode); sc = device_get_softc(dev); device_printf(dev, "%s: called\n", __func__); if (enable) { val = RD4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL); device_printf(dev, "EXTERNAL_CONTROL: %x\n", val); val |= PEC_EXTERN_VBUS; val |= PEC_SSC_EN; val |= PEC_REF_SSP_EN; device_printf(dev, "EXTERNAL_CONTROL: %x\n", val); WR4(sc->res, USB3PHY_PHY_EXTERNAL_CONTROL, val); val = RD4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL); device_printf(dev, "PIPE_CONTROL: %x\n", val); val |= PCC_PIPE_CLK_OPEN; device_printf(dev, "PIPE_CONTROL: %x\n", val); WR4(sc->res, USB3PHY_PIPE_CLOCK_CONTROL, val); val = RD4(sc->res, USB3PHY_APP); device_printf(dev, "APP: %x\n", val); val |= APP_FORCE_VBUS; device_printf(dev, "APP: %x\n", val); WR4(sc->res, USB3PHY_APP, val); WR4(sc->res, USB3PHY_PHY_TUNE_LOW, PTL_MAGIC); val = RD4(sc->res, USB3PHY_PHY_TUNE_HIGH); device_printf(dev, "PHY_TUNE_HIGH: %x\n", val); val |= PTH_TX_BOOST_LVL; val |= PTH_LOS_BIAS; val &= ~PTH_TX_SWING_FULL; val |= __SHIFTIN(0x55, PTH_TX_SWING_FULL); val &= ~PTH_TX_DEEMPH_6DB; val |= __SHIFTIN(0x20, PTH_TX_DEEMPH_6DB); val &= ~PTH_TX_DEEMPH_3P5DB; val |= __SHIFTIN(0x15, PTH_TX_DEEMPH_3P5DB); device_printf(dev, "PHY_TUNE_HIGH: %x\n", val); WR4(sc->res, USB3PHY_PHY_TUNE_HIGH, val); if (sc->reg) error = regulator_enable(sc->reg); } else { if (sc->reg) error = regulator_disable(sc->reg); } if (error != 0) { device_printf(dev, "couldn't %s regulator for phy\n", enable ? "enable" : "disable"); return (error); } return (0); } static int awusb3phy_get_mode(struct phynode *phynode, int *mode) { struct awusb3phy_softc *sc; device_t dev; dev = phynode_get_device(phynode); sc = device_get_softc(dev); *mode = sc->mode; return (0); } static int awusb3phy_set_mode(struct phynode *phynode, int mode) { device_t dev; struct awusb3phy_softc *sc; dev = phynode_get_device(phynode); sc = device_get_softc(dev); if (mode != PHY_USB_MODE_HOST) return (EINVAL); sc->mode = mode; return (0); } static int awusb3phy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner USB3PHY"); return (BUS_PROBE_DEFAULT); } static int awusb3phy_attach(device_t dev) { struct phynode *phynode; struct phynode_init_def phy_init; struct awusb3phy_softc *sc; clk_t clk; hwreset_t rst; phandle_t node; int error, i; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); if (bus_alloc_resources(dev, aw_usb3phy_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } /* Enable clocks */ for (i = 0; clk_get_by_ofw_index(dev, 0, i, &clk) == 0; i++) { error = clk_enable(clk); if (error != 0) { device_printf(dev, "couldn't enable clock %s\n", clk_get_name(clk)); return (error); } } /* De-assert resets */ for (i = 0; hwreset_get_by_ofw_idx(dev, 0, i, &rst) == 0; i++) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "couldn't de-assert reset %d\n", i); return (error); } } /* Get regulators */ regulator_get_by_ofw_property(dev, node, "phy-supply", &sc->reg); /* Create the phy */ phy_init.ofw_node = ofw_bus_get_node(dev); phynode = phynode_create(dev, &awusb3phy_phynode_class, &phy_init); if (phynode == NULL) { device_printf(dev, "failed to create USB PHY\n"); return (ENXIO); } if (phynode_register(phynode) == NULL) { device_printf(dev, "failed to create USB PHY\n"); return (ENXIO); } return (error); } static device_method_t awusb3phy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awusb3phy_probe), DEVMETHOD(device_attach, awusb3phy_attach), DEVMETHOD_END }; static driver_t awusb3phy_driver = { "awusb3phy", awusb3phy_methods, sizeof(struct awusb3phy_softc) }; /* aw_usb3phy needs to come up after regulators/gpio/etc, but before ehci/ohci */ EARLY_DRIVER_MODULE(awusb3phy, simplebus, awusb3phy_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(awusb3phy, 1); diff --git a/sys/arm/allwinner/aw_usbphy.c b/sys/arm/allwinner/aw_usbphy.c index 33c11e62ef7c..09725783fd22 100644 --- a/sys/arm/allwinner/aw_usbphy.c +++ b/sys/arm/allwinner/aw_usbphy.c @@ -1,524 +1,524 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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. */ /* * Allwinner USB PHY */ #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "phynode_if.h" enum awusbphy_type { AWUSBPHY_TYPE_A10 = 1, AWUSBPHY_TYPE_A13, AWUSBPHY_TYPE_A20, AWUSBPHY_TYPE_A31, AWUSBPHY_TYPE_H3, AWUSBPHY_TYPE_A64, AWUSBPHY_TYPE_A83T, AWUSBPHY_TYPE_H6, }; struct aw_usbphy_conf { int num_phys; enum awusbphy_type phy_type; bool pmu_unk1; bool phy0_route; }; static const struct aw_usbphy_conf a10_usbphy_conf = { .num_phys = 3, .phy_type = AWUSBPHY_TYPE_A10, .pmu_unk1 = false, .phy0_route = false, }; static const struct aw_usbphy_conf a13_usbphy_conf = { .num_phys = 2, .phy_type = AWUSBPHY_TYPE_A13, .pmu_unk1 = false, .phy0_route = false, }; static const struct aw_usbphy_conf a20_usbphy_conf = { .num_phys = 3, .phy_type = AWUSBPHY_TYPE_A20, .pmu_unk1 = false, .phy0_route = false, }; static const struct aw_usbphy_conf a31_usbphy_conf = { .num_phys = 3, .phy_type = AWUSBPHY_TYPE_A31, .pmu_unk1 = false, .phy0_route = false, }; static const struct aw_usbphy_conf h3_usbphy_conf = { .num_phys = 4, .phy_type = AWUSBPHY_TYPE_H3, .pmu_unk1 = true, .phy0_route = true, }; static const struct aw_usbphy_conf a64_usbphy_conf = { .num_phys = 2, .phy_type = AWUSBPHY_TYPE_A64, .pmu_unk1 = true, .phy0_route = true, }; static const struct aw_usbphy_conf a83t_usbphy_conf = { .num_phys = 3, .phy_type = AWUSBPHY_TYPE_A83T, .pmu_unk1 = false, .phy0_route = false, }; static const struct aw_usbphy_conf h6_usbphy_conf = { .num_phys = 4, .phy_type = AWUSBPHY_TYPE_H6, .pmu_unk1 = false, .phy0_route = true, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-usb-phy", (uintptr_t)&a10_usbphy_conf }, { "allwinner,sun5i-a13-usb-phy", (uintptr_t)&a13_usbphy_conf }, { "allwinner,sun6i-a31-usb-phy", (uintptr_t)&a31_usbphy_conf }, { "allwinner,sun7i-a20-usb-phy", (uintptr_t)&a20_usbphy_conf }, { "allwinner,sun8i-h3-usb-phy", (uintptr_t)&h3_usbphy_conf }, { "allwinner,sun50i-a64-usb-phy", (uintptr_t)&a64_usbphy_conf }, { "allwinner,sun8i-a83t-usb-phy", (uintptr_t)&a83t_usbphy_conf }, { "allwinner,sun50i-h6-usb-phy", (uintptr_t)&h6_usbphy_conf }, { NULL, 0 } }; struct awusbphy_softc { struct resource * phy_ctrl; struct resource ** pmu; regulator_t * reg; gpio_pin_t id_det_pin; int id_det_valid; gpio_pin_t vbus_det_pin; int vbus_det_valid; struct aw_usbphy_conf *phy_conf; int mode; }; /* Phy class and methods. */ static int awusbphy_phy_enable(struct phynode *phy, bool enable); static int awusbphy_get_mode(struct phynode *phy, int *mode); static int awusbphy_set_mode(struct phynode *phy, int mode); static phynode_usb_method_t awusbphy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable), PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode), PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode), PHYNODEMETHOD_END }; DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods, sizeof(struct phynode_usb_sc), phynode_usb_class); #define RD4(res, o) bus_read_4(res, (o)) #define WR4(res, o, v) bus_write_4(res, (o), (v)) #define CLR4(res, o, m) WR4(res, o, RD4(res, o) & ~(m)) #define SET4(res, o, m) WR4(res, o, RD4(res, o) | (m)) #define PHY_CSR 0x00 #define ID_PULLUP_EN (1 << 17) #define DPDM_PULLUP_EN (1 << 16) #define FORCE_ID (0x3 << 14) #define FORCE_ID_SHIFT 14 #define FORCE_ID_LOW 2 #define FORCE_ID_HIGH 3 #define FORCE_VBUS_VALID (0x3 << 12) #define FORCE_VBUS_VALID_SHIFT 12 #define FORCE_VBUS_VALID_LOW 2 #define FORCE_VBUS_VALID_HIGH 3 #define VBUS_CHANGE_DET (1 << 6) #define ID_CHANGE_DET (1 << 5) #define DPDM_CHANGE_DET (1 << 4) #define OTG_PHY_CFG 0x20 #define OTG_PHY_ROUTE_OTG (1 << 0) #define PMU_IRQ_ENABLE 0x00 #define PMU_AHB_INCR8 (1 << 10) #define PMU_AHB_INCR4 (1 << 9) #define PMU_AHB_INCRX_ALIGN (1 << 8) #define PMU_ULPI_BYPASS (1 << 0) #define PMU_UNK_H3 0x10 #define PMU_UNK_H3_CLR 0x2 static void awusbphy_configure(device_t dev, int phyno) { struct awusbphy_softc *sc; sc = device_get_softc(dev); if (sc->pmu[phyno] == NULL) return; if (sc->phy_conf->pmu_unk1 == true) CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR); SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS | PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN); } static int awusbphy_init(device_t dev) { struct awusbphy_softc *sc; phandle_t node; char pname[20]; uint32_t val; int error, off, rid; regulator_t reg; hwreset_t rst; clk_t clk; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Get phy_ctrl region */ if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) { device_printf(dev, "Cannot locate phy control resource\n"); return (ENXIO); } sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->phy_ctrl == NULL) { device_printf(dev, "Cannot allocate resource\n"); return (ENXIO); } /* Enable clocks */ for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) { error = clk_enable(clk); if (error != 0) { device_printf(dev, "couldn't enable clock %s\n", clk_get_name(clk)); return (error); } } /* De-assert resets */ for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "couldn't de-assert reset %d\n", off); return (error); } } /* Get GPIOs */ error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios", &sc->id_det_pin); if (error == 0) sc->id_det_valid = 1; error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios", &sc->vbus_det_pin); if (error == 0) sc->vbus_det_valid = 1; sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF, M_WAITOK | M_ZERO); sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF, M_WAITOK | M_ZERO); /* Get regulators */ for (off = 0; off < sc->phy_conf->num_phys; off++) { snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); if (regulator_get_by_ofw_property(dev, 0, pname, ®) == 0) sc->reg[off] = reg; snprintf(pname, sizeof(pname), "pmu%d", off); if (ofw_bus_find_string_index(node, "reg-names", pname, &rid) != 0) continue; sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->pmu[off] == NULL) { device_printf(dev, "Cannot allocate resource\n"); return (ENXIO); } } /* Enable OTG PHY for host mode */ val = bus_read_4(sc->phy_ctrl, PHY_CSR); val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET); val |= (ID_PULLUP_EN | DPDM_PULLUP_EN); val &= ~FORCE_ID; val |= (FORCE_ID_LOW << FORCE_ID_SHIFT); val &= ~FORCE_VBUS_VALID; val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT); bus_write_4(sc->phy_ctrl, PHY_CSR, val); return (0); } static int awusbphy_vbus_detect(device_t dev, int *val) { struct awusbphy_softc *sc; bool active; int error; sc = device_get_softc(dev); if (sc->vbus_det_valid) { error = gpio_pin_is_active(sc->vbus_det_pin, &active); if (error != 0) { device_printf(dev, "Cannot get status of id pin %d\n", error); return (error); } *val = active; return (0); } /* TODO check vbus_power-supply. */ /* * If there is no way to detect, assume present. */ *val = 1; return (0); } static int awusbphy_phy_enable(struct phynode *phynode, bool enable) { device_t dev; intptr_t phy; struct awusbphy_softc *sc; regulator_t reg; int error, vbus_det; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (phy < 0 || phy >= sc->phy_conf->num_phys) return (ERANGE); /* Configure PHY */ awusbphy_configure(dev, phy); /* Regulators are optional. If not found, return success. */ reg = sc->reg[phy]; if (reg == NULL) return (0); if (phy == 0) { /* If an external vbus is detected, do not enable phy 0 */ error = awusbphy_vbus_detect(dev, &vbus_det); if (error) goto out; /* TODO check vbus_power-supply as well. */ if (sc->vbus_det_valid && vbus_det == 1) { if (bootverbose) device_printf(dev, "External VBUS detected, " "not enabling the regulator\n"); return (0); } } if (enable) { /* Depending on the PHY we need to route OTG to OHCI/EHCI */ error = regulator_enable(reg); } else error = regulator_disable(reg); out: if (error != 0) { device_printf(dev, "couldn't %s regulator for phy %jd\n", enable ? "enable" : "disable", (intmax_t)phy); return (error); } return (0); } static int awusbphy_get_mode(struct phynode *phynode, int *mode) { struct awusbphy_softc *sc; device_t dev; dev = phynode_get_device(phynode); sc = device_get_softc(dev); *mode = sc->mode; return (0); } static int awusbphy_set_mode(struct phynode *phynode, int mode) { device_t dev; intptr_t phy; struct awusbphy_softc *sc; uint32_t val; int error, vbus_det; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (phy != 0) { if (mode != PHY_USB_MODE_HOST) return (EINVAL); return (0); } if (sc->mode == mode) return (0); if (mode == PHY_USB_MODE_OTG) /* TODO */ return (EOPNOTSUPP); error = awusbphy_vbus_detect(dev, &vbus_det); if (error != 0) return (error); val = bus_read_4(sc->phy_ctrl, PHY_CSR); val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET); val |= (ID_PULLUP_EN | DPDM_PULLUP_EN); val &= ~FORCE_VBUS_VALID; val |= (vbus_det ? FORCE_VBUS_VALID_HIGH : FORCE_VBUS_VALID_LOW) << FORCE_VBUS_VALID_SHIFT; val &= ~FORCE_ID; switch (mode) { case PHY_USB_MODE_HOST: val |= (FORCE_ID_LOW << FORCE_ID_SHIFT); if (sc->phy_conf->phy0_route) CLR4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG); break; case PHY_USB_MODE_DEVICE: val |= (FORCE_ID_HIGH << FORCE_ID_SHIFT); if (sc->phy_conf->phy0_route) SET4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG); break; default: return (EINVAL); } bus_write_4(sc->phy_ctrl, PHY_CSR, val); sc->mode = mode; return (0); } static int awusbphy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner USB PHY"); return (BUS_PROBE_DEFAULT); } static int awusbphy_attach(device_t dev) { int error; struct phynode *phynode; struct phynode_init_def phy_init; struct awusbphy_softc *sc; int i; sc = device_get_softc(dev); error = awusbphy_init(dev); if (error) { device_printf(dev, "failed to initialize USB PHY, error %d\n", error); return (error); } /* Create and register phys. */ for (i = 0; i < sc->phy_conf->num_phys; i++) { bzero(&phy_init, sizeof(phy_init)); phy_init.id = i; phy_init.ofw_node = ofw_bus_get_node(dev); phynode = phynode_create(dev, &awusbphy_phynode_class, &phy_init); if (phynode == NULL) { device_printf(dev, "failed to create USB PHY\n"); return (ENXIO); } if (phynode_register(phynode) == NULL) { device_printf(dev, "failed to create USB PHY\n"); return (ENXIO); } } return (error); } static device_method_t awusbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awusbphy_probe), DEVMETHOD(device_attach, awusbphy_attach), DEVMETHOD_END }; static driver_t awusbphy_driver = { "awusbphy", awusbphy_methods, sizeof(struct awusbphy_softc) }; /* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */ EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(awusbphy, 1); diff --git a/sys/arm/allwinner/if_awg.c b/sys/arm/allwinner/if_awg.c index 516cbefc6272..6c38a65b5040 100644 --- a/sys/arm/allwinner/if_awg.c +++ b/sys/arm/allwinner/if_awg.c @@ -1,2018 +1,2018 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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. */ /* * Allwinner Gigabit Ethernet MAC (EMAC) controller */ #include "opt_device_polling.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "syscon_if.h" #include "miibus_if.h" #include "gpio_if.h" #define RD4(sc, reg) bus_read_4((sc)->res[_RES_EMAC], (reg)) #define WR4(sc, reg, val) bus_write_4((sc)->res[_RES_EMAC], (reg), (val)) #define AWG_LOCK(sc) mtx_lock(&(sc)->mtx) #define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx); #define AWG_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) #define AWG_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) #define DESC_ALIGN 4 #define TX_DESC_COUNT 1024 #define TX_DESC_SIZE (sizeof(struct emac_desc) * TX_DESC_COUNT) #define RX_DESC_COUNT 256 #define RX_DESC_SIZE (sizeof(struct emac_desc) * RX_DESC_COUNT) #define DESC_OFF(n) ((n) * sizeof(struct emac_desc)) #define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1)) #define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1)) #define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1)) #define TX_MAX_SEGS 20 #define SOFT_RST_RETRY 1000 #define MII_BUSY_RETRY 1000 #define MDIO_FREQ 2500000 #define BURST_LEN_DEFAULT 8 #define RX_TX_PRI_DEFAULT 0 #define PAUSE_TIME_DEFAULT 0x400 #define TX_INTERVAL_DEFAULT 64 #define RX_BATCH_DEFAULT 64 /* syscon EMAC clock register */ #define EMAC_CLK_REG 0x30 #define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */ #define EMAC_CLK_EPHY_ADDR_SHIFT 20 #define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */ #define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */ #define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */ #define EMAC_CLK_RMII_EN (1 << 13) #define EMAC_CLK_ETXDC (0x7 << 10) #define EMAC_CLK_ETXDC_SHIFT 10 #define EMAC_CLK_ERXDC (0x1f << 5) #define EMAC_CLK_ERXDC_SHIFT 5 #define EMAC_CLK_PIT (0x1 << 2) #define EMAC_CLK_PIT_MII (0 << 2) #define EMAC_CLK_PIT_RGMII (1 << 2) #define EMAC_CLK_SRC (0x3 << 0) #define EMAC_CLK_SRC_MII (0 << 0) #define EMAC_CLK_SRC_EXT_RGMII (1 << 0) #define EMAC_CLK_SRC_RGMII (2 << 0) /* Burst length of RX and TX DMA transfers */ static int awg_burst_len = BURST_LEN_DEFAULT; TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); /* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */ static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT; TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri); /* Pause time field in the transmitted control frame */ static int awg_pause_time = PAUSE_TIME_DEFAULT; TUNABLE_INT("hw.awg.pause_time", &awg_pause_time); /* Request a TX interrupt every descriptors */ static int awg_tx_interval = TX_INTERVAL_DEFAULT; TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval); /* Maximum number of mbufs to send to if_input */ static int awg_rx_batch = RX_BATCH_DEFAULT; TUNABLE_INT("hw.awg.rx_batch", &awg_rx_batch); enum awg_type { EMAC_A83T = 1, EMAC_H3, EMAC_A64, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-a83t-emac", EMAC_A83T }, { "allwinner,sun8i-h3-emac", EMAC_H3 }, { "allwinner,sun50i-a64-emac", EMAC_A64 }, { NULL, 0 } }; struct awg_bufmap { bus_dmamap_t map; struct mbuf *mbuf; }; struct awg_txring { bus_dma_tag_t desc_tag; bus_dmamap_t desc_map; struct emac_desc *desc_ring; bus_addr_t desc_ring_paddr; bus_dma_tag_t buf_tag; struct awg_bufmap buf_map[TX_DESC_COUNT]; u_int cur, next, queued; u_int segs; }; struct awg_rxring { bus_dma_tag_t desc_tag; bus_dmamap_t desc_map; struct emac_desc *desc_ring; bus_addr_t desc_ring_paddr; bus_dma_tag_t buf_tag; struct awg_bufmap buf_map[RX_DESC_COUNT]; bus_dmamap_t buf_spare_map; u_int cur; }; enum { _RES_EMAC, _RES_IRQ, _RES_SYSCON, _RES_NITEMS }; struct awg_softc { struct resource *res[_RES_NITEMS]; struct mtx mtx; if_t ifp; device_t dev; device_t miibus; struct callout stat_ch; void *ih; u_int mdc_div_ratio_m; int link; int if_flags; enum awg_type type; struct syscon *syscon; struct awg_txring tx; struct awg_rxring rx; }; static struct resource_spec awg_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, { -1, 0 } }; static void awg_txeof(struct awg_softc *sc); static void awg_start_locked(struct awg_softc *sc); static void awg_tick(void *softc); static int awg_parse_delay(device_t dev, uint32_t *tx_delay, uint32_t *rx_delay); static uint32_t syscon_read_emac_clk_reg(device_t dev); static void syscon_write_emac_clk_reg(device_t dev, uint32_t val); static phandle_t awg_get_phy_node(device_t dev); static bool awg_has_internal_phy(device_t dev); /* * MII functions */ static int awg_miibus_readreg(device_t dev, int phy, int reg) { struct awg_softc *sc; int retry, val; sc = device_get_softc(dev); val = 0; WR4(sc, EMAC_MII_CMD, (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | (phy << PHY_ADDR_SHIFT) | (reg << PHY_REG_ADDR_SHIFT) | MII_BUSY); for (retry = MII_BUSY_RETRY; retry > 0; retry--) { if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) { val = RD4(sc, EMAC_MII_DATA); break; } DELAY(10); } if (retry == 0) device_printf(dev, "phy read timeout, phy=%d reg=%d\n", phy, reg); return (val); } static int awg_miibus_writereg(device_t dev, int phy, int reg, int val) { struct awg_softc *sc; int retry; sc = device_get_softc(dev); WR4(sc, EMAC_MII_DATA, val); WR4(sc, EMAC_MII_CMD, (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | (phy << PHY_ADDR_SHIFT) | (reg << PHY_REG_ADDR_SHIFT) | MII_WR | MII_BUSY); for (retry = MII_BUSY_RETRY; retry > 0; retry--) { if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) break; DELAY(10); } if (retry == 0) device_printf(dev, "phy write timeout, phy=%d reg=%d\n", phy, reg); return (0); } static void awg_miibus_statchg(device_t dev) { struct awg_softc *sc; struct mii_data *mii; uint32_t val; sc = device_get_softc(dev); AWG_ASSERT_LOCKED(sc); if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0) return; mii = device_get_softc(sc->miibus); if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: case IFM_1000_SX: case IFM_100_TX: case IFM_10_T: sc->link = 1; break; default: sc->link = 0; break; } } else sc->link = 0; if (sc->link == 0) return; val = RD4(sc, EMAC_BASIC_CTL_0); val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT; else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT; else val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) val |= BASIC_CTL_DUPLEX; WR4(sc, EMAC_BASIC_CTL_0, val); val = RD4(sc, EMAC_RX_CTL_0); val &= ~RX_FLOW_CTL_EN; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) val |= RX_FLOW_CTL_EN; WR4(sc, EMAC_RX_CTL_0, val); val = RD4(sc, EMAC_TX_FLOW_CTL); val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN); if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) val |= TX_FLOW_CTL_EN; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) val |= awg_pause_time << PAUSE_TIME_SHIFT; WR4(sc, EMAC_TX_FLOW_CTL, val); } /* * Media functions */ static void awg_media_status(if_t ifp, struct ifmediareq *ifmr) { struct awg_softc *sc; struct mii_data *mii; sc = if_getsoftc(ifp); mii = device_get_softc(sc->miibus); AWG_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; AWG_UNLOCK(sc); } static int awg_media_change(if_t ifp) { struct awg_softc *sc; struct mii_data *mii; int error; sc = if_getsoftc(ifp); mii = device_get_softc(sc->miibus); AWG_LOCK(sc); error = mii_mediachg(mii); AWG_UNLOCK(sc); return (error); } /* * Core functions */ /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */ static uint32_t bitrev32(uint32_t x) { x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); return (x >> 16) | (x << 16); } static u_int awg_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { uint32_t crc, hashreg, hashbit, *hash = arg; crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN) & 0x7f; crc = bitrev32(~crc) >> 26; hashreg = (crc >> 5); hashbit = (crc & 0x1f); hash[hashreg] |= (1 << hashbit); return (1); } static void awg_setup_rxfilter(struct awg_softc *sc) { uint32_t val, hash[2], machi, maclo; uint8_t *eaddr; if_t ifp; AWG_ASSERT_LOCKED(sc); ifp = sc->ifp; val = 0; hash[0] = hash[1] = 0; if (if_getflags(ifp) & IFF_PROMISC) val |= DIS_ADDR_FILTER; else if (if_getflags(ifp) & IFF_ALLMULTI) { val |= RX_ALL_MULTICAST; hash[0] = hash[1] = ~0; } else if (if_foreach_llmaddr(ifp, awg_hash_maddr, hash) > 0) val |= HASH_MULTICAST; /* Write our unicast address */ eaddr = if_getlladdr(ifp); machi = (eaddr[5] << 8) | eaddr[4]; maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | (eaddr[0] << 0); WR4(sc, EMAC_ADDR_HIGH(0), machi); WR4(sc, EMAC_ADDR_LOW(0), maclo); /* Multicast hash filters */ WR4(sc, EMAC_RX_HASH_0, hash[1]); WR4(sc, EMAC_RX_HASH_1, hash[0]); /* RX frame filter config */ WR4(sc, EMAC_RX_FRM_FLT, val); } static void awg_setup_core(struct awg_softc *sc) { uint32_t val; AWG_ASSERT_LOCKED(sc); /* Configure DMA burst length and priorities */ val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT; if (awg_rx_tx_pri) val |= BASIC_CTL_RX_TX_PRI; WR4(sc, EMAC_BASIC_CTL_1, val); } static void awg_enable_mac(struct awg_softc *sc, bool enable) { uint32_t tx, rx; AWG_ASSERT_LOCKED(sc); tx = RD4(sc, EMAC_TX_CTL_0); rx = RD4(sc, EMAC_RX_CTL_0); if (enable) { tx |= TX_EN; rx |= RX_EN | CHECK_CRC; } else { tx &= ~TX_EN; rx &= ~(RX_EN | CHECK_CRC); } WR4(sc, EMAC_TX_CTL_0, tx); WR4(sc, EMAC_RX_CTL_0, rx); } static void awg_get_eaddr(device_t dev, uint8_t *eaddr) { struct awg_softc *sc; uint32_t maclo, machi, rnd; u_char rootkey[16]; uint32_t rootkey_size; sc = device_get_softc(dev); machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff; maclo = RD4(sc, EMAC_ADDR_LOW(0)); rootkey_size = sizeof(rootkey); if (maclo == 0xffffffff && machi == 0xffff) { /* MAC address in hardware is invalid, create one */ if (aw_sid_get_fuse(AW_SID_FUSE_ROOTKEY, rootkey, &rootkey_size) == 0 && (rootkey[3] | rootkey[12] | rootkey[13] | rootkey[14] | rootkey[15]) != 0) { /* MAC address is derived from the root key in SID */ maclo = (rootkey[13] << 24) | (rootkey[12] << 16) | (rootkey[3] << 8) | 0x02; machi = (rootkey[15] << 8) | rootkey[14]; } else { /* Create one */ rnd = arc4random(); maclo = 0x00f2 | (rnd & 0xffff0000); machi = rnd & 0xffff; } } eaddr[0] = maclo & 0xff; eaddr[1] = (maclo >> 8) & 0xff; eaddr[2] = (maclo >> 16) & 0xff; eaddr[3] = (maclo >> 24) & 0xff; eaddr[4] = machi & 0xff; eaddr[5] = (machi >> 8) & 0xff; } /* * DMA functions */ static void awg_enable_dma_intr(struct awg_softc *sc) { /* Enable interrupts */ WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN); } static void awg_disable_dma_intr(struct awg_softc *sc) { /* Disable interrupts */ WR4(sc, EMAC_INT_EN, 0); } static void awg_init_dma(struct awg_softc *sc) { uint32_t val; AWG_ASSERT_LOCKED(sc); /* Enable interrupts */ #ifdef DEVICE_POLLING if ((if_getcapenable(sc->ifp) & IFCAP_POLLING) == 0) awg_enable_dma_intr(sc); else awg_disable_dma_intr(sc); #else awg_enable_dma_intr(sc); #endif /* Enable transmit DMA */ val = RD4(sc, EMAC_TX_CTL_1); WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD | TX_NEXT_FRAME); /* Enable receive DMA */ val = RD4(sc, EMAC_RX_CTL_1); WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD); } static void awg_stop_dma(struct awg_softc *sc) { uint32_t val; AWG_ASSERT_LOCKED(sc); /* Stop transmit DMA and flush data in the TX FIFO */ val = RD4(sc, EMAC_TX_CTL_1); val &= ~TX_DMA_EN; val |= FLUSH_TX_FIFO; WR4(sc, EMAC_TX_CTL_1, val); /* Disable interrupts */ awg_disable_dma_intr(sc); /* Disable transmit DMA */ val = RD4(sc, EMAC_TX_CTL_1); WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN); /* Disable receive DMA */ val = RD4(sc, EMAC_RX_CTL_1); WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN); } static int awg_encap(struct awg_softc *sc, struct mbuf **mp) { bus_dmamap_t map; bus_dma_segment_t segs[TX_MAX_SEGS]; int error, nsegs, cur, first, last, i; u_int csum_flags; uint32_t flags, status; struct mbuf *m; cur = first = sc->tx.cur; map = sc->tx.buf_map[first].map; m = *mp; error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); if (m == NULL) { device_printf(sc->dev, "awg_encap: m_collapse failed\n"); m_freem(*mp); *mp = NULL; return (ENOMEM); } *mp = m; error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(*mp); *mp = NULL; } } if (error != 0) { device_printf(sc->dev, "awg_encap: bus_dmamap_load_mbuf_sg failed\n"); return (error); } if (nsegs == 0) { m_freem(*mp); *mp = NULL; return (EIO); } if (sc->tx.queued + nsegs > TX_DESC_COUNT) { bus_dmamap_unload(sc->tx.buf_tag, map); return (ENOBUFS); } bus_dmamap_sync(sc->tx.buf_tag, map, BUS_DMASYNC_PREWRITE); flags = TX_FIR_DESC; status = 0; if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) csum_flags = TX_CHECKSUM_CTL_FULL; else csum_flags = TX_CHECKSUM_CTL_IP; flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); } for (i = 0; i < nsegs; i++) { sc->tx.segs++; if (i == nsegs - 1) { flags |= TX_LAST_DESC; /* * Can only request TX completion * interrupt on last descriptor. */ if (sc->tx.segs >= awg_tx_interval) { sc->tx.segs = 0; flags |= TX_INT_CTL; } } sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr); sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len); sc->tx.desc_ring[cur].status = htole32(status); flags &= ~TX_FIR_DESC; /* * Setting of the valid bit in the first descriptor is * deferred until the whole chain is fully set up. */ status = TX_DESC_CTL; ++sc->tx.queued; cur = TX_NEXT(cur); } sc->tx.cur = cur; /* Store mapping and mbuf in the last segment */ last = TX_SKIP(cur, TX_DESC_COUNT - 1); sc->tx.buf_map[first].map = sc->tx.buf_map[last].map; sc->tx.buf_map[last].map = map; sc->tx.buf_map[last].mbuf = m; /* * The whole mbuf chain has been DMA mapped, * fix the first descriptor. */ sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL); return (0); } static void awg_clean_txbuf(struct awg_softc *sc, int index) { struct awg_bufmap *bmap; --sc->tx.queued; bmap = &sc->tx.buf_map[index]; if (bmap->mbuf != NULL) { bus_dmamap_sync(sc->tx.buf_tag, bmap->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->tx.buf_tag, bmap->map); m_freem(bmap->mbuf); bmap->mbuf = NULL; } } static void awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr) { uint32_t status, size; status = RX_DESC_CTL; size = MCLBYTES - 1; sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr); sc->rx.desc_ring[index].size = htole32(size); sc->rx.desc_ring[index].status = htole32(status); } static void awg_reuse_rxdesc(struct awg_softc *sc, int index) { sc->rx.desc_ring[index].status = htole32(RX_DESC_CTL); } static int awg_newbuf_rx(struct awg_softc *sc, int index) { struct mbuf *m; bus_dma_segment_t seg; bus_dmamap_t map; int nsegs; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; m_adj(m, ETHER_ALIGN); if (bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, sc->rx.buf_spare_map, m, &seg, &nsegs, BUS_DMA_NOWAIT) != 0) { m_freem(m); return (ENOBUFS); } if (sc->rx.buf_map[index].mbuf != NULL) { bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map); } map = sc->rx.buf_map[index].map; sc->rx.buf_map[index].map = sc->rx.buf_spare_map; sc->rx.buf_spare_map = map; bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, BUS_DMASYNC_PREREAD); sc->rx.buf_map[index].mbuf = m; awg_setup_rxdesc(sc, index, seg.ds_addr); return (0); } static void awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } static int awg_setup_dma(device_t dev) { struct awg_softc *sc; int error, i; sc = device_get_softc(dev); /* Setup TX ring */ error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent tag */ DESC_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TX_DESC_SIZE, 1, /* maxsize, nsegs */ TX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->tx.desc_tag); if (error != 0) { device_printf(dev, "cannot create TX descriptor ring tag\n"); return (error); } error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map); if (error != 0) { device_printf(dev, "cannot allocate TX descriptor ring\n"); return (error); } error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map, sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb, &sc->tx.desc_ring_paddr, 0); if (error != 0) { device_printf(dev, "cannot load TX descriptor ring\n"); return (error); } for (i = 0; i < TX_DESC_COUNT; i++) sc->tx.desc_ring[i].next = htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i))); error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent tag */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->tx.buf_tag); if (error != 0) { device_printf(dev, "cannot create TX buffer tag\n"); return (error); } sc->tx.queued = 0; for (i = 0; i < TX_DESC_COUNT; i++) { error = bus_dmamap_create(sc->tx.buf_tag, 0, &sc->tx.buf_map[i].map); if (error != 0) { device_printf(dev, "cannot create TX buffer map\n"); return (error); } } /* Setup RX ring */ error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent tag */ DESC_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ RX_DESC_SIZE, 1, /* maxsize, nsegs */ RX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rx.desc_tag); if (error != 0) { device_printf(dev, "cannot create RX descriptor ring tag\n"); return (error); } error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map); if (error != 0) { device_printf(dev, "cannot allocate RX descriptor ring\n"); return (error); } error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map, sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb, &sc->rx.desc_ring_paddr, 0); if (error != 0) { device_printf(dev, "cannot load RX descriptor ring\n"); return (error); } error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent tag */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize, nsegs */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rx.buf_tag); if (error != 0) { device_printf(dev, "cannot create RX buffer tag\n"); return (error); } error = bus_dmamap_create(sc->rx.buf_tag, 0, &sc->rx.buf_spare_map); if (error != 0) { device_printf(dev, "cannot create RX buffer spare map\n"); return (error); } for (i = 0; i < RX_DESC_COUNT; i++) { sc->rx.desc_ring[i].next = htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(i))); error = bus_dmamap_create(sc->rx.buf_tag, 0, &sc->rx.buf_map[i].map); if (error != 0) { device_printf(dev, "cannot create RX buffer map\n"); return (error); } sc->rx.buf_map[i].mbuf = NULL; error = awg_newbuf_rx(sc, i); if (error != 0) { device_printf(dev, "cannot create RX buffer\n"); return (error); } } bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_PREWRITE); /* Write transmit and receive descriptor base address registers */ WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr); WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr); return (0); } static void awg_dma_start_tx(struct awg_softc *sc) { uint32_t val; AWG_ASSERT_LOCKED(sc); /* Start and run TX DMA */ val = RD4(sc, EMAC_TX_CTL_1); WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START); } /* * if_ functions */ static void awg_start_locked(struct awg_softc *sc) { struct mbuf *m; if_t ifp; int cnt, err; AWG_ASSERT_LOCKED(sc); if (!sc->link) return; ifp = sc->ifp; if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; for (cnt = 0; ; cnt++) { m = if_dequeue(ifp); if (m == NULL) break; err = awg_encap(sc, &m); if (err != 0) { if (err == ENOBUFS) if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); if (m != NULL) if_sendq_prepend(ifp, m); break; } bpf_mtap_if(ifp, m); } if (cnt != 0) { bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); awg_dma_start_tx(sc); } } static void awg_start(if_t ifp) { struct awg_softc *sc; sc = if_getsoftc(ifp); AWG_LOCK(sc); awg_start_locked(sc); AWG_UNLOCK(sc); } static void awg_init_locked(struct awg_softc *sc) { struct mii_data *mii; if_t ifp; mii = device_get_softc(sc->miibus); ifp = sc->ifp; AWG_ASSERT_LOCKED(sc); if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) return; awg_setup_rxfilter(sc); awg_setup_core(sc); awg_enable_mac(sc, true); awg_init_dma(sc); if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); mii_mediachg(mii); callout_reset(&sc->stat_ch, hz, awg_tick, sc); } static void awg_init(void *softc) { struct awg_softc *sc; sc = softc; AWG_LOCK(sc); awg_init_locked(sc); AWG_UNLOCK(sc); } static void awg_stop(struct awg_softc *sc) { if_t ifp; uint32_t val; int i; AWG_ASSERT_LOCKED(sc); ifp = sc->ifp; callout_stop(&sc->stat_ch); awg_stop_dma(sc); awg_enable_mac(sc, false); sc->link = 0; /* Finish handling transmitted buffers */ awg_txeof(sc); /* Release any untransmitted buffers. */ for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { val = le32toh(sc->tx.desc_ring[i].status); if ((val & TX_DESC_CTL) != 0) break; awg_clean_txbuf(sc, i); } sc->tx.next = i; for (; sc->tx.queued > 0; i = TX_NEXT(i)) { sc->tx.desc_ring[i].status = 0; awg_clean_txbuf(sc, i); } sc->tx.cur = sc->tx.next; bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Setup RX buffers for reuse */ bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (i = sc->rx.cur; ; i = RX_NEXT(i)) { val = le32toh(sc->rx.desc_ring[i].status); if ((val & RX_DESC_CTL) != 0) break; awg_reuse_rxdesc(sc, i); } sc->rx.cur = i; bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } static int awg_ioctl(if_t ifp, u_long cmd, caddr_t data) { struct awg_softc *sc; struct mii_data *mii; struct ifreq *ifr; int flags, mask, error; sc = if_getsoftc(ifp); mii = device_get_softc(sc->miibus); ifr = (struct ifreq *)data; error = 0; switch (cmd) { case SIOCSIFFLAGS: AWG_LOCK(sc); if (if_getflags(ifp) & IFF_UP) { if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { flags = if_getflags(ifp) ^ sc->if_flags; if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) awg_setup_rxfilter(sc); } else awg_init_locked(sc); } else { if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) awg_stop(sc); } sc->if_flags = if_getflags(ifp); AWG_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { AWG_LOCK(sc); awg_setup_rxfilter(sc); AWG_UNLOCK(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) { error = ether_poll_register(awg_poll, ifp); if (error != 0) break; AWG_LOCK(sc); awg_disable_dma_intr(sc); if_setcapenablebit(ifp, IFCAP_POLLING, 0); AWG_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); AWG_LOCK(sc); awg_enable_dma_intr(sc); if_setcapenablebit(ifp, 0, IFCAP_POLLING); AWG_UNLOCK(sc); } } #endif if (mask & IFCAP_VLAN_MTU) if_togglecapenable(ifp, IFCAP_VLAN_MTU); if (mask & IFCAP_RXCSUM) if_togglecapenable(ifp, IFCAP_RXCSUM); if (mask & IFCAP_TXCSUM) if_togglecapenable(ifp, IFCAP_TXCSUM); if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); else if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } /* * Interrupts functions */ static int awg_rxintr(struct awg_softc *sc) { if_t ifp; struct mbuf *m, *mh, *mt; int error, index, len, cnt, npkt; uint32_t status; ifp = sc->ifp; mh = mt = NULL; cnt = 0; npkt = 0; bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (index = sc->rx.cur; ; index = RX_NEXT(index)) { status = le32toh(sc->rx.desc_ring[index].status); if ((status & RX_DESC_CTL) != 0) break; len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; if (len == 0) { if ((status & (RX_NO_ENOUGH_BUF_ERR | RX_OVERFLOW_ERR)) != 0) if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); awg_reuse_rxdesc(sc, index); continue; } m = sc->rx.buf_map[index].mbuf; error = awg_newbuf_rx(sc, index); if (error != 0) { if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); awg_reuse_rxdesc(sc, index); continue; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = len; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && (status & RX_FRM_TYPE) != 0) { m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; if ((status & RX_HEADER_ERR) == 0) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if ((status & RX_PAYLOAD_ERR) == 0) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } m->m_nextpkt = NULL; if (mh == NULL) mh = m; else mt->m_nextpkt = m; mt = m; ++cnt; ++npkt; if (cnt == awg_rx_batch) { AWG_UNLOCK(sc); if_input(ifp, mh); AWG_LOCK(sc); mh = mt = NULL; cnt = 0; } } if (index != sc->rx.cur) { bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } if (mh != NULL) { AWG_UNLOCK(sc); if_input(ifp, mh); AWG_LOCK(sc); } sc->rx.cur = index; return (npkt); } static void awg_txeof(struct awg_softc *sc) { struct emac_desc *desc; uint32_t status, size; if_t ifp; int i, prog; AWG_ASSERT_LOCKED(sc); bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); ifp = sc->ifp; prog = 0; for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { desc = &sc->tx.desc_ring[i]; status = le32toh(desc->status); if ((status & TX_DESC_CTL) != 0) break; size = le32toh(desc->size); if (size & TX_LAST_DESC) { if ((status & (TX_HEADER_ERR | TX_PAYLOAD_ERR)) != 0) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); else if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } prog++; awg_clean_txbuf(sc, i); } if (prog > 0) { sc->tx.next = i; if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); } } static void awg_intr(void *arg) { struct awg_softc *sc; uint32_t val; sc = arg; AWG_LOCK(sc); val = RD4(sc, EMAC_INT_STA); WR4(sc, EMAC_INT_STA, val); if (val & RX_INT) awg_rxintr(sc); if (val & TX_INT) awg_txeof(sc); if (val & (TX_INT | TX_BUF_UA_INT)) { if (!if_sendq_empty(sc->ifp)) awg_start_locked(sc); } AWG_UNLOCK(sc); } #ifdef DEVICE_POLLING static int awg_poll(if_t ifp, enum poll_cmd cmd, int count) { struct awg_softc *sc; uint32_t val; int rx_npkts; sc = if_getsoftc(ifp); rx_npkts = 0; AWG_LOCK(sc); if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) { AWG_UNLOCK(sc); return (0); } rx_npkts = awg_rxintr(sc); awg_txeof(sc); if (!if_sendq_empty(ifp)) awg_start_locked(sc); if (cmd == POLL_AND_CHECK_STATUS) { val = RD4(sc, EMAC_INT_STA); if (val != 0) WR4(sc, EMAC_INT_STA, val); } AWG_UNLOCK(sc); return (rx_npkts); } #endif /* * syscon functions */ static uint32_t syscon_read_emac_clk_reg(device_t dev) { struct awg_softc *sc; sc = device_get_softc(dev); if (sc->syscon != NULL) return (SYSCON_READ_4(sc->syscon, EMAC_CLK_REG)); else if (sc->res[_RES_SYSCON] != NULL) return (bus_read_4(sc->res[_RES_SYSCON], 0)); return (0); } static void syscon_write_emac_clk_reg(device_t dev, uint32_t val) { struct awg_softc *sc; sc = device_get_softc(dev); if (sc->syscon != NULL) SYSCON_WRITE_4(sc->syscon, EMAC_CLK_REG, val); else if (sc->res[_RES_SYSCON] != NULL) bus_write_4(sc->res[_RES_SYSCON], 0, val); } /* * PHY functions */ static phandle_t awg_get_phy_node(device_t dev) { phandle_t node; pcell_t phy_handle; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, sizeof(phy_handle)) <= 0) return (0); return (OF_node_from_xref(phy_handle)); } static bool awg_has_internal_phy(device_t dev) { phandle_t node, phy_node; node = ofw_bus_get_node(dev); /* Legacy binding */ if (OF_hasprop(node, "allwinner,use-internal-phy")) return (true); phy_node = awg_get_phy_node(dev); return (phy_node != 0 && ofw_bus_node_is_compatible(OF_parent(phy_node), "allwinner,sun8i-h3-mdio-internal") != 0); } static int awg_parse_delay(device_t dev, uint32_t *tx_delay, uint32_t *rx_delay) { phandle_t node; uint32_t delay; if (tx_delay == NULL || rx_delay == NULL) return (EINVAL); *tx_delay = *rx_delay = 0; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "tx-delay", &delay, sizeof(delay)) >= 0) *tx_delay = delay; else if (OF_getencprop(node, "allwinner,tx-delay-ps", &delay, sizeof(delay)) >= 0) { if ((delay % 100) != 0) { device_printf(dev, "tx-delay-ps is not a multiple of 100\n"); return (EDOM); } *tx_delay = delay / 100; } if (*tx_delay > 7) { device_printf(dev, "tx-delay out of range\n"); return (ERANGE); } if (OF_getencprop(node, "rx-delay", &delay, sizeof(delay)) >= 0) *rx_delay = delay; else if (OF_getencprop(node, "allwinner,rx-delay-ps", &delay, sizeof(delay)) >= 0) { if ((delay % 100) != 0) { device_printf(dev, "rx-delay-ps is not within documented domain\n"); return (EDOM); } *rx_delay = delay / 100; } if (*rx_delay > 31) { device_printf(dev, "rx-delay out of range\n"); return (ERANGE); } return (0); } static int awg_setup_phy(device_t dev) { struct awg_softc *sc; clk_t clk_tx, clk_tx_parent; const char *tx_parent_name; char *phy_type; phandle_t node; uint32_t reg, tx_delay, rx_delay; int error; bool use_syscon; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); use_syscon = false; if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_type) == 0) return (0); if (sc->syscon != NULL || sc->res[_RES_SYSCON] != NULL) use_syscon = true; if (bootverbose) device_printf(dev, "PHY type: %s, conf mode: %s\n", phy_type, use_syscon ? "reg" : "clk"); if (use_syscon) { /* * Abstract away writing to syscon for devices like the pine64. * For the pine64, we get dtb from U-Boot and it still uses the * legacy setup of specifying syscon register in emac node * rather than as its own node and using an xref in emac. * These abstractions can go away once U-Boot dts is up-to-date. */ reg = syscon_read_emac_clk_reg(dev); reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN); if (strncmp(phy_type, "rgmii", 5) == 0) reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII; else if (strcmp(phy_type, "rmii") == 0) reg |= EMAC_CLK_RMII_EN; else reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII; /* * Fail attach if we fail to parse either of the delay * parameters. If we don't have the proper delay to write to * syscon, then awg likely won't function properly anyways. * Lack of delay is not an error! */ error = awg_parse_delay(dev, &tx_delay, &rx_delay); if (error != 0) goto fail; /* Default to 0 and we'll increase it if we need to. */ reg &= ~(EMAC_CLK_ETXDC | EMAC_CLK_ERXDC); if (tx_delay > 0) reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT); if (rx_delay > 0) reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT); if (sc->type == EMAC_H3) { if (awg_has_internal_phy(dev)) { reg |= EMAC_CLK_EPHY_SELECT; reg &= ~EMAC_CLK_EPHY_SHUTDOWN; if (OF_hasprop(node, "allwinner,leds-active-low")) reg |= EMAC_CLK_EPHY_LED_POL; else reg &= ~EMAC_CLK_EPHY_LED_POL; /* Set internal PHY addr to 1 */ reg &= ~EMAC_CLK_EPHY_ADDR; reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT); } else { reg &= ~EMAC_CLK_EPHY_SELECT; } } if (bootverbose) device_printf(dev, "EMAC clock: 0x%08x\n", reg); syscon_write_emac_clk_reg(dev, reg); } else { if (strncmp(phy_type, "rgmii", 5) == 0) tx_parent_name = "emac_int_tx"; else tx_parent_name = "mii_phy_tx"; /* Get the TX clock */ error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx); if (error != 0) { device_printf(dev, "cannot get tx clock\n"); goto fail; } /* Find the desired parent clock based on phy-mode property */ error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); if (error != 0) { device_printf(dev, "cannot get clock '%s'\n", tx_parent_name); goto fail; } /* Set TX clock parent */ error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); if (error != 0) { device_printf(dev, "cannot set tx clock parent\n"); goto fail; } /* Enable TX clock */ error = clk_enable(clk_tx); if (error != 0) { device_printf(dev, "cannot enable tx clock\n"); goto fail; } } error = 0; fail: OF_prop_free(phy_type); return (error); } static int awg_setup_extres(device_t dev) { struct awg_softc *sc; phandle_t node, phy_node; hwreset_t rst_ahb, rst_ephy; clk_t clk_ahb, clk_ephy; regulator_t reg; uint64_t freq; int error, div; sc = device_get_softc(dev); rst_ahb = rst_ephy = NULL; clk_ahb = clk_ephy = NULL; reg = NULL; node = ofw_bus_get_node(dev); phy_node = awg_get_phy_node(dev); if (phy_node == 0 && OF_hasprop(node, "phy-handle")) { error = ENXIO; device_printf(dev, "cannot get phy handle\n"); goto fail; } /* Get AHB clock and reset resources */ error = hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst_ahb); if (error != 0) error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); if (error != 0) { device_printf(dev, "cannot get ahb reset\n"); goto fail; } if (hwreset_get_by_ofw_name(dev, 0, "ephy", &rst_ephy) != 0) if (phy_node == 0 || hwreset_get_by_ofw_idx(dev, phy_node, 0, &rst_ephy) != 0) rst_ephy = NULL; error = clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk_ahb); if (error != 0) error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); if (error != 0) { device_printf(dev, "cannot get ahb clock\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "ephy", &clk_ephy) != 0) if (phy_node == 0 || clk_get_by_ofw_index(dev, phy_node, 0, &clk_ephy) != 0) clk_ephy = NULL; if (OF_hasprop(node, "syscon") && syscon_get_by_ofw_property(dev, node, "syscon", &sc->syscon) != 0) { device_printf(dev, "cannot get syscon driver handle\n"); goto fail; } /* Configure PHY for MII or RGMII mode */ if (awg_setup_phy(dev) != 0) goto fail; /* Enable clocks */ error = clk_enable(clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } if (clk_ephy != NULL) { error = clk_enable(clk_ephy); if (error != 0) { device_printf(dev, "cannot enable ephy clock\n"); goto fail; } } /* De-assert reset */ error = hwreset_deassert(rst_ahb); if (error != 0) { device_printf(dev, "cannot de-assert ahb reset\n"); goto fail; } if (rst_ephy != NULL) { /* * The ephy reset is left de-asserted by U-Boot. Assert it * here to make sure that we're in a known good state going * into the PHY reset. */ hwreset_assert(rst_ephy); error = hwreset_deassert(rst_ephy); if (error != 0) { device_printf(dev, "cannot de-assert ephy reset\n"); goto fail; } } /* Enable PHY regulator if applicable */ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { error = regulator_enable(reg); if (error != 0) { device_printf(dev, "cannot enable PHY regulator\n"); goto fail; } } /* Determine MDC clock divide ratio based on AHB clock */ error = clk_get_freq(clk_ahb, &freq); if (error != 0) { device_printf(dev, "cannot get AHB clock frequency\n"); goto fail; } div = freq / MDIO_FREQ; if (div <= 16) sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16; else if (div <= 32) sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32; else if (div <= 64) sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64; else if (div <= 128) sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128; else { device_printf(dev, "cannot determine MDC clock divide ratio\n"); error = ENXIO; goto fail; } if (bootverbose) device_printf(dev, "AHB frequency %ju Hz, MDC div: 0x%x\n", (uintmax_t)freq, sc->mdc_div_ratio_m); return (0); fail: if (reg != NULL) regulator_release(reg); if (clk_ephy != NULL) clk_release(clk_ephy); if (clk_ahb != NULL) clk_release(clk_ahb); if (rst_ephy != NULL) hwreset_release(rst_ephy); if (rst_ahb != NULL) hwreset_release(rst_ahb); return (error); } #ifdef AWG_DEBUG static void awg_dump_regs(device_t dev) { static const struct { const char *name; u_int reg; } regs[] = { { "BASIC_CTL_0", EMAC_BASIC_CTL_0 }, { "BASIC_CTL_1", EMAC_BASIC_CTL_1 }, { "INT_STA", EMAC_INT_STA }, { "INT_EN", EMAC_INT_EN }, { "TX_CTL_0", EMAC_TX_CTL_0 }, { "TX_CTL_1", EMAC_TX_CTL_1 }, { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL }, { "TX_DMA_LIST", EMAC_TX_DMA_LIST }, { "RX_CTL_0", EMAC_RX_CTL_0 }, { "RX_CTL_1", EMAC_RX_CTL_1 }, { "RX_DMA_LIST", EMAC_RX_DMA_LIST }, { "RX_FRM_FLT", EMAC_RX_FRM_FLT }, { "RX_HASH_0", EMAC_RX_HASH_0 }, { "RX_HASH_1", EMAC_RX_HASH_1 }, { "MII_CMD", EMAC_MII_CMD }, { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) }, { "ADDR_LOW0", EMAC_ADDR_LOW(0) }, { "TX_DMA_STA", EMAC_TX_DMA_STA }, { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC }, { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF }, { "RX_DMA_STA", EMAC_RX_DMA_STA }, { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC }, { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF }, { "RGMII_STA", EMAC_RGMII_STA }, }; struct awg_softc *sc; unsigned int n; sc = device_get_softc(dev); for (n = 0; n < nitems(regs); n++) device_printf(dev, " %-20s %08x\n", regs[n].name, RD4(sc, regs[n].reg)); } #endif #define GPIO_ACTIVE_LOW 1 static int awg_phy_reset(device_t dev) { pcell_t gpio_prop[4], delay_prop[3]; phandle_t node, gpio_node; device_t gpio; uint32_t pin, flags; uint32_t pin_value; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "allwinner,reset-gpio", gpio_prop, sizeof(gpio_prop)) <= 0) return (0); if (OF_getencprop(node, "allwinner,reset-delays-us", delay_prop, sizeof(delay_prop)) <= 0) return (ENXIO); gpio_node = OF_node_from_xref(gpio_prop[0]); if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) return (ENXIO); if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1, gpio_prop + 1, &pin, &flags) != 0) return (ENXIO); pin_value = GPIO_PIN_LOW; if (OF_hasprop(node, "allwinner,reset-active-low")) pin_value = GPIO_PIN_HIGH; if (flags & GPIO_ACTIVE_LOW) pin_value = !pin_value; GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[0]); GPIO_PIN_SET(gpio, pin, !pin_value); DELAY(delay_prop[1]); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[2]); return (0); } static int awg_reset(device_t dev) { struct awg_softc *sc; int retry; sc = device_get_softc(dev); /* Reset PHY if necessary */ if (awg_phy_reset(dev) != 0) { device_printf(dev, "failed to reset PHY\n"); return (ENXIO); } /* Soft reset all registers and logic */ WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); /* Wait for soft reset bit to self-clear */ for (retry = SOFT_RST_RETRY; retry > 0; retry--) { if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0) break; DELAY(10); } if (retry == 0) { device_printf(dev, "soft reset timed out\n"); #ifdef AWG_DEBUG awg_dump_regs(dev); #endif return (ETIMEDOUT); } return (0); } /* * Stats */ static void awg_tick(void *softc) { struct awg_softc *sc; struct mii_data *mii; if_t ifp; int link; sc = softc; ifp = sc->ifp; mii = device_get_softc(sc->miibus); AWG_ASSERT_LOCKED(sc); if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) return; link = sc->link; mii_tick(mii); if (sc->link && !link) awg_start_locked(sc); callout_reset(&sc->stat_ch, hz, awg_tick, sc); } /* * Probe/attach functions */ static int awg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Gigabit Ethernet"); return (BUS_PROBE_DEFAULT); } static int awg_attach(device_t dev) { uint8_t eaddr[ETHER_ADDR_LEN]; struct awg_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); /* Setup clocks and regulators */ error = awg_setup_extres(dev); if (error != 0) return (error); /* Read MAC address before resetting the chip */ awg_get_eaddr(dev, eaddr); /* Soft reset EMAC core */ error = awg_reset(dev); if (error != 0) return (error); /* Setup DMA descriptors */ error = awg_setup_dma(dev); if (error != 0) return (error); /* Install interrupt handler */ error = bus_setup_intr(dev, sc->res[_RES_IRQ], INTR_TYPE_NET | INTR_MPSAFE, NULL, awg_intr, sc, &sc->ih); if (error != 0) { device_printf(dev, "cannot setup interrupt handler\n"); return (error); } /* Setup ethernet interface */ sc->ifp = if_alloc(IFT_ETHER); if_setsoftc(sc->ifp, sc); if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); if_setstartfn(sc->ifp, awg_start); if_setioctlfn(sc->ifp, awg_ioctl); if_setinitfn(sc->ifp, awg_init); if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); if_setsendqready(sc->ifp); if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); #ifdef DEVICE_POLLING if_setcapabilitiesbit(sc->ifp, IFCAP_POLLING, 0); #endif /* Attach MII driver */ error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change, awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, MIIF_DOPAUSE); if (error != 0) { device_printf(dev, "cannot attach PHY\n"); return (error); } /* Attach ethernet interface */ ether_ifattach(sc->ifp, eaddr); return (0); } static device_method_t awg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awg_probe), DEVMETHOD(device_attach, awg_attach), /* MII interface */ DEVMETHOD(miibus_readreg, awg_miibus_readreg), DEVMETHOD(miibus_writereg, awg_miibus_writereg), DEVMETHOD(miibus_statchg, awg_miibus_statchg), DEVMETHOD_END }; static driver_t awg_driver = { "awg", awg_methods, sizeof(struct awg_softc), }; DRIVER_MODULE(awg, simplebus, awg_driver, 0, 0); DRIVER_MODULE(miibus, awg, miibus_driver, 0, 0); MODULE_DEPEND(awg, ether, 1, 1, 1); MODULE_DEPEND(awg, miibus, 1, 1, 1); MODULE_DEPEND(awg, aw_sid, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm/nvidia/drm2/tegra_dc.c b/sys/arm/nvidia/drm2/tegra_dc.c index 637ea3981acc..f4168f161f5e 100644 --- a/sys/arm/nvidia/drm2/tegra_dc.c +++ b/sys/arm/nvidia/drm2/tegra_dc.c @@ -1,1438 +1,1438 @@ /*- * Copyright (c) 2015 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 #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "tegra_drm_if.h" #include "tegra_dc_if.h" #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r)) #define LOCK(_sc) mtx_lock(&(_sc)->mtx) #define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define SLEEP(_sc, timeout) \ mtx_sleep(sc, &sc->mtx, 0, "tegra_dc_wait", timeout); #define LOCK_INIT(_sc) \ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_dc", MTX_DEF) #define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) #define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) #define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) #define SYNCPT_VBLANK0 26 #define SYNCPT_VBLANK1 27 #define DC_MAX_PLANES 2 /* Maximum planes */ /* DRM Formats supported by DC */ /* XXXX expand me */ static uint32_t dc_plane_formats[] = { DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, DRM_FORMAT_YUV420, DRM_FORMAT_YUV422, }; /* Complete description of one window (plane) */ struct dc_window { /* Source (in framebuffer) rectangle, in pixels */ u_int src_x; u_int src_y; u_int src_w; u_int src_h; /* Destination (on display) rectangle, in pixels */ u_int dst_x; u_int dst_y; u_int dst_w; u_int dst_h; /* Parsed pixel format */ u_int bits_per_pixel; bool is_yuv; /* any YUV mode */ bool is_yuv_planar; /* planar YUV mode */ uint32_t color_mode; /* DC_WIN_COLOR_DEPTH */ uint32_t swap; /* DC_WIN_BYTE_SWAP */ uint32_t surface_kind; /* DC_WINBUF_SURFACE_KIND */ uint32_t block_height; /* DC_WINBUF_SURFACE_KIND */ /* Parsed flipping, rotation is not supported for pitched modes */ bool flip_x; /* inverted X-axis */ bool flip_y; /* inverted Y-axis */ bool transpose_xy; /* swap X and Y-axis */ /* Color planes base addresses and strides */ bus_size_t base[3]; uint32_t stride[3]; /* stride[2] isn't used by HW */ }; struct dc_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; struct mtx mtx; clk_t clk_parent; clk_t clk_dc; hwreset_t hwreset_dc; int pitch_align; struct tegra_crtc tegra_crtc; struct drm_pending_vblank_event *event; struct drm_gem_object *cursor_gem; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-dc", 1}, {NULL, 0}, }; /* Convert standard drm pixel format to tegra windows parameters. */ static int dc_parse_drm_format(struct tegra_fb *fb, struct dc_window *win) { struct tegra_bo *bo; uint32_t cm; uint32_t sw; bool is_yuv, is_yuv_planar; int nplanes, i; switch (fb->drm_fb.pixel_format) { case DRM_FORMAT_XBGR8888: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_R8G8B8A8; is_yuv = false; is_yuv_planar = false; break; case DRM_FORMAT_XRGB8888: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_B8G8R8A8; is_yuv = false; is_yuv_planar = false; break; case DRM_FORMAT_RGB565: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_B5G6R5; is_yuv = false; is_yuv_planar = false; break; case DRM_FORMAT_UYVY: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_YCbCr422; is_yuv = true; is_yuv_planar = false; break; case DRM_FORMAT_YUYV: sw = BYTE_SWAP(SWAP2); cm = WIN_COLOR_DEPTH_YCbCr422; is_yuv = true; is_yuv_planar = false; break; case DRM_FORMAT_YUV420: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_YCbCr420P; is_yuv = true; is_yuv_planar = true; break; case DRM_FORMAT_YUV422: sw = BYTE_SWAP(NOSWAP); cm = WIN_COLOR_DEPTH_YCbCr422P; is_yuv = true; is_yuv_planar = true; break; default: /* Unsupported format */ return (-EINVAL); } /* Basic check of arguments. */ switch (fb->rotation) { case 0: case 180: break; case 90: /* Rotation is supported only */ case 270: /* for block linear surfaces */ if (!fb->block_linear) return (-EINVAL); break; default: return (-EINVAL); } /* XXX Add more checks (sizes, scaling...) */ if (win == NULL) return (0); win->surface_kind = fb->block_linear ? SURFACE_KIND_BL_16B2: SURFACE_KIND_PITCH; win->block_height = fb->block_height; switch (fb->rotation) { case 0: /* (0,0,0) */ win->transpose_xy = false; win->flip_x = false; win->flip_y = false; break; case 90: /* (1,0,1) */ win->transpose_xy = true; win->flip_x = false; win->flip_y = true; break; case 180: /* (0,1,1) */ win->transpose_xy = false; win->flip_x = true; win->flip_y = true; break; case 270: /* (1,1,0) */ win->transpose_xy = true; win->flip_x = true; win->flip_y = false; break; } win->flip_x ^= fb->flip_x; win->flip_y ^= fb->flip_y; win->color_mode = cm; win->swap = sw; win->bits_per_pixel = fb->drm_fb.bits_per_pixel; win->is_yuv = is_yuv; win->is_yuv_planar = is_yuv_planar; nplanes = drm_format_num_planes(fb->drm_fb.pixel_format); for (i = 0; i < nplanes; i++) { bo = fb->planes[i]; win->base[i] = bo->pbase + fb->drm_fb.offsets[i]; win->stride[i] = fb->drm_fb.pitches[i]; } return (0); } /* * Scaling functions. * * It's unclear if we want/must program the fractional portion * (aka bias) of init_dda registers, mainly when mirrored axis * modes are used. * For now, we use 1.0 as recommended by TRM. */ static inline uint32_t dc_scaling_init(uint32_t start) { return (1 << 12); } static inline uint32_t dc_scaling_incr(uint32_t src, uint32_t dst, uint32_t maxscale) { uint32_t val; val = (src - 1) << 12 ; /* 4.12 fixed float */ val /= (dst - 1); if (val > (maxscale << 12)) val = maxscale << 12; return val; } /* ------------------------------------------------------------------- * * HW Access. * */ /* * Setup pixel clock. * Minimal frequency is pixel clock, but output is free to select * any higher. */ static int dc_setup_clk(struct dc_softc *sc, struct drm_crtc *crtc, struct drm_display_mode *mode, uint32_t *div) { uint64_t pclk, freq; struct tegra_drm_encoder *output; struct drm_encoder *encoder; long rv; pclk = mode->clock * 1000; /* Find attached encoder */ output = NULL; list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head) { if (encoder->crtc == crtc) { output = container_of(encoder, struct tegra_drm_encoder, encoder); break; } } if (output == NULL) return (-ENODEV); if (output->setup_clock == NULL) panic("Output have not setup_clock function.\n"); rv = output->setup_clock(output, sc->clk_dc, pclk); if (rv != 0) { device_printf(sc->dev, "Cannot setup pixel clock: %llu\n", pclk); return (rv); } rv = clk_get_freq(sc->clk_dc, &freq); *div = (freq * 2 / pclk) - 2; DRM_DEBUG_KMS("frequency: %llu, DC divider: %u\n", freq, *div); return 0; } static void dc_setup_window(struct dc_softc *sc, unsigned int index, struct dc_window *win) { uint32_t h_offset, v_offset, h_size, v_size, bpp; uint32_t h_init_dda, v_init_dda, h_incr_dda, v_incr_dda; uint32_t val; #ifdef DMR_DEBUG_WINDOW printf("%s window: %d\n", __func__, index); printf(" src: x: %d, y: %d, w: %d, h: %d\n", win->src_x, win->src_y, win->src_w, win->src_h); printf(" dst: x: %d, y: %d, w: %d, h: %d\n", win->dst_x, win->dst_y, win->dst_w, win->dst_h); printf(" bpp: %d, color_mode: %d, swap: %d\n", win->bits_per_pixel, win->color_mode, win->swap); #endif if (win->is_yuv) bpp = win->is_yuv_planar ? 1 : 2; else bpp = (win->bits_per_pixel + 7) / 8; if (!win->transpose_xy) { h_size = win->src_w * bpp; v_size = win->src_h; } else { h_size = win->src_h * bpp; v_size = win->src_w; } h_offset = win->src_x * bpp; v_offset = win->src_y; if (win->flip_x) { h_offset += win->src_w * bpp - 1; } if (win->flip_y) v_offset += win->src_h - 1; /* Adjust offsets for planar yuv modes */ if (win->is_yuv_planar) { h_offset &= ~1; if (win->flip_x ) h_offset |= 1; v_offset &= ~1; if (win->flip_y ) v_offset |= 1; } /* Setup scaling. */ if (!win->transpose_xy) { h_init_dda = dc_scaling_init(win->src_x); v_init_dda = dc_scaling_init(win->src_y); h_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 4); v_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 15); } else { h_init_dda = dc_scaling_init(win->src_y); v_init_dda = dc_scaling_init(win->src_x); h_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 4); v_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 15); } #ifdef DMR_DEBUG_WINDOW printf("\n"); printf(" bpp: %d, size: h: %d v: %d, offset: h:%d v: %d\n", bpp, h_size, v_size, h_offset, v_offset); printf(" init_dda: h: %d v: %d, incr_dda: h: %d v: %d\n", h_init_dda, v_init_dda, h_incr_dda, v_incr_dda); #endif LOCK(sc); /* Select target window */ val = WINDOW_A_SELECT << index; WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, val); /* Sizes */ WR4(sc, DC_WIN_POSITION, WIN_POSITION(win->dst_x, win->dst_y)); WR4(sc, DC_WIN_SIZE, WIN_SIZE(win->dst_w, win->dst_h)); WR4(sc, DC_WIN_PRESCALED_SIZE, WIN_PRESCALED_SIZE(h_size, v_size)); /* DDA */ WR4(sc, DC_WIN_DDA_INCREMENT, WIN_DDA_INCREMENT(h_incr_dda, v_incr_dda)); WR4(sc, DC_WIN_H_INITIAL_DDA, h_init_dda); WR4(sc, DC_WIN_V_INITIAL_DDA, v_init_dda); /* Color planes base addresses and strides */ WR4(sc, DC_WINBUF_START_ADDR, win->base[0]); if (win->is_yuv_planar) { WR4(sc, DC_WINBUF_START_ADDR_U, win->base[1]); WR4(sc, DC_WINBUF_START_ADDR_V, win->base[2]); WR4(sc, DC_WIN_LINE_STRIDE, win->stride[1] << 16 | win->stride[0]); } else { WR4(sc, DC_WIN_LINE_STRIDE, win->stride[0]); } /* Offsets for rotation and axis flip */ WR4(sc, DC_WINBUF_ADDR_H_OFFSET, h_offset); WR4(sc, DC_WINBUF_ADDR_V_OFFSET, v_offset); /* Color format */ WR4(sc, DC_WIN_COLOR_DEPTH, win->color_mode); WR4(sc, DC_WIN_BYTE_SWAP, win->swap); /* Tiling */ val = win->surface_kind; if (win->surface_kind == SURFACE_KIND_BL_16B2) val |= SURFACE_KIND_BLOCK_HEIGHT(win->block_height); WR4(sc, DC_WINBUF_SURFACE_KIND, val); /* Color space coefs for YUV modes */ if (win->is_yuv) { WR4(sc, DC_WINC_CSC_YOF, 0x00f0); WR4(sc, DC_WINC_CSC_KYRGB, 0x012a); WR4(sc, DC_WINC_CSC_KUR, 0x0000); WR4(sc, DC_WINC_CSC_KVR, 0x0198); WR4(sc, DC_WINC_CSC_KUG, 0x039b); WR4(sc, DC_WINC_CSC_KVG, 0x032f); WR4(sc, DC_WINC_CSC_KUB, 0x0204); WR4(sc, DC_WINC_CSC_KVB, 0x0000); } val = WIN_ENABLE; if (win->is_yuv) val |= CSC_ENABLE; else if (win->bits_per_pixel < 24) val |= COLOR_EXPAND; if (win->flip_y) val |= V_DIRECTION; if (win->flip_x) val |= H_DIRECTION; if (win->transpose_xy) val |= SCAN_COLUMN; WR4(sc, DC_WINC_WIN_OPTIONS, val); #ifdef DMR_DEBUG_WINDOW /* Set underflow debug mode -> highlight missing pixels. */ WR4(sc, DC_WINBUF_UFLOW_CTRL, UFLOW_CTR_ENABLE); WR4(sc, DC_WINBUF_UFLOW_DBG_PIXEL, 0xFFFF0000); #endif UNLOCK(sc); } /* ------------------------------------------------------------------- * * Plane functions. * */ static int dc_plane_update(struct drm_plane *drm_plane, struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { struct tegra_plane *plane; struct tegra_crtc *crtc; struct tegra_fb *fb; struct dc_softc *sc; struct dc_window win; int rv; plane = container_of(drm_plane, struct tegra_plane, drm_plane); fb = container_of(drm_fb, struct tegra_fb, drm_fb); crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); memset(&win, 0, sizeof(win)); win.src_x = src_x >> 16; win.src_y = src_y >> 16; win.src_w = src_w >> 16; win.src_h = src_h >> 16; win.dst_x = crtc_x; win.dst_y = crtc_y; win.dst_w = crtc_w; win.dst_h = crtc_h; rv = dc_parse_drm_format(fb, &win); if (rv != 0) { DRM_WARNING("unsupported pixel format %d\n", fb->drm_fb.pixel_format); return (rv); } dc_setup_window(sc, plane->index, &win); WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << plane->index); WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << plane->index); return (0); } static int dc_plane_disable(struct drm_plane *drm_plane) { struct tegra_plane *plane; struct tegra_crtc *crtc; struct dc_softc *sc; uint32_t val, idx; if (drm_plane->crtc == NULL) return (0); plane = container_of(drm_plane, struct tegra_plane, drm_plane); crtc = container_of(drm_plane->crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); idx = plane->index; LOCK(sc); WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT << idx); val = RD4(sc, DC_WINC_WIN_OPTIONS); val &= ~WIN_ENABLE; WR4(sc, DC_WINC_WIN_OPTIONS, val); UNLOCK(sc); WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << idx); WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << idx); return (0); } static void dc_plane_destroy(struct drm_plane *plane) { dc_plane_disable(plane); drm_plane_cleanup(plane); free(plane, DRM_MEM_KMS); } static const struct drm_plane_funcs dc_plane_funcs = { .update_plane = dc_plane_update, .disable_plane = dc_plane_disable, .destroy = dc_plane_destroy, }; /* ------------------------------------------------------------------- * * CRTC helper functions. * */ static void dc_crtc_dpms(struct drm_crtc *crtc, int mode) { /* Empty function */ } static bool dc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted) { return (true); } static int dc_set_base(struct dc_softc *sc, int x, int y, struct tegra_fb *fb) { struct dc_window win; int rv; memset(&win, 0, sizeof(win)); win.src_x = x; win.src_y = y; win.src_w = fb->drm_fb.width; win.src_h = fb->drm_fb.height; win.dst_x = x; win.dst_y = y; win.dst_w = fb->drm_fb.width; win.dst_h = fb->drm_fb.height; rv = dc_parse_drm_format(fb, &win); if (rv != 0) { DRM_WARNING("unsupported pixel format %d\n", fb->drm_fb.pixel_format); return (rv); } dc_setup_window(sc, 0, &win); return (0); } static int dc_crtc_mode_set(struct drm_crtc *drm_crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted, int x, int y, struct drm_framebuffer *old_fb) { struct dc_softc *sc; struct tegra_crtc *crtc; struct tegra_fb *fb; struct dc_window win; uint32_t div, h_ref_to_sync, v_ref_to_sync; int rv; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); h_ref_to_sync = 1; v_ref_to_sync = 1; /* Setup timing */ rv = dc_setup_clk(sc, drm_crtc, mode, &div); if (rv != 0) { device_printf(sc->dev, "Cannot set pixel clock\n"); return (rv); } /* Timing */ WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, 0); WR4(sc, DC_DISP_REF_TO_SYNC, (v_ref_to_sync << 16) | h_ref_to_sync); WR4(sc, DC_DISP_SYNC_WIDTH, ((mode->vsync_end - mode->vsync_start) << 16) | ((mode->hsync_end - mode->hsync_start) << 0)); WR4(sc, DC_DISP_BACK_PORCH, ((mode->vtotal - mode->vsync_end) << 16) | ((mode->htotal - mode->hsync_end) << 0)); WR4(sc, DC_DISP_FRONT_PORCH, ((mode->vsync_start - mode->vdisplay) << 16) | ((mode->hsync_start - mode->hdisplay) << 0)); WR4(sc, DC_DISP_DISP_ACTIVE, (mode->vdisplay << 16) | mode->hdisplay); WR4(sc, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT(DF1P1C)); WR4(sc,DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER(PCD1)); memset(&win, 0, sizeof(win)); win.src_x = x; win.src_y = y; win.src_w = mode->hdisplay; win.src_h = mode->vdisplay; win.dst_x = x; win.dst_y = y; win.dst_w = mode->hdisplay; win.dst_h = mode->vdisplay; rv = dc_parse_drm_format(fb, &win); if (rv != 0) { DRM_WARNING("unsupported pixel format %d\n", drm_crtc->fb->pixel_format); return (rv); } dc_setup_window(sc, 0, &win); return (0); } static int dc_crtc_mode_set_base(struct drm_crtc *drm_crtc, int x, int y, struct drm_framebuffer *old_fb) { struct dc_softc *sc; struct tegra_crtc *crtc; struct tegra_fb *fb; int rv; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); sc = device_get_softc(crtc->dev); rv = dc_set_base(sc, x, y, fb); /* Commit */ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ); return (rv); } static void dc_crtc_prepare(struct drm_crtc *drm_crtc) { struct dc_softc *sc; struct tegra_crtc *crtc; uint32_t val; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); WR4(sc, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL); /* XXX allocate syncpoint from host1x */ WR4(sc, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | (sc->tegra_crtc.nvidia_head == 0 ? SYNCPT_VBLANK0: SYNCPT_VBLANK1)); WR4(sc, DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); val = RD4(sc, DC_CMD_DISPLAY_COMMAND); val |= DISPLAY_CTRL_MODE(CTRL_MODE_C_DISPLAY); WR4(sc, DC_CMD_DISPLAY_COMMAND, val); WR4(sc, DC_CMD_INT_MASK, WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); WR4(sc, DC_CMD_INT_ENABLE, VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); } static void dc_crtc_commit(struct drm_crtc *drm_crtc) { struct dc_softc *sc; struct tegra_crtc *crtc; uint32_t val; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); val = RD4(sc, DC_CMD_INT_MASK); val |= FRAME_END_INT; WR4(sc, DC_CMD_INT_MASK, val); val = RD4(sc, DC_CMD_INT_ENABLE); val |= FRAME_END_INT; WR4(sc, DC_CMD_INT_ENABLE, val); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ); } static void dc_crtc_load_lut(struct drm_crtc *crtc) { /* empty function */ } static const struct drm_crtc_helper_funcs dc_crtc_helper_funcs = { .dpms = dc_crtc_dpms, .mode_fixup = dc_crtc_mode_fixup, .mode_set = dc_crtc_mode_set, .mode_set_base = dc_crtc_mode_set_base, .prepare = dc_crtc_prepare, .commit = dc_crtc_commit, .load_lut = dc_crtc_load_lut, }; static int drm_crtc_index(struct drm_crtc *crtc) { int idx; struct drm_crtc *tmp; idx = 0; list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { if (tmp == crtc) return (idx); idx++; } panic("Cannot find CRTC"); } /* ------------------------------------------------------------------- * * Exported functions (mainly vsync related). * * XXX revisit this -> convert to bus methods? */ int tegra_dc_get_pipe(struct drm_crtc *drm_crtc) { struct tegra_crtc *crtc; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); return (crtc->nvidia_head); } void tegra_dc_enable_vblank(struct drm_crtc *drm_crtc) { struct dc_softc *sc; struct tegra_crtc *crtc; uint32_t val; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); LOCK(sc); val = RD4(sc, DC_CMD_INT_MASK); val |= VBLANK_INT; WR4(sc, DC_CMD_INT_MASK, val); UNLOCK(sc); } void tegra_dc_disable_vblank(struct drm_crtc *drm_crtc) { struct dc_softc *sc; struct tegra_crtc *crtc; uint32_t val; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); LOCK(sc); val = RD4(sc, DC_CMD_INT_MASK); val &= ~VBLANK_INT; WR4(sc, DC_CMD_INT_MASK, val); UNLOCK(sc); } static void dc_finish_page_flip(struct dc_softc *sc) { struct drm_crtc *drm_crtc; struct drm_device *drm; struct tegra_fb *fb; struct tegra_bo *bo; uint32_t base; int idx; drm_crtc = &sc->tegra_crtc.drm_crtc; drm = drm_crtc->dev; fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); mtx_lock(&drm->event_lock); if (sc->event == NULL) { mtx_unlock(&drm->event_lock); return; } LOCK(sc); /* Read active copy of WINBUF_START_ADDR */ WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT); WR4(sc, DC_CMD_STATE_ACCESS, READ_MUX); base = RD4(sc, DC_WINBUF_START_ADDR); WR4(sc, DC_CMD_STATE_ACCESS, 0); UNLOCK(sc); /* Is already active */ bo = tegra_fb_get_plane(fb, 0); if (base == (bo->pbase + fb->drm_fb.offsets[0])) { idx = drm_crtc_index(drm_crtc); drm_send_vblank_event(drm, idx, sc->event); drm_vblank_put(drm, idx); sc->event = NULL; } mtx_unlock(&drm->event_lock); } void tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, struct drm_file *file) { struct dc_softc *sc; struct tegra_crtc *crtc; struct drm_device *drm; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); drm = drm_crtc->dev; mtx_lock(&drm->event_lock); if ((sc->event != NULL) && (sc->event->base.file_priv == file)) { sc->event->base.destroy(&sc->event->base); drm_vblank_put(drm, drm_crtc_index(drm_crtc)); sc->event = NULL; } mtx_unlock(&drm->event_lock); } /* ------------------------------------------------------------------- * * CRTC functions. * */ static int dc_page_flip(struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb, struct drm_pending_vblank_event *event) { struct dc_softc *sc; struct tegra_crtc *crtc; struct tegra_fb *fb; struct drm_device *drm; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); drm = drm_crtc->dev; if (sc->event != NULL) return (-EBUSY); if (event != NULL) { event->pipe = sc->tegra_crtc.nvidia_head; sc->event = event; drm_vblank_get(drm, event->pipe); } dc_set_base(sc, drm_crtc->x, drm_crtc->y, fb); drm_crtc->fb = drm_fb; /* Commit */ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); return (0); } static int dc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file, uint32_t handle, uint32_t width, uint32_t height) { struct dc_softc *sc; struct tegra_crtc *crtc; struct drm_gem_object *gem; struct tegra_bo *bo; int i; uint32_t val, *src, *dst; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); if (width != height) return (-EINVAL); switch (width) { case 32: val = CURSOR_SIZE(C32x32); break; case 64: val = CURSOR_SIZE(C64x64); break; case 128: val = CURSOR_SIZE(C128x128); break; case 256: val = CURSOR_SIZE(C256x256); break; default: return (-EINVAL); } bo = NULL; gem = NULL; if (handle != 0) { gem = drm_gem_object_lookup(drm_crtc->dev, file, handle); if (gem == NULL) return (-ENOENT); bo = container_of(gem, struct tegra_bo, gem_obj); } if (sc->cursor_gem != NULL) { drm_gem_object_unreference(sc->cursor_gem); } sc->cursor_gem = gem; if (bo != NULL) { /* * Copy cursor into cache and convert it from ARGB to RGBA. * XXXX - this is broken by design - client can write to BO at * any time. We can dedicate other window for cursor or switch * to sw cursor in worst case. */ src = (uint32_t *)bo->vbase; dst = (uint32_t *)crtc->cursor_vbase; for (i = 0; i < width * height; i++) dst[i] = (src[i] << 8) | (src[i] >> 24); val |= CURSOR_CLIP(CC_DISPLAY); val |= CURSOR_START_ADDR(crtc->cursor_pbase); WR4(sc, DC_DISP_CURSOR_START_ADDR, val); val = RD4(sc, DC_DISP_BLEND_CURSOR_CONTROL); val &= ~CURSOR_DST_BLEND_FACTOR_SELECT(~0); val &= ~CURSOR_SRC_BLEND_FACTOR_SELECT(~0); val |= CURSOR_MODE_SELECT; val |= CURSOR_DST_BLEND_FACTOR_SELECT(DST_NEG_K1_TIMES_SRC); val |= CURSOR_SRC_BLEND_FACTOR_SELECT(SRC_BLEND_K1_TIMES_SRC); val |= CURSOR_ALPHA(~0); WR4(sc, DC_DISP_BLEND_CURSOR_CONTROL, val); val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); val |= CURSOR_ENABLE; WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); } else { val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); val &= ~CURSOR_ENABLE; WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); } /* XXX This fixes cursor underflow issues, but why ? */ WR4(sc, DC_DISP_CURSOR_UNDERFLOW_CTRL, CURSOR_UFLOW_CYA); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | CURSOR_UPDATE ); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | CURSOR_ACT_REQ); return (0); } static int dc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) { struct dc_softc *sc; struct tegra_crtc *crtc; crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); sc = device_get_softc(crtc->dev); WR4(sc, DC_DISP_CURSOR_POSITION, CURSOR_POSITION(x, y)); WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_UPDATE); WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_ACT_REQ); return (0); } static void dc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); memset(crtc, 0, sizeof(*crtc)); } static const struct drm_crtc_funcs dc_crtc_funcs = { .page_flip = dc_page_flip, .cursor_set = dc_cursor_set, .cursor_move = dc_cursor_move, .set_config = drm_crtc_helper_set_config, .destroy = dc_destroy, }; /* ------------------------------------------------------------------- * * Bus and infrastructure. * */ static int dc_init_planes(struct dc_softc *sc, struct tegra_drm *drm) { int i, rv; struct tegra_plane *plane; rv = 0; for (i = 0; i < DC_MAX_PLANES; i++) { plane = malloc(sizeof(*plane), DRM_MEM_KMS, M_WAITOK | M_ZERO); plane->index = i + 1; rv = drm_plane_init(&drm->drm_dev, &plane->drm_plane, 1 << sc->tegra_crtc.nvidia_head, &dc_plane_funcs, dc_plane_formats, nitems(dc_plane_formats), false); if (rv != 0) { free(plane, DRM_MEM_KMS); return (rv); } } return 0; } static void dc_display_enable(device_t dev, bool enable) { struct dc_softc *sc; uint32_t val; sc = device_get_softc(dev); /* Set display mode */ val = enable ? CTRL_MODE_C_DISPLAY: CTRL_MODE_STOP; WR4(sc, DC_CMD_DISPLAY_COMMAND, DISPLAY_CTRL_MODE(val)); /* and commit it*/ WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE); WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ); } static void dc_hdmi_enable(device_t dev, bool enable) { struct dc_softc *sc; uint32_t val; sc = device_get_softc(dev); val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); if (enable) val |= HDMI_ENABLE; else val &= ~HDMI_ENABLE; WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); } static void dc_setup_timing(device_t dev, int h_pulse_start) { struct dc_softc *sc; sc = device_get_softc(dev); /* Setup display timing */ WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(1)); WR4(sc, DC_DISP_DISP_COLOR_CONTROL, DITHER_CONTROL(DITHER_DISABLE) | BASE_COLOR_SIZE(SIZE_BASE888)); WR4(sc, DC_DISP_DISP_SIGNAL_OPTIONS0, H_PULSE2_ENABLE); WR4(sc, DC_DISP_H_PULSE2_CONTROL, PULSE_CONTROL_QUAL(QUAL_VACTIVE) | PULSE_CONTROL_LAST(LAST_END_A)); WR4(sc, DC_DISP_H_PULSE2_POSITION_A, PULSE_START(h_pulse_start) | PULSE_END(h_pulse_start + 8)); } static void dc_intr(void *arg) { struct dc_softc *sc; uint32_t status; sc = arg; /* Confirm interrupt */ status = RD4(sc, DC_CMD_INT_STATUS); WR4(sc, DC_CMD_INT_STATUS, status); if (status & VBLANK_INT) { drm_handle_vblank(sc->tegra_crtc.drm_crtc.dev, sc->tegra_crtc.nvidia_head); dc_finish_page_flip(sc); } } static int dc_init_client(device_t dev, device_t host1x, struct tegra_drm *drm) { struct dc_softc *sc; int rv; sc = device_get_softc(dev); if (drm->pitch_align < sc->pitch_align) drm->pitch_align = sc->pitch_align; drm_crtc_init(&drm->drm_dev, &sc->tegra_crtc.drm_crtc, &dc_crtc_funcs); drm_mode_crtc_set_gamma_size(&sc->tegra_crtc.drm_crtc, 256); drm_crtc_helper_add(&sc->tegra_crtc.drm_crtc, &dc_crtc_helper_funcs); rv = dc_init_planes(sc, drm); if (rv!= 0){ device_printf(dev, "Cannot init planes\n"); return (rv); } WR4(sc, DC_CMD_INT_TYPE, WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); WR4(sc, DC_CMD_INT_POLARITY, WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); WR4(sc, DC_CMD_INT_ENABLE, 0); WR4(sc, DC_CMD_INT_MASK, 0); rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, dc_intr, sc, &sc->irq_ih); if (rv != 0) { device_printf(dev, "Cannot register interrupt handler\n"); return (rv); } /* allocate memory for cursor cache */ sc->tegra_crtc.cursor_vbase = kmem_alloc_contig(256 * 256 * 4, M_WAITOK | M_ZERO, 0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_WRITE_COMBINING); sc->tegra_crtc.cursor_pbase = vtophys((uintptr_t)sc->tegra_crtc.cursor_vbase); return (0); } static int dc_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm) { struct dc_softc *sc; sc = device_get_softc(dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sc->irq_ih = NULL; return (0); } static int get_fdt_resources(struct dc_softc *sc, phandle_t node) { int rv; rv = hwreset_get_by_ofw_name(sc->dev, 0, "dc", &sc->hwreset_dc); if (rv != 0) { device_printf(sc->dev, "Cannot get 'dc' reset\n"); return (rv); } rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent); if (rv != 0) { device_printf(sc->dev, "Cannot get 'parent' clock\n"); return (rv); } rv = clk_get_by_ofw_name(sc->dev, 0, "dc", &sc->clk_dc); if (rv != 0) { device_printf(sc->dev, "Cannot get 'dc' clock\n"); return (rv); } rv = OF_getencprop(node, "nvidia,head", &sc->tegra_crtc.nvidia_head, sizeof(sc->tegra_crtc.nvidia_head)); if (rv <= 0) { device_printf(sc->dev, "Cannot get 'nvidia,head' property\n"); return (rv); } return (0); } static int enable_fdt_resources(struct dc_softc *sc) { int id, rv; rv = clk_set_parent_by_clk(sc->clk_dc, sc->clk_parent); if (rv != 0) { device_printf(sc->dev, "Cannot set parent for 'dc' clock\n"); return (rv); } id = (sc->tegra_crtc.nvidia_head == 0) ? TEGRA_POWERGATE_DIS: TEGRA_POWERGATE_DISB; rv = tegra_powergate_sequence_power_up(id, sc->clk_dc, sc->hwreset_dc); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'DIS' powergate\n"); return (rv); } return (0); } static int dc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Tegra Display Controller"); return (BUS_PROBE_DEFAULT); } static int dc_attach(device_t dev) { struct dc_softc *sc; phandle_t node; int rid, rv; sc = device_get_softc(dev); sc->dev = dev; sc->tegra_crtc.dev = dev; node = ofw_bus_get_node(sc->dev); LOCK_INIT(sc); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } rv = get_fdt_resources(sc, node); if (rv != 0) { device_printf(dev, "Cannot parse FDT resources\n"); goto fail; } rv = enable_fdt_resources(sc); if (rv != 0) { device_printf(dev, "Cannot enable FDT resources\n"); goto fail; } /* * Tegra124 * - 64 for RGB modes * - 128 for YUV planar modes * - 256 for block linear modes */ sc->pitch_align = 256; rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (rv != 0) { device_printf(dev, "Cannot register DRM device\n"); goto fail; } return (bus_generic_attach(dev)); fail: TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->clk_parent != NULL) clk_release(sc->clk_parent); if (sc->clk_dc != NULL) clk_release(sc->clk_dc); if (sc->hwreset_dc != NULL) hwreset_release(sc->hwreset_dc); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); return (ENXIO); } static int dc_detach(device_t dev) { struct dc_softc *sc; sc = device_get_softc(dev); TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->clk_parent != NULL) clk_release(sc->clk_parent); if (sc->clk_dc != NULL) clk_release(sc->clk_dc); if (sc->hwreset_dc != NULL) hwreset_release(sc->hwreset_dc); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); return (bus_generic_detach(dev)); } static device_method_t tegra_dc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dc_probe), DEVMETHOD(device_attach, dc_attach), DEVMETHOD(device_detach, dc_detach), /* tegra drm interface */ DEVMETHOD(tegra_drm_init_client, dc_init_client), DEVMETHOD(tegra_drm_exit_client, dc_exit_client), /* tegra dc interface */ DEVMETHOD(tegra_dc_display_enable, dc_display_enable), DEVMETHOD(tegra_dc_hdmi_enable, dc_hdmi_enable), DEVMETHOD(tegra_dc_setup_timing, dc_setup_timing), DEVMETHOD_END }; DEFINE_CLASS_0(tegra_dc, tegra_dc_driver, tegra_dc_methods, sizeof(struct dc_softc)); DRIVER_MODULE(tegra_dc, host1x, tegra_dc_driver, NULL, NULL); diff --git a/sys/arm/nvidia/drm2/tegra_hdmi.c b/sys/arm/nvidia/drm2/tegra_hdmi.c index 1c5e86bde498..174a9718bbfa 100644 --- a/sys/arm/nvidia/drm2/tegra_hdmi.c +++ b/sys/arm/nvidia/drm2/tegra_hdmi.c @@ -1,1315 +1,1315 @@ /*- * Copyright (c) 2015 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 #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "tegra_dc_if.h" #include "tegra_drm_if.h" #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r)) /* HDA stream format verb. */ #define AC_FMT_CHAN_GET(x) (((x) >> 0) & 0xf) #define AC_FMT_CHAN_BITS_GET(x) (((x) >> 4) & 0x7) #define AC_FMT_DIV_GET(x) (((x) >> 8) & 0x7) #define AC_FMT_MUL_GET(x) (((x) >> 11) & 0x7) #define AC_FMT_BASE_44K (1 << 14) #define AC_FMT_TYPE_NON_PCM (1 << 15) #define HDMI_REKEY_DEFAULT 56 #define HDMI_ELD_BUFFER_SIZE 96 #define HDMI_DC_CLOCK_MULTIPIER 2 struct audio_reg { uint32_t audio_clk; bus_size_t acr_reg; bus_size_t nval_reg; bus_size_t aval_reg; }; static const struct audio_reg audio_regs[] = { { .audio_clk = 32000, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320, }, { .audio_clk = 44100, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441, }, { .audio_clk = 88200, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882, }, { .audio_clk = 176400, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764, }, { .audio_clk = 48000, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480, }, { .audio_clk = 96000, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960, }, { .audio_clk = 192000, .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW, .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920, .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920, }, }; struct tmds_config { uint32_t pclk; uint32_t pll0; uint32_t pll1; uint32_t drive_c; uint32_t pe_c; uint32_t peak_c; uint32_t pad_ctls; }; static const struct tmds_config tegra124_tmds_config[] = { { /* 480p/576p / 25.2MHz/27MHz */ .pclk = 27000000, .pll0 = 0x01003010, .pll1 = 0x00301B00, .drive_c = 0x1F1F1F1F, .pe_c = 0x00000000, .peak_c = 0x03030303, .pad_ctls = 0x800034BB, }, { /* 720p/1080i / 74.25MHz */ .pclk = 74250000, .pll0 = 0x01003110, .pll1 = 0x00301500, .drive_c = 0x2C2C2C2C, .pe_c = 0x00000000, .peak_c = 0x07070707, .pad_ctls = 0x800034BB, }, { /* 1080p / 148.5MHz */ .pclk = 148500000, .pll0 = 0x01003310, .pll1 = 0x00301500, .drive_c = 0x33333333, .pe_c = 0x00000000, .peak_c = 0x0C0C0C0C, .pad_ctls = 0x800034BB, }, { /* 2216p / 297MHz */ .pclk = UINT_MAX, .pll0 = 0x01003F10, .pll1 = 0x00300F00, .drive_c = 0x37373737, .pe_c = 0x00000000, .peak_c = 0x17171717, .pad_ctls = 0x800036BB, }, }; struct hdmi_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; clk_t clk_parent; clk_t clk_hdmi; hwreset_t hwreset_hdmi; regulator_t supply_hdmi; regulator_t supply_pll; regulator_t supply_vdd; uint64_t pclk; boolean_t hdmi_mode; int audio_src_type; int audio_freq; int audio_chans; struct tegra_drm *drm; struct tegra_drm_encoder output; const struct tmds_config *tmds_config; int n_tmds_configs; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-hdmi", 1}, {NULL, 0}, }; /* These functions have been copied from newer version of drm_edid.c */ /* ELD Header Block */ #define DRM_ELD_HEADER_BLOCK_SIZE 4 #define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ static int drm_eld_size(const uint8_t *eld) { return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; } static int drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, struct drm_display_mode *mode) { int rv; if (!frame || !mode) return -EINVAL; rv = hdmi_avi_infoframe_init(frame); if (rv < 0) return rv; if (mode->flags & DRM_MODE_FLAG_DBLCLK) frame->pixel_repeat = 1; frame->video_code = drm_match_cea_mode(mode); frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; #ifdef FREEBSD_NOTYET /* * Populate picture aspect ratio from either * user input (if specified) or from the CEA mode list. */ if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) frame->picture_aspect = mode->picture_aspect_ratio; else if (frame->video_code > 0) frame->picture_aspect = drm_get_cea_aspect_ratio( frame->video_code); #endif frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; return 0; } /* --------------------------------------------------------------------- */ static int hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk) { struct hdmi_softc *sc; uint64_t freq; int rv; sc = device_get_softc(output->dev); /* Disable consumers clock for while. */ rv = clk_disable(sc->clk_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot disable 'hdmi' clock\n"); return (rv); } rv = clk_disable(clk); if (rv != 0) { device_printf(sc->dev, "Cannot disable display clock\n"); return (rv); } /* Set frequency for Display Controller PLL. */ freq = HDMI_DC_CLOCK_MULTIPIER * pclk; rv = clk_set_freq(sc->clk_parent, freq, 0); if (rv != 0) { device_printf(output->dev, "Cannot set display pixel frequency\n"); return (rv); } /* Reparent display controller */ rv = clk_set_parent_by_clk(clk, sc->clk_parent); if (rv != 0) { device_printf(output->dev, "Cannot set parent clock\n"); return (rv); } rv = clk_set_freq(clk, freq, 0); if (rv != 0) { device_printf(output->dev, "Cannot set display controller frequency\n"); return (rv); } rv = clk_set_freq(sc->clk_hdmi, pclk, 0); if (rv != 0) { device_printf(output->dev, "Cannot set display controller frequency\n"); return (rv); } /* And reenable consumers clock. */ rv = clk_enable(clk); if (rv != 0) { device_printf(sc->dev, "Cannot enable display clock\n"); return (rv); } rv = clk_enable(sc->clk_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); return (rv); } rv = clk_get_freq(clk, &freq); if (rv != 0) { device_printf(output->dev, "Cannot get display controller frequency\n"); return (rv); } DRM_DEBUG_KMS("DC frequency: %llu\n", freq); return (0); } /* ------------------------------------------------------------------- * * Infoframes. * */ static void avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode) { struct hdmi_avi_infoframe frame; uint8_t buf[17], *hdr, *pb; ssize_t rv; rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); if (rv < 0) { device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv); return; } rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); if (rv < 0) { device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv); return; } hdr = buf + 0; pb = buf + 3; WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW, (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH, (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW, (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH, (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, AVI_INFOFRAME_CTRL_ENABLE); } static void audio_setup_infoframe(struct hdmi_softc *sc) { struct hdmi_audio_infoframe frame; uint8_t buf[14], *hdr, *pb; ssize_t rv; rv = hdmi_audio_infoframe_init(&frame); frame.channels = sc->audio_chans; rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf)); if (rv < 0) { device_printf(sc->dev, "Cannot pack audio infoframe\n"); return; } hdr = buf + 0; pb = buf + 3; WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW, (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH, (pb[5] << 8) | (pb[4] << 0)); WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, AUDIO_INFOFRAME_CTRL_ENABLE); } /* ------------------------------------------------------------------- * * Audio * */ static void init_hda_eld(struct hdmi_softc *sc) { size_t size; int i ; uint32_t val; size = drm_eld_size(sc->output.connector.eld); for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) { val = i << 8; if (i < size) val |= sc->output.connector.eld[i]; WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val); } WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE, SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT); } static int get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg, bus_size_t *aval_reg) { int i; const struct audio_reg *reg; for (i = 0; i < nitems(audio_regs) ; i++) { reg = audio_regs + i; if (reg->audio_clk == freq) { if (acr_reg != NULL) *acr_reg = reg->acr_reg; if (nval_reg != NULL) *nval_reg = reg->nval_reg; if (aval_reg != NULL) *aval_reg = reg->aval_reg; return (0); } } return (ERANGE); } #define FR_BITS 16 #define TO_FFP(x) (((int64_t)(x)) << FR_BITS) #define TO_INT(x) ((int)((x) >> FR_BITS)) static int get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz, uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a) { int min_n; int max_n; int ideal_n; int n; int cts; int aval; int64_t err_f; int64_t min_err_f; int64_t cts_f; int64_t aval_f; int64_t half_f; /* constant 0.5 */ bool better_n; /* * All floats are in fixed I48.16 format. * * Ideal ACR interval is 1000 hz (1 ms); * acceptable is 300 hz .. 1500 hz */ min_n = 128 * audio_freq_hz / 1500; max_n = 128 * audio_freq_hz / 300; ideal_n = 128 * audio_freq_hz / 1000; min_err_f = TO_FFP(100); half_f = TO_FFP(1) / 2; *best_n = 0; *best_cts = 0; *best_a = 0; for (n = min_n; n <= max_n; n++) { cts_f = TO_FFP(pixclk_freq_hz); cts_f *= n; cts_f /= 128 * audio_freq_hz; cts = TO_INT(cts_f + half_f); /* round */ err_f = cts_f - TO_FFP(cts); if (err_f < 0) err_f = -err_f; aval_f = TO_FFP(24000000); aval_f *= n; aval_f /= 128 * audio_freq_hz; aval = TO_INT(aval_f); /* truncate */ better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n); if (TO_FFP(aval) == aval_f && (err_f < min_err_f || (err_f == min_err_f && better_n))) { min_err_f = err_f; *best_n = (uint32_t)n; *best_cts = (uint32_t)cts; *best_a = (uint32_t)aval; if (err_f == 0 && n == ideal_n) break; } } return (0); } #undef FR_BITS #undef TO_FFP #undef TO_INT static int audio_setup(struct hdmi_softc *sc) { uint32_t val; uint32_t audio_n; uint32_t audio_cts; uint32_t audio_aval; uint64_t hdmi_freq; bus_size_t aval_reg; int rv; if (!sc->hdmi_mode) return (ENOTSUP); rv = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg); if (rv != 0) { device_printf(sc->dev, "Unsupported audio frequency.\n"); return (rv); } rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq); if (rv != 0) { device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv); return (rv); } rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n, &audio_aval); if (rv != 0) { device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv); return (rv); } /* Audio infoframe. */ audio_setup_infoframe(sc); /* Setup audio source */ WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0, SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) | SOR_AUDIO_CNTRL0_INJECT_NULLSMPL); val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); val |= SOR_AUDIO_SPARE0_HBR_ENABLE; WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val); WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0); WR4(sc, HDMI_NV_PDISP_AUDIO_N, AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | AUDIO_N_VALUE(audio_n - 1)); WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH, ACR_SUBPACK_N(audio_n) | ACR_ENABLE); WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, ACR_SUBPACK_CTS(audio_cts)); WR4(sc, HDMI_NV_PDISP_HDMI_SPARE, SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1)); val = RD4(sc, HDMI_NV_PDISP_AUDIO_N); val &= ~AUDIO_N_RESETF; WR4(sc, HDMI_NV_PDISP_AUDIO_N, val); WR4(sc, aval_reg, audio_aval); return (0); } static void audio_disable(struct hdmi_softc *sc) { uint32_t val; /* Disable audio */ val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); val &= ~GENERIC_CTRL_AUDIO; WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); /* Disable audio infoframes */ val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); val &= ~AUDIO_INFOFRAME_CTRL_ENABLE; WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); } static void audio_enable(struct hdmi_softc *sc) { uint32_t val; if (!sc->hdmi_mode) audio_disable(sc); /* Enable audio infoframes */ val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); val |= AUDIO_INFOFRAME_CTRL_ENABLE; WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); /* Enable audio */ val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); val |= GENERIC_CTRL_AUDIO; WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); } /* ------------------------------------------------------------------- * * HDMI. * */ /* Process format change notification from HDA */ static void hda_intr(struct hdmi_softc *sc) { uint32_t val; int rv; if (!sc->hdmi_mode) return; val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); if ((val & (1 << 30)) == 0) { audio_disable(sc); return; } /* XXX Move this to any header */ /* Keep in sync with HDA */ sc->audio_freq = val & 0x00FFFFFF; sc->audio_chans = (val >> 24) & 0x0f; DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans, sc->audio_freq); rv = audio_setup(sc); if (rv != 0) { audio_disable(sc); return; } audio_enable(sc); } static void tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds) { WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0); WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1); WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c); WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c); WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c); WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls); } static int hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode) { int i; uint32_t val; /* Enable TMDS macro */ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); val &= ~SOR_PLL0_PWR; val &= ~SOR_PLL0_VCOPD; val &= ~SOR_PLL0_PULLDOWN; WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); DELAY(10); val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); val &= ~SOR_PLL0_PDBG; WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW); WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0); /* Wait until SOR is ready */ for (i = 1000; i > 0; i--) { val = RD4(sc, HDMI_NV_PDISP_SOR_PWR); if ((val & SOR_PWR_SETTING_NEW) == 0) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timeouted while enabling SOR power.\n"); return (ETIMEDOUT); } val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) | SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) | SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) | SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A); if (mode->flags & DRM_MODE_FLAG_NHSYNC) val |= SOR_STATE2_ASY_HSYNCPOL_NEG; if (mode->flags & DRM_MODE_FLAG_NVSYNC) val |= SOR_STATE2_ASY_VSYNCPOL_NEG; WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val); WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL | SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE)); WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE); val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1); val |= SOR_STATE1_ATTACHED; WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val); WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); return 0; } static int hdmi_disable(struct hdmi_softc *sc) { struct tegra_crtc *crtc; device_t dc; uint32_t val; dc = NULL; if (sc->output.encoder.crtc != NULL) { crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, drm_crtc); dc = crtc->dev; } if (dc != NULL) { TEGRA_DC_HDMI_ENABLE(dc, false); TEGRA_DC_DISPLAY_ENABLE(dc, false); } audio_disable(sc); val = RD4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); val &= ~AVI_INFOFRAME_CTRL_ENABLE; WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val); /* Disable interrupts */ WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0); WR4(sc, HDMI_NV_PDISP_INT_MASK, 0); return (0); } static int hdmi_enable(struct hdmi_softc *sc) { uint64_t freq; struct drm_display_mode *mode; struct tegra_crtc *crtc; uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start; uint32_t h_max_ac_packet, div8_2; device_t dc; int i, rv; mode = &sc->output.encoder.crtc->mode; crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, drm_crtc); dc = crtc->dev; /* Compute all timings first. */ sc->pclk = mode->clock * 1000; h_sync_width = mode->hsync_end - mode->hsync_start; h_back_porch = mode->htotal - mode->hsync_end; h_front_porch = mode->hsync_start - mode->hdisplay; h_pulse_start = 1 + h_sync_width + h_back_porch - 10; h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch - HDMI_REKEY_DEFAULT - 18) / 32; /* Check if HDMI device is connected and detected. */ if (sc->output.connector.edid_blob_ptr == NULL) { sc->hdmi_mode = false; } else { sc->hdmi_mode = drm_detect_hdmi_monitor( (struct edid *)sc->output.connector.edid_blob_ptr->data); } /* Get exact HDMI pixel frequency. */ rv = clk_get_freq(sc->clk_hdmi, &freq); if (rv != 0) { device_printf(sc->dev, "Cannot get 'hdmi' clock frequency\n"); return (rv); } DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq); /* Wakeup SOR power */ val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); val &= ~SOR_PLL0_PDBG; WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); DELAY(10); val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); val &= ~SOR_PLL0_PWR; WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); /* Setup timings */ TEGRA_DC_SETUP_TIMING(dc, h_pulse_start); WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW, VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_ENABLE); /* Setup video source and adjust video range */ val = 0; if (crtc->nvidia_head != 0) HDMI_SRC_DISPLAYB; if ((mode->hdisplay != 640) || (mode->vdisplay != 480)) val |= ARM_VIDEO_RANGE_LIMITED; WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val); /* Program SOR reference clock - it uses 8.2 fractional divisor */ div8_2 = (freq * 4) / 1000000; val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2); WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val); /* Setup audio */ if (sc->hdmi_mode) { rv = audio_setup(sc); if (rv != 0) sc->hdmi_mode = false; } /* Init HDA ELD */ init_hda_eld(sc); val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT); val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet); if (sc->hdmi_mode) val |= HDMI_CTRL_ENABLE; WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val); /* Setup TMDS */ for (i = 0; i < sc->n_tmds_configs; i++) { if (sc->pclk <= sc->tmds_config[i].pclk) { tmds_init(sc, sc->tmds_config + i); break; } } /* Program sequencer. */ WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL, SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8)); val = SOR_SEQ_INST_WAIT_TIME(1) | SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) | SOR_SEQ_INST_HALT | SOR_SEQ_INST_DRIVE_PWM_OUT_LO; WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val); WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val); val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM); val &= ~SOR_CSTM_LVDS_ENABLE; val &= ~SOR_CSTM_ROTCLK(~0); val |= SOR_CSTM_ROTCLK(2); val &= ~SOR_CSTM_MODE(~0); val |= SOR_CSTM_MODE(CSTM_MODE_TMDS); val |= SOR_CSTM_PLLDIV; WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val); TEGRA_DC_DISPLAY_ENABLE(dc, false); rv = hdmi_sor_start(sc, mode); if (rv != 0) return (rv); TEGRA_DC_HDMI_ENABLE(dc, true); TEGRA_DC_DISPLAY_ENABLE(dc, true); /* Enable HDA codec interrupt */ WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0); WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0); if (sc->hdmi_mode) { avi_setup_infoframe(sc, mode); audio_enable(sc); } return (0); } /* ------------------------------------------------------------------- * * DRM Interface. * */ static enum drm_mode_status hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct tegra_drm_encoder *output; struct hdmi_softc *sc; int rv; uint64_t freq; output = container_of(connector, struct tegra_drm_encoder, connector); sc = device_get_softc(output->dev); freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000; rv = clk_test_freq(sc->clk_parent, freq, 0); DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv); if (rv != 0) return (MODE_NOCLOCK); return (MODE_OK); } static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { .get_modes = tegra_drm_connector_get_modes, .mode_valid = hdmi_connector_mode_valid, .best_encoder = tegra_drm_connector_best_encoder, }; static const struct drm_connector_funcs hdmi_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = tegra_drm_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, }; static const struct drm_encoder_funcs hdmi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; static void hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) { /* Empty function. */ } static bool hdmi_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted) { return (true); } static void hdmi_encoder_prepare(struct drm_encoder *encoder) { /* Empty function. */ } static void hdmi_encoder_commit(struct drm_encoder *encoder) { /* Empty function. */ } static void hdmi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted) { struct tegra_drm_encoder *output; struct hdmi_softc *sc; int rv; output = container_of(encoder, struct tegra_drm_encoder, encoder); sc = device_get_softc(output->dev); rv = hdmi_enable(sc); if (rv != 0) device_printf(sc->dev, "Cannot enable HDMI port\n"); } static void hdmi_encoder_disable(struct drm_encoder *encoder) { struct tegra_drm_encoder *output; struct hdmi_softc *sc; int rv; output = container_of(encoder, struct tegra_drm_encoder, encoder); sc = device_get_softc(output->dev); if (sc == NULL) return; rv = hdmi_disable(sc); if (rv != 0) device_printf(sc->dev, "Cannot disable HDMI port\n"); } static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = { .dpms = hdmi_encoder_dpms, .mode_fixup = hdmi_encoder_mode_fixup, .prepare = hdmi_encoder_prepare, .commit = hdmi_encoder_commit, .mode_set = hdmi_encoder_mode_set, .disable = hdmi_encoder_disable, }; /* ------------------------------------------------------------------- * * Bus and infrastructure. * */ static int hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm) { struct hdmi_softc *sc; phandle_t node; int rv; sc = device_get_softc(dev); node = ofw_bus_get_node(sc->dev); sc->drm = drm; sc->output.setup_clock = &hdmi_setup_clock; rv = tegra_drm_encoder_attach(&sc->output, node); if (rv != 0) { device_printf(dev, "Cannot attach output connector\n"); return(ENXIO); } /* Connect this encoder + connector to DRM. */ drm_connector_init(&drm->drm_dev, &sc->output.connector, &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); drm_connector_helper_add(&sc->output.connector, &hdmi_connector_helper_funcs); sc->output.connector.dpms = DRM_MODE_DPMS_OFF; drm_encoder_init(&drm->drm_dev, &sc->output.encoder, &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs); drm_mode_connector_attach_encoder(&sc->output.connector, &sc->output.encoder); rv = tegra_drm_encoder_init(&sc->output, drm); if (rv < 0) { device_printf(sc->dev, "Unable to init HDMI output\n"); return (rv); } sc->output.encoder.possible_crtcs = 0x3; return (0); } static int hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm) { struct hdmi_softc *sc; sc = device_get_softc(dev); tegra_drm_encoder_exit(&sc->output, drm); return (0); } static int get_fdt_resources(struct hdmi_softc *sc, phandle_t node) { int rv; rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply", &sc->supply_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot get 'hdmi' regulator\n"); return (ENXIO); } rv = regulator_get_by_ofw_property(sc->dev,0, "pll-supply", &sc->supply_pll); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pll' regulator\n"); return (ENXIO); } rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply", &sc->supply_vdd); if (rv != 0) { device_printf(sc->dev, "Cannot get 'vdd' regulator\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot get 'hdmi' reset\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent); if (rv != 0) { device_printf(sc->dev, "Cannot get 'parent' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot get 'hdmi' clock\n"); return (ENXIO); } return (0); } static int enable_fdt_resources(struct hdmi_softc *sc) { int rv; rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent); if (rv != 0) { device_printf(sc->dev, "Cannot set parent for 'hdmi' clock\n"); return (rv); } /* 594 MHz is arbitrarily selected value */ rv = clk_set_freq(sc->clk_parent, 594000000, 0); if (rv != 0) { device_printf(sc->dev, "Cannot set frequency for 'hdmi' parent clock\n"); return (rv); } rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0); if (rv != 0) { device_printf(sc->dev, "Cannot set frequency for 'hdmi' parent clock\n"); return (rv); } rv = regulator_enable(sc->supply_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'hdmi' regulator\n"); return (rv); } rv = regulator_enable(sc->supply_pll); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pll' regulator\n"); return (rv); } rv = regulator_enable(sc->supply_vdd); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'vdd' regulator\n"); return (rv); } rv = clk_enable(sc->clk_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_hdmi); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'hdmi' reset\n"); return (rv); } return (0); } static void hdmi_intr(void *arg) { struct hdmi_softc *sc; uint32_t status; sc = arg; /* Confirm interrupt */ status = RD4(sc, HDMI_NV_PDISP_INT_STATUS); WR4(sc, HDMI_NV_PDISP_INT_STATUS, status); /* process audio verb from HDA */ if (status & INT_CODEC_SCRATCH0) hda_intr(sc); } static int hdmi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Tegra HDMI"); return (BUS_PROBE_DEFAULT); } static int hdmi_attach(device_t dev) { struct hdmi_softc *sc; phandle_t node; int rid, rv; sc = device_get_softc(dev); sc->dev = dev; sc->output.dev = sc->dev; node = ofw_bus_get_node(sc->dev); sc->audio_src_type = SOURCE_SELECT_AUTO; sc->audio_freq = 44100; sc->audio_chans = 2; sc->hdmi_mode = false; sc->tmds_config = tegra124_tmds_config; sc->n_tmds_configs = nitems(tegra124_tmds_config); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, hdmi_intr, sc, &sc->irq_ih); if (rv != 0) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); goto fail; } rv = get_fdt_resources(sc, node); if (rv != 0) { device_printf(dev, "Cannot parse FDT resources\n"); goto fail; } rv = enable_fdt_resources(sc); if (rv != 0) { device_printf(dev, "Cannot enable FDT resources\n"); goto fail; } rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (rv != 0) { device_printf(dev, "Cannot register DRM device\n"); goto fail; } return (bus_generic_attach(dev)); fail: TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->clk_parent != NULL) clk_release(sc->clk_parent); if (sc->clk_hdmi != NULL) clk_release(sc->clk_hdmi); if (sc->hwreset_hdmi != NULL) hwreset_release(sc->hwreset_hdmi); if (sc->supply_hdmi != NULL) regulator_release(sc->supply_hdmi); if (sc->supply_pll != NULL) regulator_release(sc->supply_pll); if (sc->supply_vdd != NULL) regulator_release(sc->supply_vdd); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static int hdmi_detach(device_t dev) { struct hdmi_softc *sc; sc = device_get_softc(dev); TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->clk_parent != NULL) clk_release(sc->clk_parent); if (sc->clk_hdmi != NULL) clk_release(sc->clk_hdmi); if (sc->hwreset_hdmi != NULL) hwreset_release(sc->hwreset_hdmi); if (sc->supply_hdmi != NULL) regulator_release(sc->supply_hdmi); if (sc->supply_pll != NULL) regulator_release(sc->supply_pll); if (sc->supply_vdd != NULL) regulator_release(sc->supply_vdd); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (bus_generic_detach(dev)); } static device_method_t tegra_hdmi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hdmi_probe), DEVMETHOD(device_attach, hdmi_attach), DEVMETHOD(device_detach, hdmi_detach), /* tegra drm interface */ DEVMETHOD(tegra_drm_init_client, hdmi_init_client), DEVMETHOD(tegra_drm_exit_client, hdmi_exit_client), DEVMETHOD_END }; DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods, sizeof(struct hdmi_softc)); DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver, 0, 0); diff --git a/sys/arm/nvidia/drm2/tegra_host1x.c b/sys/arm/nvidia/drm2/tegra_host1x.c index 284c5c2e8465..4384ab0bc976 100644 --- a/sys/arm/nvidia/drm2/tegra_host1x.c +++ b/sys/arm/nvidia/drm2/tegra_host1x.c @@ -1,639 +1,639 @@ /*- * Copyright (c) 2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include "fb_if.h" #include "tegra_drm_if.h" #define WR4(_sc, _r, _v) bus_rite_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) #define LOCK(_sc) sx_xlock(&(_sc)->lock) #define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) #define SLEEP(_sc, timeout) sx_sleep(sc, &sc->lock, 0, "host1x", timeout); #define LOCK_INIT(_sc) sx_init(&_sc->lock, "host1x") #define LOCK_DESTROY(_sc) sx_destroy(&_sc->lock) #define ASSERT_LOCKED(_sc) sx_assert(&_sc->lock, SA_LOCKED) #define ASSERT_UNLOCKED(_sc) sx_assert(&_sc->lock, SA_UNLOCKED) static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-host1x", 1}, {NULL, 0} }; #define DRIVER_NAME "tegra" #define DRIVER_DESC "NVIDIA Tegra TK1" #define DRIVER_DATE "20151101" #define DRIVER_MAJOR 0 #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 0 struct client_info; TAILQ_HEAD(client_list, client_info); typedef struct client_list client_list_t; struct client_info { TAILQ_ENTRY(client_info) list_e; device_t client; int activated; }; struct host1x_softc { struct simplebus_softc simplebus_sc; /* must be first */ device_t dev; struct sx lock; int attach_done; struct resource *mem_res; struct resource *syncpt_irq_res; void *syncpt_irq_h; struct resource *gen_irq_res; void *gen_irq_h; clk_t clk; hwreset_t reset; struct intr_config_hook irq_hook; int drm_inited; client_list_t clients; struct tegra_drm *tegra_drm; }; static void host1x_output_poll_changed(struct drm_device *drm_dev) { struct tegra_drm *drm; drm = container_of(drm_dev, struct tegra_drm, drm_dev); if (drm->fb != NULL) drm_fb_helper_hotplug_event(&drm->fb->fb_helper); } static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = tegra_drm_fb_create, .output_poll_changed = host1x_output_poll_changed, }; static int host1x_drm_init(struct host1x_softc *sc) { struct client_info *entry; int rv; LOCK(sc); TAILQ_FOREACH(entry, &sc->clients, list_e) { if (entry->activated) continue; rv = TEGRA_DRM_INIT_CLIENT(entry->client, sc->dev, sc->tegra_drm); if (rv != 0) { device_printf(sc->dev, "Cannot init DRM client %s: %d\n", device_get_name(entry->client), rv); return (rv); } entry->activated = 1; } UNLOCK(sc); return (0); } static int host1x_drm_exit(struct host1x_softc *sc) { struct client_info *entry; int rv; #ifdef FREEBSD_NOTYET struct drm_device *dev, *tmp; #endif LOCK(sc); if (!sc->drm_inited) { UNLOCK(sc); return (0); } TAILQ_FOREACH_REVERSE(entry, &sc->clients, client_list, list_e) { if (!entry->activated) continue; rv = TEGRA_DRM_EXIT_CLIENT(entry->client, sc->dev, sc->tegra_drm); if (rv != 0) { device_printf(sc->dev, "Cannot exit DRM client %s: %d\n", device_get_name(entry->client), rv); } entry->activated = 0; } #ifdef FREEBSD_NOTYET list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) drm_put_dev(dev); #endif sc->drm_inited = 0; UNLOCK(sc); return (0); } static int host1x_drm_load(struct drm_device *drm_dev, unsigned long flags) { struct host1x_softc *sc; int rv; sc = device_get_softc(drm_dev->dev); drm_mode_config_init(drm_dev); drm_dev->mode_config.min_width = 32; drm_dev->mode_config.min_height = 32; drm_dev->mode_config.max_width = 4096; drm_dev->mode_config.max_height = 4096; drm_dev->mode_config.funcs = &mode_config_funcs; rv = host1x_drm_init(sc); if (rv != 0) goto fail_host1x; drm_dev->irq_enabled = true; drm_dev->max_vblank_count = 0xffffffff; drm_dev->vblank_disable_allowed = true; rv = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); if (rv != 0) goto fail_vblank; drm_mode_config_reset(drm_dev); rv = tegra_drm_fb_init(drm_dev); if (rv != 0) goto fail_fb; drm_kms_helper_poll_init(drm_dev); return (0); fail_fb: tegra_drm_fb_destroy(drm_dev); drm_vblank_cleanup(drm_dev); fail_vblank: host1x_drm_exit(sc); fail_host1x: drm_mode_config_cleanup(drm_dev); return (rv); } static int host1x_drm_unload(struct drm_device *drm_dev) { struct host1x_softc *sc; int rv; sc = device_get_softc(drm_dev->dev); drm_kms_helper_poll_fini(drm_dev); tegra_drm_fb_destroy(drm_dev); drm_mode_config_cleanup(drm_dev); rv = host1x_drm_exit(sc); if (rv < 0) return (rv); return (0); } static int host1x_drm_open(struct drm_device *drm_dev, struct drm_file *filp) { return (0); } static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) { struct drm_crtc *crtc; list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) tegra_dc_cancel_page_flip(crtc, file); } static void host1x_drm_lastclose(struct drm_device *drm_dev) { struct tegra_drm *drm; drm = container_of(drm_dev, struct tegra_drm, drm_dev); if (drm->fb != NULL) drm_fb_helper_restore_fbdev_mode(&drm->fb->fb_helper); } static int host1x_drm_enable_vblank(struct drm_device *drm_dev, int pipe) { struct drm_crtc *crtc; list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { if (pipe == tegra_dc_get_pipe(crtc)) { tegra_dc_enable_vblank(crtc); return (0); } } return (-ENODEV); } static void host1x_drm_disable_vblank(struct drm_device *drm_dev, int pipe) { struct drm_crtc *crtc; list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { if (pipe == tegra_dc_get_pipe(crtc)) { tegra_dc_disable_vblank(crtc); return; } } } static struct drm_ioctl_desc host1x_drm_ioctls[] = { }; struct drm_driver tegra_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = host1x_drm_load, .unload = host1x_drm_unload, .open = host1x_drm_open, .preclose = tegra_drm_preclose, .lastclose = host1x_drm_lastclose, .get_vblank_counter = drm_vblank_count, .enable_vblank = host1x_drm_enable_vblank, .disable_vblank = host1x_drm_disable_vblank, /* Fields filled by tegra_bo_driver_register() .gem_free_object .gem_pager_ops .dumb_create .dumb_map_offset .dumb_destroy */ .ioctls = host1x_drm_ioctls, .num_ioctls = nitems(host1x_drm_ioctls), .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; /* * ----------------- Device methods ------------------------- */ static void host1x_irq_hook(void *arg) { struct host1x_softc *sc; int rv; sc = arg; config_intrhook_disestablish(&sc->irq_hook); tegra_bo_driver_register(&tegra_drm_driver); rv = drm_get_platform_dev(sc->dev, &sc->tegra_drm->drm_dev, &tegra_drm_driver); if (rv != 0) { device_printf(sc->dev, "drm_get_platform_dev(): %d\n", rv); return; } sc->drm_inited = 1; } static struct fb_info * host1x_fb_helper_getinfo(device_t dev) { struct host1x_softc *sc; sc = device_get_softc(dev); if (sc->tegra_drm == NULL) return (NULL); return (tegra_drm_fb_getinfo(&sc->tegra_drm->drm_dev)); } static int host1x_register_client(device_t dev, device_t client) { struct host1x_softc *sc; struct client_info *entry; sc = device_get_softc(dev); entry = malloc(sizeof(struct client_info), M_DEVBUF, M_WAITOK | M_ZERO); entry->client = client; entry->activated = 0; LOCK(sc); TAILQ_INSERT_TAIL(&sc->clients, entry, list_e); UNLOCK(sc); return (0); } static int host1x_deregister_client(device_t dev, device_t client) { struct host1x_softc *sc; struct client_info *entry; sc = device_get_softc(dev); LOCK(sc); TAILQ_FOREACH(entry, &sc->clients, list_e) { if (entry->client == client) { if (entry->activated) panic("Tegra DRM: Attempt to deregister " "activated client"); TAILQ_REMOVE(&sc->clients, entry, list_e); free(entry, M_DEVBUF); UNLOCK(sc); return (0); } } UNLOCK(sc); return (0); } static void host1x_gen_intr(void *arg) { struct host1x_softc *sc; sc = (struct host1x_softc *)arg; LOCK(sc); UNLOCK(sc); } static void host1x_syncpt_intr(void *arg) { struct host1x_softc *sc; sc = (struct host1x_softc *)arg; LOCK(sc); UNLOCK(sc); } static void host1x_new_pass(device_t dev) { struct host1x_softc *sc; int rv, rid; phandle_t node; /* * We attach during BUS_PASS_BUS (because we must overcome simplebus), * but some of our FDT resources are not ready until BUS_PASS_DEFAULT */ sc = device_get_softc(dev); if (sc->attach_done || bus_current_pass < BUS_PASS_DEFAULT) { bus_generic_new_pass(dev); return; } sc->attach_done = 1; node = ofw_bus_get_node(dev); /* Allocate our IRQ resource. */ rid = 0; sc->syncpt_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->syncpt_irq_res == NULL) { device_printf(dev, "Cannot allocate interrupt.\n"); rv = ENXIO; goto fail; } rid = 1; sc->gen_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->gen_irq_res == NULL) { device_printf(dev, "Cannot allocate interrupt.\n"); rv = ENXIO; goto fail; } /* FDT resources */ rv = hwreset_get_by_ofw_name(sc->dev, 0, "host1x", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get fuse reset\n"); goto fail; } rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get i2c clock: %d\n", rv); goto fail; } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock: %d\n", rv); goto fail; } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot clear reset\n"); goto fail; } /* Setup interrupts */ rv = bus_setup_intr(dev, sc->gen_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_gen_intr, sc, &sc->gen_irq_h); if (rv) { device_printf(dev, "Cannot setup gen interrupt.\n"); goto fail; } rv = bus_setup_intr(dev, sc->syncpt_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_syncpt_intr, sc, &sc->syncpt_irq_h); if (rv) { device_printf(dev, "Cannot setup syncpt interrupt.\n"); goto fail; } simplebus_init(dev, 0); for (node = OF_child(node); node > 0; node = OF_peer(node)) simplebus_add_device(dev, node, 0, NULL, -1, NULL); sc->irq_hook.ich_func = host1x_irq_hook; sc->irq_hook.ich_arg = sc; config_intrhook_establish(&sc->irq_hook); bus_generic_new_pass(dev); return; fail: device_detach(dev); return; } static int host1x_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int host1x_attach(device_t dev) { int rv, rid; struct host1x_softc *sc; sc = device_get_softc(dev); sc->tegra_drm = malloc(sizeof(struct tegra_drm), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); /* crosslink together all worlds */ sc->dev = dev; sc->tegra_drm->drm_dev.dev_private = &sc->tegra_drm; sc->tegra_drm->drm_dev.dev = dev; TAILQ_INIT(&sc->clients); LOCK_INIT(sc); /* Get the memory resource for the register mapping. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot map registers.\n"); rv = ENXIO; goto fail; } return (bus_generic_attach(dev)); fail: if (sc->tegra_drm != NULL) free(sc->tegra_drm, DRM_MEM_DRIVER); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); return (rv); } static int host1x_detach(device_t dev) { struct host1x_softc *sc; sc = device_get_softc(dev); host1x_drm_exit(sc); if (sc->gen_irq_h != NULL) bus_teardown_intr(dev, sc->gen_irq_res, sc->gen_irq_h); if (sc->tegra_drm != NULL) free(sc->tegra_drm, DRM_MEM_DRIVER); if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->syncpt_irq_h != NULL) bus_teardown_intr(dev, sc->syncpt_irq_res, sc->syncpt_irq_h); if (sc->gen_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 1, sc->gen_irq_res); if (sc->syncpt_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->syncpt_irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); return (bus_generic_detach(dev)); } static device_method_t host1x_methods[] = { /* Device interface */ DEVMETHOD(device_probe, host1x_probe), DEVMETHOD(device_attach, host1x_attach), DEVMETHOD(device_detach, host1x_detach), /* Bus interface */ DEVMETHOD(bus_new_pass, host1x_new_pass), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, host1x_fb_helper_getinfo), /* tegra drm interface */ DEVMETHOD(tegra_drm_register_client, host1x_register_client), DEVMETHOD(tegra_drm_deregister_client, host1x_deregister_client), DEVMETHOD_END }; DEFINE_CLASS_1(host1x, host1x_driver, host1x_methods, sizeof(struct host1x_softc), simplebus_driver); EARLY_DRIVER_MODULE(host1x, simplebus, host1x_driver, 0, 0, BUS_PASS_BUS); /* Bindings for fbd device. */ extern driver_t fbd_driver; DRIVER_MODULE(fbd, host1x, fbd_driver, 0, 0); diff --git a/sys/arm/nvidia/tegra124/tegra124_car.c b/sys/arm/nvidia/tegra124/tegra124_car.c index 440f5a5b1044..57d7760494a6 100644 --- a/sys/arm/nvidia/tegra124/tegra124_car.c +++ b/sys/arm/nvidia/tegra124/tegra124_car.c @@ -1,599 +1,599 @@ /*- * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include "clkdev_if.h" #include "hwreset_if.h" #include "tegra124_car.h" static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-car", 1}, {NULL, 0}, }; #define PLIST(x) static const char *x[] /* Pure multiplexer. */ #define MUX(_id, cname, plists, o, s, w) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = plists, \ .clkdef.parent_cnt = nitems(plists), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .width = w, \ } /* Fractional divider (7.1). */ #define DIV7_1(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .i_shift = (s) + 1, \ .i_width = 7, \ .f_shift = s, \ .f_width = 1, \ } /* Integer divider. */ #define DIV(_id, cname, plist, o, s, w, f) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .i_shift = s, \ .i_width = w, \ .div_flags = f, \ } /* Gate in PLL block. */ #define GATE_PLL(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 3, \ .on_value = 3, \ .off_value = 0, \ } /* Standard gate. */ #define GATE(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 1, \ .on_value = 1, \ .off_value = 0, \ } /* Inverted gate. */ #define GATE_INV(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 1, \ .on_value = 0, \ .off_value = 1, \ } /* Fixed rate clock. */ #define FRATE(_id, cname, _freq) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = NULL, \ .clkdef.parent_cnt = 0, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .freq = _freq, \ } /* Fixed rate multipier/divider. */ #define FACT(_id, cname, pname, _mult, _div) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .mult = _mult, \ .div = _div, \ } static uint32_t osc_freqs[16] = { [0] = 13000000, [1] = 16800000, [4] = 19200000, [5] = 38400000, [8] = 12000000, [9] = 48000000, [12] = 260000000, }; /* Parent lists. */ PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */ PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"}; PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"}; PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"}; PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60"}; PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"}; /* Clocks adjusted online. */ static struct clk_fixed_def fixed_clk_m = FRATE(TEGRA124_CLK_CLK_M, "clk_m", 12000000); static struct clk_fixed_def fixed_osc_div_clk = FACT(0, "osc_div_clk", "clk_m", 1, 1); static struct clk_fixed_def tegra124_fixed_clks[] = { /* Core clocks. */ FRATE(0, "clk_s", 32768), FACT(0, "clk_m_div2", "clk_m", 1, 2), FACT(0, "clk_m_div4", "clk_m", 1, 3), FACT(0, "pllU_60", "pllU_out", 1, 8), FACT(0, "pllU_48", "pllU_out", 1, 10), FACT(0, "pllU_12", "pllU_out", 1, 40), FACT(TEGRA124_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2), FACT(TEGRA124_CLK_PLL_D2_OUT0, "pllD2_out0", "pllD2_out", 1, 1), FACT(0, "pllX_out0", "pllX_out", 1, 2), FACT(0, "pllC_UD", "pllC_out0", 1, 1), FACT(0, "pllM_UD", "pllM_out0", 1, 1), /* Audio clocks. */ FRATE(0, "audio0", 10000000), FRATE(0, "audio1", 10000000), FRATE(0, "audio2", 10000000), FRATE(0, "audio3", 10000000), FRATE(0, "audio4", 10000000), FRATE(0, "ext_vimclk", 10000000), /* XUSB */ FACT(TEGRA124_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2), }; static struct clk_mux_def tegra124_mux_clks[] = { /* Core clocks. */ MUX(0, "pllD2_src", mux_pll_srcs, PLLD2_BASE, 25, 2), MUX(0, "pllDP_src", mux_pll_srcs, PLLDP_BASE, 25, 2), MUX(0, "pllC4_src", mux_pll_srcs, PLLC4_BASE, 25, 2), MUX(0, "pllE_src1", mux_plle_src1, PLLE_AUX, 2, 1), MUX(0, "pllE_src", mux_plle_src, PLLE_AUX, 28, 1), /* Base peripheral clocks. */ MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1), MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1), /* USB. */ MUX(TEGRA124_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1), MUX(0, "xusb_ss_mux", mux_xusb_ss, CLK_SOURCE_XUSB_SS, 24, 1), }; static struct clk_gate_def tegra124_gate_clks[] = { /* Core clocks. */ GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0), GATE_PLL(0, "pllM_out1", "pllM_out1_div", PLLM_OUT, 0), GATE_PLL(TEGRA124_CLK_PLL_U_480M, "pllU_480", "pllU_out", PLLU_BASE, 22), GATE_PLL(0, "pllP_outX0", "pllP_outX0_div", PLLP_RESHIFT, 0), GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0), GATE_PLL(0, "pllP_out2", "pllP_out2_div", PLLP_OUTA, 16), GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0), GATE_PLL(0, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16), GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16), GATE_PLL(0, "pllA_out0", "pllA_out1_div", PLLA_OUT, 0), /* Base peripheral clocks. */ GATE(TEGRA124_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0), GATE(TEGRA124_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1), GATE_INV(TEGRA124_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7), GATE_INV(TEGRA124_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3), }; static struct clk_div_def tegra124_div_clks[] = { /* Core clocks. */ DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 2), DIV7_1(0, "pllM_out1_div", "pllM_out0", PLLM_OUT, 8), DIV7_1(0, "pllP_outX0_div", "pllP_out0", PLLP_RESHIFT, 2), DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8), DIV7_1(0, "pllP_out2_div", "pllP_out0", PLLP_OUTA, 24), DIV7_1(0, "pllP_out3_div", "pllP_out0", PLLP_OUTB, 8), DIV7_1(0, "pllP_out4_div", "pllP_out0", PLLP_OUTB, 24), DIV7_1(0, "pllP_out5_div", "pllP_out0", PLLP_OUTC, 24), DIV7_1(0, "pllA_out1_div", "pllA_out", PLLA_OUT, 8), /* Base peripheral clocks. */ DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0), DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0), }; /* Initial setup table. */ static struct tegra124_init_item clk_init_table[] = { /* clock, partent, frequency, enable */ {"uarta", "pllP_out0", 408000000, 0}, {"uartb", "pllP_out0", 408000000, 0}, {"uartc", "pllP_out0", 408000000, 0}, {"uartd", "pllP_out0", 408000000, 0}, {"pllA_out", NULL, 282240000, 1}, {"pllA_out0", NULL, 11289600, 1}, {"extperiph1", "pllA_out0", 0, 1}, {"i2s0", "pllA_out0", 11289600, 0}, {"i2s1", "pllA_out0", 11289600, 0}, {"i2s2", "pllA_out0", 11289600, 0}, {"i2s3", "pllA_out0", 11289600, 0}, {"i2s4", "pllA_out0", 11289600, 0}, {"vde", "pllP_out0", 0, 0}, {"host1x", "pllP_out0", 136000000, 1}, {"sclk", "pllP_out2", 102000000, 1}, {"dvfs_soc", "pllP_out0", 51000000, 1}, {"dvfs_ref", "pllP_out0", 51000000, 1}, {"pllC_out0", NULL, 600000000, 0}, {"pllC_out1", NULL, 100000000, 0}, {"spi4", "pllP_out0", 12000000, 1}, {"tsec", "pllC3_out0", 0, 0}, {"msenc", "pllC3_out0", 0, 0}, {"pllREFE_out", NULL, 672000000, 0}, {"pc_xusb_ss", "pllU_480", 120000000, 0}, {"xusb_ss", "pc_xusb_ss", 120000000, 0}, {"pc_xusb_fs", "pllU_48", 48000000, 0}, {"xusb_hs", "pllU_60", 60000000, 0}, {"pc_xusb_falcon", "pllREFE_out", 224000000, 0}, {"xusb_core_host", "pllREFE_out", 112000000, 0}, {"sata", "pllP_out0", 102000000, 0}, {"sata_oob", "pllP_out0", 204000000, 0}, {"sata_cold", NULL, 0, 1}, {"emc", NULL, 0, 1}, {"mselect", NULL, 0, 1}, {"csite", NULL, 0, 1}, {"tsensor", "clk_m", 400000, 0}, /* tegra124 only*/ {"soc_therm", "pllP_out0", 51000000, 0}, {"cclk_g", NULL, 0, 1}, {"hda", "pllP_out0", 102000000, 0}, {"hda2codec_2x", "pllP_out0", 48000000, 0}, }; static void init_divs(struct tegra124_car_softc *sc, struct clk_div_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_div_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_div_register failed"); } } static void init_gates(struct tegra124_car_softc *sc, struct clk_gate_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_gate_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_gate_register failed"); } } static void init_muxes(struct tegra124_car_softc *sc, struct clk_mux_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_mux_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_mux_register failed"); } } static void init_fixeds(struct tegra124_car_softc *sc, struct clk_fixed_def *clks, int nclks) { int i, rv; uint32_t val; int osc_idx; CLKDEV_READ_4(sc->dev, OSC_CTRL, &val); osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; fixed_clk_m.freq = osc_freqs[osc_idx]; if (fixed_clk_m.freq == 0) panic("Undefined input frequency"); rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m); if (rv != 0) panic("clk_fixed_register failed"); val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; fixed_osc_div_clk.div = 1 << val; rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div_clk); if (rv != 0) panic("clk_fixed_register failed"); for (i = 0; i < nclks; i++) { rv = clknode_fixed_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_fixed_register failed"); } } static void postinit_clock(struct tegra124_car_softc *sc) { int i; struct tegra124_init_item *tbl; struct clknode *clknode; int rv; for (i = 0; i < nitems(clk_init_table); i++) { tbl = &clk_init_table[i]; clknode = clknode_find_by_name(tbl->name); if (clknode == NULL) { device_printf(sc->dev, "Cannot find clock %s\n", tbl->name); continue; } if (tbl->parent != NULL) { rv = clknode_set_parent_by_name(clknode, tbl->parent); if (rv != 0) { device_printf(sc->dev, "Cannot set parent for %s (to %s): %d\n", tbl->name, tbl->parent, rv); continue; } } if (tbl->frequency != 0) { rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999); if (rv != 0) { device_printf(sc->dev, "Cannot set frequency for %s: %d\n", tbl->name, rv); continue; } } if (tbl->enable!= 0) { rv = clknode_enable(clknode); if (rv != 0) { device_printf(sc->dev, "Cannot enable %s: %d\n", tbl->name, rv); continue; } } } } static void register_clocks(device_t dev) { struct tegra124_car_softc *sc; sc = device_get_softc(dev); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("clkdom == NULL"); tegra124_init_plls(sc); init_fixeds(sc, tegra124_fixed_clks, nitems(tegra124_fixed_clks)); init_muxes(sc, tegra124_mux_clks, nitems(tegra124_mux_clks)); init_divs(sc, tegra124_div_clks, nitems(tegra124_div_clks)); init_gates(sc, tegra124_gate_clks, nitems(tegra124_gate_clks)); tegra124_periph_clock(sc); tegra124_super_mux_clock(sc); clkdom_finit(sc->clkdom); clkdom_xlock(sc->clkdom); postinit_clock(sc); clkdom_unlock(sc->clkdom); if (bootverbose) clkdom_dump(sc->clkdom); } static int tegra124_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct tegra124_car_softc *sc; sc = device_get_softc(dev); *val = bus_read_4(sc->mem_res, addr); return (0); } static int tegra124_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct tegra124_car_softc *sc; sc = device_get_softc(dev); bus_write_4(sc->mem_res, addr, val); return (0); } static int tegra124_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, uint32_t set_mask) { struct tegra124_car_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = bus_read_4(sc->mem_res, addr); reg &= ~clear_mask; reg |= set_mask; bus_write_4(sc->mem_res, addr, reg); return (0); } static void tegra124_car_clkdev_device_lock(device_t dev) { struct tegra124_car_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void tegra124_car_clkdev_device_unlock(device_t dev) { struct tegra124_car_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int tegra124_car_detach(device_t dev) { device_printf(dev, "Error: Clock driver cannot be detached\n"); return (EBUSY); } static int tegra124_car_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Tegra Clock Driver"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int tegra124_car_attach(device_t dev) { struct tegra124_car_softc *sc = device_get_softc(dev); int rid, rv; sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Resource setup. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "cannot allocate memory resource\n"); rv = ENXIO; goto fail; } register_clocks(dev); hwreset_register_ofw_provider(dev); return (0); fail: if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (rv); } static int tegra124_car_hwreset_assert(device_t dev, intptr_t id, bool value) { struct tegra124_car_softc *sc = device_get_softc(dev); return (tegra124_hwreset_by_idx(sc, id, value)); } static device_method_t tegra124_car_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra124_car_probe), DEVMETHOD(device_attach, tegra124_car_attach), DEVMETHOD(device_detach, tegra124_car_detach), /* Clkdev interface*/ DEVMETHOD(clkdev_read_4, tegra124_car_clkdev_read_4), DEVMETHOD(clkdev_write_4, tegra124_car_clkdev_write_4), DEVMETHOD(clkdev_modify_4, tegra124_car_clkdev_modify_4), DEVMETHOD(clkdev_device_lock, tegra124_car_clkdev_device_lock), DEVMETHOD(clkdev_device_unlock, tegra124_car_clkdev_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, tegra124_car_hwreset_assert), DEVMETHOD_END }; static DEFINE_CLASS_0(car, tegra124_car_driver, tegra124_car_methods, sizeof(struct tegra124_car_softc)); EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver, NULL, NULL, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra124/tegra124_pmc.c b/sys/arm/nvidia/tegra124/tegra124_pmc.c index 6b42659d453a..83bd0a1e6983 100644 --- a/sys/arm/nvidia/tegra124/tegra124_pmc.c +++ b/sys/arm/nvidia/tegra124/tegra124_pmc.c @@ -1,559 +1,559 @@ /*- * 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 #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #define PMC_CNTRL 0x000 #define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20) #define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 20 #define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19) #define PMC_CNTRL_FUSE_OVERRIDE (1 << 18) #define PMC_CNTRL_INTR_POLARITY (1 << 17) #define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) #define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) #define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) #define PMC_CNTRL_AOINIT (1 << 13) #define PMC_CNTRL_PWRGATE_DIS (1 << 12) #define PMC_CNTRL_SYSCLK_OE (1 << 11) #define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) #define PMC_CNTRL_PWRREQ_OE (1 << 9) #define PMC_CNTRL_PWRREQ_POLARITY (1 << 8) #define PMC_CNTRL_BLINK_EN (1 << 7) #define PMC_CNTRL_GLITCHDET_DIS (1 << 6) #define PMC_CNTRL_LATCHWAKE_EN (1 << 5) #define PMC_CNTRL_MAIN_RST (1 << 4) #define PMC_CNTRL_KBC_RST (1 << 3) #define PMC_CNTRL_RTC_RST (1 << 2) #define PMC_CNTRL_RTC_CLK_DIS (1 << 1) #define PMC_CNTRL_KBC_CLK_DIS (1 << 0) #define PMC_DPD_SAMPLE 0x020 #define PMC_CLAMP_STATUS 0x02C #define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_PWRGATE_TOGGLE 0x030 #define PMC_PWRGATE_TOGGLE_START (1 << 8) #define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0) #define PMC_REMOVE_CLAMPING_CMD 0x034 #define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_PWRGATE_STATUS 0x038 #define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_SCRATCH0 0x050 #define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) #define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) #define PMC_SCRATCH0_MODE_RCM (1 << 1) #define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ PMC_SCRATCH0_MODE_BOOTLOADER | \ PMC_SCRATCH0_MODE_RCM) #define PMC_CPUPWRGOOD_TIMER 0x0c8 #define PMC_CPUPWROFF_TIMER 0x0cc #define PMC_SCRATCH41 0x140 #define PMC_SENSOR_CTRL 0x1b0 #define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2) #define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) #define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0) #define PMC_IO_DPD_REQ 0x1b8 #define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30) #define PMC_IO_DPD_REQ_CODE_OFF (1 << 30) #define PMC_IO_DPD_REQ_CODE_ON (2 << 30) #define PMC_IO_DPD_REQ_CODE_MASK (3 << 30) #define PMC_IO_DPD_STATUS 0x1bc #define PMC_IO_DPD_STATUS_HDMI (1 << 28) #define PMC_IO_DPD2_REQ 0x1c0 #define PMC_IO_DPD2_STATUS 0x1c4 #define PMC_IO_DPD2_STATUS_HV (1 << 6) #define PMC_SEL_DPD_TIM 0x1c8 #define PMC_SCRATCH54 0x258 #define PMC_SCRATCH54_DATA_SHIFT 8 #define PMC_SCRATCH54_ADDR_SHIFT 0 #define PMC_SCRATCH55 0x25c #define PMC_SCRATCH55_RST_ENABLE (1 << 31) #define PMC_SCRATCH55_CNTRL_TYPE (1 << 30) #define PMC_SCRATCH55_CNTRL_ID_SHIFT 27 #define PMC_SCRATCH55_CNTRL_ID_MASK 0x07 #define PMC_SCRATCH55_PINMUX_SHIFT 24 #define PMC_SCRATCH55_PINMUX_MASK 0x07 #define PMC_SCRATCH55_CHECKSUM_SHIFT 16 #define PMC_SCRATCH55_CHECKSUM_MASK 0xFF #define PMC_SCRATCH55_16BITOP (1 << 15) #define PMC_SCRATCH55_I2CSLV1_SHIFT 0 #define PMC_SCRATCH55_I2CSLV1_MASK 0x7F #define PMC_GPU_RG_CNTRL 0x2d4 #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) #define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx) #define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ device_get_nameunit(_sc->dev), "tegra124_pmc", MTX_DEF) #define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); #define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); #define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); struct tegra124_pmc_softc { device_t dev; struct resource *mem_res; clk_t clk; struct mtx mtx; uint32_t rate; enum tegra_suspend_mode suspend_mode; uint32_t cpu_good_time; uint32_t cpu_off_time; uint32_t core_osc_time; uint32_t core_pmu_time; uint32_t core_off_time; int corereq_high; int sysclkreq_high; int combined_req; int cpu_pwr_good_en; uint32_t lp0_vec_phys; uint32_t lp0_vec_size; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-pmc", 1}, {NULL, 0}, }; static struct tegra124_pmc_softc *pmc_sc; static inline struct tegra124_pmc_softc * tegra124_pmc_get_sc(void) { if (pmc_sc == NULL) panic("To early call to Tegra PMC driver.\n"); return (pmc_sc); } static int tegra124_pmc_set_powergate(struct tegra124_pmc_softc *sc, enum tegra_powergate_id id, int ena) { uint32_t reg; int i; PMC_LOCK(sc); reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id); if (((reg != 0) && ena) || ((reg == 0) && !ena)) { PMC_UNLOCK(sc); return (0); } for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_PWRGATE_TOGGLE); if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting for TOGGLE_START\n"); WR4(sc, PMC_PWRGATE_TOGGLE, PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id)); for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_PWRGATE_TOGGLE); if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting for TOGGLE_START\n"); PMC_UNLOCK(sc); return (0); } int tegra_powergate_remove_clamping(enum tegra_powergate_id id) { struct tegra124_pmc_softc *sc; uint32_t reg; enum tegra_powergate_id swid; int i; sc = tegra124_pmc_get_sc(); if (id == TEGRA_POWERGATE_3D) { WR4(sc, PMC_GPU_RG_CNTRL, 0); return (0); } reg = RD4(sc, PMC_PWRGATE_STATUS); if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0) panic("Attempt to remove clamping for unpowered partition.\n"); if (id == TEGRA_POWERGATE_PCX) swid = TEGRA_POWERGATE_VDE; else if (id == TEGRA_POWERGATE_VDE) swid = TEGRA_POWERGATE_PCX; else swid = id; WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid)); for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD); if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when remove clamping\n"); reg = RD4(sc, PMC_CLAMP_STATUS); if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0) panic("Cannot remove clamping\n"); return (0); } int tegra_powergate_is_powered(enum tegra_powergate_id id) { struct tegra124_pmc_softc *sc; uint32_t reg; sc = tegra124_pmc_get_sc(); reg = RD4(sc, PMC_PWRGATE_STATUS); return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0); } int tegra_powergate_power_on(enum tegra_powergate_id id) { struct tegra124_pmc_softc *sc; int rv, i; sc = tegra124_pmc_get_sc(); rv = tegra124_pmc_set_powergate(sc, id, 1); if (rv != 0) { device_printf(sc->dev, "Cannot set powergate: %d\n", id); return (rv); } for (i = 100; i > 0; i--) { if (tegra_powergate_is_powered(id)) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting on power up\n"); return (rv); } int tegra_powergate_power_off(enum tegra_powergate_id id) { struct tegra124_pmc_softc *sc; int rv, i; sc = tegra124_pmc_get_sc(); rv = tegra124_pmc_set_powergate(sc, id, 0); if (rv != 0) { device_printf(sc->dev, "Cannot set powergate: %d\n", id); return (rv); } for (i = 100; i > 0; i--) { if (!tegra_powergate_is_powered(id)) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting on power off\n"); return (rv); } int tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk, hwreset_t rst) { struct tegra124_pmc_softc *sc; int rv; sc = tegra124_pmc_get_sc(); rv = hwreset_assert(rst); if (rv != 0) { device_printf(sc->dev, "Cannot assert reset\n"); return (rv); } rv = clk_stop(clk); if (rv != 0) { device_printf(sc->dev, "Cannot stop clock\n"); goto clk_fail; } rv = tegra_powergate_power_on(id); if (rv != 0) { device_printf(sc->dev, "Cannot power on powergate\n"); goto clk_fail; } rv = clk_enable(clk); if (rv != 0) { device_printf(sc->dev, "Cannot enable clock\n"); goto clk_fail; } DELAY(20); rv = tegra_powergate_remove_clamping(id); if (rv != 0) { device_printf(sc->dev, "Cannot remove clamping\n"); goto fail; } rv = hwreset_deassert(rst); if (rv != 0) { device_printf(sc->dev, "Cannot unreset reset\n"); goto fail; } return 0; fail: clk_disable(clk); clk_fail: hwreset_assert(rst); tegra_powergate_power_off(id); return (rv); } static int tegra124_pmc_parse_fdt(struct tegra124_pmc_softc *sc, phandle_t node) { int rv; uint32_t tmp; uint32_t tmparr[2]; rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp)); if (rv > 0) { switch (tmp) { case 0: sc->suspend_mode = TEGRA_SUSPEND_LP0; break; case 1: sc->suspend_mode = TEGRA_SUSPEND_LP1; break; case 2: sc->suspend_mode = TEGRA_SUSPEND_LP2; break; default: sc->suspend_mode = TEGRA_SUSPEND_NONE; break; } } rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->cpu_good_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->cpu_off_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr, sizeof(tmparr)); if (rv == sizeof(tmparr)) { sc->core_osc_time = tmparr[0]; sc->core_pmu_time = tmparr[1]; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->core_off_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } sc->corereq_high = OF_hasprop(node, "nvidia,core-power-req-active-high"); sc->sysclkreq_high = OF_hasprop(node, "nvidia,sys-clock-req-active-high"); sc->combined_req = OF_hasprop(node, "nvidia,combined-power-req"); sc->cpu_pwr_good_en = OF_hasprop(node, "nvidia,cpu-pwr-good-en"); rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr)); if (rv == sizeof(tmparr)) { sc->lp0_vec_phys = tmparr[0]; sc->core_pmu_time = tmparr[1]; sc->lp0_vec_size = TEGRA_SUSPEND_NONE; if (sc->suspend_mode == TEGRA_SUSPEND_LP0) sc->suspend_mode = TEGRA_SUSPEND_LP1; } return 0; } static int tegra124_pmc_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(dev, "Tegra PMC"); return (BUS_PROBE_DEFAULT); } static int tegra124_pmc_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int tegra124_pmc_attach(device_t dev) { struct tegra124_pmc_softc *sc; int rid, rv; uint32_t reg; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rv = tegra124_pmc_parse_fdt(sc, node); if (rv != 0) { device_printf(sc->dev, "Cannot parse FDT data\n"); return (rv); } rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk); if (rv != 0) { device_printf(sc->dev, "Cannot get \"pclk\" clock\n"); return (ENXIO); } rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } PMC_LOCK_INIT(sc); /* Enable CPU power request. */ reg = RD4(sc, PMC_CNTRL); reg |= PMC_CNTRL_CPU_PWRREQ_OE; WR4(sc, PMC_CNTRL, reg); /* Set sysclk output polarity */ reg = RD4(sc, PMC_CNTRL); if (sc->sysclkreq_high) reg &= ~PMC_CNTRL_SYSCLK_POLARITY; else reg |= PMC_CNTRL_SYSCLK_POLARITY; WR4(sc, PMC_CNTRL, reg); /* Enable sysclk request. */ reg = RD4(sc, PMC_CNTRL); reg |= PMC_CNTRL_SYSCLK_OE; WR4(sc, PMC_CNTRL, reg); /* * Remove HDMI from deep power down mode. * XXX mote this to HDMI driver */ reg = RD4(sc, PMC_IO_DPD_STATUS); reg &= ~ PMC_IO_DPD_STATUS_HDMI; WR4(sc, PMC_IO_DPD_STATUS, reg); reg = RD4(sc, PMC_IO_DPD2_STATUS); reg &= ~ PMC_IO_DPD2_STATUS_HV; WR4(sc, PMC_IO_DPD2_STATUS, reg); if (pmc_sc != NULL) panic("tegra124_pmc: double driver attach"); pmc_sc = sc; return (0); } static device_method_t tegra124_pmc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra124_pmc_probe), DEVMETHOD(device_attach, tegra124_pmc_attach), DEVMETHOD(device_detach, tegra124_pmc_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(pmc, tegra124_pmc_driver, tegra124_pmc_methods, sizeof(struct tegra124_pmc_softc)); EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver, NULL, NULL, 70); diff --git a/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c index eb749a2ffbc9..0b611abc39cb 100644 --- a/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c +++ b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c @@ -1,1202 +1,1202 @@ /*- * 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 #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "phydev_if.h" /* FUSE calibration data. */ #define FUSE_XUSB_CALIB 0x0F0 #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F); #define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03); #define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03); #define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F); #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F); /* Registers. */ #define XUSB_PADCTL_USB2_PAD_MUX 0x004 #define XUSB_PADCTL_USB2_PORT_CAP 0x008 #define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25) #define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24) #define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4)) #define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4)) #define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4)) #define USB2_PORT_CAP_PORT_CAP_OTG 0x3 #define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2 #define USB2_PORT_CAP_PORT_CAP_HOST 0x1 #define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0 #define XUSB_PADCTL_SS_PORT_MAP 0x014 #define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4)) #define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4)) #define XUSB_PADCTL_ELPG_PROGRAM 0x01C #define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26) #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25) #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24) #define ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4)) #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4)) #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4)) #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 #define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) #define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 0xF) << 12) #define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 #define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) #define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5) #define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4) #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4) #define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4) #define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8) #define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4) #define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2) #define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0) #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4) #define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23) #define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22) #define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21) #define USB2_OTG_PAD_CTL0_PD2 (1 << 20) #define USB2_OTG_PAD_CTL0_PD (1 << 19) #define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18) #define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16) #define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14) #define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12) #define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6) #define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4) #define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11) #define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9) #define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7) #define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3) #define USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1) #define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8 #define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14) #define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13) #define USB2_BIAS_PAD_CTL0_PD (1 << 12) #define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9) #define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7) #define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5) #define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2) #define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0) #define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8 #define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16) #define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12) #define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8) #define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4) #define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0) #define XUSB_PADCTL_USB3_PAD_MUX 0x134 #define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) #define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6) #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138 #define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27) #define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24) #define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3) #define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1) #define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0) #define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C #define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140 #define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144 #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148 #define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1) #define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0) #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150 #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154 #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158 #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) struct padctl_softc { device_t dev; struct resource *mem_res; hwreset_t rst; int phy_ena_cnt; /* Fuses calibration data */ uint32_t hs_curr_level_0; uint32_t hs_curr_level_123; uint32_t hs_iref_cap; uint32_t hs_term_range_adj; uint32_t hs_squelch_level; uint32_t hs_curr_level_offset; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-xusb-padctl", 1}, {NULL, 0}, }; /* Ports. */ enum padctl_port_type { PADCTL_PORT_USB2, PADCTL_PORT_ULPI, PADCTL_PORT_HSIC, PADCTL_PORT_USB3, }; struct padctl_lane; struct padctl_port { enum padctl_port_type type; const char *name; const char *base_name; int idx; int (*init)(struct padctl_softc *sc, struct padctl_port *port); /* Runtime data. */ bool enabled; regulator_t supply_vbus; /* USB2, USB3 */ bool internal; /* ULPI, USB2, USB3 */ uint32_t companion; /* USB3 */ struct padctl_lane *lane; }; static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port); #define PORT(t, n, p, i) { \ .type = t, \ .name = n "-" #p, \ .base_name = n, \ .idx = p, \ .init = i, \ } static struct padctl_port ports_tbl[] = { PORT(PADCTL_PORT_USB2, "usb2", 0, NULL), PORT(PADCTL_PORT_USB2, "usb2", 1, NULL), PORT(PADCTL_PORT_USB2, "usb2", 2, NULL), PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL), PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init), PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init), }; /* Pads - a group of lannes. */ enum padctl_pad_type { PADCTL_PAD_USB2, PADCTL_PAD_ULPI, PADCTL_PAD_HSIC, PADCTL_PAD_PCIE, PADCTL_PAD_SATA, }; struct padctl_lane; struct padctl_pad { const char *name; enum padctl_pad_type type; int (*powerup)(struct padctl_softc *sc, struct padctl_lane *lane); int (*powerdown)(struct padctl_softc *sc, struct padctl_lane *lane); /* Runtime data. */ bool enabled; struct padctl_lane *lanes[8]; /* Safe maximum value. */ int nlanes; }; static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane); static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); #define PAD(n, t, u, d) { \ .name = n, \ .type = t, \ .powerup = u, \ .powerdown = d, \ } static struct padctl_pad pads_tbl[] = { PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown), PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL), PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL), PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown), PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown), }; /* Lanes. */ static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; static char *usb_mux[] = {"snps", "xusb"}; static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"}; struct padctl_lane { const char *name; int idx; bus_size_t reg; uint32_t shift; uint32_t mask; char **mux; int nmux; /* Runtime data. */ bool enabled; struct padctl_pad *pad; struct padctl_port *port; int mux_idx; }; #define LANE(n, p, r, s, m, mx) { \ .name = n "-" #p, \ .idx = p, \ .reg = r, \ .shift = s, \ .mask = m, \ .mux = mx, \ .nmux = nitems(mx), \ } static struct padctl_lane lanes_tbl[] = { LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux), LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux), LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux), LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux), LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux), LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux), LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux), LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux), LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux), LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux), LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux), LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux), }; /* Define all possible mappings for USB3 port lanes */ struct padctl_lane_map { int port_idx; enum padctl_pad_type pad_type; int lane_idx; }; #define LANE_MAP(pi, pt, li) { \ .port_idx = pi, \ .pad_type = pt, \ .lane_idx = li, \ } static struct padctl_lane_map lane_map_tbl[] = { LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */ LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */ /* -- or -- */ LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */ }; /* Phy class and methods. */ static int xusbpadctl_phy_enable(struct phynode *phy, bool enable); static phynode_method_t xusbpadctl_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class, xusbpadctl_phynode_methods, 0, phynode_class); static struct padctl_port *search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane); /* ------------------------------------------------------------------------- * * PHY functions */ static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP); if (port->internal) reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx); else reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx); reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0); reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion); WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx)); reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0); reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0); reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0); reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24); reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070); reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF); WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg); WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx), 0x002008EE); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); return (0); } static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int i; reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0); WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN; reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN; reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg |= IOPHY_PLL_P0_CTL1_PLL_RST; WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); for (i = 100; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Failed to power up PCIe phy\n"); return (ETIMEDOUT); } reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); return (0); } static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int i; reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); for (i = 100; i >= 0; i--) { reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) break; DELAY(100); } if (i <= 0) { device_printf(sc->dev, "Failed to power up SATA phy\n"); return (ETIMEDOUT); } reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE; WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE; WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); DELAY(100); return (0); } static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0); reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0); reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level); reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5); WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP); reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0); reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST); WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx)); reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0); reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0); reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0); reg &= ~USB2_OTG_PAD_CTL0_PD; reg &= ~USB2_OTG_PAD_CTL0_PD2; reg &= ~USB2_OTG_PAD_CTL0_PD_ZI; reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14); if (lane->idx == 0) { reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0); reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3); } else { reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123); reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0); } WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx)); reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0); reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0); reg &= ~USB2_OTG_PAD_CTL1_PD_DR; reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP; reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP; reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj); reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap); WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg); if (port != NULL && port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); return (0); } static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg |= USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); if (port != NULL && port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot disable vbus regulator\n"); return (rv); } } return (0); } static int phy_powerup(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); return (0); } static int phy_powerdown(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); return (0); } static int xusbpadctl_phy_enable(struct phynode *phy, bool enable) { device_t dev; intptr_t id; struct padctl_softc *sc; struct padctl_lane *lane; struct padctl_pad *pad; int rv; dev = phynode_get_device(phy); id = phynode_get_id(phy); sc = device_get_softc(dev); if (id < 0 || id >= nitems(lanes_tbl)) { device_printf(dev, "Unknown phy: %d\n", id); return (ENXIO); } lane = lanes_tbl + id; if (!lane->enabled) { device_printf(dev, "Lane is not enabled/configured: %s\n", lane->name); return (ENXIO); } pad = lane->pad; if (enable) { if (sc->phy_ena_cnt == 0) { rv = phy_powerup(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt++; } if (enable) rv = pad->powerup(sc, lane); else rv = pad->powerdown(sc, lane); if (rv != 0) return (rv); if (!enable) { if (sc->phy_ena_cnt == 1) { rv = phy_powerdown(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt--; } return (0); } /* ------------------------------------------------------------------------- * * FDT processing */ static struct padctl_port * search_port(struct padctl_softc *sc, char *port_name) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (strcmp(port_name, ports_tbl[i].name) == 0) return (&ports_tbl[i]); } return (NULL); } static struct padctl_port * search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (!ports_tbl[i].enabled) continue; if (ports_tbl[i].lane == lane) return (ports_tbl + i); } return (NULL); } static struct padctl_lane * search_lane(struct padctl_softc *sc, char *lane_name) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (strcmp(lane_name, lanes_tbl[i].name) == 0) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (!lanes_tbl[i].enabled) continue; if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_usb3_pad_lane(struct padctl_softc *sc, int idx) { int i; struct padctl_lane *lane, *tmp; lane = NULL; for (i = 0; i < nitems(lane_map_tbl); i++) { if (idx != lane_map_tbl[i].port_idx) continue; tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type, lane_map_tbl[i].lane_idx); if (tmp == NULL) continue; if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0) continue; if (lane != NULL) { device_printf(sc->dev, "Duplicated mappings found for" " lanes: %s and %s\n", lane->name, tmp->name); return (NULL); } lane = tmp; } return (lane); } static struct padctl_pad * search_pad(struct padctl_softc *sc, char *pad_name) { int i; for (i = 0; i < nitems(pads_tbl); i++) { if (strcmp(pad_name, pads_tbl[i].name) == 0) return (pads_tbl + i); } return (NULL); } static int search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name) { int i; for (i = 0; i < lane->nmux; i++) { if (strcmp(fnc_name, lane->mux[i]) == 0) return (i); } return (-1); } static int config_lane(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, lane->reg); reg &= ~(lane->mask << lane->shift); reg |= (lane->mux_idx & lane->mask) << lane->shift; WR4(sc, lane->reg, reg); return (0); } static int process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad) { struct padctl_lane *lane; struct phynode *phynode; struct phynode_init_def phy_init; char *name; char *function; int rv; name = NULL; function = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane name.\n"); return (ENXIO); } lane = search_lane(sc, name); if (lane == NULL) { device_printf(sc->dev, "Unknown lane: %s\n", name); rv = ENXIO; goto end; } /* Read function (mux) settings. */ rv = OF_getprop_alloc(node, "nvidia,function", (void **)&function); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane function.\n"); rv = ENXIO; goto end; } lane->mux_idx = search_mux(sc, lane, function); if (lane->mux_idx == ~0) { device_printf(sc->dev, "Unknown function %s for lane %s\n", function, name); rv = ENXIO; goto end; } rv = config_lane(sc, lane); if (rv != 0) { device_printf(sc->dev, "Cannot configure lane: %s: %d\n", name, rv); rv = ENXIO; goto end; } lane->pad = pad; lane->enabled = true; pad->lanes[pad->nlanes++] = lane; /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = lane - lanes_tbl; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy\n"); rv = ENXIO; goto end; } if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot create phy\n"); return (ENXIO); } rv = 0; end: if (name != NULL) OF_prop_free(name); if (function != NULL) OF_prop_free(function); return (rv); } static int process_pad(struct padctl_softc *sc, phandle_t node) { struct padctl_pad *pad; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read pad name.\n"); return (ENXIO); } pad = search_pad(sc, name); if (pad == NULL) { device_printf(sc->dev, "Unknown pad: %s\n", name); rv = ENXIO; goto end; } /* Read and process associated lanes. */ node = ofw_bus_find_child(node, "lanes"); if (node <= 0) { device_printf(sc->dev, "Cannot find regulators subnode\n"); rv = ENXIO; goto end; } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_lane(sc, node, pad); if (rv != 0) goto end; } pad->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int process_port(struct padctl_softc *sc, phandle_t node) { struct padctl_port *port; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read port name.\n"); return (ENXIO); } port = search_port(sc, name); if (port == NULL) { device_printf(sc->dev, "Unknown port: %s\n", name); rv = ENXIO; goto end; } if (port->type == PADCTL_PORT_USB3) { rv = OF_getencprop(node, "nvidia,usb2-companion", &(port->companion), sizeof(port->companion)); if (rv <= 0) { device_printf(sc->dev, "Missing 'nvidia,usb2-companion' property " "for port: %s\n", name); rv = ENXIO; goto end; } } if (OF_hasprop(node, "vbus-supply")) { rv = regulator_get_by_ofw_property(sc->dev, 0, "vbus-supply", &port->supply_vbus); if (rv <= 0) { device_printf(sc->dev, "Cannot get 'vbus-supply' regulator " "for port: %s\n", name); rv = ENXIO; goto end; } } if (OF_hasprop(node, "nvidia,internal")) port->internal = true; /* Find assigned lane */ if (port->lane == NULL) { switch(port->type) { /* Routing is fixed for USB2, ULPI AND HSIC. */ case PADCTL_PORT_USB2: port->lane = search_pad_lane(sc, PADCTL_PAD_USB2, port->idx); break; case PADCTL_PORT_ULPI: port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI, port->idx); break; case PADCTL_PORT_HSIC: port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC, port->idx); break; case PADCTL_PORT_USB3: port->lane = search_usb3_pad_lane(sc, port->idx); break; } } if (port->lane == NULL) { device_printf(sc->dev, "Cannot find lane for port: %s\n", name); rv = ENXIO; goto end; } port->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int parse_fdt(struct padctl_softc *sc, phandle_t base_node) { phandle_t node; int rv; rv = 0; node = ofw_bus_find_child(base_node, "pads"); if (node <= 0) { device_printf(sc->dev, "Cannot find pads subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_pad(sc, node); if (rv != 0) return (rv); } node = ofw_bus_find_child(base_node, "ports"); if (node <= 0) { device_printf(sc->dev, "Cannot find ports subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_port(sc, node); if (rv != 0) return (rv); } return (0); } static void load_calibration(struct padctl_softc *sc) { uint32_t reg; /* All XUSB pad calibrations are packed into single dword.*/ reg = tegra_fuse_read_4(FUSE_XUSB_CALIB); sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg); sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg); sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg); sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg); sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg); } /* ------------------------------------------------------------------------- * * BUS functions */ static int xusbpadctl_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(dev, "Tegra XUSB phy"); return (BUS_PROBE_DEFAULT); } static int xusbpadctl_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int xusbpadctl_attach(device_t dev) { struct padctl_softc * sc; int i, rid, rv; struct padctl_port *port; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst); if (rv != 0) { device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); return (rv); } rv = hwreset_deassert(sc->rst); if (rv != 0) { device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); return (rv); } load_calibration(sc); rv = parse_fdt(sc, node); if (rv != 0) { device_printf(dev, "Cannot parse fdt configuration: %d\n", rv); return (rv); } for (i = 0; i < nitems(ports_tbl); i++) { port = ports_tbl + i; if (!port->enabled) continue; if (port->init == NULL) continue; rv = port->init(sc, port); if (rv != 0) { device_printf(dev, "Cannot init port '%s'\n", port->name); return (rv); } } return (0); } static device_method_t tegra_xusbpadctl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xusbpadctl_probe), DEVMETHOD(device_attach, xusbpadctl_attach), DEVMETHOD(device_detach, xusbpadctl_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, tegra_xusbpadctl_methods, sizeof(struct padctl_softc)); EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, NULL, NULL, 73); diff --git a/sys/arm/nvidia/tegra_ahci.c b/sys/arm/nvidia/tegra_ahci.c index eeb49d6ea5a8..de60f02e97ea 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 #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"); 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/nvidia/tegra_efuse.c b/sys/arm/nvidia/tegra_efuse.c index 9e151f6ed564..35d9380a18a5 100644 --- a/sys/arm/nvidia/tegra_efuse.c +++ b/sys/arm/nvidia/tegra_efuse.c @@ -1,527 +1,527 @@ /*- * Copyright (c) 2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #define FUSES_START 0x100 #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (FUSES_START + (_r))) struct efuse_soc; struct tegra_efuse_softc { device_t dev; struct resource *mem_res; struct efuse_soc *soc; clk_t clk; hwreset_t reset; }; struct tegra_efuse_softc *dev_sc; struct tegra_sku_info tegra_sku_info; static char *tegra_rev_name[] = { [TEGRA_REVISION_UNKNOWN] = "unknown", [TEGRA_REVISION_A01] = "A01", [TEGRA_REVISION_A02] = "A02", [TEGRA_REVISION_A03] = "A03", [TEGRA_REVISION_A03p] = "A03 prime", [TEGRA_REVISION_A04] = "A04", }; struct efuse_soc { void (*init)(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku); }; static void tegra124_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku); struct efuse_soc tegra124_efuse_soc = { .init = tegra124_init, }; static void tegra210_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku); struct efuse_soc tegra210_efuse_soc = { .init = tegra210_init, }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-efuse", (intptr_t)&tegra124_efuse_soc}, {"nvidia,tegra210-efuse", (intptr_t)&tegra210_efuse_soc}, {NULL, 0} }; /* ---------------------- Tegra 124 specific code & data --------------- */ #define TEGRA124_CPU_PROCESS_CORNERS 2 #define TEGRA124_GPU_PROCESS_CORNERS 2 #define TEGRA124_SOC_PROCESS_CORNERS 2 #define TEGRA124_FUSE_SKU_INFO 0x10 #define TEGRA124_FUSE_CPU_SPEEDO_0 0x14 #define TEGRA124_FUSE_CPU_IDDQ 0x18 #define TEGRA124_FUSE_FT_REV 0x28 #define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c #define TEGRA124_FUSE_CPU_SPEEDO_2 0x30 #define TEGRA124_FUSE_SOC_SPEEDO_0 0x34 #define TEGRA124_FUSE_SOC_SPEEDO_1 0x38 #define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c #define TEGRA124_FUSE_SOC_IDDQ 0x40 #define TEGRA124_FUSE_GPU_IDDQ 0x128 enum { TEGRA124_THRESHOLD_INDEX_0, TEGRA124_THRESHOLD_INDEX_1, TEGRA124_THRESHOLD_INDEX_COUNT, }; static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] = { {2190, UINT_MAX}, {0, UINT_MAX}, }; static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] = { {1965, UINT_MAX}, {0, UINT_MAX}, }; static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] = { {2101, UINT_MAX}, {0, UINT_MAX}, }; static void tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku, int *threshold) { /* Set default */ sku->cpu_speedo_id = 0; sku->soc_speedo_id = 0; sku->gpu_speedo_id = 0; *threshold = TEGRA124_THRESHOLD_INDEX_0; switch (sku->sku_id) { case 0x00: /* Eng sku */ case 0x0F: case 0x23: /* Using the default */ break; case 0x83: sku->cpu_speedo_id = 2; break; case 0x1F: case 0x87: case 0x27: sku->cpu_speedo_id = 2; sku->soc_speedo_id = 0; sku->gpu_speedo_id = 1; *threshold = TEGRA124_THRESHOLD_INDEX_0; break; case 0x81: case 0x21: case 0x07: sku->cpu_speedo_id = 1; sku->soc_speedo_id = 1; sku->gpu_speedo_id = 1; *threshold = TEGRA124_THRESHOLD_INDEX_1; break; case 0x49: case 0x4A: case 0x48: sku->cpu_speedo_id = 4; sku->soc_speedo_id = 2; sku->gpu_speedo_id = 3; *threshold = TEGRA124_THRESHOLD_INDEX_1; break; default: device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id); break; } } static void tegra124_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku) { int i, threshold; sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO); sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ); sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ); sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ); sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0); sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0); sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2); if (sku->cpu_speedo_value == 0) { device_printf(sc->dev, "CPU Speedo value is not fused.\n"); return; } tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold); for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) { if (sku->soc_speedo_value < tegra124_soc_process_speedos[threshold][i]) break; } sku->soc_process_id = i; for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) { if (sku->cpu_speedo_value < tegra124_cpu_process_speedos[threshold][i]) break; } sku->cpu_process_id = i; for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) { if (sku->gpu_speedo_value < tegra124_gpu_process_speedos[threshold][i]) break; } sku->gpu_process_id = i; } /* ----------------- End of Tegra 124 specific code & data --------------- */ /* -------------------- Tegra 201 specific code & data ------------------- */ #define TEGRA210_CPU_PROCESS_CORNERS 2 #define TEGRA210_GPU_PROCESS_CORNERS 2 #define TEGRA210_SOC_PROCESS_CORNERS 3 #define TEGRA210_FUSE_SKU_INFO 0x010 #define TEGRA210_FUSE_CPU_SPEEDO_0 0x014 #define TEGRA210_FUSE_CPU_IDDQ 0x018 #define TEGRA210_FUSE_FT_REV 0x028 #define TEGRA210_FUSE_CPU_SPEEDO_1 0x02c #define TEGRA210_FUSE_CPU_SPEEDO_2 0x030 #define TEGRA210_FUSE_SOC_SPEEDO_0 0x034 #define TEGRA210_FUSE_SOC_SPEEDO_1 0x038 #define TEGRA210_FUSE_SOC_SPEEDO_2 0x03c #define TEGRA210_FUSE_SOC_IDDQ 0x040 #define TEGRA210_FUSE_GPU_IDDQ 0x128 #define TEGRA210_FUSE_SPARE 0x270 enum { TEGRA210_THRESHOLD_INDEX_0, TEGRA210_THRESHOLD_INDEX_1, TEGRA210_THRESHOLD_INDEX_COUNT, }; static uint32_t tegra210_cpu_process_speedos[][TEGRA210_CPU_PROCESS_CORNERS] = { {2119, UINT_MAX}, {2119, UINT_MAX}, }; static uint32_t tegra210_gpu_process_speedos[][TEGRA210_GPU_PROCESS_CORNERS] = { {UINT_MAX, UINT_MAX}, {UINT_MAX, UINT_MAX}, }; static uint32_t tegra210_soc_process_speedos[][TEGRA210_SOC_PROCESS_CORNERS] = { {1950, 2100, UINT_MAX}, {1950, 2100, UINT_MAX}, }; static uint32_t tegra210_get_speedo_revision(struct tegra_efuse_softc *sc) { uint32_t reg; uint32_t val; val = 0; /* Revision i encoded in spare fields */ reg = RD4(sc, TEGRA210_FUSE_SPARE + 2 * 4); val |= (reg & 1) << 0; reg = RD4(sc, TEGRA210_FUSE_SPARE + 3 * 4); val |= (reg & 1) << 1; reg = RD4(sc, TEGRA210_FUSE_SPARE + 4 * 4); val |= (reg & 1) << 2; return (val); } static void tegra210_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku, int speedo_rev, int *threshold) { /* Set defaults */ sku->cpu_speedo_id = 0; sku->soc_speedo_id = 0; sku->gpu_speedo_id = 0; *threshold = TEGRA210_THRESHOLD_INDEX_0; switch (sku->sku_id) { case 0x00: /* Eng sku */ case 0x01: /* Eng sku */ case 0x07: case 0x17: case 0x27: /* Use defaults */ if (speedo_rev >= 2) sku->gpu_speedo_id = 1; break; case 0x13: if (speedo_rev >= 2) sku->gpu_speedo_id = 1; sku->cpu_speedo_id = 1; break; default: device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id); break; } } static void tegra210_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku) { int i, threshold, speedo_rev; uint32_t cpu_speedo[3], soc_speedo[3]; cpu_speedo[0] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_0); cpu_speedo[1] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_1); cpu_speedo[2] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_2); soc_speedo[0] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_0); soc_speedo[1] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_1); soc_speedo[2] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_2); sku->cpu_iddq_value = RD4(sc, TEGRA210_FUSE_CPU_IDDQ); sku->soc_iddq_value = RD4(sc, TEGRA210_FUSE_SOC_IDDQ); sku->gpu_iddq_value = RD4(sc, TEGRA210_FUSE_GPU_IDDQ); speedo_rev = tegra210_get_speedo_revision(sc); device_printf(sc->dev, " Speedo revision: %u\n", speedo_rev); if (speedo_rev >= 3) { sku->cpu_speedo_value = cpu_speedo[0]; sku->gpu_speedo_value = cpu_speedo[2]; sku->soc_speedo_value = soc_speedo[0]; } else if (speedo_rev == 2) { sku->cpu_speedo_value = (-1938 + (1095 * cpu_speedo[0] / 100)) / 10; sku->gpu_speedo_value = (-1662 + (1082 * cpu_speedo[2] / 100)) / 10; sku->soc_speedo_value = ( -705 + (1037 * soc_speedo[0] / 100)) / 10; } else { sku->cpu_speedo_value = 2100; sku->gpu_speedo_value = cpu_speedo[2] - 75; sku->soc_speedo_value = 1900; } tegra210_rev_sku_to_speedo_ids(sc, sku, speedo_rev, &threshold); for (i = 0; i < TEGRA210_SOC_PROCESS_CORNERS; i++) { if (sku->soc_speedo_value < tegra210_soc_process_speedos[threshold][i]) break; } sku->soc_process_id = i; for (i = 0; i < TEGRA210_CPU_PROCESS_CORNERS; i++) { if (sku->cpu_speedo_value < tegra210_cpu_process_speedos[threshold][i]) break; } sku->cpu_process_id = i; for (i = 0; i < TEGRA210_GPU_PROCESS_CORNERS; i++) { if (sku->gpu_speedo_value < tegra210_gpu_process_speedos[threshold][i]) break; } sku->gpu_process_id = i; } /* ----------------- End of Tegra 210 specific code & data --------------- */ uint32_t tegra_fuse_read_4(int addr) { if (dev_sc == NULL) panic("tegra_fuse_read_4 called too early"); return (RD4(dev_sc, addr)); } static void tegra_efuse_dump_sku(void) { printf(" TEGRA SKU Info:\n"); printf(" chip_id: %u\n", tegra_sku_info.chip_id); printf(" sku_id: %u\n", tegra_sku_info.sku_id); printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id); printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id); printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value); printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value); printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id); printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id); printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value); printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value); printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id); printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id); printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value); printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value); printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]); } static int tegra_efuse_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int tegra_efuse_attach(device_t dev) { int rv, rid; struct tegra_efuse_softc *sc; sc = device_get_softc(dev); sc->dev = dev; sc->soc = (struct efuse_soc *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Get the memory resource for the register mapping. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot map registers.\n"); rv = ENXIO; goto fail; } /* OFW resources. */ rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get fuse clock: %d\n", rv); goto fail; } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock: %d\n", rv); goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get fuse reset\n"); goto fail; } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot clear reset\n"); goto fail; } sc->soc->init(sc, &tegra_sku_info); dev_sc = sc; if (bootverbose) tegra_efuse_dump_sku(); return (bus_generic_attach(dev)); fail: dev_sc = NULL; if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (rv); } static int tegra_efuse_detach(device_t dev) { struct tegra_efuse_softc *sc; sc = device_get_softc(dev); dev_sc = NULL; if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (bus_generic_detach(dev)); } static device_method_t tegra_efuse_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_efuse_probe), DEVMETHOD(device_attach, tegra_efuse_attach), DEVMETHOD(device_detach, tegra_efuse_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods, sizeof(struct tegra_efuse_softc)); EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, NULL, NULL, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra_ehci.c b/sys/arm/nvidia/tegra_ehci.c index 1d34ed45dd90..033b7b9794fb 100644 --- a/sys/arm/nvidia/tegra_ehci.c +++ b/sys/arm/nvidia/tegra_ehci.c @@ -1,315 +1,315 @@ /*- * 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 /* * EHCI driver for Tegra SoCs. */ #include "opt_bus.h" #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 #include #include #include "usbdevs.h" #define TEGRA_EHCI_REG_OFF 0x100 #define TEGRA_EHCI_REG_SIZE 0x100 /* Compatible devices. */ #define TEGRA124_EHCI 1 #define TEGRA210_EHCI 2 static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-ehci", (uintptr_t)TEGRA124_EHCI}, {"nvidia,tegra210-ehci", (uintptr_t)TEGRA210_EHCI}, {NULL, 0}, }; struct tegra_ehci_softc { ehci_softc_t ehci_softc; device_t dev; struct resource *ehci_mem_res; /* EHCI core regs. */ struct resource *ehci_irq_res; /* EHCI core IRQ. */ int usb_alloc_called; clk_t clk; phy_t phy; hwreset_t reset; }; static void tegra_ehci_post_reset(struct ehci_softc *ehci_softc) { uint32_t usbmode; /* Force HOST mode. */ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM); usbmode &= ~EHCI_UM_CM; usbmode |= EHCI_UM_CM_HOST; device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n"); EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode); } static int tegra_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Nvidia Tegra EHCI controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int tegra_ehci_detach(device_t dev) { struct tegra_ehci_softc *sc; ehci_softc_t *esc; sc = device_get_softc(dev); esc = &sc->ehci_softc; if (sc->clk != NULL) clk_release(sc->clk); if (esc->sc_bus.bdev != NULL) device_delete_child(dev, esc->sc_bus.bdev); if (esc->sc_flags & EHCI_SCFLG_DONEINIT) ehci_detach(esc); if (esc->sc_intr_hdl != NULL) bus_teardown_intr(dev, esc->sc_irq_res, esc->sc_intr_hdl); if (sc->ehci_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ehci_irq_res); if (sc->ehci_mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->ehci_mem_res); if (sc->usb_alloc_called) usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc); /* During module unload there are lots of children leftover. */ device_delete_children(dev); return (0); } static int tegra_ehci_attach(device_t dev) { struct tegra_ehci_softc *sc; ehci_softc_t *esc; int rv, rid; uint64_t freq; sc = device_get_softc(dev); sc->dev = dev; esc = &sc->ehci_softc; /* Allocate resources. */ rid = 0; sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->ehci_mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); rv = ENXIO; goto out; } rid = 0; sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->ehci_irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); rv = ENXIO; goto out; } rv = hwreset_get_by_ofw_name(dev, 0, "usb", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get reset\n"); rv = ENXIO; goto out; } rv = phy_get_by_ofw_property(sc->dev, 0, "nvidia,phy", &sc->phy); if (rv != 0) { device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n"); rv = ENXIO; goto out; } rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get clock\n"); goto out; } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock\n"); goto out; } freq = 0; rv = clk_get_freq(sc->clk, &freq); if (rv != 0) { device_printf(dev, "Cannot get clock frequency\n"); goto out; } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot clear reset: %d\n", rv); rv = ENXIO; goto out; } rv = phy_enable(sc->phy); if (rv != 0) { device_printf(dev, "Cannot enable phy: %d\n", rv); goto out; } /* Fill data for EHCI driver. */ esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc; esc->sc_vendor_post_reset = tegra_ehci_post_reset; esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res); esc->sc_bus.parent = dev; esc->sc_bus.devices = esc->sc_devices; esc->sc_bus.devices_max = EHCI_MAX_DEVICES; esc->sc_bus.dma_bits = 32; /* Allocate all DMA memory. */ rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc); sc->usb_alloc_called = 1; if (rv != 0) { device_printf(dev, "usb_bus_mem_alloc_all() failed\n"); rv = ENOMEM; goto out; } /* * Set handle to USB related registers subregion used by * generic EHCI driver. */ rv = bus_space_subregion(esc->sc_io_tag, rman_get_bushandle(sc->ehci_mem_res), TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl); if (rv != 0) { device_printf(dev, "Could not create USB memory subregion\n"); rv = ENXIO; goto out; } /* Setup interrupt handler. */ rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); if (rv != 0) { device_printf(dev, "Could not setup IRQ\n"); goto out; } /* Add USB bus device. */ esc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (esc->sc_bus.bdev == NULL) { device_printf(dev, "Could not add USB device\n"); goto out; } device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus); esc->sc_id_vendor = USB_VENDOR_FREESCALE; strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor)); /* Set flags that affect ehci_init() behavior. */ esc->sc_flags |= EHCI_SCFLG_TT; esc->sc_flags |= EHCI_SCFLG_NORESTERM; rv = ehci_init(esc); if (rv != 0) { device_printf(dev, "USB init failed: %d\n", rv); goto out; } esc->sc_flags |= EHCI_SCFLG_DONEINIT; /* Probe the bus. */ rv = device_probe_and_attach(esc->sc_bus.bdev); if (rv != 0) { device_printf(dev, "device_probe_and_attach() failed\n"); goto out; } return (0); out: tegra_ehci_detach(dev); return (rv); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_ehci_probe), DEVMETHOD(device_attach, tegra_ehci_attach), DEVMETHOD(device_detach, tegra_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD_END }; static DEFINE_CLASS_0(ehci, ehci_driver, ehci_methods, sizeof(struct tegra_ehci_softc)); DRIVER_MODULE(tegra_ehci, simplebus, ehci_driver, NULL, NULL); MODULE_DEPEND(tegra_ehci, usb, 1, 1, 1); diff --git a/sys/arm/nvidia/tegra_i2c.c b/sys/arm/nvidia/tegra_i2c.c index 1849ae33e3a2..1b959c5c5bbe 100644 --- a/sys/arm/nvidia/tegra_i2c.c +++ b/sys/arm/nvidia/tegra_i2c.c @@ -1,797 +1,797 @@ /*- * 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 /* * I2C driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include "iicbus_if.h" #define I2C_CNFG 0x000 #define I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT (1 << 15) #define I2C_CNFG_DEBOUNCE_CNT(x) (((x) & 0x07) << 12) #define I2C_CNFG_NEW_MASTER_FSM (1 << 11) #define I2C_CNFG_PACKET_MODE_EN (1 << 10) #define I2C_CNFG_SEND (1 << 9) #define I2C_CNFG_NOACK (1 << 8) #define I2C_CNFG_CMD2 (1 << 7) #define I2C_CNFG_CMD1 (1 << 6) #define I2C_CNFG_START (1 << 5) #define I2C_CNFG_SLV2 (1 << 4) #define I2C_CNFG_LENGTH_SHIFT 1 #define I2C_CNFG_LENGTH_MASK 0x7 #define I2C_CNFG_A_MOD (1 << 0) #define I2C_CMD_ADDR0 0x004 #define I2C_CMD_ADDR1 0x008 #define I2C_CMD_DATA1 0x00c #define I2C_CMD_DATA2 0x010 #define I2C_STATUS 0x01c #define I2C_SL_CNFG 0x020 #define I2C_SL_RCVD 0x024 #define I2C_SL_STATUS 0x028 #define I2C_SL_ADDR1 0x02c #define I2C_SL_ADDR2 0x030 #define I2C_TLOW_SEXT 0x034 #define I2C_SL_DELAY_COUNT 0x03c #define I2C_SL_INT_MASK 0x040 #define I2C_SL_INT_SOURCE 0x044 #define I2C_SL_INT_SET 0x048 #define I2C_TX_PACKET_FIFO 0x050 #define I2C_RX_FIFO 0x054 #define I2C_PACKET_TRANSFER_STATUS 0x058 #define I2C_FIFO_CONTROL 0x05c #define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x) (((x) & 0x07) << 13) #define I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x) (((x) & 0x07) << 10) #define I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH (1 << 9) #define I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH (1 << 8) #define I2C_FIFO_CONTROL_TX_FIFO_TRIG(x) (((x) & 0x07) << 5) #define I2C_FIFO_CONTROL_RX_FIFO_TRIG(x) (((x) & 0x07) << 2) #define I2C_FIFO_CONTROL_TX_FIFO_FLUSH (1 << 1) #define I2C_FIFO_CONTROL_RX_FIFO_FLUSH (1 << 0) #define I2C_FIFO_STATUS 0x060 #define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON (1 << 25) #define I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x) (((x) >> 20) & 0xF) #define I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x) (((x) >> 16) & 0xF) #define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x) (((x) >> 4) & 0xF) #define I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x) (((x) >> 0) & 0xF) #define I2C_INTERRUPT_MASK_REGISTER 0x064 #define I2C_INTERRUPT_STATUS_REGISTER 0x068 #define I2C_INT_SLV_ACK_WITHHELD (1 << 28) #define I2C_INT_SLV_RD2WR (1 << 27) #define I2C_INT_SLV_WR2RD (1 << 26) #define I2C_INT_SLV_PKT_XFER_ERR (1 << 25) #define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24) #define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23) #define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22) #define I2C_INT_SLV_TFIFO_OVF (1 << 21) #define I2C_INT_SLV_RFIFO_UNF (1 << 20) #define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17) #define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16) #define I2C_INT_BUS_CLEAR_DONE (1 << 11) #define I2C_INT_TLOW_MEXT_TIMEOUT (1 << 10) #define I2C_INT_TLOW_SEXT_TIMEOUT (1 << 9) #define I2C_INT_TIMEOUT (1 << 8) #define I2C_INT_PACKET_XFER_COMPLETE (1 << 7) #define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1 << 6) #define I2C_INT_TFIFO_OVR (1 << 5) #define I2C_INT_RFIFO_UNF (1 << 4) #define I2C_INT_NOACK (1 << 3) #define I2C_INT_ARB_LOST (1 << 2) #define I2C_INT_TFIFO_DATA_REQ (1 << 1) #define I2C_INT_RFIFO_DATA_REQ (1 << 0) #define I2C_ERROR_MASK (I2C_INT_ARB_LOST | I2C_INT_NOACK | \ I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR) #define I2C_CLK_DIVISOR 0x06c #define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16 #define I2C_CLK_DIVISOR_STD_FAST_MODE_MASK 0xffff #define I2C_CLK_DIVISOR_HSMODE_SHIFT 0 #define I2C_CLK_DIVISOR_HSMODE_MASK 0xffff #define I2C_INTERRUPT_SOURCE_REGISTER 0x070 #define I2C_INTERRUPT_SET_REGISTER 0x074 #define I2C_SLV_TX_PACKET_FIFO 0x07c #define I2C_SLV_PACKET_STATUS 0x080 #define I2C_BUS_CLEAR_CONFIG 0x084 #define I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x) (((x) & 0xFF) << 16) #define I2C_BUS_CLEAR_CONFIG_BC_STOP_COND (1 << 2) #define I2C_BUS_CLEAR_CONFIG_BC_TERMINATE (1 << 1) #define I2C_BUS_CLEAR_CONFIG_BC_ENABLE (1 << 0) #define I2C_BUS_CLEAR_STATUS 0x088 #define I2C_BUS_CLEAR_STATUS_BC_STATUS (1 << 0) #define I2C_CONFIG_LOAD 0x08c #define I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD (1 << 2) #define I2C_CONFIG_LOAD_SLV_CONFIG_LOAD (1 << 1) #define I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD (1 << 0) #define I2C_INTERFACE_TIMING_0 0x094 #define I2C_INTERFACE_TIMING_1 0x098 #define I2C_HS_INTERFACE_TIMING_0 0x09c #define I2C_HS_INTERFACE_TIMING_1 0x0a0 /* Protocol header 0 */ #define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 #define PACKET_HEADER0_HEADER_SIZE_MASK 0x3 #define PACKET_HEADER0_PACKET_ID_SHIFT 16 #define PACKET_HEADER0_PACKET_ID_MASK 0xff #define PACKET_HEADER0_CONT_ID_SHIFT 12 #define PACKET_HEADER0_CONT_ID_MASK 0xf #define PACKET_HEADER0_PROTOCOL_I2C (1 << 4) #define PACKET_HEADER0_TYPE_SHIFT 0 #define PACKET_HEADER0_TYPE_MASK 0x7 /* I2C header */ #define I2C_HEADER_HIGHSPEED_MODE (1 << 22) #define I2C_HEADER_CONT_ON_NAK (1 << 21) #define I2C_HEADER_SEND_START_BYTE (1 << 20) #define I2C_HEADER_READ (1 << 19) #define I2C_HEADER_10BIT_ADDR (1 << 18) #define I2C_HEADER_IE_ENABLE (1 << 17) #define I2C_HEADER_REPEAT_START (1 << 16) #define I2C_HEADER_CONTINUE_XFER (1 << 15) #define I2C_HEADER_MASTER_ADDR_SHIFT 12 #define I2C_HEADER_MASTER_ADDR_MASK 0x7 #define I2C_HEADER_SLAVE_ADDR_SHIFT 0 #define I2C_HEADER_SLAVE_ADDR_MASK 0x3ff #define I2C_CLK_DIVISOR_STD_FAST_MODE 0x19 #define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8 #define I2C_REQUEST_TIMEOUT (5 * hz) #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) #define LOCK(_sc) mtx_lock(&(_sc)->mtx) #define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define SLEEP(_sc, timeout) \ mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout); #define LOCK_INIT(_sc) \ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF) #define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) #define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) #define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-i2c", 1}, {"nvidia,tegra210-i2c", 1}, {NULL, 0} }; enum tegra_i2c_xfer_type { XFER_STOP, /* Send stop condition after xfer */ XFER_REPEAT_START, /* Send repeated start after xfer */ XFER_CONTINUE /* Don't send nothing */ } ; struct tegra_i2c_softc { device_t dev; struct mtx mtx; struct resource *mem_res; struct resource *irq_res; void *irq_h; device_t iicbus; clk_t clk; hwreset_t reset; uint32_t core_freq; uint32_t bus_freq; int bus_inuse; struct iic_msg *msg; int msg_idx; uint32_t bus_err; int done; }; static int tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc) { int timeout; uint32_t reg; reg = RD4(sc, I2C_FIFO_CONTROL); reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH; WR4(sc, I2C_FIFO_CONTROL, reg); timeout = 10; while (timeout > 0) { reg = RD4(sc, I2C_FIFO_CONTROL); reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH; if (reg == 0) break; DELAY(10); } if (timeout <= 0) { device_printf(sc->dev, "FIFO flush timedout\n"); return (ETIMEDOUT); } return (0); } static void tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq) { int div; div = ((sc->core_freq / clk_freq) / 10) - 1; if ((sc->core_freq / (10 * (div + 1))) > clk_freq) div++; if (div > 65535) div = 65535; WR4(sc, I2C_CLK_DIVISOR, (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) | (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT)); } static void tegra_i2c_bus_clear(struct tegra_i2c_softc *sc) { int timeout; uint32_t reg, status; WR4(sc, I2C_BUS_CLEAR_CONFIG, I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) | I2C_BUS_CLEAR_CONFIG_BC_STOP_COND | I2C_BUS_CLEAR_CONFIG_BC_TERMINATE); WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); for (timeout = 1000; timeout > 0; timeout--) { if (RD4(sc, I2C_CONFIG_LOAD) == 0) break; DELAY(10); } if (timeout <= 0) device_printf(sc->dev, "config load timeouted\n"); reg = RD4(sc, I2C_BUS_CLEAR_CONFIG); reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE; WR4(sc, I2C_BUS_CLEAR_CONFIG,reg); for (timeout = 1000; timeout > 0; timeout--) { if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) & I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0) break; DELAY(10); } if (timeout <= 0) device_printf(sc->dev, "bus clear timeouted\n"); status = RD4(sc, I2C_BUS_CLEAR_STATUS); if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0) device_printf(sc->dev, "bus clear failed\n"); } static int tegra_i2c_hw_init(struct tegra_i2c_softc *sc) { int rv, timeout; /* Reset the core. */ rv = hwreset_assert(sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot assert reset\n"); return (rv); } DELAY(10); rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot clear reset\n"); return (rv); } WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | I2C_CNFG_DEBOUNCE_CNT(2)); tegra_i2c_setup_clk(sc, sc->bus_freq); WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) | I2C_FIFO_CONTROL_RX_FIFO_TRIG(0)); WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); for (timeout = 1000; timeout > 0; timeout--) { if (RD4(sc, I2C_CONFIG_LOAD) == 0) break; DELAY(10); } if (timeout <= 0) device_printf(sc->dev, "config load timeouted\n"); tegra_i2c_bus_clear(sc); return (0); } static int tegra_i2c_tx(struct tegra_i2c_softc *sc) { uint32_t reg; int cnt, i; if (sc->msg_idx >= sc->msg->len) panic("Invalid call to tegra_i2c_tx\n"); while(sc->msg_idx < sc->msg->len) { reg = RD4(sc, I2C_FIFO_STATUS); if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0) break; cnt = min(4, sc->msg->len - sc->msg_idx); reg = 0; for (i = 0; i < cnt; i++) { reg |= sc->msg->buf[sc->msg_idx] << (i * 8); sc->msg_idx++; } WR4(sc, I2C_TX_PACKET_FIFO, reg); } if (sc->msg_idx >= sc->msg->len) return (0); return (sc->msg->len - sc->msg_idx - 1); } static int tegra_i2c_rx(struct tegra_i2c_softc *sc) { uint32_t reg; int cnt, i; if (sc->msg_idx >= sc->msg->len) panic("Invalid call to tegra_i2c_rx\n"); while(sc->msg_idx < sc->msg->len) { reg = RD4(sc, I2C_FIFO_STATUS); if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0) break; cnt = min(4, sc->msg->len - sc->msg_idx); reg = RD4(sc, I2C_RX_FIFO); for (i = 0; i < cnt; i++) { sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF; sc->msg_idx++; } } if (sc->msg_idx >= sc->msg->len) return (0); return (sc->msg->len - sc->msg_idx - 1); } static void tegra_i2c_intr(void *arg) { struct tegra_i2c_softc *sc; uint32_t status, reg; int rv; sc = (struct tegra_i2c_softc *)arg; LOCK(sc); status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER); if (sc->msg == NULL) { /* Unexpected interrupt - disable FIFOs, clear reset. */ reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); reg &= ~I2C_INT_TFIFO_DATA_REQ; reg &= ~I2C_INT_RFIFO_DATA_REQ; WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); UNLOCK(sc); return; } if ((status & I2C_ERROR_MASK) != 0) { if (status & I2C_INT_NOACK) sc->bus_err = IIC_ENOACK; if (status & I2C_INT_ARB_LOST) sc->bus_err = IIC_EBUSERR; if ((status & I2C_INT_TFIFO_OVR) || (status & I2C_INT_RFIFO_UNF)) sc->bus_err = IIC_EBUSERR; sc->done = 1; } else if ((status & I2C_INT_RFIFO_DATA_REQ) && (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) { rv = tegra_i2c_rx(sc); if (rv == 0) { reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); reg &= ~I2C_INT_RFIFO_DATA_REQ; WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); } } else if ((status & I2C_INT_TFIFO_DATA_REQ) && (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) { rv = tegra_i2c_tx(sc); if (rv == 0) { reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); reg &= ~I2C_INT_TFIFO_DATA_REQ; WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); } } else if ((status & I2C_INT_RFIFO_DATA_REQ) || (status & I2C_INT_TFIFO_DATA_REQ)) { device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n", status); reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); reg &= ~I2C_INT_TFIFO_DATA_REQ; reg &= ~I2C_INT_RFIFO_DATA_REQ; WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); } if (status & I2C_INT_PACKET_XFER_COMPLETE) sc->done = 1; WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); if (sc->done) { WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); wakeup(&(sc->done)); } UNLOCK(sc); } static void tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg, enum tegra_i2c_xfer_type xtype) { uint32_t tmp, mask; /* Packet header. */ tmp = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | PACKET_HEADER0_PROTOCOL_I2C | (1 << PACKET_HEADER0_CONT_ID_SHIFT) | (1 << PACKET_HEADER0_PACKET_ID_SHIFT); WR4(sc, I2C_TX_PACKET_FIFO, tmp); /* Packet size. */ WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1); /* I2C header. */ tmp = I2C_HEADER_IE_ENABLE; if (xtype == XFER_CONTINUE) tmp |= I2C_HEADER_CONTINUE_XFER; else if (xtype == XFER_REPEAT_START) tmp |= I2C_HEADER_REPEAT_START; tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT; if (msg->flags & IIC_M_RD) { tmp |= I2C_HEADER_READ; tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT; } else tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT); WR4(sc, I2C_TX_PACKET_FIFO, tmp); /* Interrupt mask. */ mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE; if (msg->flags & IIC_M_RD) mask |= I2C_INT_RFIFO_DATA_REQ; else mask |= I2C_INT_TFIFO_DATA_REQ; WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask); } static int tegra_i2c_poll(struct tegra_i2c_softc *sc) { int timeout; for(timeout = 10000; timeout > 0; timeout--) { UNLOCK(sc); tegra_i2c_intr(sc); LOCK(sc); if (sc->done != 0) break; DELAY(1); } if (timeout <= 0) return (ETIMEDOUT); return (0); } static int tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int rv, i; struct tegra_i2c_softc *sc; enum tegra_i2c_xfer_type xtype; sc = device_get_softc(dev); LOCK(sc); /* Get the bus. */ while (sc->bus_inuse == 1) SLEEP(sc, 0); sc->bus_inuse = 1; rv = 0; for (i = 0; i < nmsgs; i++) { sc->msg = &msgs[i]; sc->msg_idx = 0; sc->bus_err = 0; sc->done = 0; /* Check for valid parameters. */ if (sc->msg == NULL || sc->msg->buf == NULL || sc->msg->len == 0) { rv = EINVAL; break; } /* Get flags for next transfer. */ if (i == (nmsgs - 1)) { if (msgs[i].flags & IIC_M_NOSTOP) xtype = XFER_CONTINUE; else xtype = XFER_STOP; } else { if (msgs[i + 1].flags & IIC_M_NOSTART) xtype = XFER_CONTINUE; else xtype = XFER_REPEAT_START; } tegra_i2c_start_msg(sc, sc->msg, xtype); if (cold) rv = tegra_i2c_poll(sc); else rv = msleep(&sc->done, &sc->mtx, PZERO, "iic", I2C_REQUEST_TIMEOUT); WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); if (rv == 0) rv = sc->bus_err; if (rv != 0) break; } if (rv != 0) { tegra_i2c_hw_init(sc); tegra_i2c_flush_fifo(sc); } sc->msg = NULL; sc->msg_idx = 0; sc->bus_err = 0; sc->done = 0; /* Wake up the processes that are waiting for the bus. */ sc->bus_inuse = 0; wakeup(sc); UNLOCK(sc); return (rv); } static int tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct tegra_i2c_softc *sc; int busfreq; sc = device_get_softc(dev); busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); sc = device_get_softc(dev); LOCK(sc); tegra_i2c_setup_clk(sc, busfreq); UNLOCK(sc); return (0); } static int tegra_i2c_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int tegra_i2c_attach(device_t dev) { int rv, rid; phandle_t node; struct tegra_i2c_softc *sc; uint64_t freq; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); LOCK_INIT(sc); /* Get the memory resource for the register mapping. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot map registers.\n"); rv = ENXIO; goto fail; } /* Allocate our IRQ resource. */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate interrupt.\n"); rv = ENXIO; goto fail; } /* FDT resources. */ rv = clk_get_by_ofw_name(dev, 0, "div-clk", &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get i2c clock: %d\n", rv); goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, 0, "i2c", &sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot get i2c reset\n"); return (ENXIO); } rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq, sizeof(sc->bus_freq)); if (rv != sizeof(sc->bus_freq)) { sc->bus_freq = 100000; } /* Request maximum frequency for I2C block 136MHz (408MHz / 3). */ rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN); if (rv != 0) { device_printf(dev, "Cannot set clock frequency\n"); goto fail; } rv = clk_get_freq(sc->clk, &freq); if (rv != 0) { device_printf(dev, "Cannot get clock frequency\n"); goto fail; } sc->core_freq = (uint32_t)freq; rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock: %d\n", rv); goto fail; } /* Init hardware. */ rv = tegra_i2c_hw_init(sc); if (rv) { device_printf(dev, "tegra_i2c_activate failed\n"); goto fail; } /* Setup interrupt. */ rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, tegra_i2c_intr, sc, &sc->irq_h); if (rv) { device_printf(dev, "Cannot setup interrupt.\n"); goto fail; } /* Attach the iicbus. */ sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "Could not allocate iicbus instance.\n"); rv = ENXIO; goto fail; } /* Probe and attach the iicbus. */ return (bus_generic_attach(dev)); fail: if (sc->irq_h != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_h); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); return (rv); } static int tegra_i2c_detach(device_t dev) { struct tegra_i2c_softc *sc; sc = device_get_softc(dev); tegra_i2c_hw_init(sc); if (sc->irq_h != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_h); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); LOCK_DESTROY(sc); if (sc->iicbus) device_delete_child(dev, sc->iicbus); return (bus_generic_detach(dev)); } static phandle_t tegra_i2c_get_node(device_t bus, device_t dev) { /* Share controller node with iibus device. */ return (ofw_bus_get_node(bus)); } static device_method_t tegra_i2c_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_i2c_probe), DEVMETHOD(device_attach, tegra_i2c_attach), DEVMETHOD(device_detach, tegra_i2c_detach), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, tegra_i2c_get_node), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, tegra_i2c_iicbus_reset), DEVMETHOD(iicbus_transfer, tegra_i2c_transfer), DEVMETHOD_END }; static DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods, sizeof(struct tegra_i2c_softc)); EARLY_DRIVER_MODULE(tegra_iic, simplebus, tegra_i2c_driver, NULL, NULL, 73); diff --git a/sys/arm/nvidia/tegra_pcie.c b/sys/arm/nvidia/tegra_pcie.c index 82e282146d68..a22ae02ce4bc 100644 --- a/sys/arm/nvidia/tegra_pcie.c +++ b/sys/arm/nvidia/tegra_pcie.c @@ -1,1624 +1,1624 @@ /*- * 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 /* * Nvidia Integrated PCI/PCI-Express controller driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_bus_if.h" #include "msi_if.h" #include "pcib_if.h" #include "pic_if.h" #define AFI_AXI_BAR0_SZ 0x000 #define AFI_AXI_BAR1_SZ 0x004 #define AFI_AXI_BAR2_SZ 0x008 #define AFI_AXI_BAR3_SZ 0x00c #define AFI_AXI_BAR4_SZ 0x010 #define AFI_AXI_BAR5_SZ 0x014 #define AFI_AXI_BAR0_START 0x018 #define AFI_AXI_BAR1_START 0x01c #define AFI_AXI_BAR2_START 0x020 #define AFI_AXI_BAR3_START 0x024 #define AFI_AXI_BAR4_START 0x028 #define AFI_AXI_BAR5_START 0x02c #define AFI_FPCI_BAR0 0x030 #define AFI_FPCI_BAR1 0x034 #define AFI_FPCI_BAR2 0x038 #define AFI_FPCI_BAR3 0x03c #define AFI_FPCI_BAR4 0x040 #define AFI_FPCI_BAR5 0x044 #define AFI_MSI_BAR_SZ 0x060 #define AFI_MSI_FPCI_BAR_ST 0x064 #define AFI_MSI_AXI_BAR_ST 0x068 #define AFI_MSI_VEC(x) (0x06c + 4 * (x)) #define AFI_MSI_EN_VEC(x) (0x08c + 4 * (x)) #define AFI_MSI_INTR_IN_REG 32 #define AFI_MSI_REGS 8 #define AFI_CONFIGURATION 0x0ac #define AFI_CONFIGURATION_EN_FPCI (1 << 0) #define AFI_FPCI_ERROR_MASKS 0x0b0 #define AFI_INTR_MASK 0x0b4 #define AFI_INTR_MASK_MSI_MASK (1 << 8) #define AFI_INTR_MASK_INT_MASK (1 << 0) #define AFI_INTR_CODE 0x0b8 #define AFI_INTR_CODE_MASK 0xf #define AFI_INTR_CODE_INT_CODE_INI_SLVERR 1 #define AFI_INTR_CODE_INT_CODE_INI_DECERR 2 #define AFI_INTR_CODE_INT_CODE_TGT_SLVERR 3 #define AFI_INTR_CODE_INT_CODE_TGT_DECERR 4 #define AFI_INTR_CODE_INT_CODE_TGT_WRERR 5 #define AFI_INTR_CODE_INT_CODE_SM_MSG 6 #define AFI_INTR_CODE_INT_CODE_DFPCI_DECERR 7 #define AFI_INTR_CODE_INT_CODE_AXI_DECERR 8 #define AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT 9 #define AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE 10 #define AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE 11 #define AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE 12 #define AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE 13 #define AFI_INTR_CODE_INT_CODE_P2P_ERROR 14 #define AFI_INTR_SIGNATURE 0x0bc #define AFI_UPPER_FPCI_ADDRESS 0x0c0 #define AFI_SM_INTR_ENABLE 0x0c4 #define AFI_SM_INTR_RP_DEASSERT (1 << 14) #define AFI_SM_INTR_RP_ASSERT (1 << 13) #define AFI_SM_INTR_HOTPLUG (1 << 12) #define AFI_SM_INTR_PME (1 << 11) #define AFI_SM_INTR_FATAL_ERROR (1 << 10) #define AFI_SM_INTR_UNCORR_ERROR (1 << 9) #define AFI_SM_INTR_CORR_ERROR (1 << 8) #define AFI_SM_INTR_INTD_DEASSERT (1 << 7) #define AFI_SM_INTR_INTC_DEASSERT (1 << 6) #define AFI_SM_INTR_INTB_DEASSERT (1 << 5) #define AFI_SM_INTR_INTA_DEASSERT (1 << 4) #define AFI_SM_INTR_INTD_ASSERT (1 << 3) #define AFI_SM_INTR_INTC_ASSERT (1 << 2) #define AFI_SM_INTR_INTB_ASSERT (1 << 1) #define AFI_SM_INTR_INTA_ASSERT (1 << 0) #define AFI_AFI_INTR_ENABLE 0x0c8 #define AFI_AFI_INTR_ENABLE_CODE(code) (1 << (code)) #define AFI_PCIE_CONFIG 0x0f8 #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0x6 #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1 (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1 (0x1 << 20) #define AFI_FUSE 0x104 #define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) #define AFI_PEX0_CTRL 0x110 #define AFI_PEX1_CTRL 0x118 #define AFI_PEX2_CTRL 0x128 #define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) #define AFI_PEX_CTRL_REFCLK_EN (1 << 3) #define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) #define AFI_PEX_CTRL_RST_L (1 << 0) #define AFI_AXI_BAR6_SZ 0x134 #define AFI_AXI_BAR7_SZ 0x138 #define AFI_AXI_BAR8_SZ 0x13c #define AFI_AXI_BAR6_START 0x140 #define AFI_AXI_BAR7_START 0x144 #define AFI_AXI_BAR8_START 0x148 #define AFI_FPCI_BAR6 0x14c #define AFI_FPCI_BAR7 0x150 #define AFI_FPCI_BAR8 0x154 #define AFI_PLLE_CONTROL 0x160 #define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) #define AFI_PLLE_CONTROL_BYPASS_PCIE2PLLE_CONTROL (1 << 8) #define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) #define AFI_PLLE_CONTROL_PCIE2PLLE_CONTROL_EN (1 << 0) #define AFI_PEXBIAS_CTRL 0x168 /* Configuration space */ #define RP_VEND_XP 0x0F00 #define RP_VEND_XP_DL_UP (1 << 30) #define RP_VEND_CTL2 0x0fa8 #define RP_VEND_CTL2_PCA_ENABLE (1 << 7) #define RP_PRIV_MISC 0x0FE0 #define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) #define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) #define RP_LINK_CONTROL_STATUS 0x0090 #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 #define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 /* PADS space */ #define PADS_REFCLK_CFG0 0x000c8 #define PADS_REFCLK_CFG1 0x000cc /* Wait 50 ms (per port) for link. */ #define TEGRA_PCIE_LINKUP_TIMEOUT 50000 /* FPCI Address space */ #define FPCI_MAP_IO 0xFDFC000000ULL #define FPCI_MAP_TYPE0_CONFIG 0xFDFC000000ULL #define FPCI_MAP_TYPE1_CONFIG 0xFDFF000000ULL #define FPCI_MAP_EXT_TYPE0_CONFIG 0xFE00000000ULL #define FPCI_MAP_EXT_TYPE1_CONFIG 0xFE10000000ULL #define TEGRA_PCIB_MSI_ENABLE #define DEBUG #ifdef DEBUG #define debugf(fmt, args...) do { printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif /* * Configuration space format: * [27:24] extended register * [23:16] bus * [15:11] slot (device) * [10: 8] function * [ 7: 0] register */ #define PCI_CFG_EXT_REG(reg) ((((reg) >> 8) & 0x0f) << 24) #define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) #define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) #define PCI_CFG_FUN(fun) (((fun) & 0x07) << 8) #define PCI_CFG_BASE_REG(reg) ((reg) & 0xff) #define PADS_WR4(_sc, _r, _v) bus_write_4((_sc)->pads_mem_res, (_r), (_v)) #define PADS_RD4(_sc, _r) bus_read_4((_sc)->pads_mem_res, (_r)) #define AFI_WR4(_sc, _r, _v) bus_write_4((_sc)->afi_mem_res, (_r), (_v)) #define AFI_RD4(_sc, _r) bus_read_4((_sc)->afi_mem_res, (_r)) static struct { bus_size_t axi_start; bus_size_t fpci_start; bus_size_t size; } bars[] = { {AFI_AXI_BAR0_START, AFI_FPCI_BAR0, AFI_AXI_BAR0_SZ}, /* BAR 0 */ {AFI_AXI_BAR1_START, AFI_FPCI_BAR1, AFI_AXI_BAR1_SZ}, /* BAR 1 */ {AFI_AXI_BAR2_START, AFI_FPCI_BAR2, AFI_AXI_BAR2_SZ}, /* BAR 2 */ {AFI_AXI_BAR3_START, AFI_FPCI_BAR3, AFI_AXI_BAR3_SZ}, /* BAR 3 */ {AFI_AXI_BAR4_START, AFI_FPCI_BAR4, AFI_AXI_BAR4_SZ}, /* BAR 4 */ {AFI_AXI_BAR5_START, AFI_FPCI_BAR5, AFI_AXI_BAR5_SZ}, /* BAR 5 */ {AFI_AXI_BAR6_START, AFI_FPCI_BAR6, AFI_AXI_BAR6_SZ}, /* BAR 6 */ {AFI_AXI_BAR7_START, AFI_FPCI_BAR7, AFI_AXI_BAR7_SZ}, /* BAR 7 */ {AFI_AXI_BAR8_START, AFI_FPCI_BAR8, AFI_AXI_BAR8_SZ}, /* BAR 8 */ {AFI_MSI_AXI_BAR_ST, AFI_MSI_FPCI_BAR_ST, AFI_MSI_BAR_SZ}, /* MSI 9 */ }; struct pcie_soc { char **regulator_names; bool cml_clk; bool pca_enable; uint32_t pads_refclk_cfg0; uint32_t pads_refclk_cfg1; }; /* Tegra 124 config. */ static char *tegra124_reg_names[] = { "avddio-pex-supply", "dvddio-pex-supply", "avdd-pex-pll-supply", "hvdd-pex-supply", "hvdd-pex-pll-e-supply", "vddio-pex-ctl-supply", "avdd-pll-erefe-supply", NULL }; static struct pcie_soc tegra124_soc = { .regulator_names = tegra124_reg_names, .cml_clk = true, .pca_enable = false, .pads_refclk_cfg0 = 0x44ac44ac, }; /* Tegra 210 config. */ static char *tegra210_reg_names[] = { "avdd-pll-uerefe-supply", "hvddio-pex-supply", "dvddio-pex-supply", "dvdd-pex-pll-supply", "hvdd-pex-pll-e-supply", "vddio-pex-ctl-supply", NULL }; static struct pcie_soc tegra210_soc = { .regulator_names = tegra210_reg_names, .cml_clk = true, .pca_enable = true, .pads_refclk_cfg0 = 0x90b890b8, }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-pcie", (uintptr_t)&tegra124_soc}, {"nvidia,tegra210-pcie", (uintptr_t)&tegra210_soc}, {NULL, 0}, }; #define TEGRA_FLAG_MSI_USED 0x0001 struct tegra_pcib_irqsrc { struct intr_irqsrc isrc; u_int irq; u_int flags; }; struct tegra_pcib_port { int enabled; int port_idx; /* chip port index */ int num_lanes; /* number of lanes */ bus_size_t afi_pex_ctrl; /* offset of afi_pex_ctrl */ phy_t phy; /* port phy */ /* Config space properties. */ bus_addr_t rp_base_addr; /* PA of config window */ bus_size_t rp_size; /* size of config window */ bus_space_handle_t cfg_handle; /* handle of config window */ }; #define TEGRA_PCIB_MAX_PORTS 3 #define TEGRA_PCIB_MAX_MSI AFI_MSI_INTR_IN_REG * AFI_MSI_REGS struct tegra_pcib_softc { struct ofw_pci_softc ofw_pci; device_t dev; struct pcie_soc *soc; struct mtx mtx; struct resource *pads_mem_res; struct resource *afi_mem_res; struct resource *cfg_mem_res; struct resource *irq_res; struct resource *msi_irq_res; void *intr_cookie; void *msi_intr_cookie; struct ofw_pci_range mem_range; struct ofw_pci_range pref_mem_range; struct ofw_pci_range io_range; clk_t clk_pex; clk_t clk_afi; clk_t clk_pll_e; clk_t clk_cml; hwreset_t hwreset_pex; hwreset_t hwreset_afi; hwreset_t hwreset_pcie_x; regulator_t regulators[16]; /* Safe maximum */ vm_offset_t msi_page; /* VA of MSI page */ bus_addr_t cfg_base_addr; /* base address of config */ bus_size_t cfg_cur_offs; /* currently mapped window */ bus_space_handle_t cfg_handle; /* handle of config window */ bus_space_tag_t bus_tag; /* tag of config window */ int lanes_cfg; int num_ports; struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS]; struct tegra_pcib_irqsrc *isrcs; }; static int tegra_pcib_maxslots(device_t dev) { return (16); } static int tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin) { struct tegra_pcib_softc *sc; u_int irq; sc = device_get_softc(bus); irq = intr_map_clone_irq(rman_get_start(sc->irq_res)); device_printf(bus, "route pin %d for device %d.%d to %u\n", pin, pci_get_slot(dev), pci_get_function(dev), irq); return (irq); } static int tegra_pcbib_map_cfg(struct tegra_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { bus_size_t offs; int flags, rv; offs = sc->cfg_base_addr; offs |= PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | PCI_CFG_FUN(func) | PCI_CFG_EXT_REG(reg); if ((sc->cfg_handle != 0) && (sc->cfg_cur_offs == offs)) return (0); if (sc->cfg_handle != 0) bus_space_unmap(sc->bus_tag, sc->cfg_handle, 0x800); #if defined(BUS_SPACE_MAP_NONPOSTED) flags = BUS_SPACE_MAP_NONPOSTED; #else flags = 0; #endif rv = bus_space_map(sc->bus_tag, offs, 0x800, flags, &sc->cfg_handle); if (rv != 0) device_printf(sc->dev, "Cannot map config space\n"); else sc->cfg_cur_offs = offs; return (rv); } static uint32_t tegra_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct tegra_pcib_softc *sc; bus_space_handle_t hndl; uint32_t off; uint32_t val; int rv, i; sc = device_get_softc(dev); if (bus == 0) { if (func != 0) return (0xFFFFFFFF); for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if ((sc->ports[i] != NULL) && (sc->ports[i]->port_idx == slot)) { hndl = sc->ports[i]->cfg_handle; off = reg & 0xFFF; break; } } if (i >= TEGRA_PCIB_MAX_PORTS) return (0xFFFFFFFF); } else { rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); if (rv != 0) return (0xFFFFFFFF); hndl = sc->cfg_handle; off = PCI_CFG_BASE_REG(reg); } val = bus_space_read_4(sc->bus_tag, hndl, off & ~3); switch (bytes) { case 4: break; case 2: if (off & 3) val >>= 16; val &= 0xffff; break; case 1: val >>= ((off & 3) << 3); val &= 0xff; break; } return val; } static void tegra_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct tegra_pcib_softc *sc; bus_space_handle_t hndl; uint32_t off; uint32_t val2; int rv, i; sc = device_get_softc(dev); if (bus == 0) { if (func != 0) return; for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if ((sc->ports[i] != NULL) && (sc->ports[i]->port_idx == slot)) { hndl = sc->ports[i]->cfg_handle; off = reg & 0xFFF; break; } } if (i >= TEGRA_PCIB_MAX_PORTS) return; } else { rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); if (rv != 0) return; hndl = sc->cfg_handle; off = PCI_CFG_BASE_REG(reg); } switch (bytes) { case 4: bus_space_write_4(sc->bus_tag, hndl, off, val); break; case 2: val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); val2 &= ~(0xffff << ((off & 3) << 3)); val2 |= ((val & 0xffff) << ((off & 3) << 3)); bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); break; case 1: val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); val2 &= ~(0xff << ((off & 3) << 3)); val2 |= ((val & 0xff) << ((off & 3) << 3)); bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); break; } } static int tegra_pci_intr(void *arg) { struct tegra_pcib_softc *sc = arg; uint32_t code, signature; code = bus_read_4(sc->afi_mem_res, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; signature = bus_read_4(sc->afi_mem_res, AFI_INTR_SIGNATURE); bus_write_4(sc->afi_mem_res, AFI_INTR_CODE, 0); if (code == AFI_INTR_CODE_INT_CODE_SM_MSG) return(FILTER_STRAY); printf("tegra_pci_intr: code %x sig %x\n", code, signature); return (FILTER_HANDLED); } /* ----------------------------------------------------------------------- * * PCI MSI interface */ static int tegra_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { phandle_t msi_parent; /* XXXX ofw_bus_msimap() don't works for Tegra DT. ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); */ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, irqs)); } static int tegra_pcib_release_msi(device_t pci, device_t child, int count, int *irqs) { phandle_t msi_parent; /* XXXX ofw_bus_msimap() don't works for Tegra DT. ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); */ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); return (intr_release_msi(pci, child, msi_parent, count, irqs)); } static int tegra_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { phandle_t msi_parent; /* XXXX ofw_bus_msimap() don't works for Tegra DT. ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); */ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); } #ifdef TEGRA_PCIB_MSI_ENABLE /* -------------------------------------------------------------------------- * * Interrupts * */ static inline void tegra_pcib_isrc_mask(struct tegra_pcib_softc *sc, struct tegra_pcib_irqsrc *tgi, uint32_t val) { uint32_t reg; int offs, bit; offs = tgi->irq / AFI_MSI_INTR_IN_REG; bit = 1 << (tgi->irq % AFI_MSI_INTR_IN_REG); if (val != 0) AFI_WR4(sc, AFI_MSI_VEC(offs), bit); reg = AFI_RD4(sc, AFI_MSI_EN_VEC(offs)); if (val != 0) reg |= bit; else reg &= ~bit; AFI_WR4(sc, AFI_MSI_EN_VEC(offs), reg); } static int tegra_pcib_msi_intr(void *arg) { u_int irq, i, bit, reg; struct tegra_pcib_softc *sc; struct trapframe *tf; struct tegra_pcib_irqsrc *tgi; sc = (struct tegra_pcib_softc *)arg; tf = curthread->td_intr_frame; for (i = 0; i < AFI_MSI_REGS; i++) { reg = AFI_RD4(sc, AFI_MSI_VEC(i)); /* Handle one vector. */ while (reg != 0) { bit = ffs(reg) - 1; /* Send EOI */ AFI_WR4(sc, AFI_MSI_VEC(i), 1 << bit); irq = i * AFI_MSI_INTR_IN_REG + bit; tgi = &sc->isrcs[irq]; if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) { /* Disable stray. */ tegra_pcib_isrc_mask(sc, tgi, 0); device_printf(sc->dev, "Stray irq %u disabled\n", irq); } reg = AFI_RD4(sc, AFI_MSI_VEC(i)); } } return (FILTER_HANDLED); } static int tegra_pcib_msi_attach(struct tegra_pcib_softc *sc) { int error; uint32_t irq; const char *name; sc->isrcs = malloc(sizeof(*sc->isrcs) * TEGRA_PCIB_MAX_MSI, M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->dev); for (irq = 0; irq < TEGRA_PCIB_MAX_MSI; irq++) { sc->isrcs[irq].irq = irq; error = intr_isrc_register(&sc->isrcs[irq].isrc, sc->dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } if (intr_msi_register(sc->dev, OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0) return (ENXIO); return (0); } static int tegra_pcib_msi_detach(struct tegra_pcib_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void tegra_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_pcib_softc *sc; struct tegra_pcib_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_pcib_irqsrc *)isrc; tegra_pcib_isrc_mask(sc, tgi, 0); } static void tegra_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct tegra_pcib_softc *sc; struct tegra_pcib_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_pcib_irqsrc *)isrc; tegra_pcib_isrc_mask(sc, tgi, 1); } /* MSI interrupts are edge trigered -> do nothing */ static void tegra_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static void tegra_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc) { } static void tegra_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { } static int tegra_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { if (data == NULL || data->type != INTR_MAP_DATA_MSI) return (ENOTSUP); if (isrc->isrc_handlers == 0) tegra_pcib_msi_enable_intr(dev, isrc); return (0); } static int tegra_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct tegra_pcib_softc *sc; struct tegra_pcib_irqsrc *tgi; sc = device_get_softc(dev); tgi = (struct tegra_pcib_irqsrc *)isrc; if (isrc->isrc_handlers == 0) tegra_pcib_isrc_mask(sc, tgi, 0); return (0); } static int tegra_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount, device_t *pic, struct intr_irqsrc **srcs) { struct tegra_pcib_softc *sc; int i, irq, end_irq; bool found; KASSERT(powerof2(count), ("%s: bad count", __func__)); KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); sc = device_get_softc(dev); mtx_lock(&sc->mtx); found = false; for (irq = 0; (irq + count - 1) < TEGRA_PCIB_MAX_MSI; irq++) { /* Start on an aligned interrupt */ if ((irq & (maxcount - 1)) != 0) continue; /* Assume we found a valid range until shown otherwise */ found = true; /* Check this range is valid */ for (end_irq = irq; end_irq < irq + count; end_irq++) { /* This is already used */ if ((sc->isrcs[end_irq].flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED) { found = false; break; } } if (found) break; } /* Not enough interrupts were found */ if (!found || irq == (TEGRA_PCIB_MAX_MSI - 1)) { mtx_unlock(&sc->mtx); return (ENXIO); } for (i = 0; i < count; i++) { /* Mark the interrupt as used */ sc->isrcs[irq + i].flags |= TEGRA_FLAG_MSI_USED; } mtx_unlock(&sc->mtx); for (i = 0; i < count; i++) srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i]; *pic = device_get_parent(dev); return (0); } static int tegra_pcib_msi_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **isrc) { struct tegra_pcib_softc *sc; struct tegra_pcib_irqsrc *ti; int i; sc = device_get_softc(dev); mtx_lock(&sc->mtx); for (i = 0; i < count; i++) { ti = (struct tegra_pcib_irqsrc *)isrc[i]; KASSERT((ti->flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED, ("%s: Trying to release an unused MSI-X interrupt", __func__)); ti->flags &= ~TEGRA_FLAG_MSI_USED; } mtx_unlock(&sc->mtx); return (0); } static int tegra_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, uint64_t *addr, uint32_t *data) { struct tegra_pcib_softc *sc = device_get_softc(dev); struct tegra_pcib_irqsrc *ti = (struct tegra_pcib_irqsrc *)isrc; *addr = vtophys(sc->msi_page); *data = ti->irq; return (0); } #endif /* ------------------------------------------------------------------- */ static bus_size_t tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port) { switch (port) { case 0: return (AFI_PEX0_CTRL); case 1: return (AFI_PEX1_CTRL); case 2: return (AFI_PEX2_CTRL); default: panic("invalid port number: %d\n", port); } } static int tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc) { int i, rv; rv = hwreset_assert(sc->hwreset_pcie_x); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'pcie_x' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_afi); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'afi' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_pex); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'pex' reset\n"); return (rv); } tegra_powergate_power_off(TEGRA_POWERGATE_PCX); /* 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); } } rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCX, sc->clk_pex, sc->hwreset_pex); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'PCX' powergate\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_afi); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'afi' reset\n"); return (rv); } rv = clk_enable(sc->clk_afi); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'afi' clock\n"); return (rv); } if (sc->soc->cml_clk) { rv = clk_enable(sc->clk_cml); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'cml' clock\n"); return (rv); } } rv = clk_enable(sc->clk_pll_e); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pll_e' clock\n"); return (rv); } return (0); } static struct tegra_pcib_port * tegra_pcib_parse_port(struct tegra_pcib_softc *sc, phandle_t node) { struct tegra_pcib_port *port; uint32_t tmp[5]; char tmpstr[6]; int rv; port = malloc(sizeof(struct tegra_pcib_port), M_DEVBUF, M_WAITOK); rv = OF_getprop(node, "status", tmpstr, sizeof(tmpstr)); if (rv <= 0 || strcmp(tmpstr, "okay") == 0 || strcmp(tmpstr, "ok") == 0) port->enabled = 1; else port->enabled = 0; rv = OF_getencprop(node, "assigned-addresses", tmp, sizeof(tmp)); if (rv != sizeof(tmp)) { device_printf(sc->dev, "Cannot parse assigned-address: %d\n", rv); goto fail; } port->rp_base_addr = tmp[2]; port->rp_size = tmp[4]; port->port_idx = OFW_PCI_PHYS_HI_DEVICE(tmp[0]) - 1; if (port->port_idx >= TEGRA_PCIB_MAX_PORTS) { device_printf(sc->dev, "Invalid port index: %d\n", port->port_idx); goto fail; } /* XXX - TODO: * Implement proper function for parsing pci "reg" property: * - it have PCI bus format * - its relative to matching "assigned-addresses" */ rv = OF_getencprop(node, "reg", tmp, sizeof(tmp)); if (rv != sizeof(tmp)) { device_printf(sc->dev, "Cannot parse reg: %d\n", rv); goto fail; } port->rp_base_addr += tmp[2]; rv = OF_getencprop(node, "nvidia,num-lanes", &port->num_lanes, sizeof(port->num_lanes)); if (rv != sizeof(port->num_lanes)) { device_printf(sc->dev, "Cannot parse nvidia,num-lanes: %d\n", rv); goto fail; } if (port->num_lanes > 4) { device_printf(sc->dev, "Invalid nvidia,num-lanes: %d\n", port->num_lanes); goto fail; } port->afi_pex_ctrl = tegra_pcib_pex_ctrl(sc, port->port_idx); sc->lanes_cfg |= port->num_lanes << (4 * port->port_idx); /* Phy. */ rv = phy_get_by_ofw_name(sc->dev, node, "pcie-0", &port->phy); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pcie-0' phy for port %d\n", port->port_idx); goto fail; } return (port); fail: free(port, M_DEVBUF); return (NULL); } static int tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node) { phandle_t child; struct tegra_pcib_port *port; 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, "pex", &sc->hwreset_pex); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pex' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "afi", &sc->hwreset_afi); if (rv != 0) { device_printf(sc->dev, "Cannot get 'afi' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "pcie_x", &sc->hwreset_pcie_x); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pcie_x' reset\n"); return (ENXIO); } /* Clocks. */ rv = clk_get_by_ofw_name(sc->dev, 0, "pex", &sc->clk_pex); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pex' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "afi", &sc->clk_afi); if (rv != 0) { device_printf(sc->dev, "Cannot get 'afi' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pll_e' clock\n"); return (ENXIO); } if (sc->soc->cml_clk) { rv = clk_get_by_ofw_name(sc->dev, 0, "cml", &sc->clk_cml); if (rv != 0) { device_printf(sc->dev, "Cannot get 'cml' clock\n"); return (ENXIO); } } /* Ports */ sc->num_ports = 0; for (child = OF_child(node); child != 0; child = OF_peer(child)) { port = tegra_pcib_parse_port(sc, child); if (port == NULL) { device_printf(sc->dev, "Cannot parse PCIe port node\n"); return (ENXIO); } sc->ports[sc->num_ports++] = port; } return (0); } static int tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc, struct ofw_pci_range *ranges, int nranges) { int i; for (i = 2; i < nranges; i++) { if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == OFW_PCI_PHYS_HI_SPACE_IO) { if (sc->io_range.size != 0) { device_printf(sc->dev, "Duplicated IO range found in DT\n"); return (ENXIO); } sc->io_range = ranges[i]; } if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == OFW_PCI_PHYS_HI_SPACE_MEM32)) { if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) { if (sc->pref_mem_range.size != 0) { device_printf(sc->dev, "Duplicated memory range found " "in DT\n"); return (ENXIO); } sc->pref_mem_range = ranges[i]; } else { if (sc->mem_range.size != 0) { device_printf(sc->dev, "Duplicated memory range found " "in DT\n"); return (ENXIO); } sc->mem_range = ranges[i]; } } } if ((sc->io_range.size == 0) || (sc->mem_range.size == 0) || (sc->pref_mem_range.size == 0)) { device_printf(sc->dev, " Not all required ranges are found in DT\n"); return (ENXIO); } return (0); } /* * Hardware config. */ static int tegra_pcib_wait_for_link(struct tegra_pcib_softc *sc, struct tegra_pcib_port *port) { uint32_t reg; int i; /* Setup link detection. */ reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, RP_PRIV_MISC, 4); reg &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; reg |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0, RP_PRIV_MISC, reg, 4); for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, RP_VEND_XP, 4); if (reg & RP_VEND_XP_DL_UP) break; DELAY(1); } if (i <= 0) return (ETIMEDOUT); for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, RP_LINK_CONTROL_STATUS, 4); if (reg & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) break; DELAY(1); } if (i <= 0) return (ETIMEDOUT); return (0); } static void tegra_pcib_port_enable(struct tegra_pcib_softc *sc, int port_num) { struct tegra_pcib_port *port; uint32_t reg; int rv; port = sc->ports[port_num]; /* Put port to reset. */ reg = AFI_RD4(sc, port->afi_pex_ctrl); reg &= ~AFI_PEX_CTRL_RST_L; AFI_WR4(sc, port->afi_pex_ctrl, reg); AFI_RD4(sc, port->afi_pex_ctrl); DELAY(10); /* Enable clocks. */ reg |= AFI_PEX_CTRL_REFCLK_EN; reg |= AFI_PEX_CTRL_CLKREQ_EN; reg |= AFI_PEX_CTRL_OVERRIDE_EN; AFI_WR4(sc, port->afi_pex_ctrl, reg); AFI_RD4(sc, port->afi_pex_ctrl); DELAY(100); /* Release reset. */ reg |= AFI_PEX_CTRL_RST_L; AFI_WR4(sc, port->afi_pex_ctrl, reg); if (sc->soc->pca_enable) { reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, RP_VEND_CTL2, 4); reg |= RP_VEND_CTL2_PCA_ENABLE; tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0, RP_VEND_CTL2, reg, 4); } rv = tegra_pcib_wait_for_link(sc, port); if (bootverbose) device_printf(sc->dev, " port %d (%d lane%s): Link is %s\n", port->port_idx, port->num_lanes, port->num_lanes > 1 ? "s": "", rv == 0 ? "up": "down"); } static void tegra_pcib_port_disable(struct tegra_pcib_softc *sc, uint32_t port_num) { struct tegra_pcib_port *port; uint32_t reg; port = sc->ports[port_num]; /* Put port to reset. */ reg = AFI_RD4(sc, port->afi_pex_ctrl); reg &= ~AFI_PEX_CTRL_RST_L; AFI_WR4(sc, port->afi_pex_ctrl, reg); AFI_RD4(sc, port->afi_pex_ctrl); DELAY(10); /* Disable clocks. */ reg &= ~AFI_PEX_CTRL_CLKREQ_EN; reg &= ~AFI_PEX_CTRL_REFCLK_EN; AFI_WR4(sc, port->afi_pex_ctrl, reg); if (bootverbose) device_printf(sc->dev, " port %d (%d lane%s): Disabled\n", port->port_idx, port->num_lanes, port->num_lanes > 1 ? "s": ""); } static void tegra_pcib_set_bar(struct tegra_pcib_softc *sc, int bar, uint32_t axi, uint64_t fpci, uint32_t size, int is_memory) { uint32_t fpci_reg; uint32_t axi_reg; uint32_t size_reg; axi_reg = axi & ~0xFFF; size_reg = size >> 12; fpci_reg = (uint32_t)(fpci >> 8) & ~0xF; fpci_reg |= is_memory ? 0x1 : 0x0; AFI_WR4(sc, bars[bar].axi_start, axi_reg); AFI_WR4(sc, bars[bar].size, size_reg); AFI_WR4(sc, bars[bar].fpci_start, fpci_reg); } static int tegra_pcib_enable(struct tegra_pcib_softc *sc) { int rv; int i; uint32_t reg; rv = tegra_pcib_enable_fdt_resources(sc); if (rv != 0) { device_printf(sc->dev, "Cannot enable FDT resources\n"); return (rv); } /* Enable PLLE control. */ reg = AFI_RD4(sc, AFI_PLLE_CONTROL); reg &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; reg |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; AFI_WR4(sc, AFI_PLLE_CONTROL, reg); /* Set bias pad. */ AFI_WR4(sc, AFI_PEXBIAS_CTRL, 0); /* Configure mode and ports. */ reg = AFI_RD4(sc, AFI_PCIE_CONFIG); reg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; if (sc->lanes_cfg == 0x14) { if (bootverbose) device_printf(sc->dev, "Using x1,x4 configuration\n"); reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1; } else if (sc->lanes_cfg == 0x12) { if (bootverbose) device_printf(sc->dev, "Using x1,x2 configuration\n"); reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1; } else { device_printf(sc->dev, "Unsupported lanes configuration: 0x%X\n", sc->lanes_cfg); } reg |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL; for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if ((sc->ports[i] != NULL)) reg &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(sc->ports[i]->port_idx); } AFI_WR4(sc, AFI_PCIE_CONFIG, reg); /* Enable Gen2 support. */ reg = AFI_RD4(sc, AFI_FUSE); reg &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; AFI_WR4(sc, AFI_FUSE, reg); for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if (sc->ports[i] != NULL) { rv = phy_enable(sc->ports[i]->phy); if (rv != 0) { device_printf(sc->dev, "Cannot enable phy for port %d\n", sc->ports[i]->port_idx); return (rv); } } } /* Configure PCIe reference clock */ PADS_WR4(sc, PADS_REFCLK_CFG0, sc->soc->pads_refclk_cfg0); if (sc->num_ports > 2) PADS_WR4(sc, PADS_REFCLK_CFG1, sc->soc->pads_refclk_cfg1); rv = hwreset_deassert(sc->hwreset_pcie_x); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'pci_x' reset\n"); return (rv); } /* Enable config space. */ reg = AFI_RD4(sc, AFI_CONFIGURATION); reg |= AFI_CONFIGURATION_EN_FPCI; AFI_WR4(sc, AFI_CONFIGURATION, reg); /* Enable AFI errors. */ reg = 0; reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_SLVERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_DECERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_SLVERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_DECERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_WRERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_SM_MSG); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_DFPCI_DECERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_AXI_DECERR); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE); reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_P2P_ERROR); AFI_WR4(sc, AFI_AFI_INTR_ENABLE, reg); AFI_WR4(sc, AFI_SM_INTR_ENABLE, 0xffffffff); /* Enable INT, disable MSI. */ AFI_WR4(sc, AFI_INTR_MASK, AFI_INTR_MASK_INT_MASK); /* Mask all FPCI errors. */ AFI_WR4(sc, AFI_FPCI_ERROR_MASKS, 0); /* Setup AFI translation windows. */ /* BAR 0 - type 1 extended configuration. */ tegra_pcib_set_bar(sc, 0, rman_get_start(sc->cfg_mem_res), FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0); /* BAR 1 - downstream I/O. */ tegra_pcib_set_bar(sc, 1, sc->io_range.host, FPCI_MAP_IO, sc->io_range.size, 0); /* BAR 2 - downstream prefetchable memory 1:1. */ tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host, sc->pref_mem_range.host, sc->pref_mem_range.size, 1); /* BAR 3 - downstream not prefetchable memory 1:1 .*/ tegra_pcib_set_bar(sc, 3, sc->mem_range.host, sc->mem_range.host, sc->mem_range.size, 1); /* BAR 3-8 clear. */ tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0); tegra_pcib_set_bar(sc, 5, 0, 0, 0, 0); tegra_pcib_set_bar(sc, 6, 0, 0, 0, 0); tegra_pcib_set_bar(sc, 7, 0, 0, 0, 0); tegra_pcib_set_bar(sc, 8, 0, 0, 0, 0); /* MSI BAR - clear. */ tegra_pcib_set_bar(sc, 9, 0, 0, 0, 0); return(0); } #ifdef TEGRA_PCIB_MSI_ENABLE static int tegra_pcib_attach_msi(device_t dev) { struct tegra_pcib_softc *sc; uint32_t reg; int i, rv; sc = device_get_softc(dev); sc->msi_page = (uintptr_t)kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); /* MSI BAR */ tegra_pcib_set_bar(sc, 9, vtophys(sc->msi_page), vtophys(sc->msi_page), PAGE_SIZE, 0); /* Disable and clear all interrupts. */ for (i = 0; i < AFI_MSI_REGS; i++) { AFI_WR4(sc, AFI_MSI_EN_VEC(i), 0); AFI_WR4(sc, AFI_MSI_VEC(i), 0xFFFFFFFF); } rv = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, tegra_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie); if (rv != 0) { device_printf(dev, "cannot setup MSI interrupt handler\n"); rv = ENXIO; goto out; } if (tegra_pcib_msi_attach(sc) != 0) { device_printf(dev, "WARNING: unable to attach PIC\n"); tegra_pcib_msi_detach(sc); goto out; } /* Unmask MSI interrupt. */ reg = AFI_RD4(sc, AFI_INTR_MASK); reg |= AFI_INTR_MASK_MSI_MASK; AFI_WR4(sc, AFI_INTR_MASK, reg); out: return (rv); } #endif static int tegra_pcib_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Nvidia Integrated PCI/PCI-E Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int tegra_pcib_attach(device_t dev) { struct tegra_pcib_softc *sc; phandle_t node; int rv; int rid; struct tegra_pcib_port *port; int i; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF); node = ofw_bus_get_node(dev); sc->soc = (struct pcie_soc *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; rv = tegra_pcib_parse_fdt_resources(sc, node); if (rv != 0) { device_printf(dev, "Cannot get FDT resources\n"); return (rv); } /* Allocate bus_space resources. */ rid = 0; sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->pads_mem_res == NULL) { device_printf(dev, "Cannot allocate PADS register\n"); rv = ENXIO; goto out; } /* * XXX - FIXME * tag for config space is not filled when RF_ALLOCATED flag is used. */ sc->bus_tag = rman_get_bustag(sc->pads_mem_res); rid = 1; sc->afi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->afi_mem_res == NULL) { device_printf(dev, "Cannot allocate AFI register\n"); rv = ENXIO; goto out; } rid = 2; sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ALLOCATED); if (sc->cfg_mem_res == NULL) { device_printf(dev, "Cannot allocate config space memory\n"); rv = ENXIO; goto out; } sc->cfg_base_addr = rman_get_start(sc->cfg_mem_res); /* Map RP slots */ for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if (sc->ports[i] == NULL) continue; port = sc->ports[i]; rv = bus_space_map(sc->bus_tag, port->rp_base_addr, port->rp_size, 0, &port->cfg_handle); if (rv != 0) { device_printf(sc->dev, "Cannot allocate memory for " "port: %d\n", i); rv = ENXIO; goto out; } } /* * Get PCI interrupt */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); rv = ENXIO; goto out; } rid = 1; sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate MSI IRQ resources\n"); rv = ENXIO; goto out; } sc->ofw_pci.sc_range_mask = 0x3; rv = ofw_pcib_init(dev); if (rv != 0) goto out; rv = tegra_pcib_decode_ranges(sc, sc->ofw_pci.sc_range, sc->ofw_pci.sc_nrange); if (rv != 0) goto out; if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, tegra_pci_intr, NULL, sc, &sc->intr_cookie)) { device_printf(dev, "cannot setup interrupt handler\n"); rv = ENXIO; goto out; } /* * Enable PCIE device. */ rv = tegra_pcib_enable(sc); if (rv != 0) goto out; for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { if (sc->ports[i] == NULL) continue; if (sc->ports[i]->enabled) tegra_pcib_port_enable(sc, i); else tegra_pcib_port_disable(sc, i); } #ifdef TEGRA_PCIB_MSI_ENABLE rv = tegra_pcib_attach_msi(dev); if (rv != 0) goto out; #endif device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); out: return (rv); } static device_method_t tegra_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_pcib_probe), DEVMETHOD(device_attach, tegra_pcib_attach), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, tegra_pcib_maxslots), DEVMETHOD(pcib_read_config, tegra_pcib_read_config), DEVMETHOD(pcib_write_config, tegra_pcib_write_config), DEVMETHOD(pcib_route_interrupt, tegra_pcib_route_interrupt), DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi), DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi), DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), #ifdef TEGRA_PCIB_MSI_ENABLE /* MSI/MSI-X */ DEVMETHOD(msi_alloc_msi, tegra_pcib_msi_alloc_msi), DEVMETHOD(msi_release_msi, tegra_pcib_msi_release_msi), DEVMETHOD(msi_map_msi, tegra_pcib_msi_map_msi), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, tegra_pcib_msi_disable_intr), DEVMETHOD(pic_enable_intr, tegra_pcib_msi_enable_intr), DEVMETHOD(pic_setup_intr, tegra_pcib_msi_setup_intr), DEVMETHOD(pic_teardown_intr, tegra_pcib_msi_teardown_intr), DEVMETHOD(pic_post_filter, tegra_pcib_msi_post_filter), DEVMETHOD(pic_post_ithread, tegra_pcib_msi_post_ithread), DEVMETHOD(pic_pre_ithread, tegra_pcib_msi_pre_ithread), #endif /* OFW bus interface */ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, tegra_pcib_driver, tegra_pcib_methods, sizeof(struct tegra_pcib_softc), ofw_pcib_driver); DRIVER_MODULE(tegra_pcib, simplebus, tegra_pcib_driver, NULL, NULL); diff --git a/sys/arm/nvidia/tegra_sdhci.c b/sys/arm/nvidia/tegra_sdhci.c index 6877b7021980..2b152227d95f 100644 --- a/sys/arm/nvidia/tegra_sdhci.c +++ b/sys/arm/nvidia/tegra_sdhci.c @@ -1,472 +1,472 @@ /*- * 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 /* * SDHCI driver glue for NVIDIA Tegra family * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include "sdhci_if.h" #include "opt_mmccam.h" /* Tegra SDHOST controller vendor register definitions */ #define SDMMC_VENDOR_CLOCK_CNTRL 0x100 #define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8 #define VENDOR_CLOCK_CNTRL_CLK_MASK 0xFF #define SDMMC_VENDOR_SYS_SW_CNTRL 0x104 #define SDMMC_VENDOR_CAP_OVERRIDES 0x10C #define SDMMC_VENDOR_BOOT_CNTRL 0x110 #define SDMMC_VENDOR_BOOT_ACK_TIMEOUT 0x114 #define SDMMC_VENDOR_BOOT_DAT_TIMEOUT 0x118 #define SDMMC_VENDOR_DEBOUNCE_COUNT 0x11C #define SDMMC_VENDOR_MISC_CNTRL 0x120 #define VENDOR_MISC_CTRL_ENABLE_SDR104 0x8 #define VENDOR_MISC_CTRL_ENABLE_SDR50 0x10 #define VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 #define VENDOR_MISC_CTRL_ENABLE_DDR50 0x200 #define SDMMC_MAX_CURRENT_OVERRIDE 0x124 #define SDMMC_MAX_CURRENT_OVERRIDE_HI 0x128 #define SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 0x1D0 #define SDMMC_VENDOR_PHWRESET_VAL0 0x1D4 #define SDMMC_VENDOR_PHWRESET_VAL1 0x1D8 #define SDMMC_VENDOR_PHWRESET_VAL2 0x1DC #define SDMMC_SDMEMCOMPPADCTRL_0 0x1E0 #define SDMMC_AUTO_CAL_CONFIG 0x1E4 #define SDMMC_AUTO_CAL_INTERVAL 0x1E8 #define SDMMC_AUTO_CAL_STATUS 0x1EC #define SDMMC_SDMMC_MCCIF_FIFOCTRL 0x1F4 #define SDMMC_TIMEOUT_WCOAL_SDMMC 0x1F8 /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-sdhci", 1}, {"nvidia,tegra210-sdhci", 1}, {NULL, 0}, }; struct tegra_sdhci_softc { device_t dev; struct resource * mem_res; struct resource * irq_res; void * intr_cookie; u_int quirks; /* Chip specific quirks */ u_int caps; /* If we override SDHCI_CAPABILITIES */ uint32_t max_clk; /* Max possible freq */ clk_t clk; hwreset_t reset; gpio_pin_t gpio_power; struct sdhci_fdt_gpio *gpio; int force_card_present; struct sdhci_slot slot; }; static inline uint32_t RD4(struct tegra_sdhci_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static uint8_t tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); return (bus_read_1(sc->mem_res, off)); } static uint16_t tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); return (bus_read_2(sc->mem_res, off)); } static uint32_t tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct tegra_sdhci_softc *sc; uint32_t val32; sc = device_get_softc(dev); val32 = bus_read_4(sc->mem_res, off); /* Force the card-present state if necessary. */ if (off == SDHCI_PRESENT_STATE && sc->force_card_present) val32 |= SDHCI_CARD_PRESENT; return (val32); } static void tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); bus_read_multi_4(sc->mem_res, off, data, count); } static void tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); bus_write_1(sc->mem_res, off, val); } static void tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); bus_write_2(sc->mem_res, off, val); } static void tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); bus_write_4(sc->mem_res, off, val); } static void tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct tegra_sdhci_softc *sc; sc = device_get_softc(dev); bus_write_multi_4(sc->mem_res, off, data, count); } static void tegra_sdhci_intr(void *arg) { struct tegra_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->slot); RD4(sc, SDHCI_INT_STATUS); } static int tegra_sdhci_get_ro(device_t brdev, device_t reqdev) { struct tegra_sdhci_softc *sc = device_get_softc(brdev); return (sdhci_fdt_gpio_get_readonly(sc->gpio)); } static bool tegra_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot) { struct tegra_sdhci_softc *sc = device_get_softc(dev); return (sdhci_fdt_gpio_get_present(sc->gpio)); } static int tegra_sdhci_probe(device_t dev) { struct tegra_sdhci_softc *sc; phandle_t node; pcell_t cid; const struct ofw_compat_data *cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); cd = ofw_bus_search_compatible(dev, compat_data); if (cd->ocd_data == 0) return (ENXIO); node = ofw_bus_get_node(dev); device_set_desc(dev, "Tegra SDHCI controller"); /* Allow dts to patch quirks, slots, and max-frequency. */ if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0) sc->quirks = cid; if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0) sc->max_clk = cid; return (BUS_PROBE_DEFAULT); } static int tegra_sdhci_attach(device_t dev) { struct tegra_sdhci_softc *sc; int rid, rv; uint64_t freq; phandle_t node, prop; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "cannot allocate memory window\n"); rv = ENXIO; goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->irq_res) { device_printf(dev, "cannot allocate interrupt\n"); rv = ENXIO; goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, 0, "sdhci", &sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot get 'sdhci' reset\n"); goto fail; } rv = hwreset_assert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot reset 'sdhci' reset\n"); goto fail; } gpio_pin_get_by_ofw_property(sc->dev, node, "power-gpios", &sc->gpio_power); if (OF_hasprop(node, "assigned-clocks")) { rv = clk_set_assigned(sc->dev, node); if (rv != 0) { device_printf(dev, "Cannot set assigned clocks\n"); goto fail; } } rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get clock\n"); goto fail; } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock\n"); goto fail; } rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN); if (rv != 0) { device_printf(dev, "Cannot set clock\n"); } rv = clk_get_freq(sc->clk, &freq); if (rv != 0) { device_printf(dev, "Cannot get clock frequency\n"); goto fail; } DELAY(4000); rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot unreset 'sdhci' reset\n"); goto fail; } if (bootverbose) device_printf(dev, " Base MMC clock: %jd\n", (uintmax_t)freq); /* Fill slot information. */ sc->max_clk = (int)freq; sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_MISSING_CAPS; /* Limit real slot capabilities. */ sc->caps = RD4(sc, SDHCI_CAPABILITIES); if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) { sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA); switch (prop) { case 8: sc->caps |= MMC_CAP_8_BIT_DATA; /* FALLTHROUGH */ case 4: sc->caps |= MMC_CAP_4_BIT_DATA; break; case 1: break; default: device_printf(dev, "Bad bus-width value %u\n", prop); break; } } if (OF_hasprop(node, "non-removable")) sc->force_card_present = 1; /* * Clear clock field, so SDHCI driver uses supplied frequency. * in sc->slot.max_clk */ sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK; sc->slot.quirks = sc->quirks; sc->slot.max_clk = sc->max_clk; sc->slot.caps = sc->caps; if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) { device_printf(dev, "cannot setup interrupt handler\n"); rv = ENXIO; goto fail; } rv = sdhci_init_slot(dev, &sc->slot, 0); if (rv != 0) { goto fail; } sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot); bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->slot); return (0); fail: if (sc->gpio != NULL) sdhci_fdt_gpio_teardown(sc->gpio); if (sc->intr_cookie != NULL) bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); if (sc->gpio_power != NULL) gpio_pin_release(sc->gpio_power); if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (rv); } static int tegra_sdhci_detach(device_t dev) { struct tegra_sdhci_softc *sc = device_get_softc(dev); struct sdhci_slot *slot = &sc->slot; bus_generic_detach(dev); sdhci_fdt_gpio_teardown(sc->gpio); clk_release(sc->clk); bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), sc->irq_res); sdhci_cleanup_slot(slot); bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem_res), sc->mem_res); return (0); } static device_method_t tegra_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_sdhci_probe), DEVMETHOD(device_attach, tegra_sdhci_attach), DEVMETHOD(device_detach, tegra_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, tegra_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, tegra_sdhci_read_1), DEVMETHOD(sdhci_read_2, tegra_sdhci_read_2), DEVMETHOD(sdhci_read_4, tegra_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, tegra_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, tegra_sdhci_write_1), DEVMETHOD(sdhci_write_2, tegra_sdhci_write_2), DEVMETHOD(sdhci_write_4, tegra_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, tegra_sdhci_write_multi_4), DEVMETHOD(sdhci_get_card_present, tegra_sdhci_get_card_present), DEVMETHOD_END }; static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods, sizeof(struct tegra_sdhci_softc)); DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, NULL, NULL); SDHCI_DEPEND(sdhci_tegra); #ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhci); #endif diff --git a/sys/arm/nvidia/tegra_soctherm.c b/sys/arm/nvidia/tegra_soctherm.c index 0018833e5a43..fa6d0d9fa5cd 100644 --- a/sys/arm/nvidia/tegra_soctherm.c +++ b/sys/arm/nvidia/tegra_soctherm.c @@ -1,841 +1,841 @@ /*- * 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 /* * Thermometer and thermal zones driver for Tegra SoCs. * Calibration data and algo are taken from Linux, because this part of SoC * is undocumented in TRM. */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include "tegra_soctherm_if.h" /* Per sensors registers - base is 0x0c0*/ #define TSENSOR_CONFIG0 0x000 #define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8) #define TSENSOR_CONFIG0_STATUS_CLR (1 << 5) #define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4) #define TSENSOR_CONFIG0_OVERFLOW (1 << 3) #define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2) #define TSENSOR_CONFIG0_RO_SEL (1 << 1) #define TSENSOR_CONFIG0_STOP (1 << 0) #define TSENSOR_CONFIG1 0x004 #define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31) #define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24) #define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15) #define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0) #define TSENSOR_CONFIG2 0x008 #define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16) #define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0) #define TSENSOR_STATUS0 0x00c #define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31) #define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff) #define TSENSOR_STATUS1 0x010 #define TSENSOR_STATUS1_TEMP_VALID (1U << 31) #define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff) #define TSENSOR_STATUS2 0x014 #define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff) #define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff) /* Readbacks */ #define READBACK_VALUE(x) (((x) >> 8) & 0xff) #define READBACK_ADD_HALF (1 << 7) #define READBACK_NEGATE (1 << 0) /* Global registers */ #define TSENSOR_PDIV 0x1c0 #define TSENSOR_HOTSPOT_OFF 0x1c4 #define TSENSOR_TEMP1 0x1c8 #define TSENSOR_TEMP2 0x1cc /* Fuses */ #define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0 #define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13 /* Layout is different for Tegra124 and Tegra210 */ #define FUSE_TSENSOR_COMMON 0x180 #define TEGRA124_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 0) & 0x3ff) #define TEGRA124_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 10) & 0x7ff) #define TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT 21 #define TEGRA124_FUSE_COMMON_SHIFT_FT_BITS 5 #define TEGRA210_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 11) & 0x3ff) #define TEGRA210_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 21) & 0x7ff) #define TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT 0 #define TEGRA210_FUSE_COMMON_SHIFT_CP_BITS 6 #define TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT 6 #define TEGRA210_FUSE_COMMON_SHIFT_FT_BITS 5 /* Only for Tegra124 */ #define FUSE_SPARE_REALIGNMENT_REG 0x1fc #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0 #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6 #define TEGRA124_NOMINAL_CALIB_FT 105 #define TEGRA124_NOMINAL_CALIB_CP 25 #define TEGRA210_NOMINAL_CALIB_FT 105 #define TEGRA210_NOMINAL_CALIB_CP 25 #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) static struct sysctl_ctx_list soctherm_sysctl_ctx; struct tsensor_cfg { uint32_t tall; uint32_t tsample; uint32_t tiddq_en; uint32_t ten_count; uint32_t pdiv; uint32_t tsample_ate; uint32_t pdiv_ate; }; struct soctherm_shared_cal { uint32_t base_cp; uint32_t base_ft; int32_t actual_temp_cp; int32_t actual_temp_ft; }; struct tsensor { char *name; int id; bus_addr_t sensor_base; bus_addr_t calib_fuse; int fuse_corr_alpha; int fuse_corr_beta; int16_t therm_a; int16_t therm_b; }; struct soctherm_soc; struct soctherm_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; clk_t tsensor_clk; clk_t soctherm_clk; hwreset_t reset; struct soctherm_soc *soc; struct soctherm_shared_cal shared_cal; }; struct soctherm_soc { void (*shared_cal)(struct soctherm_softc *sc); uint32_t tsensor_pdiv; uint32_t tsensor_hotspot_off; struct tsensor_cfg *tsensor_cfg; struct tsensor *tsensors; int ntsensors; }; /* Tegra124 config */ static struct tsensor_cfg t124_tsensor_config = { .tall = 16300, .tsample = 120, .tiddq_en = 1, .ten_count = 1, .pdiv = 8, .tsample_ate = 480, .pdiv_ate = 8 }; static struct tsensor t124_tsensors[] = { { .name = "cpu0", .id = TEGRA124_SOCTHERM_SENSOR_CPU, .sensor_base = 0x0c0, .calib_fuse = 0x098, .fuse_corr_alpha = 1135400, .fuse_corr_beta = -6266900, }, { .name = "cpu1", .id = -1, .sensor_base = 0x0e0, .calib_fuse = 0x084, .fuse_corr_alpha = 1122220, .fuse_corr_beta = -5700700, }, { .name = "cpu2", .id = -1, .sensor_base = 0x100, .calib_fuse = 0x088, .fuse_corr_alpha = 1127000, .fuse_corr_beta = -6768200, }, { .name = "cpu3", .id = -1, .sensor_base = 0x120, .calib_fuse = 0x12c, .fuse_corr_alpha = 1110900, .fuse_corr_beta = -6232000, }, { .name = "mem0", .id = TEGRA124_SOCTHERM_SENSOR_MEM, .sensor_base = 0x140, .calib_fuse = 0x158, .fuse_corr_alpha = 1122300, .fuse_corr_beta = -5936400, }, { .name = "mem1", .id = -1, .sensor_base = 0x160, .calib_fuse = 0x15c, .fuse_corr_alpha = 1145700, .fuse_corr_beta = -7124600, }, { .name = "gpu", .id = TEGRA124_SOCTHERM_SENSOR_GPU, .sensor_base = 0x180, .calib_fuse = 0x154, .fuse_corr_alpha = 1120100, .fuse_corr_beta = -6000500, }, { .name = "pllX", .id = TEGRA124_SOCTHERM_SENSOR_PLLX, .sensor_base = 0x1a0, .calib_fuse = 0x160, .fuse_corr_alpha = 1106500, .fuse_corr_beta = -6729300, }, }; static void tegra124_shared_cal(struct soctherm_softc *sc); static struct soctherm_soc tegra124_soc = { .shared_cal = tegra124_shared_cal, .tsensor_pdiv = 0x8888, .tsensor_hotspot_off = 0x00060600 , .tsensor_cfg = &t124_tsensor_config, .tsensors = t124_tsensors, .ntsensors = nitems(t124_tsensors), }; /* Tegra210 config */ static struct tsensor_cfg t210_tsensor_config = { .tall = 16300, .tsample = 120, .tiddq_en = 1, .ten_count = 1, .pdiv = 8, .tsample_ate = 480, .pdiv_ate = 8 }; static struct tsensor t210_tsensors[] = { { .name = "cpu0", .id = TEGRA124_SOCTHERM_SENSOR_CPU, .sensor_base = 0x0c0, .calib_fuse = 0x098, .fuse_corr_alpha = 1085000, .fuse_corr_beta = 3244200, }, { .name = "cpu1", .id = -1, .sensor_base = 0x0e0, .calib_fuse = 0x084, .fuse_corr_alpha = 1126200, .fuse_corr_beta = -67500, }, { .name = "cpu2", .id = -1, .sensor_base = 0x100, .calib_fuse = 0x088, .fuse_corr_alpha = 1098400, .fuse_corr_beta = 2251100, }, { .name = "cpu3", .id = -1, .sensor_base = 0x120, .calib_fuse = 0x12c, .fuse_corr_alpha = 1108000, .fuse_corr_beta = 602700, }, { .name = "mem0", .id = TEGRA124_SOCTHERM_SENSOR_MEM, .sensor_base = 0x140, .calib_fuse = 0x158, .fuse_corr_alpha = 1069200, .fuse_corr_beta = 3549900, }, { .name = "mem1", .id = -1, .sensor_base = 0x160, .calib_fuse = 0x15c, .fuse_corr_alpha = 1173700, .fuse_corr_beta = -6263600, }, { .name = "gpu", .id = TEGRA124_SOCTHERM_SENSOR_GPU, .sensor_base = 0x180, .calib_fuse = 0x154, .fuse_corr_alpha = 1074300, .fuse_corr_beta = 2734900, }, { .name = "pllX", .id = TEGRA124_SOCTHERM_SENSOR_PLLX, .sensor_base = 0x1a0, .calib_fuse = 0x160, .fuse_corr_alpha = 1039700, .fuse_corr_beta = 6829100, }, }; static void tegra210_shared_cal(struct soctherm_softc *sc); static struct soctherm_soc tegra210_soc = { .shared_cal = tegra210_shared_cal, .tsensor_pdiv = 0x8888, .tsensor_hotspot_off = 0x000A0500 , .tsensor_cfg = &t210_tsensor_config, .tsensors = t210_tsensors, .ntsensors = nitems(t210_tsensors), }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-soctherm", (uintptr_t)&tegra124_soc}, {"nvidia,tegra210-soctherm", (uintptr_t)&tegra210_soc}, {NULL, 0}, }; /* Extract signed integer bitfield from register */ static int extract_signed(uint32_t reg, int shift, int bits) { int32_t val; uint32_t mask; mask = (1 << bits) - 1; val = ((reg >> shift) & mask) << (32 - bits); val >>= 32 - bits; return ((int32_t)val); } static inline int64_t div64_s64_precise(int64_t a, int64_t b) { int64_t r, al; al = a << 16; r = (al * 2 + 1) / (2 * b); return (r >> 16); } static void tegra124_shared_cal(struct soctherm_softc *sc) { uint32_t val; int calib_cp, calib_ft; struct soctherm_shared_cal *cal; cal = &sc->shared_cal; val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON); cal->base_cp = TEGRA124_FUSE_COMMON_CP_TS_BASE(val); cal->base_ft = TEGRA124_FUSE_COMMON_FT_TS_BASE(val); calib_ft = extract_signed(val, TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT, TEGRA124_FUSE_COMMON_SHIFT_FT_BITS); val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG); calib_cp = extract_signed(val, FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT, FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS); cal->actual_temp_cp = 2 * TEGRA124_NOMINAL_CALIB_CP + calib_cp; cal->actual_temp_ft = 2 * TEGRA124_NOMINAL_CALIB_FT + calib_ft; #ifdef DEBUG printf("%s: base_cp: %u, base_ft: %d," " actual_temp_cp: %d, actual_temp_ft: %d\n", __func__, cal->base_cp, cal->base_ft, cal->actual_temp_cp, cal->actual_temp_ft); #endif } static void tegra210_shared_cal(struct soctherm_softc *sc) { uint32_t val; int calib_cp, calib_ft; struct soctherm_shared_cal *cal; cal = &sc->shared_cal; val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON); cal->base_cp = TEGRA210_FUSE_COMMON_CP_TS_BASE(val); cal->base_ft = TEGRA210_FUSE_COMMON_FT_TS_BASE(val); calib_ft = extract_signed(val, TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT, TEGRA210_FUSE_COMMON_SHIFT_FT_BITS); calib_cp = extract_signed(val, TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT, TEGRA210_FUSE_COMMON_SHIFT_CP_BITS); cal->actual_temp_cp = 2 * TEGRA210_NOMINAL_CALIB_CP + calib_cp; cal->actual_temp_ft = 2 * TEGRA210_NOMINAL_CALIB_FT + calib_ft; #ifdef DEBUG printf("%s: base_cp: %u, base_ft: %d," " actual_temp_cp: %d, actual_temp_ft: %d\n", __func__, cal->base_cp, cal->base_ft, cal->actual_temp_cp, cal->actual_temp_ft); #endif } static void tsensor_calibration(struct soctherm_softc *sc, struct tsensor *sensor) { uint32_t val; int mult, div, calib_cp, calib_ft; int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp; int temp_a, temp_b; struct tsensor_cfg *cfg; struct soctherm_shared_cal *cal; int64_t tmp; cfg = sc->soc->tsensor_cfg; cal = &sc->shared_cal; val = tegra_fuse_read_4(sensor->calib_fuse); calib_cp = extract_signed(val, FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT, FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS); actual_tsensor_cp = cal->base_cp * 64 + calib_cp; calib_ft = extract_signed(val, FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT, FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS); actual_tsensor_ft = cal->base_ft * 32 + calib_ft; delta_sens = actual_tsensor_ft - actual_tsensor_cp; delta_temp = cal->actual_temp_ft - cal->actual_temp_cp; mult = cfg->pdiv * cfg->tsample_ate; div = cfg->tsample * cfg->pdiv_ate; temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult, (int64_t) delta_sens * div); tmp = (int64_t)actual_tsensor_ft * cal->actual_temp_cp - (int64_t)actual_tsensor_cp * cal->actual_temp_ft; temp_b = div64_s64_precise(tmp, (int64_t)delta_sens); temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha, 1000000); temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha + sensor->fuse_corr_beta, 1000000); sensor->therm_a = (int16_t)temp_a; sensor->therm_b = (int16_t)temp_b; #ifdef DEBUG printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)" " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n", __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF, calib_cp, calib_cp, calib_ft, calib_ft); printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n", (uint16_t)sensor->therm_a, sensor->therm_a, (uint16_t)sensor->therm_b, sensor->therm_b); #endif } static void soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor) { struct tsensor_cfg *cfg; uint32_t val; cfg = sc->soc->tsensor_cfg; tsensor_calibration(sc, sensor); val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); val |= TSENSOR_CONFIG0_STOP; val |= TSENSOR_CONFIG0_STATUS_CLR; WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); val = TSENSOR_CONFIG0_TALL(cfg->tall); val |= TSENSOR_CONFIG0_STOP; WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); val = TSENSOR_CONFIG1_TSAMPLE(cfg->tsample - 1); val |= TSENSOR_CONFIG1_TIDDQ_EN(cfg->tiddq_en); val |= TSENSOR_CONFIG1_TEN_COUNT(cfg->ten_count); val |= TSENSOR_CONFIG1_TEMP_ENABLE; WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val); val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) | TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b); WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val); val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); val &= ~TSENSOR_CONFIG0_STOP; WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); #ifdef DEBUG printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) ); #endif } static int soctherm_convert_raw(uint32_t val) { int32_t t; t = READBACK_VALUE(val) * 1000; if (val & READBACK_ADD_HALF) t += 500; if (val & READBACK_NEGATE) t *= -1; return (t); } static int soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp) { int timeout; uint32_t val; /* wait for valid sample */ for (timeout = 100; timeout > 0; timeout--) { val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1); if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0) break; DELAY(100); } if (timeout <= 0) device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name); *temp = soctherm_convert_raw(val); #ifdef DEBUG printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp); printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) ); #endif return (0); } static int soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) { struct soctherm_softc *sc; int i; sc = device_get_softc(dev); /* The direct sensor map starts at 0x100 */ if (id >= 0x100) { id -= 0x100; if (id >= sc->soc->ntsensors) return (ERANGE); return(soctherm_read_temp(sc, sc->soc->tsensors + id, val)); } /* Linux (DT) compatible thermal zones */ for (i = 0; i < sc->soc->ntsensors; i++) { if (sc->soc->tsensors->id == id) { return(soctherm_read_temp(sc, sc->soc->tsensors + id, val)); } } return (ERANGE); } static int soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS) { struct soctherm_softc *sc; int val; int rv; int id; /* Write request */ if (req->newptr != NULL) return (EINVAL); sc = arg1; id = arg2; if (id >= sc->soc->ntsensors) return (ERANGE); rv = soctherm_read_temp(sc, sc->soc->tsensors + id, &val); if (rv != 0) return (rv); val = val / 100; val += 2731; rv = sysctl_handle_int(oidp, &val, 0, req); return (rv); } static int soctherm_init_sysctl(struct soctherm_softc *sc) { int i; struct sysctl_oid *oid, *tmp; sysctl_ctx_init(&soctherm_sysctl_ctx); /* create node for hw.temp */ oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, ""); if (oid == NULL) return (ENXIO); /* Add sensors */ for (i = sc->soc->ntsensors - 1; i >= 0; i--) { tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, sc->soc->tsensors[i].name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i, soctherm_sysctl_temperature, "IK", "SoC Temperature"); if (tmp == NULL) return (ENXIO); } return (0); } static int soctherm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Tegra temperature sensors"); return (BUS_PROBE_DEFAULT); } static int soctherm_attach(device_t dev) { struct soctherm_softc *sc; phandle_t node; int i, rid, rv; sc = device_get_softc(dev); sc->dev = dev; sc->soc = (struct soctherm_soc *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; node = ofw_bus_get_node(sc->dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } /* if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, soctherm_intr, NULL, sc, &sc->irq_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); goto fail; } */ /* OWF resources */ rv = hwreset_get_by_ofw_name(dev, 0, "soctherm", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get fuse reset\n"); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "tsensor", &sc->tsensor_clk); if (rv != 0) { device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "soctherm", &sc->soctherm_clk); if (rv != 0) { device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv); goto fail; } rv = hwreset_assert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot assert reset\n"); goto fail; } rv = clk_enable(sc->tsensor_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv); goto fail; } rv = clk_enable(sc->soctherm_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv); goto fail; } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot clear reset\n"); goto fail; } sc->soc->shared_cal(sc); WR4(sc, TSENSOR_PDIV, sc->soc->tsensor_pdiv); WR4(sc, TSENSOR_HOTSPOT_OFF, sc->soc->tsensor_hotspot_off); for (i = 0; i < sc->soc->ntsensors; i++) soctherm_init_tsensor(sc, sc->soc->tsensors + i); rv = soctherm_init_sysctl(sc); if (rv != 0) { device_printf(sc->dev, "Cannot initialize sysctls\n"); goto fail; } OF_device_register_xref(OF_xref_from_node(node), dev); return (bus_generic_attach(dev)); fail: if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&soctherm_sysctl_ctx); if (sc->tsensor_clk != NULL) clk_release(sc->tsensor_clk); if (sc->soctherm_clk != NULL) clk_release(sc->soctherm_clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static int soctherm_detach(device_t dev) { struct soctherm_softc *sc; sc = device_get_softc(dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&soctherm_sysctl_ctx); if (sc->tsensor_clk != NULL) clk_release(sc->tsensor_clk); if (sc->soctherm_clk != NULL) clk_release(sc->soctherm_clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static device_method_t tegra_soctherm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, soctherm_probe), DEVMETHOD(device_attach, soctherm_attach), DEVMETHOD(device_detach, soctherm_detach), /* SOCTHERM interface */ DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp), DEVMETHOD_END }; static DEFINE_CLASS_0(soctherm, tegra_soctherm_driver, tegra_soctherm_methods, sizeof(struct soctherm_softc)); EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver, NULL, NULL, 79); diff --git a/sys/arm/nvidia/tegra_uart.c b/sys/arm/nvidia/tegra_uart.c index 31f92d34a6d3..e18b77ecc321 100644 --- a/sys/arm/nvidia/tegra_uart.c +++ b/sys/arm/nvidia/tegra_uart.c @@ -1,249 +1,249 @@ /*- * 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 /* * UART driver for Tegra SoCs. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * High-level UART interface. */ struct tegra_softc { struct ns8250_softc ns8250_base; clk_t clk; hwreset_t reset; }; /* * UART class interface. */ static int tegra_uart_attach(struct uart_softc *sc) { int rv; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; rv = ns8250_bus_attach(sc); if (rv != 0) return (rv); ns8250->ier_rxbits = 0x1d; ns8250->ier_mask = 0xc0; ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); return (0); } static void tegra_uart_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; u_char ier; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interrupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); ier = uart_getreg(bas, REG_IER); uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0) ; uart_setreg(bas, REG_FCR, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void tegra_uart_ungrab(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_FCR, ns8250->fcr); uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static kobj_method_t tegra_methods[] = { KOBJMETHOD(uart_probe, ns8250_bus_probe), KOBJMETHOD(uart_attach, tegra_uart_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, tegra_uart_grab), KOBJMETHOD(uart_ungrab, tegra_uart_ungrab), KOBJMETHOD_END }; static struct uart_class tegra_uart_class = { "tegra class", tegra_methods, sizeof(struct tegra_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = 0, }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class}, {"nvidia,tegra210-uart", (uintptr_t)&tegra_uart_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS(compat_data); /* * UART Driver interface. */ static int uart_fdt_get_shift1(phandle_t node) { pcell_t shift; if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) shift = 2; return ((int)shift); } static int tegra_uart_probe(device_t dev) { struct tegra_softc *sc; phandle_t node; uint64_t freq; int shift; int rv; const struct ofw_compat_data *cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); cd = ofw_bus_search_compatible(dev, compat_data); if (cd->ocd_data == 0) return (ENXIO); sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; rv = hwreset_get_by_ofw_name(dev, 0, "serial", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get 'serial' reset\n"); return (ENXIO); } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot unreset 'serial' reset\n"); return (ENXIO); } node = ofw_bus_get_node(dev); shift = uart_fdt_get_shift1(node); rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get UART clock: %d\n", rv); return (ENXIO); } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } rv = clk_get_freq(sc->clk, &freq); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0)); } static int tegra_uart_detach(device_t dev) { struct tegra_softc *sc; sc = device_get_softc(dev); if (sc->clk != NULL) { clk_release(sc->clk); } return (uart_bus_detach(dev)); } static device_method_t tegra_uart_bus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_uart_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, tegra_uart_detach), { 0, 0 } }; static driver_t tegra_uart_driver = { uart_driver_name, tegra_uart_bus_methods, sizeof(struct tegra_softc), }; DRIVER_MODULE(tegra_uart, simplebus, tegra_uart_driver, 0, 0); diff --git a/sys/arm/nvidia/tegra_usbphy.c b/sys/arm/nvidia/tegra_usbphy.c index 438ba719170c..2499a568fe4c 100644 --- a/sys/arm/nvidia/tegra_usbphy.c +++ b/sys/arm/nvidia/tegra_usbphy.c @@ -1,848 +1,848 @@ /*- * 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 /* * USB phy driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include "phynode_if.h" #define CTRL_ICUSB_CTRL 0x15c #define ICUSB_CTR_IC_ENB1 (1 << 3) #define CTRL_USB_USBMODE 0x1f8 #define USB_USBMODE_MASK (3 << 0) #define USB_USBMODE_HOST (3 << 0) #define USB_USBMODE_DEVICE (2 << 0) #define CTRL_USB_HOSTPC1_DEVLC 0x1b4 #define USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) #define USB_HOSTPC1_DEVLC_STS (1 << 28) #define USB_HOSTPC1_DEVLC_PHCD (1 << 22) #define IF_USB_SUSP_CTRL 0x400 #define FAST_WAKEUP_RESP (1 << 26) #define UTMIP_SUSPL1_SET (1 << 25) #define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) #define USB_SUSP_SET (1 << 14) #define UTMIP_PHY_ENB (1 << 12) #define UTMIP_RESET (1 << 11) #define USB_SUSP_POL (1 << 10) #define USB_PHY_CLK_VALID_INT_ENB (1 << 9) #define USB_PHY_CLK_VALID_INT_STS (1 << 8) #define USB_PHY_CLK_VALID (1 << 7) #define USB_CLKEN (1 << 6) #define USB_SUSP_CLR (1 << 5) #define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) #define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) #define USB_WAKE_ON_RESUME_EN (1 << 2) #define USB_WAKEUP_INT_ENB (1 << 1) #define USB_WAKEUP_INT_STS (1 << 0) #define IF_USB_PHY_VBUS_SENSORS 0x404 #define B_SESS_END_SW_VALUE (1 << 4) #define B_SESS_END_SW_EN (1 << 3) #define UTMIP_XCVR_CFG0 0x808 #define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) #define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22) #define UTMIP_XCVR_LSBIAS_SEL (1 << 21) #define UTMIP_XCVR_DISCON_METHOD (1 << 20) #define UTMIP_FORCE_PDZI_POWERUP (1 << 19) #define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) #define UTMIP_FORCE_PD2_POWERUP (1 << 17) #define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) #define UTMIP_FORCE_PD_POWERUP (1 << 15) #define UTMIP_FORCE_PD_POWERDOWN (1 << 14) #define UTMIP_XCVR_TERMEN (1 << 13) #define UTMIP_XCVR_HSLOOPBACK (1 << 12) #define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) #define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) #define UTMIP_XCVR_FSSLEW(x) (((x) & 0x3) << 6) #define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) #define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) #define UTMIP_BIAS_CFG0 0x80C #define UTMIP_IDDIG_C_VAL (1 << 30) #define UTMIP_IDDIG_C_SEL (1 << 29) #define UTMIP_IDDIG_B_VAL (1 << 28) #define UTMIP_IDDIG_B_SEL (1 << 27) #define UTMIP_IDDIG_A_VAL (1 << 26) #define UTMIP_IDDIG_A_SEL (1 << 25) #define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) #define UTMIP_IDPD_VAL (1 << 23) #define UTMIP_IDPD_SEL (1 << 22) #define UTMIP_IDDIG_VAL (1 << 21) #define UTMIP_IDDIG_SEL (1 << 20) #define UTMIP_GPI_VAL (1 << 19) #define UTMIP_GPI_SEL (1 << 18) #define UTMIP_ACTIVE_TERM_OFFSET(x) (((x) & 0x7) << 15) #define UTMIP_ACTIVE_PULLUP_OFFSET(x) (((x) & 0x7) << 12) #define UTMIP_OTGPD (1 << 11) #define UTMIP_BIASPD (1 << 10) #define UTMIP_VBUS_LEVEL_LEVEL(x) (((x) & 0x3) << 8) #define UTMIP_SESS_LEVEL_LEVEL(x) (((x) & 0x3) << 6) #define UTMIP_HSCHIRP_LEVEL(x) (((x) & 0x3) << 4) #define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) #define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) #define UTMIP_HSRX_CFG0 0x810 #define UTMIP_KEEP_PATT_ON_ACTIVE(x) (((x) & 0x3) << 30) #define UTMIP_ALLOW_CONSEC_UPDN (1 << 29) #define UTMIP_REALIGN_ON_NEW_PKT (1 << 28) #define UTMIP_PCOUNT_UPDN_DIV(x) (((x) & 0xf) << 24) #define UTMIP_SQUELCH_EOP_DLY(x) (((x) & 0x7) << 21) #define UTMIP_NO_STRIPPING (1 << 20) #define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) #define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) #define UTMIP_ELASTIC_OVERRUN_DISABLE (1 << 9) #define UTMIP_ELASTIC_UNDERRUN_DISABLE (1 << 8) #define UTMIP_PASS_CHIRP (1 << 7) #define UTMIP_PASS_FEEDBACK (1 << 6) #define UTMIP_PCOUNT_INERTIA(x) (((x) & 0x3) << 4) #define UTMIP_PHASE_ADJUST(x) (((x) & 0x3) << 2) #define UTMIP_THREE_SYNCBITS (1 << 1) #define UTMIP_USE4SYNC_TRAN (1 << 0) #define UTMIP_HSRX_CFG1 0x814 #define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1F) << 1) #define UTMIP_HS_ALLOW_KEEP_ALIVE (1 << 0) #define UTMIP_TX_CFG0 0x820 #define UTMIP_FS_PREAMBLE_J (1 << 19) #define UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE (1 << 18) #define UTMIP_FS_PREAMBLE_OUTPUT_ENABLE (1 << 17) #define UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR (1 << 16) #define UTMIP_HS_READY_WAIT_FOR_VALID (1 << 15) #define UTMIP_HS_TX_IPG_DLY(x) (((x) & 0x1f) << 10) #define UTMIP_HS_DISCON_EOP_ONLY (1 << 9) #define UTMIP_HS_DISCON_DISABLE (1 << 8) #define UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 7) #define UTMIP_HS_PREAMBLE_OUTPUT_ENABLE (1 << 6) #define UTMIP_SIE_RESUME_ON_LINESTATE (1 << 5) #define UTMIP_SOF_ON_NO_STUFF (1 << 4) #define UTMIP_SOF_ON_NO_ENCODE (1 << 3) #define UTMIP_NO_STUFFING (1 << 2) #define UTMIP_NO_ENCODING (1 << 1) #define UTMIP_NO_SYNC_NO_EOP (1 << 0) #define UTMIP_MISC_CFG0 0x824 #define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) #define UTMIP_DPDM_OBSERVE (1 << 26) #define UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON (1 << 25) #define UTMIP_ALLOW_LS_ON_SOFT_DISCON (1 << 24) #define UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP (1 << 23) #define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) #define UTMIP_LS_TO_FS_SKIP_4MS (1 << 21) #define UTMIP_INJECT_ERROR_TYPE(x) (((x) & 0x3) << 19) #define UTMIP_FORCE_HS_CLOCK_ON (1 << 18) #define UTMIP_DISABLE_HS_TERM (1 << 17) #define UTMIP_FORCE_HS_TERM (1 << 16) #define UTMIP_DISABLE_PULLUP_DP (1 << 15) #define UTMIP_DISABLE_PULLUP_DM (1 << 14) #define UTMIP_DISABLE_PULLDN_DP (1 << 13) #define UTMIP_DISABLE_PULLDN_DM (1 << 12) #define UTMIP_FORCE_PULLUP_DP (1 << 11) #define UTMIP_FORCE_PULLUP_DM (1 << 10) #define UTMIP_FORCE_PULLDN_DP (1 << 9) #define UTMIP_FORCE_PULLDN_DM (1 << 8) #define UTMIP_STABLE_COUNT(x) (((x) & 0x7) << 5) #define UTMIP_STABLE_ALL (1 << 4) #define UTMIP_NO_FREE_ON_SUSPEND (1 << 3) #define UTMIP_NEVER_FREE_RUNNING_TERMS (1 << 2) #define UTMIP_ALWAYS_FREE_RUNNING_TERMS (1 << 1) #define UTMIP_COMB_TERMS (1 << 0) #define UTMIP_MISC_CFG1 0x828 #define UTMIP_PHY_XTAL_CLOCKEN (1 << 30) #define UTMIP_DEBOUNCE_CFG0 0x82C #define UTMIP_BIAS_DEBOUNCE_B(x) (((x) & 0xffff) << 16) #define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) #define UTMIP_BAT_CHRG_CFG0 0x830 #define UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) (((x) & 0x1f) << 8) #define UTMIP_OP_I_SRC_ENG (1 << 5) #define UTMIP_ON_SRC_ENG (1 << 4) #define UTMIP_OP_SRC_ENG (1 << 3) #define UTMIP_ON_SINK_ENG (1 << 2) #define UTMIP_OP_SINK_ENG (1 << 1) #define UTMIP_PD_CHRG (1 << 0) #define UTMIP_SPARE_CFG0 0x834 #define FUSE_HS_IREF_CAP_CFG (1 << 7) #define FUSE_HS_SQUELCH_LEVEL (1 << 6) #define FUSE_SPARE (1 << 5) #define FUSE_TERM_RANGE_ADJ_SEL (1 << 4) #define FUSE_SETUP_SEL (1 << 3) #define HS_RX_LATE_SQUELCH (1 << 2) #define HS_RX_FLUSH_ALAP (1 << 1) #define HS_RX_IPG_ERROR_ENABLE (1 << 0) #define UTMIP_XCVR_CFG1 0x838 #define UTMIP_XCVR_RPU_RANGE_ADJ(x) (((x) & 0x3) << 26) #define UTMIP_XCVR_HS_IREF_CAP(x) (((x) & 0x3) << 24) #define UTMIP_XCVR_SPARE(x) (((x) & 0x3) << 22) #define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) #define UTMIP_RCTRL_SW_SET (1 << 17) #define UTMIP_RCTRL_SW_VAL(x) (((x) & 0x1f) << 12) #define UTMIP_TCTRL_SW_SET (1 << 11) #define UTMIP_TCTRL_SW_VAL(x) (((x) & 0x1f) << 6) #define UTMIP_FORCE_PDDR_POWERUP (1 << 5) #define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) #define UTMIP_FORCE_PDCHRP_POWERUP (1 << 3) #define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) #define UTMIP_FORCE_PDDISC_POWERUP (1 << 1) #define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_DEBOUNCE_TIMESCALE(x) (((x) & 0x3f) << 8) #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) #define UTMIP_VBUS_WAKEUP_POWERDOWN (1 << 2) #define UTMIP_FORCE_PDTRK_POWERUP (1 << 1) #define UTMIP_FORCE_PDTRK_POWERDOWN (1 << 0) static int usbpby_enable_cnt; enum usb_ifc_type { USB_IFC_TYPE_UNKNOWN = 0, USB_IFC_TYPE_UTMI, USB_IFC_TYPE_ULPI }; enum usb_dr_mode { USB_DR_MODE_UNKNOWN = 0, USB_DR_MODE_DEVICE, USB_DR_MODE_HOST, USB_DR_MODE_OTG }; struct usbphy_softc { device_t dev; struct resource *mem_res; struct resource *pads_res; clk_t clk_reg; clk_t clk_pads; clk_t clk_pllu; regulator_t supply_vbus; hwreset_t reset_usb; hwreset_t reset_pads; enum usb_ifc_type ifc_type; enum usb_dr_mode dr_mode; bool have_utmi_regs; /* UTMI params */ int hssync_start_delay; int elastic_limit; int idle_wait_delay; int term_range_adj; int xcvr_lsfslew; int xcvr_lsrslew; int xcvr_hsslew; int hssquelch_level; int hsdiscon_level; int xcvr_setup; int xcvr_setup_use_fuses; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-usb-phy", 1}, {"nvidia,tegra30-usb-phy", 1}, {NULL, 0}, }; /* Phy controller class and methods. */ static int usbphy_phy_enable(struct phynode *phy, bool enable); static phynode_method_t usbphy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, usbphy_phy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(usbphy_phynode, usbphy_phynode_class, usbphy_phynode_methods, 0, phynode_class); #define RD4(sc, offs) \ bus_read_4(sc->mem_res, offs) #define WR4(sc, offs, val) \ bus_write_4(sc->mem_res, offs, val) static int reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val) { int i; for (i = 0; i < 1000; i++) { if ((RD4(sc, reg) & mask) == val) return (0); DELAY(10); } return (ETIMEDOUT); } static int usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable) { uint32_t val; int rv; val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); if (enable) val &= ~USB_HOSTPC1_DEVLC_PHCD; else val |= USB_HOSTPC1_DEVLC_PHCD; WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID, enable ? USB_PHY_CLK_VALID: 0); if (rv != 0) { device_printf(sc->dev, "USB phy clock timeout.\n"); return (ETIMEDOUT); } return (0); } static int usbphy_utmi_enable(struct usbphy_softc *sc) { int rv; uint32_t val; /* Reset phy */ val = RD4(sc, IF_USB_SUSP_CTRL); val |= UTMIP_RESET; WR4(sc, IF_USB_SUSP_CTRL, val); val = RD4(sc, UTMIP_TX_CFG0); val |= UTMIP_FS_PREAMBLE_J; WR4(sc, UTMIP_TX_CFG0, val); val = RD4(sc, UTMIP_HSRX_CFG0); val &= ~UTMIP_IDLE_WAIT(~0); val &= ~UTMIP_ELASTIC_LIMIT(~0); val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay); val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit); WR4(sc, UTMIP_HSRX_CFG0, val); val = RD4(sc, UTMIP_HSRX_CFG1); val &= ~UTMIP_HS_SYNC_START_DLY(~0); val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay); WR4(sc, UTMIP_HSRX_CFG1, val); val = RD4(sc, UTMIP_DEBOUNCE_CFG0); val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); val |= UTMIP_BIAS_DEBOUNCE_A(0x7530); /* For 12MHz */ WR4(sc, UTMIP_DEBOUNCE_CFG0, val); val = RD4(sc, UTMIP_MISC_CFG0); val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; WR4(sc, UTMIP_MISC_CFG0, val); if (sc->dr_mode == USB_DR_MODE_DEVICE) { val = RD4(sc,IF_USB_SUSP_CTRL); val &= ~USB_WAKE_ON_CNNT_EN_DEV; val &= ~USB_WAKE_ON_DISCON_EN_DEV; WR4(sc, IF_USB_SUSP_CTRL, val); val = RD4(sc, UTMIP_BAT_CHRG_CFG0); val &= ~UTMIP_PD_CHRG; WR4(sc, UTMIP_BAT_CHRG_CFG0, val); } else { val = RD4(sc, UTMIP_BAT_CHRG_CFG0); val |= UTMIP_PD_CHRG; WR4(sc, UTMIP_BAT_CHRG_CFG0, val); } usbpby_enable_cnt++; if (usbpby_enable_cnt == 1) { rv = hwreset_deassert(sc->reset_pads); if (rv != 0) { device_printf(sc->dev, "Cannot unreset 'utmi-pads' reset\n"); return (rv); } rv = clk_enable(sc->clk_pads); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'utmi-pads' clock\n"); return (rv); } val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); val &= ~UTMIP_OTGPD; val &= ~UTMIP_BIASPD; val &= ~UTMIP_HSSQUELCH_LEVEL(~0); val &= ~UTMIP_HSDISCON_LEVEL(~0); val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0); val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level); val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level); val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level); bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); rv = clk_disable(sc->clk_pads); if (rv != 0) { device_printf(sc->dev, "Cannot disable 'utmi-pads' clock\n"); return (rv); } } val = RD4(sc, UTMIP_XCVR_CFG0); val &= ~UTMIP_FORCE_PD_POWERDOWN; val &= ~UTMIP_FORCE_PD2_POWERDOWN ; val &= ~UTMIP_FORCE_PDZI_POWERDOWN; val &= ~UTMIP_XCVR_LSBIAS_SEL; val &= ~UTMIP_XCVR_LSFSLEW(~0); val &= ~UTMIP_XCVR_LSRSLEW(~0); val &= ~UTMIP_XCVR_HSSLEW(~0); val &= ~UTMIP_XCVR_HSSLEW_MSB(~0); val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew); val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew); val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew); val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew); if (!sc->xcvr_setup_use_fuses) { val &= ~UTMIP_XCVR_SETUP(~0); val &= ~UTMIP_XCVR_SETUP_MSB(~0); val |= UTMIP_XCVR_SETUP(sc->xcvr_setup); val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup); } WR4(sc, UTMIP_XCVR_CFG0, val); val = RD4(sc, UTMIP_XCVR_CFG1); val &= ~UTMIP_FORCE_PDDISC_POWERDOWN; val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN; val &= ~UTMIP_FORCE_PDDR_POWERDOWN; val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0); val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj); WR4(sc, UTMIP_XCVR_CFG1, val); val = RD4(sc, UTMIP_BIAS_CFG1); val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); val |= UTMIP_BIAS_PDTRK_COUNT(0x5); WR4(sc, UTMIP_BIAS_CFG1, val); val = RD4(sc, UTMIP_SPARE_CFG0); if (sc->xcvr_setup_use_fuses) val |= FUSE_SETUP_SEL; else val &= ~FUSE_SETUP_SEL; WR4(sc, UTMIP_SPARE_CFG0, val); val = RD4(sc, IF_USB_SUSP_CTRL); val |= UTMIP_PHY_ENB; WR4(sc, IF_USB_SUSP_CTRL, val); val = RD4(sc, IF_USB_SUSP_CTRL); val &= ~UTMIP_RESET; WR4(sc, IF_USB_SUSP_CTRL, val); usbphy_utmi_phy_clk(sc, true); val = RD4(sc, CTRL_USB_USBMODE); val &= ~USB_USBMODE_MASK; if (sc->dr_mode == USB_DR_MODE_HOST) val |= USB_USBMODE_HOST; else val |= USB_USBMODE_DEVICE; WR4(sc, CTRL_USB_USBMODE, val); val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); val &= ~USB_HOSTPC1_DEVLC_PTS(~0); val |= USB_HOSTPC1_DEVLC_PTS(0); WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); return (0); } static int usbphy_utmi_disable(struct usbphy_softc *sc) { int rv; uint32_t val; usbphy_utmi_phy_clk(sc, false); if (sc->dr_mode == USB_DR_MODE_DEVICE) { val = RD4(sc, IF_USB_SUSP_CTRL); val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); val |= USB_WAKE_ON_CNNT_EN_DEV; val |= USB_WAKEUP_DEBOUNCE_COUNT(5); WR4(sc, IF_USB_SUSP_CTRL, val); } val = RD4(sc, IF_USB_SUSP_CTRL); val |= UTMIP_RESET; WR4(sc, IF_USB_SUSP_CTRL, val); val = RD4(sc, UTMIP_BAT_CHRG_CFG0); val |= UTMIP_PD_CHRG; WR4(sc, UTMIP_BAT_CHRG_CFG0, val); val = RD4(sc, UTMIP_XCVR_CFG0); val |= UTMIP_FORCE_PD_POWERDOWN; val |= UTMIP_FORCE_PD2_POWERDOWN; val |= UTMIP_FORCE_PDZI_POWERDOWN; WR4(sc, UTMIP_XCVR_CFG0, val); val = RD4(sc, UTMIP_XCVR_CFG1); val |= UTMIP_FORCE_PDDISC_POWERDOWN; val |= UTMIP_FORCE_PDCHRP_POWERDOWN; val |= UTMIP_FORCE_PDDR_POWERDOWN; WR4(sc, UTMIP_XCVR_CFG1, val); usbpby_enable_cnt--; if (usbpby_enable_cnt <= 0) { rv = clk_enable(sc->clk_pads); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'utmi-pads' clock\n"); return (rv); } val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); val |= UTMIP_OTGPD; val |= UTMIP_BIASPD; bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); rv = clk_disable(sc->clk_pads); if (rv != 0) { device_printf(sc->dev, "Cannot disable 'utmi-pads' clock\n"); return (rv); } } return (0); } static int usbphy_phy_enable(struct phynode *phy, bool enable) { device_t dev; struct usbphy_softc *sc; int rv = 0; dev = phynode_get_device(phy); sc = device_get_softc(dev); if (sc->ifc_type != USB_IFC_TYPE_UTMI) { device_printf(sc->dev, "Only UTMI interface is supported.\n"); return (ENXIO); } if (enable) rv = usbphy_utmi_enable(sc); else rv = usbphy_utmi_disable(sc); return (rv); } static enum usb_ifc_type usb_get_ifc_mode(device_t dev, phandle_t node, char *name) { char *tmpstr; int rv; enum usb_ifc_type ret; rv = OF_getprop_alloc(node, name, (void **)&tmpstr); if (rv <= 0) return (USB_IFC_TYPE_UNKNOWN); ret = USB_IFC_TYPE_UNKNOWN; if (strcmp(tmpstr, "utmi") == 0) ret = USB_IFC_TYPE_UTMI; else if (strcmp(tmpstr, "ulpi") == 0) ret = USB_IFC_TYPE_ULPI; else device_printf(dev, "Unsupported phy type: %s\n", tmpstr); OF_prop_free(tmpstr); return (ret); } static enum usb_dr_mode usb_get_dr_mode(device_t dev, phandle_t node, char *name) { char *tmpstr; int rv; enum usb_dr_mode ret; rv = OF_getprop_alloc(node, name, (void **)&tmpstr); if (rv <= 0) return (USB_DR_MODE_UNKNOWN); ret = USB_DR_MODE_UNKNOWN; if (strcmp(tmpstr, "device") == 0) ret = USB_DR_MODE_DEVICE; else if (strcmp(tmpstr, "host") == 0) ret = USB_DR_MODE_HOST; else if (strcmp(tmpstr, "otg") == 0) ret = USB_DR_MODE_OTG; else device_printf(dev, "Unknown dr mode: %s\n", tmpstr); OF_prop_free(tmpstr); return (ret); } static int usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node) { int rv; rv = OF_getencprop(node, "nvidia,hssync-start-delay", &sc->hssync_start_delay, sizeof (sc->hssync_start_delay)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,elastic-limit", &sc->elastic_limit, sizeof (sc->elastic_limit)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,idle-wait-delay", &sc->idle_wait_delay, sizeof (sc->idle_wait_delay)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,term-range-adj", &sc->term_range_adj, sizeof (sc->term_range_adj)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,xcvr-lsfslew", &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,xcvr-lsrslew", &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,xcvr-hsslew", &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,hssquelch-level", &sc->hssquelch_level, sizeof (sc->hssquelch_level)); if (rv <= 0) return (ENXIO); rv = OF_getencprop(node, "nvidia,hsdiscon-level", &sc->hsdiscon_level, sizeof (sc->hsdiscon_level)); if (rv <= 0) return (ENXIO); rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses"); if (rv >= 1) { sc->xcvr_setup_use_fuses = 1; } else { rv = OF_getencprop(node, "nvidia,xcvr-setup", &sc->xcvr_setup, sizeof (sc->xcvr_setup)); if (rv <= 0) return (ENXIO); } return (0); } static int usbphy_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(dev, "Tegra USB phy"); return (BUS_PROBE_DEFAULT); } static int usbphy_attach(device_t dev) { struct usbphy_softc *sc; int rid, rv; phandle_t node; struct phynode *phynode; struct phynode_init_def phy_init; sc = device_get_softc(dev); sc->dev = dev; rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } rid = 1; sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } node = ofw_bus_get_node(dev); rv = hwreset_get_by_ofw_name(sc->dev, 0, "usb", &sc->reset_usb); if (rv != 0) { device_printf(dev, "Cannot get 'usb' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->reset_pads); if (rv != 0) { device_printf(dev, "Cannot get 'utmi-pads' reset\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "reg", &sc->clk_reg); if (rv != 0) { device_printf(sc->dev, "Cannot get 'reg' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "pll_u", &sc->clk_pllu); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pll_u' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->clk_pads); if (rv != 0) { device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n"); return (ENXIO); } rv = hwreset_deassert(sc->reset_usb); if (rv != 0) { device_printf(dev, "Cannot unreset 'usb' reset\n"); return (ENXIO); } rv = clk_enable(sc->clk_pllu); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pllu' clock\n"); return (ENXIO); } rv = clk_enable(sc->clk_reg); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'reg' clock\n"); return (ENXIO); } if (OF_hasprop(node, "nvidia,has-utmi-pad-registers")) sc->have_utmi_regs = true; sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode"); if (sc->dr_mode == USB_DR_MODE_UNKNOWN) sc->dr_mode = USB_DR_MODE_HOST; sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type"); /* We supports only utmi phy mode for now .... */ if (sc->ifc_type != USB_IFC_TYPE_UTMI) { device_printf(dev, "Unsupported phy type\n"); return (ENXIO); } rv = usbphy_utmi_read_params(sc, node); if (rv < 0) return rv; if (OF_hasprop(node, "vbus-supply")) { rv = regulator_get_by_ofw_property(sc->dev, 0, "vbus-supply", &sc->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot get \"vbus\" regulator\n"); return (ENXIO); } rv = regulator_enable(sc->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable \"vbus\" regulator\n"); return (rv); } } /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = 1; phy_init.ofw_node = node; phynode = phynode_create(dev, &usbphy_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy\n"); return (ENXIO); } if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot create phy\n"); return (ENXIO); } return (0); } static int usbphy_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static device_method_t tegra_usbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, usbphy_probe), DEVMETHOD(device_attach, usbphy_attach), DEVMETHOD(device_detach, usbphy_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(usbphy, tegra_usbphy_driver, tegra_usbphy_methods, sizeof(struct usbphy_softc)); EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver, NULL, NULL, 79); diff --git a/sys/arm/nvidia/tegra_xhci.c b/sys/arm/nvidia/tegra_xhci.c index e80e34f38c38..21ce4dc0540d 100644 --- a/sys/arm/nvidia/tegra_xhci.c +++ b/sys/arm/nvidia/tegra_xhci.c @@ -1,1122 +1,1122 @@ /*- * 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 /* * XHCI driver for Tegra SoCs. */ #include "opt_bus.h" #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" /* FPCI address space */ #define T_XUSB_CFG_0 0x000 #define T_XUSB_CFG_1 0x004 #define CFG_1_BUS_MASTER (1 << 2) #define CFG_1_MEMORY_SPACE (1 << 1) #define CFG_1_IO_SPACE (1 << 0) #define T_XUSB_CFG_2 0x008 #define T_XUSB_CFG_3 0x00C #define T_XUSB_CFG_4 0x010 #define CFG_4_BASE_ADDRESS(x) (((x) & 0x1FFFF) << 15) #define T_XUSB_CFG_5 0x014 #define T_XUSB_CFG_ARU_MAILBOX_CMD 0x0E4 #define ARU_MAILBOX_CMD_INT_EN (1U << 31) #define ARU_MAILBOX_CMD_DEST_XHCI (1 << 30) #define ARU_MAILBOX_CMD_DEST_SMI (1 << 29) #define ARU_MAILBOX_CMD_DEST_PME (1 << 28) #define ARU_MAILBOX_CMD_DEST_FALC (1 << 27) #define T_XUSB_CFG_ARU_MAILBOX_DATA_IN 0x0E8 #define ARU_MAILBOX_DATA_IN_DATA(x) (((x) & 0xFFFFFF) << 0) #define ARU_MAILBOX_DATA_IN_TYPE(x) (((x) & 0x0000FF) << 24) #define T_XUSB_CFG_ARU_MAILBOX_DATA_OUT 0x0EC #define ARU_MAILBOX_DATA_OUT_DATA(x) (((x) >> 0) & 0xFFFFFF) #define ARU_MAILBOX_DATA_OUT_TYPE(x) (((x) >> 24) & 0x0000FF) #define T_XUSB_CFG_ARU_MAILBOX_OWNER 0x0F0 #define ARU_MAILBOX_OWNER_SW 2 #define ARU_MAILBOX_OWNER_FW 1 #define ARU_MAILBOX_OWNER_NONE 0 #define XUSB_CFG_ARU_C11_CSBRANGE 0x41C /* ! UNDOCUMENTED ! */ #define ARU_C11_CSBRANGE_PAGE(x) ((x) >> 9) #define ARU_C11_CSBRANGE_ADDR(x) (0x800 + ((x) & 0x1FF)) #define XUSB_CFG_ARU_SMI_INTR 0x428 /* ! UNDOCUMENTED ! */ #define ARU_SMI_INTR_EN (1 << 3) #define ARU_SMI_INTR_FW_HANG (1 << 1) #define XUSB_CFG_ARU_RST 0x42C /* ! UNDOCUMENTED ! */ #define ARU_RST_RESET (1 << 0) #define XUSB_HOST_CONFIGURATION 0x180 #define CONFIGURATION_CLKEN_OVERRIDE (1U<< 31) #define CONFIGURATION_PW_NO_DEVSEL_ERR_CYA (1 << 19) #define CONFIGURATION_INITIATOR_READ_IDLE (1 << 18) #define CONFIGURATION_INITIATOR_WRITE_IDLE (1 << 17) #define CONFIGURATION_WDATA_LEAD_CYA (1 << 15) #define CONFIGURATION_WR_INTRLV_CYA (1 << 14) #define CONFIGURATION_TARGET_READ_IDLE (1 << 11) #define CONFIGURATION_TARGET_WRITE_IDLE (1 << 10) #define CONFIGURATION_MSI_VEC_EMPTY (1 << 9) #define CONFIGURATION_UFPCI_MSIAW (1 << 7) #define CONFIGURATION_UFPCI_PWPASSPW (1 << 6) #define CONFIGURATION_UFPCI_PASSPW (1 << 5) #define CONFIGURATION_UFPCI_PWPASSNPW (1 << 4) #define CONFIGURATION_DFPCI_PWPASSNPW (1 << 3) #define CONFIGURATION_DFPCI_RSPPASSPW (1 << 2) #define CONFIGURATION_DFPCI_PASSPW (1 << 1) #define CONFIGURATION_EN_FPCI (1 << 0) /* IPFS address space */ #define XUSB_HOST_FPCI_ERROR_MASKS 0x184 #define FPCI_ERROR_MASTER_ABORT (1 << 2) #define FPCI_ERRORI_DATA_ERROR (1 << 1) #define FPCI_ERROR_TARGET_ABORT (1 << 0) #define XUSB_HOST_INTR_MASK 0x188 #define INTR_IP_INT_MASK (1 << 16) #define INTR_MSI_MASK (1 << 8) #define INTR_INT_MASK (1 << 0) #define XUSB_HOST_CLKGATE_HYSTERESIS 0x1BC /* CSB Falcon CPU */ #define XUSB_FALCON_CPUCTL 0x100 #define CPUCTL_STOPPED (1 << 5) #define CPUCTL_HALTED (1 << 4) #define CPUCTL_HRESET (1 << 3) #define CPUCTL_SRESET (1 << 2) #define CPUCTL_STARTCPU (1 << 1) #define CPUCTL_IINVAL (1 << 0) #define XUSB_FALCON_BOOTVEC 0x104 #define XUSB_FALCON_DMACTL 0x10C #define XUSB_FALCON_IMFILLRNG1 0x154 #define IMFILLRNG1_TAG_HI(x) (((x) & 0xFFF) << 16) #define IMFILLRNG1_TAG_LO(x) (((x) & 0xFFF) << 0) #define XUSB_FALCON_IMFILLCTL 0x158 /* CSB mempool */ #define XUSB_CSB_MEMPOOL_APMAP 0x10181C #define APMAP_BOOTPATH (1U << 31) #define XUSB_CSB_MEMPOOL_ILOAD_ATTR 0x101A00 #define XUSB_CSB_MEMPOOL_ILOAD_BASE_LO 0x101A04 #define XUSB_CSB_MEMPOOL_ILOAD_BASE_HI 0x101A08 #define XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE 0x101A10 #define L2IMEMOP_SIZE_OFFSET(x) (((x) & 0x3FF) << 8) #define L2IMEMOP_SIZE_SIZE(x) (((x) & 0x0FF) << 24) #define XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG 0x101A14 #define L2IMEMOP_INVALIDATE_ALL (0x40 << 24) #define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << 24) #define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101A18 #define L2IMEMOP_RESULT_VLD (1U << 31) #define XUSB_CSB_IMEM_BLOCK_SIZE 256 #define TEGRA_XHCI_SS_HIGH_SPEED 120000000 #define TEGRA_XHCI_SS_LOW_SPEED 12000000 /* MBOX commands. */ #define MBOX_CMD_MSG_ENABLED 1 #define MBOX_CMD_INC_FALC_CLOCK 2 #define MBOX_CMD_DEC_FALC_CLOCK 3 #define MBOX_CMD_INC_SSPI_CLOCK 4 #define MBOX_CMD_DEC_SSPI_CLOCK 5 #define MBOX_CMD_SET_BW 6 #define MBOX_CMD_SET_SS_PWR_GATING 7 #define MBOX_CMD_SET_SS_PWR_UNGATING 8 #define MBOX_CMD_SAVE_DFE_CTLE_CTX 9 #define MBOX_CMD_AIRPLANE_MODE_ENABLED 10 #define MBOX_CMD_AIRPLANE_MODE_DISABLED 11 #define MBOX_CMD_START_HSIC_IDLE 12 #define MBOX_CMD_STOP_HSIC_IDLE 13 #define MBOX_CMD_DBC_WAKE_STACK 14 #define MBOX_CMD_HSIC_PRETEND_CONNECT 15 #define MBOX_CMD_RESET_SSPI 16 #define MBOX_CMD_DISABLE_SS_LFPS_DETECTION 17 #define MBOX_CMD_ENABLE_SS_LFPS_DETECTION 18 /* MBOX responses. */ #define MBOX_CMD_ACK (0x80 + 0) #define MBOX_CMD_NAK (0x80 + 1) #define IPFS_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_ipfs, (_r), (_v)) #define IPFS_RD4(_sc, _r) bus_read_4((_sc)->mem_res_ipfs, (_r)) #define FPCI_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_fpci, (_r), (_v)) #define FPCI_RD4(_sc, _r) bus_read_4((_sc)->mem_res_fpci, (_r)) #define LOCK(_sc) mtx_lock(&(_sc)->mtx) #define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define SLEEP(_sc, timeout) \ mtx_sleep(sc, &sc->mtx, 0, "tegra_xhci", timeout); #define LOCK_INIT(_sc) \ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_xhci", MTX_DEF) #define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) #define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) #define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) struct tegra_xusb_fw_hdr { uint32_t boot_loadaddr_in_imem; uint32_t boot_codedfi_offset; uint32_t boot_codetag; uint32_t boot_codesize; uint32_t phys_memaddr; uint16_t reqphys_memsize; uint16_t alloc_phys_memsize; uint32_t rodata_img_offset; uint32_t rodata_section_start; uint32_t rodata_section_end; uint32_t main_fnaddr; uint32_t fwimg_cksum; uint32_t fwimg_created_time; uint32_t imem_resident_start; uint32_t imem_resident_end; uint32_t idirect_start; uint32_t idirect_end; uint32_t l2_imem_start; uint32_t l2_imem_end; uint32_t version_id; uint8_t init_ddirect; uint8_t reserved[3]; uint32_t phys_addr_log_buffer; uint32_t total_log_entries; uint32_t dequeue_ptr; uint32_t dummy[2]; uint32_t fwimg_len; uint8_t magic[8]; uint32_t ss_low_power_entry_timeout; uint8_t num_hsic_port; uint8_t ss_portmap; uint8_t build; uint8_t padding[137]; /* Pad to 256 bytes */ }; struct xhci_soc; struct tegra_xhci_softc { struct xhci_softc xhci_softc; device_t dev; struct xhci_soc *soc; struct mtx mtx; struct resource *mem_res_fpci; struct resource *mem_res_ipfs; struct resource *irq_res_mbox; void *irq_hdl_mbox; clk_t clk_xusb_host; clk_t clk_xusb_gate; clk_t clk_xusb_falcon_src; clk_t clk_xusb_ss; clk_t clk_xusb_hs_src; clk_t clk_xusb_fs_src; hwreset_t hwreset_xusb_host; hwreset_t hwreset_xusb_ss; regulator_t regulators[16]; /* Safe maximum */ phy_t phys[8]; /* Safe maximum */ struct intr_config_hook irq_hook; bool xhci_inited; void *fw_vaddr; vm_size_t fw_size; }; struct xhci_soc { char *fw_name; char **regulator_names; char **phy_names; }; /* Tegra 124 config */ static char *tegra124_reg_names[] = { "avddio-pex-supply", "dvddio-pex-supply", "avdd-usb-supply", "avdd-pll-utmip-supply", "avdd-pll-erefe-supply", "avdd-usb-ss-pll-supply", "hvdd-usb-ss-supply", "hvdd-usb-ss-pll-e-supply", NULL }; static char *tegra124_phy_names[] = { "usb2-0", "usb2-1", "usb2-2", "usb3-0", NULL }; static struct xhci_soc tegra124_soc = { .fw_name = "tegra124_xusb_fw", .regulator_names = tegra124_reg_names, .phy_names = tegra124_phy_names, }; /* Tegra 210 config */ static char *tegra210_reg_names[] = { "dvddio-pex-supply", "hvddio-pex-supply", "avdd-usb-supply", "avdd-pll-utmip-supply", "avdd-pll-uerefe-supply", "dvdd-usb-ss-pll-supply", "hvdd-usb-ss-pll-e-supply", NULL }; static char *tegra210_phy_names[] = { "usb2-0", "usb2-1", "usb2-2", "usb2-3", "usb3-0", "usb3-1", NULL }; static struct xhci_soc tegra210_soc = { .fw_name = "tegra210_xusb_fw", .regulator_names = tegra210_reg_names, .phy_names = tegra210_phy_names, }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-xusb", (uintptr_t)&tegra124_soc}, {"nvidia,tegra210-xusb", (uintptr_t)&tegra210_soc}, {NULL, 0} }; static uint32_t CSB_RD4(struct tegra_xhci_softc *sc, uint32_t addr) { FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr)); return (FPCI_RD4(sc, ARU_C11_CSBRANGE_ADDR(addr))); } static void CSB_WR4(struct tegra_xhci_softc *sc, uint32_t addr, uint32_t val) { FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr)); FPCI_WR4(sc, ARU_C11_CSBRANGE_ADDR(addr), val); } static int get_fdt_resources(struct tegra_xhci_softc *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); } } rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_host", &sc->hwreset_xusb_host); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_host' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_ss", &sc->hwreset_xusb_ss); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_ss' reset\n"); return (ENXIO); } /* Phys. */ for (i = 0; sc->soc->phy_names[i] != NULL; i++) { if (i >= nitems(sc->phys)) { device_printf(sc->dev, "Too many phys present in DT.\n"); return (EOVERFLOW); } rv = phy_get_by_ofw_name(sc->dev, 0, sc->soc->phy_names[i], sc->phys + i); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get '%s' phy.\n", sc->soc->phy_names[i]); return (ENXIO); } } rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_host", &sc->clk_xusb_host); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_host' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_falcon_src", &sc->clk_xusb_falcon_src); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_falcon_src' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_ss", &sc->clk_xusb_ss); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_ss' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_hs_src", &sc->clk_xusb_hs_src); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_hs_src' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_fs_src", &sc->clk_xusb_fs_src); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_fs_src' clock\n"); return (ENXIO); } /* Clock xusb_gate is missing in mainstream DT */ rv = clk_get_by_name(sc->dev, "xusb_gate", &sc->clk_xusb_gate); if (rv != 0) { device_printf(sc->dev, "Cannot get 'xusb_gate' clock\n"); return (ENXIO); } return (0); } static int enable_fdt_resources(struct tegra_xhci_softc *sc) { int i, rv; rv = hwreset_assert(sc->hwreset_xusb_host); if (rv != 0) { device_printf(sc->dev, "Cannot reset 'xusb_host' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_xusb_ss); if (rv != 0) { device_printf(sc->dev, "Cannot reset 'xusb_ss' reset\n"); return (rv); } /* 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); } } /* Power off XUSB host and XUSB SS domains. */ rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); if (rv != 0) { device_printf(sc->dev, "Cannot powerdown 'xusba' domain\n"); return (rv); } rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); if (rv != 0) { device_printf(sc->dev, "Cannot powerdown 'xusbc' domain\n"); return (rv); } /* Setup XUSB ss_src clock first */ clk_set_freq(sc->clk_xusb_ss, TEGRA_XHCI_SS_HIGH_SPEED, 0); if (rv != 0) return (rv); /* The XUSB gate clock must be enabled before XUSBA can be powered. */ rv = clk_enable(sc->clk_xusb_gate); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'xusb_gate' clock\n"); return (rv); } /* Power on XUSB host and XUSB SS domains. */ rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, sc->clk_xusb_host, sc->hwreset_xusb_host); if (rv != 0) { device_printf(sc->dev, "Cannot powerup 'xusbc' domain\n"); return (rv); } rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA, sc->clk_xusb_ss, sc->hwreset_xusb_ss); if (rv != 0) { device_printf(sc->dev, "Cannot powerup 'xusba' domain\n"); return (rv); } /* Enable rest of clocks */ rv = clk_enable(sc->clk_xusb_falcon_src); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'xusb_falcon_src' clock\n"); return (rv); } rv = clk_enable(sc->clk_xusb_fs_src); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'xusb_fs_src' clock\n"); return (rv); } rv = clk_enable(sc->clk_xusb_hs_src); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'xusb_hs_src' clock\n"); return (rv); } /* Phys. */ for (i = 0; i < nitems(sc->phys); i++) { if (sc->phys[i] == NULL) continue; rv = phy_enable(sc->phys[i]); if (rv != 0) { device_printf(sc->dev, "Cannot enable '%s' phy\n", sc->soc->phy_names[i]); return (rv); } } return (0); } /* Respond by ACK/NAK back to FW */ static void mbox_send_ack(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data) { uint32_t reg; reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data); FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg); reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN; FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); } /* Sent command to FW */ static int mbox_send_cmd(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data) { uint32_t reg; int i; reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); if (reg != ARU_MAILBOX_OWNER_NONE) { device_printf(sc->dev, "CPU mailbox is busy: 0x%08X\n", reg); return (EBUSY); } /* XXX Is this right? Retry loop? Wait before send? */ FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_SW); reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); if (reg != ARU_MAILBOX_OWNER_SW) { device_printf(sc->dev, "Cannot acquire CPU mailbox: 0x%08X\n", reg); return (EBUSY); } reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data); FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg); reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN; FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); for (i = 250; i > 0; i--) { reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); if (reg == ARU_MAILBOX_OWNER_NONE) break; DELAY(100); } if (i <= 0) { device_printf(sc->dev, "Command response timeout: 0x%08X\n", reg); return (ETIMEDOUT); } return(0); } static void process_msg(struct tegra_xhci_softc *sc, uint32_t req_cmd, uint32_t req_data, uint32_t *resp_cmd, uint32_t *resp_data) { uint64_t freq; int rv; /* In most cases, data are echoed back. */ *resp_data = req_data; switch (req_cmd) { case MBOX_CMD_INC_FALC_CLOCK: case MBOX_CMD_DEC_FALC_CLOCK: rv = clk_set_freq(sc->clk_xusb_falcon_src, req_data * 1000ULL, 0); if (rv == 0) { rv = clk_get_freq(sc->clk_xusb_falcon_src, &freq); *resp_data = (uint32_t)(freq / 1000); } *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK; break; case MBOX_CMD_INC_SSPI_CLOCK: case MBOX_CMD_DEC_SSPI_CLOCK: rv = clk_set_freq(sc->clk_xusb_ss, req_data * 1000ULL, 0); if (rv == 0) { rv = clk_get_freq(sc->clk_xusb_ss, &freq); *resp_data = (uint32_t)(freq / 1000); } *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK; break; case MBOX_CMD_SET_BW: /* No respense is expected. */ *resp_cmd = 0; break; case MBOX_CMD_SET_SS_PWR_GATING: case MBOX_CMD_SET_SS_PWR_UNGATING: *resp_cmd = MBOX_CMD_NAK; break; case MBOX_CMD_SAVE_DFE_CTLE_CTX: /* Not implemented yet. */ *resp_cmd = MBOX_CMD_ACK; break; case MBOX_CMD_START_HSIC_IDLE: case MBOX_CMD_STOP_HSIC_IDLE: /* Not implemented yet. */ *resp_cmd = MBOX_CMD_NAK; break; case MBOX_CMD_DISABLE_SS_LFPS_DETECTION: case MBOX_CMD_ENABLE_SS_LFPS_DETECTION: /* Not implemented yet. */ *resp_cmd = MBOX_CMD_NAK; break; case MBOX_CMD_AIRPLANE_MODE_ENABLED: case MBOX_CMD_AIRPLANE_MODE_DISABLED: case MBOX_CMD_DBC_WAKE_STACK: case MBOX_CMD_HSIC_PRETEND_CONNECT: case MBOX_CMD_RESET_SSPI: device_printf(sc->dev, "Received unused/unexpected command: %u\n", req_cmd); *resp_cmd = 0; break; default: device_printf(sc->dev, "Received unknown command: %u\n", req_cmd); } } static void intr_mbox(void *arg) { struct tegra_xhci_softc *sc; uint32_t reg, msg, resp_cmd, resp_data; sc = (struct tegra_xhci_softc *)arg; /* Clear interrupt first */ reg = FPCI_RD4(sc, XUSB_CFG_ARU_SMI_INTR); FPCI_WR4(sc, XUSB_CFG_ARU_SMI_INTR, reg); if (reg & ARU_SMI_INTR_FW_HANG) { device_printf(sc->dev, "XUSB CPU firmware hang!!! CPUCTL: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL)); } msg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_OUT); resp_cmd = 0; process_msg(sc, ARU_MAILBOX_DATA_OUT_TYPE(msg), ARU_MAILBOX_DATA_OUT_DATA(msg), &resp_cmd, &resp_data); if (resp_cmd != 0) mbox_send_ack(sc, resp_cmd, resp_data); else FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_NONE); reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); reg &= ~ARU_MAILBOX_CMD_DEST_SMI; FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); } static int load_fw(struct tegra_xhci_softc *sc) { const struct firmware *fw; const struct tegra_xusb_fw_hdr *fw_hdr; vm_paddr_t fw_paddr, fw_base; void *fw_vaddr; vm_size_t fw_size; uint32_t code_tags, code_size; struct clocktime fw_clock; struct timespec fw_timespec; int i; /* Reset ARU */ FPCI_WR4(sc, XUSB_CFG_ARU_RST, ARU_RST_RESET); DELAY(3000); /* Check if FALCON already runs */ if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO) != 0) { device_printf(sc->dev, "XUSB CPU is already loaded, CPUCTL: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL)); return (0); } fw = firmware_get(sc->soc->fw_name); if (fw == NULL) { device_printf(sc->dev, "Cannot read xusb firmware\n"); return (ENOENT); } /* Allocate uncached memory and copy firmware into. */ fw_hdr = (const struct tegra_xusb_fw_hdr *)fw->data; fw_size = fw_hdr->fwimg_len; fw_vaddr = kmem_alloc_contig(fw_size, M_WAITOK, 0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); fw_paddr = vtophys((uintptr_t)fw_vaddr); fw_hdr = (const struct tegra_xusb_fw_hdr *)fw_vaddr; memcpy(fw_vaddr, fw->data, fw_size); firmware_put(fw, FIRMWARE_UNLOAD); sc->fw_vaddr = fw_vaddr; sc->fw_size = fw_size; /* Setup firmware physical address and size. */ fw_base = fw_paddr + sizeof(*fw_hdr); CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_ATTR, fw_size); CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO, fw_base & 0xFFFFFFFF); CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_HI, (uint64_t)fw_base >> 32); CSB_WR4(sc, XUSB_CSB_MEMPOOL_APMAP, APMAP_BOOTPATH); /* Invalidate full L2IMEM context. */ CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG, L2IMEMOP_INVALIDATE_ALL); /* Program load of L2IMEM by boot code. */ code_tags = howmany(fw_hdr->boot_codetag, XUSB_CSB_IMEM_BLOCK_SIZE); code_size = howmany(fw_hdr->boot_codesize, XUSB_CSB_IMEM_BLOCK_SIZE); CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE, L2IMEMOP_SIZE_OFFSET(code_tags) | L2IMEMOP_SIZE_SIZE(code_size)); /* Execute L2IMEM boot code fetch. */ CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG, L2IMEMOP_LOAD_LOCKED_RESULT); /* Program FALCON auto-fill range and block count */ CSB_WR4(sc, XUSB_FALCON_IMFILLCTL, code_size); CSB_WR4(sc, XUSB_FALCON_IMFILLRNG1, IMFILLRNG1_TAG_LO(code_tags) | IMFILLRNG1_TAG_HI(code_tags + code_size)); CSB_WR4(sc, XUSB_FALCON_DMACTL, 0); /* Wait for CPU */ for (i = 500; i > 0; i--) { if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT) & L2IMEMOP_RESULT_VLD) break; DELAY(100); } if (i <= 0) { device_printf(sc->dev, "Timedout while wating for DMA, " "state: 0x%08X\n", CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT)); return (ETIMEDOUT); } /* Boot FALCON cpu */ CSB_WR4(sc, XUSB_FALCON_BOOTVEC, fw_hdr->boot_codetag); CSB_WR4(sc, XUSB_FALCON_CPUCTL, CPUCTL_STARTCPU); /* Wait for CPU */ for (i = 50; i > 0; i--) { if (CSB_RD4(sc, XUSB_FALCON_CPUCTL) == CPUCTL_STOPPED) break; DELAY(100); } if (i <= 0) { device_printf(sc->dev, "Timedout while wating for FALCON cpu, " "state: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL)); return (ETIMEDOUT); } fw_timespec.tv_sec = fw_hdr->fwimg_created_time; fw_timespec.tv_nsec = 0; clock_ts_to_ct(&fw_timespec, &fw_clock); device_printf(sc->dev, " Falcon firmware version: %02X.%02X.%04X," " (%d/%d/%d %d:%02d:%02d UTC)\n", (fw_hdr->version_id >> 24) & 0xFF,(fw_hdr->version_id >> 15) & 0xFF, fw_hdr->version_id & 0xFFFF, fw_clock.day, fw_clock.mon, fw_clock.year, fw_clock.hour, fw_clock.min, fw_clock.sec); return (0); } static int init_hw(struct tegra_xhci_softc *sc) { int rv; uint32_t reg; rman_res_t base_addr; base_addr = rman_get_start(sc->xhci_softc.sc_io_res); /* Enable FPCI access */ reg = IPFS_RD4(sc, XUSB_HOST_CONFIGURATION); reg |= CONFIGURATION_EN_FPCI; IPFS_WR4(sc, XUSB_HOST_CONFIGURATION, reg); IPFS_RD4(sc, XUSB_HOST_CONFIGURATION); /* Program bar for XHCI base address */ reg = FPCI_RD4(sc, T_XUSB_CFG_4); reg &= ~CFG_4_BASE_ADDRESS(~0); reg |= CFG_4_BASE_ADDRESS((uint32_t)base_addr >> 15); FPCI_WR4(sc, T_XUSB_CFG_4, reg); FPCI_WR4(sc, T_XUSB_CFG_5, (uint32_t)((uint64_t)(base_addr) >> 32)); /* Enable bus master */ reg = FPCI_RD4(sc, T_XUSB_CFG_1); reg |= CFG_1_IO_SPACE; reg |= CFG_1_MEMORY_SPACE; reg |= CFG_1_BUS_MASTER; FPCI_WR4(sc, T_XUSB_CFG_1, reg); /* Enable Interrupts */ reg = IPFS_RD4(sc, XUSB_HOST_INTR_MASK); reg |= INTR_IP_INT_MASK; IPFS_WR4(sc, XUSB_HOST_INTR_MASK, reg); /* Set hysteresis */ IPFS_WR4(sc, XUSB_HOST_CLKGATE_HYSTERESIS, 128); rv = load_fw(sc); if (rv != 0) return rv; return (0); } static int tegra_xhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Nvidia Tegra XHCI controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int tegra_xhci_detach(device_t dev) { struct tegra_xhci_softc *sc; struct xhci_softc *xsc; sc = device_get_softc(dev); xsc = &sc->xhci_softc; /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->xhci_inited) { usb_callout_drain(&xsc->sc_callout); xhci_halt_controller(xsc); } if (xsc->sc_irq_res && xsc->sc_intr_hdl) { bus_teardown_intr(dev, xsc->sc_irq_res, xsc->sc_intr_hdl); xsc->sc_intr_hdl = NULL; } if (xsc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(xsc->sc_irq_res), xsc->sc_irq_res); xsc->sc_irq_res = NULL; } if (xsc->sc_io_res != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(xsc->sc_io_res), xsc->sc_io_res); xsc->sc_io_res = NULL; } if (sc->xhci_inited) xhci_uninit(xsc); if (sc->irq_hdl_mbox != NULL) bus_teardown_intr(dev, sc->irq_res_mbox, sc->irq_hdl_mbox); if (sc->fw_vaddr != NULL) kmem_free(sc->fw_vaddr, sc->fw_size); LOCK_DESTROY(sc); return (0); } static int tegra_xhci_attach(device_t dev) { struct tegra_xhci_softc *sc; struct xhci_softc *xsc; int rv, rid; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; sc->soc = (struct xhci_soc *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; node = ofw_bus_get_node(dev); xsc = &sc->xhci_softc; LOCK_INIT(sc); rv = get_fdt_resources(sc, node); if (rv != 0) { rv = ENXIO; goto error; } rv = enable_fdt_resources(sc); if (rv != 0) { rv = ENXIO; goto error; } /* Allocate resources. */ rid = 0; xsc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (xsc->sc_io_res == NULL) { device_printf(dev, "Could not allocate HCD memory resources\n"); rv = ENXIO; goto error; } rid = 1; sc->mem_res_fpci = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res_fpci == NULL) { device_printf(dev, "Could not allocate FPCI memory resources\n"); rv = ENXIO; goto error; } rid = 2; sc->mem_res_ipfs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res_ipfs == NULL) { device_printf(dev, "Could not allocate IPFS memory resources\n"); rv = ENXIO; goto error; } rid = 0; xsc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (xsc->sc_irq_res == NULL) { device_printf(dev, "Could not allocate HCD IRQ resources\n"); rv = ENXIO; goto error; } rid = 1; sc->irq_res_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res_mbox == NULL) { device_printf(dev, "Could not allocate MBOX IRQ resources\n"); rv = ENXIO; goto error; } rv = init_hw(sc); if (rv != 0) { device_printf(dev, "Could not initialize XUSB hardware\n"); goto error; } /* Wakeup and enable firmaware */ rv = mbox_send_cmd(sc, MBOX_CMD_MSG_ENABLED, 0); if (rv != 0) { device_printf(sc->dev, "Could not enable XUSB firmware\n"); goto error; } /* Fill data for XHCI driver. */ xsc->sc_bus.parent = dev; xsc->sc_bus.devices = xsc->sc_devices; xsc->sc_bus.devices_max = XHCI_MAX_DEVICES; xsc->sc_io_tag = rman_get_bustag(xsc->sc_io_res); xsc->sc_io_hdl = rman_get_bushandle(xsc->sc_io_res); xsc->sc_io_size = rman_get_size(xsc->sc_io_res); strlcpy(xsc->sc_vendor, "Nvidia", sizeof(xsc->sc_vendor)); /* Add USB bus device. */ xsc->sc_bus.bdev = device_add_child(sc->dev, "usbus", -1); if (xsc->sc_bus.bdev == NULL) { device_printf(sc->dev, "Could not add USB device\n"); rv = ENXIO; goto error; } device_set_ivars(xsc->sc_bus.bdev, &xsc->sc_bus); device_set_desc(xsc->sc_bus.bdev, "Nvidia USB 3.0 controller"); rv = xhci_init(xsc, sc->dev, 1); if (rv != 0) { device_printf(sc->dev, "USB init failed: %d\n", rv); goto error; } sc->xhci_inited = true; rv = xhci_start_controller(xsc); if (rv != 0) { device_printf(sc->dev, "Could not start XHCI controller: %d\n", rv); goto error; } rv = bus_setup_intr(dev, sc->irq_res_mbox, INTR_TYPE_MISC | INTR_MPSAFE, NULL, intr_mbox, sc, &sc->irq_hdl_mbox); if (rv != 0) { device_printf(dev, "Could not setup error IRQ: %d\n",rv); xsc->sc_intr_hdl = NULL; goto error; } rv = bus_setup_intr(dev, xsc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)xhci_interrupt, xsc, &xsc->sc_intr_hdl); if (rv != 0) { device_printf(dev, "Could not setup error IRQ: %d\n",rv); xsc->sc_intr_hdl = NULL; goto error; } /* Probe the bus. */ rv = device_probe_and_attach(xsc->sc_bus.bdev); if (rv != 0) { device_printf(sc->dev, "Could not initialize USB: %d\n", rv); goto error; } return (0); error: panic("XXXXX"); tegra_xhci_detach(dev); return (rv); } static device_method_t xhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_xhci_probe), DEVMETHOD(device_attach, tegra_xhci_attach), DEVMETHOD(device_detach, tegra_xhci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD_END }; static DEFINE_CLASS_0(xhci, xhci_driver, xhci_methods, sizeof(struct tegra_xhci_softc)); DRIVER_MODULE(tegra_xhci, simplebus, xhci_driver, NULL, NULL); MODULE_DEPEND(tegra_xhci, usb, 1, 1, 1); diff --git a/sys/arm/qualcomm/ipq4018_usb_hs_phy.c b/sys/arm/qualcomm/ipq4018_usb_hs_phy.c index 75bfa931e8e3..618911fef640 100644 --- a/sys/arm/qualcomm/ipq4018_usb_hs_phy.c +++ b/sys/arm/qualcomm/ipq4018_usb_hs_phy.c @@ -1,229 +1,229 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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 #include #include #include #include #include #include "phynode_if.h" #include "phynode_usb_if.h" static struct ofw_compat_data compat_data[] = { {"qcom,usb-hs-ipq4019-phy", 1}, {NULL, 0}, }; struct ipq4018_usb_hs_phy_softc { device_t dev; }; struct ipq4018_usb_hs_phynode_sc { struct phynode_usb_sc usb_sc; int mode; hwreset_t por_rst; hwreset_t srif_rst; }; static int ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable) { struct ipq4018_usb_hs_phynode_sc *sc; device_t dev; int rv; dev = phynode_get_device(phynode); sc = phynode_get_softc(phynode); /* * * For power-off - assert por, sleep for 10ms, assert srif, * sleep for 10ms */ rv = hwreset_assert(sc->por_rst); if (rv != 0) goto done; DELAY(10*1000); rv = hwreset_assert(sc->srif_rst); if (rv != 0) goto done; DELAY(10*1000); /* * For power-on - power off first, then deassert srif, then * sleep for 10ms, then deassert por. */ if (enable) { rv = hwreset_deassert(sc->srif_rst); if (rv != 0) goto done; DELAY(10*1000); rv = hwreset_deassert(sc->por_rst); if (rv != 0) goto done; DELAY(10*1000); } done: if (rv != 0) { device_printf(dev, "%s: failed, rv=%d\n", __func__, rv); } return (rv); } /* Phy controller class and methods. */ static phynode_method_t ipq4018_usb_hs_phynode_methods[] = { PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_hs_phynode_phy_enable), PHYNODEUSBMETHOD_END }; DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class, ipq4018_usb_hs_phynode_methods, sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class); static int ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc, phandle_t node) { struct phynode *phynode; struct phynode_init_def phy_init; struct ipq4018_usb_hs_phynode_sc *phy_sc; int rv; hwreset_t por_rst = NULL, srif_rst = NULL; /* FDT resources */ rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'por_rst' reset\n"); goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'srif_rst' reset\n"); goto fail; } /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = 1; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy.\n"); return (ENXIO); } phy_sc = phynode_get_softc(phynode); phy_sc->por_rst = por_rst; phy_sc->srif_rst = srif_rst; if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot register phy.\n"); return (ENXIO); } (void) ipq4018_usb_hs_phynode_phy_enable(phynode, true); return (0); fail: if (por_rst != NULL) hwreset_release(por_rst); if (srif_rst != NULL) hwreset_release(srif_rst); return (ENXIO); } static int ipq4018_usb_hs_usbphy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY"); return (BUS_PROBE_DEFAULT); } static int ipq4018_usb_hs_usbphy_attach(device_t dev) { struct ipq4018_usb_hs_phy_softc *sc; phandle_t node; int rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); rv = ipq4018_usb_hs_usbphy_init_phy(sc, node); if (rv != 0) goto fail; return (bus_generic_attach(dev)); fail: return (ENXIO); } static int ipq4018_usb_hs_usbphy_detach(device_t dev) { return (0); } static device_method_t ipq4018_usb_hs_usbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ipq4018_usb_hs_usbphy_probe), DEVMETHOD(device_attach, ipq4018_usb_hs_usbphy_attach), DEVMETHOD(device_detach, ipq4018_usb_hs_usbphy_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver, ipq4018_usb_hs_usbphy_methods, sizeof(struct ipq4018_usb_hs_phy_softc)); EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus, ipq4018_usb_hs_usbphy_driver, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); diff --git a/sys/arm/qualcomm/ipq4018_usb_ss_phy.c b/sys/arm/qualcomm/ipq4018_usb_ss_phy.c index 22dc92ffd2ec..66ef4c9fe732 100644 --- a/sys/arm/qualcomm/ipq4018_usb_ss_phy.c +++ b/sys/arm/qualcomm/ipq4018_usb_ss_phy.c @@ -1,209 +1,209 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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 #include #include #include #include #include #include "phynode_if.h" #include "phynode_usb_if.h" static struct ofw_compat_data compat_data[] = { {"qcom,usb-ss-ipq4019-phy", 1}, {NULL, 0}, }; struct ipq4018_usb_ss_phy_softc { device_t dev; }; struct ipq4018_usb_ss_phynode_sc { struct phynode_usb_sc usb_sc; int mode; hwreset_t por_rst; }; static int ipq4018_usb_ss_phynode_phy_enable(struct phynode *phynode, bool enable) { struct ipq4018_usb_ss_phynode_sc *sc; device_t dev; int rv; dev = phynode_get_device(phynode); sc = phynode_get_softc(phynode); /* * For power-off - assert por, sleep for 10ms */ rv = hwreset_assert(sc->por_rst); if (rv != 0) goto done; DELAY(10*1000); /* * For power-on - power off first, then deassert por. */ if (enable) { rv = hwreset_deassert(sc->por_rst); if (rv != 0) goto done; DELAY(10*1000); } done: if (rv != 0) { device_printf(dev, "%s: failed, rv=%d\n", __func__, rv); } return (rv); } /* Phy controller class and methods. */ static phynode_method_t ipq4018_usb_ss_phynode_methods[] = { PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_ss_phynode_phy_enable), PHYNODEUSBMETHOD_END }; DEFINE_CLASS_1(ipq4018_usb_ss_phynode, ipq4018_usb_ss_phynode_class, ipq4018_usb_ss_phynode_methods, sizeof(struct ipq4018_usb_ss_phynode_sc), phynode_usb_class); static int ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc *sc, phandle_t node) { struct phynode *phynode; struct phynode_init_def phy_init; struct ipq4018_usb_ss_phynode_sc *phy_sc; int rv; hwreset_t por_rst = NULL; /* FDT resources */ rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'por_rst' reset\n"); goto fail; } /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = 1; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &ipq4018_usb_ss_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy.\n"); return (ENXIO); } phy_sc = phynode_get_softc(phynode); phy_sc->por_rst = por_rst; if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot register phy.\n"); return (ENXIO); } (void) ipq4018_usb_ss_phynode_phy_enable(phynode, true); return (0); fail: if (por_rst != NULL) hwreset_release(por_rst); return (ENXIO); } static int ipq4018_usb_ss_usbphy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "IPQ4018/IPQ4019 USB SS PHY"); return (BUS_PROBE_DEFAULT); } static int ipq4018_usb_ss_usbphy_attach(device_t dev) { struct ipq4018_usb_ss_phy_softc *sc; phandle_t node; int rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); rv = ipq4018_usb_ss_usbphy_init_phy(sc, node); if (rv != 0) goto fail; return (bus_generic_attach(dev)); fail: return (ENXIO); } static int ipq4018_usb_ss_usbphy_detach(device_t dev) { return (0); } static device_method_t ipq4018_usb_ss_usbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ipq4018_usb_ss_usbphy_probe), DEVMETHOD(device_attach, ipq4018_usb_ss_usbphy_attach), DEVMETHOD(device_detach, ipq4018_usb_ss_usbphy_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(ipq4018_usb_ss_usbphy, ipq4018_usb_ss_usbphy_driver, ipq4018_usb_ss_usbphy_methods, sizeof(struct ipq4018_usb_ss_phy_softc)); EARLY_DRIVER_MODULE(ipq4018_usb_ss_usbphy, simplebus, ipq4018_usb_ss_usbphy_driver, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); diff --git a/sys/arm64/nvidia/tegra210/tegra210_car.c b/sys/arm64/nvidia/tegra210/tegra210_car.c index 2046782805d6..bff91024192f 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_car.c +++ b/sys/arm64/nvidia/tegra210/tegra210_car.c @@ -1,597 +1,597 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2020 Michal Meloun * * 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 #include #include #include #include #include -#include +#include #include #include #include #include #include "clkdev_if.h" #include "hwreset_if.h" #include "tegra210_car.h" static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-car", 1}, {NULL, 0}, }; #define PLIST(x) static const char *x[] /* Pure multiplexer. */ #define MUX(_id, cname, plists, o, s, w) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = plists, \ .clkdef.parent_cnt = nitems(plists), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .width = w, \ } /* Fractional divider (7.1). */ #define DIV7_1(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .i_shift = (s) + 1, \ .i_width = 7, \ .f_shift = s, \ .f_width = 1, \ } /* Integer divider. */ #define DIV(_id, cname, plist, o, s, w, f) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .i_shift = s, \ .i_width = w, \ .div_flags = f, \ } /* Gate in PLL block. */ #define GATE_PLL(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 3, \ .on_value = 3, \ .off_value = 0, \ } /* Standard gate. */ #define GATE(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 1, \ .on_value = 1, \ .off_value = 0, \ } /* Inverted gate. */ #define GATE_INV(_id, cname, plist, o, s) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = o, \ .shift = s, \ .mask = 1, \ .on_value = 0, \ .off_value = 1, \ } /* Fixed rate clock. */ #define FRATE(_id, cname, _freq) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = NULL, \ .clkdef.parent_cnt = 0, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .freq = _freq, \ } /* Fixed rate multipier/divider. */ #define FACT(_id, cname, pname, _mult, _div) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .mult = _mult, \ .div = _div, \ } static uint32_t osc_freqs[16] = { [0] = 13000000, [1] = 16800000, [4] = 19200000, [5] = 38400000, [8] = 12000000, [9] = 48000000, }; /* Parent lists. */ PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60", "pc_xusb_ss" }; PLIST(mux_xusb_ssp) = {"xusb_ss", "osc_div_clk"}; /* Clocks adjusted online. */ static struct clk_fixed_def fixed_osc = FRATE(TEGRA210_CLK_CLK_M, "osc", 38400000); static struct clk_fixed_def fixed_clk_m = FACT(0, "clk_m", "osc", 1, 1); static struct clk_fixed_def fixed_osc_div = FACT(0, "osc_div_clk", "osc", 1, 1); static struct clk_fixed_def tegra210_fixed_clks[] = { /* Core clocks. */ FRATE(0, "bogus", 1), FRATE(0, "clk_s", 32768), /* Audio clocks. */ FRATE(0, "vimclk_sync", 1), FRATE(0, "i2s1_sync", 1), FRATE(0, "i2s2_sync", 1), FRATE(0, "i2s3_sync", 1), FRATE(0, "i2s4_sync", 1), FRATE(0, "i2s5_sync", 1), FRATE(0, "spdif_in_sync", 1), /* XUSB */ FACT(TEGRA210_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2), /* SOR */ FACT(0, "sor_safe_div", "pllP_out0", 1, 17), FACT(0, "dpaux_div", "sor_safe", 1, 17), FACT(0, "dpaux1_div", "sor_safe", 1, 17), /* Not Yet Implemented */ FRATE(0, "audio", 10000000), FRATE(0, "audio0", 10000000), FRATE(0, "audio1", 10000000), FRATE(0, "audio2", 10000000), FRATE(0, "audio3", 10000000), FRATE(0, "audio4", 10000000), FRATE(0, "ext_vimclk", 10000000), FRATE(0, "audiod1", 10000000), FRATE(0, "audiod2", 10000000), FRATE(0, "audiod3", 10000000), FRATE(0, "dfllCPU_out", 10000000), }; static struct clk_mux_def tegra210_mux_clks[] = { /* USB. */ MUX(TEGRA210_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 2), MUX(0, "xusb_ssp", mux_xusb_ssp, CLK_SOURCE_XUSB_SS, 24, 1), }; static struct clk_gate_def tegra210_gate_clks[] = { /* Base peripheral clocks. */ GATE_INV(TEGRA210_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7), GATE_INV(TEGRA210_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3), GATE(TEGRA210_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0), GATE(TEGRA210_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1), GATE(0, "pllD_dsi_csi", "pllD_out0", PLLD_MISC, 21), GATE(0, "pllP_hsio", "pllP_out0", PLLP_MISC1, 29), GATE(0, "pllP_xusb", "pllP_hsio", PLLP_MISC1, 28), }; static struct clk_div_def tegra210_div_clks[] = { /* Base peripheral clocks. */ DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0), DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0), }; /* Initial setup table. */ static struct tegra210_init_item clk_init_table[] = { /* clock, partent, frequency, enable */ {"uarta", "pllP_out0", 408000000, 0}, {"uartb", "pllP_out0", 408000000, 0}, {"uartc", "pllP_out0", 408000000, 0}, {"uartd", "pllP_out0", 408000000, 0}, {"pllA", NULL, 564480000, 1}, {"pllA_out0", NULL, 11289600, 1}, {"extperiph1", "pllA_out0", 0, 1}, {"i2s1", "pllA_out0", 11289600, 0}, {"i2s2", "pllA_out0", 11289600, 0}, {"i2s3", "pllA_out0", 11289600, 0}, {"i2s4", "pllA_out0", 11289600, 0}, {"i2s5", "pllA_out0", 11289600, 0}, {"host1x", "pllP_out0", 136000000, 1}, {"sclk", "pllP_out2", 102000000, 1}, {"dvfs_soc", "pllP_out0", 51000000, 1}, {"dvfs_ref", "pllP_out0", 51000000, 1}, {"spi4", "pllP_out0", 12000000, 1}, {"pllREFE", NULL, 672000000, 0}, {"xusb", NULL, 0, 1}, {"xusb_ss", "pllU_480", 120000000, 0}, {"pc_xusb_fs", "pllU_48", 48000000, 0}, {"xusb_hs", "pc_xusb_ss", 120000000, 0}, {"xusb_ssp", "xusb_ss", 120000000, 0}, {"pc_xusb_falcon", "pllP_xusb", 204000000, 0}, {"pc_xusb_core_host", "pllP_xusb", 102000000, 0}, {"pc_xusb_core_dev", "pllP_xusb", 102000000, 0}, {"sata", "pllP_out0", 104000000, 0}, {"sata_oob", "pllP_out0", 204000000, 0}, {"emc", NULL, 0, 1}, {"mselect", NULL, 0, 1}, {"csite", NULL, 0, 1}, {"dbgapb", NULL, 0, 1 }, {"tsensor", "clk_m", 400000, 0}, {"i2c1", "pllP_out0", 0, 0}, {"i2c2", "pllP_out0", 0, 0}, {"i2c3", "pllP_out0", 0, 0}, {"i2c4", "pllP_out0", 0, 0}, {"i2c5", "pllP_out0", 0, 0}, {"i2c6", "pllP_out0", 0, 0}, {"pllDP_out0", NULL, 270000000, 0}, {"soc_therm", "pllP_out0", 51000000, 0}, {"cclk_g", NULL, 0, 1}, {"pllU_out1", NULL, 48000000, 1}, {"pllU_out2", NULL, 60000000, 1}, {"pllC4", NULL, 1000000000, 1}, {"pllC4_out0", NULL, 1000000000, 1}, }; static void init_divs(struct tegra210_car_softc *sc, struct clk_div_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_div_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_div_register failed"); } } static void init_gates(struct tegra210_car_softc *sc, struct clk_gate_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_gate_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_gate_register failed"); } } static void init_muxes(struct tegra210_car_softc *sc, struct clk_mux_def *clks, int nclks) { int i, rv; for (i = 0; i < nclks; i++) { rv = clknode_mux_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_mux_register failed"); } } static void init_fixeds(struct tegra210_car_softc *sc, struct clk_fixed_def *clks, int nclks) { int i, rv; uint32_t val; int osc_idx; CLKDEV_READ_4(sc->dev, OSC_CTRL, &val); osc_idx = OSC_CTRL_OSC_FREQ_GET(val); fixed_osc.freq = osc_freqs[osc_idx]; if (fixed_osc.freq == 0) panic("Undefined input frequency"); rv = clknode_fixed_register(sc->clkdom, &fixed_osc); if (rv != 0) panic("clk_fixed_register failed"); fixed_osc_div.div = 1 << OSC_CTRL_PLL_REF_DIV_GET(val); rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div); if (rv != 0) panic("clk_fixed_register failed"); CLKDEV_READ_4(sc->dev, SPARE_REG0, &val); fixed_clk_m.div = SPARE_REG0_MDIV_GET(val) + 1; rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m); if (rv != 0) panic("clk_fixed_register failed"); for (i = 0; i < nclks; i++) { rv = clknode_fixed_register(sc->clkdom, clks + i); if (rv != 0) panic("clk_fixed_register failed"); } } static void postinit_clock(struct tegra210_car_softc *sc) { int i; struct tegra210_init_item *tbl; struct clknode *clknode; int rv; for (i = 0; i < nitems(clk_init_table); i++) { tbl = &clk_init_table[i]; clknode = clknode_find_by_name(tbl->name); if (clknode == NULL) { device_printf(sc->dev, "Cannot find clock %s\n", tbl->name); continue; } if (tbl->parent != NULL) { rv = clknode_set_parent_by_name(clknode, tbl->parent); if (rv != 0) { device_printf(sc->dev, "Cannot set parent for %s (to %s): %d\n", tbl->name, tbl->parent, rv); continue; } } if (tbl->frequency != 0) { rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999); if (rv != 0) { device_printf(sc->dev, "Cannot set frequency for %s: %d\n", tbl->name, rv); continue; } } if (tbl->enable!= 0) { rv = clknode_enable(clknode); if (rv != 0) { device_printf(sc->dev, "Cannot enable %s: %d\n", tbl->name, rv); continue; } } } } static void register_clocks(device_t dev) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("clkdom == NULL"); init_fixeds(sc, tegra210_fixed_clks, nitems(tegra210_fixed_clks)); tegra210_init_plls(sc); init_muxes(sc, tegra210_mux_clks, nitems(tegra210_mux_clks)); init_divs(sc, tegra210_div_clks, nitems(tegra210_div_clks)); init_gates(sc, tegra210_gate_clks, nitems(tegra210_gate_clks)); tegra210_periph_clock(sc); tegra210_super_mux_clock(sc); clkdom_finit(sc->clkdom); clkdom_xlock(sc->clkdom); postinit_clock(sc); clkdom_unlock(sc->clkdom); if (bootverbose) clkdom_dump(sc->clkdom); } static int tegra210_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); *val = bus_read_4(sc->mem_res, addr); return (0); } static int tegra210_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); bus_write_4(sc->mem_res, addr, val); return (0); } static int tegra210_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, uint32_t set_mask) { struct tegra210_car_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = bus_read_4(sc->mem_res, addr); reg &= ~clear_mask; reg |= set_mask; bus_write_4(sc->mem_res, addr, reg); return (0); } static void tegra210_car_clkdev_device_lock(device_t dev) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void tegra210_car_clkdev_device_unlock(device_t dev) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int tegra210_car_detach(device_t dev) { device_printf(dev, "Error: Clock driver cannot be detached\n"); return (EBUSY); } static int tegra210_car_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Tegra Clock Driver"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int tegra210_car_attach(device_t dev) { struct tegra210_car_softc *sc = device_get_softc(dev); int rid, rv; sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Resource setup. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "cannot allocate memory resource\n"); rv = ENXIO; goto fail; } register_clocks(dev); hwreset_register_ofw_provider(dev); return (0); fail: if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (rv); } static int tegra210_car_hwreset_assert(device_t dev, intptr_t id, bool value) { struct tegra210_car_softc *sc = device_get_softc(dev); return (tegra210_hwreset_by_idx(sc, id, value)); } static device_method_t tegra210_car_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra210_car_probe), DEVMETHOD(device_attach, tegra210_car_attach), DEVMETHOD(device_detach, tegra210_car_detach), /* Clkdev interface*/ DEVMETHOD(clkdev_read_4, tegra210_car_clkdev_read_4), DEVMETHOD(clkdev_write_4, tegra210_car_clkdev_write_4), DEVMETHOD(clkdev_modify_4, tegra210_car_clkdev_modify_4), DEVMETHOD(clkdev_device_lock, tegra210_car_clkdev_device_lock), DEVMETHOD(clkdev_device_unlock, tegra210_car_clkdev_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, tegra210_car_hwreset_assert), DEVMETHOD_END }; static DEFINE_CLASS_0(car, tegra210_car_driver, tegra210_car_methods, sizeof(struct tegra210_car_softc)); EARLY_DRIVER_MODULE(tegra210_car, simplebus, tegra210_car_driver, NULL, NULL, BUS_PASS_TIMER); diff --git a/sys/arm64/nvidia/tegra210/tegra210_pmc.c b/sys/arm64/nvidia/tegra210/tegra210_pmc.c index a04ec212a8c7..0f0343a317ce 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_pmc.c +++ b/sys/arm64/nvidia/tegra210/tegra210_pmc.c @@ -1,624 +1,624 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2020 Michal Meloun * * 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 +#include #include #include #include #include #define PMC_CNTRL 0x000 #define PMC_CNTRL_SHUTDOWN_OE (1 << 22) #define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20) #define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 20 #define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19) #define PMC_CNTRL_FUSE_OVERRIDE (1 << 18) #define PMC_CNTRL_INTR_POLARITY (1 << 17) #define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) #define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) #define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) #define PMC_CNTRL_AOINIT (1 << 13) #define PMC_CNTRL_PWRGATE_DIS (1 << 12) #define PMC_CNTRL_SYSCLK_OE (1 << 11) #define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) #define PMC_CNTRL_PWRREQ_OE (1 << 9) #define PMC_CNTRL_PWRREQ_POLARITY (1 << 8) #define PMC_CNTRL_BLINK_EN (1 << 7) #define PMC_CNTRL_GLITCHDET_DIS (1 << 6) #define PMC_CNTRL_LATCHWAKE_EN (1 << 5) #define PMC_CNTRL_MAIN_RST (1 << 4) #define PMC_CNTRL_KBC_RST (1 << 3) #define PMC_CNTRL_RTC_RST (1 << 2) #define PMC_CNTRL_RTC_CLK_DIS (1 << 1) #define PMC_CNTRL_KBC_CLK_DIS (1 << 0) #define PMC_DPD_SAMPLE 0x020 #define PMC_CLAMP_STATUS 0x02C #define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_PWRGATE_TOGGLE 0x030 #define PMC_PWRGATE_TOGGLE_START (1 << 8) #define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0) #define PMC_REMOVE_CLAMPING_CMD 0x034 #define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_PWRGATE_STATUS 0x038 #define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F)) #define PMC_SCRATCH0 0x050 #define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) #define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) #define PMC_SCRATCH0_MODE_RCM (1 << 1) #define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ PMC_SCRATCH0_MODE_BOOTLOADER | \ PMC_SCRATCH0_MODE_RCM) #define PMC_CPUPWRGOOD_TIMER 0x0c8 #define PMC_CPUPWROFF_TIMER 0x0cc #define PMC_SCRATCH41 0x140 #define PMC_SENSOR_CTRL 0x1b0 #define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2) #define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) #define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0) #define PMC_IO_DPD_REQ 0x1b8 #define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30) #define PMC_IO_DPD_REQ_CODE_OFF (1 << 30) #define PMC_IO_DPD_REQ_CODE_ON (2 << 30) #define PMC_IO_DPD_REQ_CODE_MASK (3 << 30) #define PMC_IO_DPD_STATUS 0x1bc #define PMC_IO_DPD_STATUS_HDMI (1 << 28) #define PMC_IO_DPD2_REQ 0x1c0 #define PMC_IO_DPD2_STATUS 0x1c4 #define PMC_IO_DPD2_STATUS_HV (1 << 6) #define PMC_SEL_DPD_TIM 0x1c8 #define PMC_SCRATCH54 0x258 #define PMC_SCRATCH54_DATA_SHIFT 8 #define PMC_SCRATCH54_ADDR_SHIFT 0 #define PMC_SCRATCH55 0x25c #define PMC_SCRATCH55_RST_ENABLE (1 << 31) #define PMC_SCRATCH55_CNTRL_TYPE (1 << 30) #define PMC_SCRATCH55_CNTRL_ID_SHIFT 27 #define PMC_SCRATCH55_CNTRL_ID_MASK 0x07 #define PMC_SCRATCH55_PINMUX_SHIFT 24 #define PMC_SCRATCH55_PINMUX_MASK 0x07 #define PMC_SCRATCH55_CHECKSUM_SHIFT 16 #define PMC_SCRATCH55_CHECKSUM_MASK 0xFF #define PMC_SCRATCH55_16BITOP (1 << 15) #define PMC_SCRATCH55_I2CSLV1_SHIFT 0 #define PMC_SCRATCH55_I2CSLV1_MASK 0x7F #define PMC_GPU_RG_CNTRL 0x2d4 /* Secure access */ #define PMC_SMC 0xc2fffe00 #define PMC_SMC_READ 0xaa #define PMC_SMC_WRITE 0xbb #define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx) #define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ device_get_nameunit(_sc->dev), "tegra210_pmc", MTX_DEF) #define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); #define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); #define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); struct tegra210_pmc_softc { device_t dev; struct resource *mem_res; clk_t clk; struct mtx mtx; bool secure_access; uint32_t rate; enum tegra_suspend_mode suspend_mode; uint32_t cpu_good_time; uint32_t cpu_off_time; uint32_t core_osc_time; uint32_t core_pmu_time; uint32_t core_off_time; int corereq_high; int sysclkreq_high; int combined_req; int cpu_pwr_good_en; uint32_t lp0_vec_phys; uint32_t lp0_vec_size; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-pmc", 1}, {NULL, 0}, }; static struct tegra210_pmc_softc *pmc_sc; static inline struct tegra210_pmc_softc * tegra210_pmc_get_sc(void) { if (pmc_sc == NULL) panic("To early call to Tegra PMC driver.\n"); return (pmc_sc); } static void WR4(struct tegra210_pmc_softc *sc, bus_size_t r, uint32_t v) { struct arm_smccc_res res; if (sc->secure_access) { arm_smccc_smc(PMC_SMC, PMC_SMC_WRITE, r, v, 0, 0, 0, 0, &res); if (res.a0 != 0) device_printf(sc->dev," PMC SMC write failed: %lu\n", res.a0); } bus_write_4(sc->mem_res, r, v); } static uint32_t RD4(struct tegra210_pmc_softc *sc, bus_size_t r) { struct arm_smccc_res res; if (sc->secure_access) { arm_smccc_smc(PMC_SMC, PMC_SMC_READ, r, 0, 0, 0, 0, 0, &res); if (res.a0 != 0) device_printf(sc->dev," PMC SMC write failed: %lu\n", res.a0); return((uint32_t)res.a1); } return(bus_read_4(sc->mem_res, r)); } static int tegra210_pmc_set_powergate(struct tegra210_pmc_softc *sc, enum tegra_powergate_id id, int ena) { uint32_t reg; int i; PMC_LOCK(sc); reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id); if (((reg != 0) && ena) || ((reg == 0) && !ena)) { PMC_UNLOCK(sc); return (0); } for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_PWRGATE_TOGGLE); if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting for TOGGLE_START\n"); WR4(sc, PMC_PWRGATE_TOGGLE, PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id)); for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_PWRGATE_TOGGLE); if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting for TOGGLE_START\n"); PMC_UNLOCK(sc); return (0); } int tegra_powergate_remove_clamping(enum tegra_powergate_id id) { struct tegra210_pmc_softc *sc; uint32_t reg; enum tegra_powergate_id swid; int i; sc = tegra210_pmc_get_sc(); if (id == TEGRA_POWERGATE_3D) { WR4(sc, PMC_GPU_RG_CNTRL, 0); return (0); } reg = RD4(sc, PMC_PWRGATE_STATUS); if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0) panic("Attempt to remove clamping for unpowered partition.\n"); if (id == TEGRA_POWERGATE_PCX) swid = TEGRA_POWERGATE_VDE; else if (id == TEGRA_POWERGATE_VDE) swid = TEGRA_POWERGATE_PCX; else swid = id; WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid)); for (i = 100; i > 0; i--) { reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD); if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when remove clamping\n"); reg = RD4(sc, PMC_CLAMP_STATUS); if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0) panic("Cannot remove clamping\n"); return (0); } int tegra_powergate_is_powered(enum tegra_powergate_id id) { struct tegra210_pmc_softc *sc; uint32_t reg; sc = tegra210_pmc_get_sc(); reg = RD4(sc, PMC_PWRGATE_STATUS); return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0); } int tegra_powergate_power_on(enum tegra_powergate_id id) { struct tegra210_pmc_softc *sc; int rv, i; sc = tegra210_pmc_get_sc(); rv = tegra210_pmc_set_powergate(sc, id, 1); if (rv != 0) { device_printf(sc->dev, "Cannot set powergate: %d\n", id); return (rv); } for (i = 100; i > 0; i--) { if (tegra_powergate_is_powered(id)) break; DELAY(1); } if (i <= 0) { device_printf(sc->dev, "Timeout when waiting on power up\n"); return(ETIMEDOUT); } return (rv); } int tegra_powergate_power_off(enum tegra_powergate_id id) { struct tegra210_pmc_softc *sc; int rv, i; sc = tegra210_pmc_get_sc(); rv = tegra210_pmc_set_powergate(sc, id, 0); if (rv != 0) { device_printf(sc->dev, "Cannot set powergate: %d\n", id); return (rv); } for (i = 100; i > 0; i--) { if (!tegra_powergate_is_powered(id)) break; DELAY(1); } if (i <= 0) device_printf(sc->dev, "Timeout when waiting on power off\n"); return (rv); } int tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk, hwreset_t rst) { struct tegra210_pmc_softc *sc; int rv; sc = tegra210_pmc_get_sc(); rv = hwreset_assert(rst); if (rv != 0) { device_printf(sc->dev, "Cannot assert reset\n"); return (rv); } rv = clk_stop(clk); if (rv != 0) { device_printf(sc->dev, "Cannot stop clock\n"); goto clk_fail; } rv = tegra_powergate_power_on(id); if (rv != 0) { device_printf(sc->dev, "Cannot power on powergate\n"); goto clk_fail; } rv = clk_enable(clk); if (rv != 0) { device_printf(sc->dev, "Cannot enable clock\n"); goto clk_fail; } DELAY(20); rv = tegra_powergate_remove_clamping(id); if (rv != 0) { device_printf(sc->dev, "Cannot remove clamping\n"); goto fail; } rv = hwreset_deassert(rst); if (rv != 0) { device_printf(sc->dev, "Cannot unreset reset\n"); goto fail; } return 0; fail: clk_disable(clk); clk_fail: hwreset_assert(rst); tegra_powergate_power_off(id); return (rv); } static int tegra210_pmc_parse_fdt(struct tegra210_pmc_softc *sc, phandle_t node) { int rv; uint32_t tmp; uint32_t tmparr[2]; rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp)); if (rv > 0) { switch (tmp) { case 0: sc->suspend_mode = TEGRA_SUSPEND_LP0; break; case 1: sc->suspend_mode = TEGRA_SUSPEND_LP1; break; case 2: sc->suspend_mode = TEGRA_SUSPEND_LP2; break; default: sc->suspend_mode = TEGRA_SUSPEND_NONE; break; } } rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->cpu_good_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->cpu_off_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr, sizeof(tmparr)); if (rv == sizeof(tmparr)) { sc->core_osc_time = tmparr[0]; sc->core_pmu_time = tmparr[1]; sc->suspend_mode = TEGRA_SUSPEND_NONE; } rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp)); if (rv > 0) { sc->core_off_time = tmp; sc->suspend_mode = TEGRA_SUSPEND_NONE; } sc->corereq_high = OF_hasprop(node, "nvidia,core-power-req-active-high"); sc->sysclkreq_high = OF_hasprop(node, "nvidia,sys-clock-req-active-high"); sc->combined_req = OF_hasprop(node, "nvidia,combined-power-req"); sc->cpu_pwr_good_en = OF_hasprop(node, "nvidia,cpu-pwr-good-en"); rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr)); if (rv == sizeof(tmparr)) { sc->lp0_vec_phys = tmparr[0]; sc->core_pmu_time = tmparr[1]; sc->lp0_vec_size = TEGRA_SUSPEND_NONE; if (sc->suspend_mode == TEGRA_SUSPEND_LP0) sc->suspend_mode = TEGRA_SUSPEND_LP1; } return 0; } static void tegra210_pmc_check_secure(struct tegra210_pmc_softc *sc) { uint32_t orig; sc->secure_access = false; /* * If PMC is coverd by secure trust zone, all reads returns 0, * Use scratch0 register acvcess test */ orig = RD4(sc, PMC_SCRATCH0); WR4(sc, PMC_SCRATCH0, 0xDEADBEEF); if (RD4(sc, PMC_SCRATCH0) == 0) { sc->secure_access = true; return; } WR4(sc, PMC_SCRATCH0, 0xBADC0DE); if (RD4(sc, PMC_SCRATCH0) == 0) { sc->secure_access = true; return; } WR4(sc, PMC_SCRATCH0, orig); } static int tegra210_pmc_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(dev, "Tegra PMC"); return (BUS_PROBE_DEFAULT); } static int tegra210_pmc_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int tegra210_pmc_attach(device_t dev) { struct tegra210_pmc_softc *sc; int rid, rv; uint32_t reg; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); PMC_LOCK_INIT(sc); rv = tegra210_pmc_parse_fdt(sc, node); if (rv != 0) { device_printf(sc->dev, "Cannot parse FDT data\n"); return (rv); } rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk); if (rv != 0) { device_printf(sc->dev, "Cannot get \"pclk\" clock\n"); return (ENXIO); } rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } tegra210_pmc_check_secure(sc); /* Enable CPU power request. */ reg = RD4(sc, PMC_CNTRL); reg |= PMC_CNTRL_CPU_PWRREQ_OE; WR4(sc, PMC_CNTRL, reg); /* Set sysclk output polarity */ reg = RD4(sc, PMC_CNTRL); if (sc->sysclkreq_high) reg &= ~PMC_CNTRL_SYSCLK_POLARITY; else reg |= PMC_CNTRL_SYSCLK_POLARITY; WR4(sc, PMC_CNTRL, reg); /* Enable sysclk request. */ reg = RD4(sc, PMC_CNTRL); reg |= PMC_CNTRL_SYSCLK_OE; WR4(sc, PMC_CNTRL, reg); /* * Remove HDMI from deep power down mode. * XXX mote this to HDMI driver */ reg = RD4(sc, PMC_IO_DPD_STATUS); reg &= ~ PMC_IO_DPD_STATUS_HDMI; WR4(sc, PMC_IO_DPD_STATUS, reg); reg = RD4(sc, PMC_IO_DPD2_STATUS); reg &= ~ PMC_IO_DPD2_STATUS_HV; WR4(sc, PMC_IO_DPD2_STATUS, reg); if (pmc_sc != NULL) panic("tegra210_pmc: double driver attach"); pmc_sc = sc; return (0); } static device_method_t tegra210_pmc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra210_pmc_probe), DEVMETHOD(device_attach, tegra210_pmc_attach), DEVMETHOD(device_detach, tegra210_pmc_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(pmc, tegra210_pmc_driver, tegra210_pmc_methods, sizeof(struct tegra210_pmc_softc)); EARLY_DRIVER_MODULE(tegra210_pmc, simplebus, tegra210_pmc_driver, NULL, NULL, 70); diff --git a/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c b/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c index bfcd8894034a..75662386427f 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c +++ b/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c @@ -1,1957 +1,1957 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2020 Michal Meloun * * 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 #include #include #include #include #include #include #include #include #include #include "phynode_if.h" /* FUSE calibration data. */ #define FUSE_SKU_CALIB_0 0x0F0 #define FUSE_SKU_CALIB_0_HS_CURR_LEVEL_123(x, i) (((x) >> (11 + ((i) - 1) * 6)) & 0x3F); #define FUSE_SKU_CALIB_0_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F); #define FUSE_SKU_CALIB_0_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F); #define FUSE_USB_CALIB_EXT_0 0x250 #define FUSE_USB_CALIB_EXT_0_RPD_CTRL(x) (((x) >> 0) & 0x1F); /* Registers. */ #define XUSB_PADCTL_USB2_PAD_MUX 0x004 #define XUSB_PADCTL_USB2_PORT_CAP 0x008 #define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4)) #define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4)) #define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4)) #define USB2_PORT_CAP_PORT_CAP_OTG 0x3 #define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2 #define USB2_PORT_CAP_PORT_CAP_HOST 0x1 #define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0 #define XUSB_PADCTL_SS_PORT_MAP 0x014 #define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4)) #define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4)) #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) #define ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN (1 << 29) #define ELPG_PROGRAM1_SSP_ELPG_VCORE_DOWN(x) (1 << (2 + (x) * 3)) #define ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (1 + (x) * 3)) #define ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN(x) (1 << (0 + (x) * 3)) #define XUSB_PADCTL_USB3_PAD_MUX 0x028 #define USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x))) #define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(x) (0x084 + (x) * 0x40) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPU_OVRD_VAL (1 << 23) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPU_OVRD ( 1 << 22) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPD_OVRD_VAL (1 << 21) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPD_OVRD (1 << 20) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPU_OVRD_VAL (1 << 19) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPU_OVRD (1 << 18) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPD_OVRD_VAL (1 << 17) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPD_OVRD (1 << 16) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_DYN_DLY(x) (((x) & 0x3) << 9) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(x) (((x) & 0x3) << 7) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_DIV_DET_EN (1 << 4) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VOP_DIV2P7_DET (1 << 3) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VOP_DIV2P0_DET (1 << 2) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VON_DIV2P7_DET (1 << 1) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VON_DIV2P0_DET (1 << 0) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x088 + (x) * 0x40) #define USB2_OTG_PAD_CTL0_PD_ZI (1 << 29) #define USB2_OTG_PAD_CTL0_PD2_OVRD_EN (1 << 28) #define USB2_OTG_PAD_CTL0_PD2 (1 << 27) #define USB2_OTG_PAD_CTL0_PD (1 << 26) #define USB2_OTG_PAD_CTL0_TERM_EN (1 << 25) #define USB2_OTG_PAD_CTL0_LS_FSLEW(x) (((x) & 0x0F) << 21) #define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x0F) << 17) #define USB2_OTG_PAD_CTL0_FS_FSLEW(x) (((x) & 0x0F) << 13) #define USB2_OTG_PAD_CTL0_FS_RSLEW(x) (((x) & 0x0F) << 9) #define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6) #define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x08C + (x) * 0x40) #define USB2_OTG_PAD_CTL1_RPD_CTRL(x) (((x) & 0x1F) << 26) #define USB2_OTG_PAD_CTL1_RPU_STATUS_HIGH (1 << 25) #define USB2_OTG_PAD_CTL1_RPU_SWITCH_LOW (1 << 24) #define USB2_OTG_PAD_CTL1_RPU_SWITCH_OVRD (1 << 23) #define USB2_OTG_PAD_CTL1_HS_LOOPBACK_OVRD_VAL (1 << 22) #define USB2_OTG_PAD_CTL1_HS_LOOPBACK_OVRD_EN (1 << 21) #define USB2_OTG_PAD_CTL1_PTERM_RANGE_ADJ(x) (((x) & 0x0F) << 17) #define USB2_OTG_PAD_CTL1_PD_DISC_OVRD_VAL (1 << 16) #define USB2_OTG_PAD_CTL1_PD_CHRP_OVRD_VAL (1 << 15) #define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x03) << 13) #define USB2_OTG_PAD_CTL1_HS_COUP_EN(x) (((x) & 0x03) << 11) #define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x0F) << 7) #define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0x0F) << 3) #define USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) #define USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0(x) (0x0C0 + (x) * 0x40) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0284 #define USB2_BIAS_PAD_CTL0_TRK_PWR_ENA (1 << 29) #define USB2_BIAS_PAD_CTL0_SPARE(x) (((x) & 0xF) << 25) #define USB2_BIAS_PAD_CTL0_CHG_DIV(x) (((x) & 0xF) << 21) #define USB2_BIAS_PAD_CTL0_TEMP_COEF(x) (((x) & 0x7) << 18) #define USB2_BIAS_PAD_CTL0_VREF_CTRL(x) (((x) & 0x7) << 15) #define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 12) #define USB2_BIAS_PAD_CTL0_PD (1 << 11) #define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x7) << 8) #define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 6) #define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 3) #define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x7) << 0) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x0288 #define USB2_BIAS_PAD_CTL1_FORCE_TRK_CLK_EN (1 << 30) #define USB2_BIAS_PAD_CTL1_TRK_SW_OVRD (1 << 29) #define USB2_BIAS_PAD_CTL1_TRK_DONE (1 << 28) #define USB2_BIAS_PAD_CTL1_TRK_START (1 << 27) #define USB2_BIAS_PAD_CTL1_PD_TRK (1 << 26) #define USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(x) (((x) & 0x7F) << 19) #define USB2_BIAS_PAD_CTL1_TRK_START_TIMER(x) (((x) & 0x7F) << 12) #define USB2_BIAS_PAD_CTL1_PCTRL(x) (((x) & 0x3F) << 6) #define USB2_BIAS_PAD_CTL1_TCTRL(x) (((x) & 0x3F) << 0) #define XUSB_PADCTL_HSIC_PAD_CTL0(x) (0x300 + (x) * 0x20) #define HSIC_PAD_CTL0_RPU_STROBE (1 << 18) #define HSIC_PAD_CTL0_RPU_DATA1 (1 << 17) #define HSIC_PAD_CTL0_RPU_DATA0 (1 << 16) #define HSIC_PAD_CTL0_RPD_STROBE (1 << 15) #define HSIC_PAD_CTL0_RPD_DATA1 (1 << 14) #define HSIC_PAD_CTL0_RPD_DATA0 (1 << 13) #define HSIC_PAD_CTL0_LPBK_STROBE (1 << 12) #define HSIC_PAD_CTL0_LPBK_DATA1 (1 << 11) #define HSIC_PAD_CTL0_LPBK_DATA0 (1 << 10) #define HSIC_PAD_CTL0_PD_ZI_STROBE (1 << 9) #define HSIC_PAD_CTL0_PD_ZI_DATA1 (1 << 8) #define HSIC_PAD_CTL0_PD_ZI_DATA0 (1 << 7) #define HSIC_PAD_CTL0_PD_RX_STROBE (1 << 6) #define HSIC_PAD_CTL0_PD_RX_DATA1 (1 << 5) #define HSIC_PAD_CTL0_PD_RX_DATA0 (1 << 4) #define HSIC_PAD_CTL0_PD_TX_STROBE (1 << 3) #define HSIC_PAD_CTL0_PD_TX_DATA1 (1 << 2) #define HSIC_PAD_CTL0_PD_TX_DATA0 (1 << 1) #define HSIC_PAD_CTL0_IDDQ (1 << 0) #define XUSB_PADCTL_HSIC_PAD_CTL1(x) (0x304 + (x) * 0x20) #define HSIC_PAD_CTL1_RTERM(x) (((x) & 0xF) << 12) #define HSIC_PAD_CTL1_HSIC_OPT(x) (((x) & 0xF) << 8) #define HSIC_PAD_CTL1_TX_SLEW(x) (((x) & 0xF) << 4) #define HSIC_PAD_CTL1_TX_RTUNEP(x) (((x) & 0xF) << 0) #define XUSB_PADCTL_HSIC_PAD_CTL2(x) (0x308 + (x) * 0x20) #define HSIC_PAD_CTL2_RX_STROBE_TRIM(x) (((x) & 0xF) << 8) #define HSIC_PAD_CTL2_RX_DATA1_TRIM(x) (((x) & 0xF) << 4) #define HSIC_PAD_CTL2_RX_DATA0_TRIM(x) (((x) & 0xF) << 0) #define XUSB_PADCTL_HSIC_PAD_TRK_CTL 0x340 #define HSIC_PAD_TRK_CTL_AUTO_RTERM_EN (1 << 24) #define HSIC_PAD_TRK_CTL_FORCE_TRK_CLK_EN (1 << 23) #define HSIC_PAD_TRK_CTL_TRK_SW_OVRD (1 << 22) #define HSIC_PAD_TRK_CTL_TRK_DONE (1 << 21) #define HSIC_PAD_TRK_CTL_TRK_START (1 << 20) #define HSIC_PAD_TRK_CTL_PD_TRK (1 << 19) #define HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(x) (((x) & 0x3F) << 12) #define HSIC_PAD_TRK_CTL_TRK_START_TIMER(x) (((x) & 0x7F) << 5) #define HSIC_PAD_TRK_CTL_RTERM_OUT(x) (((x) & 0x1F) << 0) #define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x344 #define XUSB_PADCTL_UPHY_PLL_P0_CTL1 0x360 #define UPHY_PLL_P0_CTL1_PLL0_FREQ_PSDIV(x) (((x) & 0x03) << 28) #define UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(x) (((x) & 0xFF) << 20) #define UPHY_PLL_P0_CTL1_PLL0_FREQ_MDIV(x) (((x) & 0x03) << 16) #define UPHY_PLL_P0_CTL1_PLL0_LOCKDET_STATUS (1 << 15) #define UPHY_PLL_P0_CTL1_PLL0_MODE_GET(x) (((x) >> 8) & 0x03) #define UPHY_PLL_P0_CTL1_PLL0_BYPASS_EN (1 << 7) #define UPHY_PLL_P0_CTL1_PLL0_FREERUN_EN (1 << 6) #define UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD (1 << 4) #define UPHY_PLL_P0_CTL1_PLL0_ENABLE (1 << 3) #define UPHY_PLL_P0_CTL1_PLL0_SLEEP(x) (((x) & 0x03) << 1) #define UPHY_PLL_P0_CTL1_PLL0_IDDQ (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL2 0x364 #define UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(x) (((x) & 0xFFFFFF) << 4) #define UPHY_PLL_P0_CTL2_PLL0_CAL_RESET (1 << 3) #define UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD (1 << 2) #define UPHY_PLL_P0_CTL2_PLL0_CAL_DONE (1 << 1) #define UPHY_PLL_P0_CTL2_PLL0_CAL_EN (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c #define UPHY_PLL_P0_CTL4_PLL0_TCLKOUT_EN (1 << 28) #define UPHY_PLL_P0_CTL4_PLL0_CLKDIST_CTRL(x) (((x) & 0xF) << 20) #define UPHY_PLL_P0_CTL4_PLL0_XDIGCLK_EN (1 << 19) #define UPHY_PLL_P0_CTL4_PLL0_XDIGCLK_SEL(x) (((x) & 0x7) << 16) #define UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_EN (1 << 15) #define UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(x) (((x) & 0x3) << 12) #define UPHY_PLL_P0_CTL4_PLL0_FBCLKBUF_EN (1 << 9) #define UPHY_PLL_P0_CTL4_PLL0_REFCLKBUF_EN (1 << 8) #define UPHY_PLL_P0_CTL4_PLL0_REFCLK_SEL(x) (((x) & 0xF) << 4) #define UPHY_PLL_P0_CTL4_PLL0_REFCLK_TERM100 (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL5 0x370 #define UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(x) (((x) & 0xFF) << 16) #define UPHY_PLL_P0_CTL5_PLL0_LPF_CTRL(x) (((x) & 0xFF) << 8) #define UPHY_PLL_P0_CTL5_PLL0_CP_CTRL(x) (((x) & 0x0F) << 4) #define UPHY_PLL_P0_CTL5_PLL0_PFD_CTRL(x) (((x) & 0x03) << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL8 0x37c #define UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE (1U << 31) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_VAL(x) (((x) & 0x1F) << 24) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_BYP_EN (1 << 23) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_BYP_CODE(x) (((x) & 0x1F) << 16) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD (1 << 15) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN (1 << 13) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_EN (1 << 12) #define UPHY_PLL_P0_CTL8_PLL0_BGAP_CTRL(x) (((x) & 0xFFF) << 0) #define XUSB_PADCTL_UPHY_MISC_PAD_P_CTL1(x) (0x460 + (x) * 0x40) #define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860 #define UPHY_PLL_S0_CTL1_PLL0_FREQ_PSDIV(x) (((x) & 0x03) << 28) #define UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(x) (((x) & 0xFF) << 20) #define UPHY_PLL_S0_CTL1_PLL0_FREQ_MDIV(x) (((x) & 0x03) << 16) #define UPHY_PLL_S0_CTL1_PLL0_LOCKDET_STATUS (1 << 15) #define UPHY_PLL_S0_CTL1_PLL0_MODE_GET(x) (((x) >> 8) & 0x03) #define UPHY_PLL_S0_CTL1_PLL0_BYPASS_EN (1 << 7) #define UPHY_PLL_S0_CTL1_PLL0_FREERUN_EN (1 << 6) #define UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD (1 << 4) #define UPHY_PLL_S0_CTL1_PLL0_ENABLE (1 << 3) #define UPHY_PLL_S0_CTL1_PLL0_SLEEP(x) (((x) & 0x03) << 1) #define UPHY_PLL_S0_CTL1_PLL0_IDDQ (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864 #define UPHY_PLL_S0_CTL2_PLL0_CAL_CTRL(x) (((x) & 0xFFFFFF) << 4) #define UPHY_PLL_S0_CTL2_PLL0_CAL_RESET (1 << 3) #define UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD (1 << 2) #define UPHY_PLL_S0_CTL2_PLL0_CAL_DONE (1 << 1) #define UPHY_PLL_S0_CTL2_PLL0_CAL_EN (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL4 0x86c #define UPHY_PLL_S0_CTL4_PLL0_TCLKOUT_EN (1 << 28) #define UPHY_PLL_S0_CTL4_PLL0_CLKDIST_CTRL(x) (((x) & 0xF) << 20) #define UPHY_PLL_S0_CTL4_PLL0_XDIGCLK_EN (1 << 19) #define UPHY_PLL_S0_CTL4_PLL0_XDIGCLK_SEL(x) (((x) & 0x7) << 16) #define UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_EN (1 << 15) #define UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(x) (((x) & 0x3) << 12) #define UPHY_PLL_S0_CTL4_PLL0_FBCLKBUF_EN (1 << 9) #define UPHY_PLL_S0_CTL4_PLL0_REFCLKBUF_EN (1 << 8) #define UPHY_PLL_S0_CTL4_PLL0_REFCLK_SEL(x) (((x) & 0xF) << 4) #define UPHY_PLL_S0_CTL4_PLL0_REFCLK_TERM100 (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL5 0x870 #define UPHY_PLL_S0_CTL5_PLL0_DCO_CTRL(x) (((x) & 0xFF) << 16) #define UPHY_PLL_S0_CTL5_PLL0_LPF_CTRL(x) (((x) & 0xFF) << 8) #define UPHY_PLL_S0_CTL5_PLL0_CP_CTRL(x) (((x) & 0x0F) << 4) #define UPHY_PLL_S0_CTL5_PLL0_PFD_CTRL(x) (((x) & 0x03) << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c #define UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE (1U << 31) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_VAL(x) (((x) & 0x1F) << 24) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_BYP_EN (1 << 23) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_BYP_CODE(x) (((x) & 0x1F) << 16) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD (1 << 15) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN (1 << 13) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_EN (1 << 12) #define UPHY_PLL_S0_CTL8_PLL0_BGAP_CTRL(x) (((x) & 0xFFF) << 0) #define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960 #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(x) (0xa60 + (x) * 0x40) #define UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(x) (((x) & 0x3) << 16) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(x) (0xa64 + (x) * 0x40) #define UPHY_USB3_PAD_ECTL2_RX_IQ_CTRL(x) (((x) & 0x000F) << 16) #define UPHY_USB3_PAD_ECTL2_RX_CTLE(x) (((x) & 0xFFFF) << 0) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL3(x) (0xa68 + (x) * 0x40) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(x) (0xa6c + (x) * 0x40) #define UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(x) (((x) & 0xFFFF) << 16) #define UPHY_USB3_PAD_ECTL4_RX_PI_CTRL(x) (((x) & 0x00FF) << 0) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6(x) (0xa74 + (x) * 0x40) #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) struct padctl_softc { device_t dev; struct resource *mem_res; hwreset_t rst; int phy_ena_cnt; int pcie_ena_cnt; int sata_ena_cnt; /* Fuses calibration data */ /* USB2 */ uint32_t hs_curr_level[4]; uint32_t hs_curr_level_offs; /* Not inited yet, always 0 */ uint32_t hs_term_range_adj; uint32_t rpd_ctrl; /* HSIC */ uint32_t rx_strobe_trim; /* Not inited yet, always 0 */ uint32_t rx_data0_trim; /* Not inited yet, always 0 */ uint32_t rx_data1_trim; /* Not inited yet, always 0 */ uint32_t tx_rtune_p; /* Not inited yet, always 0 */ uint32_t strobe_trim; /* Not inited yet, always 0 */ }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-xusb-padctl", 1}, {NULL, 0}, }; /* Ports. */ enum padctl_port_type { PADCTL_PORT_USB2, PADCTL_PORT_HSIC, PADCTL_PORT_USB3, }; struct padctl_lane; struct padctl_port { enum padctl_port_type type; const char *name; const char *base_name; int idx; int (*init)(struct padctl_softc *sc, struct padctl_port *port); /* Runtime data. */ phandle_t xref; bool enabled; bool internal; uint32_t companion; regulator_t supply_vbus; struct padctl_lane *lane; }; static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port); #define PORT(t, n, p, i) { \ .type = t, \ .name = n "-" #p, \ .base_name = n, \ .idx = p, \ .init = i, \ } static struct padctl_port ports_tbl[] = { PORT(PADCTL_PORT_USB2, "usb2", 0, NULL), PORT(PADCTL_PORT_USB2, "usb2", 1, NULL), PORT(PADCTL_PORT_USB2, "usb2", 2, NULL), PORT(PADCTL_PORT_USB2, "usb2", 3, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL), PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init), PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init), }; /* Pads - a group of lannes. */ enum padctl_pad_type { PADCTL_PAD_USB2, PADCTL_PAD_HSIC, PADCTL_PAD_PCIE, PADCTL_PAD_SATA, }; struct padctl_lane; struct padctl_pad { const char *name; enum padctl_pad_type type; const char *clock_name; char *reset_name; /* XXX constify !!!!!! */ int (*enable)(struct padctl_softc *sc, struct padctl_lane *lane); int (*disable)(struct padctl_softc *sc, struct padctl_lane *lane); /* Runtime data. */ bool enabled; clk_t clk; hwreset_t reset; int nlanes; struct padctl_lane *lanes[8]; /* Safe maximum value. */ }; static int usb2_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int usb2_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int hsic_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int hsic_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_disable(struct padctl_softc *sc, struct padctl_lane *lane); #define PAD(n, t, cn, rn, e, d) { \ .name = n, \ .type = t, \ .clock_name = cn, \ .reset_name = rn, \ .enable = e, \ .disable = d, \ } static struct padctl_pad pads_tbl[] = { PAD("usb2", PADCTL_PAD_USB2, "trk", NULL, usb2_enable, usb2_disable), PAD("hsic", PADCTL_PAD_HSIC, "trk", NULL, hsic_enable, hsic_disable), PAD("pcie", PADCTL_PAD_PCIE, "pll", "phy", pcie_enable, pcie_disable), PAD("sata", PADCTL_PAD_SATA, "pll", "phy", sata_enable, sata_disable), }; /* Lanes. */ static char *usb_mux[] = {"snps", "xusb", "uart", "rsvd"}; static char *hsic_mux[] = {"snps", "xusb"}; static char *pci_mux[] = {"pcie-x1", "usb3-ss", "sata", "pcie-x4"}; struct padctl_lane { const char *name; int idx; bus_size_t reg; uint32_t shift; uint32_t mask; char **mux; int nmux; /* Runtime data. */ bool enabled; phandle_t xref; struct padctl_pad *pad; struct padctl_port *port; int mux_idx; }; #define LANE(n, p, r, s, m, mx) { \ .name = n "-" #p, \ .idx = p, \ .reg = r, \ .shift = s, \ .mask = m, \ .mux = mx, \ .nmux = nitems(mx), \ } static struct padctl_lane lanes_tbl[] = { LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, usb_mux), LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, usb_mux), LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, usb_mux), LANE("usb2", 3, XUSB_PADCTL_USB2_PAD_MUX, 6, 0x3, usb_mux), LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, hsic_mux), LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, hsic_mux), LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 12, 0x3, pci_mux), LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 14, 0x3, pci_mux), LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux), LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux), LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux), LANE("pcie", 5, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux), LANE("pcie", 6, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux), LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 30, 0x3, pci_mux), }; /* Define all possible mappings for USB3 port lanes */ struct padctl_lane_map { int port_idx; enum padctl_pad_type pad_type; int lane_idx; }; #define LANE_MAP(pi, pt, li) { \ .port_idx = pi, \ .pad_type = pt, \ .lane_idx = li, \ } static struct padctl_lane_map lane_map_tbl[] = { LANE_MAP(0, PADCTL_PAD_PCIE, 6), /* port USB3-0 -> lane PCIE-0 */ LANE_MAP(1, PADCTL_PAD_PCIE, 5), /* port USB3-1 -> lane PCIE-1 */ LANE_MAP(2, PADCTL_PAD_PCIE, 0), /* port USB3-2 -> lane PCIE-0 */ LANE_MAP(2, PADCTL_PAD_PCIE, 2), /* port USB3-2 -> lane PCIE-2 */ LANE_MAP(3, PADCTL_PAD_PCIE, 4), /* port USB3-3 -> lane PCIE-4 */ }; /* Phy class and methods. */ static int xusbpadctl_phy_enable(struct phynode *phy, bool enable); static phynode_method_t xusbpadctl_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class, xusbpadctl_phynode_methods, 0, phynode_class); static struct padctl_port *search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane); static void tegra210_xusb_pll_hw_control_enable(void) {} static void tegra210_xusb_pll_hw_sequence_start(void) {} static void tegra210_sata_pll_hw_control_enable(void) {} static void tegra210_sata_pll_hw_sequence_start(void) {} /* ------------------------------------------------------------------------- * * PEX functions */ static int uphy_pex_enable(struct padctl_softc *sc, struct padctl_pad *pad) { uint32_t reg; int rv, i; if (sc->pcie_ena_cnt > 0) { sc->pcie_ena_cnt++; return (0); } /* 22.8.4 UPHY PLLs, Step 4, page 1346 */ /* 1. Deassert PLL/Lane resets. */ rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); return (rv); } rv = hwreset_deassert(pad->reset); if (rv < 0) { device_printf(sc->dev, "Cannot unreset pad '%s': %d\n", pad->name, rv); clk_disable(pad->clk); return (rv); } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(~0); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(0x136); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5); reg &= ~UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(~0); reg |= UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(0x2a); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg |= UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); /* * 2. For the following registers, default values * take care of the desired frequency. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4); reg &= ~UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(~0); reg &= ~UPHY_PLL_P0_CTL4_PLL0_REFCLK_SEL(~0); reg |= UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(0x2); reg |= UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_FREQ_MDIV(~0); reg &= ~UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(~0); reg |= UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(0x19); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_IDDQ; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_SLEEP(~0); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); /* 3. Wait 100 ns. */ DELAY(10); /* XXX This in not in TRM */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4); reg |= UPHY_PLL_P0_CTL4_PLL0_REFCLKBUF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4, reg); /* 4. Calibration. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); for (i = 30; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); if (reg & UPHY_PLL_P0_CTL2_PLL0_CAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 1 " "for pad '%s' (0x%08X).\n", pad->name, reg); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); if ((reg & UPHY_PLL_P0_CTL2_PLL0_CAL_DONE) == 0) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 5. Enable the PLL (20 μs Lock time) */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg |= UPHY_PLL_P0_CTL1_PLL0_ENABLE; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); if (reg & UPHY_PLL_P0_CTL1_PLL0_LOCKDET_STATUS) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout while enabling PLL " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 6. RCAL. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_EN; reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); if (reg & UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); if (!(reg & UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE)) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); /* Enable Hardware Power Sequencer. */ tegra210_xusb_pll_hw_control_enable(); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); DELAY(50); tegra210_xusb_pll_hw_sequence_start(); sc->pcie_ena_cnt++; return (0); err: hwreset_deassert(pad->reset); clk_disable(pad->clk); return (rv); } static void uphy_pex_disable(struct padctl_softc *sc, struct padctl_pad *pad) { int rv; sc->pcie_ena_cnt--; if (sc->pcie_ena_cnt <= 0) { rv = hwreset_assert(pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot reset pad '%s': %d\n", pad->name, rv); } rv = clk_disable(pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot dicable clock for pad '%s': %d\n", pad->name, rv); } } } static int uphy_sata_enable(struct padctl_softc *sc, struct padctl_pad *pad, bool usb) { uint32_t reg; int rv, i; /* 22.8.4 UPHY PLLs, Step 4, page 1346 */ /* 1. Deassert PLL/Lane resets. */ if (sc->sata_ena_cnt > 0) { sc->sata_ena_cnt++; return (0); } rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); return (rv); } rv = hwreset_deassert(pad->reset); if (rv < 0) { device_printf(sc->dev, "Cannot unreset pad '%s': %d\n", pad->name, rv); clk_disable(pad->clk); return (rv); } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(~0); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(0x136); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5); reg &= ~UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(~0); reg |= UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(0x2a); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg |= UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg |= UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); /* * 2. For the following registers, default values * take care of the desired frequency. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4); reg &= ~UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(~0); reg &= ~UPHY_PLL_S0_CTL4_PLL0_REFCLK_SEL(~0); reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_EN; if (usb) reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(0x2); else reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(0x0); /* XXX PLL0_XDIGCLK_EN */ /* value &= ~(1 << 19); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4, reg); */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_FREQ_MDIV(~0); reg &= ~UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(~0); if (usb) reg |= UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(0x19); else reg |= UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(0x1e); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_IDDQ; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_SLEEP(~0); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); /* 3. Wait 100 ns. */ DELAY(1); /* XXX This in not in TRM */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4); reg |= UPHY_PLL_S0_CTL4_PLL0_REFCLKBUF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4, reg); /* 4. Calibration. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg |= UPHY_PLL_S0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); for (i = 30; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); if (reg & UPHY_PLL_S0_CTL2_PLL0_CAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg &= ~UPHY_PLL_S0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); if ((reg & UPHY_PLL_S0_CTL2_PLL0_CAL_DONE) == 0) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 5. Enable the PLL (20 μs Lock time) */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg |= UPHY_PLL_S0_CTL1_PLL0_ENABLE; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); if (reg & UPHY_PLL_S0_CTL1_PLL0_LOCKDET_STATUS) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout while enabling PLL " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 6. RCAL. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_EN; reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); if (reg & UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); if (!(reg & UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE)) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); /* Enable Hardware Power Sequencer. */ tegra210_sata_pll_hw_control_enable(); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg &= ~UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); DELAY(50); tegra210_sata_pll_hw_sequence_start(); sc->sata_ena_cnt++; return (0); err: hwreset_deassert(pad->reset); clk_disable(pad->clk); return (rv); } static void uphy_sata_disable(struct padctl_softc *sc, struct padctl_pad *pad) { int rv; sc->sata_ena_cnt--; if (sc->sata_ena_cnt <= 0) { rv = hwreset_assert(pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot reset pad '%s': %d\n", pad->name, rv); } rv = clk_disable(pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot dicable clock for pad '%s': %d\n", pad->name, rv); } } } static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port) { uint32_t reg; struct padctl_pad *pad; int rv; pad = port->lane->pad; reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP); if (port->internal) reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx); else reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx); reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0); reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion); WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg); if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(~0); reg |= UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(2); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(port->idx), reg); reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL2_RX_CTLE(~0); reg |= UPHY_USB3_PAD_ECTL2_RX_CTLE(0x00fc); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(port->idx), reg); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3(port->idx), 0xc0077f1f); reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(~0); reg |= UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(0x01c7); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(port->idx), reg); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6(port->idx), 0xfcf01368); if (pad->type == PADCTL_PAD_SATA) rv = uphy_sata_enable(sc, pad, true); else rv = uphy_pex_enable(sc, pad); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_VCORE_DOWN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN_EARLY(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int pcie_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int rv; rv = uphy_pex_enable(sc, lane->pad); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int pcie_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); uphy_pex_disable(sc, lane->pad); return (0); } static int sata_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int rv; rv = uphy_sata_enable(sc, lane->pad, false); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int sata_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); uphy_sata_disable(sc, lane->pad); return (0); } static int hsic_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } WR4(sc, XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL, sc->strobe_trim); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx)); reg &= ~HSIC_PAD_CTL1_TX_RTUNEP(~0); reg |= HSIC_PAD_CTL1_TX_RTUNEP(sc->tx_rtune_p); WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL2(lane->idx)); reg &= ~HSIC_PAD_CTL2_RX_STROBE_TRIM(~0); reg &= ~HSIC_PAD_CTL2_RX_DATA1_TRIM(~0); reg &= ~HSIC_PAD_CTL2_RX_DATA0_TRIM(~0); reg |= HSIC_PAD_CTL2_RX_STROBE_TRIM(sc->rx_strobe_trim); reg |= HSIC_PAD_CTL2_RX_DATA1_TRIM(sc->rx_data1_trim); reg |= HSIC_PAD_CTL2_RX_DATA0_TRIM(sc->rx_data0_trim); WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL2(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx)); reg &= ~HSIC_PAD_CTL0_RPU_DATA0; reg &= ~HSIC_PAD_CTL0_RPU_DATA1; reg &= ~HSIC_PAD_CTL0_RPU_STROBE; reg &= ~HSIC_PAD_CTL0_PD_RX_DATA0; reg &= ~HSIC_PAD_CTL0_PD_RX_DATA1; reg &= ~HSIC_PAD_CTL0_PD_RX_STROBE; reg &= ~HSIC_PAD_CTL0_PD_ZI_DATA0; reg &= ~HSIC_PAD_CTL0_PD_ZI_DATA1; reg &= ~HSIC_PAD_CTL0_PD_ZI_STROBE; reg &= ~HSIC_PAD_CTL0_PD_TX_DATA0; reg &= ~HSIC_PAD_CTL0_PD_TX_DATA1; reg &= ~HSIC_PAD_CTL0_PD_TX_STROBE; reg |= HSIC_PAD_CTL0_RPD_DATA0; reg |= HSIC_PAD_CTL0_RPD_DATA1; reg |= HSIC_PAD_CTL0_RPD_STROBE; WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx), reg); rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); if (port->supply_vbus != NULL) regulator_disable(port->supply_vbus); return (rv); } reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL); reg &= ~HSIC_PAD_TRK_CTL_TRK_START_TIMER(~0); reg &= ~HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(~0); reg |= HSIC_PAD_TRK_CTL_TRK_START_TIMER(0x1e); reg |= HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(0x0a); WR4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL, reg); DELAY(10); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL); reg &= ~HSIC_PAD_TRK_CTL_PD_TRK; WR4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL, reg); DELAY(50); clk_disable(pad->clk); return (0); } static int hsic_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx)); reg |= HSIC_PAD_CTL0_PD_RX_DATA0; reg |= HSIC_PAD_CTL0_PD_RX_DATA1; reg |= HSIC_PAD_CTL0_PD_RX_STROBE; reg |= HSIC_PAD_CTL0_PD_ZI_DATA0; reg |= HSIC_PAD_CTL0_PD_ZI_DATA1; reg |= HSIC_PAD_CTL0_PD_ZI_STROBE; reg |= HSIC_PAD_CTL0_PD_TX_DATA0; reg |= HSIC_PAD_CTL0_PD_TX_DATA1; reg |= HSIC_PAD_CTL0_PD_TX_STROBE; WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx), reg); if (port->supply_vbus != NULL) { rv = regulator_disable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot disable vbus regulator\n"); return (rv); } } return (0); } static int usb2_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0); reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0); reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(0x7); WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP); reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0); reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST); WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx)); reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0); reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0); reg &= ~USB2_OTG_PAD_CTL0_PD; reg &= ~USB2_OTG_PAD_CTL0_PD2; reg &= ~USB2_OTG_PAD_CTL0_PD_ZI; reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14); reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level[lane->idx] + sc->hs_curr_level_offs); WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx)); reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0); reg &= ~USB2_OTG_PAD_CTL1_RPD_CTRL(~0); reg &= ~USB2_OTG_PAD_CTL1_PD_DR; reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_OVRD; reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_OVRD; reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj); reg |= USB2_OTG_PAD_CTL1_RPD_CTRL(sc->rpd_ctrl); WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(lane->idx)); reg &= ~USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(~0); reg |= USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; WR4(sc, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(lane->idx), reg); if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); if (port->supply_vbus != NULL) regulator_disable(port->supply_vbus); return (rv); } reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg &= ~USB2_BIAS_PAD_CTL1_TRK_START_TIMER(~0); reg &= ~USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(~0); reg |= USB2_BIAS_PAD_CTL1_TRK_START_TIMER(0x1e); reg |= USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(0x0a); WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); return (0); } static int usb2_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg |= USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); if (port->supply_vbus != NULL) { rv = regulator_disable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot disable vbus regulator\n"); return (rv); } } rv = clk_disable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot disable clock for pad '%s': %d\n", pad->name, rv); return (rv); } return (0); } static int pad_common_enable(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int pad_common_disable(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int xusbpadctl_phy_enable(struct phynode *phy, bool enable) { device_t dev; intptr_t id; struct padctl_softc *sc; struct padctl_lane *lane; struct padctl_pad *pad; int rv; dev = phynode_get_device(phy); id = phynode_get_id(phy); sc = device_get_softc(dev); if (id < 0 || id >= nitems(lanes_tbl)) { device_printf(dev, "Unknown phy: %d\n", (int)id); return (ENXIO); } lane = lanes_tbl + id; if (!lane->enabled) { device_printf(dev, "Lane is not enabled/configured: %s\n", lane->name); return (ENXIO); } pad = lane->pad; if (enable) { if (sc->phy_ena_cnt == 0) { rv = pad_common_enable(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt++; } if (enable) rv = pad->enable(sc, lane); else rv = pad->disable(sc, lane); if (rv != 0) return (rv); if (!enable) { if (sc->phy_ena_cnt == 1) { rv = pad_common_disable(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt--; } return (0); } /* ------------------------------------------------------------------------- * * FDT processing */ static struct padctl_port * search_port(struct padctl_softc *sc, char *port_name) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (strcmp(port_name, ports_tbl[i].name) == 0) return (&ports_tbl[i]); } return (NULL); } static struct padctl_port * search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (!ports_tbl[i].enabled) continue; if (ports_tbl[i].lane == lane) return (ports_tbl + i); } return (NULL); } static struct padctl_lane * search_lane(struct padctl_softc *sc, char *lane_name) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (strcmp(lane_name, lanes_tbl[i].name) == 0) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (!lanes_tbl[i].enabled) continue; if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_usb3_pad_lane(struct padctl_softc *sc, int idx) { int i; struct padctl_lane *lane, *tmp; lane = NULL; for (i = 0; i < nitems(lane_map_tbl); i++) { if (idx != lane_map_tbl[i].port_idx) continue; tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type, lane_map_tbl[i].lane_idx); if (tmp == NULL) continue; if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0) continue; if (lane != NULL) { device_printf(sc->dev, "Duplicated mappings found for" " lanes: %s and %s\n", lane->name, tmp->name); return (NULL); } lane = tmp; } return (lane); } static struct padctl_pad * search_pad(struct padctl_softc *sc, char *pad_name) { int i; for (i = 0; i < nitems(pads_tbl); i++) { if (strcmp(pad_name, pads_tbl[i].name) == 0) return (pads_tbl + i); } return (NULL); } static int search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name) { int i; for (i = 0; i < lane->nmux; i++) { if (strcmp(fnc_name, lane->mux[i]) == 0) return (i); } return (-1); } static int config_lane(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, lane->reg); reg &= ~(lane->mask << lane->shift); reg |= (lane->mux_idx & lane->mask) << lane->shift; WR4(sc, lane->reg, reg); return (0); } static int process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad) { struct padctl_lane *lane; struct phynode *phynode; struct phynode_init_def phy_init; char *name; char *function; int rv; name = NULL; function = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane name.\n"); return (ENXIO); } lane = search_lane(sc, name); if (lane == NULL) { device_printf(sc->dev, "Unknown lane: %s\n", name); rv = ENXIO; goto end; } /* Read function (mux) settings. */ rv = OF_getprop_alloc(node, "nvidia,function", (void **)&function); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane function.\n"); rv = ENXIO; goto end; } lane->mux_idx = search_mux(sc, lane, function); if (lane->mux_idx == ~0) { device_printf(sc->dev, "Unknown function %s for lane %s\n", function, name); rv = ENXIO; goto end; } rv = config_lane(sc, lane); if (rv != 0) { device_printf(sc->dev, "Cannot configure lane: %s: %d\n", name, rv); rv = ENXIO; goto end; } lane->xref = OF_xref_from_node(node); lane->pad = pad; lane->enabled = true; pad->lanes[pad->nlanes++] = lane; /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = lane - lanes_tbl; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy\n"); rv = ENXIO; goto end; } if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot create phy\n"); return (ENXIO); } rv = 0; end: if (name != NULL) OF_prop_free(name); if (function != NULL) OF_prop_free(function); return (rv); } static int process_pad(struct padctl_softc *sc, phandle_t node) { phandle_t xref; struct padctl_pad *pad; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read pad name.\n"); return (ENXIO); } pad = search_pad(sc, name); if (pad == NULL) { device_printf(sc->dev, "Unknown pad: %s\n", name); rv = ENXIO; goto end; } if (pad->clock_name != NULL) { rv = clk_get_by_ofw_name(sc->dev, node, pad->clock_name, &pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' clock\n", pad->clock_name); return (ENXIO); } } if (pad->reset_name != NULL) { rv = hwreset_get_by_ofw_name(sc->dev, node, pad->reset_name, &pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' reset\n", pad->reset_name); return (ENXIO); } } /* Read and process associated lanes. */ node = ofw_bus_find_child(node, "lanes"); if (node <= 0) { device_printf(sc->dev, "Cannot find 'lanes' subnode\n"); rv = ENXIO; goto end; } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_lane(sc, node, pad); if (rv != 0) goto end; xref = OF_xref_from_node(node); OF_device_register_xref(xref, sc->dev); } pad->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int process_port(struct padctl_softc *sc, phandle_t node) { struct padctl_port *port; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read port name.\n"); return (ENXIO); } port = search_port(sc, name); if (port == NULL) { device_printf(sc->dev, "Unknown port: %s\n", name); rv = ENXIO; goto end; } regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", &port->supply_vbus); if (OF_hasprop(node, "nvidia,internal")) port->internal = true; /* Find assigned lane */ if (port->lane == NULL) { switch(port->type) { /* Routing is fixed for USB2 AND HSIC. */ case PADCTL_PORT_USB2: port->lane = search_pad_lane(sc, PADCTL_PAD_USB2, port->idx); break; case PADCTL_PORT_HSIC: port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC, port->idx); break; case PADCTL_PORT_USB3: port->lane = search_usb3_pad_lane(sc, port->idx); break; } } if (port->lane == NULL) { device_printf(sc->dev, "Cannot find lane for port: %s\n", name); rv = ENXIO; goto end; } if (port->type == PADCTL_PORT_USB3) { rv = OF_getencprop(node, "nvidia,usb2-companion", &(port->companion), sizeof(port->companion)); if (rv <= 0) { device_printf(sc->dev, "Missing 'nvidia,usb2-companion' property " "for port: %s\n", name); rv = ENXIO; goto end; } } port->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int parse_fdt(struct padctl_softc *sc, phandle_t base_node) { phandle_t node; int rv; rv = 0; node = ofw_bus_find_child(base_node, "pads"); if (node <= 0) { device_printf(sc->dev, "Cannot find pads subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_pad(sc, node); if (rv != 0) return (rv); } node = ofw_bus_find_child(base_node, "ports"); if (node <= 0) { device_printf(sc->dev, "Cannot find ports subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_port(sc, node); if (rv != 0) return (rv); } return (0); } static void load_calibration(struct padctl_softc *sc) { uint32_t reg; int i; reg = tegra_fuse_read_4(FUSE_SKU_CALIB_0); sc->hs_curr_level[0] = FUSE_SKU_CALIB_0_HS_CURR_LEVEL_0(reg); for (i = 1; i < nitems(sc->hs_curr_level); i++) { sc->hs_curr_level[i] = FUSE_SKU_CALIB_0_HS_CURR_LEVEL_123(reg, i); } sc->hs_term_range_adj = FUSE_SKU_CALIB_0_HS_TERM_RANGE_ADJ(reg); tegra_fuse_read_4(FUSE_USB_CALIB_EXT_0); sc->rpd_ctrl = FUSE_USB_CALIB_EXT_0_RPD_CTRL(reg); } /* ------------------------------------------------------------------------- * * BUS functions */ static int xusbpadctl_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(dev, "Tegra XUSB phy"); return (BUS_PROBE_DEFAULT); } static int xusbpadctl_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int xusbpadctl_attach(device_t dev) { struct padctl_softc * sc; int i, rid, rv; struct padctl_port *port; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst); if (rv != 0) { device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); return (rv); } rv = hwreset_deassert(sc->rst); if (rv != 0) { device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); return (rv); } load_calibration(sc); rv = parse_fdt(sc, node); if (rv != 0) { device_printf(dev, "Cannot parse fdt configuration: %d\n", rv); return (rv); } for (i = 0; i < nitems(ports_tbl); i++) { port = ports_tbl + i; if (!port->enabled) continue; if (port->init == NULL) continue; rv = port->init(sc, port); if (rv != 0) { device_printf(dev, "Cannot init port '%s'\n", port->name); return (rv); } } return (0); } static device_method_t tegra_xusbpadctl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xusbpadctl_probe), DEVMETHOD(device_attach, xusbpadctl_attach), DEVMETHOD(device_detach, xusbpadctl_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, tegra_xusbpadctl_methods, sizeof(struct padctl_softc)); EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, NULL, NULL, 73); diff --git a/sys/arm64/rockchip/rk3568_combphy.c b/sys/arm64/rockchip/rk3568_combphy.c index f9ba38e663ad..5152f3eed0d1 100644 --- a/sys/arm64/rockchip/rk3568_combphy.c +++ b/sys/arm64/rockchip/rk3568_combphy.c @@ -1,469 +1,469 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, 2022 Soren Schmidt * * 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 #include -#include +#include #include #include #include #include #include "syscon_if.h" #include "phydev_if.h" #include "phynode_if.h" static struct ofw_compat_data compat_data[] = { {"rockchip,rk3568-naneng-combphy", 1}, {NULL, 0} }; struct rk3568_combphy_softc { device_t dev; phandle_t node; struct resource *mem; struct phynode *phynode; struct syscon *pipe_grf; struct syscon *pipe_phy_grf; clk_t ref_clk; clk_t apb_clk; clk_t pipe_clk; hwreset_t phy_reset; int mode; }; #define PHYREG6 0x14 #define PHYREG6_PLL_DIV_MASK 0xc0 #define PHYREG6_PLL_DIV_2 (1 << 6) #define PHYREG7 0x18 #define PHYREG7_TX_RTERM_50OHM (8 << 4) #define PHYREG7_RX_RTERM_44OHM (15 << 0) #define PHYREG8 0x1c #define PHYREG8_SSC_EN 0x10 #define PHYREG11 0x28 #define PHYREG11_SU_TRIM_0_7 0xf0 #define PHYREG12 0x2c #define PHYREG12_PLL_LPF_ADJ_VALUE 4 #define PHYREG15 0x38 #define PHYREG15_CTLE_EN 0x01 #define PHYREG15_SSC_CNT_MASK 0xc0 #define PHYREG15_SSC_CNT_VALUE (1 << 6) #define PHYREG16 0x3c #define PHYREG16_SSC_CNT_VALUE 0x5f #define PHYREG18 0x44 #define PHYREG18_PLL_LOOP 0x32 #define PHYREG32 0x7c #define PHYREG32_SSC_MASK 0xf0 #define PHYREG32_SSC_UPWARD (0 << 4) #define PHYREG32_SSC_DOWNWARD (1 << 4) #define PHYREG32_SSC_OFFSET_500PPM (1 << 6) #define PHYREG33 0x80 #define PHYREG33_PLL_KVCO_MASK 0x1c #define PHYREG33_PLL_KVCO_VALUE (2 << 2) #define PIPE_MASK_ALL (0xffff << 16) #define PIPE_PHY_GRF_PIPE_CON0 0x00 #define PIPE_DATABUSWIDTH_MASK 0x3 #define PIPE_DATABUSWIDTH_32BIT 0 #define PIPE_DATABUSWIDTH_16BIT 1 #define PIPE_PHYMODE_MASK (3 << 2) #define PIPE_PHYMODE_PCIE (0 << 2) #define PIPE_PHYMODE_USB3 (1 << 2) #define PIPE_PHYMODE_SATA (2 << 2) #define PIPE_RATE_MASK (3 << 4) #define PIPE_RATE_PCIE_2_5GBPS (0 << 4) #define PIPE_RATE_PCIE_5GBPS (1 << 4) #define PIPE_RATE_USB3_5GBPS (0 << 4) #define PIPE_RATE_SATA_1GBPS5 (0 << 4) #define PIPE_RATE_SATA_3GBPS (1 << 4) #define PIPE_RATE_SATA_6GBPS (2 << 4) #define PIPE_MAC_PCLKREQ_N (1 << 8) #define PIPE_L1SUB_ENTREQ (1 << 9) #define PIPE_RXTERM (1 << 12) #define PIPE_PHY_GRF_PIPE_CON1 0x04 #define PHY_CLK_SEL_MASK (3 << 13) #define PHY_CLK_SEL_24M (0 << 13) #define PHY_CLK_SEL_25M (1 << 13) #define PHY_CLK_SEL_100M (2 << 13) #define PIPE_PHY_GRF_PIPE_CON2 0x08 #define SEL_PIPE_TXCOMPLIANCE_I (1 << 15) #define SEL_PIPE_TXELECIDLE (1 << 12) #define SEL_PIPE_RXTERM (1 << 8) #define SEL_PIPE_BYPASS_CODEC (1 << 7) #define SEL_PIPE_PIPE_EBUF (1 << 6) #define SEL_PIPE_PIPE_PHYMODE (1 << 1) #define SEL_PIPE_DATABUSWIDTH (1 << 0) #define PIPE_PHY_GRF_PIPE_CON3 0x0c #define PIPE_SEL_MASK (3 << 13) #define PIPE_SEL_PCIE (0 << 13) #define PIPE_SEL_USB3 (1 << 13) #define PIPE_SEL_SATA (2 << 13) #define PIPE_CLK_REF_SRC_I_MASK (3 << 8) #define PIPE_CLK_REF_SRC_I_PLL_CKREF_INNER (2 << 8) #define PIPE_RXELECIDLE (1 << 10) #define PIPE_FROM_PCIE_IO (1 << 11) #define PIPE_GRF_PIPE_CON0 0x00 #define SATA2_PHY_SPDMODE_1GBPS5 (0 << 12) #define SATA2_PHY_SPDMODE_3GBPS (1 << 12) #define SATA2_PHY_SPDMODE_6GBPS (2 << 12) #define SATA1_PHY_SPDMODE_1GBPS5 (0 << 8) #define SATA1_PHY_SPDMODE_3GBPS (1 << 8) #define SATA1_PHY_SPDMODE_6GBPS (2 << 8) #define SATA0_PHY_SPDMODE_1GBPS5 (0 << 4) #define SATA0_PHY_SPDMODE_3GBPS (1 << 4) #define SATA0_PHY_SPDMODE_6GBPS (2 << 4) #define PIPE_GRF_SATA_CON0 0x10 #define PIPE_GRF_SATA_CON1 0x14 #define PIPE_GRF_SATA_CON2 0x18 #define PIPE_GRF_XPCS_CON0 0x40 /* PHY class and methods */ static int rk3568_combphy_enable(struct phynode *phynode, bool enable) { device_t dev = phynode_get_device(phynode); struct rk3568_combphy_softc *sc = device_get_softc(dev); uint64_t rate; if (enable == false) return (0); switch (sc->mode) { case PHY_TYPE_SATA: device_printf(dev, "configuring for SATA"); /* tx_rterm 50 ohm & rx_rterm 44 ohm */ bus_write_4(sc->mem, PHYREG7, PHYREG7_TX_RTERM_50OHM | PHYREG7_RX_RTERM_44OHM); /* Adaptive CTLE */ bus_write_4(sc->mem, PHYREG15, bus_read_4(sc->mem, PHYREG15) | PHYREG15_CTLE_EN); /* config grf_pipe for PCIe */ SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON3, PIPE_MASK_ALL | PIPE_SEL_SATA | PIPE_RXELECIDLE | 0x7); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON2, PIPE_MASK_ALL | SEL_PIPE_TXCOMPLIANCE_I | SEL_PIPE_DATABUSWIDTH | 0xc3); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON0, PIPE_MASK_ALL | PIPE_RXTERM | PIPE_DATABUSWIDTH_16BIT | PIPE_RATE_SATA_3GBPS | PIPE_PHYMODE_SATA); SYSCON_WRITE_4(sc->pipe_grf, PIPE_GRF_PIPE_CON0, PIPE_MASK_ALL | SATA0_PHY_SPDMODE_6GBPS | SATA1_PHY_SPDMODE_6GBPS | SATA2_PHY_SPDMODE_6GBPS); break; case PHY_TYPE_PCIE: device_printf(dev, "configuring for PCIe"); /* Set SSC downward spread spectrum */ bus_write_4(sc->mem, PHYREG32, (bus_read_4(sc->mem, PHYREG32) & PHYREG32_SSC_MASK) | PHYREG32_SSC_DOWNWARD); /* config grf_pipe for PCIe */ SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON3, PIPE_MASK_ALL | PIPE_SEL_PCIE | PIPE_CLK_REF_SRC_I_PLL_CKREF_INNER); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON2, PIPE_MASK_ALL | SEL_PIPE_RXTERM | SEL_PIPE_DATABUSWIDTH); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON0, PIPE_MASK_ALL | PIPE_RXTERM | PIPE_DATABUSWIDTH_32BIT | PIPE_RATE_PCIE_2_5GBPS | PIPE_PHYMODE_PCIE); break; case PHY_TYPE_USB3: device_printf(dev, "configuring for USB3"); /* Set SSC downward spread spectrum */ bus_write_4(sc->mem, PHYREG32, (bus_read_4(sc->mem, PHYREG32) & PHYREG32_SSC_MASK) | PHYREG32_SSC_DOWNWARD); /* Adaptive CTLE */ bus_write_4(sc->mem, PHYREG15, bus_read_4(sc->mem, PHYREG15) | PHYREG15_CTLE_EN); /* Set PLL KVCO fine tuning signals */ bus_write_4(sc->mem, PHYREG33, (bus_read_4(sc->mem, PHYREG33) & PHYREG33_PLL_KVCO_MASK) | PHYREG33_PLL_KVCO_VALUE); /* Enable controlling random jitter. */ bus_write_4(sc->mem, PHYREG12, PHYREG12_PLL_LPF_ADJ_VALUE); /* Set PLL input clock divider 1/2 */ bus_write_4(sc->mem, PHYREG6, (bus_read_4(sc->mem, PHYREG6) & PHYREG6_PLL_DIV_MASK) | PHYREG6_PLL_DIV_2); /* Set PLL loop divider */ bus_write_4(sc->mem, PHYREG18, PHYREG18_PLL_LOOP); /* Set PLL LPF R1 to su_trim[0:7] */ bus_write_4(sc->mem, PHYREG11, PHYREG11_SU_TRIM_0_7); /* config grf_pipe for USB3 */ SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON3, PIPE_MASK_ALL | PIPE_SEL_USB3); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON2, PIPE_MASK_ALL); SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON0, PIPE_MASK_ALL | PIPE_DATABUSWIDTH_16BIT | PIPE_PHYMODE_USB3 | PIPE_RATE_USB3_5GBPS); break; default: printf("Unsupported mode=%d\n", sc->mode); return (-1); } clk_get_freq(sc->ref_clk, &rate); printf(" ref_clk=%lu\n", rate); switch (rate) { case 24000000: SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON1, (PHY_CLK_SEL_MASK << 16) | PHY_CLK_SEL_24M); if (sc->mode == PHY_TYPE_USB3 || sc->mode == PHY_TYPE_SATA) { /* Adaptive CTLE */ bus_write_4(sc->mem, PHYREG15, (bus_read_4(sc->mem, PHYREG15) & PHYREG15_SSC_CNT_MASK) | PHYREG15_SSC_CNT_VALUE); /* SSC control period */ bus_write_4(sc->mem, PHYREG16, PHYREG16_SSC_CNT_VALUE); } break; case 25000000: SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON1, (PHY_CLK_SEL_MASK << 16) | PHY_CLK_SEL_25M); break; case 100000000: SYSCON_WRITE_4(sc->pipe_phy_grf, PIPE_PHY_GRF_PIPE_CON1, (PHY_CLK_SEL_MASK << 16) | PHY_CLK_SEL_100M); if (sc->mode == PHY_TYPE_PCIE) { /* Set PLL KVCO fine tuning signals */ bus_write_4(sc->mem, PHYREG33, (bus_read_4(sc->mem, PHYREG33) & PHYREG33_PLL_KVCO_MASK) | PHYREG33_PLL_KVCO_VALUE); /* Enable controlling random jitter. */ bus_write_4(sc->mem, PHYREG12, PHYREG12_PLL_LPF_ADJ_VALUE); /* Set PLL input clock divider 1/2 */ bus_write_4(sc->mem, PHYREG6, (bus_read_4(sc->mem, PHYREG6) & PHYREG6_PLL_DIV_MASK) | PHYREG6_PLL_DIV_2); /* Set PLL loop divider */ bus_write_4(sc->mem, PHYREG18, PHYREG18_PLL_LOOP); /* Set PLL LPF R1 to su_trim[0:7] */ bus_write_4(sc->mem, PHYREG11, PHYREG11_SU_TRIM_0_7); } if (sc->mode == PHY_TYPE_SATA) { /* Set SSC downward spread spectrum */ bus_write_4(sc->mem, PHYREG32, (bus_read_4(sc->mem, PHYREG32) & ~0x000000f0) | PHYREG32_SSC_DOWNWARD | PHYREG32_SSC_OFFSET_500PPM); } break; default: device_printf(dev, "unknown ref rate=%lu\n", rate); break; } if (OF_hasprop(sc->node, "rockchip,ext-refclk")) { device_printf(dev, "UNSUPPORTED rockchip,ext-refclk\n"); } if (OF_hasprop(sc->node, "rockchip,enable-ssc")) { device_printf(dev, "setting rockchip,enable-ssc\n"); bus_write_4(sc->mem, PHYREG8, bus_read_4(sc->mem, PHYREG8) | PHYREG8_SSC_EN); } if (hwreset_deassert(sc->phy_reset)) device_printf(dev, "phy_reset failed to clear\n"); return (0); } static phynode_method_t rk3568_combphy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, rk3568_combphy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(rk3568_combphy_phynode, rk3568_combphy_phynode_class, rk3568_combphy_phynode_methods, 0, phynode_class); /* Device class and methods */ static int rk3568_combphy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip combo PHY"); return (BUS_PROBE_DEFAULT); } static int rk3568_combphy_attach(device_t dev) { struct rk3568_combphy_softc *sc = device_get_softc(dev); struct phynode_init_def phy_init; struct phynode *phynode; int rid = 0; sc->dev = dev; sc->node = ofw_bus_get_node(dev); /* Get memory resource */ if (!(sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } /* Get syncons handles */ if (OF_hasprop(sc->node, "rockchip,pipe-grf") && syscon_get_by_ofw_property(dev, sc->node, "rockchip,pipe-grf", &sc->pipe_grf)) return (ENXIO); if (OF_hasprop(sc->node, "rockchip,pipe-phy-grf") && syscon_get_by_ofw_property(dev, sc->node, "rockchip,pipe-phy-grf", &sc->pipe_phy_grf)) return (ENXIO); /* Get & enable clocks */ if (clk_get_by_ofw_name(dev, 0, "ref", &sc->ref_clk)) { device_printf(dev, "getting ref failed\n"); return (ENXIO); } if (clk_enable(sc->ref_clk)) device_printf(dev, "enable ref failed\n"); if (clk_get_by_ofw_name(dev, 0, "apb", &sc->apb_clk)) { device_printf(dev, "getting apb failed\n"); return (ENXIO); } if (clk_enable(sc->apb_clk)) device_printf(dev, "enable apb failed\n"); if (clk_get_by_ofw_name(dev, 0, "pipe", &sc->pipe_clk)) { device_printf(dev, "getting pipe failed\n"); return (ENXIO); } if (clk_enable(sc->pipe_clk)) device_printf(dev, "enable pipe failed\n"); /* get & assert reset */ if (hwreset_get_by_ofw_idx(dev, sc->node, 0, &sc->phy_reset)) { device_printf(dev, "Cannot get reset\n"); return (ENXIO); } hwreset_assert(sc->phy_reset); bzero(&phy_init, sizeof(phy_init)); phy_init.id = 0; phy_init.ofw_node = sc->node; if (!(phynode = phynode_create(dev, &rk3568_combphy_phynode_class, &phy_init))) { device_printf(dev, "failed to create combphy PHY\n"); return (ENXIO); } if (!phynode_register(phynode)) { device_printf(dev, "failed to register combphy PHY\n"); return (ENXIO); } sc->phynode = phynode; sc->mode = 0; return (0); } static int rk3568_combphy_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { struct rk3568_combphy_softc *sc = device_get_softc(dev); if (phydev_default_ofw_map(dev, xref, ncells, cells, id)) return (ERANGE); /* Store the phy mode that is handed to us in id */ sc->mode = *id; /* Set our id to 0 so the std phy_get_*() works as usual */ *id = 0; return (0); } static device_method_t rk3568_combphy_methods[] = { DEVMETHOD(device_probe, rk3568_combphy_probe), DEVMETHOD(device_attach, rk3568_combphy_attach), DEVMETHOD(phydev_map, rk3568_combphy_map), DEVMETHOD_END }; DEFINE_CLASS_1(rk3568_combphy, rk3568_combphy_driver, rk3568_combphy_methods, sizeof(struct simple_mfd_softc), simple_mfd_driver); EARLY_DRIVER_MODULE(rk3568_combphy, simplebus, rk3568_combphy_driver, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE); diff --git a/sys/arm64/rockchip/rk3568_pcie.c b/sys/arm64/rockchip/rk3568_pcie.c index b8ad5b8a33f5..5343a5dd3f2c 100644 --- a/sys/arm64/rockchip/rk3568_pcie.c +++ b/sys/arm64/rockchip/rk3568_pcie.c @@ -1,397 +1,397 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, 2022 Soren Schmidt * * 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, * without modification, immediately at the beginning of the file. * 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 ``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 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 #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include "pcib_if.h" /* APB Registers */ #define PCIE_CLIENT_GENERAL_CON 0x0000 #define DEVICE_TYPE_MASK 0x00f0 #define DEVICE_TYPE_RC (1<<6) #define LINK_REQ_RST_GRT (1<<3) #define LTSSM_ENABLE (1<<2) #define PCIE_CLIENT_INTR_MASK_MSG_RX 0x0018 #define PCIE_CLIENT_INTR_MASK_LEGACY 0x001c #define PCIE_CLIENT_INTR_MASK_ERR 0x0020 #define PCIE_CLIENT_INTR_MASK_MISC 0x0024 #define PCIE_CLIENT_INTR_MASK_PMC 0x0028 #define PCIE_CLIENT_GENERAL_DEBUG_INFO 0x0104 #define PCIE_CLIENT_HOT_RESET_CTRL 0x0180 #define APP_LSSTM_ENABLE_ENHANCE (1<<4) #define PCIE_CLIENT_LTSSM_STATUS 0x0300 #define RDLH_LINK_UP (1<<17) #define SMLH_LINK_UP (1<<16) #define SMLH_LTSSM_STATE_MASK 0x003f #define SMLH_LTSSM_STATE_LINK_UP ((1<<4) | (1<<0)) struct rk3568_pcie_softc { struct pci_dw_softc dw_sc; /* Must be first */ device_t dev; int apb_rid; struct resource *apb_res; int dbi_rid; struct resource *dbi_res; int irq_rid; struct resource *irq_res; void *irq_handle; phandle_t node; struct gpiobus_pin *reset_gpio; clk_t aclk_mst, aclk_slv, aclk_dbi, pclk, aux; regulator_t regulator; hwreset_t hwreset; phy_t phy; }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3568-pcie", 1}, {NULL, 0} }; static void rk3568_intr(void *data) { struct rk3568_pcie_softc *sc = data; device_printf(sc->dev, "INTERRUPT!!\n"); } static int rk3568_pcie_get_link(device_t dev, bool *status) { struct rk3568_pcie_softc *sc = device_get_softc(dev); uint32_t val; val = bus_read_4(sc->apb_res, PCIE_CLIENT_LTSSM_STATUS); if (((val & (RDLH_LINK_UP | SMLH_LINK_UP)) == (RDLH_LINK_UP | SMLH_LINK_UP)) && ((val & SMLH_LTSSM_STATE_MASK) == SMLH_LTSSM_STATE_LINK_UP)) *status = true; else *status = false; return (0); } static int rk3568_pcie_init_soc(device_t dev) { struct rk3568_pcie_softc *sc = device_get_softc(dev); int err, count; bool status; /* Assert reset */ if (hwreset_assert(sc->hwreset)) device_printf(dev, "Could not assert reset\n"); /* Powerup PCIe */ if (regulator_enable(sc->regulator)) device_printf(dev, "Cannot enable regulator\n"); /* Enable PHY */ if (phy_enable(sc->phy)) device_printf(dev, "Cannot enable phy\n"); /* Deassert reset */ if (hwreset_deassert(sc->hwreset)) device_printf(dev, "Could not deassert reset\n"); /* Enable clocks */ if ((err = clk_enable(sc->aclk_mst))) { device_printf(dev, "Could not enable aclk_mst clk\n"); return (ENXIO); } if ((err = clk_enable(sc->aclk_slv))) { device_printf(dev, "Could not enable aclk_slv clk\n"); return (ENXIO); } if ((err = clk_enable(sc->aclk_dbi))) { device_printf(dev, "Could not enable aclk_dbi clk\n"); return (ENXIO); } if ((err = clk_enable(sc->pclk))) { device_printf(dev, "Could not enable pclk clk\n"); return (ENXIO); } if ((err = clk_enable(sc->aux))) { device_printf(dev, "Could not enable aux clk\n"); return (ENXIO); } /* Set Root Complex (RC) mode */ bus_write_4(sc->apb_res, PCIE_CLIENT_HOT_RESET_CTRL, (APP_LSSTM_ENABLE_ENHANCE << 16) | APP_LSSTM_ENABLE_ENHANCE); bus_write_4(sc->apb_res, PCIE_CLIENT_GENERAL_CON, (DEVICE_TYPE_MASK << 16) | DEVICE_TYPE_RC); /* Assert reset PCIe */ if ((err = gpio_pin_set_active(sc->reset_gpio, false))) device_printf(dev, "reset_gpio set failed\n"); /* Start Link Training and Status State Machine (LTSSM) */ bus_write_4(sc->apb_res, PCIE_CLIENT_GENERAL_CON, (LINK_REQ_RST_GRT | LTSSM_ENABLE) << 16 | (LINK_REQ_RST_GRT | LTSSM_ENABLE)); DELAY(100000); /* Release reset */ if ((err = gpio_pin_set_active(sc->reset_gpio, true))) device_printf(dev, "reset_gpio release failed\n"); /* Wait for link up/stable */ for (count = 20; count; count--) { rk3568_pcie_get_link(dev, &status); if (status) break; DELAY(100000); if (count == 0) { device_printf(dev, "Link up timeout!\n"); return (ENXIO); } } if ((err = pci_dw_init(dev))) return (ENXIO); /* Delay to have things settle */ DELAY(100000); /* Enable all MSG interrupts */ bus_write_4(sc->apb_res, PCIE_CLIENT_INTR_MASK_MSG_RX, 0x7fff0000); /* Enable all Legacy interrupts */ bus_write_4(sc->apb_res, PCIE_CLIENT_INTR_MASK_LEGACY, 0x00ff0000); /* Enable all Error interrupts */ bus_write_4(sc->apb_res, PCIE_CLIENT_INTR_MASK_ERR, 0x0fff0000); return (0); } static int rk3568_pcie_detach(device_t dev) { struct rk3568_pcie_softc *sc = device_get_softc(dev); /* Release allocated resources */ if (sc->irq_handle) bus_teardown_intr(dev, sc->irq_res, sc->irq_handle); if (sc->phy) phy_release(sc->phy); if (sc->aux) clk_release(sc->aux); if (sc->pclk) clk_release(sc->pclk); if (sc->aclk_dbi) clk_release(sc->aclk_dbi); if (sc->aclk_slv) clk_release(sc->aclk_slv); if (sc->aclk_mst) clk_release(sc->aclk_mst); if (sc->hwreset) hwreset_release(sc->hwreset); if (sc->regulator) regulator_release(sc->regulator); if (sc->irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); if (sc->dbi_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->dbi_rid, sc->dbi_res); if (sc->apb_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->apb_rid, sc->apb_res); return (0); } static int rk3568_pcie_attach(device_t dev) { struct rk3568_pcie_softc *sc = device_get_softc(dev); int error; sc->dev = dev; sc->node = ofw_bus_get_node(dev); /* Setup resources */ if ((error = ofw_bus_find_string_index(sc->node, "reg-names", "apb", &sc->apb_rid))) { device_printf(dev, "Cannot get APB memory: %d\n", error); goto fail; } if (!(sc->apb_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->apb_rid, RF_ACTIVE))) { device_printf(dev, "Cannot allocate APB resource\n"); goto fail; } if ((error = ofw_bus_find_string_index(sc->node, "reg-names", "dbi", &sc->dbi_rid))) { device_printf(dev, "Cannot get DBI memory: %d\n", error); goto fail; } if (!(sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->dbi_rid, RF_ACTIVE))) { device_printf(dev, "Cannot allocate DBI resource\n"); goto fail; } if (!(sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE))) { device_printf(dev, "Cannot allocate IRQ resource\n"); goto fail; } /* Get regulator if present */ if (regulator_get_by_ofw_property(dev, 0, "vpcie3v3-supply", &sc->regulator)) { device_printf(dev, "Cannot get regulator\n"); goto fail; } /* Get reset */ if (hwreset_get_by_ofw_name(dev, 0, "pipe", &sc->hwreset)) { device_printf(dev, "Can not get reset\n"); goto fail; } /* Get GPIO reset */ if (OF_hasprop(sc->node, "reset-gpios")) { if (gpio_pin_get_by_ofw_property(dev, sc->node, "reset-gpios", &sc->reset_gpio)) { device_printf(dev, "Cannot get reset-gpios\n"); goto fail; } gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT); gpio_pin_set_active(sc->reset_gpio, true); } /* Get clocks */ if (clk_get_by_ofw_name(dev, 0, "aclk_mst", &sc->aclk_mst)) { device_printf(dev, "Can not get aclk_mst clk\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "aclk_slv", &sc->aclk_slv)) { device_printf(dev, "Can not get aclk_slv clk\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "aclk_dbi", &sc->aclk_dbi)) { device_printf(dev, "Can not get aclk_dbi clk\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk)) { device_printf(dev, "Can not get pclk clk\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "aux", &sc->aux)) { device_printf(dev, "Can not get aux clk\n"); goto fail; } /* Get PHY */ if (phy_get_by_ofw_name(dev, 0, "pcie-phy", &sc->phy)) { device_printf(dev, "Cannot get 'pcie-phy'\n"); goto fail; } if ((error = rk3568_pcie_init_soc(dev))) goto fail; /* Enable interrupt */ if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk3568_intr, sc, &sc->irq_handle))) { device_printf(dev, "unable to setup interrupt\n"); goto fail; } return (bus_generic_attach(dev)); fail: rk3568_pcie_detach(dev); return (ENXIO); } static int rk3568_pcie_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(dev, "RockChip RK3568 PCI-express controller"); return (BUS_PROBE_DEFAULT); } static device_method_t rk3568_pcie_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk3568_pcie_probe), DEVMETHOD(device_attach, rk3568_pcie_attach), DEVMETHOD(device_detach, rk3568_pcie_detach), /* PCI DW interface */ DEVMETHOD(pci_dw_get_link, rk3568_pcie_get_link), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, rk3568_pcie_driver, rk3568_pcie_methods, sizeof(struct rk3568_pcie_softc), pci_dw_driver); DRIVER_MODULE(rk3568_pcie, simplebus, rk3568_pcie_driver, NULL, NULL); diff --git a/sys/arm64/rockchip/rk3568_pciephy.c b/sys/arm64/rockchip/rk3568_pciephy.c index 0f1aa5d280a8..f277253b577d 100644 --- a/sys/arm64/rockchip/rk3568_pciephy.c +++ b/sys/arm64/rockchip/rk3568_pciephy.c @@ -1,263 +1,263 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, 2022 Soren Schmidt * * 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 #include -#include +#include #include #include #include #include #include "syscon_if.h" #include "phydev_if.h" #include "phynode_if.h" #define GRF_PCIE30PHY_CON1 0x04 #define GRF_PCIE30PHY_CON4 0x10 #define GRF_PCIE30PHY_CON5 0x14 #define GRF_PCIE30PHY_CON6 0x18 #define GRF_BIFURCATION_LANE_1 0 #define GRF_BIFURCATION_LANE_2 1 #define GRF_PCIE30PHY_WR_EN (0xf << 16) #define GRF_PCIE30PHY_CON9 0x24 #define GRF_PCIE30PHY_DA_OCM_MASK (1 << (15 + 16)) #define GRF_PCIE30PHY_DA_OCM ((1 << 15) | GRF_PCIE30PHY_DA_OCM_MASK) #define GRF_PCIE30PHY_STATUS0 0x80 #define SRAM_INIT_DONE (1 << 14) static struct ofw_compat_data compat_data[] = { {"rockchip,rk3568-pcie3-phy", 1}, {NULL, 0} }; struct rk3568_pciephy_softc { device_t dev; phandle_t node; struct resource *mem; struct phynode *phynode; struct syscon *phy_grf; clk_t refclk_m; clk_t refclk_n; clk_t pclk; hwreset_t phy_reset; }; static void rk3568_pciephy_bifurcate(device_t dev, int control, uint32_t lane) { struct rk3568_pciephy_softc *sc = device_get_softc(dev); switch (lane) { case 0: SYSCON_WRITE_4(sc->phy_grf, control, GRF_PCIE30PHY_WR_EN); return; case 1: SYSCON_WRITE_4(sc->phy_grf, control, GRF_PCIE30PHY_WR_EN | GRF_BIFURCATION_LANE_1); break; case 2: SYSCON_WRITE_4(sc->phy_grf, control, GRF_PCIE30PHY_WR_EN | GRF_BIFURCATION_LANE_2); break; default: device_printf(dev, "Illegal lane %d\n", lane); return; } if (bootverbose) device_printf(dev, "lane %d @ pcie3x%d\n", lane, (control == GRF_PCIE30PHY_CON5) ? 1 : 2); } /* PHY class and methods */ static int rk3568_pciephy_enable(struct phynode *phynode, bool enable) { device_t dev = phynode_get_device(phynode); struct rk3568_pciephy_softc *sc = device_get_softc(dev); int count; if (enable) { /* Pull PHY out of reset */ hwreset_deassert(sc->phy_reset); /* Poll for SRAM loaded and ready */ for (count = 100; count; count--) { if (SYSCON_READ_4(sc->phy_grf, GRF_PCIE30PHY_STATUS0) & SRAM_INIT_DONE) break; DELAY(10000); if (count == 0) { device_printf(dev, "SRAM init timeout!\n"); return (ENXIO); } } } return (0); } static phynode_method_t rk3568_pciephy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, rk3568_pciephy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(rk3568_pciephy_phynode, rk3568_pciephy_phynode_class, rk3568_pciephy_phynode_methods, 0, phynode_class); /* Device class and methods */ static int rk3568_pciephy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip PCIe PHY"); return (BUS_PROBE_DEFAULT); } static int rk3568_pciephy_attach(device_t dev) { struct rk3568_pciephy_softc *sc = device_get_softc(dev); struct phynode_init_def phy_init; struct phynode *phynode; uint32_t data_lanes[2] = { 0, 0 }; int rid = 0; sc->dev = dev; sc->node = ofw_bus_get_node(dev); /* Get memory resource */ if (!(sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } /* Get syncons handle */ if (OF_hasprop(sc->node, "rockchip,phy-grf") && syscon_get_by_ofw_property(dev, sc->node, "rockchip,phy-grf", &sc->phy_grf)) return (ENXIO); /* Get & enable clocks */ if (clk_get_by_ofw_name(dev, 0, "refclk_m", &sc->refclk_m)) { device_printf(dev, "getting refclk_m failed\n"); return (ENXIO); } if (clk_enable(sc->refclk_m)) device_printf(dev, "enable refclk_m failed\n"); if (clk_get_by_ofw_name(dev, 0, "refclk_n", &sc->refclk_n)) { device_printf(dev, "getting refclk_n failed\n"); return (ENXIO); } if (clk_enable(sc->refclk_n)) device_printf(dev, "enable refclk_n failed\n"); if (clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk)) { device_printf(dev, "getting pclk failed\n"); return (ENXIO); } if (clk_enable(sc->pclk)) device_printf(dev, "enable pclk failed\n"); /* Get & assert reset */ if (hwreset_get_by_ofw_idx(dev, sc->node, 0, &sc->phy_reset)) { device_printf(dev, "Cannot get reset\n"); } else hwreset_assert(sc->phy_reset); /* Set RC/EP mode not implemented yet (RC mode only) */ /* Set bifurcation according to "data-lanes" entry */ if (OF_hasprop(sc->node, "data-lanes")) { OF_getencprop(sc->node, "data-lanes", data_lanes, sizeof(data_lanes)); } else if (bootverbose) device_printf(dev, "lane 1 & 2 @pcie3x2\n"); /* Deassert PCIe PMA output clamp mode */ SYSCON_WRITE_4(sc->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM); /* Configure PHY HW accordingly */ rk3568_pciephy_bifurcate(dev, GRF_PCIE30PHY_CON5, data_lanes[0]); rk3568_pciephy_bifurcate(dev, GRF_PCIE30PHY_CON6, data_lanes[1]); if (data_lanes[0] || data_lanes[1]) SYSCON_WRITE_4(sc->phy_grf, GRF_PCIE30PHY_CON1, GRF_PCIE30PHY_DA_OCM); else SYSCON_WRITE_4(sc->phy_grf, GRF_PCIE30PHY_CON1, GRF_PCIE30PHY_DA_OCM_MASK); bzero(&phy_init, sizeof(phy_init)); phy_init.id = PHY_NONE; phy_init.ofw_node = sc->node; if (!(phynode = phynode_create(dev, &rk3568_pciephy_phynode_class, &phy_init))) { device_printf(dev, "failed to create pciephy PHY\n"); return (ENXIO); } if (!phynode_register(phynode)) { device_printf(dev, "failed to register pciephy PHY\n"); return (ENXIO); } sc->phynode = phynode; return (0); } static device_method_t rk3568_pciephy_methods[] = { DEVMETHOD(device_probe, rk3568_pciephy_probe), DEVMETHOD(device_attach, rk3568_pciephy_attach), DEVMETHOD_END }; DEFINE_CLASS_1(rk3568_pciephy, rk3568_pciephy_driver, rk3568_pciephy_methods, sizeof(struct simple_mfd_softc), simple_mfd_driver); EARLY_DRIVER_MODULE(rk3568_pciephy, simplebus, rk3568_pciephy_driver, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE); diff --git a/sys/arm64/rockchip/rk_i2s.c b/sys/arm64/rockchip/rk_i2s.c index fb1c23b80eb9..3f7cd71eda27 100644 --- a/sys/arm64/rockchip/rk_i2s.c +++ b/sys/arm64/rockchip/rk_i2s.c @@ -1,651 +1,651 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Oleksandr Tymoshenko * * 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 ``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 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 #include #include -#include +#include #include #include "syscon_if.h" #include "opt_snd.h" #include #include #include "audio_dai_if.h" #define AUDIO_BUFFER_SIZE 48000 * 4 #define I2S_TXCR 0x0000 #define I2S_CSR_2 (0 << 15) #define I2S_CSR_4 (1 << 15) #define I2S_CSR_6 (2 << 15) #define I2S_CSR_8 (3 << 15) #define I2S_TXCR_IBM_NORMAL (0 << 9) #define I2S_TXCR_IBM_LJ (1 << 9) #define I2S_TXCR_IBM_RJ (2 << 9) #define I2S_TXCR_PBM_NODELAY (0 << 7) #define I2S_TXCR_PBM_1 (1 << 7) #define I2S_TXCR_PBM_2 (2 << 7) #define I2S_TXCR_PBM_3 (3 << 7) #define I2S_TXCR_TFS_I2S (0 << 5) #define I2S_TXCR_TFS_PCM (1 << 5) #define I2S_TXCR_VDW_16 (0xf << 0) #define I2S_RXCR 0x0004 #define I2S_RXCR_IBM_NORMAL (0 << 9) #define I2S_RXCR_IBM_LJ (1 << 9) #define I2S_RXCR_IBM_RJ (2 << 9) #define I2S_RXCR_PBM_NODELAY (0 << 7) #define I2S_RXCR_PBM_1 (1 << 7) #define I2S_RXCR_PBM_2 (2 << 7) #define I2S_RXCR_PBM_3 (3 << 7) #define I2S_RXCR_TFS_I2S (0 << 5) #define I2S_RXCR_TFS_PCM (1 << 5) #define I2S_RXCR_VDW_16 (0xf << 0) #define I2S_CKR 0x0008 #define I2S_CKR_MSS_MASK (1 << 27) #define I2S_CKR_MSS_MASTER (0 << 27) #define I2S_CKR_MSS_SLAVE (1 << 27) #define I2S_CKR_CKP (1 << 26) #define I2S_CKR_MDIV(n) (((n) - 1) << 16) #define I2S_CKR_MDIV_MASK (0xff << 16) #define I2S_CKR_RSD(n) (((n) - 1) << 8) #define I2S_CKR_RSD_MASK (0xff << 8) #define I2S_CKR_TSD(n) (((n) - 1) << 0) #define I2S_CKR_TSD_MASK (0xff << 0) #define I2S_TXFIFOLR 0x000c #define TXFIFO0LR_MASK 0x3f #define I2S_DMACR 0x0010 #define I2S_DMACR_RDE_ENABLE (1 << 24) #define I2S_DMACR_RDL(n) ((n) << 16) #define I2S_DMACR_TDE_ENABLE (1 << 8) #define I2S_DMACR_TDL(n) ((n) << 0) #define I2S_INTCR 0x0014 #define I2S_INTCR_RFT(n) (((n) - 1) << 20) #define I2S_INTCR_TFT(n) (((n) - 1) << 4) #define I2S_INTCR_RXFIE (1 << 16) #define I2S_INTCR_TXUIC (1 << 2) #define I2S_INTCR_TXEIE (1 << 0) #define I2S_INTSR 0x0018 #define I2S_INTSR_RXFI (1 << 16) #define I2S_INTSR_TXUI (1 << 1) #define I2S_INTSR_TXEI (1 << 0) #define I2S_XFER 0x001c #define I2S_XFER_RXS_START (1 << 1) #define I2S_XFER_TXS_START (1 << 0) #define I2S_CLR 0x0020 #define I2S_CLR_RXC (1 << 1) #define I2S_CLR_TXC (1 << 0) #define I2S_TXDR 0x0024 #define I2S_RXDR 0x0028 #define I2S_RXFIFOLR 0x002c #define RXFIFO0LR_MASK 0x3f /* syscon */ #define GRF_SOC_CON8 0xe220 #define I2S_IO_DIRECTION_MASK 7 #define I2S_IO_DIRECTION_SHIFT 11 #define I2S_IO_8CH_OUT_2CH_IN 0 #define I2S_IO_6CH_OUT_4CH_IN 4 #define I2S_IO_4CH_OUT_6CH_IN 6 #define I2S_IO_2CH_OUT_8CH_IN 7 #define DIV_ROUND_CLOSEST(n,d) (((n) + (d) / 2) / (d)) #define RK_I2S_SAMPLING_RATE 48000 #define FIFO_SIZE 32 static struct ofw_compat_data compat_data[] = { { "rockchip,rk3066-i2s", 1 }, { "rockchip,rk3399-i2s", 1 }, { NULL, 0 } }; static struct resource_spec rk_i2s_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct rk_i2s_softc { device_t dev; struct resource *res[2]; struct mtx mtx; clk_t clk; clk_t hclk; void * intrhand; struct syscon *grf; /* pointers to playback/capture buffers */ uint32_t play_ptr; uint32_t rec_ptr; }; #define RK_I2S_LOCK(sc) mtx_lock(&(sc)->mtx) #define RK_I2S_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RK_I2S_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define RK_I2S_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int rk_i2s_probe(device_t dev); static int rk_i2s_attach(device_t dev); static int rk_i2s_detach(device_t dev); static uint32_t sc_fmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps rk_i2s_caps = {RK_I2S_SAMPLING_RATE, RK_I2S_SAMPLING_RATE, sc_fmt, 0}; static int rk_i2s_init(struct rk_i2s_softc *sc) { uint32_t val; int error; clk_set_freq(sc->clk, RK_I2S_SAMPLING_RATE * 256, CLK_SET_ROUND_DOWN); error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "cannot enable i2s_clk clock\n"); return (ENXIO); } val = I2S_INTCR_TFT(FIFO_SIZE/2); val |= I2S_INTCR_RFT(FIFO_SIZE/2); RK_I2S_WRITE_4(sc, I2S_INTCR, val); if (sc->grf && ofw_bus_is_compatible(sc->dev, "rockchip,rk3399-i2s")) { val = (I2S_IO_2CH_OUT_8CH_IN << I2S_IO_DIRECTION_SHIFT); val |= (I2S_IO_DIRECTION_MASK << I2S_IO_DIRECTION_SHIFT) << 16; SYSCON_WRITE_4(sc->grf, GRF_SOC_CON8, val); #if 0 // HACK: enable IO domain val = (1 << 1); val |= (1 << 1) << 16; SYSCON_WRITE_4(sc->grf, 0xe640, val); #endif } RK_I2S_WRITE_4(sc, I2S_XFER, 0); return (0); } static int rk_i2s_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(dev, "Rockchip I2S"); return (BUS_PROBE_DEFAULT); } static int rk_i2s_attach(device_t dev) { struct rk_i2s_softc *sc; int error; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, rk_i2s_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } error = clk_get_by_ofw_name(dev, 0, "i2s_hclk", &sc->hclk); if (error != 0) { device_printf(dev, "cannot get i2s_hclk clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "i2s_clk", &sc->clk); if (error != 0) { device_printf(dev, "cannot get i2s_clk clock\n"); goto fail; } /* Activate the module clock. */ error = clk_enable(sc->hclk); if (error != 0) { device_printf(dev, "cannot enable i2s_hclk clock\n"); goto fail; } node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } rk_i2s_init(sc); OF_device_register_xref(OF_xref_from_node(node), dev); return (0); fail: rk_i2s_detach(dev); return (error); } static int rk_i2s_detach(device_t dev) { struct rk_i2s_softc *i2s; i2s = device_get_softc(dev); if (i2s->hclk != NULL) clk_release(i2s->hclk); if (i2s->clk) clk_release(i2s->clk); if (i2s->intrhand != NULL) bus_teardown_intr(i2s->dev, i2s->res[1], i2s->intrhand); bus_release_resources(dev, rk_i2s_spec, i2s->res); mtx_destroy(&i2s->mtx); return (0); } static int rk_i2s_dai_init(device_t dev, uint32_t format) { uint32_t val, txcr, rxcr; struct rk_i2s_softc *sc; int fmt, pol, clk; sc = device_get_softc(dev); fmt = AUDIO_DAI_FORMAT_FORMAT(format); pol = AUDIO_DAI_FORMAT_POLARITY(format); clk = AUDIO_DAI_FORMAT_CLOCK(format); /* Set format */ val = RK_I2S_READ_4(sc, I2S_CKR); val &= ~(I2S_CKR_MSS_MASK); switch (clk) { case AUDIO_DAI_CLOCK_CBM_CFM: val |= I2S_CKR_MSS_MASTER; break; case AUDIO_DAI_CLOCK_CBS_CFS: val |= I2S_CKR_MSS_SLAVE; break; default: return (EINVAL); } switch (pol) { case AUDIO_DAI_POLARITY_IB_NF: val |= I2S_CKR_CKP; break; case AUDIO_DAI_POLARITY_NB_NF: val &= ~I2S_CKR_CKP; break; default: return (EINVAL); } RK_I2S_WRITE_4(sc, I2S_CKR, val); txcr = I2S_TXCR_VDW_16 | I2S_CSR_2; rxcr = I2S_RXCR_VDW_16 | I2S_CSR_2; switch (fmt) { case AUDIO_DAI_FORMAT_I2S: txcr |= I2S_TXCR_IBM_NORMAL; rxcr |= I2S_RXCR_IBM_NORMAL; break; case AUDIO_DAI_FORMAT_LJ: txcr |= I2S_TXCR_IBM_LJ; rxcr |= I2S_RXCR_IBM_LJ; break; case AUDIO_DAI_FORMAT_RJ: txcr |= I2S_TXCR_IBM_RJ; rxcr |= I2S_RXCR_IBM_RJ; break; case AUDIO_DAI_FORMAT_DSPA: txcr |= I2S_TXCR_TFS_PCM; rxcr |= I2S_RXCR_TFS_PCM; txcr |= I2S_TXCR_PBM_1; rxcr |= I2S_RXCR_PBM_1; break; case AUDIO_DAI_FORMAT_DSPB: txcr |= I2S_TXCR_TFS_PCM; rxcr |= I2S_RXCR_TFS_PCM; txcr |= I2S_TXCR_PBM_2; rxcr |= I2S_RXCR_PBM_2; break; default: return EINVAL; } RK_I2S_WRITE_4(sc, I2S_TXCR, txcr); RK_I2S_WRITE_4(sc, I2S_RXCR, rxcr); RK_I2S_WRITE_4(sc, I2S_XFER, 0); return (0); } static int rk_i2s_dai_intr(device_t dev, struct snd_dbuf *play_buf, struct snd_dbuf *rec_buf) { struct rk_i2s_softc *sc; uint32_t status; uint32_t level; uint32_t val = 0x00; int ret = 0; sc = device_get_softc(dev); RK_I2S_LOCK(sc); status = RK_I2S_READ_4(sc, I2S_INTSR); if (status & I2S_INTSR_TXEI) { level = RK_I2S_READ_4(sc, I2S_TXFIFOLR) & TXFIFO0LR_MASK; uint8_t *samples; uint32_t count, size, readyptr, written; count = sndbuf_getready(play_buf); if (count > FIFO_SIZE - 1) count = FIFO_SIZE - 1; size = sndbuf_getsize(play_buf); readyptr = sndbuf_getreadyptr(play_buf); samples = (uint8_t*)sndbuf_getbuf(play_buf); written = 0; for (; level < count; level++) { val = (samples[readyptr++ % size] << 0); val |= (samples[readyptr++ % size] << 8); val |= (samples[readyptr++ % size] << 16); val |= (samples[readyptr++ % size] << 24); written += 4; RK_I2S_WRITE_4(sc, I2S_TXDR, val); } sc->play_ptr += written; sc->play_ptr %= size; ret |= AUDIO_DAI_PLAY_INTR; } if (status & I2S_INTSR_RXFI) { level = RK_I2S_READ_4(sc, I2S_RXFIFOLR) & RXFIFO0LR_MASK; uint8_t *samples; uint32_t count, size, freeptr, recorded; count = sndbuf_getfree(rec_buf); size = sndbuf_getsize(rec_buf); freeptr = sndbuf_getfreeptr(rec_buf); samples = (uint8_t*)sndbuf_getbuf(rec_buf); recorded = 0; if (level > count / 4) level = count / 4; for (; level > 0; level--) { val = RK_I2S_READ_4(sc, I2S_RXDR); samples[freeptr++ % size] = val & 0xff; samples[freeptr++ % size] = (val >> 8) & 0xff; samples[freeptr++ % size] = (val >> 16) & 0xff; samples[freeptr++ % size] = (val >> 24) & 0xff; recorded += 4; } sc->rec_ptr += recorded; sc->rec_ptr %= size; ret |= AUDIO_DAI_REC_INTR; } RK_I2S_UNLOCK(sc); return (ret); } static struct pcmchan_caps * rk_i2s_dai_get_caps(device_t dev) { return (&rk_i2s_caps); } static int rk_i2s_dai_trigger(device_t dev, int go, int pcm_dir) { struct rk_i2s_softc *sc = device_get_softc(dev); uint32_t val; uint32_t clear_bit; if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC)) return (EINVAL); switch (go) { case PCMTRIG_START: val = RK_I2S_READ_4(sc, I2S_INTCR); if (pcm_dir == PCMDIR_PLAY) val |= I2S_INTCR_TXEIE; else if (pcm_dir == PCMDIR_REC) val |= I2S_INTCR_RXFIE; RK_I2S_WRITE_4(sc, I2S_INTCR, val); val = I2S_XFER_TXS_START | I2S_XFER_RXS_START; RK_I2S_WRITE_4(sc, I2S_XFER, val); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: val = RK_I2S_READ_4(sc, I2S_INTCR); if (pcm_dir == PCMDIR_PLAY) val &= ~I2S_INTCR_TXEIE; else if (pcm_dir == PCMDIR_REC) val &= ~I2S_INTCR_RXFIE; RK_I2S_WRITE_4(sc, I2S_INTCR, val); /* * If there is no other activity going on, stop transfers */ if ((val & (I2S_INTCR_TXEIE | I2S_INTCR_RXFIE)) == 0) { RK_I2S_WRITE_4(sc, I2S_XFER, 0); if (pcm_dir == PCMDIR_PLAY) clear_bit = I2S_CLR_TXC; else if (pcm_dir == PCMDIR_REC) clear_bit = I2S_CLR_RXC; else return (EINVAL); val = RK_I2S_READ_4(sc, I2S_CLR); val |= clear_bit; RK_I2S_WRITE_4(sc, I2S_CLR, val); while ((RK_I2S_READ_4(sc, I2S_CLR) & clear_bit) != 0) DELAY(1); } RK_I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) sc->play_ptr = 0; else sc->rec_ptr = 0; RK_I2S_UNLOCK(sc); break; } return (0); } static uint32_t rk_i2s_dai_get_ptr(device_t dev, int pcm_dir) { struct rk_i2s_softc *sc; uint32_t ptr; sc = device_get_softc(dev); RK_I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) ptr = sc->play_ptr; else ptr = sc->rec_ptr; RK_I2S_UNLOCK(sc); return ptr; } static int rk_i2s_dai_setup_intr(device_t dev, driver_intr_t intr_handler, void *intr_arg) { struct rk_i2s_softc *sc = device_get_softc(dev); if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, intr_handler, intr_arg, &sc->intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } return (0); } static uint32_t rk_i2s_dai_set_chanformat(device_t dev, uint32_t format) { return (0); } static int rk_i2s_dai_set_sysclk(device_t dev, unsigned int rate, int dai_dir) { struct rk_i2s_softc *sc; int error; sc = device_get_softc(dev); error = clk_disable(sc->clk); if (error != 0) { device_printf(sc->dev, "could not disable i2s_clk clock\n"); return (error); } error = clk_set_freq(sc->clk, rate, CLK_SET_ROUND_DOWN); if (error != 0) device_printf(sc->dev, "could not set i2s_clk freq\n"); error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "could not enable i2s_clk clock\n"); return (error); } return (0); } static uint32_t rk_i2s_dai_set_chanspeed(device_t dev, uint32_t speed) { struct rk_i2s_softc *sc; int error; uint32_t val; uint32_t bus_clock_div, lr_clock_div; uint64_t bus_clk_freq; uint64_t clk_freq; sc = device_get_softc(dev); /* Set format */ val = RK_I2S_READ_4(sc, I2S_CKR); if ((val & I2S_CKR_MSS_SLAVE) == 0) { error = clk_get_freq(sc->clk, &clk_freq); if (error != 0) { device_printf(sc->dev, "failed to get clk frequency: err=%d\n", error); return (error); } bus_clk_freq = 2 * 32 * speed; bus_clock_div = DIV_ROUND_CLOSEST(clk_freq, bus_clk_freq); lr_clock_div = bus_clk_freq / speed; val &= ~(I2S_CKR_MDIV_MASK | I2S_CKR_RSD_MASK | I2S_CKR_TSD_MASK); val |= I2S_CKR_MDIV(bus_clock_div); val |= I2S_CKR_RSD(lr_clock_div); val |= I2S_CKR_TSD(lr_clock_div); RK_I2S_WRITE_4(sc, I2S_CKR, val); } return (speed); } static device_method_t rk_i2s_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_i2s_probe), DEVMETHOD(device_attach, rk_i2s_attach), DEVMETHOD(device_detach, rk_i2s_detach), DEVMETHOD(audio_dai_init, rk_i2s_dai_init), DEVMETHOD(audio_dai_setup_intr, rk_i2s_dai_setup_intr), DEVMETHOD(audio_dai_set_sysclk, rk_i2s_dai_set_sysclk), DEVMETHOD(audio_dai_set_chanspeed, rk_i2s_dai_set_chanspeed), DEVMETHOD(audio_dai_set_chanformat, rk_i2s_dai_set_chanformat), DEVMETHOD(audio_dai_intr, rk_i2s_dai_intr), DEVMETHOD(audio_dai_get_caps, rk_i2s_dai_get_caps), DEVMETHOD(audio_dai_trigger, rk_i2s_dai_trigger), DEVMETHOD(audio_dai_get_ptr, rk_i2s_dai_get_ptr), DEVMETHOD_END }; static driver_t rk_i2s_driver = { "i2s", rk_i2s_methods, sizeof(struct rk_i2s_softc), }; DRIVER_MODULE(rk_i2s, simplebus, rk_i2s_driver, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm64/rockchip/rk_pcie.c b/sys/arm64/rockchip/rk_pcie.c index cccb04ffdcd9..cc6c842a6f6a 100644 --- a/sys/arm64/rockchip/rk_pcie.c +++ b/sys/arm64/rockchip/rk_pcie.c @@ -1,1432 +1,1432 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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. * */ /* Rockchip PCIe controller driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #define ATU_CFG_BUS(x) (((x) & 0x0ff) << 20) #define ATU_CFG_SLOT(x) (((x) & 0x01f) << 15) #define ATU_CFG_FUNC(x) (((x) & 0x007) << 12) #define ATU_CFG_REG(x) (((x) & 0xfff) << 0) #define ATU_TYPE_MEM 0x2 #define ATU_TYPE_IO 0x6 #define ATU_TYPE_CFG0 0xA #define ATU_TYPE_CFG1 0xB #define ATY_TYPE_NOR_MSG 0xC #define ATU_OB_REGIONS 33 #define ATU_OB_REGION_SHIFT 20 #define ATU_OB_REGION_SIZE (1 << ATU_OB_REGION_SHIFT) #define ATU_OB_REGION_0_SIZE (( ATU_OB_REGIONS - 1) * ATU_OB_REGION_SIZE) #define ATU_IB_REGIONS 3 #define PCIE_CLIENT_BASIC_STRAP_CONF 0x000000 #define STRAP_CONF_GEN_2 (1 << 7) #define STRAP_CONF_MODE_RC (1 << 6) #define STRAP_CONF_LANES(n) ((((n) / 2) & 0x3) << 4) #define STRAP_CONF_ARI_EN (1 << 3) #define STRAP_CONF_SR_IOV_EN (1 << 2) #define STRAP_CONF_LINK_TRAIN_EN (1 << 1) #define STRAP_CONF_CONF_EN (1 << 0) #define PCIE_CLIENT_HOT_RESET_CTRL 0x000018 #define HOT_RESET_CTRL_LINK_DOWN_RESET (1 << 1) #define HOT_RESET_CTRL_HOT_RESET_IN (1 << 0) #define PCIE_CLIENT_BASIC_STATUS0 0x000044 #define PCIE_CLIENT_BASIC_STATUS1 0x000048 #define STATUS1_LINK_ST_GET(x) (((x) >> 20) & 0x3) #define STATUS1_LINK_ST_UP 3 #define PCIE_CLIENT_INT_MASK 0x00004C #define PCIE_CLIENT_INT_STATUS 0x000050 #define PCIE_CLIENT_INT_LEGACY_DONE (1 << 15) #define PCIE_CLIENT_INT_MSG (1 << 14) #define PCIE_CLIENT_INT_HOT_RST (1 << 13) #define PCIE_CLIENT_INT_DPA (1 << 12) #define PCIE_CLIENT_INT_FATAL_ERR (1 << 11) #define PCIE_CLIENT_INT_NFATAL_ERR (1 << 10) #define PCIE_CLIENT_INT_CORR_ERR (1 << 9) #define PCIE_CLIENT_INT_INTD (1 << 8) #define PCIE_CLIENT_INT_INTC (1 << 7) #define PCIE_CLIENT_INT_INTB (1 << 6) #define PCIE_CLIENT_INT_INTA (1 << 5) #define PCIE_CLIENT_INT_LOCAL (1 << 4) #define PCIE_CLIENT_INT_UDMA (1 << 3) #define PCIE_CLIENT_INT_PHY (1 << 2) #define PCIE_CLIENT_INT_HOT_PLUG (1 << 1) #define PCIE_CLIENT_INT_PWR_STCG (1 << 0) #define PCIE_CLIENT_INT_LEGACY (PCIE_CLIENT_INT_INTA | \ PCIE_CLIENT_INT_INTB | \ PCIE_CLIENT_INT_INTC | \ PCIE_CLIENT_INT_INTD) #define PCIE_CORE_CTRL0 0x900000 #define CORE_CTRL_LANES_GET(x) (((x) >> 20) & 0x3) #define PCIE_CORE_CTRL1 0x900004 #define PCIE_CORE_CONFIG_VENDOR 0x900044 #define PCIE_CORE_INT_STATUS 0x90020c #define PCIE_CORE_INT_PRFPE (1 << 0) #define PCIE_CORE_INT_CRFPE (1 << 1) #define PCIE_CORE_INT_RRPE (1 << 2) #define PCIE_CORE_INT_PRFO (1 << 3) #define PCIE_CORE_INT_CRFO (1 << 4) #define PCIE_CORE_INT_RT (1 << 5) #define PCIE_CORE_INT_RTR (1 << 6) #define PCIE_CORE_INT_PE (1 << 7) #define PCIE_CORE_INT_MTR (1 << 8) #define PCIE_CORE_INT_UCR (1 << 9) #define PCIE_CORE_INT_FCE (1 << 10) #define PCIE_CORE_INT_CT (1 << 11) #define PCIE_CORE_INT_UTC (1 << 18) #define PCIE_CORE_INT_MMVC (1 << 19) #define PCIE_CORE_INT_MASK 0x900210 #define PCIE_CORE_PHY_FUNC_CONF 0x9002C0 #define PCIE_CORE_RC_BAR_CONF 0x900300 #define PCIE_RC_CONFIG_STD_BASE 0x800000 #define PCIE_RC_CONFIG_PRIV_BASE 0xA00000 #define PCIE_RC_CONFIG_DCSR 0xA000C8 #define PCIE_RC_CONFIG_DCSR_MPS_MASK (0x7 << 5) #define PCIE_RC_CONFIG_DCSR_MPS_128 (0 << 5) #define PCIE_RC_CONFIG_DCSR_MPS_256 (1 << 5) #define PCIE_RC_CONFIG_LINK_CAP 0xA00CC #define PCIE_RC_CONFIG_LINK_CAP_L0S (1 << 10) #define PCIE_RC_CONFIG_LCS 0xA000D0 #define PCIE_RC_CONFIG_THP_CAP 0xA00274 #define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK 0xFFF00000 #define PCIE_CORE_OB_ADDR0(n) (0xC00000 + 0x20 * (n) + 0x00) #define PCIE_CORE_OB_ADDR1(n) (0xC00000 + 0x20 * (n) + 0x04) #define PCIE_CORE_OB_DESC0(n) (0xC00000 + 0x20 * (n) + 0x08) #define PCIE_CORE_OB_DESC1(n) (0xC00000 + 0x20 * (n) + 0x0C) #define PCIE_CORE_OB_DESC2(n) (0xC00000 + 0x20 * (n) + 0x10) #define PCIE_CORE_OB_DESC3(n) (0xC00000 + 0x20 * (n) + 0x14) #define PCIE_CORE_IB_ADDR0(n) (0xC00800 + 0x8 * (n) + 0x00) #define PCIE_CORE_IB_ADDR1(n) (0xC00800 + 0x8 * (n) + 0x04) #define PRIV_CFG_RD4(sc, reg) \ (uint32_t)rk_pcie_local_cfg_read(sc, true, reg, 4) #define PRIV_CFG_RD2(sc, reg) \ (uint16_t)rk_pcie_local_cfg_read(sc, true, reg, 2) #define PRIV_CFG_RD1(sc, reg) \ (uint8_t)rk_pcie_local_cfg_read(sc, true, reg, 1) #define PRIV_CFG_WR4(sc, reg, val) \ rk_pcie_local_cfg_write(sc, true, reg, val, 4) #define PRIV_CFG_WR2(sc, reg, val) \ rk_pcie_local_cfg_write(sc, true, reg, val, 2) #define PRIV_CFG_WR1(sc, reg, val) \ rk_pcie_local_cfg_write(sc, true, reg, val, 1) #define APB_WR4(_sc, _r, _v) bus_write_4((_sc)->apb_mem_res, (_r), (_v)) #define APB_RD4(_sc, _r) bus_read_4((_sc)->apb_mem_res, (_r)) #define MAX_LANES 4 #define RK_PCIE_ENABLE_MSI #define RK_PCIE_ENABLE_MSIX struct rk_pcie_softc { struct ofw_pci_softc ofw_pci; /* Must be first */ struct resource *axi_mem_res; struct resource *apb_mem_res; struct resource *client_irq_res; struct resource *legacy_irq_res; struct resource *sys_irq_res; void *client_irq_cookie; void *legacy_irq_cookie; void *sys_irq_cookie; device_t dev; phandle_t node; struct mtx mtx; struct ofw_pci_range mem_range; struct ofw_pci_range pref_mem_range; struct ofw_pci_range io_range; bool coherent; bus_dma_tag_t dmat; int num_lanes; bool link_is_gen2; bool no_l0s; u_int bus_start; u_int bus_end; u_int root_bus; u_int sub_bus; regulator_t supply_12v; regulator_t supply_3v3; regulator_t supply_1v8; regulator_t supply_0v9; hwreset_t hwreset_core; hwreset_t hwreset_mgmt; hwreset_t hwreset_mgmt_sticky; hwreset_t hwreset_pipe; hwreset_t hwreset_pm; hwreset_t hwreset_aclk; hwreset_t hwreset_pclk; clk_t clk_aclk; clk_t clk_aclk_perf; clk_t clk_hclk; clk_t clk_pm; phy_t phys[MAX_LANES]; gpio_pin_t gpio_ep; }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"rockchip,rk3399-pcie", 1}, {NULL, 0}, }; static uint32_t rk_pcie_local_cfg_read(struct rk_pcie_softc *sc, bool priv, u_int reg, int bytes) { uint32_t val; bus_addr_t base; if (priv) base = PCIE_RC_CONFIG_PRIV_BASE; else base = PCIE_RC_CONFIG_STD_BASE; switch (bytes) { case 4: val = bus_read_4(sc->apb_mem_res, base + reg); break; case 2: val = bus_read_2(sc->apb_mem_res, base + reg); break; case 1: val = bus_read_1(sc->apb_mem_res, base + reg); break; default: val = 0xFFFFFFFF; } return (val); } static void rk_pcie_local_cfg_write(struct rk_pcie_softc *sc, bool priv, u_int reg, uint32_t val, int bytes) { uint32_t val2; bus_addr_t base; if (priv) base = PCIE_RC_CONFIG_PRIV_BASE; else base = PCIE_RC_CONFIG_STD_BASE; switch (bytes) { case 4: bus_write_4(sc->apb_mem_res, base + reg, val); break; case 2: val2 = bus_read_4(sc->apb_mem_res, base + (reg & ~3)); val2 &= ~(0xffff << ((reg & 3) << 3)); val2 |= ((val & 0xffff) << ((reg & 3) << 3)); bus_write_4(sc->apb_mem_res, base + (reg & ~3), val2); break; case 1: val2 = bus_read_4(sc->apb_mem_res, base + (reg & ~3)); val2 &= ~(0xff << ((reg & 3) << 3)); val2 |= ((val & 0xff) << ((reg & 3) << 3)); bus_write_4(sc->apb_mem_res, base + (reg & ~3), val2); break; } } static bool rk_pcie_check_dev(struct rk_pcie_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { uint32_t val; if (bus < sc->bus_start || bus > sc->bus_end || slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX) return (false); if (bus == sc->root_bus) { /* we have only 1 device with 1 function root port */ if (slot > 0 || func > 0) return (false); return (true); } /* link is needed for accessing non-root busses */ val = APB_RD4(sc, PCIE_CLIENT_BASIC_STATUS1); if (STATUS1_LINK_ST_GET(val) != STATUS1_LINK_ST_UP) return (false); /* only one device can be on first subordinate bus */ if (bus == sc->sub_bus && slot != 0 ) return (false); return (true); } static void rk_pcie_map_out_atu(struct rk_pcie_softc *sc, int idx, int type, int num_bits, uint64_t pa) { uint32_t addr0; uint64_t max_size __diagused; /* Check HW constrains */ max_size = idx == 0 ? ATU_OB_REGION_0_SIZE: ATU_OB_REGION_SIZE; KASSERT(idx < ATU_OB_REGIONS, ("Invalid region index: %d\n", idx)); KASSERT(num_bits >= 7 && num_bits <= 63, ("Bit width of region is invalid: %d\n", num_bits)); KASSERT(max_size <= (1ULL << (num_bits + 1)), ("Bit width is invalid for given region[%d]: %d\n", idx, num_bits)); addr0 = (uint32_t)pa & 0xFFFFFF00; addr0 |= num_bits; APB_WR4(sc, PCIE_CORE_OB_ADDR0(idx), addr0); APB_WR4(sc, PCIE_CORE_OB_ADDR1(idx), (uint32_t)(pa >> 32)); APB_WR4(sc, PCIE_CORE_OB_DESC0(idx), 1 << 23 | type); APB_WR4(sc, PCIE_CORE_OB_DESC1(idx), sc->root_bus); /* Readback for sync */ APB_RD4(sc, PCIE_CORE_OB_DESC1(idx)); } static void rk_pcie_map_cfg_atu(struct rk_pcie_softc *sc, int idx, int type) { /* Check HW constrains */ KASSERT(idx < ATU_OB_REGIONS, ("Invalid region index: %d\n", idx)); /* * Config window is only 25 bits width, so we cannot encode full bus * range into it. Remaining bits of bus number should be taken from * DESC1 field. */ APB_WR4(sc, PCIE_CORE_OB_ADDR0(idx), 25 - 1); APB_WR4(sc, PCIE_CORE_OB_ADDR1(idx), 0); APB_WR4(sc, PCIE_CORE_OB_DESC0(idx), 1 << 23 | type); APB_WR4(sc, PCIE_CORE_OB_DESC1(idx), sc->root_bus); /* Readback for sync */ APB_RD4(sc, PCIE_CORE_OB_DESC1(idx)); } static void rk_pcie_map_in_atu(struct rk_pcie_softc *sc, int idx, int num_bits, uint64_t pa) { uint32_t addr0; /* Check HW constrains */ KASSERT(idx < ATU_IB_REGIONS, ("Invalid region index: %d\n", idx)); KASSERT(num_bits >= 7 && num_bits <= 63, ("Bit width of region is invalid: %d\n", num_bits)); addr0 = (uint32_t)pa & 0xFFFFFF00; addr0 |= num_bits; APB_WR4(sc, PCIE_CORE_IB_ADDR0(idx), addr0); APB_WR4(sc, PCIE_CORE_IB_ADDR1(idx), (uint32_t)(pa >> 32)); /* Readback for sync */ APB_RD4(sc, PCIE_CORE_IB_ADDR1(idx)); } static int rk_pcie_decode_ranges(struct rk_pcie_softc *sc, struct ofw_pci_range *ranges, int nranges) { int i; for (i = 0; i < nranges; i++) { switch(ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_IO: if (sc->io_range.size != 0) { device_printf(sc->dev, "Duplicated IO range found in DT\n"); return (ENXIO); } sc->io_range = ranges[i]; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) { if (sc->pref_mem_range.size != 0) { device_printf(sc->dev, "Duplicated memory range found " "in DT\n"); return (ENXIO); } sc->pref_mem_range = ranges[i]; } else { if (sc->mem_range.size != 0) { device_printf(sc->dev, "Duplicated memory range found " "in DT\n"); return (ENXIO); } sc->mem_range = ranges[i]; } } } if (sc->mem_range.size == 0) { device_printf(sc->dev, " At least memory range should be defined in DT.\n"); return (ENXIO); } return (0); } /*----------------------------------------------------------------------------- * * P C I B I N T E R F A C E */ static uint32_t rk_pcie_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct rk_pcie_softc *sc; uint32_t d32, data; uint16_t d16; uint8_t d8; uint64_t addr; int type, ret; sc = device_get_softc(dev); if (!rk_pcie_check_dev(sc, bus, slot, func, reg)) return (0xFFFFFFFFU); if (bus == sc->root_bus) return (rk_pcie_local_cfg_read(sc, false, reg, bytes)); addr = ATU_CFG_BUS(bus) | ATU_CFG_SLOT(slot) | ATU_CFG_FUNC(func) | ATU_CFG_REG(reg); type = bus == sc->sub_bus ? ATU_TYPE_CFG0: ATU_TYPE_CFG1; rk_pcie_map_cfg_atu(sc, 0, type); ret = -1; switch (bytes) { case 1: ret = bus_peek_1(sc->axi_mem_res, addr, &d8); data = d8; break; case 2: ret = bus_peek_2(sc->axi_mem_res, addr, &d16); data = d16; break; case 4: ret = bus_peek_4(sc->axi_mem_res, addr, &d32); data = d32; break; } if (ret != 0) data = 0xFFFFFFFF; return (data); } static void rk_pcie_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct rk_pcie_softc *sc; uint64_t addr; int type; sc = device_get_softc(dev); if (!rk_pcie_check_dev(sc, bus, slot, func, reg)) return; if (bus == sc->root_bus) return (rk_pcie_local_cfg_write(sc, false, reg, val, bytes)); addr = ATU_CFG_BUS(bus) | ATU_CFG_SLOT(slot) | ATU_CFG_FUNC(func) | ATU_CFG_REG(reg); type = bus == sc->sub_bus ? ATU_TYPE_CFG0: ATU_TYPE_CFG1; rk_pcie_map_cfg_atu(sc, 0, type); switch (bytes) { case 1: bus_poke_1(sc->axi_mem_res, addr, (uint8_t)val); break; case 2: bus_poke_2(sc->axi_mem_res, addr, (uint16_t)val); break; case 4: bus_poke_4(sc->axi_mem_res, addr, val); break; default: break; } } #ifdef RK_PCIE_ENABLE_MSI static int rk_pcie_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); rv = intr_alloc_msi(pci, child, msi_parent, count, maxcount,irqs); return (rv); } static int rk_pcie_release_msi(device_t pci, device_t child, int count, int *irqs) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); rv = intr_release_msi(pci, child, msi_parent, count, irqs); return (rv); } #endif static int rk_pcie_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); rv = intr_map_msi(pci, child, msi_parent, irq, addr, data); return (rv); } #ifdef RK_PCIE_ENABLE_MSIX static int rk_pcie_alloc_msix(device_t pci, device_t child, int *irq) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); rv = intr_alloc_msix(pci, child, msi_parent, irq); return (rv); } static int rk_pcie_release_msix(device_t pci, device_t child, int irq) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); rv = intr_release_msix(pci, child, msi_parent, irq); return (rv); } #endif static int rk_pcie_get_id(device_t pci, device_t child, enum pci_id_type type, uintptr_t *id) { phandle_t node; int rv; uint32_t rid; uint16_t pci_rid; if (type != PCI_ID_MSI) return (pcib_get_id(pci, child, type, id)); node = ofw_bus_get_node(pci); pci_rid = pci_get_rid(child); rv = ofw_bus_msimap(node, pci_rid, NULL, &rid); if (rv != 0) return (rv); *id = rid; return (0); } static int rk_pcie_route_interrupt(device_t bus, device_t dev, int pin) { struct rk_pcie_softc *sc; u_int irq; sc = device_get_softc(bus); irq = intr_map_clone_irq(rman_get_start(sc->legacy_irq_res)); device_printf(bus, "route pin %d for device %d.%d to %u\n", pin, pci_get_slot(dev), pci_get_function(dev), irq); return (irq); } /*----------------------------------------------------------------------------- * * B U S / D E V I C E I N T E R F A C E */ static int rk_pcie_parse_fdt_resources(struct rk_pcie_softc *sc) { int i, rv; char buf[16]; /* Regulators. All are optional. */ rv = regulator_get_by_ofw_property(sc->dev, 0, "vpcie12v-supply", &sc->supply_12v); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev,"Cannot get 'vpcie12' regulator\n"); return (ENXIO); } rv = regulator_get_by_ofw_property(sc->dev, 0, "vpcie3v3-supply", &sc->supply_3v3); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev,"Cannot get 'vpcie3v3' regulator\n"); return (ENXIO); } rv = regulator_get_by_ofw_property(sc->dev, 0, "vpcie1v8-supply", &sc->supply_1v8); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev,"Cannot get 'vpcie1v8' regulator\n"); return (ENXIO); } rv = regulator_get_by_ofw_property(sc->dev, 0, "vpcie0v9-supply", &sc->supply_0v9); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev,"Cannot get 'vpcie0v9' regulator\n"); return (ENXIO); } /* Resets. */ rv = hwreset_get_by_ofw_name(sc->dev, 0, "core", &sc->hwreset_core); if (rv != 0) { device_printf(sc->dev, "Cannot get 'core' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "mgmt", &sc->hwreset_mgmt); if (rv != 0) { device_printf(sc->dev, "Cannot get 'mgmt' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "mgmt-sticky", &sc->hwreset_mgmt_sticky); if (rv != 0) { device_printf(sc->dev, "Cannot get 'mgmt-sticky' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "pipe", &sc->hwreset_pipe); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pipe' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "pm", &sc->hwreset_pm); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pm' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "aclk", &sc->hwreset_aclk); if (rv != 0) { device_printf(sc->dev, "Cannot get 'aclk' reset\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(sc->dev, 0, "pclk", &sc->hwreset_pclk); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pclk' reset\n"); return (ENXIO); } /* Clocks. */ rv = clk_get_by_ofw_name(sc->dev, 0, "aclk", &sc->clk_aclk); if (rv != 0) { device_printf(sc->dev, "Cannot get 'aclk' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "aclk-perf", &sc->clk_aclk_perf); if (rv != 0) { device_printf(sc->dev, "Cannot get 'aclk-perf' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "hclk", &sc->clk_hclk); if (rv != 0) { device_printf(sc->dev, "Cannot get 'hclk' clock\n"); return (ENXIO); } rv = clk_get_by_ofw_name(sc->dev, 0, "pm", &sc->clk_pm); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pm' clock\n"); return (ENXIO); } /* Phys. */ for (i = 0; i < MAX_LANES; i++ ) { sprintf (buf, "pcie-phy-%d", i); rv = phy_get_by_ofw_name(sc->dev, 0, buf, sc->phys + i); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' phy\n", buf); return (ENXIO); } } /* GPIO for PERST#. Optional */ rv = gpio_pin_get_by_ofw_property(sc->dev, sc->node, "ep-gpios", &sc->gpio_ep); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'ep-gpios' gpio\n"); return (ENXIO); } return (0); } static int rk_pcie_enable_resources(struct rk_pcie_softc *sc) { int i, rv; uint32_t val; /* Assert all resets */ rv = hwreset_assert(sc->hwreset_pclk); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'pclk' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_aclk); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'aclk' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_pm); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'pm' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_pipe); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'pipe' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_mgmt_sticky); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'mgmt_sticky' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_mgmt); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'hmgmt' reset\n"); return (rv); } rv = hwreset_assert(sc->hwreset_core); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'hcore' reset\n"); return (rv); } DELAY(10000); /* Enable clockls */ rv = clk_enable(sc->clk_aclk); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'aclk' clock\n"); return (rv); } rv = clk_enable(sc->clk_aclk_perf); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'aclk_perf' clock\n"); return (rv); } rv = clk_enable(sc->clk_hclk); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'hclk' clock\n"); return (rv); } rv = clk_enable(sc->clk_pm); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pm' clock\n"); return (rv); } /* Power up regulators */ if (sc->supply_12v != NULL) { rv = regulator_enable(sc->supply_12v); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'vpcie12' regulator\n"); return (rv); } } if (sc->supply_3v3 != NULL) { rv = regulator_enable(sc->supply_3v3); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'vpcie3v3' regulator\n"); return (rv); } } if (sc->supply_1v8 != NULL) { rv = regulator_enable(sc->supply_1v8); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'vpcie1v8' regulator\n"); return (rv); } } if (sc->supply_0v9 != NULL) { rv = regulator_enable(sc->supply_0v9); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'vpcie1v8' regulator\n"); return (rv); } } DELAY(1000); /* Deassert basic resets*/ rv = hwreset_deassert(sc->hwreset_pm); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'pm' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_aclk); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'aclk' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_pclk); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'pclk' reset\n"); return (rv); } /* Set basic PCIe core mode (RC, lanes, gen1 or 2) */ val = STRAP_CONF_GEN_2 << 16 | (sc->link_is_gen2 ? STRAP_CONF_GEN_2: 0); val |= STRAP_CONF_MODE_RC << 16 | STRAP_CONF_MODE_RC; val |= STRAP_CONF_LANES(~0) << 16 | STRAP_CONF_LANES(sc->num_lanes); val |= STRAP_CONF_ARI_EN << 16 | STRAP_CONF_ARI_EN; val |= STRAP_CONF_CONF_EN << 16 | STRAP_CONF_CONF_EN; APB_WR4(sc, PCIE_CLIENT_BASIC_STRAP_CONF, val); for (i = 0; i < MAX_LANES; i++) { rv = phy_enable(sc->phys[i]); if (rv != 0) { device_printf(sc->dev, "Cannot enable phy %d\n", i); return (rv); } } /* Deassert rest of resets - order is important ! */ rv = hwreset_deassert(sc->hwreset_mgmt_sticky); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'mgmt_sticky' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_core); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'core' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_mgmt); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'mgmt' reset\n"); return (rv); } rv = hwreset_deassert(sc->hwreset_pipe); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'pipe' reset\n"); return (rv); } return (0); } static int rk_pcie_setup_hw(struct rk_pcie_softc *sc) { uint32_t val; int i, rv; /* Assert PERST# if defined */ if (sc->gpio_ep != NULL) { rv = gpio_pin_set_active(sc->gpio_ep, 0); if (rv != 0) { device_printf(sc->dev, "Cannot clear 'gpio-ep' gpio\n"); return (rv); } } rv = rk_pcie_enable_resources(sc); if (rv != 0) return(rv); /* Fix wrong default value for transmited FTS for L0s exit */ val = APB_RD4(sc, PCIE_CORE_CTRL1); val |= 0xFFFF << 8; APB_WR4(sc, PCIE_CORE_CTRL1, val); /* Setup PCIE Link Status & Control register */ val = APB_RD4(sc, PCIE_RC_CONFIG_LCS); val |= PCIEM_LINK_CTL_COMMON_CLOCK; APB_WR4(sc, PCIE_RC_CONFIG_LCS, val); val = APB_RD4(sc, PCIE_RC_CONFIG_LCS); val |= PCIEM_LINK_CTL_RCB; APB_WR4(sc, PCIE_RC_CONFIG_LCS, val); /* Enable training for GEN1 */ APB_WR4(sc, PCIE_CLIENT_BASIC_STRAP_CONF, STRAP_CONF_LINK_TRAIN_EN << 16 | STRAP_CONF_LINK_TRAIN_EN); /* Deassert PERST# if defined */ if (sc->gpio_ep != NULL) { rv = gpio_pin_set_active(sc->gpio_ep, 1); if (rv != 0) { device_printf(sc->dev, "Cannot set 'gpio-ep' gpio\n"); return (rv); } } /* Wait for link */ for (i = 500; i > 0; i--) { val = APB_RD4(sc, PCIE_CLIENT_BASIC_STATUS1); if (STATUS1_LINK_ST_GET(val) == STATUS1_LINK_ST_UP) break; DELAY(1000); } if (i <= 0) { device_printf(sc->dev, "Gen1 link training timeouted: 0x%08X.\n", val); return (0); } if (sc->link_is_gen2) { val = APB_RD4(sc, PCIE_RC_CONFIG_LCS); val |= PCIEM_LINK_CTL_RETRAIN_LINK; APB_WR4(sc, PCIE_RC_CONFIG_LCS, val); /* Wait for link */ for (i = 500; i > 0; i--) { val = APB_RD4(sc, PCIE_CLIENT_BASIC_STATUS1); if (STATUS1_LINK_ST_GET(val) == STATUS1_LINK_ST_UP) break; DELAY(1000); } if (i <= 0) device_printf(sc->dev, "Gen2 link training " "timeouted: 0x%08X.\n", val); } val = APB_RD4(sc, PCIE_CORE_CTRL0); val = CORE_CTRL_LANES_GET(val); if (bootverbose) device_printf(sc->dev, "Link width: %d\n", 1 << val); return (0); } static int rk_pcie_setup_sw(struct rk_pcie_softc *sc) { uint32_t val; int i, region; pcib_bridge_init(sc->dev); /* Setup config registers */ APB_WR4(sc, PCIE_CORE_CONFIG_VENDOR, 0x1D87); /* Rockchip vendor ID*/ PRIV_CFG_WR1(sc, PCIR_CLASS, PCIC_BRIDGE); PRIV_CFG_WR1(sc, PCIR_SUBCLASS, PCIS_BRIDGE_PCI); PRIV_CFG_WR1(sc, PCIR_PRIBUS_1, sc->root_bus); PRIV_CFG_WR1(sc, PCIR_SECBUS_1, sc->sub_bus); PRIV_CFG_WR1(sc, PCIR_SUBBUS_1, sc->bus_end); PRIV_CFG_WR2(sc, PCIR_COMMAND, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_SERRESPEN); /* Don't advertise L1 power substate */ val = APB_RD4(sc, PCIE_RC_CONFIG_THP_CAP); val &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK; APB_WR4(sc, PCIE_RC_CONFIG_THP_CAP, val); /* Don't advertise L0s */ if (sc->no_l0s) { val = APB_RD4(sc, PCIE_RC_CONFIG_LINK_CAP); val &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK; APB_WR4(sc, PCIE_RC_CONFIG_LINK_CAP_L0S, val); } /*Adjust maximum payload size*/ val = APB_RD4(sc, PCIE_RC_CONFIG_DCSR); val &= ~PCIE_RC_CONFIG_DCSR_MPS_MASK; val |= PCIE_RC_CONFIG_DCSR_MPS_128; APB_WR4(sc, PCIE_RC_CONFIG_DCSR, val); /* * Prepare IB ATU * map whole address range in 1:1 mappings */ rk_pcie_map_in_atu(sc, 2, 64 - 1, 0); /* Prepare OB ATU */ /* - region 0 (32 MB) is used for config access */ region = 0; rk_pcie_map_out_atu(sc, region++, ATU_TYPE_CFG0, 25 - 1, 0); /* - then map memory (by using 1MB regions */ for (i = 0; i < sc->mem_range.size / ATU_OB_REGION_SIZE; i++) { rk_pcie_map_out_atu(sc, region++, ATU_TYPE_MEM, ATU_OB_REGION_SHIFT - 1, sc->mem_range.pci + ATU_OB_REGION_SIZE * i); } /* - IO space is next, one region typically*/ for (i = 0; i < sc->io_range.size / ATU_OB_REGION_SIZE; i++) { rk_pcie_map_out_atu(sc, region++, ATU_TYPE_IO, ATU_OB_REGION_SHIFT - 1, sc->io_range.pci + ATU_OB_REGION_SIZE * i); } APB_WR4(sc, PCIE_CORE_RC_BAR_CONF, 0); return (0); } static int rk_pcie_sys_irq(void *arg) { struct rk_pcie_softc *sc; uint32_t irq; sc = (struct rk_pcie_softc *)arg; irq = APB_RD4(sc, PCIE_CLIENT_INT_STATUS); if (irq & PCIE_CLIENT_INT_LOCAL) { irq = APB_RD4(sc, PCIE_CORE_INT_STATUS); APB_WR4(sc, PCIE_CORE_INT_STATUS, irq); APB_WR4(sc, PCIE_CLIENT_INT_STATUS, PCIE_CLIENT_INT_LOCAL); device_printf(sc->dev, "'sys' interrupt received: 0x%04X\n", irq); } return (FILTER_HANDLED); } static int rk_pcie_client_irq(void *arg) { struct rk_pcie_softc *sc; uint32_t irq; sc = (struct rk_pcie_softc *)arg; irq = APB_RD4(sc, PCIE_CLIENT_INT_STATUS); /* Clear causes handled by other interrups */ irq &= ~PCIE_CLIENT_INT_LOCAL; irq &= ~PCIE_CLIENT_INT_LEGACY; APB_WR4(sc, PCIE_CLIENT_INT_STATUS, irq); device_printf(sc->dev, "'client' interrupt received: 0x%04X\n", irq); return (FILTER_HANDLED); } static int rk_pcie_legacy_irq(void *arg) { struct rk_pcie_softc *sc; uint32_t irq; sc = (struct rk_pcie_softc *)arg; irq = APB_RD4(sc, PCIE_CLIENT_INT_STATUS); irq &= PCIE_CLIENT_INT_LEGACY; APB_WR4(sc, PCIE_CLIENT_INT_STATUS, irq); /* all legacy interrupt are shared, do nothing */ return (FILTER_STRAY); } static bus_dma_tag_t rk_pcie_get_dma_tag(device_t dev, device_t child) { struct rk_pcie_softc *sc; sc = device_get_softc(dev); return (sc->dmat); } static int rk_pcie_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Rockchip PCIe controller"); return (BUS_PROBE_DEFAULT); } static int rk_pcie_attach(device_t dev) { struct resource_map_request req; struct resource_map map; struct rk_pcie_softc *sc; uint32_t val; int rv, rid, max_speed; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); mtx_init(&sc->mtx, "rk_pcie_mtx", NULL, MTX_DEF); /* XXX Should not be this configurable ? */ sc->bus_start = 0; sc->bus_end = 0x1F; sc->root_bus = sc->bus_start; sc->sub_bus = 1; /* Read FDT properties */ rv = rk_pcie_parse_fdt_resources(sc); if (rv != 0) goto out; sc->coherent = OF_hasprop(sc->node, "dma-coherent"); sc->no_l0s = OF_hasprop(sc->node, "aspm-no-l0s"); rv = OF_getencprop(sc->node, "num-lanes", &sc->num_lanes, sizeof(sc->num_lanes)); if (rv != sizeof(sc->num_lanes)) sc->num_lanes = 1; if (sc->num_lanes != 1 && sc->num_lanes != 2 && sc->num_lanes != 4) { device_printf(dev, "invalid number of lanes: %d\n",sc->num_lanes); sc->num_lanes = 0; rv = ENXIO; goto out; } rv = OF_getencprop(sc->node, "max-link-speed", &max_speed, sizeof(max_speed)); if (rv != sizeof(max_speed) || max_speed != 1) sc->link_is_gen2 = true; else sc->link_is_gen2 = false; rv = ofw_bus_find_string_index(sc->node, "reg-names", "axi-base", &rid); if (rv != 0) { device_printf(dev, "Cannot get 'axi-base' memory\n"); rv = ENXIO; goto out; } sc->axi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_UNMAPPED); if (sc->axi_mem_res == NULL) { device_printf(dev, "Cannot allocate 'axi-base' (rid: %d)\n", rid); rv = ENXIO; goto out; } resource_init_map_request(&req); req.memattr = VM_MEMATTR_DEVICE_NP; rv = bus_map_resource(dev, SYS_RES_MEMORY, sc->axi_mem_res, &req, &map); if (rv != 0) { device_printf(dev, "Cannot map 'axi-base' (rid: %d)\n", rid); goto out; } rman_set_mapping(sc->axi_mem_res, &map); rv = ofw_bus_find_string_index(sc->node, "reg-names", "apb-base", &rid); if (rv != 0) { device_printf(dev, "Cannot get 'apb-base' memory\n"); rv = ENXIO; goto out; } sc->apb_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->apb_mem_res == NULL) { device_printf(dev, "Cannot allocate 'apb-base' (rid: %d)\n", rid); rv = ENXIO; goto out; } rv = ofw_bus_find_string_index(sc->node, "interrupt-names", "client", &rid); if (rv != 0) { device_printf(dev, "Cannot get 'client' IRQ\n"); rv = ENXIO; goto out; } sc->client_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->client_irq_res == NULL) { device_printf(dev, "Cannot allocate 'client' IRQ resource\n"); rv = ENXIO; goto out; } rv = ofw_bus_find_string_index(sc->node, "interrupt-names", "legacy", &rid); if (rv != 0) { device_printf(dev, "Cannot get 'legacy' IRQ\n"); rv = ENXIO; goto out; } sc->legacy_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->legacy_irq_res == NULL) { device_printf(dev, "Cannot allocate 'legacy' IRQ resource\n"); rv = ENXIO; goto out; } rv = ofw_bus_find_string_index(sc->node, "interrupt-names", "sys", &rid); if (rv != 0) { device_printf(dev, "Cannot get 'sys' IRQ\n"); rv = ENXIO; goto out; } sc->sys_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sys_irq_res == NULL) { device_printf(dev, "Cannot allocate 'sys' IRQ resource\n"); rv = ENXIO; goto out; } if (bootverbose) device_printf(dev, "Bus is%s cache-coherent\n", sc->coherent ? "" : " not"); rv = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE, /* maxsegsize */ sc->coherent ? BUS_DMA_COHERENT : 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dmat); if (rv != 0) goto out; rv = ofw_pcib_init(dev); if (rv != 0) goto out; rv = rk_pcie_decode_ranges(sc, sc->ofw_pci.sc_range, sc->ofw_pci.sc_nrange); if (rv != 0) goto out_full; rv = rk_pcie_setup_hw(sc); if (rv != 0) goto out_full; rv = rk_pcie_setup_sw(sc); if (rv != 0) goto out_full; rv = bus_setup_intr(dev, sc->client_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, rk_pcie_client_irq, NULL, sc, &sc->client_irq_cookie); if (rv != 0) { device_printf(dev, "cannot setup client interrupt handler\n"); rv = ENXIO; goto out_full; } rv = bus_setup_intr(dev, sc->legacy_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, rk_pcie_legacy_irq, NULL, sc, &sc->legacy_irq_cookie); if (rv != 0) { device_printf(dev, "cannot setup client interrupt handler\n"); rv = ENXIO; goto out_full; } rv = bus_setup_intr(dev, sc->sys_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, rk_pcie_sys_irq, NULL, sc, &sc->sys_irq_cookie); if (rv != 0) { device_printf(dev, "cannot setup client interrupt handler\n"); rv = ENXIO; goto out_full; } /* Enable interrupts */ val = PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD | PCIE_CLIENT_INT_PHY; APB_WR4(sc, PCIE_CLIENT_INT_MASK, (val << 16) & ~val); val = PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | PCIE_CORE_INT_MMVC; APB_WR4(sc, PCIE_CORE_INT_MASK, ~(val)); val = APB_RD4(sc, PCIE_RC_CONFIG_LCS); val |= PCIEM_LINK_CTL_LBMIE | PCIEM_LINK_CTL_LABIE; APB_WR4(sc, PCIE_RC_CONFIG_LCS, val); DELAY(250000); device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); out_full: bus_teardown_intr(dev, sc->sys_irq_res, sc->sys_irq_cookie); bus_teardown_intr(dev, sc->legacy_irq_res, sc->legacy_irq_cookie); bus_teardown_intr(dev, sc->client_irq_res, sc->client_irq_cookie); ofw_pcib_fini(dev); out: bus_dma_tag_destroy(sc->dmat); bus_free_resource(dev, SYS_RES_IRQ, sc->sys_irq_res); bus_free_resource(dev, SYS_RES_IRQ, sc->legacy_irq_res); bus_free_resource(dev, SYS_RES_IRQ, sc->client_irq_res); bus_free_resource(dev, SYS_RES_MEMORY, sc->apb_mem_res); bus_free_resource(dev, SYS_RES_MEMORY, sc->axi_mem_res); /* GPIO */ gpio_pin_release(sc->gpio_ep); /* Phys */ for (int i = 0; i < MAX_LANES; i++) { phy_release(sc->phys[i]); } /* Clocks */ clk_release(sc->clk_aclk); clk_release(sc->clk_aclk_perf); clk_release(sc->clk_hclk); clk_release(sc->clk_pm); /* Resets */ hwreset_release(sc->hwreset_core); hwreset_release(sc->hwreset_mgmt); hwreset_release(sc->hwreset_pipe); hwreset_release(sc->hwreset_pm); hwreset_release(sc->hwreset_aclk); hwreset_release(sc->hwreset_pclk); /* Regulators */ regulator_release(sc->supply_12v); regulator_release(sc->supply_3v3); regulator_release(sc->supply_1v8); regulator_release(sc->supply_0v9); return (rv); } static device_method_t rk_pcie_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_pcie_probe), DEVMETHOD(device_attach, rk_pcie_attach), /* Bus interface */ DEVMETHOD(bus_get_dma_tag, rk_pcie_get_dma_tag), /* pcib interface */ DEVMETHOD(pcib_read_config, rk_pcie_read_config), DEVMETHOD(pcib_write_config, rk_pcie_write_config), DEVMETHOD(pcib_route_interrupt, rk_pcie_route_interrupt), #ifdef RK_PCIE_ENABLE_MSI DEVMETHOD(pcib_alloc_msi, rk_pcie_alloc_msi), DEVMETHOD(pcib_release_msi, rk_pcie_release_msi), #endif #ifdef RK_PCIE_ENABLE_MSIX DEVMETHOD(pcib_alloc_msix, rk_pcie_alloc_msix), DEVMETHOD(pcib_release_msix, rk_pcie_release_msix), #endif DEVMETHOD(pcib_map_msi, rk_pcie_map_msi), DEVMETHOD(pcib_get_id, rk_pcie_get_id), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, rk_pcie_driver, rk_pcie_methods, sizeof(struct rk_pcie_softc), ofw_pcib_driver); DRIVER_MODULE( rk_pcie, simplebus, rk_pcie_driver, NULL, NULL); diff --git a/sys/arm64/rockchip/rk_pcie_phy.c b/sys/arm64/rockchip/rk_pcie_phy.c index d4c8c3eb2587..88ba4035ebb9 100644 --- a/sys/arm64/rockchip/rk_pcie_phy.c +++ b/sys/arm64/rockchip/rk_pcie_phy.c @@ -1,365 +1,365 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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. */ /* * Rockchip PHY TYPEC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "syscon_if.h" #define GRF_HIWORD_SHIFT 16 #define GRF_SOC_CON_5_PCIE 0xE214 #define CON_5_PCIE_IDLE_OFF(x) (1 <<(((x) & 0x3) + 3)) #define GRF_SOC_CON8 0xE220 #define GRF_SOC_STATUS1 0xE2A4 /* PHY config registers - write */ #define PHY_CFG_CLK_TEST 0x10 #define CLK_TEST_SEPE_RATE (1 << 3) #define PHY_CFG_CLK_SCC 0x12 #define CLK_SCC_PLL_100M (1 << 3) /* PHY config registers - read */ #define PHY_CFG_PLL_LOCK 0x10 #define CLK_PLL_LOCKED (1 << 1) #define PHY_CFG_SCC_LOCK 0x12 #define CLK_SCC_100M_GATE (1 << 2) #define STATUS1_PLL_LOCKED (1 << 9) static struct ofw_compat_data compat_data[] = { {"rockchip,rk3399-pcie-phy", 1}, {NULL, 0} }; struct rk_pcie_phy_softc { device_t dev; struct syscon *syscon; struct mtx mtx; clk_t clk_ref; hwreset_t hwreset_phy; int enable_count; }; #define PHY_LOCK(_sc) mtx_lock(&(_sc)->mtx) #define PHY_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) #define PHY_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ device_get_nameunit(_sc->dev), "rk_pcie_phyc", MTX_DEF) #define PHY_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); #define PHY_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); #define PHY_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); #define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg)) #define WR4(sc, reg, mask, val) \ SYSCON_WRITE_4((sc)->syscon, (reg), ((mask) << GRF_HIWORD_SHIFT) | (val)) #define MAX_LANE 4 static void cfg_write(struct rk_pcie_phy_softc *sc, uint32_t reg, uint32_t data) { /* setup register address and data first */ WR4(sc, GRF_SOC_CON8, 0x7FF, (reg & 0x3F) << 1 | (data & 0x0F) << 7); /* dummy readback for sync */ RD4(sc, GRF_SOC_CON8); /* Do write pulse */ WR4(sc, GRF_SOC_CON8, 1, 1); RD4(sc, GRF_SOC_CON8); DELAY(10); WR4(sc, GRF_SOC_CON8, 1, 0); RD4(sc, GRF_SOC_CON8); DELAY(10); } static uint32_t cfg_read(struct rk_pcie_phy_softc *sc, uint32_t reg) { uint32_t val; WR4(sc, GRF_SOC_CON8, 0x3FF, reg << 1); RD4(sc, GRF_SOC_CON8); DELAY(10); val = RD4(sc, GRF_SOC_STATUS1); return ((val >> 8) & 0x0f); } static int rk_pcie_phy_up(struct rk_pcie_phy_softc *sc, int id) { uint32_t val; int i, rv; PHY_LOCK(sc); sc->enable_count++; if (sc->enable_count != 1) { PHY_UNLOCK(sc); return (0); } rv = hwreset_deassert(sc->hwreset_phy); if (rv != 0) { device_printf(sc->dev, "Cannot deassert 'phy' reset\n"); PHY_UNLOCK(sc); return (rv); } /* Un-idle all lanes */ for (i = 0; i < MAX_LANE; i++) WR4(sc, GRF_SOC_CON_5_PCIE, CON_5_PCIE_IDLE_OFF(i), 0); /* Wait for PLL lock */ for (i = 100; i > 0; i--) { val = cfg_read(sc, PHY_CFG_PLL_LOCK); if (val & CLK_PLL_LOCKED) break; DELAY(1000); } if (i <= 0) { device_printf(sc->dev, "PLL lock timeouted, 0x%02X\n", val); PHY_UNLOCK(sc); return (ETIMEDOUT); } /* Switch PLL to stable 5GHz, rate adjustment is done by divider */ cfg_write(sc, PHY_CFG_CLK_TEST, CLK_TEST_SEPE_RATE); /* Enable 100MHz output for PCIe ref clock */ cfg_write(sc, PHY_CFG_CLK_SCC, CLK_SCC_PLL_100M); /* Wait for ungating of ref clock */ for (i = 100; i > 0; i--) { val = cfg_read(sc, PHY_CFG_SCC_LOCK); if ((val & CLK_SCC_100M_GATE) == 0) break; DELAY(1000); } if (i <= 0) { device_printf(sc->dev, "PLL output enable timeouted\n"); PHY_UNLOCK(sc); return (ETIMEDOUT); } /* Wait for PLL relock (to 5GHz) */ for (i = 100; i > 0; i--) { val = cfg_read(sc, PHY_CFG_PLL_LOCK); if (val & CLK_PLL_LOCKED) break; DELAY(1000); } if (i <= 0) { device_printf(sc->dev, "PLL relock timeouted\n"); PHY_UNLOCK(sc); return (ETIMEDOUT); } PHY_UNLOCK(sc); return (rv); } static int rk_pcie_phy_down(struct rk_pcie_phy_softc *sc, int id) { int rv; PHY_LOCK(sc); rv = 0; if (sc->enable_count <= 0) panic("unpaired enable/disable"); sc->enable_count--; /* Idle given lane */ WR4(sc, GRF_SOC_CON_5_PCIE, CON_5_PCIE_IDLE_OFF(id), CON_5_PCIE_IDLE_OFF(id)); if (sc->enable_count == 0) { rv = hwreset_assert(sc->hwreset_phy); if (rv != 0) device_printf(sc->dev, "Cannot assert 'phy' reset\n"); } PHY_UNLOCK(sc); return (rv); } static int rk_pcie_phy_enable(struct phynode *phynode, bool enable) { struct rk_pcie_phy_softc *sc; device_t dev; intptr_t phy; int rv; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (enable) rv = rk_pcie_phy_up(sc, (int)phy); else rv = rk_pcie_phy_down(sc, (int) phy); return (rv); } /* Phy class and methods. */ static phynode_method_t rk_pcie_phy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, rk_pcie_phy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1( rk_pcie_phy_phynode, rk_pcie_phy_phynode_class, rk_pcie_phy_phynode_methods, 0, phynode_class); static int rk_pcie_phy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Rockchip RK3399 PCIe PHY"); return (BUS_PROBE_DEFAULT); } static int rk_pcie_phy_attach(device_t dev) { struct rk_pcie_phy_softc *sc; struct phynode_init_def phy_init; struct phynode *phynode; phandle_t node; int i, rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); PHY_LOCK_INIT(sc); if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || sc->syscon == NULL) { device_printf(dev, "cannot get syscon for device\n"); rv = ENXIO; goto fail; } rv = clk_set_assigned(dev, ofw_bus_get_node(dev)); if (rv != 0 && rv != ENOENT) { device_printf(dev, "clk_set_assigned failed: %d\n", rv); rv = ENXIO; goto fail; } rv = clk_get_by_ofw_name(sc->dev, 0, "refclk", &sc->clk_ref); if (rv != 0) { device_printf(sc->dev, "Cannot get 'refclk' clock\n"); rv = ENXIO; goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, 0, "phy", &sc->hwreset_phy); if (rv != 0) { device_printf(sc->dev, "Cannot get 'phy' reset\n"); rv = ENXIO; goto fail; } rv = hwreset_assert(sc->hwreset_phy); if (rv != 0) { device_printf(sc->dev, "Cannot assert 'phy' reset\n"); rv = ENXIO; goto fail; } rv = clk_enable(sc->clk_ref); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'ref' clock\n"); rv = ENXIO; goto fail; } for (i = 0; i < MAX_LANE; i++) { phy_init.id = i; phy_init.ofw_node = node; phynode = phynode_create(dev, &rk_pcie_phy_phynode_class, &phy_init); if (phynode == NULL) { device_printf(dev, "Cannot create phy[%d]\n", i); rv = ENXIO; goto fail; } if (phynode_register(phynode) == NULL) { device_printf(dev, "Cannot register phy[%d]\n", i); rv = ENXIO; goto fail; } } return (0); fail: return (rv); } static device_method_t rk_pcie_phy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_pcie_phy_probe), DEVMETHOD(device_attach, rk_pcie_phy_attach), DEVMETHOD_END }; DEFINE_CLASS_0(rk_pcie_phy, rk_pcie_phy_driver, rk_pcie_phy_methods, sizeof(struct rk_pcie_phy_softc)); EARLY_DRIVER_MODULE(rk_pcie_phy, simplebus, rk_pcie_phy_driver, NULL, NULL, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm64/rockchip/rk_tsadc.c b/sys/arm64/rockchip/rk_tsadc.c index 8b99c384da48..ff5fd722d0f0 100644 --- a/sys/arm64/rockchip/rk_tsadc.c +++ b/sys/arm64/rockchip/rk_tsadc.c @@ -1,880 +1,880 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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 /* * Thermometer and thermal zones driver for RockChip SoCs. * Calibration data are taken from Linux, because this part of SoC * is undocumented in TRM. */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include "syscon_if.h" #include "rk_tsadc_if.h" /* Version of HW */ #define TSADC_V2 1 #define TSADC_V3 2 #define TSADC_V7 3 /* Global registers */ #define TSADC_USER_CON 0x000 #define TSADC_AUTO_CON 0x004 #define TSADC_AUTO_CON_POL_HI (1 << 8) #define TSADC_AUTO_SRC_EN(x) (1 << (4 + (x))) #define TSADC_AUTO_Q_SEL (1 << 1) /* V3 only */ #define TSADC_AUTO_CON_AUTO (1 << 0) #define TSADC_INT_EN 0x008 #define TSADC_INT_EN_2CRU_EN_SRC(x) (1 << (8 + (x))) #define TSADC_INT_EN_2GPIO_EN_SRC(x) (1 << (4 + (x))) #define TSADC_INT_PD 0x00c #define TSADC_DATA(x) (0x20 + (x) * 0x04) #define TSADC_COMP_INT(x) (0x30 + (x) * 0x04) #define TSADC_COMP_INT_SRC_EN(x) (1 << (0 + (x))) #define TSADC_COMP_SHUT(x) (0x40 + (x) * 0x04) #define TSADC_HIGHT_INT_DEBOUNCE 0x060 #define TSADC_HIGHT_TSHUT_DEBOUNCE 0x064 #define TSADC_AUTO_PERIOD 0x068 #define TSADC_AUTO_PERIOD_HT 0x06c #define TSADC_COMP0_LOW_INT 0x080 /* V3 only */ #define TSADC_COMP1_LOW_INT 0x084 /* V3 only */ /* V3 GFR registers */ #define GRF_SARADC_TESTBIT 0x0e644 #define GRF_SARADC_TESTBIT_ON (0x10001 << 2) #define GRF_TSADC_TESTBIT_L 0x0e648 #define GRF_TSADC_VCM_EN_L (0x10001 << 7) #define GRF_TSADC_TESTBIT_H 0x0e64c #define GRF_TSADC_VCM_EN_H (0x10001 << 7) #define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) /* V7 GRF register */ #define GRF_TSADC_CON 0x0600 #define GRF_TSADC_ANA_REG0 (0x10001 << 0) #define GRF_TSADC_ANA_REG1 (0x10001 << 1) #define GRF_TSADC_ANA_REG2 (0x10001 << 2) #define GRF_TSADC_TSEN (0x10001 << 8) #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) static struct sysctl_ctx_list tsadc_sysctl_ctx; struct tsensor { char *name; int id; int channel; }; struct rk_calib_entry { uint32_t raw; int temp; }; struct tsadc_calib_info { struct rk_calib_entry *table; int nentries; }; struct tsadc_conf { int version; int q_sel_ntc; int shutdown_temp; int shutdown_mode; int shutdown_pol; struct tsensor *tsensors; int ntsensors; struct tsadc_calib_info calib_info; }; struct tsadc_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; clk_t tsadc_clk; clk_t apb_pclk_clk; hwreset_array_t hwreset; struct syscon *grf; struct tsadc_conf *conf; int shutdown_temp; int shutdown_mode; int shutdown_pol; int alarm_temp; }; static struct rk_calib_entry rk3288_calib_data[] = { {3800, -40000}, {3792, -35000}, {3783, -30000}, {3774, -25000}, {3765, -20000}, {3756, -15000}, {3747, -10000}, {3737, -5000}, {3728, 0}, {3718, 5000}, {3708, 10000}, {3698, 15000}, {3688, 20000}, {3678, 25000}, {3667, 30000}, {3656, 35000}, {3645, 40000}, {3634, 45000}, {3623, 50000}, {3611, 55000}, {3600, 60000}, {3588, 65000}, {3575, 70000}, {3563, 75000}, {3550, 80000}, {3537, 85000}, {3524, 90000}, {3510, 95000}, {3496, 100000}, {3482, 105000}, {3467, 110000}, {3452, 115000}, {3437, 120000}, {3421, 125000}, }; struct tsensor rk3288_tsensors[] = { { .channel = 0, .id = 2, .name = "reserved"}, { .channel = 1, .id = 0, .name = "CPU"}, { .channel = 2, .id = 1, .name = "GPU"}, }; struct tsadc_conf rk3288_tsadc_conf = { .version = TSADC_V2, .q_sel_ntc = 0, .shutdown_temp = 95000, .shutdown_mode = 1, /* GPIO */ .shutdown_pol = 0, /* Low */ .tsensors = rk3288_tsensors, .ntsensors = nitems(rk3288_tsensors), .calib_info = { .table = rk3288_calib_data, .nentries = nitems(rk3288_calib_data), } }; static struct rk_calib_entry rk3328_calib_data[] = { {296, -40000}, {304, -35000}, {313, -30000}, {331, -20000}, {340, -15000}, {349, -10000}, {359, -5000}, {368, 0}, {378, 5000}, {388, 10000}, {398, 15000}, {408, 20000}, {418, 25000}, {429, 30000}, {440, 35000}, {451, 40000}, {462, 45000}, {473, 50000}, {485, 55000}, {496, 60000}, {508, 65000}, {521, 70000}, {533, 75000}, {546, 80000}, {559, 85000}, {572, 90000}, {586, 95000}, {600, 100000}, {614, 105000}, {629, 110000}, {644, 115000}, {659, 120000}, {675, 125000}, }; static struct tsensor rk3328_tsensors[] = { { .channel = 0, .id = 0, .name = "CPU"}, }; static struct tsadc_conf rk3328_tsadc_conf = { .version = TSADC_V2, .q_sel_ntc = 1, .shutdown_temp = 95000, .shutdown_mode = 0, /* CRU */ .shutdown_pol = 0, /* Low */ .tsensors = rk3328_tsensors, .ntsensors = nitems(rk3328_tsensors), .calib_info = { .table = rk3328_calib_data, .nentries = nitems(rk3328_calib_data), } }; static struct rk_calib_entry rk3399_calib_data[] = { {402, -40000}, {410, -35000}, {419, -30000}, {427, -25000}, {436, -20000}, {444, -15000}, {453, -10000}, {461, -5000}, {470, 0}, {478, 5000}, {487, 10000}, {496, 15000}, {504, 20000}, {513, 25000}, {521, 30000}, {530, 35000}, {538, 40000}, {547, 45000}, {555, 50000}, {564, 55000}, {573, 60000}, {581, 65000}, {590, 70000}, {599, 75000}, {607, 80000}, {616, 85000}, {624, 90000}, {633, 95000}, {642, 100000}, {650, 105000}, {659, 110000}, {668, 115000}, {677, 120000}, {685, 125000}, }; static struct tsensor rk3399_tsensors[] = { { .channel = 0, .id = 0, .name = "CPU"}, { .channel = 1, .id = 1, .name = "GPU"}, }; static struct tsadc_conf rk3399_tsadc_conf = { .version = TSADC_V3, .q_sel_ntc = 1, .shutdown_temp = 95000, .shutdown_mode = 1, /* GPIO */ .shutdown_pol = 0, /* Low */ .tsensors = rk3399_tsensors, .ntsensors = nitems(rk3399_tsensors), .calib_info = { .table = rk3399_calib_data, .nentries = nitems(rk3399_calib_data), } }; static struct rk_calib_entry rk3568_calib_data[] = { {0, -40000}, {1584, -40000}, {1620, -35000}, {1652, -30000}, {1688, -25000}, {1720, -20000}, {1756, -15000}, {1788, -10000}, {1824, -5000}, {1856, 0}, {1892, 5000}, {1924, 10000}, {1956, 15000}, {1992, 20000}, {2024, 25000}, {2060, 30000}, {2092, 35000}, {2128, 40000}, {2160, 45000}, {2196, 50000}, {2228, 55000}, {2264, 60000}, {2300, 65000}, {2332, 70000}, {2368, 75000}, {2400, 80000}, {2436, 85000}, {2468, 90000}, {2500, 95000}, {2536, 100000}, {2572, 105000}, {2604, 110000}, {2636, 115000}, {2672, 120000}, {2704, 125000}, }; static struct tsensor rk3568_tsensors[] = { { .channel = 0, .id = 0, .name = "CPU"}, { .channel = 1, .id = 1, .name = "GPU"}, }; static struct tsadc_conf rk3568_tsadc_conf = { .version = TSADC_V7, .q_sel_ntc = 1, .shutdown_temp = 95000, .shutdown_mode = 1, /* GPIO */ .shutdown_pol = 0, /* Low */ .tsensors = rk3568_tsensors, .ntsensors = nitems(rk3568_tsensors), .calib_info = { .table = rk3568_calib_data, .nentries = nitems(rk3568_calib_data), } }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-tsadc", (uintptr_t)&rk3288_tsadc_conf}, {"rockchip,rk3328-tsadc", (uintptr_t)&rk3328_tsadc_conf}, {"rockchip,rk3399-tsadc", (uintptr_t)&rk3399_tsadc_conf}, {"rockchip,rk3568-tsadc", (uintptr_t)&rk3568_tsadc_conf}, {NULL, 0} }; static uint32_t tsadc_temp_to_raw(struct tsadc_softc *sc, int temp) { struct rk_calib_entry *tbl; int denom, ntbl, raw, i; tbl = sc->conf->calib_info.table; ntbl = sc->conf->calib_info.nentries; if (temp <= tbl[0].temp) return (tbl[0].raw); if (temp >= tbl[ntbl - 1].temp) return (tbl[ntbl - 1].raw); for (i = 1; i < (ntbl - 1); i++) { /* Exact match */ if (temp == tbl[i].temp) return (tbl[i].raw); if (temp < tbl[i].temp) break; } /* * Translated value is between i and i - 1 table entries. * Do linear interpolation for it. */ raw = (int)tbl[i - 1].raw - (int)tbl[i].raw; raw *= temp - tbl[i - 1].temp; denom = tbl[i - 1].temp - tbl[i].temp; raw = tbl[i - 1].raw + raw / denom; return (raw); } static int tsadc_raw_to_temp(struct tsadc_softc *sc, uint32_t raw) { struct rk_calib_entry *tbl; int denom, ntbl, temp, i; bool descending; tbl = sc->conf->calib_info.table; ntbl = sc->conf->calib_info.nentries; descending = tbl[0].raw > tbl[1].raw; if (descending) { /* Raw column is in descending order. */ if (raw >= tbl[0].raw) return (tbl[0].temp); if (raw <= tbl[ntbl - 1].raw) return (tbl[ntbl - 1].temp); for (i = ntbl - 2; i > 0; i--) { /* Exact match */ if (raw == tbl[i].raw) return (tbl[i].temp); if (raw < tbl[i].raw) break; } } else { /* Raw column is in ascending order. */ if (raw <= tbl[0].raw) return (tbl[0].temp); if (raw >= tbl[ntbl - 1].raw) return (tbl[ntbl - 1].temp); for (i = 1; i < (ntbl - 1); i++) { /* Exact match */ if (raw == tbl[i].raw) return (tbl[i].temp); if (raw < tbl[i].raw) break; } } /* * Translated value is between i and i - 1 table entries. * Do linear interpolation for it. */ temp = (int)tbl[i - 1].temp - (int)tbl[i].temp; temp *= raw - tbl[i - 1].raw; denom = tbl[i - 1].raw - tbl[i].raw; temp = tbl[i - 1].temp + temp / denom; return (temp); } static void tsadc_init_tsensor(struct tsadc_softc *sc, struct tsensor *sensor) { uint32_t val; /* Shutdown mode */ val = RD4(sc, TSADC_INT_EN); if (sc->shutdown_mode != 0) { /* Signal shutdown of GPIO pin */ val &= ~TSADC_INT_EN_2CRU_EN_SRC(sensor->channel); val |= TSADC_INT_EN_2GPIO_EN_SRC(sensor->channel); } else { val |= TSADC_INT_EN_2CRU_EN_SRC(sensor->channel); val &= ~TSADC_INT_EN_2GPIO_EN_SRC(sensor->channel); } WR4(sc, TSADC_INT_EN, val); /* Shutdown temperature */ val = tsadc_raw_to_temp(sc, sc->shutdown_temp); WR4(sc, TSADC_COMP_SHUT(sensor->channel), val); val = RD4(sc, TSADC_AUTO_CON); val |= TSADC_AUTO_SRC_EN(sensor->channel); WR4(sc, TSADC_AUTO_CON, val); /* Alarm temperature */ val = tsadc_temp_to_raw(sc, sc->alarm_temp); WR4(sc, TSADC_COMP_INT(sensor->channel), val); val = RD4(sc, TSADC_INT_EN); val |= TSADC_COMP_INT_SRC_EN(sensor->channel); WR4(sc, TSADC_INT_EN, val); } static void tsadc_init(struct tsadc_softc *sc) { uint32_t val; /* Common part */ val = 0; /* XXX Is this right? */ if (sc->shutdown_pol != 0) val |= TSADC_AUTO_CON_POL_HI; else val &= ~TSADC_AUTO_CON_POL_HI; if (sc->conf->q_sel_ntc) val |= TSADC_AUTO_Q_SEL; WR4(sc, TSADC_AUTO_CON, val); switch (sc->conf->version) { case TSADC_V2: /* V2 init */ WR4(sc, TSADC_AUTO_PERIOD, 250); /* 250 ms */ WR4(sc, TSADC_AUTO_PERIOD_HT, 50); /* 50 ms */ WR4(sc, TSADC_HIGHT_INT_DEBOUNCE, 4); WR4(sc, TSADC_HIGHT_TSHUT_DEBOUNCE, 4); break; case TSADC_V3: /* V3 init */ if (sc->grf == NULL) { /* Errata: adjust interleave to working value */ WR4(sc, TSADC_USER_CON, 13 << 6); /* 13 clks */ } else { SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L); SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H); DELAY(30); /* 15 usec min */ SYSCON_WRITE_4(sc->grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON); SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON); DELAY(180); /* 90 usec min */ } WR4(sc, TSADC_AUTO_PERIOD, 1875); /* 2.5 ms */ WR4(sc, TSADC_AUTO_PERIOD_HT, 1875); /* 2.5 ms */ WR4(sc, TSADC_HIGHT_INT_DEBOUNCE, 4); WR4(sc, TSADC_HIGHT_TSHUT_DEBOUNCE, 4); break; case TSADC_V7: /* V7 init */ WR4(sc, TSADC_USER_CON, 0xfc0); /* 97us, at least 90us */ WR4(sc, TSADC_AUTO_PERIOD, 1622); /* 2.5ms */ WR4(sc, TSADC_HIGHT_INT_DEBOUNCE, 4); WR4(sc, TSADC_AUTO_PERIOD_HT, 1622); /* 2.5ms */ WR4(sc, TSADC_HIGHT_TSHUT_DEBOUNCE, 4); if (sc->grf) { SYSCON_WRITE_4(sc->grf, GRF_TSADC_CON, GRF_TSADC_TSEN); DELAY(15); /* 10 usec min */ SYSCON_WRITE_4(sc->grf, GRF_TSADC_CON, GRF_TSADC_ANA_REG0); SYSCON_WRITE_4(sc->grf, GRF_TSADC_CON, GRF_TSADC_ANA_REG1); SYSCON_WRITE_4(sc->grf, GRF_TSADC_CON, GRF_TSADC_ANA_REG2); DELAY(100); /* 90 usec min */ } break; } } static int tsadc_read_temp(struct tsadc_softc *sc, struct tsensor *sensor, int *temp) { uint32_t val; val = RD4(sc, TSADC_DATA(sensor->channel)); *temp = tsadc_raw_to_temp(sc, val); #ifdef DEBUG device_printf(sc->dev, "%s: Sensor(id: %d, ch: %d), val: %d temp: %d\n", __func__, sensor->id, sensor->channel, val, *temp); device_printf(sc->dev, "%s: user_con=0x%08x auto_con=0x%08x " "comp_int=0x%08x comp_shut=0x%08x\n", __func__, RD4(sc, TSADC_USER_CON), RD4(sc, TSADC_AUTO_CON), RD4(sc, TSADC_COMP_INT(sensor->channel)), RD4(sc, TSADC_COMP_SHUT(sensor->channel))); #endif return (0); } static int tsadc_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) { struct tsadc_softc *sc; int i, rv; sc = device_get_softc(dev); if (id >= sc->conf->ntsensors) return (ERANGE); for (i = 0; i < sc->conf->ntsensors; i++) { if (sc->conf->tsensors->id == id) { rv =tsadc_read_temp(sc, sc->conf->tsensors + id, val); return (rv); } } return (ERANGE); } static int tsadc_sysctl_temperature(SYSCTL_HANDLER_ARGS) { struct tsadc_softc *sc; int val; int rv; int id; /* Write request */ if (req->newptr != NULL) return (EINVAL); sc = arg1; id = arg2; if (id >= sc->conf->ntsensors) return (ERANGE); rv = tsadc_read_temp(sc, sc->conf->tsensors + id, &val); if (rv != 0) return (rv); val = val / 100; val += 2731; rv = sysctl_handle_int(oidp, &val, 0, req); return (rv); } static int tsadc_init_sysctl(struct tsadc_softc *sc) { int i; struct sysctl_oid *oid, *tmp; sysctl_ctx_init(&tsadc_sysctl_ctx); /* create node for hw.temp */ oid = SYSCTL_ADD_NODE(&tsadc_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, ""); if (oid == NULL) return (ENXIO); /* Add sensors */ for (i = sc->conf->ntsensors - 1; i >= 0; i--) { tmp = SYSCTL_ADD_PROC(&tsadc_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, sc->conf->tsensors[i].name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i, tsadc_sysctl_temperature, "IK", "SoC Temperature"); if (tmp == NULL) return (ENXIO); } return (0); } static int tsadc_intr(void *arg) { struct tsadc_softc *sc; uint32_t val; sc = (struct tsadc_softc *)arg; val = RD4(sc, TSADC_INT_PD); WR4(sc, TSADC_INT_PD, val); /* XXX Handle shutdown and alarm interrupts. */ if (val & 0x00F0) { device_printf(sc->dev, "Alarm: device temperature " "is above of shutdown level.\n"); } else if (val & 0x000F) { device_printf(sc->dev, "Alarm: device temperature " "is above of alarm level.\n"); } return (FILTER_HANDLED); } static int tsadc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip temperature sensors"); return (BUS_PROBE_DEFAULT); } static int tsadc_attach(device_t dev) { struct tsadc_softc *sc; phandle_t node; uint32_t val; int i, rid, rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); sc->conf = (struct tsadc_conf *) ofw_bus_search_compatible(dev, compat_data)->ocd_data; sc->alarm_temp = 90000; rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, tsadc_intr, NULL, sc, &sc->irq_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); goto fail; } /* FDT resources */ rv = hwreset_array_get_ofw(dev, 0, &sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot get resets\n"); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "tsadc", &sc->tsadc_clk); if (rv != 0) { device_printf(dev, "Cannot get 'tsadc' clock: %d\n", rv); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "apb_pclk", &sc->apb_pclk_clk); if (rv != 0) { device_printf(dev, "Cannot get 'apb_pclk' clock: %d\n", rv); goto fail; } /* grf is optional */ rv = syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf); if (rv != 0 && rv != ENOENT) { device_printf(dev, "Cannot get 'grf' syscon: %d\n", rv); goto fail; } rv = OF_getencprop(node, "rockchip,hw-tshut-temp", &sc->shutdown_temp, sizeof(sc->shutdown_temp)); if (rv <= 0) sc->shutdown_temp = sc->conf->shutdown_temp; rv = OF_getencprop(node, "rockchip,hw-tshut-mode", &sc->shutdown_mode, sizeof(sc->shutdown_mode)); if (rv <= 0) sc->shutdown_mode = sc->conf->shutdown_mode; rv = OF_getencprop(node, "rockchip,hw-tshut-polarity", &sc->shutdown_pol, sizeof(sc->shutdown_pol)); if (rv <= 0) sc->shutdown_pol = sc->conf->shutdown_pol; /* Wakeup controller */ rv = hwreset_array_assert(sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot assert reset\n"); goto fail; } /* Set the assigned clocks parent and freq */ rv = clk_set_assigned(sc->dev, node); if (rv != 0 && rv != ENOENT) { device_printf(dev, "clk_set_assigned failed\n"); goto fail; } rv = clk_enable(sc->tsadc_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'tsadc_clk' clock: %d\n", rv); goto fail; } rv = clk_enable(sc->apb_pclk_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'apb_pclk' clock: %d\n", rv); goto fail; } rv = hwreset_array_deassert(sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot deassert reset\n"); goto fail; } tsadc_init(sc); for (i = 0; i < sc->conf->ntsensors; i++) tsadc_init_tsensor(sc, sc->conf->tsensors + i); /* Enable auto mode */ val = RD4(sc, TSADC_AUTO_CON); val |= TSADC_AUTO_CON_AUTO; WR4(sc, TSADC_AUTO_CON, val); rv = tsadc_init_sysctl(sc); if (rv != 0) { device_printf(sc->dev, "Cannot initialize sysctls\n"); goto fail_sysctl; } OF_device_register_xref(OF_xref_from_node(node), dev); return (bus_generic_attach(dev)); fail_sysctl: sysctl_ctx_free(&tsadc_sysctl_ctx); fail: if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->tsadc_clk != NULL) clk_release(sc->tsadc_clk); if (sc->apb_pclk_clk != NULL) clk_release(sc->apb_pclk_clk); if (sc->hwreset != NULL) hwreset_array_release(sc->hwreset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static int tsadc_detach(device_t dev) { struct tsadc_softc *sc; sc = device_get_softc(dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&tsadc_sysctl_ctx); if (sc->tsadc_clk != NULL) clk_release(sc->tsadc_clk); if (sc->apb_pclk_clk != NULL) clk_release(sc->apb_pclk_clk); if (sc->hwreset != NULL) hwreset_array_release(sc->hwreset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static device_method_t rk_tsadc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tsadc_probe), DEVMETHOD(device_attach, tsadc_attach), DEVMETHOD(device_detach, tsadc_detach), /* TSADC interface */ DEVMETHOD(rk_tsadc_get_temperature, tsadc_get_temp), DEVMETHOD_END }; static DEFINE_CLASS_0(rk_tsadc, rk_tsadc_driver, rk_tsadc_methods, sizeof(struct tsadc_softc)); EARLY_DRIVER_MODULE(rk_tsadc, simplebus, rk_tsadc_driver, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); diff --git a/sys/arm64/rockchip/rk_typec_phy.c b/sys/arm64/rockchip/rk_typec_phy.c index 6e75394377e4..7f49da5e2208 100644 --- a/sys/arm64/rockchip/rk_typec_phy.c +++ b/sys/arm64/rockchip/rk_typec_phy.c @@ -1,470 +1,470 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Emmanuel Vadot * * 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. */ /* * Rockchip PHY TYPEC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "syscon_if.h" #define GRF_USB3OTG_BASE(x) (0x2430 + (0x10 * x)) #define GRF_USB3OTG_CON0(x) (GRF_USB3OTG_BASE(x) + 0x0) #define GRF_USB3OTG_CON1(x) (GRF_USB3OTG_BASE(x) + 0x4) #define USB3OTG_CON1_U3_DIS (1 << 0) #define GRF_USB3PHY_BASE(x) (0x0e580 + (0xc * (x))) #define GRF_USB3PHY_CON0(x) (GRF_USB3PHY_BASE(x) + 0x0) #define USB3PHY_CON0_USB2_ONLY (1 << 3) #define GRF_USB3PHY_CON1(x) (GRF_USB3PHY_BASE(x) + 0x4) #define GRF_USB3PHY_CON2(x) (GRF_USB3PHY_BASE(x) + 0x8) #define GRF_USB3PHY_STATUS0 0x0e5c0 #define GRF_USB3PHY_STATUS1 0x0e5c4 #define CMN_PLL0_VCOCAL_INIT (0x84 << 2) #define CMN_PLL0_VCOCAL_ITER (0x85 << 2) #define CMN_PLL0_INTDIV (0x94 << 2) #define CMN_PLL0_FRACDIV (0x95 << 2) #define CMN_PLL0_HIGH_THR (0x96 << 2) #define CMN_PLL0_DSM_DIAG (0x97 << 2) #define CMN_PLL0_SS_CTRL1 (0x98 << 2) #define CMN_PLL0_SS_CTRL2 (0x99 << 2) #define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) #define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) #define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) #define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2) #define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2) #define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2) #define CMN_DIAG_HSCLK_SEL (0x1e0 << 2) #define CMN_DIAG_HSCLK_SEL_PLL_CONFIG 0x30 #define CMN_DIAG_HSCLK_SEL_PLL_MASK 0x33 #define TX_TXCC_MGNFS_MULT_000(lane) ((0x4050 | ((lane) << 9)) << 2) #define XCVR_DIAG_BIDI_CTRL(lane) ((0x40e8 | ((lane) << 9)) << 2) #define XCVR_DIAG_LANE_FCM_EN_MGN(lane) ((0x40f2 | ((lane) << 9)) << 2) #define TX_PSC_A0(lane) ((0x4100 | ((lane) << 9)) << 2) #define TX_PSC_A1(lane) ((0x4101 | ((lane) << 9)) << 2) #define TX_PSC_A2(lane) ((0x4102 | ((lane) << 9)) << 2) #define TX_PSC_A3(lane) ((0x4103 | ((lane) << 9)) << 2) #define TX_RCVDET_EN_TMR(lane) ((0x4122 | ((lane) << 9)) << 2) #define TX_RCVDET_ST_TMR(lane) ((0x4123 | ((lane) << 9)) << 2) #define RX_PSC_A0(lane) ((0x8000 | ((lane) << 9)) << 2) #define RX_PSC_A1(lane) ((0x8001 | ((lane) << 9)) << 2) #define RX_PSC_A2(lane) ((0x8002 | ((lane) << 9)) << 2) #define RX_PSC_A3(lane) ((0x8003 | ((lane) << 9)) << 2) #define RX_PSC_CAL(lane) ((0x8006 | ((lane) << 9)) << 2) #define RX_PSC_RDY(lane) ((0x8007 | ((lane) << 9)) << 2) #define RX_SIGDET_HL_FILT_TMR(lane) ((0x8090 | ((lane) << 9)) << 2) #define RX_REE_CTRL_DATA_MASK(lane) ((0x81bb | ((lane) << 9)) << 2) #define RX_DIAG_SIGDET_TUNE(lane) ((0x81dc | ((lane) << 9)) << 2) #define PMA_LANE_CFG (0xc000 << 2) #define PIN_ASSIGN_D_F 0x5100 #define DP_MODE_CTL (0xc008 << 2) #define DP_MODE_ENTER_A2 0xc104 #define PMA_CMN_CTRL1 (0xc800 << 2) #define PMA_CMN_CTRL1_READY (1 << 0) static struct ofw_compat_data compat_data[] = { { "rockchip,rk3399-typec-phy", 1 }, { NULL, 0 } }; static struct resource_spec rk_typec_phy_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; struct rk_typec_phy_softc { device_t dev; struct resource *res; struct syscon *grf; clk_t tcpdcore; clk_t tcpdphy_ref; hwreset_t rst_uphy; hwreset_t rst_pipe; hwreset_t rst_tcphy; int mode; int phy_ctrl_id; }; #define RK_TYPEC_PHY_READ(sc, reg) bus_read_4(sc->res, (reg)) #define RK_TYPEC_PHY_WRITE(sc, reg, val) bus_write_4(sc->res, (reg), (val)) /* Phy class and methods. */ static int rk_typec_phy_enable(struct phynode *phynode, bool enable); static int rk_typec_phy_get_mode(struct phynode *phy, int *mode); static int rk_typec_phy_set_mode(struct phynode *phy, int mode); static phynode_method_t rk_typec_phy_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, rk_typec_phy_enable), PHYNODEMETHOD(phynode_usb_get_mode, rk_typec_phy_get_mode), PHYNODEMETHOD(phynode_usb_set_mode, rk_typec_phy_set_mode), PHYNODEMETHOD_END }; DEFINE_CLASS_1(rk_typec_phy_phynode, rk_typec_phy_phynode_class, rk_typec_phy_phynode_methods, sizeof(struct phynode_usb_sc), phynode_usb_class); enum RK3399_USBPHY { RK3399_TYPEC_PHY_DP = 0, RK3399_TYPEC_PHY_USB3, }; static void rk_typec_phy_set_usb2_only(struct rk_typec_phy_softc *sc, bool usb2only) { uint32_t reg; /* Disable usb3tousb2 only */ reg = SYSCON_READ_4(sc->grf, GRF_USB3PHY_CON0(sc->phy_ctrl_id)); if (usb2only) reg |= USB3PHY_CON0_USB2_ONLY; else reg &= ~USB3PHY_CON0_USB2_ONLY; /* Write Mask */ reg |= (USB3PHY_CON0_USB2_ONLY) << 16; SYSCON_WRITE_4(sc->grf, GRF_USB3PHY_CON0(sc->phy_ctrl_id), reg); /* Enable the USB3 Super Speed port */ reg = SYSCON_READ_4(sc->grf, GRF_USB3OTG_CON1(sc->phy_ctrl_id)); if (usb2only) reg |= USB3OTG_CON1_U3_DIS; else reg &= ~USB3OTG_CON1_U3_DIS; /* Write Mask */ reg |= (USB3OTG_CON1_U3_DIS) << 16; SYSCON_WRITE_4(sc->grf, GRF_USB3OTG_CON1(sc->phy_ctrl_id), reg); } static int rk_typec_phy_enable(struct phynode *phynode, bool enable) { struct rk_typec_phy_softc *sc; device_t dev; intptr_t phy; uint32_t reg; int err, retry; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (phy != RK3399_TYPEC_PHY_USB3) return (ERANGE); rk_typec_phy_set_usb2_only(sc, false); err = clk_enable(sc->tcpdcore); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->tcpdcore)); return (ENXIO); } err = clk_enable(sc->tcpdphy_ref); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->tcpdphy_ref)); clk_disable(sc->tcpdcore); return (ENXIO); } hwreset_deassert(sc->rst_tcphy); /* 24M configuration, magic values from rockchip */ RK_TYPEC_PHY_WRITE(sc, PMA_CMN_CTRL1, 0x830); for (int i = 0; i < 4; i++) { RK_TYPEC_PHY_WRITE(sc, XCVR_DIAG_LANE_FCM_EN_MGN(i), 0x90); RK_TYPEC_PHY_WRITE(sc, TX_RCVDET_EN_TMR(i), 0x960); RK_TYPEC_PHY_WRITE(sc, TX_RCVDET_ST_TMR(i), 0x30); } reg = RK_TYPEC_PHY_READ(sc, CMN_DIAG_HSCLK_SEL); reg &= ~CMN_DIAG_HSCLK_SEL_PLL_MASK; reg |= CMN_DIAG_HSCLK_SEL_PLL_CONFIG; RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_HSCLK_SEL, reg); /* PLL configuration, magic values from rockchip */ RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_VCOCAL_INIT, 0xf0); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_VCOCAL_ITER, 0x18); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_INTDIV, 0xd0); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_FRACDIV, 0x4a4a); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_HIGH_THR, 0x34); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_SS_CTRL1, 0x1ee); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_SS_CTRL2, 0x7f03); RK_TYPEC_PHY_WRITE(sc, CMN_PLL0_DSM_DIAG, 0x20); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_OVRD, 0); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_FBH_OVRD, 0); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_FBL_OVRD, 0); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_V2I_TUNE, 0x7); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_CP_TUNE, 0x45); RK_TYPEC_PHY_WRITE(sc, CMN_DIAG_PLL0_LF_PROG, 0x8); /* Configure the TX and RX line, magic values from rockchip */ RK_TYPEC_PHY_WRITE(sc, TX_PSC_A0(0), 0x7799); RK_TYPEC_PHY_WRITE(sc, TX_PSC_A1(0), 0x7798); RK_TYPEC_PHY_WRITE(sc, TX_PSC_A2(0), 0x5098); RK_TYPEC_PHY_WRITE(sc, TX_PSC_A3(0), 0x5098); RK_TYPEC_PHY_WRITE(sc, TX_TXCC_MGNFS_MULT_000(0), 0x0); RK_TYPEC_PHY_WRITE(sc, XCVR_DIAG_BIDI_CTRL(0), 0xbf); RK_TYPEC_PHY_WRITE(sc, RX_PSC_A0(1), 0xa6fd); RK_TYPEC_PHY_WRITE(sc, RX_PSC_A1(1), 0xa6fd); RK_TYPEC_PHY_WRITE(sc, RX_PSC_A2(1), 0xa410); RK_TYPEC_PHY_WRITE(sc, RX_PSC_A3(1), 0x2410); RK_TYPEC_PHY_WRITE(sc, RX_PSC_CAL(1), 0x23ff); RK_TYPEC_PHY_WRITE(sc, RX_SIGDET_HL_FILT_TMR(1), 0x13); RK_TYPEC_PHY_WRITE(sc, RX_REE_CTRL_DATA_MASK(1), 0x03e7); RK_TYPEC_PHY_WRITE(sc, RX_DIAG_SIGDET_TUNE(1), 0x1004); RK_TYPEC_PHY_WRITE(sc, RX_PSC_RDY(1), 0x2010); RK_TYPEC_PHY_WRITE(sc, XCVR_DIAG_BIDI_CTRL(1), 0xfb); RK_TYPEC_PHY_WRITE(sc, PMA_LANE_CFG, PIN_ASSIGN_D_F); RK_TYPEC_PHY_WRITE(sc, DP_MODE_CTL, DP_MODE_ENTER_A2); hwreset_deassert(sc->rst_uphy); for (retry = 10000; retry > 0; retry--) { reg = RK_TYPEC_PHY_READ(sc, PMA_CMN_CTRL1); if (reg & PMA_CMN_CTRL1_READY) break; DELAY(10); } if (retry == 0) { device_printf(sc->dev, "Timeout waiting for PMA\n"); return (ENXIO); } hwreset_deassert(sc->rst_pipe); return (0); } static int rk_typec_phy_get_mode(struct phynode *phynode, int *mode) { struct rk_typec_phy_softc *sc; intptr_t phy; device_t dev; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (phy != RK3399_TYPEC_PHY_USB3) return (ERANGE); *mode = sc->mode; return (0); } static int rk_typec_phy_set_mode(struct phynode *phynode, int mode) { struct rk_typec_phy_softc *sc; intptr_t phy; device_t dev; dev = phynode_get_device(phynode); phy = phynode_get_id(phynode); sc = device_get_softc(dev); if (phy != RK3399_TYPEC_PHY_USB3) return (ERANGE); sc->mode = mode; return (0); } static int rk_typec_phy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Rockchip RK3399 PHY TYPEC"); return (BUS_PROBE_DEFAULT); } static int rk_typec_phy_attach(device_t dev) { struct rk_typec_phy_softc *sc; struct phynode_init_def phy_init; struct phynode *phynode; phandle_t node, usb3; phandle_t reg_prop[4]; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); /* * Find out which phy we are. * There is not property for this so we need to know the * address to use the correct GRF registers. */ if (OF_getencprop(node, "reg", reg_prop, sizeof(reg_prop)) <= 0) { device_printf(dev, "Cannot guess phy controller id\n"); return (ENXIO); } switch (reg_prop[1]) { case 0xff7c0000: sc->phy_ctrl_id = 0; break; case 0xff800000: sc->phy_ctrl_id = 1; break; default: device_printf(dev, "Unknown address %x for typec-phy\n", reg_prop[1]); return (ENXIO); } if (bus_alloc_resources(dev, rk_typec_phy_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); goto fail; } if (syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "Cannot get syscon handle\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "tcpdcore", &sc->tcpdcore) != 0) { device_printf(dev, "Cannot get tcpdcore clock\n"); goto fail; } if (clk_get_by_ofw_name(dev, 0, "tcpdphy-ref", &sc->tcpdphy_ref) != 0) { device_printf(dev, "Cannot get tcpdphy-ref clock\n"); goto fail; } if (hwreset_get_by_ofw_name(dev, 0, "uphy", &sc->rst_uphy) != 0) { device_printf(dev, "Cannot get uphy reset\n"); goto fail; } if (hwreset_get_by_ofw_name(dev, 0, "uphy-pipe", &sc->rst_pipe) != 0) { device_printf(dev, "Cannot get uphy-pipe reset\n"); goto fail; } if (hwreset_get_by_ofw_name(dev, 0, "uphy-tcphy", &sc->rst_tcphy) != 0) { device_printf(dev, "Cannot get uphy-tcphy reset\n"); goto fail; } /* * Make sure that the module is asserted * We need to deassert in a certain order when we enable the phy */ hwreset_assert(sc->rst_uphy); hwreset_assert(sc->rst_pipe); hwreset_assert(sc->rst_tcphy); /* Set the assigned clocks parent and freq */ if (clk_set_assigned(dev, node) != 0) { device_printf(dev, "clk_set_assigned failed\n"); goto fail; } /* Only usb3 port is supported right now */ usb3 = ofw_bus_find_child(node, "usb3-port"); if (usb3 == 0) { device_printf(dev, "Cannot find usb3-port child node\n"); goto fail; } /* If the child isn't enable attach the driver * but do not register the PHY. */ if (!ofw_bus_node_status_okay(usb3)) return (0); phy_init.id = RK3399_TYPEC_PHY_USB3; phy_init.ofw_node = usb3; phynode = phynode_create(dev, &rk_typec_phy_phynode_class, &phy_init); if (phynode == NULL) { device_printf(dev, "failed to create phy usb3-port\n"); goto fail; } if (phynode_register(phynode) == NULL) { device_printf(dev, "failed to register phy usb3-port\n"); goto fail; } OF_device_register_xref(OF_xref_from_node(usb3), dev); return (0); fail: bus_release_resources(dev, rk_typec_phy_spec, &sc->res); return (ENXIO); } static device_method_t rk_typec_phy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_typec_phy_probe), DEVMETHOD(device_attach, rk_typec_phy_attach), DEVMETHOD_END }; static driver_t rk_typec_phy_driver = { "rk_typec_phy", rk_typec_phy_methods, sizeof(struct rk_typec_phy_softc) }; EARLY_DRIVER_MODULE(rk_typec_phy, simplebus, rk_typec_phy_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(rk_typec_phy, 1); diff --git a/sys/arm64/rockchip/rk_usbphy.c b/sys/arm64/rockchip/rk_usbphy.c index 6f9001470df8..46859d1442e3 100644 --- a/sys/arm64/rockchip/rk_usbphy.c +++ b/sys/arm64/rockchip/rk_usbphy.c @@ -1,300 +1,300 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Michal Meloun * * 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 +#include #include #include #include #include #include #include #include #include "phynode_if.h" #include "phynode_usb_if.h" #include "syscon_if.h" /* Phy registers */ #define UOC_CON0 0x00 #define UOC_CON0_SIDDQ (1 << 13) #define UOC_CON0_DISABLE (1 << 4) #define UOC_CON0_COMMON_ON_N (1 << 0) #define UOC_CON2 0x08 #define UOC_CON2_SOFT_CON_SEL (1 << 2) #define UOC_CON3 0x0c #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-usb-phy", 1}, {NULL, 0}, }; struct rk_usbphy_softc { device_t dev; }; struct rk_phynode_sc { struct phynode_usb_sc usb_sc; uint32_t base; int mode; clk_t clk; hwreset_t hwreset; regulator_t supply_vbus; struct syscon *syscon; }; static int rk_phynode_phy_enable(struct phynode *phy, bool enable) { struct rk_phynode_sc *sc; int rv; sc = phynode_get_softc(phy); rv = SYSCON_MODIFY_4(sc->syscon, sc->base + UOC_CON0, UOC_CON0_SIDDQ << 16 | UOC_CON0_SIDDQ, enable ? 0 : UOC_CON0_SIDDQ); return (rv); } static int rk_phynode_get_mode(struct phynode *phynode, int *mode) { struct rk_phynode_sc *sc; sc = phynode_get_softc(phynode); *mode = sc->mode; return (0); } static int rk_phynode_set_mode(struct phynode *phynode, int mode) { struct rk_phynode_sc *sc; sc = phynode_get_softc(phynode); sc->mode = mode; return (0); } /* Phy controller class and methods. */ static phynode_method_t rk_phynode_methods[] = { PHYNODEUSBMETHOD(phynode_enable, rk_phynode_phy_enable), PHYNODEMETHOD(phynode_usb_get_mode, rk_phynode_get_mode), PHYNODEMETHOD(phynode_usb_set_mode, rk_phynode_set_mode), PHYNODEUSBMETHOD_END }; DEFINE_CLASS_1(rk_phynode, rk_phynode_class, rk_phynode_methods, sizeof(struct rk_phynode_sc), phynode_usb_class); static int rk_usbphy_init_phy(struct rk_usbphy_softc *sc, phandle_t node) { struct phynode *phynode; struct phynode_init_def phy_init; struct rk_phynode_sc *phy_sc; int rv; uint32_t base; clk_t clk; hwreset_t hwreset; regulator_t supply_vbus; struct syscon *syscon; clk = NULL; hwreset = NULL; supply_vbus = NULL; rv = OF_getencprop(node, "reg", &base, sizeof(base)); if (rv <= 0) { device_printf(sc->dev, "cannot get 'reg' property.\n"); goto fail; } /* FDT resources. All are optional. */ rv = clk_get_by_ofw_name(sc->dev, node, "phyclk", &clk); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "cannot get 'phyclk' clock.\n"); goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, node, "phy-reset", &hwreset); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'phy-reset' reset\n"); goto fail; } rv = regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", &supply_vbus); if (rv != 0 && rv != ENOENT) { device_printf(sc->dev, "Cannot get 'vbus' regulator.\n"); goto fail; } rv = SYSCON_GET_HANDLE(sc->dev, &syscon); if (rv != 0) { device_printf(sc->dev, "Cannot get parent syscon\n"); goto fail; } /* Init HW resources */ if (hwreset != NULL) { rv = hwreset_assert(hwreset); if (rv != 0) { device_printf(sc->dev, "Cannot assert reset\n"); goto fail; } } if (clk != NULL) { rv = clk_enable(clk); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'phyclk' clock.\n"); goto fail; } } if (hwreset != NULL) { rv = hwreset_deassert(hwreset); if (rv != 0) { device_printf(sc->dev, "Cannot deassert reset\n"); goto fail; } } /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = 1; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &rk_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy.\n"); return (ENXIO); } phy_sc = phynode_get_softc(phynode); phy_sc->base = base; phy_sc->clk = clk; phy_sc->hwreset = hwreset; phy_sc->supply_vbus = supply_vbus; phy_sc->syscon = syscon; if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot register phy.\n"); return (ENXIO); } /* XXX It breaks boot */ /* rk_phynode_phy_enable(phynode, 1); */ return (0); fail: if (supply_vbus != NULL) regulator_release(supply_vbus); if (clk != NULL) clk_release(clk); if (hwreset != NULL) hwreset_release(hwreset); return (ENXIO); } static int rk_usbphy_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip USB Phy"); return (BUS_PROBE_DEFAULT); } static int rk_usbphy_attach(device_t dev) { struct rk_usbphy_softc *sc; phandle_t node, child; int rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); /* Attach child devices */ for (child = OF_child(node); child > 0; child = OF_peer(child)) { rv = rk_usbphy_init_phy(sc, child); if (rv != 0) goto fail; } return (bus_generic_attach(dev)); fail: return (ENXIO); } static int rk_usbphy_detach(device_t dev) { return (0); } static device_method_t rk_usbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_usbphy_probe), DEVMETHOD(device_attach, rk_usbphy_attach), DEVMETHOD(device_detach, rk_usbphy_detach), DEVMETHOD_END }; static DEFINE_CLASS_0(rk_usbphy, rk_usbphy_driver, rk_usbphy_methods, sizeof(struct rk_usbphy_softc)); EARLY_DRIVER_MODULE(rk_usbphy, simplebus, rk_usbphy_driver, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); diff --git a/sys/conf/files b/sys/conf/files index 8b28d6428584..95228194fc5a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5240 +1,5240 @@ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # acpi_quirks.h optional acpi \ dependency "$S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ compile-with "${AWK} -f $S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ no-obj no-implicit-rule before-depend \ clean "acpi_quirks.h" bhnd_nvram_map.h optional bhnd \ dependency "$S/dev/bhnd/tools/nvram_map_gen.sh $S/dev/bhnd/tools/nvram_map_gen.awk $S/dev/bhnd/nvram/nvram_map" \ compile-with "sh $S/dev/bhnd/tools/nvram_map_gen.sh $S/dev/bhnd/nvram/nvram_map -h" \ no-obj no-implicit-rule before-depend \ clean "bhnd_nvram_map.h" bhnd_nvram_map_data.h optional bhnd \ dependency "$S/dev/bhnd/tools/nvram_map_gen.sh $S/dev/bhnd/tools/nvram_map_gen.awk $S/dev/bhnd/nvram/nvram_map" \ compile-with "sh $S/dev/bhnd/tools/nvram_map_gen.sh $S/dev/bhnd/nvram/nvram_map -d" \ no-obj no-implicit-rule before-depend \ clean "bhnd_nvram_map_data.h" fdt_static_dtb.h optional fdt fdt_dtb_static \ compile-with "sh -c 'MACHINE=${MACHINE} $S/tools/fdt/make_dtbh.sh ${FDT_DTS_FILE} ${.CURDIR}'" \ dependency "${FDT_DTS_FILE:T:R}.dtb" \ no-obj no-implicit-rule before-depend \ clean "fdt_static_dtb.h" feeder_eq_gen.h optional sound \ dependency "$S/tools/sound/feeder_eq_mkfilter.awk" \ compile-with "${AWK} -f $S/tools/sound/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > feeder_eq_gen.h" \ no-obj no-implicit-rule before-depend \ clean "feeder_eq_gen.h" feeder_rate_gen.h optional sound \ dependency "$S/tools/sound/feeder_rate_mkfilter.awk" \ compile-with "${AWK} -f $S/tools/sound/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > feeder_rate_gen.h" \ no-obj no-implicit-rule before-depend \ clean "feeder_rate_gen.h" font.h optional sc_dflt_font \ compile-with "uudecode < ${SRCTOP}/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < ${SRCTOP}/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < ${SRCTOP}/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h" \ no-obj no-implicit-rule before-depend \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" snd_fxdiv_gen.h optional sound \ dependency "$S/tools/sound/snd_fxdiv_gen.awk" \ compile-with "${AWK} -f $S/tools/sound/snd_fxdiv_gen.awk -- > snd_fxdiv_gen.h" \ no-obj no-implicit-rule before-depend \ clean "snd_fxdiv_gen.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ no-obj no-implicit-rule before-depend \ clean "miidevs.h" kbdmuxmap.h optional kbdmux_dflt_keymap \ compile-with "${KEYMAP} -L ${KBDMUX_DFLT_KEYMAP} | ${KEYMAP_FIX} > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "kbdmuxmap.h" teken_state.h optional sc | vt \ dependency "$S/teken/gensequences $S/teken/sequences" \ compile-with "${AWK} -f $S/teken/gensequences $S/teken/sequences > teken_state.h" \ no-obj no-implicit-rule before-depend \ clean "teken_state.h" ukbdmap.h optional ukbd_dflt_keymap \ compile-with "${KEYMAP} -L ${UKBD_DFLT_KEYMAP} | ${KEYMAP_FIX} > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" usbdevs.h optional usb | hid \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -h" \ no-obj no-implicit-rule before-depend \ clean "usbdevs.h" usbdevs_data.h optional usb \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -d" \ no-obj no-implicit-rule before-depend \ clean "usbdevs_data.h" sdiodevs.h optional mmccam \ dependency "$S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs" \ compile-with "${AWK} -f $S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs -h" \ no-obj no-implicit-rule before-depend \ clean "sdiodevs.h" sdiodevs_data.h optional mmccam \ dependency "$S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs" \ compile-with "${AWK} -f $S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs -d" \ no-obj no-implicit-rule before-depend \ clean "sdiodevs_data.h" cam/cam.c optional scbus cam/cam_compat.c optional scbus cam/cam_iosched.c optional scbus cam/cam_periph.c optional scbus cam/cam_queue.c optional scbus cam/cam_sim.c optional scbus cam/cam_xpt.c optional scbus cam/ata/ata_all.c optional scbus cam/ata/ata_xpt.c optional scbus cam/ata/ata_pmp.c optional scbus cam/nvme/nvme_all.c optional scbus cam/nvme/nvme_da.c optional nda | da cam/nvme/nvme_xpt.c optional scbus cam/scsi/scsi_xpt.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/ata/ata_da.c optional ada | da cam/ctl/ctl.c optional ctl cam/ctl/ctl_backend.c optional ctl cam/ctl/ctl_backend_block.c optional ctl cam/ctl/ctl_backend_ramdisk.c optional ctl cam/ctl/ctl_cmd_table.c optional ctl cam/ctl/ctl_frontend.c optional ctl cam/ctl/ctl_frontend_cam_sim.c optional ctl cam/ctl/ctl_frontend_ioctl.c optional ctl cam/ctl/ctl_frontend_iscsi.c optional ctl cfiscsi cam/ctl/ctl_ha.c optional ctl cam/ctl/ctl_scsi_all.c optional ctl cam/ctl/ctl_tpc.c optional ctl cam/ctl/ctl_tpc_local.c optional ctl cam/ctl/ctl_error.c optional ctl cam/ctl/ctl_util.c optional ctl cam/ctl/scsi_ctl.c optional ctl cam/mmc/mmc_xpt.c optional scbus mmccam cam/mmc/mmc_sim.c optional scbus mmccam cam/mmc/mmc_sim_if.m optional scbus mmccam cam/mmc/mmc_da.c optional scbus mmccam da cam/scsi/scsi_da.c optional da cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_enc.c optional ses cam/scsi/scsi_enc_ses.c optional ses cam/scsi/scsi_enc_safte.c optional ses cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ cam/scsi/smp_all.c optional scbus # shared between zfs and dtrace cddl/compat/opensolaris/kern/opensolaris.c optional dtrace compile-with "${CDDL_C}" cddl/compat/opensolaris/kern/opensolaris_proc.c optional zfs | dtrace compile-with "${CDDL_C}" contrib/openzfs/module/os/freebsd/spl/spl_misc.c optional zfs | dtrace compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_cmn_err.c optional zfs | dtrace compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_taskq.c optional zfs | dtrace compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_kmem.c optional zfs | dtrace compile-with "${ZFS_C}" #zfs solaris portability layer contrib/openzfs/module/os/freebsd/spl/acl_common.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/callb.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/list.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_acl.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_dtrace.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_kstat.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_policy.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_procfs_list.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_string.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_sunddi.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_sysevent.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_uio.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_vfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_vm.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_zlib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/spl/spl_zone.c optional zfs compile-with "${ZFS_C}" # zfs specific #zfs avl contrib/openzfs/module/avl/avl.c optional zfs compile-with "${ZFS_C}" # zfs lua support contrib/openzfs/module/lua/lapi.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lauxlib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lbaselib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lcode.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lcompat.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lcorolib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lctype.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/ldebug.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/ldo.c optional zfs compile-with "${ZFS_C} ${NO_WINFINITE_RECURSION}" contrib/openzfs/module/lua/lfunc.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lgc.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/llex.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lmem.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lobject.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lopcodes.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lparser.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lstate.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lstring.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lstrlib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/ltable.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/ltablib.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/ltm.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lvm.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/lua/lzio.c optional zfs compile-with "${ZFS_C}" # zfs nvpair support contrib/openzfs/module/nvpair/fnvpair.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/nvpair/nvpair.c optional zfs compile-with "${ZFS_RPC_C} ${NO_WSTRINGOP_OVERREAD}" contrib/openzfs/module/nvpair/nvpair_alloc_fixed.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/nvpair/nvpair_alloc_spl.c optional zfs compile-with "${ZFS_C}" #zfs platform compatibility code contrib/openzfs/module/os/freebsd/zfs/abd_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/arc_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/crypto_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/dmu_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/event_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/hkdf.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/kmod_core.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/spa_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/sysctl_os.c optional zfs compile-with "${ZFS_C} -include $S/modules/zfs/zfs_config.h" contrib/openzfs/module/os/freebsd/zfs/vdev_file.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/vdev_label_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/vdev_geom.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_acl.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_ctldir.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_debug.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_dir.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_ioctl_compat.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_ioctl_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_racct.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zio_crypt.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/os/freebsd/zfs/zvol_os.c optional zfs compile-with "${ZFS_C}" #zfs unicode support contrib/openzfs/module/unicode/uconv.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/unicode/u8_textprep.c optional zfs compile-with "${ZFS_C}" #zfs checksums / zcommon contrib/openzfs/module/zcommon/cityhash.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfeature_common.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_comutil.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_deleg.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_fletcher.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_fletcher_superscalar.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_fletcher_superscalar4.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_namecheck.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zfs_prop.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zpool_prop.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zcommon/zprop_common.c optional zfs compile-with "${ZFS_C}" # zfs edon-r hash support contrib/openzfs/module/icp/algs/edonr/edonr.c optional zfs compile-with "${ZFS_C}" # zfs blake3 hash support contrib/openzfs/module/icp/algs/blake3/blake3.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/icp/algs/blake3/blake3_generic.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/icp/algs/blake3/blake3_impl.c optional zfs compile-with "${ZFS_C}" # zfs sha2 hash support contrib/openzfs/module/icp/algs/sha2/sha2_generic.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/icp/algs/sha2/sha256_impl.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/icp/algs/sha2/sha512_impl.c optional zfs compile-with "${ZFS_C}" #zfs core common code contrib/openzfs/module/zfs/abd.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/aggsum.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/arc.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/blake3_zfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/blkptr.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/bplist.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/bpobj.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/bptree.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/brt.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/btree.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/bqueue.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dbuf.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dbuf_stats.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dataset_kstats.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/ddt.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/ddt_zap.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_diff.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_object.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_objset.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_recv.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_redact.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_send.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_traverse.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_tx.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dmu_zfetch.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dnode.c optional zfs compile-with "${ZFS_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" \ warning "kernel contains CDDL licensed ZFS filesystem" contrib/openzfs/module/zfs/dnode_sync.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_bookmark.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_crypt.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_dataset.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_deadlist.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_deleg.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_destroy.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_dir.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_pool.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_prop.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_scan.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_synctask.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/dsl_userhold.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/edonr_zfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/fm.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/gzip.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/lzjb.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/lz4.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/lz4_zfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/metaslab.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/mmp.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/multilist.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/objlist.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/pathname.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/range_tree.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/refcount.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/rrwlock.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/sa.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/sha2_zfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/skein_zfs.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_checkpoint.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_config.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_errlog.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_history.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_log_spacemap.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_misc.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/spa_stats.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/space_map.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/space_reftree.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/txg.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/uberblock.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/unique.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_draid.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_draid_rand.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_indirect.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_indirect_births.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_indirect_mapping.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_initialize.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_label.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_mirror.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_missing.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_queue.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_raidz.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_raidz_math.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_raidz_math_scalar.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_rebuild.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_removal.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_root.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/vdev_trim.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zap.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zap_leaf.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zap_micro.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp_get.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp_global.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp_iter.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp_set.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zcp_synctask.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfeature.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_byteswap.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_chksum.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_fm.c optional zfs compile-with "${ZFS_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" contrib/openzfs/module/zfs/zfs_fuid.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_impl.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_ioctl.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_log.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_onexit.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_quota.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_ratelimit.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_replay.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_rlock.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_sa.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zfs_vnops.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zstd/zfs_zstd.c optional zfs zstdio compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zil.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zio.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zio_checksum.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zio_compress.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zio_inject.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zle.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zrlock.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zthr.c optional zfs compile-with "${ZFS_C}" contrib/openzfs/module/zfs/zvol.c optional zfs compile-with "${ZFS_C}" # dtrace specific cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c optional dtrace compile-with "${DTRACE_C}" \ warning "kernel contains CDDL licensed DTRACE" cddl/contrib/opensolaris/uts/common/dtrace/dtrace_xoroshiro128_plus.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/dtmalloc/dtmalloc.c optional dtmalloc | dtraceall compile-with "${CDDL_C}" cddl/dev/profile/profile.c optional dtrace_profile | dtraceall compile-with "${CDDL_C}" cddl/dev/sdt/sdt.c optional dtrace_sdt | dtraceall compile-with "${CDDL_C}" cddl/dev/fbt/fbt.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" cddl/dev/systrace/systrace.c optional dtrace_systrace | dtraceall compile-with "${CDDL_C}" cddl/dev/prototype.c optional dtrace_prototype | dtraceall compile-with "${CDDL_C}" fs/nfsclient/nfs_clkdtrace.c optional dtnfscl nfscl | dtraceall nfscl compile-with "${CDDL_C}" compat/freebsd32/freebsd32_abort2.c optional compat_freebsd32 compat/freebsd32/freebsd32_capability.c optional compat_freebsd32 compat/freebsd32/freebsd32_ioctl.c optional compat_freebsd32 compat/freebsd32/freebsd32_misc.c optional compat_freebsd32 compat/freebsd32/freebsd32_syscalls.c optional compat_freebsd32 compat/freebsd32/freebsd32_sysent.c optional compat_freebsd32 contrib/ck/src/ck_array.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_barrier_centralized.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_barrier_combining.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_barrier_dissemination.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_barrier_mcs.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_barrier_tournament.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_epoch.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_hp.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_hs.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_ht.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/ck/src/ck_rhs.c standard compile-with "${NORMAL_C} -I$S/contrib/ck/include" contrib/dev/acpica/common/ahids.c optional acpi acpi_debug contrib/dev/acpica/common/ahuuids.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbcmds.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbconvert.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbdisply.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbexec.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbhistry.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbinput.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbmethod.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbnames.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbobject.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbstats.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbtest.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbutils.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbxface.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmbuffer.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmcstyle.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmdeferred.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmnames.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmopcode.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmresrc.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmresrcl.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmresrcl2.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmresrcs.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmutils.c optional acpi acpi_debug contrib/dev/acpica/components/disassembler/dmwalk.c optional acpi acpi_debug contrib/dev/acpica/components/dispatcher/dsargs.c optional acpi contrib/dev/acpica/components/dispatcher/dscontrol.c optional acpi contrib/dev/acpica/components/dispatcher/dsdebug.c optional acpi contrib/dev/acpica/components/dispatcher/dsfield.c optional acpi contrib/dev/acpica/components/dispatcher/dsinit.c optional acpi contrib/dev/acpica/components/dispatcher/dsmethod.c optional acpi contrib/dev/acpica/components/dispatcher/dsmthdat.c optional acpi contrib/dev/acpica/components/dispatcher/dsobject.c optional acpi contrib/dev/acpica/components/dispatcher/dsopcode.c optional acpi contrib/dev/acpica/components/dispatcher/dspkginit.c optional acpi contrib/dev/acpica/components/dispatcher/dsutils.c optional acpi contrib/dev/acpica/components/dispatcher/dswexec.c optional acpi contrib/dev/acpica/components/dispatcher/dswload.c optional acpi contrib/dev/acpica/components/dispatcher/dswload2.c optional acpi contrib/dev/acpica/components/dispatcher/dswscope.c optional acpi contrib/dev/acpica/components/dispatcher/dswstate.c optional acpi contrib/dev/acpica/components/events/evevent.c optional acpi contrib/dev/acpica/components/events/evglock.c optional acpi contrib/dev/acpica/components/events/evgpe.c optional acpi contrib/dev/acpica/components/events/evgpeblk.c optional acpi contrib/dev/acpica/components/events/evgpeinit.c optional acpi contrib/dev/acpica/components/events/evgpeutil.c optional acpi contrib/dev/acpica/components/events/evhandler.c optional acpi contrib/dev/acpica/components/events/evmisc.c optional acpi contrib/dev/acpica/components/events/evregion.c optional acpi contrib/dev/acpica/components/events/evrgnini.c optional acpi contrib/dev/acpica/components/events/evsci.c optional acpi contrib/dev/acpica/components/events/evxface.c optional acpi contrib/dev/acpica/components/events/evxfevnt.c optional acpi contrib/dev/acpica/components/events/evxfgpe.c optional acpi contrib/dev/acpica/components/events/evxfregn.c optional acpi contrib/dev/acpica/components/executer/exconcat.c optional acpi contrib/dev/acpica/components/executer/exconfig.c optional acpi contrib/dev/acpica/components/executer/exconvrt.c optional acpi contrib/dev/acpica/components/executer/excreate.c optional acpi contrib/dev/acpica/components/executer/exdebug.c optional acpi contrib/dev/acpica/components/executer/exdump.c optional acpi contrib/dev/acpica/components/executer/exfield.c optional acpi contrib/dev/acpica/components/executer/exfldio.c optional acpi contrib/dev/acpica/components/executer/exmisc.c optional acpi contrib/dev/acpica/components/executer/exmutex.c optional acpi contrib/dev/acpica/components/executer/exnames.c optional acpi contrib/dev/acpica/components/executer/exoparg1.c optional acpi contrib/dev/acpica/components/executer/exoparg2.c optional acpi contrib/dev/acpica/components/executer/exoparg3.c optional acpi contrib/dev/acpica/components/executer/exoparg6.c optional acpi contrib/dev/acpica/components/executer/exprep.c optional acpi contrib/dev/acpica/components/executer/exregion.c optional acpi contrib/dev/acpica/components/executer/exresnte.c optional acpi contrib/dev/acpica/components/executer/exresolv.c optional acpi contrib/dev/acpica/components/executer/exresop.c optional acpi contrib/dev/acpica/components/executer/exserial.c optional acpi contrib/dev/acpica/components/executer/exstore.c optional acpi contrib/dev/acpica/components/executer/exstoren.c optional acpi contrib/dev/acpica/components/executer/exstorob.c optional acpi contrib/dev/acpica/components/executer/exsystem.c optional acpi contrib/dev/acpica/components/executer/extrace.c optional acpi contrib/dev/acpica/components/executer/exutils.c optional acpi contrib/dev/acpica/components/hardware/hwacpi.c optional acpi contrib/dev/acpica/components/hardware/hwesleep.c optional acpi contrib/dev/acpica/components/hardware/hwgpe.c optional acpi contrib/dev/acpica/components/hardware/hwpci.c optional acpi contrib/dev/acpica/components/hardware/hwregs.c optional acpi contrib/dev/acpica/components/hardware/hwsleep.c optional acpi contrib/dev/acpica/components/hardware/hwtimer.c optional acpi contrib/dev/acpica/components/hardware/hwvalid.c optional acpi contrib/dev/acpica/components/hardware/hwxface.c optional acpi contrib/dev/acpica/components/hardware/hwxfsleep.c optional acpi contrib/dev/acpica/components/namespace/nsaccess.c optional acpi \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" contrib/dev/acpica/components/namespace/nsalloc.c optional acpi contrib/dev/acpica/components/namespace/nsarguments.c optional acpi contrib/dev/acpica/components/namespace/nsconvert.c optional acpi contrib/dev/acpica/components/namespace/nsdump.c optional acpi contrib/dev/acpica/components/namespace/nseval.c optional acpi contrib/dev/acpica/components/namespace/nsinit.c optional acpi contrib/dev/acpica/components/namespace/nsload.c optional acpi contrib/dev/acpica/components/namespace/nsnames.c optional acpi contrib/dev/acpica/components/namespace/nsobject.c optional acpi contrib/dev/acpica/components/namespace/nsparse.c optional acpi contrib/dev/acpica/components/namespace/nspredef.c optional acpi contrib/dev/acpica/components/namespace/nsprepkg.c optional acpi contrib/dev/acpica/components/namespace/nsrepair.c optional acpi contrib/dev/acpica/components/namespace/nsrepair2.c optional acpi contrib/dev/acpica/components/namespace/nssearch.c optional acpi contrib/dev/acpica/components/namespace/nsutils.c optional acpi contrib/dev/acpica/components/namespace/nswalk.c optional acpi contrib/dev/acpica/components/namespace/nsxfeval.c optional acpi contrib/dev/acpica/components/namespace/nsxfname.c optional acpi contrib/dev/acpica/components/namespace/nsxfobj.c optional acpi contrib/dev/acpica/components/parser/psargs.c optional acpi contrib/dev/acpica/components/parser/psloop.c optional acpi contrib/dev/acpica/components/parser/psobject.c optional acpi contrib/dev/acpica/components/parser/psopcode.c optional acpi contrib/dev/acpica/components/parser/psopinfo.c optional acpi contrib/dev/acpica/components/parser/psparse.c optional acpi contrib/dev/acpica/components/parser/psscope.c optional acpi contrib/dev/acpica/components/parser/pstree.c optional acpi contrib/dev/acpica/components/parser/psutils.c optional acpi contrib/dev/acpica/components/parser/pswalk.c optional acpi contrib/dev/acpica/components/parser/psxface.c optional acpi contrib/dev/acpica/components/resources/rsaddr.c optional acpi contrib/dev/acpica/components/resources/rscalc.c optional acpi contrib/dev/acpica/components/resources/rscreate.c optional acpi contrib/dev/acpica/components/resources/rsdump.c optional acpi acpi_debug contrib/dev/acpica/components/resources/rsdumpinfo.c optional acpi contrib/dev/acpica/components/resources/rsinfo.c optional acpi contrib/dev/acpica/components/resources/rsio.c optional acpi contrib/dev/acpica/components/resources/rsirq.c optional acpi contrib/dev/acpica/components/resources/rslist.c optional acpi contrib/dev/acpica/components/resources/rsmemory.c optional acpi contrib/dev/acpica/components/resources/rsmisc.c optional acpi contrib/dev/acpica/components/resources/rsserial.c optional acpi contrib/dev/acpica/components/resources/rsutils.c optional acpi contrib/dev/acpica/components/resources/rsxface.c optional acpi contrib/dev/acpica/components/tables/tbdata.c optional acpi contrib/dev/acpica/components/tables/tbfadt.c optional acpi contrib/dev/acpica/components/tables/tbfind.c optional acpi contrib/dev/acpica/components/tables/tbinstal.c optional acpi contrib/dev/acpica/components/tables/tbprint.c optional acpi contrib/dev/acpica/components/tables/tbutils.c optional acpi contrib/dev/acpica/components/tables/tbxface.c optional acpi contrib/dev/acpica/components/tables/tbxfload.c optional acpi contrib/dev/acpica/components/tables/tbxfroot.c optional acpi contrib/dev/acpica/components/utilities/utaddress.c optional acpi contrib/dev/acpica/components/utilities/utalloc.c optional acpi contrib/dev/acpica/components/utilities/utascii.c optional acpi contrib/dev/acpica/components/utilities/utbuffer.c optional acpi contrib/dev/acpica/components/utilities/utcache.c optional acpi contrib/dev/acpica/components/utilities/utcksum.c optional acpi contrib/dev/acpica/components/utilities/utcopy.c optional acpi contrib/dev/acpica/components/utilities/utdebug.c optional acpi contrib/dev/acpica/components/utilities/utdecode.c optional acpi contrib/dev/acpica/components/utilities/utdelete.c optional acpi contrib/dev/acpica/components/utilities/uterror.c optional acpi contrib/dev/acpica/components/utilities/uteval.c optional acpi contrib/dev/acpica/components/utilities/utexcep.c optional acpi contrib/dev/acpica/components/utilities/utglobal.c optional acpi contrib/dev/acpica/components/utilities/uthex.c optional acpi contrib/dev/acpica/components/utilities/utids.c optional acpi contrib/dev/acpica/components/utilities/utinit.c optional acpi contrib/dev/acpica/components/utilities/utlock.c optional acpi contrib/dev/acpica/components/utilities/utmath.c optional acpi contrib/dev/acpica/components/utilities/utmisc.c optional acpi contrib/dev/acpica/components/utilities/utmutex.c optional acpi contrib/dev/acpica/components/utilities/utnonansi.c optional acpi contrib/dev/acpica/components/utilities/utobject.c optional acpi contrib/dev/acpica/components/utilities/utosi.c optional acpi contrib/dev/acpica/components/utilities/utownerid.c optional acpi contrib/dev/acpica/components/utilities/utpredef.c optional acpi contrib/dev/acpica/components/utilities/utresdecode.c optional acpi acpi_debug contrib/dev/acpica/components/utilities/utresrc.c optional acpi contrib/dev/acpica/components/utilities/utstate.c optional acpi contrib/dev/acpica/components/utilities/utstring.c optional acpi contrib/dev/acpica/components/utilities/utstrsuppt.c optional acpi contrib/dev/acpica/components/utilities/utstrtoul64.c optional acpi contrib/dev/acpica/components/utilities/utuuid.c optional acpi acpi_debug contrib/dev/acpica/components/utilities/utxface.c optional acpi contrib/dev/acpica/components/utilities/utxferror.c optional acpi contrib/dev/acpica/components/utilities/utxfinit.c optional acpi contrib/dev/acpica/os_specific/service_layers/osgendbg.c optional acpi acpi_debug netpfil/ipfilter/netinet/fil.c optional ipfilter inet \ compile-with "${NORMAL_C} ${NO_WSELF_ASSIGN} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_auth.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_fil_freebsd.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_frag.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_log.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_nat.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_proxy.c optional ipfilter inet \ compile-with "${NORMAL_C} ${NO_WSELF_ASSIGN} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_state.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_lookup.c optional ipfilter inet \ compile-with "${NORMAL_C} ${NO_WSELF_ASSIGN} -Wno-unused -Wno-error -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_pool.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_htable.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter ${NO_WTAUTOLOGICAL_POINTER_COMPARE}" netpfil/ipfilter/netinet/ip_sync.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_nat6.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_rules.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_scan.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/ip_dstlist.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-unused -I$S/netpfil/ipfilter" netpfil/ipfilter/netinet/radix_ipf.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/netpfil/ipfilter" contrib/libfdt/fdt.c optional fdt contrib/libfdt/fdt_ro.c optional fdt contrib/libfdt/fdt_rw.c optional fdt contrib/libfdt/fdt_strerror.c optional fdt contrib/libfdt/fdt_sw.c optional fdt contrib/libfdt/fdt_wip.c optional fdt contrib/libnv/cnvlist.c standard contrib/libnv/dnvlist.c standard contrib/libnv/nvlist.c standard contrib/libnv/bsd_nvpair.c standard # xz dev/xz/xz_mod.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" contrib/xz-embedded/linux/lib/xz/xz_crc32.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" contrib/xz-embedded/linux/lib/xz/xz_crc64.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" contrib/xz-embedded/linux/lib/xz/xz_dec_bcj.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" contrib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" contrib/xz-embedded/linux/lib/xz/xz_dec_stream.c optional xz \ compile-with "${NORMAL_C} -DXZ_USE_CRC64 -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/" # Zstd contrib/zstd/lib/freebsd/zstd_kmalloc.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/common/zstd_common.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/common/fse_decompress.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/common/entropy_common.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/common/error_private.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/common/xxhash.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_compress.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_compress_literals.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_compress_sequences.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_compress_superblock.c optional zstdio compile-with "${ZSTD_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" contrib/zstd/lib/compress/fse_compress.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/hist.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/huf_compress.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_double_fast.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_fast.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_lazy.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_ldm.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/compress/zstd_opt.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/decompress/zstd_ddict.c optional zstdio compile-with ${ZSTD_C} contrib/zstd/lib/decompress/zstd_decompress.c optional zstdio compile-with ${ZSTD_C} # See comment in sys/conf/kern.pre.mk contrib/zstd/lib/decompress/zstd_decompress_block.c optional zstdio \ compile-with "${ZSTD_C} ${ZSTD_DECOMPRESS_BLOCK_FLAGS}" contrib/zstd/lib/decompress/huf_decompress.c optional zstdio compile-with "${ZSTD_C} ${NO_WBITWISE_INSTEAD_OF_LOGICAL}" # Blake 2 contrib/libb2/blake2b-ref.c optional crypto | !random_loadable random_fenestrasx \ compile-with "${NORMAL_C} -I$S/crypto/blake2 -Wno-cast-qual -DSUFFIX=_ref -Wno-unused-function" contrib/libb2/blake2s-ref.c optional crypto \ compile-with "${NORMAL_C} -I$S/crypto/blake2 -Wno-cast-qual -DSUFFIX=_ref -Wno-unused-function" crypto/blake2/blake2-sw.c optional crypto \ compile-with "${NORMAL_C} -I$S/crypto/blake2 -Wno-cast-qual" crypto/camellia/camellia.c optional crypto crypto/camellia/camellia-api.c optional crypto crypto/chacha20/chacha.c standard crypto/chacha20/chacha-sw.c optional crypto crypto/chacha20_poly1305.c optional crypto crypto/curve25519.c optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium" crypto/des/des_ecb.c optional netsmb crypto/des/des_setkey.c optional netsmb crypto/openssl/ossl.c optional ossl crypto/openssl/ossl_aes.c optional ossl crypto/openssl/ossl_chacha20.c optional ossl crypto/openssl/ossl_poly1305.c optional ossl crypto/openssl/ossl_sha1.c optional ossl crypto/openssl/ossl_sha256.c optional ossl crypto/openssl/ossl_sha512.c optional ossl crypto/rc4/rc4.c optional netgraph_mppc_encryption crypto/rijndael/rijndael-alg-fst.c optional crypto | ekcd | geom_bde | \ !random_loadable | wlan_ccmp crypto/rijndael/rijndael-api-fst.c optional ekcd | geom_bde | !random_loadable crypto/rijndael/rijndael-api.c optional crypto | wlan_ccmp crypto/sha1.c optional carp | crypto | ether | \ netgraph_mppc_encryption | sctp crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | \ !random_loadable | sctp | zfs crypto/sha2/sha512c.c optional crypto | geom_bde | zfs crypto/skein/skein.c optional crypto | zfs crypto/skein/skein_block.c optional crypto | zfs crypto/siphash/siphash.c optional inet | inet6 | wg crypto/siphash/siphash_test.c optional inet | inet6 | wg ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_capture.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_lex.c optional ddb ddb/db_main.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_script.c optional ddb ddb/db_sym.c optional ddb ddb/db_thread.c optional ddb ddb/db_textdump.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb dev/aac/aac.c optional aac dev/aac/aac_cam.c optional aacp aac dev/aac/aac_debug.c optional aac dev/aac/aac_disk.c optional aac dev/aac/aac_pci.c optional aac pci dev/aacraid/aacraid.c optional aacraid dev/aacraid/aacraid_cam.c optional aacraid scbus dev/aacraid/aacraid_debug.c optional aacraid dev/aacraid/aacraid_pci.c optional aacraid pci dev/acpi_support/acpi_wmi.c optional acpi_wmi acpi dev/acpi_support/acpi_asus.c optional acpi_asus acpi dev/acpi_support/acpi_asus_wmi.c optional acpi_asus_wmi acpi dev/acpi_support/acpi_fujitsu.c optional acpi_fujitsu acpi dev/acpi_support/acpi_hp.c optional acpi_hp acpi dev/acpi_support/acpi_ibm.c optional acpi_ibm acpi dev/acpi_support/acpi_panasonic.c optional acpi_panasonic acpi dev/acpi_support/acpi_sony.c optional acpi_sony acpi dev/acpi_support/acpi_toshiba.c optional acpi_toshiba acpi dev/acpi_support/atk0110.c optional aibs acpi dev/acpica/Osd/OsdDebug.c optional acpi dev/acpica/Osd/OsdHardware.c optional acpi dev/acpica/Osd/OsdInterrupt.c optional acpi dev/acpica/Osd/OsdMemory.c optional acpi dev/acpica/Osd/OsdSchedule.c optional acpi dev/acpica/Osd/OsdStream.c optional acpi dev/acpica/Osd/OsdSynch.c optional acpi dev/acpica/Osd/OsdTable.c optional acpi dev/acpica/acpi.c optional acpi dev/acpica/acpi_acad.c optional acpi dev/acpica/acpi_apei.c optional acpi dev/acpica/acpi_battery.c optional acpi dev/acpica/acpi_button.c optional acpi dev/acpica/acpi_cmbat.c optional acpi dev/acpica/acpi_cpu.c optional acpi dev/acpica/acpi_ec.c optional acpi dev/acpica/acpi_ged.c optional acpi_ged acpi dev/acpica/acpi_isab.c optional acpi isa dev/acpica/acpi_lid.c optional acpi dev/acpica/acpi_package.c optional acpi dev/acpica/acpi_perf.c optional acpi dev/acpica/acpi_powerres.c optional acpi dev/acpica/acpi_quirk.c optional acpi dev/acpica/acpi_resource.c optional acpi dev/acpica/acpi_container.c optional acpi dev/acpica/acpi_smbat.c optional acpi dev/acpica/acpi_thermal.c optional acpi dev/acpica/acpi_throttle.c optional acpi dev/acpica/acpi_video.c optional acpi_video acpi dev/acpica/acpi_dock.c optional acpi_dock acpi dev/adlink/adlink.c optional adlink dev/ae/if_ae.c optional ae pci dev/age/if_age.c optional age pci dev/agp/agp.c optional agp pci dev/agp/agp_if.m optional agp pci dev/ahci/ahci.c optional ahci dev/ahci/ahciem.c optional ahci dev/ahci/ahci_pci.c optional ahci pci dev/aic7xxx/ahc_isa.c optional ahc isa dev/aic7xxx/ahc_pci.c optional ahc pci \ compile-with "${NORMAL_C} ${NO_WCONSTANT_CONVERSION}" dev/aic7xxx/ahd_pci.c optional ahd pci \ compile-with "${NORMAL_C} ${NO_WCONSTANT_CONVERSION}" dev/aic7xxx/aic7770.c optional ahc dev/aic7xxx/aic79xx.c optional ahd pci dev/aic7xxx/aic79xx_osm.c optional ahd pci dev/aic7xxx/aic79xx_pci.c optional ahd pci dev/aic7xxx/aic79xx_reg_print.c optional ahd pci ahd_reg_pretty_print dev/aic7xxx/aic7xxx.c optional ahc dev/aic7xxx/aic7xxx_93cx6.c optional ahc dev/aic7xxx/aic7xxx_osm.c optional ahc dev/aic7xxx/aic7xxx_pci.c optional ahc pci dev/aic7xxx/aic7xxx_reg_print.c optional ahc ahc_reg_pretty_print dev/al_eth/al_eth.c optional al_eth fdt \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" dev/al_eth/al_init_eth_lm.c optional al_eth fdt \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" dev/al_eth/al_init_eth_kr.c optional al_eth fdt \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_iofic.c optional al_iofic \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_serdes_25g.c optional al_serdes \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_serdes_hssp.c optional al_serdes \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_udma_config.c optional al_udma \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_udma_debug.c optional al_udma \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_udma_iofic.c optional al_udma \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_hal_udma_main.c optional al_udma \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/al_serdes.c optional al_serdes \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/eth/al_hal_eth_kr.c optional al_eth \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" contrib/alpine-hal/eth/al_hal_eth_main.c optional al_eth \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}" dev/alc/if_alc.c optional alc pci dev/ale/if_ale.c optional ale pci dev/alpm/alpm.c optional alpm pci dev/altera/avgen/altera_avgen.c optional altera_avgen dev/altera/avgen/altera_avgen_fdt.c optional altera_avgen fdt dev/altera/avgen/altera_avgen_nexus.c optional altera_avgen dev/altera/msgdma/msgdma.c optional altera_msgdma xdma dev/altera/sdcard/altera_sdcard.c optional altera_sdcard dev/altera/sdcard/altera_sdcard_disk.c optional altera_sdcard dev/altera/sdcard/altera_sdcard_io.c optional altera_sdcard dev/altera/sdcard/altera_sdcard_fdt.c optional altera_sdcard fdt dev/altera/sdcard/altera_sdcard_nexus.c optional altera_sdcard dev/altera/softdma/softdma.c optional altera_softdma xdma fdt dev/altera/pio/pio.c optional altera_pio dev/altera/pio/pio_if.m optional altera_pio dev/amdpm/amdpm.c optional amdpm pci | nfpm pci dev/amdsmb/amdsmb.c optional amdsmb pci # dev/ata/ata_if.m optional ata | atacore dev/ata/ata-all.c optional ata | atacore dev/ata/ata-dma.c optional ata | atacore dev/ata/ata-lowlevel.c optional ata | atacore dev/ata/ata-sata.c optional ata | atacore dev/ata/ata-isa.c optional ata isa | ataisa dev/ata/ata-pci.c optional ata pci | atapci dev/ata/chipsets/ata-acard.c optional ata pci | ataacard dev/ata/chipsets/ata-acerlabs.c optional ata pci | ataacerlabs dev/ata/chipsets/ata-amd.c optional ata pci | ataamd dev/ata/chipsets/ata-ati.c optional ata pci | ataati dev/ata/chipsets/ata-cenatek.c optional ata pci | atacenatek dev/ata/chipsets/ata-cypress.c optional ata pci | atacypress dev/ata/chipsets/ata-cyrix.c optional ata pci | atacyrix dev/ata/chipsets/ata-highpoint.c optional ata pci | atahighpoint dev/ata/chipsets/ata-intel.c optional ata pci | ataintel dev/ata/chipsets/ata-ite.c optional ata pci | ataite dev/ata/chipsets/ata-jmicron.c optional ata pci | atajmicron dev/ata/chipsets/ata-marvell.c optional ata pci | atamarvell dev/ata/chipsets/ata-micron.c optional ata pci | atamicron dev/ata/chipsets/ata-national.c optional ata pci | atanational dev/ata/chipsets/ata-netcell.c optional ata pci | atanetcell dev/ata/chipsets/ata-nvidia.c optional ata pci | atanvidia dev/ata/chipsets/ata-promise.c optional ata pci | atapromise dev/ata/chipsets/ata-serverworks.c optional ata pci | ataserverworks dev/ata/chipsets/ata-siliconimage.c optional ata pci | atasiliconimage | ataati dev/ata/chipsets/ata-sis.c optional ata pci | atasis dev/ata/chipsets/ata-via.c optional ata pci | atavia # dev/ath/if_ath.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_alq.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_beacon.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_btcoex.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_btcoex_mci.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_debug.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_descdma.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_keycache.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_ioctl.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_led.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_lna_div.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_pci.c optional ath pci \ compile-with "${ATH_C}" dev/ath/if_ath_tx.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_tx_edma.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_tx_ht.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_tdma.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_sysctl.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_rx.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_rx_edma.c optional ath \ compile-with "${ATH_C}" dev/ath/if_ath_spectral.c optional ath \ compile-with "${ATH_C}" dev/ath/ah_osdep.c optional ath \ compile-with "${ATH_C}" # dev/ath/ath_hal/ah.c optional ath \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_eeprom_v1.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_eeprom_v3.c optional ath_hal | ath_ar5211 | ath_ar5212 \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_eeprom_v14.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_eeprom_v4k.c \ optional ath_hal | ath_ar9285 \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_eeprom_9287.c \ optional ath_hal | ath_ar9287 \ compile-with "${ATH_C}" dev/ath/ath_hal/ah_regdomain.c optional ath \ compile-with "${ATH_C} ${NO_WSHIFT_COUNT_NEGATIVE} ${NO_WSHIFT_COUNT_OVERFLOW}" # ar5210 dev/ath/ath_hal/ar5210/ar5210_attach.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_beacon.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_interrupts.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_keycache.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_misc.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_phy.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_power.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_recv.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_reset.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_xmit.c optional ath_hal | ath_ar5210 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar5211 dev/ath/ath_hal/ar5211/ar5211_attach.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_beacon.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_interrupts.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_keycache.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_misc.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_phy.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_power.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_recv.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_reset.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_xmit.c optional ath_hal | ath_ar5211 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar5212 dev/ath/ath_hal/ar5212/ar5212_ani.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_attach.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_beacon.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_eeprom.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_gpio.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_interrupts.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_keycache.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_misc.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_phy.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_power.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_recv.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_reset.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_rfgain.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_xmit.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 | ath_ar9280 | \ ath_ar9285 ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar5416 (depends on ar5212) dev/ath/ath_hal/ar5416/ar5416_ani.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_attach.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_beacon.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_btcoex.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_iq.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_eeprom.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_gpio.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_interrupts.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_keycache.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_misc.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_phy.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_power.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_radar.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_recv.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_reset.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_spectral.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_xmit.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 | ath_ar9285 | \ ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar9160 (depends on ar5416) dev/ath/ath_hal/ar9001/ar9160_attach.c optional ath_hal | ath_ar9160 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar9280 (depends on ar5416) dev/ath/ath_hal/ar9002/ar9280_attach.c optional ath_hal | ath_ar9280 | \ ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9280_olc.c optional ath_hal | ath_ar9280 | \ ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar9285 (depends on ar5416 and ar9280) dev/ath/ath_hal/ar9002/ar9285_attach.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285_btcoex.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285_reset.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285_cal.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285_phy.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285_diversity.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar9287 (depends on ar5416) dev/ath/ath_hal/ar9002/ar9287_attach.c optional ath_hal | ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9287_reset.c optional ath_hal | ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9287_cal.c optional ath_hal | ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9287_olc.c optional ath_hal | ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ar9300 contrib/dev/ath/ath_hal/ar9300/ar9300_ani.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_beacon.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_eeprom.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal ${NO_WCONSTANT_CONVERSION}" contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_gpio.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_keycache.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_mci.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_misc.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_paprd.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_phy.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_power.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_radar.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_radio.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_recv_ds.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal ${NO_WSOMETIMES_UNINITIALIZED} -Wno-unused-function" contrib/dev/ath/ath_hal/ar9300/ar9300_stub.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_stub_funcs.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_spectral.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_timer.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_xmit.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" contrib/dev/ath/ath_hal/ar9300/ar9300_xmit_ds.c optional ath_hal | ath_ar9300 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal" # rf backends dev/ath/ath_hal/ar5212/ar2316.c optional ath_rf2316 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2317.c optional ath_rf2317 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2413.c optional ath_hal | ath_rf2413 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2425.c optional ath_hal | ath_rf2425 | ath_rf2417 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5111.c optional ath_hal | ath_rf5111 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5112.c optional ath_hal | ath_rf5112 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5413.c optional ath_hal | ath_rf5413 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar2133.c optional ath_hal | ath_ar5416 | \ ath_ar9130 | ath_ar9160 | ath_ar9280 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9280.c optional ath_hal | ath_ar9280 | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9285.c optional ath_hal | ath_ar9285 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar9002/ar9287.c optional ath_hal | ath_ar9287 \ compile-with "${ATH_C} -I$S/dev/ath/ath_hal" # ath rate control algorithms dev/ath/ath_rate/amrr/amrr.c optional ath_rate_amrr \ compile-with "${ATH_C}" dev/ath/ath_rate/onoe/onoe.c optional ath_rate_onoe \ compile-with "${ATH_C}" dev/ath/ath_rate/sample/sample.c optional ath_rate_sample \ compile-with "${ATH_C}" # ath DFS modules dev/ath/ath_dfs/null/dfs_null.c optional ath \ compile-with "${ATH_C}" # dev/backlight/backlight_if.m optional backlight | compat_linuxkpi dev/backlight/backlight.c optional backlight | compat_linuxkpi dev/bce/if_bce.c optional bce dev/bfe/if_bfe.c optional bfe dev/bge/if_bge.c optional bge dev/bhnd/bhnd.c optional bhnd dev/bhnd/bhnd_erom.c optional bhnd dev/bhnd/bhnd_erom_if.m optional bhnd dev/bhnd/bhnd_subr.c optional bhnd dev/bhnd/bhnd_bus_if.m optional bhnd dev/bhnd/bhndb/bhnd_bhndb.c optional bhndb bhnd dev/bhnd/bhndb/bhndb.c optional bhndb bhnd dev/bhnd/bhndb/bhndb_bus_if.m optional bhndb bhnd dev/bhnd/bhndb/bhndb_hwdata.c optional bhndb bhnd dev/bhnd/bhndb/bhndb_if.m optional bhndb bhnd dev/bhnd/bhndb/bhndb_pci.c optional bhndb_pci bhndb bhnd pci dev/bhnd/bhndb/bhndb_pci_hwdata.c optional bhndb_pci bhndb bhnd pci dev/bhnd/bhndb/bhndb_pci_sprom.c optional bhndb_pci bhndb bhnd pci dev/bhnd/bhndb/bhndb_subr.c optional bhndb bhnd dev/bhnd/bcma/bcma.c optional bcma bhnd dev/bhnd/bcma/bcma_bhndb.c optional bcma bhnd bhndb dev/bhnd/bcma/bcma_erom.c optional bcma bhnd dev/bhnd/bcma/bcma_subr.c optional bcma bhnd dev/bhnd/cores/chipc/bhnd_chipc_if.m optional bhnd dev/bhnd/cores/chipc/bhnd_sprom_chipc.c optional bhnd dev/bhnd/cores/chipc/bhnd_pmu_chipc.c optional bhnd dev/bhnd/cores/chipc/chipc.c optional bhnd dev/bhnd/cores/chipc/chipc_cfi.c optional bhnd cfi dev/bhnd/cores/chipc/chipc_gpio.c optional bhnd gpio dev/bhnd/cores/chipc/chipc_slicer.c optional bhnd cfi | bhnd spibus dev/bhnd/cores/chipc/chipc_spi.c optional bhnd spibus dev/bhnd/cores/chipc/chipc_subr.c optional bhnd dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.c optional bhnd dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_if.m optional bhnd dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_hostb_if.m optional bhnd dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl_subr.c optional bhnd dev/bhnd/cores/pci/bhnd_pci.c optional bhnd pci dev/bhnd/cores/pci/bhnd_pci_hostb.c optional bhndb bhnd pci dev/bhnd/cores/pci/bhnd_pcib.c optional bhnd_pcib bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2.c optional bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c optional bhndb bhnd pci dev/bhnd/cores/pcie2/bhnd_pcie2b.c optional bhnd_pcie2b bhnd pci dev/bhnd/cores/pmu/bhnd_pmu.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_bcm.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_btxt.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_sprom.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_tlv.c optional bhnd dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd dev/bhnd/nvram/bhnd_nvram_io.c optional bhnd dev/bhnd/nvram/bhnd_nvram_iobuf.c optional bhnd dev/bhnd/nvram/bhnd_nvram_ioptr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd dev/bhnd/nvram/bhnd_nvram_plist.c optional bhnd dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd dev/bhnd/nvram/bhnd_nvram_store_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value_prf.c optional bhnd dev/bhnd/nvram/bhnd_nvram_value_subr.c optional bhnd dev/bhnd/nvram/bhnd_sprom.c optional bhnd dev/bhnd/siba/siba.c optional siba bhnd dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb dev/bhnd/siba/siba_erom.c optional siba bhnd dev/bhnd/siba/siba_subr.c optional siba bhnd # dev/bnxt/bnxt_hwrm.c optional bnxt iflib pci dev/bnxt/bnxt_mgmt.c optional bnxt iflib pci dev/bnxt/bnxt_sysctl.c optional bnxt iflib pci dev/bnxt/bnxt_txrx.c optional bnxt iflib pci dev/bnxt/if_bnxt.c optional bnxt iflib pci dev/bwi/bwimac.c optional bwi dev/bwi/bwiphy.c optional bwi dev/bwi/bwirf.c optional bwi dev/bwi/if_bwi.c optional bwi dev/bwi/if_bwi_pci.c optional bwi pci dev/bwn/if_bwn.c optional bwn bhnd dev/bwn/if_bwn_pci.c optional bwn pci bhnd bhndb bhndb_pci dev/bwn/if_bwn_phy_common.c optional bwn bhnd dev/bwn/if_bwn_phy_g.c optional bwn bhnd dev/bwn/if_bwn_phy_lp.c optional bwn bhnd dev/bwn/if_bwn_phy_n.c optional bwn bhnd dev/bwn/if_bwn_util.c optional bwn bhnd dev/cadence/if_cgem.c optional cgem fdt dev/cardbus/card_if.m standard dev/cardbus/cardbus.c optional cardbus dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus dev/cardbus/power_if.m standard dev/cas/if_cas.c optional cas dev/cfi/cfi_bus_fdt.c optional cfi fdt dev/cfi/cfi_bus_nexus.c optional cfi dev/cfi/cfi_core.c optional cfi dev/cfi/cfi_dev.c optional cfi dev/cfi/cfi_disk.c optional cfid dev/chromebook_platform/chromebook_platform.c optional chromebook_platform dev/ciss/ciss.c optional ciss dev/clk/clk.c optional clk dev/clk/clkdev_if.m optional clk dev/clk/clknode_if.m optional clk dev/clk/clk_bus.c optional clk fdt dev/clk/clk_div.c optional clk dev/clk/clk_fixed.c optional clk dev/clk/clk_gate.c optional clk dev/clk/clk_link.c optional clk dev/clk/clk_mux.c optional clk dev/cpufreq/ichss.c optional cpufreq pci dev/cxgb/cxgb_main.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_sge.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_mc5.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_vsc7323.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_vsc8211.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_ael1002.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_aq100x.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_mv88e1xxx.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_xgmac.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_t3_hw.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_tn1010.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/sys/uipc_mvec.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_t3fw.c optional cxgb cxgb_t3fw \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgbe/t4_clip.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_filter.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_if.m optional cxgbe pci dev/cxgbe/t4_iov.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_mp_ring.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_main.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_netmap.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_sched.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_sge.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_smt.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_l2t.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_tracer.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_vf.c optional cxgbev pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/common/t4_hw.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/common/t4vf_hw.c optional cxgbev pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/crypto/t6_kern_tls.c optional cxgbe pci kern_tls \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/crypto/t4_keyctx.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/cudbg_common.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/cudbg_flash_utils.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/cudbg_lib.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/cudbg_wtp.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/fastlz.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/cudbg/fastlz_api.c optional cxgbe \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" t4fw_cfg.c optional cxgbe \ compile-with "${AWK} -f $S/tools/fw_stub.awk t4fw_cfg.fw:t4fw_cfg t4fw_cfg_uwire.fw:t4fw_cfg_uwire t4fw.fw:t4fw -mt4fw_cfg -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "t4fw_cfg.c" t4fw_cfg.fwo optional cxgbe \ dependency "t4fw_cfg.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t4fw_cfg.fwo" t4fw_cfg.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t4fw_cfg.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t4fw_cfg.fw" t4fw_cfg_uwire.fwo optional cxgbe \ dependency "t4fw_cfg_uwire.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t4fw_cfg_uwire.fwo" t4fw_cfg_uwire.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t4fw_cfg_uwire.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t4fw_cfg_uwire.fw" t4fw.fwo optional cxgbe \ dependency "t4fw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t4fw.fwo" t4fw.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t4fw-1.27.5.0.bin" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t4fw.fw" t5fw_cfg.c optional cxgbe \ compile-with "${AWK} -f $S/tools/fw_stub.awk t5fw_cfg.fw:t5fw_cfg t5fw_cfg_uwire.fw:t5fw_cfg_uwire t5fw.fw:t5fw -mt5fw_cfg -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "t5fw_cfg.c" t5fw_cfg.fwo optional cxgbe \ dependency "t5fw_cfg.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t5fw_cfg.fwo" t5fw_cfg.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t5fw_cfg.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t5fw_cfg.fw" t5fw_cfg_uwire.fwo optional cxgbe \ dependency "t5fw_cfg_uwire.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t5fw_cfg_uwire.fwo" t5fw_cfg_uwire.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t5fw_cfg_uwire.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t5fw_cfg_uwire.fw" t5fw.fwo optional cxgbe \ dependency "t5fw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t5fw.fwo" t5fw.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t5fw-1.27.5.0.bin" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t5fw.fw" t6fw_cfg.c optional cxgbe \ compile-with "${AWK} -f $S/tools/fw_stub.awk t6fw_cfg.fw:t6fw_cfg t6fw_cfg_uwire.fw:t6fw_cfg_uwire t6fw.fw:t6fw -mt6fw_cfg -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "t6fw_cfg.c" t6fw_cfg.fwo optional cxgbe \ dependency "t6fw_cfg.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t6fw_cfg.fwo" t6fw_cfg.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t6fw_cfg.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t6fw_cfg.fw" t6fw_cfg_uwire.fwo optional cxgbe \ dependency "t6fw_cfg_uwire.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t6fw_cfg_uwire.fwo" t6fw_cfg_uwire.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t6fw_cfg_uwire.txt" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t6fw_cfg_uwire.fw" t6fw.fwo optional cxgbe \ dependency "t6fw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "t6fw.fwo" t6fw.fw optional cxgbe \ dependency "$S/dev/cxgbe/firmware/t6fw-1.27.5.0.bin" \ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \ no-obj no-implicit-rule \ clean "t6fw.fw" dev/cxgbe/crypto/t4_crypto.c optional ccr \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cyapa/cyapa.c optional cyapa iicbus dev/dc/if_dc.c optional dc pci dev/dc/dcphy.c optional dc pci dev/dc/pnphy.c optional dc pci dev/dcons/dcons.c optional dcons dev/dcons/dcons_crom.c optional dcons_crom dev/dcons/dcons_os.c optional dcons dev/dialog/da9063/da9063_if.m optional da9063_pmic dev/dialog/da9063/da9063_iic.c optional da9063_pmic iicbus fdt dev/dialog/da9063/da9063_rtc.c optional da9063_rtc fdt dev/drm2/drm_agpsupport.c optional drm2 dev/drm2/drm_auth.c optional drm2 dev/drm2/drm_bufs.c optional drm2 dev/drm2/drm_buffer.c optional drm2 dev/drm2/drm_context.c optional drm2 dev/drm2/drm_crtc.c optional drm2 dev/drm2/drm_crtc_helper.c optional drm2 dev/drm2/drm_dma.c optional drm2 dev/drm2/drm_dp_helper.c optional drm2 dev/drm2/drm_dp_iic_helper.c optional drm2 dev/drm2/drm_drv.c optional drm2 dev/drm2/drm_edid.c optional drm2 dev/drm2/drm_fb_helper.c optional drm2 dev/drm2/drm_fops.c optional drm2 dev/drm2/drm_gem.c optional drm2 dev/drm2/drm_gem_names.c optional drm2 dev/drm2/drm_global.c optional drm2 dev/drm2/drm_hashtab.c optional drm2 dev/drm2/drm_ioctl.c optional drm2 dev/drm2/drm_irq.c optional drm2 dev/drm2/drm_linux_list_sort.c optional drm2 dev/drm2/drm_lock.c optional drm2 dev/drm2/drm_memory.c optional drm2 dev/drm2/drm_mm.c optional drm2 dev/drm2/drm_modes.c optional drm2 dev/drm2/drm_pci.c optional drm2 dev/drm2/drm_platform.c optional drm2 dev/drm2/drm_scatter.c optional drm2 dev/drm2/drm_stub.c optional drm2 dev/drm2/drm_sysctl.c optional drm2 dev/drm2/drm_vm.c optional drm2 dev/drm2/drm_os_freebsd.c optional drm2 dev/drm2/ttm/ttm_agp_backend.c optional drm2 dev/drm2/ttm/ttm_lock.c optional drm2 dev/drm2/ttm/ttm_object.c optional drm2 dev/drm2/ttm/ttm_tt.c optional drm2 dev/drm2/ttm/ttm_bo_util.c optional drm2 dev/drm2/ttm/ttm_bo.c optional drm2 dev/drm2/ttm/ttm_bo_manager.c optional drm2 dev/drm2/ttm/ttm_execbuf_util.c optional drm2 dev/drm2/ttm/ttm_memory.c optional drm2 dev/drm2/ttm/ttm_page_alloc.c optional drm2 dev/drm2/ttm/ttm_bo_vm.c optional drm2 dev/efidev/efidev.c optional efirt dev/efidev/efirt.c optional efirt dev/efidev/efirtc.c optional efirt dev/e1000/if_em.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/em_txrx.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/igb_txrx.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_80003es2lan.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82540.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82541.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82542.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82543.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82571.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82575.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_ich8lan.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_i210.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_api.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_base.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_mac.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_manage.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_nvm.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_phy.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_vf.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_mbx.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_osdep.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/et/if_et.c optional et dev/ena/ena.c optional ena \ compile-with "${NORMAL_C} -I$S/contrib" dev/ena/ena_datapath.c optional ena \ compile-with "${NORMAL_C} -I$S/contrib" dev/ena/ena_netmap.c optional ena \ compile-with "${NORMAL_C} -I$S/contrib" dev/ena/ena_rss.c optional ena \ compile-with "${NORMAL_C} -I$S/contrib" dev/ena/ena_sysctl.c optional ena \ compile-with "${NORMAL_C} -I$S/contrib" contrib/ena-com/ena_com.c optional ena contrib/ena-com/ena_eth_com.c optional ena dev/etherswitch/arswitch/arswitch.c optional arswitch dev/etherswitch/arswitch/arswitch_reg.c optional arswitch dev/etherswitch/arswitch/arswitch_phy.c optional arswitch dev/etherswitch/arswitch/arswitch_8216.c optional arswitch dev/etherswitch/arswitch/arswitch_8226.c optional arswitch dev/etherswitch/arswitch/arswitch_8316.c optional arswitch dev/etherswitch/arswitch/arswitch_8327.c optional arswitch dev/etherswitch/arswitch/arswitch_vlans.c optional arswitch dev/etherswitch/etherswitch.c optional etherswitch dev/etherswitch/etherswitch_if.m optional etherswitch dev/etherswitch/ip17x/ip17x.c optional ip17x dev/etherswitch/ip17x/ip175c.c optional ip17x dev/etherswitch/ip17x/ip175d.c optional ip17x dev/etherswitch/ip17x/ip17x_phy.c optional ip17x dev/etherswitch/ip17x/ip17x_vlans.c optional ip17x dev/etherswitch/miiproxy.c optional miiproxy dev/etherswitch/rtl8366/rtl8366rb.c optional rtl8366rb dev/etherswitch/e6000sw/e6000sw.c optional e6000sw fdt dev/etherswitch/e6000sw/e6060sw.c optional e6060sw dev/etherswitch/infineon/adm6996fc.c optional adm6996fc dev/etherswitch/micrel/ksz8995ma.c optional ksz8995ma dev/etherswitch/ukswitch/ukswitch.c optional ukswitch dev/evdev/cdev.c optional evdev dev/evdev/evdev.c optional evdev dev/evdev/evdev_mt.c optional evdev dev/evdev/evdev_utils.c optional evdev dev/evdev/uinput.c optional evdev uinput dev/exca/exca.c optional cbb dev/extres/phy/phy.c optional phy dev/extres/phy/phydev_if.m optional phy fdt dev/extres/phy/phynode_if.m optional phy dev/extres/phy/phy_usb.c optional phy dev/extres/phy/phynode_usb_if.m optional phy -dev/extres/hwreset/hwreset.c optional hwreset -dev/extres/hwreset/hwreset_array.c optional hwreset -dev/extres/hwreset/hwreset_if.m optional hwreset dev/extres/nvmem/nvmem.c optional nvmem fdt dev/extres/nvmem/nvmem_if.m optional nvmem dev/extres/regulator/regdev_if.m optional regulator fdt dev/extres/regulator/regnode_if.m optional regulator dev/extres/regulator/regulator.c optional regulator dev/extres/regulator/regulator_bus.c optional regulator fdt dev/extres/regulator/regulator_fixed.c optional regulator dev/extres/syscon/syscon.c optional syscon dev/extres/syscon/syscon_generic.c optional syscon fdt dev/extres/syscon/syscon_if.m optional syscon dev/extres/syscon/syscon_power.c optional syscon syscon_power dev/fb/fbd.c optional fbd | vt dev/fb/fb_if.m standard dev/fb/splash.c optional sc splash dev/fdt/fdt_clock.c optional fdt fdt_clock dev/fdt/fdt_clock_if.m optional fdt fdt_clock dev/fdt/fdt_common.c optional fdt dev/fdt/fdt_pinctrl.c optional fdt fdt_pinctrl dev/fdt/fdt_pinctrl_if.m optional fdt fdt_pinctrl dev/fdt/fdt_slicer.c optional fdt cfi | fdt mx25l | fdt n25q | fdt at45d dev/fdt/fdt_static_dtb.S optional fdt fdt_dtb_static \ dependency "${FDT_DTS_FILE:T:R}.dtb" dev/fdt/simplebus.c optional fdt dev/fdt/simple_mfd.c optional syscon fdt dev/filemon/filemon.c optional filemon dev/firewire/firewire.c optional firewire dev/firewire/fwcrom.c optional firewire dev/firewire/fwdev.c optional firewire dev/firewire/fwdma.c optional firewire dev/firewire/fwmem.c optional firewire dev/firewire/fwohci.c optional firewire dev/firewire/fwohci_pci.c optional firewire pci dev/firewire/if_fwe.c optional fwe dev/firewire/if_fwip.c optional fwip dev/firewire/sbp.c optional sbp dev/firewire/sbp_targ.c optional sbp_targ dev/flash/at45d.c optional at45d dev/flash/cqspi.c optional cqspi fdt xdma dev/flash/mx25l.c optional mx25l dev/flash/n25q.c optional n25q fdt dev/flash/qspi_if.m optional cqspi fdt | n25q fdt dev/fxp/if_fxp.c optional fxp dev/fxp/inphy.c optional fxp dev/gem/if_gem.c optional gem dev/gem/if_gem_pci.c optional gem pci dev/gve/gve_adminq.c optional gve dev/gve/gve_main.c optional gve dev/gve/gve_qpl.c optional gve dev/gve/gve_rx.c optional gve dev/gve/gve_sysctl.c optional gve dev/gve/gve_tx.c optional gve dev/gve/gve_utils.c optional gve dev/goldfish/goldfish_rtc.c optional goldfish_rtc fdt dev/gpio/dwgpio/dwgpio.c optional gpio dwgpio fdt dev/gpio/dwgpio/dwgpio_bus.c optional gpio dwgpio fdt dev/gpio/dwgpio/dwgpio_if.m optional gpio dwgpio fdt dev/gpio/gpiobacklight.c optional gpiobacklight fdt dev/gpio/gpiokeys.c optional gpiokeys fdt dev/gpio/gpiokeys_codes.c optional gpiokeys fdt dev/gpio/gpiobus.c optional gpio \ dependency "gpiobus_if.h" dev/gpio/gpioc.c optional gpio \ dependency "gpio_if.h" dev/gpio/gpioiic.c optional gpioiic dev/gpio/gpioled.c optional gpioled !fdt dev/gpio/gpioled_fdt.c optional gpioled fdt dev/gpio/gpiomdio.c optional gpiomdio mii_bitbang dev/gpio/gpiopower.c optional gpiopower fdt dev/gpio/gpioregulator.c optional gpioregulator fdt dev/gpio/gpiospi.c optional gpiospi dev/gpio/gpioths.c optional gpioths dev/gpio/gpio_if.m optional gpio dev/gpio/gpiobus_if.m optional gpio dev/gpio/gpiopps.c optional gpiopps fdt dev/gpio/ofw_gpiobus.c optional fdt gpio dev/hid/bcm5974.c optional bcm5974 dev/hid/hconf.c optional hconf dev/hid/hcons.c optional hcons dev/hid/hgame.c optional hgame dev/hid/hid.c optional hid dev/hid/hid_if.m optional hid dev/hid/hidbus.c optional hidbus dev/hid/hidmap.c optional hidmap dev/hid/hidquirk.c optional hid dev/hid/hidraw.c optional hidraw dev/hid/hkbd.c optional hkbd dev/hid/hms.c optional hms dev/hid/hmt.c optional hmt hconf dev/hid/hpen.c optional hpen dev/hid/hsctrl.c optional hsctrl dev/hid/ietp.c optional ietp dev/hid/ps4dshock.c optional ps4dshock dev/hid/xb360gp.c optional xb360gp dev/hifn/hifn7751.c optional hifn dev/hptiop/hptiop.c optional hptiop scbus dev/hwpmc/hwpmc_logging.c optional hwpmc dev/hwpmc/hwpmc_mod.c optional hwpmc dev/hwpmc/hwpmc_soft.c optional hwpmc +dev/hwreset/hwreset.c optional hwreset +dev/hwreset/hwreset_array.c optional hwreset +dev/hwreset/hwreset_if.m optional hwreset dev/ichiic/ig4_acpi.c optional ig4 acpi iicbus dev/ichiic/ig4_iic.c optional ig4 iicbus dev/ichiic/ig4_pci.c optional ig4 pci iicbus dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida dev/ida/ida_disk.c optional ida dev/ida/ida_pci.c optional ida pci dev/iicbus/acpi_iicbus.c optional acpi iicbus | acpi compat_linuxkpi dev/iicbus/icee.c optional icee dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/iicbus/iic_recover_bus.c optional iicbus | compat_linuxkpi dev/iicbus/iicbb.c optional iicbb | compat_linuxkpi dev/iicbus/iicbb_if.m optional iicbb | compat_linuxkpi dev/iicbus/iicbus.c optional iicbus | compat_linuxkpi dev/iicbus/iicbus_if.m optional iicbus | compat_linuxkpi dev/iicbus/iichid.c optional iichid acpi hid iicbus dev/iicbus/iiconf.c optional iicbus | compat_linuxkpi dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" dev/iicbus/adc/ad7418.c optional ad7418 dev/iicbus/adc/ads111x.c optional ads111x dev/iicbus/adc/pcf8591.c optional pcf8591 dev/iicbus/controller/opencores/iicoc.c optional iicoc dev/iicbus/controller/opencores/iicoc_fdt.c optional iicoc fdt dev/iicbus/controller/opencores/iicoc_pci.c optional iicoc pci dev/iicbus/mux/iicmux.c optional iicmux dev/iicbus/mux/iicmux_if.m optional iicmux dev/iicbus/mux/iic_gpiomux.c optional iic_gpiomux fdt dev/iicbus/mux/ltc430x.c optional ltc430x dev/iicbus/mux/pca954x.c optional pca954x iicbus iicmux dev/iicbus/ofw_iicbus.c optional fdt iicbus dev/iicbus/ofw_iicbus_if.m optional fdt iicbus dev/iicbus/rtc/ds1307.c optional ds1307 dev/iicbus/rtc/ds13rtc.c optional ds13rtc | ds133x | ds1374 dev/iicbus/rtc/ds1672.c optional ds1672 dev/iicbus/rtc/ds3231.c optional ds3231 dev/iicbus/rtc/isl12xx.c optional isl12xx dev/iicbus/rtc/nxprtc.c optional nxprtc | pcf8563 dev/iicbus/rtc/pcf85063.c optional pcf85063 iicbus fdt dev/iicbus/rtc/rtc8583.c optional rtc8583 dev/iicbus/rtc/rv3032.c optional rv3032 iicbus fdt dev/iicbus/rtc/rx8803.c optional rx8803 iicbus fdt dev/iicbus/rtc/s35390a.c optional s35390a dev/iicbus/sensor/htu21.c optional htu21 dev/iicbus/sensor/lm75.c optional lm75 dev/iicbus/sensor/max44009.c optional max44009 dev/iicbus/gpio/pcf8574.c optional pcf8574 dev/iicbus/gpio/tca64xx.c optional tca64xx fdt gpio dev/iicbus/pmic/fan53555.c optional fan53555 fdt | tcs4525 fdt dev/iicbus/pmic/silergy/sy8106a.c optional sy8106a fdt dev/iicbus/pmic/silergy/syr827.c optional syr827 fdt dev/igc/if_igc.c optional igc iflib pci dev/igc/igc_api.c optional igc iflib pci dev/igc/igc_base.c optional igc iflib pci dev/igc/igc_i225.c optional igc iflib pci dev/igc/igc_mac.c optional igc iflib pci dev/igc/igc_nvm.c optional igc iflib pci dev/igc/igc_phy.c optional igc iflib pci dev/igc/igc_txrx.c optional igc iflib pci dev/intpm/intpm.c optional intpm pci # XXX Work around clang warning, until maintainer approves fix. dev/ips/ips.c optional ips \ compile-with "${NORMAL_C} ${NO_WSOMETIMES_UNINITIALIZED}" dev/ips/ips_commands.c optional ips dev/ips/ips_disk.c optional ips dev/ips/ips_ioctl.c optional ips dev/ips/ips_pci.c optional ips pci dev/ipw/if_ipw.c optional ipw ipwbssfw.c optional ipwbssfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_bss.fw:ipw_bss:130 -lintel_ipw -mipw_bss -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "ipwbssfw.c" ipw_bss.fwo optional ipwbssfw | ipwfw \ dependency "ipw_bss.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "ipw_bss.fwo" ipw_bss.fw optional ipwbssfw | ipwfw \ dependency "$S/contrib/dev/ipw/ipw2100-1.3.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_bss.fw" ipwibssfw.c optional ipwibssfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_ibss.fw:ipw_ibss:130 -lintel_ipw -mipw_ibss -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "ipwibssfw.c" ipw_ibss.fwo optional ipwibssfw | ipwfw \ dependency "ipw_ibss.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "ipw_ibss.fwo" ipw_ibss.fw optional ipwibssfw | ipwfw \ dependency "$S/contrib/dev/ipw/ipw2100-1.3-i.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_ibss.fw" ipwmonitorfw.c optional ipwmonitorfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_monitor.fw:ipw_monitor:130 -lintel_ipw -mipw_monitor -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "ipwmonitorfw.c" ipw_monitor.fwo optional ipwmonitorfw | ipwfw \ dependency "ipw_monitor.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "ipw_monitor.fwo" ipw_monitor.fw optional ipwmonitorfw | ipwfw \ dependency "$S/contrib/dev/ipw/ipw2100-1.3-p.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" dev/iscsi/icl.c optional iscsi dev/iscsi/icl_conn_if.m optional cfiscsi | iscsi dev/iscsi/icl_soft.c optional iscsi dev/iscsi/icl_soft_proxy.c optional iscsi dev/iscsi/iscsi.c optional iscsi scbus dev/ismt/ismt.c optional ismt dev/isl/isl.c optional isl iicbus dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_library.c optional isp dev/isp/isp_pci.c optional isp pci dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/iwi/if_iwi.c optional iwi iwibssfw.c optional iwibssfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_bss.fw:iwi_bss:300 -lintel_iwi -miwi_bss -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwibssfw.c" iwi_bss.fwo optional iwibssfw | iwifw \ dependency "iwi_bss.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwi_bss.fwo" iwi_bss.fw optional iwibssfw | iwifw \ dependency "$S/contrib/dev/iwi/ipw2200-bss.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwi_bss.fw" iwiibssfw.c optional iwiibssfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_ibss.fw:iwi_ibss:300 -lintel_iwi -miwi_ibss -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwiibssfw.c" iwi_ibss.fwo optional iwiibssfw | iwifw \ dependency "iwi_ibss.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwi_ibss.fwo" iwi_ibss.fw optional iwiibssfw | iwifw \ dependency "$S/contrib/dev/iwi/ipw2200-ibss.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwi_ibss.fw" iwimonitorfw.c optional iwimonitorfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_monitor.fw:iwi_monitor:300 -lintel_iwi -miwi_monitor -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwimonitorfw.c" iwi_monitor.fwo optional iwimonitorfw | iwifw \ dependency "iwi_monitor.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwi_monitor.fwo" iwi_monitor.fw optional iwimonitorfw | iwifw \ dependency "$S/contrib/dev/iwi/ipw2200-sniffer.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwi_monitor.fw" dev/iwm/if_iwm.c optional iwm dev/iwm/if_iwm_7000.c optional iwm dev/iwm/if_iwm_8000.c optional iwm dev/iwm/if_iwm_9000.c optional iwm dev/iwm/if_iwm_9260.c optional iwm dev/iwm/if_iwm_binding.c optional iwm dev/iwm/if_iwm_fw.c optional iwm dev/iwm/if_iwm_led.c optional iwm dev/iwm/if_iwm_mac_ctxt.c optional iwm dev/iwm/if_iwm_notif_wait.c optional iwm dev/iwm/if_iwm_pcie_trans.c optional iwm dev/iwm/if_iwm_phy_ctxt.c optional iwm dev/iwm/if_iwm_phy_db.c optional iwm dev/iwm/if_iwm_power.c optional iwm dev/iwm/if_iwm_scan.c optional iwm dev/iwm/if_iwm_sf.c optional iwm dev/iwm/if_iwm_sta.c optional iwm dev/iwm/if_iwm_time_event.c optional iwm dev/iwm/if_iwm_util.c optional iwm iwm3160fw.c optional iwm3160fw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm3160.fw:iwm3160fw -miwm3160fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm3160fw.c" iwm3160fw.fwo optional iwm3160fw | iwmfw \ dependency "iwm3160.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm3160fw.fwo" iwm3160.fw optional iwm3160fw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-3160-17.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm3160.fw" iwm3168fw.c optional iwm3168fw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm3168.fw:iwm3168fw -miwm3168fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm3168fw.c" iwm3168fw.fwo optional iwm3168fw | iwmfw \ dependency "iwm3168.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm3168fw.fwo" iwm3168.fw optional iwm3168fw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-3168-22.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm3168.fw" iwm7260fw.c optional iwm7260fw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm7260.fw:iwm7260fw -miwm7260fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm7260fw.c" iwm7260fw.fwo optional iwm7260fw | iwmfw \ dependency "iwm7260.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm7260fw.fwo" iwm7260.fw optional iwm7260fw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-7260-17.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm7260.fw" iwm7265fw.c optional iwm7265fw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm7265.fw:iwm7265fw -miwm7265fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm7265fw.c" iwm7265fw.fwo optional iwm7265fw | iwmfw \ dependency "iwm7265.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm7265fw.fwo" iwm7265.fw optional iwm7265fw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-7265-17.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm7265.fw" iwm7265Dfw.c optional iwm7265Dfw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm7265D.fw:iwm7265Dfw -miwm7265Dfw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm7265Dfw.c" iwm7265Dfw.fwo optional iwm7265Dfw | iwmfw \ dependency "iwm7265D.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm7265Dfw.fwo" iwm7265D.fw optional iwm7265Dfw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-7265D-17.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm7265D.fw" iwm8000Cfw.c optional iwm8000Cfw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm8000C.fw:iwm8000Cfw -miwm8000Cfw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm8000Cfw.c" iwm8000Cfw.fwo optional iwm8000Cfw | iwmfw \ dependency "iwm8000C.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm8000Cfw.fwo" iwm8000C.fw optional iwm8000Cfw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-8000C-16.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm8000C.fw" iwm8265.fw optional iwm8265fw | iwmfw \ dependency "$S/contrib/dev/iwm/iwm-8265-22.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwm8265.fw" iwm8265fw.c optional iwm8265fw | iwmfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwm8265.fw:iwm8265fw -miwm8265fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwm8265fw.c" iwm8265fw.fwo optional iwm8265fw | iwmfw \ dependency "iwm8265.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwm8265fw.fwo" dev/iwn/if_iwn.c optional iwn iwn1000fw.c optional iwn1000fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn1000.fw:iwn1000fw -miwn1000fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn1000fw.c" iwn1000fw.fwo optional iwn1000fw | iwnfw \ dependency "iwn1000.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn1000fw.fwo" iwn1000.fw optional iwn1000fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-1000-39.31.5.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn1000.fw" iwn100fw.c optional iwn100fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn100.fw:iwn100fw -miwn100fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn100fw.c" iwn100fw.fwo optional iwn100fw | iwnfw \ dependency "iwn100.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn100fw.fwo" iwn100.fw optional iwn100fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-100-39.31.5.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn100.fw" iwn105fw.c optional iwn105fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn105.fw:iwn105fw -miwn105fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn105fw.c" iwn105fw.fwo optional iwn105fw | iwnfw \ dependency "iwn105.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn105fw.fwo" iwn105.fw optional iwn105fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-105-6-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn105.fw" iwn135fw.c optional iwn135fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn135.fw:iwn135fw -miwn135fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn135fw.c" iwn135fw.fwo optional iwn135fw | iwnfw \ dependency "iwn135.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn135fw.fwo" iwn135.fw optional iwn135fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-135-6-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn135.fw" iwn2000fw.c optional iwn2000fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn2000.fw:iwn2000fw -miwn2000fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn2000fw.c" iwn2000fw.fwo optional iwn2000fw | iwnfw \ dependency "iwn2000.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn2000fw.fwo" iwn2000.fw optional iwn2000fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-2000-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn2000.fw" iwn2030fw.c optional iwn2030fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn2030.fw:iwn2030fw -miwn2030fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn2030fw.c" iwn2030fw.fwo optional iwn2030fw | iwnfw \ dependency "iwn2030.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn2030fw.fwo" iwn2030.fw optional iwn2030fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwnwifi-2030-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn2030.fw" iwn4965fw.c optional iwn4965fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn4965.fw:iwn4965fw -miwn4965fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn4965fw.c" iwn4965fw.fwo optional iwn4965fw | iwnfw \ dependency "iwn4965.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn4965fw.fwo" iwn4965.fw optional iwn4965fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-4965-228.61.2.24.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn4965.fw" iwn5000fw.c optional iwn5000fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn5000.fw:iwn5000fw -miwn5000fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn5000fw.c" iwn5000fw.fwo optional iwn5000fw | iwnfw \ dependency "iwn5000.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn5000fw.fwo" iwn5000.fw optional iwn5000fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-5000-8.83.5.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn5000.fw" iwn5150fw.c optional iwn5150fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn5150.fw:iwn5150fw -miwn5150fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn5150fw.c" iwn5150fw.fwo optional iwn5150fw | iwnfw \ dependency "iwn5150.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn5150fw.fwo" iwn5150.fw optional iwn5150fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-5150-8.24.2.2.fw.uu"\ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn5150.fw" iwn6000fw.c optional iwn6000fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn6000.fw:iwn6000fw -miwn6000fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn6000fw.c" iwn6000fw.fwo optional iwn6000fw | iwnfw \ dependency "iwn6000.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn6000fw.fwo" iwn6000.fw optional iwn6000fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-6000-9.221.4.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn6000.fw" iwn6000g2afw.c optional iwn6000g2afw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn6000g2a.fw:iwn6000g2afw -miwn6000g2afw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn6000g2afw.c" iwn6000g2afw.fwo optional iwn6000g2afw | iwnfw \ dependency "iwn6000g2a.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn6000g2afw.fwo" iwn6000g2a.fw optional iwn6000g2afw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-6000g2a-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn6000g2a.fw" iwn6000g2bfw.c optional iwn6000g2bfw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn6000g2b.fw:iwn6000g2bfw -miwn6000g2bfw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn6000g2bfw.c" iwn6000g2bfw.fwo optional iwn6000g2bfw | iwnfw \ dependency "iwn6000g2b.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn6000g2bfw.fwo" iwn6000g2b.fw optional iwn6000g2bfw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-6000g2b-18.168.6.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn6000g2b.fw" iwn6050fw.c optional iwn6050fw | iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn6050.fw:iwn6050fw -miwn6050fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "iwn6050fw.c" iwn6050fw.fwo optional iwn6050fw | iwnfw \ dependency "iwn6050.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "iwn6050fw.fwo" iwn6050.fw optional iwn6050fw | iwnfw \ dependency "$S/contrib/dev/iwn/iwlwifi-6050-41.28.5.1.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "iwn6050.fw" dev/ixgbe/if_ix.c optional ix inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe -DSMP" dev/ixgbe/if_ixv.c optional ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe -DSMP" dev/ixgbe/if_bypass.c optional ix inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/if_fdir.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/if_sriov.c optional ix inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ix_txrx.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_osdep.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_phy.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_api.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_common.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_mbx.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_vf.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_82598.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_82599.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_x540.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_x550.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_dcb.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_dcb_82598.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_dcb_82599.c optional ix inet | ixv inet \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/jedec_dimm/jedec_dimm.c optional jedec_dimm smbus dev/jme/if_jme.c optional jme pci dev/kbd/kbd.c optional atkbd | pckbd | sc | ukbd | vt | hkbd dev/kbdmux/kbdmux.c optional kbdmux dev/ksyms/ksyms.c optional ksyms dev/le/am7990.c optional le dev/le/am79900.c optional le dev/le/if_le_pci.c optional le pci dev/le/lance.c optional le dev/led/led.c standard dev/lge/if_lge.c optional lge dev/liquidio/base/cn23xx_pf_device.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_console.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_ctrl.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_device.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_droq.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_mem_ops.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_request_manager.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/base/lio_response_manager.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_core.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_ioctl.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_main.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_rss.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_rxtx.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" dev/liquidio/lio_sysctl.c optional lio \ compile-with "${NORMAL_C} \ -I$S/dev/liquidio -I$S/dev/liquidio/base -DSMP" lio.c optional lio \ compile-with "${AWK} -f $S/tools/fw_stub.awk lio_23xx_nic.bin.fw:lio_23xx_nic.bin -mlio_23xx_nic.bin -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "lio.c" lio_23xx_nic.bin.fw.fwo optional lio \ dependency "lio_23xx_nic.bin.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "lio_23xx_nic.bin.fw.fwo" lio_23xx_nic.bin.fw optional lio \ dependency "$S/contrib/dev/liquidio/lio_23xx_nic.bin.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "lio_23xx_nic.bin.fw" dev/malo/if_malo.c optional malo dev/malo/if_malohal.c optional malo dev/malo/if_malo_pci.c optional malo pci dev/md/md.c optional md dev/mdio/mdio_if.m optional miiproxy | mdio dev/mdio/mdio.c optional miiproxy | mdio dev/mem/memdev.c optional mem dev/mem/memutil.c optional mem dev/mfi/mfi.c optional mfi dev/mfi/mfi_debug.c optional mfi dev/mfi/mfi_pci.c optional mfi pci dev/mfi/mfi_disk.c optional mfi dev/mfi/mfi_syspd.c optional mfi dev/mfi/mfi_tbolt.c optional mfi dev/mfi/mfi_cam.c optional mfip scbus dev/mii/acphy.c optional miibus | acphy dev/mii/amphy.c optional miibus | amphy dev/mii/atphy.c optional miibus | atphy dev/mii/axphy.c optional miibus | axphy dev/mii/bmtphy.c optional miibus | bmtphy dev/mii/brgphy.c optional miibus | brgphy dev/mii/ciphy.c optional miibus | ciphy dev/mii/dp83822phy.c optional miibus | dp83822phy dev/mii/dp83867phy.c optional miibus | dp83867phy dev/mii/e1000phy.c optional miibus | e1000phy dev/mii/gentbi.c optional miibus | gentbi dev/mii/icsphy.c optional miibus | icsphy dev/mii/ip1000phy.c optional miibus | ip1000phy dev/mii/jmphy.c optional miibus | jmphy dev/mii/lxtphy.c optional miibus | lxtphy dev/mii/mcommphy.c optional miibus | mcommphy dev/mii/micphy.c optional miibus fdt | micphy fdt dev/mii/mii.c optional miibus | mii dev/mii/mii_bitbang.c optional miibus | mii_bitbang dev/mii/mii_physubr.c optional miibus | mii dev/mii/mii_fdt.c optional miibus fdt | mii fdt dev/mii/miibus_if.m optional miibus | mii dev/mii/mv88e151x.c optional miibus | mv88e151x dev/mii/nsgphy.c optional miibus | nsgphy dev/mii/nsphy.c optional miibus | nsphy dev/mii/nsphyter.c optional miibus | nsphyter dev/mii/pnaphy.c optional miibus | pnaphy dev/mii/qsphy.c optional miibus | qsphy dev/mii/rdcphy.c optional miibus | rdcphy dev/mii/rgephy.c optional miibus | rgephy dev/mii/rlphy.c optional miibus | rlphy dev/mii/rlswitch.c optional rlswitch dev/mii/smcphy.c optional miibus | smcphy dev/mii/smscphy.c optional miibus | smscphy dev/mii/tdkphy.c optional miibus | tdkphy dev/mii/truephy.c optional miibus | truephy dev/mii/ukphy.c optional miibus | mii dev/mii/ukphy_subr.c optional miibus | mii dev/mii/vscphy.c optional miibus | vscphy dev/mii/xmphy.c optional miibus | xmphy dev/mlxfw/mlxfw_fsm.c optional mlxfw \ compile-with "${MLXFW_C}" dev/mlxfw/mlxfw_mfa2.c optional mlxfw \ compile-with "${MLXFW_C}" dev/mlxfw/mlxfw_mfa2_tlv_multi.c optional mlxfw \ compile-with "${MLXFW_C}" dev/mlx/mlx.c optional mlx dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx pci dev/mmc/mmc_subr.c optional mmc | mmcsd !mmccam dev/mmc/mmc.c optional mmc !mmccam dev/mmc/mmcbr_if.m standard dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd !mmccam dev/mmc/mmc_fdt_helpers.c optional mmc regulator clk fdt | mmccam regulator clk fdt dev/mmc/mmc_helpers.c optional mmc gpio regulator clk | mmccam gpio regulator clk dev/mmc/mmc_pwrseq.c optional mmc clk regulator fdt | mmccam clk regulator fdt dev/mmc/mmc_pwrseq_if.m optional mmc clk regulator fdt | mmccam clk regulator fdt dev/mmcnull/mmcnull.c optional mmcnull dev/mpr/mpr.c optional mpr dev/mpr/mpr_config.c optional mpr # XXX Work around clang warning, until maintainer approves fix. dev/mpr/mpr_mapping.c optional mpr \ compile-with "${NORMAL_C} ${NO_WSOMETIMES_UNINITIALIZED}" dev/mpr/mpr_pci.c optional mpr pci dev/mpr/mpr_sas.c optional mpr \ compile-with "${NORMAL_C} ${NO_WUNNEEDED_INTERNAL_DECL}" dev/mpr/mpr_sas_lsi.c optional mpr dev/mpr/mpr_table.c optional mpr dev/mpr/mpr_user.c optional mpr dev/mps/mps.c optional mps dev/mps/mps_config.c optional mps # XXX Work around clang warning, until maintainer approves fix. dev/mps/mps_mapping.c optional mps \ compile-with "${NORMAL_C} ${NO_WSOMETIMES_UNINITIALIZED}" dev/mps/mps_pci.c optional mps pci dev/mps/mps_sas.c optional mps \ compile-with "${NORMAL_C} ${NO_WUNNEEDED_INTERNAL_DECL}" dev/mps/mps_sas_lsi.c optional mps dev/mps/mps_table.c optional mps dev/mps/mps_user.c optional mps dev/mpt/mpt.c optional mpt dev/mpt/mpt_cam.c optional mpt dev/mpt/mpt_debug.c optional mpt dev/mpt/mpt_pci.c optional mpt pci dev/mpt/mpt_raid.c optional mpt dev/mpt/mpt_user.c optional mpt dev/mrsas/mrsas.c optional mrsas dev/mrsas/mrsas_cam.c optional mrsas dev/mrsas/mrsas_ioctl.c optional mrsas dev/mrsas/mrsas_fp.c optional mrsas dev/msk/if_msk.c optional msk dev/mvs/mvs.c optional mvs dev/mvs/mvs_if.m optional mvs dev/mvs/mvs_pci.c optional mvs pci dev/mwl/if_mwl.c optional mwl dev/mwl/if_mwl_pci.c optional mwl pci dev/mwl/mwlhal.c optional mwl mwlfw.c optional mwlfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk mw88W8363.fw:mw88W8363fw mwlboot.fw:mwlboot -mmwl -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "mwlfw.c" mw88W8363.fwo optional mwlfw \ dependency "mw88W8363.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "mw88W8363.fwo" mw88W8363.fw optional mwlfw \ dependency "$S/contrib/dev/mwl/mw88W8363.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "mw88W8363.fw" mwlboot.fwo optional mwlfw \ dependency "mwlboot.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "mwlboot.fwo" mwlboot.fw optional mwlfw \ dependency "$S/contrib/dev/mwl/mwlboot.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "mwlboot.fw" dev/mxge/if_mxge.c optional mxge pci dev/mxge/mxge_eth_z8e.c optional mxge pci dev/mxge/mxge_ethp_z8e.c optional mxge pci dev/mxge/mxge_rss_eth_z8e.c optional mxge pci dev/mxge/mxge_rss_ethp_z8e.c optional mxge pci dev/my/if_my.c optional my dev/netmap/if_ptnet.c optional netmap inet dev/netmap/netmap.c optional netmap dev/netmap/netmap_bdg.c optional netmap dev/netmap/netmap_freebsd.c optional netmap dev/netmap/netmap_generic.c optional netmap dev/netmap/netmap_kloop.c optional netmap dev/netmap/netmap_legacy.c optional netmap dev/netmap/netmap_mbq.c optional netmap dev/netmap/netmap_mem2.c optional netmap dev/netmap/netmap_monitor.c optional netmap dev/netmap/netmap_null.c optional netmap dev/netmap/netmap_offloadings.c optional netmap dev/netmap/netmap_pipe.c optional netmap dev/netmap/netmap_vale.c optional netmap # compile-with "${NORMAL_C} -Wconversion -Wextra" dev/nfsmb/nfsmb.c optional nfsmb pci dev/nge/if_nge.c optional nge dev/nmdm/nmdm.c optional nmdm dev/null/null.c standard dev/nvd/nvd.c optional nvd nvme dev/nvme/nvme.c optional nvme dev/nvme/nvme_ahci.c optional nvme ahci dev/nvme/nvme_ctrlr.c optional nvme dev/nvme/nvme_ctrlr_cmd.c optional nvme dev/nvme/nvme_ns.c optional nvme dev/nvme/nvme_ns_cmd.c optional nvme dev/nvme/nvme_pci.c optional nvme pci dev/nvme/nvme_qpair.c optional nvme dev/nvme/nvme_sim.c optional nvme scbus dev/nvme/nvme_sysctl.c optional nvme dev/nvme/nvme_test.c optional nvme dev/nvme/nvme_util.c optional nvme dev/oce/oce_hw.c optional oce pci dev/oce/oce_if.c optional oce pci dev/oce/oce_mbox.c optional oce pci dev/oce/oce_queue.c optional oce pci dev/oce/oce_sysctl.c optional oce pci dev/oce/oce_util.c optional oce pci dev/ocs_fc/ocs_gendump.c optional ocs_fc pci dev/ocs_fc/ocs_pci.c optional ocs_fc pci dev/ocs_fc/ocs_ioctl.c optional ocs_fc pci dev/ocs_fc/ocs_os.c optional ocs_fc pci dev/ocs_fc/ocs_utils.c optional ocs_fc pci dev/ocs_fc/ocs_hw.c optional ocs_fc pci dev/ocs_fc/ocs_hw_queues.c optional ocs_fc pci dev/ocs_fc/sli4.c optional ocs_fc pci dev/ocs_fc/ocs_sm.c optional ocs_fc pci dev/ocs_fc/ocs_device.c optional ocs_fc pci dev/ocs_fc/ocs_xport.c optional ocs_fc pci dev/ocs_fc/ocs_domain.c optional ocs_fc pci dev/ocs_fc/ocs_sport.c optional ocs_fc pci dev/ocs_fc/ocs_els.c optional ocs_fc pci dev/ocs_fc/ocs_fabric.c optional ocs_fc pci dev/ocs_fc/ocs_io.c optional ocs_fc pci dev/ocs_fc/ocs_node.c optional ocs_fc pci dev/ocs_fc/ocs_scsi.c optional ocs_fc pci dev/ocs_fc/ocs_unsol.c optional ocs_fc pci dev/ocs_fc/ocs_ddump.c optional ocs_fc pci dev/ocs_fc/ocs_mgmt.c optional ocs_fc pci dev/ocs_fc/ocs_cam.c optional ocs_fc pci dev/ofw/ofw_bus_if.m optional fdt dev/ofw/ofw_bus_subr.c optional fdt dev/ofw/ofw_cpu.c optional fdt dev/ofw/ofw_fdt.c optional fdt dev/ofw/ofw_firmware.c optional fdt dev/ofw/ofw_if.m optional fdt dev/ofw/ofw_graph.c optional fdt dev/ofw/ofw_subr.c optional fdt dev/ofw/ofwbus.c optional fdt dev/ofw/openfirm.c optional fdt dev/ofw/openfirmio.c optional fdt dev/ow/ow.c optional ow \ dependency "owll_if.h" \ dependency "own_if.h" dev/ow/owll_if.m optional ow dev/ow/own_if.m optional ow dev/ow/ow_temp.c optional ow_temp dev/ow/owc_gpiobus.c optional owc gpio dev/pbio/pbio.c optional pbio isa dev/pccbb/pccbb.c optional cbb dev/pccbb/pccbb_pci.c optional cbb pci dev/pcf/pcf.c optional pcf dev/pci/fixup_pci.c optional pci dev/pci/hostb_pci.c optional pci dev/pci/ignore_pci.c optional pci dev/pci/isa_pci.c optional pci isa dev/pci/pci.c optional pci dev/pci/pci_if.m standard dev/pci/pci_iov.c optional pci pci_iov dev/pci/pci_iov_if.m standard dev/pci/pci_iov_schema.c optional pci pci_iov dev/pci/pci_pci.c optional pci dev/pci/pci_subr.c optional pci dev/pci/pci_user.c optional pci dev/pci/pcib_if.m standard dev/pci/pcib_support.c standard dev/pci/vga_pci.c optional pci dev/pms/freebsd/driver/ini/src/agtiapi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sadisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/mpi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/saframe.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sahw.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sainit.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/saint.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sampicmd.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sampirsp.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/saphy.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/saport.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sasata.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sasmp.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sassp.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/satimer.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/sautil.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/saioctlcmd.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sallsdk/spc/mpidebug.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dminit.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dmsmp.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dmdisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dmport.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dmtimer.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/discovery/dm/dmmisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/sminit.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/smmisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/smsat.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/smsatcb.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/smsathw.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/sat/src/smtimer.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdinit.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdmisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdesgl.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdport.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdint.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdioctl.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdhw.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/ossacmnapi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tddmcmnapi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdsmcmnapi.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/common/tdtimers.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sas/ini/itdio.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sas/ini/itdcb.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sas/ini/itdinit.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sas/ini/itddisc.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sata/host/sat.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sata/host/ossasat.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/pms/RefTisa/tisa/sassata/sata/host/sathw.c optional pmspcv \ compile-with "${NORMAL_C} -Wunused-variable -Woverflow -Wparentheses -w" dev/ppbus/if_plip.c optional plip dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/pcfclock.c optional pcfclock dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppbus_if.m optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppc/ppc.c optional ppc dev/ppc/ppc_acpi.c optional ppc acpi dev/ppc/ppc_isa.c optional ppc isa dev/ppc/ppc_pci.c optional ppc pci dev/ppc/ppc_puc.c optional ppc puc dev/proto/proto_bus_isa.c optional proto acpi | proto isa dev/proto/proto_bus_pci.c optional proto pci dev/proto/proto_busdma.c optional proto dev/proto/proto_core.c optional proto dev/pst/pst-iop.c optional pst dev/pst/pst-pci.c optional pst pci dev/pst/pst-raid.c optional pst dev/pty/pty.c optional pty dev/puc/puc.c optional puc dev/puc/puc_cfg.c optional puc dev/puc/puc_pci.c optional puc pci dev/pwm/pwmc.c optional pwm | pwmc dev/pwm/pwmbus.c optional pwm | pwmbus dev/pwm/pwmbus_if.m optional pwm | pwmbus dev/pwm/ofw_pwm.c optional pwm fdt | pwmbus fdt dev/pwm/ofw_pwmbus.c optional pwm fdt | pwmbus fdt dev/pwm/pwm_backlight.c optional pwm pwm_backlight fdt backlight dev/quicc/quicc_core.c optional quicc dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral dev/ral/rt2860.c optional ral dev/ral/if_ral_pci.c optional ral pci rt2561fw.c optional rt2561fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2561.fw:rt2561fw -mrt2561 -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rt2561fw.c" rt2561fw.fwo optional rt2561fw | ralfw \ dependency "rt2561.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rt2561fw.fwo" rt2561.fw optional rt2561fw | ralfw \ dependency "$S/contrib/dev/ral/rt2561.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2561.fw" rt2561sfw.c optional rt2561sfw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2561s.fw:rt2561sfw -mrt2561s -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rt2561sfw.c" rt2561sfw.fwo optional rt2561sfw | ralfw \ dependency "rt2561s.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rt2561sfw.fwo" rt2561s.fw optional rt2561sfw | ralfw \ dependency "$S/contrib/dev/ral/rt2561s.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2561s.fw" rt2661fw.c optional rt2661fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2661.fw:rt2661fw -mrt2661 -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rt2661fw.c" rt2661fw.fwo optional rt2661fw | ralfw \ dependency "rt2661.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rt2661fw.fwo" rt2661.fw optional rt2661fw | ralfw \ dependency "$S/contrib/dev/ral/rt2661.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2661.fw" rt2860fw.c optional rt2860fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2860.fw:rt2860fw -mrt2860 -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rt2860fw.c" rt2860fw.fwo optional rt2860fw | ralfw \ dependency "rt2860.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rt2860fw.fwo" rt2860.fw optional rt2860fw | ralfw \ dependency "$S/contrib/dev/ral/rt2860.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2860.fw" dev/random/random_infra.c standard dev/random/random_harvestq.c standard dev/random/randomdev.c optional !random_loadable dev/random/fenestrasX/fx_brng.c optional !random_loadable random_fenestrasx dev/random/fenestrasX/fx_main.c optional !random_loadable random_fenestrasx \ compile-with "${NORMAL_C} -I$S/crypto/blake2" dev/random/fenestrasX/fx_pool.c optional !random_loadable random_fenestrasx \ compile-with "${NORMAL_C} -I$S/crypto/blake2" dev/random/fenestrasX/fx_rng.c optional !random_loadable random_fenestrasx \ compile-with "${NORMAL_C} -I$S/crypto/blake2" dev/random/fortuna.c optional !random_loadable !random_fenestrasx dev/random/hash.c optional !random_loadable dev/rccgpio/rccgpio.c optional rccgpio gpio dev/re/if_re.c optional re dev/rl/if_rl.c optional rl pci dev/rndtest/rndtest.c optional rndtest # dev/rtsx/rtsx.c optional rtsx pci # dev/rtwn/if_rtwn.c optional rtwn dev/rtwn/if_rtwn_beacon.c optional rtwn dev/rtwn/if_rtwn_calib.c optional rtwn dev/rtwn/if_rtwn_cam.c optional rtwn dev/rtwn/if_rtwn_efuse.c optional rtwn dev/rtwn/if_rtwn_fw.c optional rtwn dev/rtwn/if_rtwn_rx.c optional rtwn dev/rtwn/if_rtwn_task.c optional rtwn dev/rtwn/if_rtwn_tx.c optional rtwn # dev/rtwn/pci/rtwn_pci_attach.c optional rtwn_pci pci dev/rtwn/pci/rtwn_pci_reg.c optional rtwn_pci pci dev/rtwn/pci/rtwn_pci_rx.c optional rtwn_pci pci dev/rtwn/pci/rtwn_pci_tx.c optional rtwn_pci pci # dev/rtwn/usb/rtwn_usb_attach.c optional rtwn_usb dev/rtwn/usb/rtwn_usb_ep.c optional rtwn_usb dev/rtwn/usb/rtwn_usb_reg.c optional rtwn_usb dev/rtwn/usb/rtwn_usb_rx.c optional rtwn_usb dev/rtwn/usb/rtwn_usb_tx.c optional rtwn_usb # RTL8188E dev/rtwn/rtl8188e/r88e_beacon.c optional rtwn dev/rtwn/rtl8188e/r88e_calib.c optional rtwn dev/rtwn/rtl8188e/r88e_chan.c optional rtwn dev/rtwn/rtl8188e/r88e_fw.c optional rtwn dev/rtwn/rtl8188e/r88e_init.c optional rtwn dev/rtwn/rtl8188e/r88e_led.c optional rtwn dev/rtwn/rtl8188e/r88e_tx.c optional rtwn dev/rtwn/rtl8188e/r88e_rf.c optional rtwn dev/rtwn/rtl8188e/r88e_rom.c optional rtwn dev/rtwn/rtl8188e/r88e_rx.c optional rtwn dev/rtwn/rtl8188e/pci/r88ee_attach.c optional rtwn_pci pci dev/rtwn/rtl8188e/pci/r88ee_init.c optional rtwn_pci pci dev/rtwn/rtl8188e/pci/r88ee_rx.c optional rtwn_pci pci dev/rtwn/rtl8188e/usb/r88eu_attach.c optional rtwn_usb dev/rtwn/rtl8188e/usb/r88eu_init.c optional rtwn_usb # RTL8192C dev/rtwn/rtl8192c/r92c_attach.c optional rtwn dev/rtwn/rtl8192c/r92c_beacon.c optional rtwn dev/rtwn/rtl8192c/r92c_calib.c optional rtwn dev/rtwn/rtl8192c/r92c_chan.c optional rtwn dev/rtwn/rtl8192c/r92c_fw.c optional rtwn dev/rtwn/rtl8192c/r92c_init.c optional rtwn dev/rtwn/rtl8192c/r92c_llt.c optional rtwn dev/rtwn/rtl8192c/r92c_rf.c optional rtwn dev/rtwn/rtl8192c/r92c_rom.c optional rtwn dev/rtwn/rtl8192c/r92c_rx.c optional rtwn dev/rtwn/rtl8192c/r92c_tx.c optional rtwn dev/rtwn/rtl8192c/pci/r92ce_attach.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_calib.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_fw.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_init.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_led.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_rx.c optional rtwn_pci pci dev/rtwn/rtl8192c/pci/r92ce_tx.c optional rtwn_pci pci dev/rtwn/rtl8192c/usb/r92cu_attach.c optional rtwn_usb dev/rtwn/rtl8192c/usb/r92cu_init.c optional rtwn_usb dev/rtwn/rtl8192c/usb/r92cu_led.c optional rtwn_usb dev/rtwn/rtl8192c/usb/r92cu_rx.c optional rtwn_usb dev/rtwn/rtl8192c/usb/r92cu_tx.c optional rtwn_usb # RTL8192E dev/rtwn/rtl8192e/r92e_chan.c optional rtwn dev/rtwn/rtl8192e/r92e_fw.c optional rtwn dev/rtwn/rtl8192e/r92e_init.c optional rtwn dev/rtwn/rtl8192e/r92e_led.c optional rtwn dev/rtwn/rtl8192e/r92e_rf.c optional rtwn dev/rtwn/rtl8192e/r92e_rom.c optional rtwn dev/rtwn/rtl8192e/r92e_rx.c optional rtwn dev/rtwn/rtl8192e/usb/r92eu_attach.c optional rtwn_usb dev/rtwn/rtl8192e/usb/r92eu_init.c optional rtwn_usb # RTL8812A dev/rtwn/rtl8812a/r12a_beacon.c optional rtwn dev/rtwn/rtl8812a/r12a_calib.c optional rtwn dev/rtwn/rtl8812a/r12a_caps.c optional rtwn dev/rtwn/rtl8812a/r12a_chan.c optional rtwn dev/rtwn/rtl8812a/r12a_fw.c optional rtwn dev/rtwn/rtl8812a/r12a_init.c optional rtwn dev/rtwn/rtl8812a/r12a_led.c optional rtwn dev/rtwn/rtl8812a/r12a_rf.c optional rtwn dev/rtwn/rtl8812a/r12a_rom.c optional rtwn dev/rtwn/rtl8812a/r12a_rx.c optional rtwn dev/rtwn/rtl8812a/r12a_tx.c optional rtwn dev/rtwn/rtl8812a/usb/r12au_attach.c optional rtwn_usb dev/rtwn/rtl8812a/usb/r12au_init.c optional rtwn_usb dev/rtwn/rtl8812a/usb/r12au_rx.c optional rtwn_usb dev/rtwn/rtl8812a/usb/r12au_tx.c optional rtwn_usb # RTL8821A dev/rtwn/rtl8821a/r21a_beacon.c optional rtwn dev/rtwn/rtl8821a/r21a_calib.c optional rtwn dev/rtwn/rtl8821a/r21a_chan.c optional rtwn dev/rtwn/rtl8821a/r21a_fw.c optional rtwn dev/rtwn/rtl8821a/r21a_init.c optional rtwn dev/rtwn/rtl8821a/r21a_led.c optional rtwn dev/rtwn/rtl8821a/r21a_rom.c optional rtwn dev/rtwn/rtl8821a/r21a_rx.c optional rtwn dev/rtwn/rtl8821a/usb/r21au_attach.c optional rtwn_usb dev/rtwn/rtl8821a/usb/r21au_dfs.c optional rtwn_usb dev/rtwn/rtl8821a/usb/r21au_init.c optional rtwn_usb rtwn-rtl8188eefw.c optional rtwn-rtl8188eefw | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8188eefw.fw:rtwn-rtl8188eefw:111 -mrtwn-rtl8188eefw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8188eefw.c" rtwn-rtl8188eefw.fwo optional rtwn-rtl8188eefw | rtwnfw \ dependency "rtwn-rtl8188eefw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8188eefw.fwo" rtwn-rtl8188eefw.fw optional rtwn-rtl8188eefw | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8188eefw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8188eefw.fw" rtwn-rtl8188eufw.c optional rtwn-rtl8188eufw | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8188eufw.fw:rtwn-rtl8188eufw:111 -mrtwn-rtl8188eufw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8188eufw.c" rtwn-rtl8188eufw.fwo optional rtwn-rtl8188eufw | rtwnfw \ dependency "rtwn-rtl8188eufw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8188eufw.fwo" rtwn-rtl8188eufw.fw optional rtwn-rtl8188eufw | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8188eufw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8188eufw.fw" rtwn-rtl8192cfwE.c optional rtwn-rtl8192cfwE | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8192cfwE.fw:rtwn-rtl8192cfwE:111 -mrtwn-rtl8192cfwE -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8192cfwE.c" rtwn-rtl8192cfwE.fwo optional rtwn-rtl8192cfwE | rtwnfw \ dependency "rtwn-rtl8192cfwE.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8192cfwE.fwo" rtwn-rtl8192cfwE.fw optional rtwn-rtl8192cfwE | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8192cfwE.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8192cfwE.fw" rtwn-rtl8192cfwE_B.c optional rtwn-rtl8192cfwE_B | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8192cfwE_B.fw:rtwn-rtl8192cfwE_B:111 -mrtwn-rtl8192cfwE_B -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8192cfwE_B.c" rtwn-rtl8192cfwE_B.fwo optional rtwn-rtl8192cfwE_B | rtwnfw \ dependency "rtwn-rtl8192cfwE_B.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8192cfwE_B.fwo" rtwn-rtl8192cfwE_B.fw optional rtwn-rtl8192cfwE_B | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8192cfwE_B.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8192cfwE_B.fw" rtwn-rtl8192cfwT.c optional rtwn-rtl8192cfwT | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8192cfwT.fw:rtwn-rtl8192cfwT:111 -mrtwn-rtl8192cfwT -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8192cfwT.c" rtwn-rtl8192cfwT.fwo optional rtwn-rtl8192cfwT | rtwnfw \ dependency "rtwn-rtl8192cfwT.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8192cfwT.fwo" rtwn-rtl8192cfwT.fw optional rtwn-rtl8192cfwT | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8192cfwT.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8192cfwT.fw" rtwn-rtl8192cfwU.c optional rtwn-rtl8192cfwU | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8192cfwU.fw:rtwn-rtl8192cfwU:111 -mrtwn-rtl8192cfwU -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8192cfwU.c" rtwn-rtl8192cfwU.fwo optional rtwn-rtl8192cfwU | rtwnfw \ dependency "rtwn-rtl8192cfwU.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8192cfwU.fwo" rtwn-rtl8192cfwU.fw optional rtwn-rtl8192cfwU | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8192cfwU.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8192cfwU.fw" rtwn-rtl8192eufw.c optional rtwn-rtl8192eufw | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8192eufw.fw:rtwn-rtl8192eufw:111 -mrtwn-rtl8192eufw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8192eufw.c" rtwn-rtl8192eufw.fwo optional rtwn-rtl8192eufw | rtwnfw \ dependency "rtwn-rtl8192eufw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8192eufw.fwo" rtwn-rtl8192eufw.fw optional rtwn-rtl8192eufw | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8192eufw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8192eufw.fw" rtwn-rtl8812aufw.c optional rtwn-rtl8812aufw | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8812aufw.fw:rtwn-rtl8812aufw:111 -mrtwn-rtl8812aufw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8812aufw.c" rtwn-rtl8812aufw.fwo optional rtwn-rtl8812aufw | rtwnfw \ dependency "rtwn-rtl8812aufw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8812aufw.fwo" rtwn-rtl8812aufw.fw optional rtwn-rtl8812aufw | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8812aufw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8812aufw.fw" rtwn-rtl8821aufw.c optional rtwn-rtl8821aufw | rtwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rtwn-rtl8821aufw.fw:rtwn-rtl8821aufw:111 -mrtwn-rtl8821aufw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rtwn-rtl8821aufw.c" rtwn-rtl8821aufw.fwo optional rtwn-rtl8821aufw | rtwnfw \ dependency "rtwn-rtl8821aufw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rtwn-rtl8821aufw.fwo" rtwn-rtl8821aufw.fw optional rtwn-rtl8821aufw | rtwnfw \ dependency "$S/contrib/dev/rtwn/rtwn-rtl8821aufw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rtwn-rtl8821aufw.fw" dev/safe/safe.c optional safe dev/scc/scc_if.m optional scc dev/scc/scc_bfe_quicc.c optional scc quicc dev/scc/scc_core.c optional scc dev/scc/scc_dev_quicc.c optional scc quicc dev/scc/scc_dev_z8530.c optional scc dev/sdhci/sdhci.c optional sdhci dev/sdhci/sdhci_fdt.c optional sdhci fdt regulator clk dev/sdhci/sdhci_fdt_gpio.c optional sdhci fdt gpio dev/sdhci/sdhci_fsl_fdt.c optional sdhci fdt gpio regulator clk dev/sdhci/sdhci_if.m optional sdhci dev/sdhci/sdhci_acpi.c optional sdhci acpi dev/sdhci/sdhci_pci.c optional sdhci pci dev/sdio/sdio_if.m optional mmccam dev/sdio/sdio_subr.c optional mmccam dev/sdio/sdiob.c optional mmccam dev/sff/sff_if.m optional sff dev/sff/sfp_fdt.c optional sff fdt dev/sge/if_sge.c optional sge pci dev/siis/siis.c optional siis pci dev/sis/if_sis.c optional sis pci dev/sk/if_sk.c optional sk pci dev/smbios/smbios.c optional smbios dev/smbus/smb.c optional smb dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c optional smbus dev/smbus/smbus_if.m optional smbus dev/smc/if_smc.c optional smc dev/smc/if_smc_acpi.c optional smc acpi dev/smc/if_smc_fdt.c optional smc fdt dev/snp/snp.c optional snp dev/sound/clone.c optional sound dev/sound/unit.c optional sound dev/sound/pci/als4000.c optional snd_als4000 pci dev/sound/pci/atiixp.c optional snd_atiixp pci dev/sound/pci/cmi.c optional snd_cmi pci dev/sound/pci/cs4281.c optional snd_cs4281 pci dev/sound/pci/csa.c optional snd_csa pci dev/sound/pci/csapcm.c optional snd_csa pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci dev/sound/pci/emu10kx.c optional snd_emu10kx pci dev/sound/pci/emu10kx-pcm.c optional snd_emu10kx pci dev/sound/pci/emu10kx-midi.c optional snd_emu10kx pci dev/sound/pci/envy24.c optional snd_envy24 pci dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci dev/sound/pci/fm801.c optional snd_fm801 pci dev/sound/pci/ich.c optional snd_ich pci dev/sound/pci/maestro3.c optional snd_maestro3 pci dev/sound/pci/neomagic.c optional snd_neomagic pci dev/sound/pci/solo.c optional snd_solo pci dev/sound/pci/spicds.c optional snd_spicds pci dev/sound/pci/t4dwave.c optional snd_t4dwave pci dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci dev/sound/pci/vibes.c optional snd_vibes pci dev/sound/pci/hda/hdaa.c optional snd_hda pci dev/sound/pci/hda/hdaa_patches.c optional snd_hda pci dev/sound/pci/hda/hdac.c optional snd_hda pci dev/sound/pci/hda/hdac_if.m optional snd_hda pci dev/sound/pci/hda/hdacc.c optional snd_hda pci dev/sound/pci/hdspe.c optional snd_hdspe pci dev/sound/pci/hdspe-pcm.c optional snd_hdspe pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound dev/sound/pcm/buffer.c optional sound \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/channel.c optional sound dev/sound/pcm/channel_if.m optional sound dev/sound/pcm/dsp.c optional sound dev/sound/pcm/feeder.c optional sound dev/sound/pcm/feeder_chain.c optional sound dev/sound/pcm/feeder_eq.c optional sound \ dependency "feeder_eq_gen.h" \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_if.m optional sound dev/sound/pcm/feeder_format.c optional sound \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_matrix.c optional sound \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_mixer.c optional sound \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_rate.c optional sound \ dependency "feeder_rate_gen.h" \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_volume.c optional sound \ dependency "snd_fxdiv_gen.h" dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound dev/sound/pcm/sound.c optional sound dev/sound/pcm/vchan.c optional sound dev/sound/usb/uaudio.c optional snd_uaudio usb dev/sound/usb/uaudio_pcm.c optional snd_uaudio usb dev/sound/midi/midi.c optional sound dev/sound/midi/mpu401.c optional sound dev/sound/midi/mpu_if.m optional sound dev/sound/midi/mpufoi_if.m optional sound dev/sound/midi/sequencer.c optional sound dev/sound/midi/synth_if.m optional sound dev/spibus/acpi_spibus.c optional acpi spibus dev/spibus/ofw_spibus.c optional fdt spibus dev/spibus/spibus.c optional spibus \ dependency "spibus_if.h" dev/spibus/spigen.c optional spigen dev/spibus/spibus_if.m optional spibus dev/ste/if_ste.c optional ste pci dev/stge/if_stge.c optional stge dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/syscons/blank/blank_saver.c optional blank_saver dev/syscons/daemon/daemon_saver.c optional daemon_saver dev/syscons/dragon/dragon_saver.c optional dragon_saver dev/syscons/fade/fade_saver.c optional fade_saver dev/syscons/fire/fire_saver.c optional fire_saver dev/syscons/green/green_saver.c optional green_saver dev/syscons/logo/logo.c optional logo_saver dev/syscons/logo/logo_saver.c optional logo_saver dev/syscons/rain/rain_saver.c optional rain_saver dev/syscons/schistory.c optional sc dev/syscons/scmouse.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scterm-dumb.c optional sc !SC_NO_TERM_DUMB dev/syscons/scterm-sc.c optional sc !SC_NO_TERM_SC dev/syscons/scterm-teken.c optional sc !SC_NO_TERM_TEKEN dev/syscons/scvidctl.c optional sc dev/syscons/scvtb.c optional sc dev/syscons/snake/snake_saver.c optional snake_saver dev/syscons/star/star_saver.c optional star_saver dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc dev/syscons/warp/warp_saver.c optional warp_saver dev/tcp_log/tcp_log_dev.c optional tcp_blackbox inet | tcp_blackbox inet6 dev/tdfx/tdfx_pci.c optional tdfx pci dev/ti/if_ti.c optional ti pci dev/tws/tws.c optional tws dev/tws/tws_cam.c optional tws dev/tws/tws_hdm.c optional tws dev/tws/tws_services.c optional tws dev/tws/tws_user.c optional tws dev/uart/uart_bus_acpi.c optional uart acpi dev/uart/uart_bus_fdt.c optional uart fdt dev/uart/uart_bus_isa.c optional uart isa dev/uart/uart_bus_pci.c optional uart pci dev/uart/uart_bus_puc.c optional uart puc dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_cpu_acpi.c optional uart acpi dev/uart/uart_dbg.c optional uart gdb dev/uart/uart_dev_imx.c optional uart uart_imx fdt dev/uart/uart_dev_msm.c optional uart uart_msm fdt dev/uart/uart_dev_mvebu.c optional uart uart_mvebu fdt dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 | uart uart_snps dev/uart/uart_dev_pl011.c optional uart pl011 dev/uart/uart_dev_quicc.c optional uart quicc dev/uart/uart_dev_snps.c optional uart uart_snps fdt dev/uart/uart_dev_z8530.c optional uart uart_z8530 | uart scc dev/uart/uart_if.m optional uart dev/uart/uart_subr.c optional uart dev/uart/uart_tty.c optional uart # # USB controller drivers # dev/usb/controller/musb_otg.c optional musb dev/usb/controller/dwc_otg.c optional dwcotg dev/usb/controller/dwc_otg_fdt.c optional dwcotg fdt dev/usb/controller/dwc_otg_acpi.c optional dwcotg acpi dev/usb/controller/ehci.c optional ehci dev/usb/controller/ehci_msm.c optional ehci_msm fdt dev/usb/controller/ehci_pci.c optional ehci pci dev/usb/controller/ohci.c optional ohci dev/usb/controller/ohci_pci.c optional ohci pci dev/usb/controller/uhci.c optional uhci dev/usb/controller/uhci_pci.c optional uhci pci dev/usb/controller/xhci.c optional xhci dev/usb/controller/xhci_pci.c optional xhci pci dev/usb/controller/saf1761_otg.c optional saf1761otg dev/usb/controller/saf1761_otg_fdt.c optional saf1761otg fdt dev/usb/controller/uss820dci.c optional uss820dci dev/usb/controller/usb_controller.c optional usb # # USB storage drivers # dev/usb/storage/cfumass.c optional cfumass ctl dev/usb/storage/umass.c optional umass dev/usb/storage/urio.c optional urio dev/usb/storage/ustorage_fs.c optional usfs # # USB core # dev/usb/usb_busdma.c optional usb dev/usb/usb_core.c optional usb dev/usb/usb_debug.c optional usb dev/usb/usb_dev.c optional usb dev/usb/usb_device.c optional usb dev/usb/usb_dynamic.c optional usb dev/usb/usb_error.c optional usb dev/usb/usb_fdt_support.c optional usb fdt dev/usb/usb_generic.c optional usb dev/usb/usb_handle_request.c optional usb dev/usb/usb_hid.c optional usb dev/usb/usb_hub.c optional usb dev/usb/usb_hub_acpi.c optional uacpi acpi dev/usb/usb_if.m optional usb dev/usb/usb_lookup.c optional usb dev/usb/usb_mbuf.c optional usb dev/usb/usb_msctest.c optional usb dev/usb/usb_parse.c optional usb dev/usb/usb_pf.c optional usb dev/usb/usb_process.c optional usb dev/usb/usb_request.c optional usb dev/usb/usb_transfer.c optional usb dev/usb/usb_util.c optional usb # # USB network drivers # dev/usb/net/if_aue.c optional aue dev/usb/net/if_axe.c optional axe dev/usb/net/if_axge.c optional axge dev/usb/net/if_cdce.c optional cdce dev/usb/net/if_cdceem.c optional cdceem dev/usb/net/if_cue.c optional cue dev/usb/net/if_ipheth.c optional ipheth dev/usb/net/if_kue.c optional kue dev/usb/net/if_mos.c optional mos dev/usb/net/if_muge.c optional muge dev/usb/net/if_rue.c optional rue dev/usb/net/if_smsc.c optional smsc dev/usb/net/if_udav.c optional udav dev/usb/net/if_ure.c optional ure dev/usb/net/if_usie.c optional usie dev/usb/net/if_urndis.c optional urndis dev/usb/net/ruephy.c optional rue dev/usb/net/usb_ethernet.c optional uether | aue | axe | axge | cdce | \ cdceem | cue | ipheth | kue | mos | \ rue | smsc | udav | ure | urndis | muge dev/usb/net/uhso.c optional uhso # # USB WLAN drivers # dev/usb/wlan/if_rsu.c optional rsu rsu-rtl8712fw.c optional rsu-rtl8712fw | rsufw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rsu-rtl8712fw.fw:rsu-rtl8712fw:120 -mrsu-rtl8712fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "rsu-rtl8712fw.c" rsu-rtl8712fw.fwo optional rsu-rtl8712fw | rsufw \ dependency "rsu-rtl8712fw.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "rsu-rtl8712fw.fwo" rsu-rtl8712fw.fw optional rsu-rtl8712.fw | rsufw \ dependency "$S/contrib/dev/rsu/rsu-rtl8712fw.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rsu-rtl8712fw.fw" dev/usb/wlan/if_rum.c optional rum dev/usb/wlan/if_run.c optional run runfw.c optional runfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk run.fw:runfw -mrunfw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "runfw.c" runfw.fwo optional runfw \ dependency "run.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "runfw.fwo" run.fw optional runfw \ dependency "$S/contrib/dev/run/rt2870.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "run.fw" dev/usb/wlan/if_uath.c optional uath dev/usb/wlan/if_upgt.c optional upgt dev/usb/wlan/if_ural.c optional ural dev/usb/wlan/if_urtw.c optional urtw dev/usb/wlan/if_zyd.c optional zyd # # USB serial and parallel port drivers # dev/usb/serial/u3g.c optional u3g dev/usb/serial/uark.c optional uark dev/usb/serial/ubsa.c optional ubsa dev/usb/serial/ubser.c optional ubser dev/usb/serial/uchcom.c optional uchcom dev/usb/serial/ucycom.c optional ucycom dev/usb/serial/ufoma.c optional ufoma dev/usb/serial/uftdi.c optional uftdi dev/usb/serial/ugensa.c optional ugensa dev/usb/serial/uipaq.c optional uipaq dev/usb/serial/ulpt.c optional ulpt dev/usb/serial/umcs.c optional umcs dev/usb/serial/umct.c optional umct dev/usb/serial/umodem.c optional umodem dev/usb/serial/umoscom.c optional umoscom dev/usb/serial/uplcom.c optional uplcom dev/usb/serial/uslcom.c optional uslcom dev/usb/serial/uvisor.c optional uvisor dev/usb/serial/uvscom.c optional uvscom dev/usb/serial/usb_serial.c optional ucom | u3g | uark | ubsa | ubser | \ uchcom | ucycom | ufoma | uftdi | \ ugensa | uipaq | umcs | umct | \ umodem | umoscom | uplcom | usie | \ uslcom | uvisor | uvscom # # USB misc drivers # dev/usb/misc/cp2112.c optional cp2112 dev/usb/misc/udbp.c optional udbp dev/usb/misc/ugold.c optional ugold dev/usb/misc/uled.c optional uled # # USB input drivers # dev/usb/input/atp.c optional atp dev/usb/input/uep.c optional uep dev/usb/input/uhid.c optional uhid dev/usb/input/uhid_snes.c optional uhid_snes dev/usb/input/ukbd.c optional ukbd dev/usb/input/ums.c optional ums dev/usb/input/usbhid.c optional usbhid dev/usb/input/wmt.c optional wmt dev/usb/input/wsp.c optional wsp # # USB quirks # dev/usb/quirk/usb_quirk.c optional usb # # USB templates # dev/usb/template/usb_template.c optional usb_template dev/usb/template/usb_template_audio.c optional usb_template dev/usb/template/usb_template_cdce.c optional usb_template dev/usb/template/usb_template_kbd.c optional usb_template dev/usb/template/usb_template_modem.c optional usb_template dev/usb/template/usb_template_mouse.c optional usb_template dev/usb/template/usb_template_msc.c optional usb_template dev/usb/template/usb_template_mtp.c optional usb_template dev/usb/template/usb_template_phone.c optional usb_template dev/usb/template/usb_template_serialnet.c optional usb_template dev/usb/template/usb_template_midi.c optional usb_template dev/usb/template/usb_template_multi.c optional usb_template dev/usb/template/usb_template_cdceem.c optional usb_template # # USB video drivers # dev/usb/video/udl.c optional udl # # USB END # dev/videomode/videomode.c optional videomode dev/videomode/edid.c optional videomode dev/videomode/pickmode.c optional videomode dev/videomode/vesagtf.c optional videomode dev/veriexec/verified_exec.c optional mac_veriexec dev/vge/if_vge.c optional vge dev/viapm/viapm.c optional viapm pci dev/virtio/virtio.c optional virtio dev/virtio/virtqueue.c optional virtio dev/virtio/virtio_bus_if.m optional virtio dev/virtio/virtio_if.m optional virtio dev/virtio/pci/virtio_pci.c optional virtio_pci dev/virtio/pci/virtio_pci_if.m optional virtio_pci dev/virtio/pci/virtio_pci_legacy.c optional virtio_pci dev/virtio/pci/virtio_pci_modern.c optional virtio_pci dev/virtio/mmio/virtio_mmio.c optional virtio_mmio dev/virtio/mmio/virtio_mmio_acpi.c optional virtio_mmio acpi dev/virtio/mmio/virtio_mmio_cmdline.c optional virtio_mmio dev/virtio/mmio/virtio_mmio_fdt.c optional virtio_mmio fdt dev/virtio/mmio/virtio_mmio_if.m optional virtio_mmio dev/virtio/network/if_vtnet.c optional vtnet dev/virtio/block/virtio_blk.c optional virtio_blk dev/virtio/balloon/virtio_balloon.c optional virtio_balloon dev/virtio/gpu/virtio_gpu.c optional virtio_gpu dev/virtio/scsi/virtio_scsi.c optional virtio_scsi dev/virtio/random/virtio_random.c optional virtio_random dev/virtio/console/virtio_console.c optional virtio_console dev/vkbd/vkbd.c optional vkbd dev/vmgenc/vmgenc_acpi.c optional acpi dev/vmware/vmxnet3/if_vmx.c optional vmx dev/vmware/vmci/vmci.c optional vmci dev/vmware/vmci/vmci_datagram.c optional vmci dev/vmware/vmci/vmci_doorbell.c optional vmci dev/vmware/vmci/vmci_driver.c optional vmci dev/vmware/vmci/vmci_event.c optional vmci dev/vmware/vmci/vmci_hashtable.c optional vmci dev/vmware/vmci/vmci_kernel_if.c optional vmci dev/vmware/vmci/vmci_qpair.c optional vmci dev/vmware/vmci/vmci_queue_pair.c optional vmci dev/vmware/vmci/vmci_resource.c optional vmci dev/vmware/pvscsi/pvscsi.c optional pvscsi dev/vr/if_vr.c optional vr pci dev/vt/colors/vt_termcolors.c optional vt dev/vt/font/vt_font_default.c optional vt dev/vt/font/vt_mouse_cursor.c optional vt dev/vt/hw/efifb/efifb.c optional vt_efifb dev/vt/hw/simplefb/simplefb.c optional vt_simplefb fdt dev/vt/hw/vbefb/vbefb.c optional vt_vbefb dev/vt/hw/fb/vt_fb.c optional vt dev/vt/hw/vga/vt_vga.c optional vt vt_vga dev/vt/logo/logo_freebsd.c optional vt splash dev/vt/logo/logo_beastie.c optional vt splash dev/vt/vt_buf.c optional vt dev/vt/vt_consolectl.c optional vt dev/vt/vt_core.c optional vt dev/vt/vt_cpulogos.c optional vt splash dev/vt/vt_font.c optional vt dev/vt/vt_sysmouse.c optional vt dev/vte/if_vte.c optional vte pci dev/watchdog/watchdog.c standard dev/wg/if_wg.c optional wg \ compile-with "${NORMAL_C} -include $S/dev/wg/compat.h" dev/wg/wg_cookie.c optional wg \ compile-with "${NORMAL_C} -include $S/dev/wg/compat.h" dev/wg/wg_crypto.c optional wg \ compile-with "${NORMAL_C} -include $S/dev/wg/compat.h" dev/wg/wg_noise.c optional wg \ compile-with "${NORMAL_C} -include $S/dev/wg/compat.h" dev/wpi/if_wpi.c optional wpi pci wpifw.c optional wpifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk wpi.fw:wpifw:153229 -mwpi -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "wpifw.c" wpifw.fwo optional wpifw \ dependency "wpi.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "wpifw.fwo" wpi.fw optional wpifw \ dependency "$S/contrib/dev/wpi/iwlwifi-3945-15.32.2.9.fw.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "wpi.fw" dev/xdma/controller/pl330.c optional xdma pl330 fdt dev/xdma/xdma.c optional xdma dev/xdma/xdma_bank.c optional xdma dev/xdma/xdma_bio.c optional xdma dev/xdma/xdma_fdt_test.c optional xdma xdma_test fdt dev/xdma/xdma_if.m optional xdma dev/xdma/xdma_iommu.c optional xdma dev/xdma/xdma_mbuf.c optional xdma dev/xdma/xdma_queue.c optional xdma dev/xdma/xdma_sg.c optional xdma dev/xdma/xdma_sglist.c optional xdma dev/xen/balloon/balloon.c optional xenhvm dev/xen/blkfront/blkfront.c optional xenhvm dev/xen/blkback/blkback.c optional xenhvm dev/xen/bus/xen_intr.c optional xenhvm dev/xen/bus/xenpv.c optional xenhvm dev/xen/console/xen_console.c optional xenhvm dev/xen/control/control.c optional xenhvm dev/xen/cpu/xen_acpi_cpu.c optional xenhvm acpi dev/xen/efi/pvefi.c optional xenhvm xenefi efirt dev/xen/grant_table/grant_table.c optional xenhvm dev/xen/netback/netback.c optional xenhvm dev/xen/netfront/netfront.c optional xenhvm dev/xen/timer/xen_timer.c optional xenhvm xentimer dev/xen/xenpci/xenpci.c optional xenpci dev/xen/xenstore/xenstore.c optional xenhvm dev/xen/xenstore/xenstore_dev.c optional xenhvm dev/xen/xenstore/xenstored_dev.c optional xenhvm dev/xen/evtchn/evtchn_dev.c optional xenhvm dev/xen/privcmd/privcmd.c optional xenhvm dev/xen/gntdev/gntdev.c optional xenhvm dev/xen/debug/debug.c optional xenhvm dev/xl/if_xl.c optional xl pci dev/xl/xlphy.c optional xl pci fs/autofs/autofs.c optional autofs fs/autofs/autofs_vfsops.c optional autofs fs/autofs/autofs_vnops.c optional autofs fs/deadfs/dead_vnops.c standard fs/devfs/devfs_devs.c standard fs/devfs/devfs_dir.c standard fs/devfs/devfs_rule.c standard fs/devfs/devfs_vfsops.c standard fs/devfs/devfs_vnops.c standard fs/fdescfs/fdesc_vfsops.c optional fdescfs fs/fdescfs/fdesc_vnops.c optional fdescfs fs/fifofs/fifo_vnops.c standard fs/cuse/cuse.c optional cuse fs/fuse/fuse_device.c optional fusefs fs/fuse/fuse_file.c optional fusefs fs/fuse/fuse_internal.c optional fusefs fs/fuse/fuse_io.c optional fusefs fs/fuse/fuse_ipc.c optional fusefs fs/fuse/fuse_main.c optional fusefs fs/fuse/fuse_node.c optional fusefs fs/fuse/fuse_vfsops.c optional fusefs fs/fuse/fuse_vnops.c optional fusefs fs/mntfs/mntfs_vnops.c standard fs/msdosfs/msdosfs_conv.c optional msdosfs fs/msdosfs/msdosfs_denode.c optional msdosfs fs/msdosfs/msdosfs_fat.c optional msdosfs fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv fs/msdosfs/msdosfs_lookup.c optional msdosfs fs/msdosfs/msdosfs_vfsops.c optional msdosfs fs/msdosfs/msdosfs_vnops.c optional msdosfs fs/nfs/nfs_commonkrpc.c optional nfscl | nfslockd | nfsd fs/nfs/nfs_commonsubs.c optional nfscl | nfslockd | nfsd fs/nfs/nfs_commonport.c optional nfscl | nfslockd | nfsd fs/nfs/nfs_commonacl.c optional nfscl | nfslockd | nfsd fs/nfsclient/nfs_clcomsubs.c optional nfscl fs/nfsclient/nfs_clsubs.c optional nfscl fs/nfsclient/nfs_clstate.c optional nfscl fs/nfsclient/nfs_clkrpc.c optional nfscl fs/nfsclient/nfs_clrpcops.c optional nfscl fs/nfsclient/nfs_clvnops.c optional nfscl fs/nfsclient/nfs_clnode.c optional nfscl fs/nfsclient/nfs_clvfsops.c optional nfscl fs/nfsclient/nfs_clport.c optional nfscl fs/nfsclient/nfs_clbio.c optional nfscl fs/nfsclient/nfs_clnfsiod.c optional nfscl fs/nfsserver/nfs_fha_new.c optional nfsd inet fs/nfsserver/nfs_nfsdsocket.c optional nfsd inet fs/nfsserver/nfs_nfsdsubs.c optional nfsd inet fs/nfsserver/nfs_nfsdstate.c optional nfsd inet fs/nfsserver/nfs_nfsdkrpc.c optional nfsd inet fs/nfsserver/nfs_nfsdserv.c optional nfsd inet fs/nfsserver/nfs_nfsdport.c optional nfsd inet fs/nfsserver/nfs_nfsdcache.c optional nfsd inet fs/nullfs/null_subr.c optional nullfs fs/nullfs/null_vfsops.c optional nullfs fs/nullfs/null_vnops.c optional nullfs fs/procfs/procfs.c optional procfs fs/procfs/procfs_dbregs.c optional procfs fs/procfs/procfs_fpregs.c optional procfs fs/procfs/procfs_map.c optional procfs fs/procfs/procfs_mem.c optional procfs fs/procfs/procfs_note.c optional procfs fs/procfs/procfs_osrel.c optional procfs fs/procfs/procfs_regs.c optional procfs fs/procfs/procfs_rlimit.c optional procfs fs/procfs/procfs_status.c optional procfs fs/procfs/procfs_type.c optional procfs fs/pseudofs/pseudofs.c optional pseudofs fs/pseudofs/pseudofs_fileno.c optional pseudofs fs/pseudofs/pseudofs_vncache.c optional pseudofs fs/pseudofs/pseudofs_vnops.c optional pseudofs fs/smbfs/smbfs_io.c optional smbfs fs/smbfs/smbfs_node.c optional smbfs fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs fs/tarfs/tarfs_io.c optional tarfs compile-with "${NORMAL_C} -I$S/contrib/zstd/lib/freebsd" fs/tarfs/tarfs_subr.c optional tarfs fs/tarfs/tarfs_vfsops.c optional tarfs fs/tarfs/tarfs_vnops.c optional tarfs fs/udf/osta.c optional udf fs/udf/udf_iconv.c optional udf_iconv fs/udf/udf_vfsops.c optional udf fs/udf/udf_vnops.c optional udf fs/unionfs/union_subr.c optional unionfs fs/unionfs/union_vfsops.c optional unionfs fs/unionfs/union_vnops.c optional unionfs fs/tmpfs/tmpfs_vnops.c optional tmpfs fs/tmpfs/tmpfs_fifoops.c optional tmpfs fs/tmpfs/tmpfs_vfsops.c optional tmpfs fs/tmpfs/tmpfs_subr.c optional tmpfs gdb/gdb_cons.c optional gdb gdb/gdb_main.c optional gdb gdb/gdb_packet.c optional gdb gdb/netgdb.c optional ddb debugnet gdb netgdb inet geom/bde/g_bde.c optional geom_bde geom/bde/g_bde_crypt.c optional geom_bde geom/bde/g_bde_lock.c optional geom_bde geom/bde/g_bde_work.c optional geom_bde geom/cache/g_cache.c optional geom_cache geom/concat/g_concat.c optional geom_concat geom/eli/g_eli.c optional geom_eli geom/eli/g_eli_crypto.c optional geom_eli geom/eli/g_eli_ctl.c optional geom_eli geom/eli/g_eli_hmac.c optional geom_eli geom/eli/g_eli_integrity.c optional geom_eli geom/eli/g_eli_key.c optional geom_eli geom/eli/g_eli_key_cache.c optional geom_eli geom/eli/g_eli_privacy.c optional geom_eli geom/eli/pkcs5v2.c optional geom_eli geom/gate/g_gate.c optional geom_gate geom/geom_bsd_enc.c optional geom_part_bsd geom/geom_ccd.c optional ccd | geom_ccd geom/geom_ctl.c standard geom/geom_dev.c standard geom/geom_disk.c standard geom/geom_dump.c standard geom/geom_event.c standard geom/geom_flashmap.c optional fdt cfi | fdt mx25l | mmcsd | fdt n25q | fdt at45d geom/geom_io.c standard geom/geom_kern.c standard geom/geom_map.c optional geom_map geom/geom_redboot.c optional geom_redboot geom/geom_slice.c standard geom/geom_subr.c standard geom/geom_vfs.c standard geom/journal/g_journal.c optional geom_journal geom/journal/g_journal_ufs.c optional geom_journal geom/label/g_label.c optional geom_label | geom_label_gpt geom/label/g_label_ext2fs.c optional geom_label geom/label/g_label_flashmap.c optional geom_label geom/label/g_label_iso9660.c optional geom_label geom/label/g_label_msdosfs.c optional geom_label geom/label/g_label_ntfs.c optional geom_label geom/label/g_label_reiserfs.c optional geom_label geom/label/g_label_ufs.c optional geom_label geom/label/g_label_gpt.c optional geom_label | geom_label_gpt geom/label/g_label_disk_ident.c optional geom_label geom/linux_lvm/g_linux_lvm.c optional geom_linux_lvm geom/mirror/g_mirror.c optional geom_mirror geom/mirror/g_mirror_ctl.c optional geom_mirror geom/mountver/g_mountver.c optional geom_mountver geom/multipath/g_multipath.c optional geom_multipath geom/nop/g_nop.c optional geom_nop geom/part/g_part.c standard geom/part/g_part_if.m standard geom/part/g_part_apm.c optional geom_part_apm geom/part/g_part_bsd.c optional geom_part_bsd geom/part/g_part_bsd64.c optional geom_part_bsd64 geom/part/g_part_ebr.c optional geom_part_ebr geom/part/g_part_gpt.c optional geom_part_gpt geom/part/g_part_ldm.c optional geom_part_ldm geom/part/g_part_mbr.c optional geom_part_mbr geom/raid/g_raid.c optional geom_raid geom/raid/g_raid_ctl.c optional geom_raid geom/raid/g_raid_md_if.m optional geom_raid geom/raid/g_raid_tr_if.m optional geom_raid geom/raid/md_ddf.c optional geom_raid geom/raid/md_intel.c optional geom_raid geom/raid/md_jmicron.c optional geom_raid geom/raid/md_nvidia.c optional geom_raid geom/raid/md_promise.c optional geom_raid geom/raid/md_sii.c optional geom_raid geom/raid/tr_concat.c optional geom_raid geom/raid/tr_raid0.c optional geom_raid geom/raid/tr_raid1.c optional geom_raid geom/raid/tr_raid1e.c optional geom_raid geom/raid/tr_raid5.c optional geom_raid geom/raid3/g_raid3.c optional geom_raid3 geom/raid3/g_raid3_ctl.c optional geom_raid3 geom/shsec/g_shsec.c optional geom_shsec geom/stripe/g_stripe.c optional geom_stripe geom/union/g_union.c optional geom_union geom/uzip/g_uzip.c optional geom_uzip geom/uzip/g_uzip_lzma.c optional geom_uzip geom/uzip/g_uzip_wrkthr.c optional geom_uzip geom/uzip/g_uzip_zlib.c optional geom_uzip geom/uzip/g_uzip_zstd.c optional geom_uzip zstdio \ compile-with "${NORMAL_C} -I$S/contrib/zstd/lib/freebsd" geom/vinum/geom_vinum.c optional geom_vinum geom/vinum/geom_vinum_create.c optional geom_vinum geom/vinum/geom_vinum_drive.c optional geom_vinum geom/vinum/geom_vinum_plex.c optional geom_vinum geom/vinum/geom_vinum_volume.c optional geom_vinum geom/vinum/geom_vinum_subr.c optional geom_vinum geom/vinum/geom_vinum_raid5.c optional geom_vinum geom/vinum/geom_vinum_share.c optional geom_vinum geom/vinum/geom_vinum_list.c optional geom_vinum geom/vinum/geom_vinum_rm.c optional geom_vinum geom/vinum/geom_vinum_init.c optional geom_vinum geom/vinum/geom_vinum_state.c optional geom_vinum geom/vinum/geom_vinum_rename.c optional geom_vinum geom/vinum/geom_vinum_move.c optional geom_vinum geom/vinum/geom_vinum_events.c optional geom_vinum geom/virstor/binstream.c optional geom_virstor geom/virstor/g_virstor.c optional geom_virstor geom/virstor/g_virstor_md.c optional geom_virstor geom/zero/g_zero.c optional geom_zero fs/ext2fs/ext2_acl.c optional ext2fs fs/ext2fs/ext2_alloc.c optional ext2fs fs/ext2fs/ext2_balloc.c optional ext2fs fs/ext2fs/ext2_bmap.c optional ext2fs fs/ext2fs/ext2_csum.c optional ext2fs fs/ext2fs/ext2_extattr.c optional ext2fs fs/ext2fs/ext2_extents.c optional ext2fs fs/ext2fs/ext2_inode.c optional ext2fs fs/ext2fs/ext2_inode_cnv.c optional ext2fs fs/ext2fs/ext2_hash.c optional ext2fs fs/ext2fs/ext2_htree.c optional ext2fs fs/ext2fs/ext2_lookup.c optional ext2fs fs/ext2fs/ext2_subr.c optional ext2fs fs/ext2fs/ext2_vfsops.c optional ext2fs fs/ext2fs/ext2_vnops.c optional ext2fs # isa/isa_if.m standard isa/isa_common.c optional isa isa/isahint.c optional isa isa/pnp.c optional isa isapnp isa/pnpparse.c optional isa isapnp fs/cd9660/cd9660_bmap.c optional cd9660 fs/cd9660/cd9660_lookup.c optional cd9660 fs/cd9660/cd9660_node.c optional cd9660 fs/cd9660/cd9660_rrip.c optional cd9660 fs/cd9660/cd9660_util.c optional cd9660 fs/cd9660/cd9660_vfsops.c optional cd9660 fs/cd9660/cd9660_vnops.c optional cd9660 fs/cd9660/cd9660_iconv.c optional cd9660_iconv gnu/gcov/gcc_4_7.c optional gcov \ warning "kernel contains GPL licensed gcov support" gnu/gcov/gcov_fs.c optional gcov lindebugfs \ compile-with "${LINUXKPI_C}" gnu/gcov/gcov_subr.c optional gcov kern/bus_if.m standard kern/clock_if.m standard kern/cpufreq_if.m standard kern/device_if.m standard kern/imgact_binmisc.c optional imgact_binmisc kern/imgact_elf.c standard kern/imgact_elf32.c optional compat_freebsd32 kern/imgact_shell.c standard kern/init_main.c standard kern/init_sysent.c standard kern/ksched.c optional _kposix_priority_scheduling kern/kern_acct.c standard kern/kern_alq.c optional alq kern/kern_boottrace.c standard kern/kern_clock.c standard kern/kern_clocksource.c standard kern/kern_condvar.c standard kern/kern_conf.c standard kern/kern_cons.c standard kern/kern_cpu.c standard kern/kern_cpuset.c standard kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_devctl.c standard kern/kern_dtrace.c optional kdtrace_hooks kern/kern_dump.c standard kern/kern_environment.c standard kern/kern_et.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fail.c standard kern/kern_ffclock.c standard kern/kern_fork.c standard kern/kern_hhook.c standard kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard kern/kern_kcov.c optional kcov \ compile-with "${NORMAL_C:N-fsanitize*} ${NORMAL_C:M-fsanitize=kernel-memory}" kern/kern_khelp.c standard kern/kern_kthread.c standard kern/kern_ktr.c optional ktr kern/kern_ktrace.c standard kern/kern_linker.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_lockstat.c optional kdtrace_hooks kern/kern_loginclass.c standard kern/kern_malloc.c standard kern/kern_mbuf.c standard kern/kern_membarrier.c standard kern/kern_mib.c standard kern/kern_module.c standard kern/kern_mtxpool.c standard kern/kern_mutex.c standard kern/kern_ntptime.c standard kern/kern_osd.c standard kern/kern_physio.c standard kern/kern_pmc.c standard kern/kern_poll.c optional device_polling kern/kern_priv.c standard kern/kern_proc.c standard kern/kern_procctl.c standard kern/kern_prot.c standard kern/kern_racct.c optional racct kern/kern_rangelock.c standard kern/kern_rctl.c standard kern/kern_resource.c standard kern/kern_rmlock.c standard kern/kern_rwlock.c standard kern/kern_sdt.c optional kdtrace_hooks kern/kern_sema.c standard kern/kern_sendfile.c standard kern/kern_sharedpage.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_switch.c standard kern/kern_sx.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_tc.c standard kern/kern_thr.c standard kern/kern_thread.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_tslog.c optional tslog kern/kern_ubsan.c optional kubsan kern/kern_umtx.c standard kern/kern_uuid.c standard kern/kern_vnodedumper.c standard kern/kern_xxx.c standard kern/link_elf.c standard kern/linker_if.m standard kern/md4c.c optional netsmb kern/md5c.c standard kern/p1003_1b.c standard kern/posix4_mib.c standard kern/sched_4bsd.c optional sched_4bsd kern/sched_ule.c optional sched_ule kern/serdev_if.m standard kern/stack_protector.c standard \ compile-with "${NORMAL_C:N-fstack-protector*}" kern/subr_acl_nfs4.c optional ufs_acl | zfs kern/subr_acl_posix1e.c optional ufs_acl kern/subr_asan.c optional kasan \ compile-with "${NORMAL_C:N-fsanitize*:N-fstack-protector*}" kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_boot.c standard kern/subr_bus.c standard kern/subr_bus_dma.c standard kern/subr_bufring.c standard kern/subr_capability.c standard kern/subr_clock.c standard kern/subr_compressor.c standard \ compile-with "${NORMAL_C} -I$S/contrib/zstd/lib/freebsd" kern/subr_coverage.c optional coverage \ compile-with "${NORMAL_C:N-fsanitize*}" kern/subr_counter.c standard kern/subr_csan.c optional kcsan \ compile-with "${NORMAL_C:N-fsanitize*:N-fstack-protector*}" kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_early.c standard kern/subr_epoch.c standard kern/subr_eventhandler.c standard kern/subr_fattime.c standard kern/subr_firmware.c optional firmware kern/subr_filter.c standard kern/subr_gtaskqueue.c standard kern/subr_hash.c standard kern/subr_hints.c standard kern/subr_kdb.c standard kern/subr_kobj.c standard kern/subr_lock.c standard kern/subr_log.c standard kern/subr_mchain.c optional libmchain kern/subr_memdesc.c standard kern/subr_module.c standard kern/subr_msan.c optional kmsan \ compile-with "${NORMAL_C:N-fsanitize*:N-fno-sanitize*:N-fstack-protector*}" kern/subr_msgbuf.c standard kern/subr_param.c standard kern/subr_pcpu.c standard kern/subr_pctrie.c standard kern/subr_pidctrl.c standard kern/subr_power.c standard kern/subr_prf.c standard kern/subr_prng.c standard kern/subr_prof.c standard kern/subr_rangeset.c standard kern/subr_rman.c standard kern/subr_rtc.c standard kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_sglist.c standard kern/subr_sleepqueue.c standard kern/subr_smp.c standard kern/subr_smr.c standard kern/subr_stack.c optional ddb | stack | ktr kern/subr_stats.c optional stats kern/subr_taskqueue.c standard kern/subr_terminal.c optional vt kern/subr_trap.c standard kern/subr_turnstile.c standard kern/subr_uio.c standard kern/subr_unit.c standard kern/subr_vmem.c standard kern/subr_witness.c optional witness kern/sys_capability.c standard kern/sys_eventfd.c standard kern/sys_generic.c standard kern/sys_getrandom.c standard kern/sys_pipe.c standard kern/sys_procdesc.c standard kern/sys_process.c standard kern/sys_socket.c standard kern/sys_timerfd.c standard kern/syscalls.c standard kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c optional compat_43tty kern/tty_info.c standard kern/tty_inq.c standard kern/tty_outq.c standard kern/tty_pts.c standard kern/tty_tty.c standard kern/tty_ttydisc.c standard kern/uipc_accf.c standard kern/uipc_debug.c optional ddb kern/uipc_domain.c standard kern/uipc_ktls.c optional kern_tls kern/uipc_mbuf.c standard kern/uipc_mbuf2.c standard kern/uipc_mbufhash.c standard kern/uipc_mqueue.c optional p1003_1b_mqueue kern/uipc_sem.c optional p1003_1b_semaphores kern/uipc_shm.c standard kern/uipc_sockbuf.c standard kern/uipc_socket.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_acl.c standard kern/vfs_aio.c standard kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_default.c standard kern/vfs_export.c standard kern/vfs_extattr.c standard kern/vfs_hash.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_mount.c standard kern/vfs_mountroot.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard # # Kernel GSS-API # gssd.h optional kgssapi \ dependency "$S/kgssapi/gssd.x" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -hM $S/kgssapi/gssd.x | grep -v pthread.h > gssd.h" \ no-obj no-implicit-rule before-depend local \ clean "gssd.h" gssd_xdr.c optional kgssapi \ dependency "$S/kgssapi/gssd.x gssd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -c $S/kgssapi/gssd.x -o gssd_xdr.c" \ no-ctfconvert no-implicit-rule before-depend local \ clean "gssd_xdr.c" gssd_clnt.c optional kgssapi \ dependency "$S/kgssapi/gssd.x gssd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -lM $S/kgssapi/gssd.x | grep -v string.h > gssd_clnt.c" \ no-ctfconvert no-implicit-rule before-depend local \ clean "gssd_clnt.c" kgssapi/gss_accept_sec_context.c optional kgssapi kgssapi/gss_add_oid_set_member.c optional kgssapi kgssapi/gss_acquire_cred.c optional kgssapi kgssapi/gss_canonicalize_name.c optional kgssapi kgssapi/gss_create_empty_oid_set.c optional kgssapi kgssapi/gss_delete_sec_context.c optional kgssapi kgssapi/gss_display_status.c optional kgssapi kgssapi/gss_export_name.c optional kgssapi kgssapi/gss_get_mic.c optional kgssapi kgssapi/gss_init_sec_context.c optional kgssapi kgssapi/gss_impl.c optional kgssapi kgssapi/gss_import_name.c optional kgssapi kgssapi/gss_ip_to_dns.c optional kgssapi kgssapi/gss_names.c optional kgssapi kgssapi/gss_pname_to_uid.c optional kgssapi kgssapi/gss_release_buffer.c optional kgssapi kgssapi/gss_release_cred.c optional kgssapi kgssapi/gss_release_name.c optional kgssapi kgssapi/gss_release_oid_set.c optional kgssapi kgssapi/gss_set_cred_option.c optional kgssapi kgssapi/gss_test_oid_set_member.c optional kgssapi kgssapi/gss_unwrap.c optional kgssapi kgssapi/gss_verify_mic.c optional kgssapi kgssapi/gss_wrap.c optional kgssapi kgssapi/gss_wrap_size_limit.c optional kgssapi kgssapi/gssd_prot.c optional kgssapi kgssapi/krb5/krb5_mech.c optional kgssapi kgssapi/krb5/kcrypto.c optional kgssapi kgssapi/krb5/kcrypto_aes.c optional kgssapi kgssapi/kgss_if.m optional kgssapi kgssapi/gsstest.c optional kgssapi_debug # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., # libkern/divdi3.c is needed by i386 but not alpha. Also, some of these # routines may be optimized for a particular platform. In either case, # the file should be moved to conf/files. from here. # libkern/arc4random.c standard libkern/arc4random_uniform.c standard libkern/asprintf.c standard libkern/bcd.c standard libkern/bsearch.c standard libkern/crc16.c standard libkern/explicit_bzero.c standard libkern/fnmatch.c standard libkern/gsb_crc32.c standard libkern/iconv.c optional libiconv libkern/iconv_converter_if.m optional libiconv libkern/iconv_ucs.c optional libiconv libkern/iconv_xlat.c optional libiconv libkern/iconv_xlat16.c optional libiconv libkern/inet_aton.c standard libkern/inet_ntoa.c standard libkern/inet_ntop.c standard libkern/inet_pton.c standard libkern/jenkins_hash.c standard libkern/murmur3_32.c standard libkern/memcchr.c standard libkern/memchr.c standard libkern/memmem.c optional gdb libkern/qsort.c standard libkern/qsort_r.c standard libkern/random.c standard libkern/scanc.c standard libkern/strcasecmp.c standard libkern/strcasestr.c standard libkern/strcat.c standard libkern/strchr.c standard libkern/strchrnul.c standard libkern/strcpy.c standard libkern/strcspn.c standard libkern/strdup.c standard libkern/strndup.c standard libkern/strlcat.c standard libkern/strlcpy.c standard libkern/strncat.c standard libkern/strncpy.c standard libkern/strnlen.c standard libkern/strnstr.c standard libkern/strrchr.c standard libkern/strsep.c standard libkern/strspn.c standard libkern/strstr.c standard libkern/strtol.c standard libkern/strtoq.c standard libkern/strtoul.c standard libkern/strtouq.c standard libkern/strvalid.c standard libkern/timingsafe_bcmp.c standard contrib/zlib/adler32.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/compress.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/crc32.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/deflate.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/inffast.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/inflate.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/inftrees.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/trees.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/uncompr.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" contrib/zlib/zutil.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib \ compile-with "${ZLIB_C}" dev/zlib/zlib_mod.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib dev/zlib/zcalloc.c optional crypto | geom_uzip | \ mxge | ddb_ctf | gzio | zfs | zlib net/altq/altq_cbq.c optional altq net/altq/altq_codel.c optional altq net/altq/altq_hfsc.c optional altq net/altq/altq_fairq.c optional altq net/altq/altq_priq.c optional altq net/altq/altq_red.c optional altq net/altq/altq_rio.c optional altq net/altq/altq_rmclass.c optional altq net/altq/altq_subr.c optional altq net/bpf.c standard net/bpf_buffer.c optional bpf net/bpf_jitter.c optional bpf_jitter net/bpf_filter.c optional bpf | netgraph_bpf net/bpf_zerocopy.c optional bpf net/bridgestp.c optional bridge | if_bridge net/ieee8023ad_lacp.c optional lagg net/if.c standard net/ifq.c standard net/if_bridge.c optional bridge inet | if_bridge inet net/if_clone.c standard net/if_dead.c standard net/if_disc.c optional disc net/if_edsc.c optional edsc net/if_enc.c optional enc inet | enc inet6 net/if_epair.c optional epair net/if_ethersubr.c optional ether net/if_fwsubr.c optional fwip net/if_gif.c optional gif inet | gif inet6 | \ netgraph_gif inet | netgraph_gif inet6 net/if_gre.c optional gre inet | gre inet6 net/if_ipsec.c optional inet ipsec | inet6 ipsec net/if_lagg.c optional lagg net/if_loop.c optional loop net/if_llatbl.c standard net/if_me.c optional me inet net/if_media.c standard net/if_mib.c standard net/if_ovpn.c optional ovpn inet | ovpn inet6 net/if_stf.c optional stf inet inet6 net/if_tuntap.c optional tuntap net/if_vlan.c optional vlan net/if_vxlan.c optional vxlan inet | vxlan inet6 net/ifdi_if.m optional ether pci iflib net/iflib.c optional ether pci iflib net/mp_ring.c optional ether iflib net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression net/netisr.c standard net/debugnet.c optional inet debugnet net/debugnet_inet.c optional inet debugnet net/pfil.c optional ether | inet net/radix.c standard net/route.c standard net/route/nhgrp.c optional route_mpath net/route/nhgrp_ctl.c optional route_mpath net/route/nhop.c standard net/route/nhop_ctl.c standard net/route/nhop_utils.c standard net/route/fib_algo.c optional fib_algo net/route/route_ctl.c standard net/route/route_ddb.c optional ddb net/route/route_helpers.c standard net/route/route_ifaddrs.c standard net/route/route_rtentry.c standard net/route/route_subscription.c standard net/route/route_tables.c standard net/route/route_temporal.c standard net/rss_config.c optional inet rss | inet6 rss net/rtsock.c standard net/slcompress.c optional netgraph_vjc net/toeplitz.c optional inet rss | inet6 rss | route_mpath net/vnet.c optional vimage net80211/ieee80211.c optional wlan net80211/ieee80211_acl.c optional wlan wlan_acl net80211/ieee80211_action.c optional wlan net80211/ieee80211_adhoc.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_ageq.c optional wlan net80211/ieee80211_amrr.c optional wlan | wlan_amrr net80211/ieee80211_crypto.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_crypto_ccmp.c optional wlan wlan_ccmp net80211/ieee80211_crypto_none.c optional wlan net80211/ieee80211_crypto_tkip.c optional wlan wlan_tkip net80211/ieee80211_crypto_wep.c optional wlan wlan_wep net80211/ieee80211_ddb.c optional wlan ddb net80211/ieee80211_dfs.c optional wlan net80211/ieee80211_freebsd.c optional wlan net80211/ieee80211_hostap.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_ht.c optional wlan net80211/ieee80211_hwmp.c optional wlan ieee80211_support_mesh net80211/ieee80211_input.c optional wlan net80211/ieee80211_ioctl.c optional wlan net80211/ieee80211_mesh.c optional wlan ieee80211_support_mesh \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_monitor.c optional wlan net80211/ieee80211_node.c optional wlan net80211/ieee80211_output.c optional wlan net80211/ieee80211_phy.c optional wlan net80211/ieee80211_power.c optional wlan net80211/ieee80211_proto.c optional wlan net80211/ieee80211_radiotap.c optional wlan net80211/ieee80211_ratectl.c optional wlan net80211/ieee80211_ratectl_none.c optional wlan net80211/ieee80211_regdomain.c optional wlan net80211/ieee80211_rssadapt.c optional wlan wlan_rssadapt net80211/ieee80211_scan.c optional wlan net80211/ieee80211_scan_sta.c optional wlan net80211/ieee80211_sta.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_superg.c optional wlan ieee80211_support_superg net80211/ieee80211_scan_sw.c optional wlan net80211/ieee80211_tdma.c optional wlan ieee80211_support_tdma net80211/ieee80211_vht.c optional wlan net80211/ieee80211_wds.c optional wlan net80211/ieee80211_xauth.c optional wlan wlan_xauth net80211/ieee80211_alq.c optional wlan ieee80211_alq netgraph/bluetooth/common/ng_bluetooth.c optional netgraph_bluetooth netgraph/bluetooth/drivers/ubt/ng_ubt.c optional netgraph_bluetooth_ubt usb netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c optional netgraph_bluetooth_ubt usb netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c optional netgraph_bluetooth_ubtbcmfw usb netgraph/bluetooth/hci/ng_hci_cmds.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_evnt.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_main.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_misc.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_ulpi.c optional netgraph_bluetooth_hci netgraph/bluetooth/l2cap/ng_l2cap_cmds.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_evnt.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_llpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_main.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_misc.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/socket/ng_btsocket.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_hci_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_rfcomm.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_sco.c optional netgraph_bluetooth_socket netgraph/netflow/netflow.c optional netgraph_netflow netgraph/netflow/netflow_v9.c optional netgraph_netflow netgraph/netflow/ng_netflow.c optional netgraph_netflow netgraph/ng_UI.c optional netgraph_UI netgraph/ng_async.c optional netgraph_async netgraph/ng_base.c optional netgraph netgraph/ng_bpf.c optional netgraph_bpf netgraph/ng_bridge.c optional netgraph_bridge netgraph/ng_car.c optional netgraph_car netgraph/ng_checksum.c optional netgraph_checksum netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_deflate.c optional netgraph_deflate netgraph/ng_device.c optional netgraph_device netgraph/ng_echo.c optional netgraph_echo netgraph/ng_eiface.c optional netgraph_eiface netgraph/ng_ether.c optional netgraph_ether netgraph/ng_ether_echo.c optional netgraph_ether_echo netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_gif.c optional netgraph_gif inet6 | netgraph_gif inet netgraph/ng_gif_demux.c optional netgraph_gif_demux netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface netgraph/ng_ip_input.c optional netgraph_ip_input netgraph/ng_ipfw.c optional netgraph_ipfw inet ipfirewall netgraph/ng_ksocket.c optional netgraph_ksocket netgraph/ng_l2tp.c optional netgraph_l2tp netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_macfilter.c optional netgraph_macfilter netgraph/ng_mppc.c optional netgraph_mppc_compression | \ netgraph_mppc_encryption netgraph/ng_nat.c optional netgraph_nat inet libalias netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph netgraph/ng_patch.c optional netgraph_patch netgraph/ng_pipe.c optional netgraph_pipe netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe netgraph/ng_pptpgre.c optional netgraph_pptpgre netgraph/ng_pred1.c optional netgraph_pred1 netgraph/ng_rfc1490.c optional netgraph_rfc1490 netgraph/ng_socket.c optional netgraph_socket netgraph/ng_split.c optional netgraph_split netgraph/ng_tag.c optional netgraph_tag netgraph/ng_tcpmss.c optional netgraph_tcpmss netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_vjc.c optional netgraph_vjc netgraph/ng_vlan.c optional netgraph_vlan netgraph/ng_vlan_rotate.c optional netgraph_vlan_rotate netinet/accf_data.c optional accept_filter_data inet netinet/accf_dns.c optional accept_filter_dns inet netinet/accf_http.c optional accept_filter_http inet netinet/if_ether.c optional inet ether netinet/igmp.c optional inet netinet/in.c optional inet netinet/in_cksum.c optional inet | inet6 netinet/in_debug.c optional inet ddb netinet/in_kdtrace.c optional inet | inet6 netinet/ip_carp.c optional inet carp | inet6 carp netinet/in_fib.c optional inet netinet/in_fib_algo.c optional inet fib_algo netinet/in_gif.c optional gif inet | netgraph_gif inet netinet/ip_gre.c optional gre inet netinet/ip_id.c optional inet netinet/in_jail.c optional inet netinet/in_mcast.c optional inet netinet/in_pcb.c optional inet | inet6 netinet/in_prot.c optional inet | inet6 netinet/in_proto.c optional inet | inet6 netinet/in_rmx.c optional inet netinet/in_rss.c optional inet rss netinet/ip_divert.c optional ipdivert inet | ipdivert inet6 netinet/ip_ecn.c optional inet | inet6 netinet/ip_encap.c optional inet | inet6 netinet/ip_fastfwd.c optional inet netinet/ip_icmp.c optional inet | inet6 netinet/ip_input.c optional inet netinet/ip_mroute.c optional mrouting inet netinet/ip_options.c optional inet netinet/ip_output.c optional inet netinet/ip_reass.c optional inet netinet/raw_ip.c optional inet | inet6 netinet/cc/cc.c optional cc_newreno inet | cc_vegas inet | \ cc_htcp inet | cc_hd inet | cc_dctcp inet | cc_cubic inet | \ cc_chd inet | cc_cdg inet | cc_newreno inet6 | cc_vegas inet6 | \ cc_htcp inet6 | cc_hd inet6 |cc_dctcp inet6 | cc_cubic inet6 | \ cc_chd inet6 | cc_cdg inet6 netinet/cc/cc_cdg.c optional inet cc_cdg tcp_hhook netinet/cc/cc_chd.c optional inet cc_chd tcp_hhook netinet/cc/cc_cubic.c optional inet cc_cubic | inet6 cc_cubic netinet/cc/cc_dctcp.c optional inet cc_dctcp | inet6 cc_dctcp netinet/cc/cc_hd.c optional inet cc_hd tcp_hhook netinet/cc/cc_htcp.c optional inet cc_htcp | inet6 cc_htcp netinet/cc/cc_newreno.c optional inet cc_newreno | inet6 cc_newreno netinet/cc/cc_vegas.c optional inet cc_vegas tcp_hhook netinet/khelp/h_ertt.c optional inet tcp_hhook netinet/sctp_asconf.c optional inet sctp | inet6 sctp netinet/sctp_auth.c optional inet sctp | inet6 sctp netinet/sctp_bsd_addr.c optional inet sctp | inet6 sctp netinet/sctp_cc_functions.c optional inet sctp | inet6 sctp netinet/sctp_crc32.c optional inet | inet6 netinet/sctp_indata.c optional inet sctp | inet6 sctp netinet/sctp_input.c optional inet sctp | inet6 sctp netinet/sctp_kdtrace.c optional inet sctp | inet6 sctp netinet/sctp_module.c optional inet sctp | inet6 sctp netinet/sctp_output.c optional inet sctp | inet6 sctp netinet/sctp_pcb.c optional inet sctp | inet6 sctp netinet/sctp_peeloff.c optional inet sctp | inet6 sctp netinet/sctp_ss_functions.c optional inet sctp | inet6 sctp netinet/sctp_syscalls.c optional inet sctp | inet6 sctp netinet/sctp_sysctl.c optional inet sctp | inet6 sctp netinet/sctp_timer.c optional inet sctp | inet6 sctp netinet/sctp_usrreq.c optional inet sctp | inet6 sctp netinet/sctputil.c optional inet sctp | inet6 sctp netinet/siftr.c optional inet siftr alq | inet6 siftr alq netinet/tcp_ecn.c optional inet | inet6 netinet/tcp_fastopen.c optional inet tcp_rfc7413 | inet6 tcp_rfc7413 netinet/tcp_hostcache.c optional inet | inet6 netinet/tcp_input.c optional inet | inet6 netinet/tcp_log_buf.c optional tcp_blackbox inet | tcp_blackbox inet6 netinet/tcp_lro.c optional inet | inet6 netinet/tcp_lro_hpts.c optional tcphpts inet | tcphpts inet6 netinet/tcp_output.c optional inet | inet6 netinet/tcp_offload.c optional tcp_offload inet | tcp_offload inet6 netinet/tcp_hpts.c optional tcphpts inet | tcphpts inet6 netinet/tcp_ratelimit.c optional ratelimit inet | ratelimit inet6 netinet/tcp_pcap.c optional inet tcppcap | inet6 tcppcap \ compile-with "${NORMAL_C} ${NO_WNONNULL}" netinet/tcp_reass.c optional inet | inet6 netinet/tcp_sack.c optional inet | inet6 netinet/tcp_stacks/bbr.c optional inet tcphpts tcp_bbr | inet6 tcphpts tcp_bbr \ compile-with "${NORMAL_C} -DMODNAME=tcp_bbr -DSTACKNAME=bbr" netinet/tcp_stacks/rack.c optional inet tcphpts tcp_rack | inet6 tcphpts tcp_rack \ compile-with "${NORMAL_C} -DMODNAME=tcp_rack -DSTACKNAME=rack" netinet/tcp_stacks/rack_bbr_common.c optional inet tcphpts tcp_bbr | inet tcphpts tcp_rack | inet6 tcphpts tcp_bbr | inet6 tcphpts tcp_rack netinet/tcp_stacks/sack_filter.c optional inet tcphpts tcp_bbr | inet tcphpts tcp_rack | inet6 tcphpts tcp_bbr | inet6 tcphpts tcp_rack netinet/tcp_stacks/tailq_hash.c optional inet tcphpts tcp_bbr | inet tcphpts tcp_rack | inet6 tcphpts tcp_bbr | inet6 tcphpts tcp_rack netinet/tcp_stats.c optional stats inet | stats inet6 netinet/tcp_subr.c optional inet | inet6 netinet/tcp_syncache.c optional inet | inet6 netinet/tcp_timer.c optional inet | inet6 netinet/tcp_timewait.c optional inet | inet6 netinet/tcp_usrreq.c optional inet | inet6 netinet/udp_usrreq.c optional inet | inet6 netinet/libalias/alias.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_db.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_mod.c optional libalias | netgraph_nat netinet/libalias/alias_proxy.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_util.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_sctp.c optional libalias inet | netgraph_nat inet netinet/netdump/netdump_client.c optional inet debugnet netdump netinet6/dest6.c optional inet6 netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 netinet6/in6_cksum.c optional inet6 netinet6/in6_fib.c optional inet6 netinet6/in6_fib_algo.c optional inet6 fib_algo netinet6/in6_gif.c optional gif inet6 | netgraph_gif inet6 netinet6/in6_ifattach.c optional inet6 netinet6/in6_jail.c optional inet6 netinet6/in6_mcast.c optional inet6 netinet6/in6_pcb.c optional inet6 netinet6/in6_proto.c optional inet6 netinet6/in6_rmx.c optional inet6 netinet6/in6_rss.c optional inet6 rss netinet6/in6_src.c optional inet6 netinet6/ip6_fastfwd.c optional inet6 netinet6/ip6_forward.c optional inet6 netinet6/ip6_gre.c optional gre inet6 netinet6/ip6_id.c optional inet6 netinet6/ip6_input.c optional inet6 netinet6/ip6_mroute.c optional mrouting inet6 netinet6/ip6_output.c optional inet6 netinet6/mld6.c optional inet6 netinet6/nd6.c optional inet6 netinet6/nd6_nbr.c optional inet6 netinet6/nd6_rtr.c optional inet6 netinet6/raw_ip6.c optional inet6 netinet6/route6.c optional inet6 netinet6/scope6.c optional inet6 netinet6/sctp6_usrreq.c optional inet6 sctp netinet6/udp6_usrreq.c optional inet6 netipsec/ipsec.c optional ipsec inet | ipsec inet6 netipsec/ipsec_input.c optional ipsec inet | ipsec inet6 netipsec/ipsec_mbuf.c optional ipsec inet | ipsec inet6 netipsec/ipsec_mod.c optional ipsec inet | ipsec inet6 netipsec/ipsec_output.c optional ipsec inet | ipsec inet6 netipsec/ipsec_pcb.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 netipsec/key.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 netipsec/key_debug.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 netipsec/keysock.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 netipsec/subr_ipsec.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 netipsec/udpencap.c optional ipsec inet netipsec/xform_ah.c optional ipsec inet | ipsec inet6 netipsec/xform_esp.c optional ipsec inet | ipsec inet6 netipsec/xform_ipcomp.c optional ipsec inet | ipsec inet6 netipsec/xform_tcp.c optional ipsec inet tcp_signature | \ ipsec inet6 tcp_signature | ipsec_support inet tcp_signature | \ ipsec_support inet6 tcp_signature netlink/netlink_generic_kpi.c standard netlink/netlink_glue.c standard netlink/netlink_message_parser.c standard netlink/netlink_domain.c optional netlink netlink/netlink_generic.c optional netlink netlink/netlink_io.c optional netlink netlink/netlink_message_writer.c optional netlink netlink/netlink_module.c optional netlink netlink/netlink_route.c optional netlink netlink/route/iface_drivers.c optional netlink netlink/route/iface.c optional netlink netlink/route/neigh.c optional netlink netlink/route/nexthop.c optional netlink netlink/route/rt.c optional netlink netpfil/ipfw/dn_aqm_codel.c optional inet dummynet netpfil/ipfw/dn_aqm_pie.c optional inet dummynet netpfil/ipfw/dn_heap.c optional inet dummynet netpfil/ipfw/dn_sched_fifo.c optional inet dummynet netpfil/ipfw/dn_sched_fq_codel.c optional inet dummynet netpfil/ipfw/dn_sched_fq_pie.c optional inet dummynet netpfil/ipfw/dn_sched_prio.c optional inet dummynet netpfil/ipfw/dn_sched_qfq.c optional inet dummynet netpfil/ipfw/dn_sched_rr.c optional inet dummynet netpfil/ipfw/dn_sched_wf2q.c optional inet dummynet netpfil/ipfw/ip_dummynet.c optional inet dummynet netpfil/ipfw/ip_dn_io.c optional inet dummynet netpfil/ipfw/ip_dn_glue.c optional inet dummynet netpfil/ipfw/ip_fw2.c optional inet ipfirewall netpfil/ipfw/ip_fw_bpf.c optional inet ipfirewall netpfil/ipfw/ip_fw_dynamic.c optional inet ipfirewall \ compile-with "${NORMAL_C} -I$S/contrib/ck/include" netpfil/ipfw/ip_fw_eaction.c optional inet ipfirewall netpfil/ipfw/ip_fw_log.c optional inet ipfirewall netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall netpfil/ipfw/ip_fw_table.c optional inet ipfirewall netpfil/ipfw/ip_fw_table_algo.c optional inet ipfirewall netpfil/ipfw/ip_fw_table_value.c optional inet ipfirewall netpfil/ipfw/ip_fw_iface.c optional inet ipfirewall netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat netpfil/ipfw/nat64/ip_fw_nat64.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nat64/nat64clat.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nat64/nat64clat_control.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nat64/nat64lsn.c optional inet inet6 ipfirewall \ ipfirewall_nat64 compile-with "${NORMAL_C} -I$S/contrib/ck/include" netpfil/ipfw/nat64/nat64lsn_control.c optional inet inet6 ipfirewall \ ipfirewall_nat64 compile-with "${NORMAL_C} -I$S/contrib/ck/include" netpfil/ipfw/nat64/nat64stl.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nat64/nat64stl_control.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nat64/nat64_translate.c optional inet inet6 ipfirewall \ ipfirewall_nat64 netpfil/ipfw/nptv6/ip_fw_nptv6.c optional inet inet6 ipfirewall \ ipfirewall_nptv6 netpfil/ipfw/nptv6/nptv6.c optional inet inet6 ipfirewall \ ipfirewall_nptv6 netpfil/ipfw/pmod/ip_fw_pmod.c optional inet ipfirewall_pmod netpfil/ipfw/pmod/tcpmod.c optional inet ipfirewall_pmod netpfil/pf/if_pflog.c optional pflog pf inet netpfil/pf/if_pfsync.c optional pfsync pf inet netpfil/pf/pf.c optional pf inet netpfil/pf/pf_if.c optional pf inet netpfil/pf/pf_ioctl.c optional pf inet netpfil/pf/pf_lb.c optional pf inet netpfil/pf/pf_norm.c optional pf inet netpfil/pf/pf_nl.c optional pf inet netpfil/pf/pf_nv.c optional pf inet netpfil/pf/pf_osfp.c optional pf inet netpfil/pf/pf_ruleset.c optional pf inet netpfil/pf/pf_syncookies.c optional pf inet netpfil/pf/pf_table.c optional pf inet netpfil/pf/pfsync_nv.c optional pfsync pf inet netpfil/pf/in4_cksum.c optional pf inet netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb netsmb/smb_iod.c optional netsmb netsmb/smb_rq.c optional netsmb netsmb/smb_smb.c optional netsmb netsmb/smb_subr.c optional netsmb netsmb/smb_trantcp.c optional netsmb netsmb/smb_usr.c optional netsmb nfs/bootp_subr.c optional bootp nfscl nfs/krpc_subr.c optional bootp nfscl nfs/nfs_diskless.c optional nfscl nfs_root nfs/nfs_nfssvc.c optional nfscl | nfslockd | nfsd nlm/nlm_advlock.c optional nfslockd | nfsd nlm/nlm_prot_clnt.c optional nfslockd | nfsd nlm/nlm_prot_impl.c optional nfslockd | nfsd nlm/nlm_prot_server.c optional nfslockd | nfsd nlm/nlm_prot_svc.c optional nfslockd | nfsd nlm/nlm_prot_xdr.c optional nfslockd | nfsd nlm/sm_inter_xdr.c optional nfslockd | nfsd # Linux Kernel Programming Interface compat/linuxkpi/common/src/linux_80211.c optional compat_linuxkpi wlan \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_80211_macops.c optional compat_linuxkpi wlan \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_kmod.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_acpi.c optional compat_linuxkpi acpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_compat.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_current.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_devres.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_dmi.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_domain.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_firmware.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_fpu.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_hrtimer.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_i2c.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_i2cbb.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_interrupt.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_kobject.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_kthread.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_lock.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_mhi.c optional compat_linuxkpi wlan \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_netdev.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_page.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_pci.c optional compat_linuxkpi pci \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_tasklet.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_idr.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_radix.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_rcu.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C} -I$S/contrib/ck/include" compat/linuxkpi/common/src/linux_schedule.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_shmemfs.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_shrinker.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_skbuff.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_slab.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_usb.c optional compat_linuxkpi usb \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_work.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_xarray.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/lkpi_iic_if.m optional compat_linuxkpi compat/linuxkpi/common/src/linux_seq_file.c optional compat_linuxkpi | lindebugfs \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_simple_attr.c optional compat_linuxkpi | lindebugfs \ compile-with "${LINUXKPI_C}" compat/lindebugfs/lindebugfs.c optional lindebugfs \ compile-with "${LINUXKPI_C}" # OpenFabrics Enterprise Distribution (Infiniband) net/if_infiniband.c optional ofed | lagg ofed/drivers/infiniband/core/ib_addr.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_agent.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_cache.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_cm.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_cma.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_core_uverbs.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_cq.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_device.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_fmr_pool.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_iwcm.c optional ofed \ compile-with "${OFED_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" ofed/drivers/infiniband/core/ib_iwpm_msg.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_iwpm_util.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_mad.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_mad_rmpp.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_multicast.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_packer.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_rdma_core.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_roce_gid_mgmt.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_sa_query.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_smi.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_sysfs.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_ucm.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_ucma.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_ud_header.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_umem.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_user_mad.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_cmd.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_ioctl.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_main.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_marshall.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_async_fd.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_counters.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_cq.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_device.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_dm.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_flow_action.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_std_types_mr.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_uverbs_uapi.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/core/ib_verbs.c optional ofed \ compile-with "${OFED_C}" ofed/drivers/infiniband/ulp/ipoib/ipoib_cm.c optional ipoib \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" #ofed/drivers/infiniband/ulp/ipoib/ipoib_fs.c optional ipoib \ # compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" ofed/drivers/infiniband/ulp/ipoib/ipoib_ib.c optional ipoib \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c optional ipoib \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c optional ipoib \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" ofed/drivers/infiniband/ulp/ipoib/ipoib_verbs.c optional ipoib \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" #ofed/drivers/infiniband/ulp/ipoib/ipoib_vlan.c optional ipoib \ # compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/ipoib/" ofed/drivers/infiniband/ulp/sdp/sdp_bcopy.c optional sdp inet \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/sdp/" ofed/drivers/infiniband/ulp/sdp/sdp_main.c optional sdp inet \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/sdp/" ofed/drivers/infiniband/ulp/sdp/sdp_rx.c optional sdp inet \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/sdp/ ${NO_WUNUSED_BUT_SET_VARIABLE}" ofed/drivers/infiniband/ulp/sdp/sdp_cma.c optional sdp inet \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/sdp/" ofed/drivers/infiniband/ulp/sdp/sdp_tx.c optional sdp inet \ compile-with "${OFED_C} -I$S/ofed/drivers/infiniband/ulp/sdp/ ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/irdma/icrdma.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_cm.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_ctrl.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_hmc.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_hw.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/icrdma_hw.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/fbsd_kcompat.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_kcompat.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_pble.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_puda.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_uda.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_uk.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_utils.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_verbs.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/irdma/irdma_ws.c optional irdma ice inet inet6 pci ofed \ compile-with "${OFED_C} -I$S/dev/ice/" dev/mthca/mthca_allocator.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_av.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_catas.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_cmd.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_cq.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_eq.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_mad.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_main.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_mcg.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_memfree.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_mr.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_pd.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_profile.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_provider.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_qp.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_reset.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_srq.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mthca/mthca_uar.c optional mthca pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_alias_GUID.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_mcg.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_sysfs.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_cm.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_ah.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_cq.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_doorbell.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_mad.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_main.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_mr.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_qp.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_srq.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_ib/mlx4_ib_wc.c optional mlx4ib pci ofed \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_alloc.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_catas.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_cmd.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_cq.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_eq.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_fw.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_fw_qos.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_icm.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_intf.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_main.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_mcg.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_mr.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_pd.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_port.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_profile.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_qp.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_reset.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_sense.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_srq.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_core/mlx4_resource_tracker.c optional mlx4 pci \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_cq.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_main.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_netdev.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_port.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_resources.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_rx.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx4/mlx4_en/mlx4_en_tx.c optional mlx4en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_ah.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_cong.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_cq.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_devx.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_doorbell.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_gsi.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_mad.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_main.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_mem.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_mr.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_qp.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_srq.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_ib/mlx5_ib_virt.c optional mlx5ib pci ofed \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_alloc.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_cmd.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_cq.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_diag_cnt.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_diagnostics.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_eq.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_eswitch.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fc_cmd.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fs_cmd.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fs_counters.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fs_tcp.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fs_tree.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fw.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_fwdump.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_health.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_mad.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_main.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_mcg.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_mpfs.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_mr.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_pagealloc.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_pd.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_port.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_qp.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_rl.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_srq.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_tls.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_transobj.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_uar.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_vport.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_vsc.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_core/mlx5_wq.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_lib/mlx5_gid.c optional mlx5 pci \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_dim.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_ethtool.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_main.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_tx.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_flow_table.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_hw_tls.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_hw_tls_rx.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_iq.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_rx.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_rl.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_txrx.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" dev/mlx5/mlx5_en/mlx5_en_port_buffer.c optional mlx5en pci inet inet6 \ compile-with "${OFED_C}" # crypto support opencrypto/cbc_mac.c optional crypto opencrypto/criov.c optional crypto opencrypto/crypto.c optional crypto opencrypto/cryptodev.c optional cryptodev opencrypto/cryptodev_if.m optional crypto opencrypto/cryptosoft.c optional crypto opencrypto/cryptodeflate.c optional crypto opencrypto/gmac.c optional crypto opencrypto/gfmult.c optional crypto opencrypto/ktls_ocf.c optional kern_tls opencrypto/rmd160.c optional crypto opencrypto/xform_aes_cbc.c optional crypto opencrypto/xform_aes_icm.c optional crypto opencrypto/xform_aes_xts.c optional crypto opencrypto/xform_cbc_mac.c optional crypto opencrypto/xform_chacha20_poly1305.c optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium" opencrypto/xform_cml.c optional crypto opencrypto/xform_deflate.c optional crypto opencrypto/xform_gmac.c optional crypto opencrypto/xform_null.c optional crypto opencrypto/xform_poly1305.c optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium" opencrypto/xform_rmd160.c optional crypto opencrypto/xform_sha1.c optional crypto opencrypto/xform_sha2.c optional crypto contrib/libsodium/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium -Wno-unused-function" contrib/libsodium/src/libsodium/crypto_core/hchacha20/core_hchacha20.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_onetimeauth/poly1305/donna/poly1305_donna.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_scalarmult/curve25519/scalarmult_curve25519.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium -Wno-unused-function" contrib/libsodium/src/libsodium/crypto_stream/chacha20/stream_chacha20.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" contrib/libsodium/src/libsodium/crypto_verify/sodium/verify.c \ optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include/sodium -I$S/crypto/libsodium" crypto/libsodium/randombytes.c optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium" crypto/libsodium/utils.c optional crypto \ compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium" rpc/auth_none.c optional krpc | nfslockd | nfscl | nfsd rpc/auth_unix.c optional krpc | nfslockd | nfscl | nfsd rpc/authunix_prot.c optional krpc | nfslockd | nfscl | nfsd rpc/clnt_bck.c optional krpc | nfslockd | nfscl | nfsd rpc/clnt_dg.c optional krpc | nfslockd | nfscl | nfsd rpc/clnt_rc.c optional krpc | nfslockd | nfscl | nfsd rpc/clnt_vc.c optional krpc | nfslockd | nfscl | nfsd rpc/getnetconfig.c optional krpc | nfslockd | nfscl | nfsd rpc/replay.c optional krpc | nfslockd | nfscl | nfsd rpc/rpc_callmsg.c optional krpc | nfslockd | nfscl | nfsd rpc/rpc_generic.c optional krpc | nfslockd | nfscl | nfsd rpc/rpc_prot.c optional krpc | nfslockd | nfscl | nfsd rpc/rpcb_clnt.c optional krpc | nfslockd | nfscl | nfsd rpc/rpcb_prot.c optional krpc | nfslockd | nfscl | nfsd rpc/svc.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_auth.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_auth_unix.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_dg.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_generic.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_vc.c optional krpc | nfslockd | nfscl | nfsd # # Kernel RPC-over-TLS # rpctlscd.h optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlscd.x" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -hM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v pthread.h > rpctlscd.h" \ no-obj no-implicit-rule before-depend local \ clean "rpctlscd.h" rpctlscd_xdr.c optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlscd.x rpctlscd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -c $S/rpc/rpcsec_tls/rpctlscd.x -o rpctlscd_xdr.c" no-ctfconvert \ no-implicit-rule before-depend local \ clean "rpctlscd_xdr.c" rpctlscd_clnt.c optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlscd.x rpctlscd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -lM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v string.h > rpctlscd_clnt.c" no-ctfconvert \ no-implicit-rule before-depend local \ clean "rpctlscd_clnt.c" rpctlssd.h optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlssd.x" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -hM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v pthread.h > rpctlssd.h" \ no-obj no-implicit-rule before-depend local \ clean "rpctlssd.h" rpctlssd_xdr.c optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlssd.x rpctlssd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -c $S/rpc/rpcsec_tls/rpctlssd.x -o rpctlssd_xdr.c" no-ctfconvert \ no-implicit-rule before-depend local \ clean "rpctlssd_xdr.c" rpctlssd_clnt.c optional krpc | nfslockd | nfscl | nfsd \ dependency "$S/rpc/rpcsec_tls/rpctlssd.x rpctlssd.h" \ compile-with "RPCGEN_CPP='${CPP}' rpcgen -lM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v string.h > rpctlssd_clnt.c" no-ctfconvert \ no-implicit-rule before-depend local \ clean "rpctlssd_clnt.c" rpc/rpcsec_tls/rpctls_impl.c optional krpc | nfslockd | nfscl | nfsd rpc/rpcsec_tls/auth_tls.c optional krpc | nfslockd | nfscl | nfsd rpc/rpcsec_gss/rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/rpcsec_gss_conf.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/rpcsec_gss_misc.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/rpcsec_gss_prot.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/svc_rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi security/audit/audit.c optional audit security/audit/audit_arg.c optional audit security/audit/audit_bsm.c optional audit security/audit/audit_bsm_db.c optional audit security/audit/audit_bsm_klib.c optional audit security/audit/audit_dtrace.c optional dtaudit audit | dtraceall audit compile-with "${CDDL_C}" security/audit/audit_pipe.c optional audit security/audit/audit_syscalls.c standard security/audit/audit_trigger.c optional audit security/audit/audit_worker.c optional audit security/audit/bsm_domain.c optional audit security/audit/bsm_errno.c optional audit security/audit/bsm_fcntl.c optional audit security/audit/bsm_socket_type.c optional audit security/audit/bsm_token.c optional audit security/mac/mac_audit.c optional mac audit security/mac/mac_cred.c optional mac security/mac/mac_kdb.c optional mac security/mac/mac_framework.c optional mac security/mac/mac_inet.c optional mac inet | mac inet6 security/mac/mac_inet6.c optional mac inet6 security/mac/mac_label.c optional mac security/mac/mac_net.c optional mac security/mac/mac_pipe.c optional mac security/mac/mac_posix_sem.c optional mac security/mac/mac_posix_shm.c optional mac security/mac/mac_priv.c optional mac security/mac/mac_process.c optional mac security/mac/mac_socket.c optional mac security/mac/mac_syscalls.c standard security/mac/mac_system.c optional mac security/mac/mac_sysv_msg.c optional mac security/mac/mac_sysv_sem.c optional mac security/mac/mac_sysv_shm.c optional mac security/mac/mac_vfs.c optional mac security/mac_biba/mac_biba.c optional mac_biba security/mac_ddb/mac_ddb.c optional mac_ddb security/mac_bsdextended/mac_bsdextended.c optional mac_bsdextended security/mac_bsdextended/ugidfw_system.c optional mac_bsdextended security/mac_bsdextended/ugidfw_vnode.c optional mac_bsdextended security/mac_ifoff/mac_ifoff.c optional mac_ifoff security/mac_ipacl/mac_ipacl.c optional mac_ipacl security/mac_lomac/mac_lomac.c optional mac_lomac security/mac_mls/mac_mls.c optional mac_mls security/mac_none/mac_none.c optional mac_none security/mac_ntpd/mac_ntpd.c optional mac_ntpd security/mac_partition/mac_partition.c optional mac_partition security/mac_portacl/mac_portacl.c optional mac_portacl security/mac_priority/mac_priority.c optional mac_priority security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids security/mac_stub/mac_stub.c optional mac_stub security/mac_test/mac_test.c optional mac_test security/mac_grantbylabel/mac_grantbylabel.c optional mac_grantbylabel security/mac_veriexec/mac_veriexec.c optional mac_veriexec security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec security/mac_veriexec/veriexec_metadata.c optional mac_veriexec security/mac_veriexec_parser/mac_veriexec_parser.c optional mac_veriexec mac_veriexec_parser security/mac_veriexec/mac_veriexec_rmd160.c optional mac_veriexec_rmd160 security/mac_veriexec/mac_veriexec_sha1.c optional mac_veriexec_sha1 security/mac_veriexec/mac_veriexec_sha256.c optional mac_veriexec_sha256 security/mac_veriexec/mac_veriexec_sha384.c optional mac_veriexec_sha384 security/mac_veriexec/mac_veriexec_sha512.c optional mac_veriexec_sha512 teken/teken.c optional sc !SC_NO_TERM_TEKEN | vt ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_snapshot.c optional ffs ufs/ffs/ffs_softdep.c optional ffs ufs/ffs/ffs_subr.c optional ffs | geom_label ufs/ffs/ffs_tables.c optional ffs | geom_label ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_rawread.c optional ffs directio ufs/ffs/ffs_suspend.c optional ffs ufs/ufs/ufs_acl.c optional ffs ufs/ufs/ufs_bmap.c optional ffs ufs/ufs/ufs_dirhash.c optional ffs ufs/ufs/ufs_extattr.c optional ffs ufs/ufs/ufs_gjournal.c optional ffs UFS_GJOURNAL ufs/ufs/ufs_inode.c optional ffs ufs/ufs/ufs_lookup.c optional ffs ufs/ufs/ufs_quota.c optional ffs ufs/ufs/ufs_vfsops.c optional ffs ufs/ufs/ufs_vnops.c optional ffs vm/device_pager.c standard vm/phys_pager.c standard vm/redzone.c optional DEBUG_REDZONE vm/sg_pager.c standard vm/swap_pager.c standard vm/uma_core.c standard vm/uma_dbg.c standard vm/memguard.c optional DEBUG_MEMGUARD vm/vm_domainset.c standard vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pager.c standard vm/vm_phys.c standard vm/vm_radix.c standard vm/vm_reserv.c standard vm/vm_swapout.c optional !NO_SWAPPING vm/vm_swapout_dummy.c optional NO_SWAPPING vm/vm_unix.c standard vm/vnode_pager.c standard xen/features.c optional xenhvm xen/xen_common.c optional xenhvm xen/xenbus/xenbus_if.m optional xenhvm xen/xenbus/xenbus.c optional xenhvm xen/xenbus/xenbusb_if.m optional xenhvm xen/xenbus/xenbusb.c optional xenhvm xen/xenbus/xenbusb_front.c optional xenhvm xen/xenbus/xenbusb_back.c optional xenhvm xen/xenmem/xenmem_if.m optional xenhvm xdr/xdr.c optional xdr | krpc | nfslockd | nfscl | nfsd xdr/xdr_array.c optional xdr | krpc | nfslockd | nfscl | nfsd xdr/xdr_mbuf.c optional xdr | krpc | nfslockd | nfscl | nfsd xdr/xdr_mem.c optional xdr | krpc | nfslockd | nfscl | nfsd xdr/xdr_reference.c optional xdr | krpc | nfslockd | nfscl | nfsd xdr/xdr_sizeof.c optional xdr | krpc | nfslockd | nfscl | nfsd diff --git a/sys/dev/clk/allwinner/aw_ccung.c b/sys/dev/clk/allwinner/aw_ccung.c index f446b77b6226..751ebba6c349 100644 --- a/sys/dev/clk/allwinner/aw_ccung.c +++ b/sys/dev/clk/allwinner/aw_ccung.c @@ -1,359 +1,359 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2017,2018 Emmanuel Vadot * * 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 ``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 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. */ /* * Allwinner Clock Control Unit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #ifdef __aarch64__ #include "opt_soc.h" #endif #include "clkdev_if.h" #include "hwreset_if.h" #if 0 #define dprintf(format, arg...) device_printf(dev, "%s: " format, __func__, arg) #else #define dprintf(format, arg...) #endif static struct resource_spec aw_ccung_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg)) #define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static int aw_ccung_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct aw_ccung_softc *sc; sc = device_get_softc(dev); dprintf("offset=%lx write %x\n", addr, val); CCU_WRITE4(sc, addr, val); return (0); } static int aw_ccung_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct aw_ccung_softc *sc; sc = device_get_softc(dev); *val = CCU_READ4(sc, addr); dprintf("offset=%lx Read %x\n", addr, *val); return (0); } static int aw_ccung_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) { struct aw_ccung_softc *sc; uint32_t reg; sc = device_get_softc(dev); dprintf("offset=%lx clr: %x set: %x\n", addr, clr, set); reg = CCU_READ4(sc, addr); reg &= ~clr; reg |= set; CCU_WRITE4(sc, addr, reg); return (0); } static int aw_ccung_reset_assert(device_t dev, intptr_t id, bool reset) { struct aw_ccung_softc *sc; uint32_t val; sc = device_get_softc(dev); dprintf("%sassert reset id %ld\n", reset ? "" : "De", id); if (id >= sc->nresets || sc->resets[id].offset == 0) return (0); mtx_lock(&sc->mtx); val = CCU_READ4(sc, sc->resets[id].offset); dprintf("offset=%x Read %x\n", sc->resets[id].offset, val); if (reset) val &= ~(1 << sc->resets[id].shift); else val |= 1 << sc->resets[id].shift; dprintf("offset=%x Write %x\n", sc->resets[id].offset, val); CCU_WRITE4(sc, sc->resets[id].offset, val); mtx_unlock(&sc->mtx); return (0); } static int aw_ccung_reset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct aw_ccung_softc *sc; uint32_t val; sc = device_get_softc(dev); if (id >= sc->nresets || sc->resets[id].offset == 0) return (0); mtx_lock(&sc->mtx); val = CCU_READ4(sc, sc->resets[id].offset); dprintf("offset=%x Read %x\n", sc->resets[id].offset, val); *reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true; mtx_unlock(&sc->mtx); return (0); } static void aw_ccung_device_lock(device_t dev) { struct aw_ccung_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void aw_ccung_device_unlock(device_t dev) { struct aw_ccung_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int aw_ccung_register_gates(struct aw_ccung_softc *sc) { struct clk_gate_def def; int i; for (i = 0; i < sc->ngates; i++) { if (sc->gates[i].name == NULL) continue; memset(&def, 0, sizeof(def)); def.clkdef.id = i; def.clkdef.name = sc->gates[i].name; def.clkdef.parent_names = &sc->gates[i].parent_name; def.clkdef.parent_cnt = 1; def.offset = sc->gates[i].offset; def.shift = sc->gates[i].shift; def.mask = 1; def.on_value = 1; def.off_value = 0; clknode_gate_register(sc->clkdom, &def); } return (0); } static void aw_ccung_init_clocks(struct aw_ccung_softc *sc) { struct clknode *clknode; int i, error; for (i = 0; i < sc->n_clk_init; i++) { clknode = clknode_find_by_name(sc->clk_init[i].name); if (clknode == NULL) { device_printf(sc->dev, "Cannot find clock %s\n", sc->clk_init[i].name); continue; } if (sc->clk_init[i].parent_name != NULL) { if (bootverbose) device_printf(sc->dev, "Setting %s as parent for %s\n", sc->clk_init[i].parent_name, sc->clk_init[i].name); error = clknode_set_parent_by_name(clknode, sc->clk_init[i].parent_name); if (error != 0) { device_printf(sc->dev, "Cannot set parent to %s for %s\n", sc->clk_init[i].parent_name, sc->clk_init[i].name); continue; } } if (sc->clk_init[i].default_freq != 0) { if (bootverbose) device_printf(sc->dev, "Setting freq %ju for %s\n", sc->clk_init[i].default_freq, sc->clk_init[i].name); error = clknode_set_freq(clknode, sc->clk_init[i].default_freq, 0 , 0); if (error != 0) { device_printf(sc->dev, "Cannot set frequency for %s to %ju\n", sc->clk_init[i].name, sc->clk_init[i].default_freq); continue; } } if (sc->clk_init[i].enable) { error = clknode_enable(clknode); if (error != 0) { device_printf(sc->dev, "Cannot enable %s\n", sc->clk_init[i].name); continue; } } } } int aw_ccung_attach(device_t dev) { struct aw_ccung_softc *sc; int i; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, aw_ccung_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("Cannot create clkdom\n"); for (i = 0; i < sc->nclks; i++) { switch (sc->clks[i].type) { case AW_CLK_UNDEFINED: break; case AW_CLK_MUX: clknode_mux_register(sc->clkdom, sc->clks[i].clk.mux); break; case AW_CLK_DIV: clknode_div_register(sc->clkdom, sc->clks[i].clk.div); break; case AW_CLK_FIXED: clknode_fixed_register(sc->clkdom, sc->clks[i].clk.fixed); break; case AW_CLK_NKMP: aw_clk_nkmp_register(sc->clkdom, sc->clks[i].clk.nkmp); break; case AW_CLK_NM: aw_clk_nm_register(sc->clkdom, sc->clks[i].clk.nm); break; case AW_CLK_M: aw_clk_m_register(sc->clkdom, sc->clks[i].clk.m); break; case AW_CLK_PREDIV_MUX: aw_clk_prediv_mux_register(sc->clkdom, sc->clks[i].clk.prediv_mux); break; case AW_CLK_FRAC: aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac); break; case AW_CLK_MIPI: aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi); break; case AW_CLK_NP: aw_clk_np_register(sc->clkdom, sc->clks[i].clk.np); break; case AW_CLK_NMM: aw_clk_nmm_register(sc->clkdom, sc->clks[i].clk.nmm); break; } } if (sc->gates) aw_ccung_register_gates(sc); if (clkdom_finit(sc->clkdom) != 0) panic("cannot finalize clkdom initialization\n"); clkdom_xlock(sc->clkdom); aw_ccung_init_clocks(sc); clkdom_unlock(sc->clkdom); if (bootverbose) clkdom_dump(sc->clkdom); /* If we have resets, register our self as a reset provider */ if (sc->resets) hwreset_register_ofw_provider(dev); return (0); } static device_method_t aw_ccung_methods[] = { /* clkdev interface */ DEVMETHOD(clkdev_write_4, aw_ccung_write_4), DEVMETHOD(clkdev_read_4, aw_ccung_read_4), DEVMETHOD(clkdev_modify_4, aw_ccung_modify_4), DEVMETHOD(clkdev_device_lock, aw_ccung_device_lock), DEVMETHOD(clkdev_device_unlock, aw_ccung_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, aw_ccung_reset_assert), DEVMETHOD(hwreset_is_asserted, aw_ccung_reset_is_asserted), DEVMETHOD_END }; DEFINE_CLASS_0(aw_ccung, aw_ccung_driver, aw_ccung_methods, sizeof(struct aw_ccung_softc)); diff --git a/sys/dev/clk/allwinner/ccu_de2.c b/sys/dev/clk/allwinner/ccu_de2.c index 4bd29d139c11..cc4debdbfc89 100644 --- a/sys/dev/clk/allwinner/ccu_de2.c +++ b/sys/dev/clk/allwinner/ccu_de2.c @@ -1,233 +1,233 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 Emmanuel Vadot * * 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 ``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 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 #ifdef __aarch64__ #include "opt_soc.h" #endif #include #include #include -#include +#include #include #include #include enum CCU_DE2 { H3_CCU = 1, A64_CCU, }; /* Non exported clocks */ #define CLK_MIXER0_DIV 3 #define CLK_MIXER1_DIV 4 #define CLK_WB_DIV 5 static struct aw_ccung_reset h3_de2_ccu_resets[] = { CCU_RESET(RST_MIXER0, 0x08, 0) CCU_RESET(RST_WB, 0x08, 2) }; static struct aw_ccung_reset a64_de2_ccu_resets[] = { CCU_RESET(RST_MIXER0, 0x08, 0) CCU_RESET(RST_MIXER1, 0x08, 1) CCU_RESET(RST_WB, 0x08, 2) }; static struct aw_ccung_gate h3_de2_ccu_gates[] = { CCU_GATE(CLK_BUS_MIXER0, "mixer0", "mixer0-div", 0x00, 0) CCU_GATE(CLK_BUS_WB, "wb", "wb-div", 0x00, 2) CCU_GATE(CLK_MIXER0, "bus-mixer0", "bus-de", 0x04, 0) CCU_GATE(CLK_WB, "bus-wb", "bus-de", 0x04, 2) }; static struct aw_ccung_gate a64_de2_ccu_gates[] = { CCU_GATE(CLK_BUS_MIXER0, "mixer0", "mixer0-div", 0x00, 0) CCU_GATE(CLK_BUS_MIXER1, "mixer1", "mixer1-div", 0x00, 1) CCU_GATE(CLK_BUS_WB, "wb", "wb-div", 0x00, 2) CCU_GATE(CLK_MIXER0, "bus-mixer0", "bus-de", 0x04, 0) CCU_GATE(CLK_MIXER1, "bus-mixer1", "bus-de", 0x04, 1) CCU_GATE(CLK_WB, "bus-wb", "bus-de", 0x04, 2) }; static const char *div_parents[] = {"de"}; NM_CLK(mixer0_div_clk, CLK_MIXER0_DIV, /* id */ "mixer0-div", div_parents, /* names, parents */ 0x0C, /* offset */ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/ 0, 4, 0, 0, /* M flags */ 0, 0, /* mux */ 0, /* gate */ AW_CLK_SCALE_CHANGE); /* flags */ NM_CLK(mixer1_div_clk, CLK_MIXER1_DIV, /* id */ "mixer1-div", div_parents, /* names, parents */ 0x0C, /* offset */ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/ 4, 4, 0, 0, /* M flags */ 0, 0, /* mux */ 0, /* gate */ AW_CLK_SCALE_CHANGE); /* flags */ NM_CLK(wb_div_clk, CLK_WB_DIV, /* id */ "wb-div", div_parents, /* names, parents */ 0x0C, /* offset */ 0, 0, 1, AW_CLK_FACTOR_FIXED, /* N factor (fake)*/ 8, 4, 0, 0, /* M flags */ 0, 0, /* mux */ 0, /* gate */ AW_CLK_SCALE_CHANGE); /* flags */ static struct aw_ccung_clk h3_de2_ccu_clks[] = { { .type = AW_CLK_NM, .clk.nm = &mixer0_div_clk}, { .type = AW_CLK_NM, .clk.nm = &wb_div_clk}, }; static struct aw_ccung_clk a64_de2_ccu_clks[] = { { .type = AW_CLK_NM, .clk.nm = &mixer0_div_clk}, { .type = AW_CLK_NM, .clk.nm = &mixer1_div_clk}, { .type = AW_CLK_NM, .clk.nm = &wb_div_clk}, }; static struct ofw_compat_data compat_data[] = { {"allwinner,sun8i-h3-de2-clk", H3_CCU}, {"allwinner,sun50i-a64-de2-clk", A64_CCU}, {NULL, 0} }; static int ccu_de2_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner DE2 Clock Control Unit"); return (BUS_PROBE_DEFAULT); } static int ccu_de2_attach(device_t dev) { struct aw_ccung_softc *sc; phandle_t node; clk_t mod, bus; hwreset_t rst_de; enum CCU_DE2 type; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); type = (enum CCU_DE2)ofw_bus_search_compatible(dev, compat_data)->ocd_data; switch (type) { case H3_CCU: sc->resets = h3_de2_ccu_resets; sc->nresets = nitems(h3_de2_ccu_resets); sc->gates = h3_de2_ccu_gates; sc->ngates = nitems(h3_de2_ccu_gates); sc->clks = h3_de2_ccu_clks; sc->nclks = nitems(h3_de2_ccu_clks); break; case A64_CCU: sc->resets = a64_de2_ccu_resets; sc->nresets = nitems(a64_de2_ccu_resets); sc->gates = a64_de2_ccu_gates; sc->ngates = nitems(a64_de2_ccu_gates); sc->clks = a64_de2_ccu_clks; sc->nclks = nitems(a64_de2_ccu_clks); break; } if (hwreset_get_by_ofw_idx(dev, node, 0, &rst_de) != 0) { device_printf(dev, "Cannot get de reset\n"); return (ENXIO); } if (hwreset_deassert(rst_de) != 0) { device_printf(dev, "Cannot de-assert de reset\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, node, "mod", &mod) != 0) { device_printf(dev, "Cannot get mod clock\n"); return (ENXIO); } if (clk_enable(mod) != 0) { device_printf(dev, "Cannot enable mod clock\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, node, "bus", &bus) != 0) { device_printf(dev, "Cannot get bus clock\n"); return (ENXIO); } if (clk_enable(bus) != 0) { device_printf(dev, "Cannot enable bus clock\n"); return (ENXIO); } return (aw_ccung_attach(dev)); } static device_method_t ccu_de2_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ccu_de2_probe), DEVMETHOD(device_attach, ccu_de2_attach), DEVMETHOD_END }; DEFINE_CLASS_1(ccu_de2, ccu_de2_driver, ccu_de2_methods, sizeof(struct aw_ccung_softc), aw_ccung_driver); EARLY_DRIVER_MODULE(ccu_de2, simplebus, ccu_de2_driver, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LAST); diff --git a/sys/dev/clk/rockchip/rk_cru.c b/sys/dev/clk/rockchip/rk_cru.c index a7d1b26166f1..6b0978e885b5 100644 --- a/sys/dev/clk/rockchip/rk_cru.c +++ b/sys/dev/clk/rockchip/rk_cru.c @@ -1,305 +1,305 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 Emmanuel Vadot * * 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 ``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 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. */ /* * RockChip Clock and Reset Unit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include "clkdev_if.h" #include "hwreset_if.h" static struct resource_spec rk_cru_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg)) #define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) void rk3328_cru_register_clocks(struct rk_cru_softc *sc); static int rk_cru_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct rk_cru_softc *sc; sc = device_get_softc(dev); CCU_WRITE4(sc, addr, val); return (0); } static int rk_cru_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct rk_cru_softc *sc; sc = device_get_softc(dev); *val = CCU_READ4(sc, addr); return (0); } static int rk_cru_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) { struct rk_cru_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = CCU_READ4(sc, addr); reg &= ~clr; reg |= set; CCU_WRITE4(sc, addr, reg); return (0); } static int rk_cru_reset_assert(device_t dev, intptr_t id, bool reset) { struct rk_cru_softc *sc; uint32_t reg; int bit; uint32_t val; sc = device_get_softc(dev); if (id > sc->reset_num) return (ENXIO); reg = sc->reset_offset + id / 16 * 4; bit = id % 16; mtx_lock(&sc->mtx); val = 0; if (reset) val = (1 << bit); CCU_WRITE4(sc, reg, val | ((1 << bit) << 16)); mtx_unlock(&sc->mtx); return (0); } static int rk_cru_reset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct rk_cru_softc *sc; uint32_t reg; int bit; uint32_t val; sc = device_get_softc(dev); if (id > sc->reset_num) return (ENXIO); reg = sc->reset_offset + id / 16 * 4; bit = id % 16; mtx_lock(&sc->mtx); val = CCU_READ4(sc, reg); mtx_unlock(&sc->mtx); *reset = false; if (val & (1 << bit)) *reset = true; return (0); } static void rk_cru_device_lock(device_t dev) { struct rk_cru_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void rk_cru_device_unlock(device_t dev) { struct rk_cru_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int rk_cru_register_gates(struct rk_cru_softc *sc) { struct rk_clk_gate_def def; int i; for (i = 0; i < sc->ngates; i++) { if (sc->gates[i].name == NULL) continue; memset(&def, 0, sizeof(def)); def.clkdef.id = sc->gates[i].id; def.clkdef.name = sc->gates[i].name; def.clkdef.parent_names = &sc->gates[i].parent_name; def.clkdef.parent_cnt = 1; def.offset = sc->gates[i].offset; def.shift = sc->gates[i].shift; def.mask = 1; def.on_value = 0; def.off_value = 1; rk_clk_gate_register(sc->clkdom, &def); } return (0); } int rk_cru_attach(device_t dev) { struct rk_cru_softc *sc; phandle_t node; int i; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); if (bus_alloc_resources(dev, rk_cru_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("Cannot create clkdom\n"); for (i = 0; i < sc->nclks; i++) { switch (sc->clks[i].type) { case RK_CLK_UNDEFINED: break; case RK3066_CLK_PLL: rk3066_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); break; case RK3328_CLK_PLL: rk3328_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); break; case RK3399_CLK_PLL: rk3399_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); break; case RK_CLK_COMPOSITE: rk_clk_composite_register(sc->clkdom, sc->clks[i].clk.composite); break; case RK_CLK_MUX: rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux); break; case RK_CLK_ARMCLK: rk_clk_armclk_register(sc->clkdom, sc->clks[i].clk.armclk); break; case RK_CLK_FIXED: clknode_fixed_register(sc->clkdom, sc->clks[i].clk.fixed); break; case RK_CLK_FRACT: rk_clk_fract_register(sc->clkdom, sc->clks[i].clk.fract); break; case RK_CLK_LINK: clknode_link_register(sc->clkdom, sc->clks[i].clk.link); break; default: device_printf(dev, "Unknown clock type\n"); return (ENXIO); } } if (sc->gates) rk_cru_register_gates(sc); if (clkdom_finit(sc->clkdom) != 0) panic("cannot finalize clkdom initialization\n"); if (bootverbose) clkdom_dump(sc->clkdom); clk_set_assigned(dev, node); /* register our self as a reset provider */ hwreset_register_ofw_provider(dev); return (0); } static device_method_t rk_cru_methods[] = { /* clkdev interface */ DEVMETHOD(clkdev_write_4, rk_cru_write_4), DEVMETHOD(clkdev_read_4, rk_cru_read_4), DEVMETHOD(clkdev_modify_4, rk_cru_modify_4), DEVMETHOD(clkdev_device_lock, rk_cru_device_lock), DEVMETHOD(clkdev_device_unlock, rk_cru_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, rk_cru_reset_assert), DEVMETHOD(hwreset_is_asserted, rk_cru_reset_is_asserted), DEVMETHOD_END }; DEFINE_CLASS_0(rk_cru, rk_cru_driver, rk_cru_methods, sizeof(struct rk_cru_softc)); diff --git a/sys/dev/clk/xilinx/zynqmp_reset.c b/sys/dev/clk/xilinx/zynqmp_reset.c index 52074d4c62af..c89a4c8ff58c 100644 --- a/sys/dev/clk/xilinx/zynqmp_reset.c +++ b/sys/dev/clk/xilinx/zynqmp_reset.c @@ -1,251 +1,251 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG * * 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 #include -#include +#include #include #include "hwreset_if.h" #include "zynqmp_firmware_if.h" #define ZYNQMP_RESET_PCIE_CFG 0 #define ZYNQMP_RESET_PCIE_BRIDGE 1 #define ZYNQMP_RESET_PCIE_CTRL 2 #define ZYNQMP_RESET_DP 3 #define ZYNQMP_RESET_SWDT_CRF 4 #define ZYNQMP_RESET_AFI_FM5 5 #define ZYNQMP_RESET_AFI_FM4 6 #define ZYNQMP_RESET_AFI_FM3 7 #define ZYNQMP_RESET_AFI_FM2 8 #define ZYNQMP_RESET_AFI_FM1 9 #define ZYNQMP_RESET_AFI_FM0 10 #define ZYNQMP_RESET_GDMA 11 #define ZYNQMP_RESET_GPU_PP1 12 #define ZYNQMP_RESET_GPU_PP0 13 #define ZYNQMP_RESET_GPU 14 #define ZYNQMP_RESET_GT 15 #define ZYNQMP_RESET_SATA 16 #define ZYNQMP_RESET_ACPU3_PWRON 17 #define ZYNQMP_RESET_ACPU2_PWRON 18 #define ZYNQMP_RESET_ACPU1_PWRON 19 #define ZYNQMP_RESET_ACPU0_PWRON 20 #define ZYNQMP_RESET_APU_L2 21 #define ZYNQMP_RESET_ACPU3 22 #define ZYNQMP_RESET_ACPU2 23 #define ZYNQMP_RESET_ACPU1 24 #define ZYNQMP_RESET_ACPU0 25 #define ZYNQMP_RESET_DDR 26 #define ZYNQMP_RESET_APM_FPD 27 #define ZYNQMP_RESET_SOFT 28 #define ZYNQMP_RESET_GEM0 29 #define ZYNQMP_RESET_GEM1 30 #define ZYNQMP_RESET_GEM2 31 #define ZYNQMP_RESET_GEM3 32 #define ZYNQMP_RESET_QSPI 33 #define ZYNQMP_RESET_UART0 34 #define ZYNQMP_RESET_UART1 35 #define ZYNQMP_RESET_SPI0 36 #define ZYNQMP_RESET_SPI1 37 #define ZYNQMP_RESET_SDIO0 38 #define ZYNQMP_RESET_SDIO1 39 #define ZYNQMP_RESET_CAN0 40 #define ZYNQMP_RESET_CAN1 41 #define ZYNQMP_RESET_I2C0 42 #define ZYNQMP_RESET_I2C1 43 #define ZYNQMP_RESET_TTC0 44 #define ZYNQMP_RESET_TTC1 45 #define ZYNQMP_RESET_TTC2 46 #define ZYNQMP_RESET_TTC3 47 #define ZYNQMP_RESET_SWDT_CRL 48 #define ZYNQMP_RESET_NAND 49 #define ZYNQMP_RESET_ADMA 50 #define ZYNQMP_RESET_GPIO 51 #define ZYNQMP_RESET_IOU_CC 52 #define ZYNQMP_RESET_TIMESTAMP 53 #define ZYNQMP_RESET_RPU_R50 54 #define ZYNQMP_RESET_RPU_R51 55 #define ZYNQMP_RESET_RPU_AMBA 56 #define ZYNQMP_RESET_OCM 57 #define ZYNQMP_RESET_RPU_PGE 58 #define ZYNQMP_RESET_USB0_CORERESET 59 #define ZYNQMP_RESET_USB1_CORERESET 60 #define ZYNQMP_RESET_USB0_HIBERRESET 61 #define ZYNQMP_RESET_USB1_HIBERRESET 62 #define ZYNQMP_RESET_USB0_APB 63 #define ZYNQMP_RESET_USB1_APB 64 #define ZYNQMP_RESET_IPI 65 #define ZYNQMP_RESET_APM_LPD 66 #define ZYNQMP_RESET_RTC 67 #define ZYNQMP_RESET_SYSMON 68 #define ZYNQMP_RESET_AFI_FM6 69 #define ZYNQMP_RESET_LPD_SWDT 70 #define ZYNQMP_RESET_FPD 71 #define ZYNQMP_RESET_RPU_DBG1 72 #define ZYNQMP_RESET_RPU_DBG0 73 #define ZYNQMP_RESET_DBG_LPD 74 #define ZYNQMP_RESET_DBG_FPD 75 #define ZYNQMP_RESET_APLL 76 #define ZYNQMP_RESET_DPLL 77 #define ZYNQMP_RESET_VPLL 78 #define ZYNQMP_RESET_IOPLL 79 #define ZYNQMP_RESET_RPLL 80 #define ZYNQMP_RESET_GPO3_PL_0 81 #define ZYNQMP_RESET_GPO3_PL_1 82 #define ZYNQMP_RESET_GPO3_PL_2 83 #define ZYNQMP_RESET_GPO3_PL_3 84 #define ZYNQMP_RESET_GPO3_PL_4 85 #define ZYNQMP_RESET_GPO3_PL_5 86 #define ZYNQMP_RESET_GPO3_PL_6 87 #define ZYNQMP_RESET_GPO3_PL_7 88 #define ZYNQMP_RESET_GPO3_PL_8 89 #define ZYNQMP_RESET_GPO3_PL_9 90 #define ZYNQMP_RESET_GPO3_PL_10 91 #define ZYNQMP_RESET_GPO3_PL_11 92 #define ZYNQMP_RESET_GPO3_PL_12 93 #define ZYNQMP_RESET_GPO3_PL_13 94 #define ZYNQMP_RESET_GPO3_PL_14 95 #define ZYNQMP_RESET_GPO3_PL_15 96 #define ZYNQMP_RESET_GPO3_PL_16 97 #define ZYNQMP_RESET_GPO3_PL_17 98 #define ZYNQMP_RESET_GPO3_PL_18 99 #define ZYNQMP_RESET_GPO3_PL_19 100 #define ZYNQMP_RESET_GPO3_PL_20 101 #define ZYNQMP_RESET_GPO3_PL_21 102 #define ZYNQMP_RESET_GPO3_PL_22 103 #define ZYNQMP_RESET_GPO3_PL_23 104 #define ZYNQMP_RESET_GPO3_PL_24 105 #define ZYNQMP_RESET_GPO3_PL_25 106 #define ZYNQMP_RESET_GPO3_PL_26 107 #define ZYNQMP_RESET_GPO3_PL_27 108 #define ZYNQMP_RESET_GPO3_PL_28 109 #define ZYNQMP_RESET_GPO3_PL_29 110 #define ZYNQMP_RESET_GPO3_PL_30 111 #define ZYNQMP_RESET_GPO3_PL_31 112 #define ZYNQMP_RESET_RPU_LS 113 #define ZYNQMP_RESET_PS_ONLY 114 #define ZYNQMP_RESET_PL 115 #define ZYNQMP_RESET_PS_PL0 116 #define ZYNQMP_RESET_PS_PL1 117 #define ZYNQMP_RESET_PS_PL2 118 #define ZYNQMP_RESET_PS_PL3 119 #define ZYNQMP_RESET_MAX ZYNQMP_RESET_PS_PL3 struct zynqmp_reset_softc { device_t dev; device_t parent; }; static int zynqmp_reset_assert(device_t dev, intptr_t id, bool reset) { struct zynqmp_reset_softc *sc; int rv; if (id > ZYNQMP_RESET_MAX) return (EINVAL); sc = device_get_softc(dev); rv = ZYNQMP_FIRMWARE_RESET_ASSERT(sc->parent, id, reset); return (rv); } static int zynqmp_reset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct zynqmp_reset_softc *sc; int rv; if (id > ZYNQMP_RESET_MAX) return (EINVAL); sc = device_get_softc(dev); rv = ZYNQMP_FIRMWARE_RESET_GET_STATUS(sc->parent, id, reset); return (rv); } static int zynqmp_reset_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-reset")) return (ENXIO); device_set_desc(dev, "ZynqMP Reset Controller"); return (BUS_PROBE_DEFAULT); } static int zynqmp_reset_attach(device_t dev) { struct zynqmp_reset_softc *sc; sc = device_get_softc(dev); sc->dev = dev; sc->parent = device_get_parent(dev); /* register our self as a reset provider */ hwreset_register_ofw_provider(dev); return (0); } static device_method_t zynqmp_reset_methods[] = { /* device_if */ DEVMETHOD(device_probe, zynqmp_reset_probe), DEVMETHOD(device_attach, zynqmp_reset_attach), /* Reset interface */ DEVMETHOD(hwreset_assert, zynqmp_reset_assert), DEVMETHOD(hwreset_is_asserted, zynqmp_reset_is_asserted), DEVMETHOD_END }; static driver_t zynqmp_reset_driver = { "zynqmp_reset", zynqmp_reset_methods, sizeof(struct zynqmp_reset_softc), }; EARLY_DRIVER_MODULE(zynqmp_reset, simplebus, zynqmp_reset_driver, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LAST); diff --git a/sys/dev/dwc/dwc1000_core.c b/sys/dev/dwc/dwc1000_core.c index d25c31e66e28..ba895f991b50 100644 --- a/sys/dev/dwc/dwc1000_core.c +++ b/sys/dev/dwc/dwc1000_core.c @@ -1,447 +1,447 @@ /*- * Copyright (c) 2014 Ruslan Bukin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* * Ethernet media access controller (EMAC) * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) * * EMAC is an instance of the Synopsys DesignWare 3504-0 * Universal 10/100/1000 Ethernet MAC (DWC_gmac). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "if_dwc_if.h" struct dwc_hash_maddr_ctx { struct dwc_softc *sc; uint32_t hash[8]; }; #define STATS_HARVEST_INTERVAL 2 /* Pause time field in the transmitted control frame */ static int dwc_pause_time = 0xffff; TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); /* * MIIBUS functions */ int dwc1000_miibus_read_reg(device_t dev, int phy, int reg) { struct dwc_softc *sc; uint16_t mii; size_t cnt; int rv = 0; sc = device_get_softc(dev); mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) | GMII_ADDRESS_GB; /* Busy flag */ WRITE4(sc, GMII_ADDRESS, mii); for (cnt = 0; cnt < 1000; cnt++) { if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { rv = READ4(sc, GMII_DATA); break; } DELAY(10); } return rv; } int dwc1000_miibus_write_reg(device_t dev, int phy, int reg, int val) { struct dwc_softc *sc; uint16_t mii; size_t cnt; sc = device_get_softc(dev); mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) | GMII_ADDRESS_GB | GMII_ADDRESS_GW; WRITE4(sc, GMII_DATA, val); WRITE4(sc, GMII_ADDRESS, mii); for (cnt = 0; cnt < 1000; cnt++) { if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { break; } DELAY(10); } return (0); } void dwc1000_miibus_statchg(device_t dev) { struct dwc_softc *sc; struct mii_data *mii; uint32_t reg; /* * Called by the MII bus driver when the PHY establishes * link to set the MAC interface registers. */ sc = device_get_softc(dev); DWC_ASSERT_LOCKED(sc); mii = sc->mii_softc; if (mii->mii_media_status & IFM_ACTIVE) sc->link_is_up = true; else sc->link_is_up = false; reg = READ4(sc, MAC_CONFIGURATION); switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: case IFM_1000_SX: reg &= ~(CONF_FES | CONF_PS); break; case IFM_100_TX: reg |= (CONF_FES | CONF_PS); break; case IFM_10_T: reg &= ~(CONF_FES); reg |= (CONF_PS); break; case IFM_NONE: sc->link_is_up = false; return; default: sc->link_is_up = false; device_printf(dev, "Unsupported media %u\n", IFM_SUBTYPE(mii->mii_media_active)); return; } if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) reg |= (CONF_DM); else reg &= ~(CONF_DM); WRITE4(sc, MAC_CONFIGURATION, reg); reg = FLOW_CONTROL_UP; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) reg |= FLOW_CONTROL_TX; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) reg |= FLOW_CONTROL_RX; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT; WRITE4(sc, FLOW_CONTROL, reg); IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); } void dwc1000_core_setup(struct dwc_softc *sc) { uint32_t reg; DWC_ASSERT_LOCKED(sc); /* Enable core */ reg = READ4(sc, MAC_CONFIGURATION); reg |= (CONF_JD | CONF_ACS | CONF_BE); WRITE4(sc, MAC_CONFIGURATION, reg); } void dwc1000_enable_mac(struct dwc_softc *sc, bool enable) { uint32_t reg; DWC_ASSERT_LOCKED(sc); reg = READ4(sc, MAC_CONFIGURATION); if (enable) reg |= CONF_TE | CONF_RE; else reg &= ~(CONF_TE | CONF_RE); WRITE4(sc, MAC_CONFIGURATION, reg); } void dwc1000_enable_csum_offload(struct dwc_softc *sc) { uint32_t reg; DWC_ASSERT_LOCKED(sc); reg = READ4(sc, MAC_CONFIGURATION); if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0) reg |= CONF_IPC; else reg &= ~CONF_IPC; WRITE4(sc, MAC_CONFIGURATION, reg); } static const uint8_t nibbletab[] = { /* 0x0 0000 -> 0000 */ 0x0, /* 0x1 0001 -> 1000 */ 0x8, /* 0x2 0010 -> 0100 */ 0x4, /* 0x3 0011 -> 1100 */ 0xc, /* 0x4 0100 -> 0010 */ 0x2, /* 0x5 0101 -> 1010 */ 0xa, /* 0x6 0110 -> 0110 */ 0x6, /* 0x7 0111 -> 1110 */ 0xe, /* 0x8 1000 -> 0001 */ 0x1, /* 0x9 1001 -> 1001 */ 0x9, /* 0xa 1010 -> 0101 */ 0x5, /* 0xb 1011 -> 1101 */ 0xd, /* 0xc 1100 -> 0011 */ 0x3, /* 0xd 1101 -> 1011 */ 0xb, /* 0xe 1110 -> 0111 */ 0x7, /* 0xf 1111 -> 1111 */ 0xf, }; static uint8_t bitreverse(uint8_t x) { return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; } static u_int dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { struct dwc_hash_maddr_ctx *ctx = arg; uint32_t crc, hashbit, hashreg; uint8_t val; crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); /* Take lower 8 bits and reverse it */ val = bitreverse(~crc & 0xff); /* * TODO: There is probably a HW_FEATURES bit which isn't * related to the extended descriptors that describe this */ if (!ctx->sc->dma_ext_desc) val >>= 2; /* Only need lower 6 bits */ hashreg = (val >> 5); hashbit = (val & 31); ctx->hash[hashreg] |= (1 << hashbit); return (1); } void dwc1000_setup_rxfilter(struct dwc_softc *sc) { struct dwc_hash_maddr_ctx ctx; if_t ifp; uint8_t *eaddr; uint32_t ffval, hi, lo; int nhash, i; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; /* * TODO: There is probably a HW_FEATURES bit which isn't * related to the extended descriptors that describe this */ nhash = sc->dma_ext_desc == false ? 2 : 8; /* * Set the multicast (group) filter hash. */ if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) { ffval = (FRAME_FILTER_PM); for (i = 0; i < nhash; i++) ctx.hash[i] = ~0; } else { ffval = (FRAME_FILTER_HMC); for (i = 0; i < nhash; i++) ctx.hash[i] = 0; ctx.sc = sc; if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); } /* * Set the individual address filter hash. */ if ((if_getflags(ifp) & IFF_PROMISC) != 0) ffval |= (FRAME_FILTER_PR); /* * Set the primary address. */ eaddr = if_getlladdr(ifp); lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | (eaddr[3] << 24); hi = eaddr[4] | (eaddr[5] << 8); WRITE4(sc, MAC_ADDRESS_LOW(0), lo); WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); WRITE4(sc, MAC_FRAME_FILTER, ffval); if (!sc->dma_ext_desc) { WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); } else { for (i = 0; i < nhash; i++) WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); } } void dwc1000_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) { uint32_t hi, lo, rnd; /* * Try to recover a MAC address from the running hardware. If there's * something non-zero there, assume the bootloader did the right thing * and just use it. * * Otherwise, set the address to a convenient locally assigned address, * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally * assigned bit set, and the broadcast/multicast bit clear. */ lo = READ4(sc, MAC_ADDRESS_LOW(0)); hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; if ((lo != 0xffffffff) || (hi != 0xffff)) { hwaddr[0] = (lo >> 0) & 0xff; hwaddr[1] = (lo >> 8) & 0xff; hwaddr[2] = (lo >> 16) & 0xff; hwaddr[3] = (lo >> 24) & 0xff; hwaddr[4] = (hi >> 0) & 0xff; hwaddr[5] = (hi >> 8) & 0xff; } else { rnd = arc4random() & 0x00ffffff; hwaddr[0] = 'b'; hwaddr[1] = 's'; hwaddr[2] = 'd'; hwaddr[3] = rnd >> 16; hwaddr[4] = rnd >> 8; hwaddr[5] = rnd >> 0; } } /* * Stats */ static void dwc1000_clear_stats(struct dwc_softc *sc) { uint32_t reg; reg = READ4(sc, MMC_CONTROL); reg |= (MMC_CONTROL_CNTRST); WRITE4(sc, MMC_CONTROL, reg); } void dwc1000_harvest_stats(struct dwc_softc *sc) { if_t ifp; /* We don't need to harvest too often. */ if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) return; sc->stats_harvest_count = 0; ifp = sc->ifp; if_inc_counter(ifp, IFCOUNTER_IERRORS, READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + READ4(sc, RXLENGTHERROR)); if_inc_counter(ifp, IFCOUNTER_OERRORS, READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); dwc1000_clear_stats(sc); } void dwc1000_intr(struct dwc_softc *sc) { uint32_t reg; DWC_ASSERT_LOCKED(sc); reg = READ4(sc, INTERRUPT_STATUS); if (reg) READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); } void dwc1000_intr_disable(struct dwc_softc *sc) { WRITE4(sc, INTERRUPT_ENABLE, 0); } diff --git a/sys/dev/dwc/dwc1000_dma.c b/sys/dev/dwc/dwc1000_dma.c index e89ccee5b0ff..c510c252072c 100644 --- a/sys/dev/dwc/dwc1000_dma.c +++ b/sys/dev/dwc/dwc1000_dma.c @@ -1,889 +1,889 @@ /*- * Copyright (c) 2014 Ruslan Bukin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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 #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #define WATCHDOG_TIMEOUT_SECS 5 #define DMA_RESET_TIMEOUT 100 /* TX descriptors - TDESC0 is almost unified */ #define TDESC0_OWN (1U << 31) #define TDESC0_IHE (1U << 16) /* IP Header Error */ #define TDESC0_ES (1U << 15) /* Error Summary */ #define TDESC0_JT (1U << 14) /* Jabber Timeout */ #define TDESC0_FF (1U << 13) /* Frame Flushed */ #define TDESC0_PCE (1U << 12) /* Payload Checksum Error */ #define TDESC0_LOC (1U << 11) /* Loss of Carrier */ #define TDESC0_NC (1U << 10) /* No Carrier */ #define TDESC0_LC (1U << 9) /* Late Collision */ #define TDESC0_EC (1U << 8) /* Excessive Collision */ #define TDESC0_VF (1U << 7) /* VLAN Frame */ #define TDESC0_CC_MASK 0xf #define TDESC0_CC_SHIFT 3 /* Collision Count */ #define TDESC0_ED (1U << 2) /* Excessive Deferral */ #define TDESC0_UF (1U << 1) /* Underflow Error */ #define TDESC0_DB (1U << 0) /* Deferred Bit */ /* TX descriptors - TDESC0 extended format only */ #define ETDESC0_IC (1U << 30) /* Interrupt on Completion */ #define ETDESC0_LS (1U << 29) /* Last Segment */ #define ETDESC0_FS (1U << 28) /* First Segment */ #define ETDESC0_DC (1U << 27) /* Disable CRC */ #define ETDESC0_DP (1U << 26) /* Disable Padding */ #define ETDESC0_CIC_NONE (0U << 22) /* Checksum Insertion Control */ #define ETDESC0_CIC_HDR (1U << 22) #define ETDESC0_CIC_SEG (2U << 22) #define ETDESC0_CIC_FULL (3U << 22) #define ETDESC0_TER (1U << 21) /* Transmit End of Ring */ #define ETDESC0_TCH (1U << 20) /* Second Address Chained */ /* TX descriptors - TDESC1 normal format */ #define NTDESC1_IC (1U << 31) /* Interrupt on Completion */ #define NTDESC1_LS (1U << 30) /* Last Segment */ #define NTDESC1_FS (1U << 29) /* First Segment */ #define NTDESC1_CIC_NONE (0U << 27) /* Checksum Insertion Control */ #define NTDESC1_CIC_HDR (1U << 27) #define NTDESC1_CIC_SEG (2U << 27) #define NTDESC1_CIC_FULL (3U << 27) #define NTDESC1_DC (1U << 26) /* Disable CRC */ #define NTDESC1_TER (1U << 25) /* Transmit End of Ring */ #define NTDESC1_TCH (1U << 24) /* Second Address Chained */ /* TX descriptors - TDESC1 extended format */ #define ETDESC1_DP (1U << 23) /* Disable Padding */ #define ETDESC1_TBS2_MASK 0x7ff #define ETDESC1_TBS2_SHIFT 11 /* Receive Buffer 2 Size */ #define ETDESC1_TBS1_MASK 0x7ff #define ETDESC1_TBS1_SHIFT 0 /* Receive Buffer 1 Size */ /* RX descriptor - RDESC0 is unified */ #define RDESC0_OWN (1U << 31) #define RDESC0_AFM (1U << 30) /* Dest. Address Filter Fail */ #define RDESC0_FL_MASK 0x3fff #define RDESC0_FL_SHIFT 16 /* Frame Length */ #define RDESC0_ES (1U << 15) /* Error Summary */ #define RDESC0_DE (1U << 14) /* Descriptor Error */ #define RDESC0_SAF (1U << 13) /* Source Address Filter Fail */ #define RDESC0_LE (1U << 12) /* Length Error */ #define RDESC0_OE (1U << 11) /* Overflow Error */ #define RDESC0_VLAN (1U << 10) /* VLAN Tag */ #define RDESC0_FS (1U << 9) /* First Descriptor */ #define RDESC0_LS (1U << 8) /* Last Descriptor */ #define RDESC0_ICE (1U << 7) /* IPC Checksum Error */ #define RDESC0_LC (1U << 6) /* Late Collision */ #define RDESC0_FT (1U << 5) /* Frame Type */ #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ #define RDESC0_RE (1U << 3) /* Receive Error */ #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ #define RDESC0_CE (1U << 1) /* CRC Error */ #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ /* RX descriptors - RDESC1 normal format */ #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ #define NRDESC1_RBS2_MASK 0x7ff #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ #define NRDESC1_RBS1_MASK 0x7ff #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ /* RX descriptors - RDESC1 enhanced format */ #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ #define ERDESC1_RBS2_MASK 0x7ffff #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ #define ERDESC1_RBS1_MASK 0x7ffff #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ /* * The hardware imposes alignment restrictions on various objects involved in * DMA transfers. These values are expressed in bytes (not bits). */ #define DWC_DESC_RING_ALIGN 2048 static inline uint32_t next_txidx(struct dwc_softc *sc, uint32_t curidx) { return ((curidx + 1) % TX_DESC_COUNT); } static inline uint32_t next_rxidx(struct dwc_softc *sc, uint32_t curidx) { return ((curidx + 1) % RX_DESC_COUNT); } static void dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } inline static void txdesc_clear(struct dwc_softc *sc, int idx) { sc->tx_desccount--; sc->txdesc_ring[idx].addr1 = (uint32_t)(0); sc->txdesc_ring[idx].desc0 = 0; sc->txdesc_ring[idx].desc1 = 0; } inline static void txdesc_setup(struct dwc_softc *sc, int idx, bus_addr_t paddr, uint32_t len, uint32_t flags, bool first, bool last) { uint32_t desc0, desc1; if (!sc->dma_ext_desc) { desc0 = 0; desc1 = NTDESC1_TCH | len | flags; if (first) desc1 |= NTDESC1_FS; if (last) desc1 |= NTDESC1_LS | NTDESC1_IC; } else { desc0 = ETDESC0_TCH | flags; if (first) desc0 |= ETDESC0_FS; if (last) desc0 |= ETDESC0_LS | ETDESC0_IC; desc1 = len; } ++sc->tx_desccount; sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); sc->txdesc_ring[idx].desc0 = desc0; sc->txdesc_ring[idx].desc1 = desc1; wmb(); sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; wmb(); } inline static uint32_t rxdesc_setup(struct dwc_softc *sc, int idx, bus_addr_t paddr) { uint32_t nidx; sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; nidx = next_rxidx(sc, idx); sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + (nidx * sizeof(struct dwc_hwdesc)); if (!sc->dma_ext_desc) sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | MIN(MCLBYTES, NRDESC1_RBS1_MASK); else sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | MIN(MCLBYTES, ERDESC1_RBS1_MASK); wmb(); sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; wmb(); return (nidx); } int dma1000_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) { struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; int error, nsegs; struct mbuf * m; uint32_t flags = 0; int i; int last; error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, *mp, segs, &nsegs, 0); if (error == EFBIG) { /* * The map may be partially mapped from the first call. * Make sure to reset it. */ bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) return (ENOMEM); *mp = m; error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, *mp, segs, &nsegs, 0); } if (error != 0) return (ENOMEM); if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); return (ENOMEM); } m = *mp; if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { if (!sc->dma_ext_desc) flags = NTDESC1_CIC_FULL; else flags = ETDESC0_CIC_FULL; } else { if (!sc->dma_ext_desc) flags = NTDESC1_CIC_HDR; else flags = ETDESC0_CIC_HDR; } } bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, BUS_DMASYNC_PREWRITE); sc->txbuf_map[idx].mbuf = m; for (i = 0; i < nsegs; i++) { txdesc_setup(sc, sc->tx_desc_head, segs[i].ds_addr, segs[i].ds_len, (i == 0) ? flags : 0, /* only first desc needs flags */ (i == 0), (i == nsegs - 1)); last = sc->tx_desc_head; sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); } sc->txbuf_map[idx].last_desc_idx = last; return (0); } static int dma1000_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) { struct bus_dma_segment seg; int error, nsegs; m_adj(m, ETHER_ALIGN); error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, m, &seg, &nsegs, 0); if (error != 0) return (error); KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, BUS_DMASYNC_PREREAD); sc->rxbuf_map[idx].mbuf = m; rxdesc_setup(sc, idx, seg.ds_addr); return (0); } static struct mbuf * dwc_alloc_mbufcl(struct dwc_softc *sc) { struct mbuf *m; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m != NULL) m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; return (m); } static struct mbuf * dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, struct dwc_bufmap *map) { if_t ifp; struct mbuf *m, *m0; int len; uint32_t rdesc0; m = map->mbuf; ifp = sc->ifp; rdesc0 = desc ->desc0; if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != (RDESC0_FS | RDESC0_LS)) { /* * Something very wrong happens. The whole packet should be * recevied in one descriptr. Report problem. */ device_printf(sc->dev, "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", __func__, rdesc0); return (NULL); } len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; if (len < 64) { /* * Lenght is invalid, recycle old mbuf * Probably impossible case */ return (NULL); } /* Allocate new buffer */ m0 = dwc_alloc_mbufcl(sc); if (m0 == NULL) { /* no new mbuf available, recycle old */ if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); return (NULL); } /* Do dmasync for newly received packet */ bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_tag, map->map); /* Received packet is valid, process it */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = len; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && (rdesc0 & RDESC0_FT) != 0) { m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; if ((rdesc0 & RDESC0_ICE) == 0) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if ((rdesc0 & RDESC0_PCE) == 0) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } /* Remove trailing FCS */ m_adj(m, -ETHER_CRC_LEN); DWC_UNLOCK(sc); if_input(ifp, m); DWC_LOCK(sc); return (m0); } void dma1000_txfinish_locked(struct dwc_softc *sc) { struct dwc_bufmap *bmap; struct dwc_hwdesc *desc; if_t ifp; int idx, last_idx; bool map_finished; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; /* check if all descriptors of the map are done */ while (sc->tx_map_tail != sc->tx_map_head) { map_finished = true; bmap = &sc->txbuf_map[sc->tx_map_tail]; idx = sc->tx_desc_tail; last_idx = next_txidx(sc, bmap->last_desc_idx); while (idx != last_idx) { desc = &sc->txdesc_ring[idx]; if ((desc->desc0 & TDESC0_OWN) != 0) { map_finished = false; break; } idx = next_txidx(sc, idx); } if (!map_finished) break; bus_dmamap_sync(sc->txbuf_tag, bmap->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->txbuf_tag, bmap->map); m_freem(bmap->mbuf); bmap->mbuf = NULL; sc->tx_mapcount--; while (sc->tx_desc_tail != last_idx) { txdesc_clear(sc, sc->tx_desc_tail); sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); } sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } /* If there are no buffers outstanding, muzzle the watchdog. */ if (sc->tx_desc_tail == sc->tx_desc_head) { sc->tx_watchdog_count = 0; } } void dma1000_txstart(struct dwc_softc *sc) { int enqueued; struct mbuf *m; enqueued = 0; for (;;) { if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0); break; } if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0); break; } m = if_dequeue(sc->ifp); if (m == NULL) break; if (dma1000_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { if_sendq_prepend(sc->ifp, m); if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0); break; } bpf_mtap_if(sc->ifp, m); sc->tx_map_head = next_txidx(sc, sc->tx_map_head); sc->tx_mapcount++; ++enqueued; } if (enqueued != 0) { WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; } } void dma1000_rxfinish_locked(struct dwc_softc *sc) { struct mbuf *m; int error, idx; struct dwc_hwdesc *desc; DWC_ASSERT_LOCKED(sc); for (;;) { idx = sc->rx_idx; desc = sc->rxdesc_ring + idx; if ((desc->desc0 & RDESC0_OWN) != 0) break; m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); if (m == NULL) { wmb(); desc->desc0 = RDESC0_OWN; wmb(); } else { /* We cannot create hole in RX ring */ error = dma1000_setup_rxbuf(sc, idx, m); if (error != 0) panic("dma1000_setup_rxbuf failed: error %d\n", error); } sc->rx_idx = next_rxidx(sc, sc->rx_idx); } } /* * Start the DMA controller */ void dma1000_start(struct dwc_softc *sc) { uint32_t reg; DWC_ASSERT_LOCKED(sc); /* Initializa DMA and enable transmitters */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_TSF | MODE_OSF | MODE_FUF); reg &= ~(MODE_RSF); reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); WRITE4(sc, OPERATION_MODE, reg); WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); /* Start DMA */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_ST | MODE_SR); WRITE4(sc, OPERATION_MODE, reg); } /* * Stop the DMA controller */ void dma1000_stop(struct dwc_softc *sc) { uint32_t reg; DWC_ASSERT_LOCKED(sc); /* Stop DMA TX */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_ST); WRITE4(sc, OPERATION_MODE, reg); /* Flush TX */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_FTF); WRITE4(sc, OPERATION_MODE, reg); /* Stop DMA RX */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_SR); WRITE4(sc, OPERATION_MODE, reg); } int dma1000_reset(struct dwc_softc *sc) { uint32_t reg; int i; reg = READ4(sc, BUS_MODE); reg |= (BUS_MODE_SWR); WRITE4(sc, BUS_MODE, reg); for (i = 0; i < DMA_RESET_TIMEOUT; i++) { if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) break; DELAY(10); } if (i >= DMA_RESET_TIMEOUT) { return (ENXIO); } return (0); } /* * Create the bus_dma resources */ int dma1000_init(struct dwc_softc *sc) { struct mbuf *m; uint32_t reg; int error; int nidx; int idx; reg = BUS_MODE_USP; if (!sc->nopblx8) reg |= BUS_MODE_EIGHTXPBL; reg |= (sc->txpbl << BUS_MODE_PBL_SHIFT); reg |= (sc->rxpbl << BUS_MODE_RPBL_SHIFT); if (sc->fixed_burst) reg |= BUS_MODE_FIXEDBURST; if (sc->mixed_burst) reg |= BUS_MODE_MIXEDBURST; if (sc->aal) reg |= BUS_MODE_AAL; WRITE4(sc, BUS_MODE, reg); reg = READ4(sc, HW_FEATURE); if (reg & HW_FEATURE_EXT_DESCRIPTOR) sc->dma_ext_desc = true; /* * DMA must be stop while changing descriptor list addresses. */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_ST | MODE_SR); WRITE4(sc, OPERATION_MODE, reg); /* * Set up TX descriptor ring, descriptors, and dma maps. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TX_DESC_SIZE, 1, /* maxsize, nsegments */ TX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->txdesc_tag); if (error != 0) { device_printf(sc->dev, "could not create TX ring DMA tag.\n"); goto out; } error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->txdesc_map); if (error != 0) { device_printf(sc->dev, "could not allocate TX descriptor ring.\n"); goto out; } error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, &sc->txdesc_ring_paddr, 0); if (error != 0) { device_printf(sc->dev, "could not load TX descriptor ring map.\n"); goto out; } for (idx = 0; idx < TX_DESC_COUNT; idx++) { nidx = next_txidx(sc, idx); sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + (nidx * sizeof(struct dwc_hwdesc)); } error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ TX_MAP_MAX_SEGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->txbuf_tag); if (error != 0) { device_printf(sc->dev, "could not create TX ring DMA tag.\n"); goto out; } for (idx = 0; idx < TX_MAP_COUNT; idx++) { error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, &sc->txbuf_map[idx].map); if (error != 0) { device_printf(sc->dev, "could not create TX buffer DMA map.\n"); goto out; } } for (idx = 0; idx < TX_DESC_COUNT; idx++) txdesc_clear(sc, idx); WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); /* * Set up RX descriptor ring, descriptors, dma maps, and mbufs. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ RX_DESC_SIZE, 1, /* maxsize, nsegments */ RX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rxdesc_tag); if (error != 0) { device_printf(sc->dev, "could not create RX ring DMA tag.\n"); goto out; } error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rxdesc_map); if (error != 0) { device_printf(sc->dev, "could not allocate RX descriptor ring.\n"); goto out; } error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, &sc->rxdesc_ring_paddr, 0); if (error != 0) { device_printf(sc->dev, "could not load RX descriptor ring map.\n"); goto out; } error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize, nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rxbuf_tag); if (error != 0) { device_printf(sc->dev, "could not create RX buf DMA tag.\n"); goto out; } for (idx = 0; idx < RX_DESC_COUNT; idx++) { error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, &sc->rxbuf_map[idx].map); if (error != 0) { device_printf(sc->dev, "could not create RX buffer DMA map.\n"); goto out; } if ((m = dwc_alloc_mbufcl(sc)) == NULL) { device_printf(sc->dev, "Could not alloc mbuf\n"); error = ENOMEM; goto out; } if ((error = dma1000_setup_rxbuf(sc, idx, m)) != 0) { device_printf(sc->dev, "could not create new RX buffer.\n"); goto out; } } WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); out: if (error != 0) return (ENXIO); return (0); } /* * Free the bus_dma resources */ void dma1000_free(struct dwc_softc *sc) { bus_dmamap_t map; int idx; /* Clean up RX DMA resources and free mbufs. */ for (idx = 0; idx < RX_DESC_COUNT; ++idx) { if ((map = sc->rxbuf_map[idx].map) != NULL) { bus_dmamap_unload(sc->rxbuf_tag, map); bus_dmamap_destroy(sc->rxbuf_tag, map); m_freem(sc->rxbuf_map[idx].mbuf); } } if (sc->rxbuf_tag != NULL) bus_dma_tag_destroy(sc->rxbuf_tag); if (sc->rxdesc_map != NULL) { bus_dmamap_unload(sc->rxdesc_tag, sc->rxdesc_map); bus_dmamem_free(sc->rxdesc_tag, sc->rxdesc_ring, sc->rxdesc_map); } if (sc->rxdesc_tag != NULL) bus_dma_tag_destroy(sc->rxdesc_tag); /* Clean up TX DMA resources. */ for (idx = 0; idx < TX_DESC_COUNT; ++idx) { if ((map = sc->txbuf_map[idx].map) != NULL) { /* TX maps are already unloaded. */ bus_dmamap_destroy(sc->txbuf_tag, map); } } if (sc->txbuf_tag != NULL) bus_dma_tag_destroy(sc->txbuf_tag); if (sc->txdesc_map != NULL) { bus_dmamap_unload(sc->txdesc_tag, sc->txdesc_map); bus_dmamem_free(sc->txdesc_tag, sc->txdesc_ring, sc->txdesc_map); } if (sc->txdesc_tag != NULL) bus_dma_tag_destroy(sc->txdesc_tag); } /* * Interrupt function */ int dma1000_intr(struct dwc_softc *sc) { uint32_t reg; int rv; DWC_ASSERT_LOCKED(sc); rv = 0; reg = READ4(sc, DMA_STATUS); if (reg & DMA_STATUS_NIS) { if (reg & DMA_STATUS_RI) dma1000_rxfinish_locked(sc); if (reg & DMA_STATUS_TI) { dma1000_txfinish_locked(sc); dma1000_txstart(sc); } } if (reg & DMA_STATUS_AIS) { if (reg & DMA_STATUS_FBI) { /* Fatal bus error */ rv = EIO; } } WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); return (rv); } diff --git a/sys/dev/dwc/if_dwc.c b/sys/dev/dwc/if_dwc.c index be44a6be193b..17657045b73c 100644 --- a/sys/dev/dwc/if_dwc.c +++ b/sys/dev/dwc/if_dwc.c @@ -1,702 +1,702 @@ /*- * Copyright (c) 2014 Ruslan Bukin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* * Ethernet media access controller (EMAC) * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) * * EMAC is an instance of the Synopsys DesignWare 3504-0 * Universal 10/100/1000 Ethernet MAC (DWC_gmac). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "if_dwc_if.h" #include "gpio_if.h" #include "miibus_if.h" static struct resource_spec dwc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static void dwc_stop_locked(struct dwc_softc *sc); static void dwc_tick(void *arg); /* * Media functions */ static void dwc_media_status(if_t ifp, struct ifmediareq *ifmr) { struct dwc_softc *sc; struct mii_data *mii; sc = if_getsoftc(ifp); mii = sc->mii_softc; DWC_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; DWC_UNLOCK(sc); } static int dwc_media_change_locked(struct dwc_softc *sc) { return (mii_mediachg(sc->mii_softc)); } static int dwc_media_change(if_t ifp) { struct dwc_softc *sc; int error; sc = if_getsoftc(ifp); DWC_LOCK(sc); error = dwc_media_change_locked(sc); DWC_UNLOCK(sc); return (error); } /* * if_ functions */ static void dwc_txstart_locked(struct dwc_softc *sc) { if_t ifp; DWC_ASSERT_LOCKED(sc); if (!sc->link_is_up) return; ifp = sc->ifp; if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; dma1000_txstart(sc); } static void dwc_txstart(if_t ifp) { struct dwc_softc *sc = if_getsoftc(ifp); DWC_LOCK(sc); dwc_txstart_locked(sc); DWC_UNLOCK(sc); } static void dwc_init_locked(struct dwc_softc *sc) { if_t ifp = sc->ifp; DWC_ASSERT_LOCKED(sc); if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) return; /* * Call mii_mediachg() which will call back into dwc1000_miibus_statchg() * to set up the remaining config registers based on current media. */ mii_mediachg(sc->mii_softc); dwc1000_setup_rxfilter(sc); dwc1000_core_setup(sc); dwc1000_enable_mac(sc, true); dwc1000_enable_csum_offload(sc); dma1000_start(sc); if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); } static void dwc_init(void *if_softc) { struct dwc_softc *sc = if_softc; DWC_LOCK(sc); dwc_init_locked(sc); DWC_UNLOCK(sc); } static void dwc_stop_locked(struct dwc_softc *sc) { if_t ifp; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->tx_watchdog_count = 0; sc->stats_harvest_count = 0; callout_stop(&sc->dwc_callout); dma1000_stop(sc); dwc1000_enable_mac(sc, false); } static int dwc_ioctl(if_t ifp, u_long cmd, caddr_t data) { struct dwc_softc *sc; struct mii_data *mii; struct ifreq *ifr; int flags, mask, error; sc = if_getsoftc(ifp); ifr = (struct ifreq *)data; error = 0; switch (cmd) { case SIOCSIFFLAGS: DWC_LOCK(sc); if (if_getflags(ifp) & IFF_UP) { if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { flags = if_getflags(ifp) ^ sc->if_flags; if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) dwc1000_setup_rxfilter(sc); } else { if (!sc->is_detaching) dwc_init_locked(sc); } } else { if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) dwc_stop_locked(sc); } sc->if_flags = if_getflags(ifp); DWC_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { DWC_LOCK(sc); dwc1000_setup_rxfilter(sc); DWC_UNLOCK(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = sc->mii_softc; error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); if (mask & IFCAP_VLAN_MTU) { /* No work to do except acknowledge the change took */ if_togglecapenable(ifp, IFCAP_VLAN_MTU); } if (mask & IFCAP_RXCSUM) if_togglecapenable(ifp, IFCAP_RXCSUM); if (mask & IFCAP_TXCSUM) if_togglecapenable(ifp, IFCAP_TXCSUM); if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); else if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { DWC_LOCK(sc); dwc1000_enable_csum_offload(sc); DWC_UNLOCK(sc); } break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } /* * Interrupts functions */ static void dwc_intr(void *arg) { struct dwc_softc *sc; int rv; sc = arg; DWC_LOCK(sc); dwc1000_intr(sc); rv = dma1000_intr(sc); if (rv == EIO) { device_printf(sc->dev, "Ethernet DMA error, restarting controller.\n"); dwc_stop_locked(sc); dwc_init_locked(sc); } DWC_UNLOCK(sc); } static void dwc_tick(void *arg) { struct dwc_softc *sc; if_t ifp; int link_was_up; sc = arg; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) return; /* * Typical tx watchdog. If this fires it indicates that we enqueued * packets for output and never got a txdone interrupt for them. Maybe * it's a missed interrupt somehow, just pretend we got one. */ if (sc->tx_watchdog_count > 0) { if (--sc->tx_watchdog_count == 0) { dma1000_txfinish_locked(sc); } } /* Gather stats from hardware counters. */ dwc1000_harvest_stats(sc); /* Check the media status. */ link_was_up = sc->link_is_up; mii_tick(sc->mii_softc); if (sc->link_is_up && !link_was_up) dwc_txstart_locked(sc); /* Schedule another check one second from now. */ callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); } static int dwc_reset_phy(struct dwc_softc *sc) { pcell_t gpio_prop[4]; pcell_t delay_prop[3]; phandle_t gpio_node; device_t gpio; uint32_t pin, flags; uint32_t pin_value; /* * All those properties are deprecated but still used in some DTS. * The new way to deal with this is to use the generic bindings * present in the ethernet-phy node. */ if (OF_getencprop(sc->node, "snps,reset-gpio", gpio_prop, sizeof(gpio_prop)) <= 0) return (0); if (OF_getencprop(sc->node, "snps,reset-delays-us", delay_prop, sizeof(delay_prop)) <= 0) { device_printf(sc->dev, "Wrong property for snps,reset-delays-us"); return (ENXIO); } gpio_node = OF_node_from_xref(gpio_prop[0]); if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { device_printf(sc->dev, "Can't find gpio controller for phy reset\n"); return (ENXIO); } if (GPIO_MAP_GPIOS(gpio, sc->node, gpio_node, nitems(gpio_prop) - 1, gpio_prop + 1, &pin, &flags) != 0) { device_printf(sc->dev, "Can't map gpio for phy reset\n"); return (ENXIO); } pin_value = GPIO_PIN_LOW; if (OF_hasprop(sc->node, "snps,reset-active-low")) pin_value = GPIO_PIN_HIGH; GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[0] * 5); GPIO_PIN_SET(gpio, pin, !pin_value); DELAY(delay_prop[1] * 5); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[2] * 5); return (0); } static int dwc_clock_init(struct dwc_softc *sc) { int rv; int64_t freq; /* Required clock */ rv = clk_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->clk_stmmaceth); if (rv != 0) { device_printf(sc->dev, "Cannot get GMAC main clock\n"); return (ENXIO); } if ((rv = clk_enable(sc->clk_stmmaceth)) != 0) { device_printf(sc->dev, "could not enable main clock\n"); return (rv); } /* Optional clock */ rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk_pclk); if (rv != 0) return (0); if ((rv = clk_enable(sc->clk_pclk)) != 0) { device_printf(sc->dev, "could not enable peripheral clock\n"); return (rv); } if (bootverbose) { clk_get_freq(sc->clk_stmmaceth, &freq); device_printf(sc->dev, "MAC clock(%s) freq: %jd\n", clk_get_name(sc->clk_stmmaceth), (intmax_t)freq); } return (0); } static int dwc_reset_deassert(struct dwc_softc *sc) { int rv; /* Required reset */ rv = hwreset_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->rst_stmmaceth); if (rv != 0) { device_printf(sc->dev, "Cannot get GMAC reset\n"); return (ENXIO); } rv = hwreset_deassert(sc->rst_stmmaceth); if (rv != 0) { device_printf(sc->dev, "could not de-assert GMAC reset\n"); return (rv); } /* Optional reset */ rv = hwreset_get_by_ofw_name(sc->dev, 0, "ahb", &sc->rst_ahb); if (rv != 0) return (0); rv = hwreset_deassert(sc->rst_ahb); if (rv != 0) { device_printf(sc->dev, "could not de-assert AHB reset\n"); return (rv); } return (0); } /* * Probe/Attach functions */ static int dwc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "snps,dwmac")) return (ENXIO); device_set_desc(dev, "Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int dwc_attach(device_t dev) { uint8_t macaddr[ETHER_ADDR_LEN]; struct dwc_softc *sc; if_t ifp; int error; uint32_t pbl; sc = device_get_softc(dev); sc->dev = dev; sc->rx_idx = 0; sc->tx_desccount = TX_DESC_COUNT; sc->tx_mapcount = 0; sc->node = ofw_bus_get_node(dev); sc->phy_mode = mii_fdt_get_contype(sc->node); switch (sc->phy_mode) { case MII_CONTYPE_RGMII: case MII_CONTYPE_RGMII_ID: case MII_CONTYPE_RGMII_RXID: case MII_CONTYPE_RGMII_TXID: case MII_CONTYPE_RMII: case MII_CONTYPE_MII: break; default: device_printf(dev, "Unsupported MII type\n"); return (ENXIO); } if (OF_getencprop(sc->node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0) pbl = DMA_DEFAULT_PBL; if (OF_getencprop(sc->node, "snps,txpbl", &sc->txpbl, sizeof(uint32_t)) <= 0) sc->txpbl = pbl; if (OF_getencprop(sc->node, "snps,rxpbl", &sc->rxpbl, sizeof(uint32_t)) <= 0) sc->rxpbl = pbl; if (OF_hasprop(sc->node, "snps,no-pbl-x8") == 1) sc->nopblx8 = true; if (OF_hasprop(sc->node, "snps,fixed-burst") == 1) sc->fixed_burst = true; if (OF_hasprop(sc->node, "snps,mixed-burst") == 1) sc->mixed_burst = true; if (OF_hasprop(sc->node, "snps,aal") == 1) sc->aal = true; error = clk_set_assigned(dev, ofw_bus_get_node(dev)); if (error != 0) { device_printf(dev, "clk_set_assigned failed\n"); return (error); } /* Enable main clock */ if ((error = dwc_clock_init(sc)) != 0) return (error); /* De-assert main reset */ if ((error = dwc_reset_deassert(sc)) != 0) return (error); if (IF_DWC_INIT(dev) != 0) return (ENXIO); if ((sc->mii_clk = IF_DWC_MII_CLK(dev)) < 0) { device_printf(dev, "Cannot get mii clock value %d\n", -sc->mii_clk); return (ENXIO); } if (bus_alloc_resources(dev, dwc_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Read MAC before reset */ dwc1000_get_hwaddr(sc, macaddr); /* Reset the PHY if needed */ if (dwc_reset_phy(sc) != 0) { device_printf(dev, "Can't reset the PHY\n"); bus_release_resources(dev, dwc_spec, sc->res); return (ENXIO); } /* Reset */ if ((error = dma1000_reset(sc)) != 0) { device_printf(sc->dev, "Can't reset DMA controller.\n"); bus_release_resources(sc->dev, dwc_spec, sc->res); return (error); } if (dma1000_init(sc)) { bus_release_resources(dev, dwc_spec, sc->res); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(sc->dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); /* Setup interrupt handler. */ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, NULL, dwc_intr, sc, &sc->intr_cookie); if (error != 0) { device_printf(dev, "could not setup interrupt handler.\n"); bus_release_resources(dev, dwc_spec, sc->res); return (ENXIO); } /* Set up the ethernet interface. */ sc->ifp = ifp = if_alloc(IFT_ETHER); if_setsoftc(ifp, sc); if_initname(ifp, device_get_name(dev), device_get_unit(dev)); if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); if_setstartfn(ifp, dwc_txstart); if_setioctlfn(ifp, dwc_ioctl); if_setinitfn(ifp, dwc_init); if_setsendqlen(ifp, TX_MAP_COUNT - 1); if_setsendqready(sc->ifp); if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); /* Attach the mii driver. */ error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (error != 0) { device_printf(dev, "PHY attach failed\n"); bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); bus_release_resources(dev, dwc_spec, sc->res); return (ENXIO); } sc->mii_softc = device_get_softc(sc->miibus); /* All ready to run, attach the ethernet interface. */ ether_ifattach(ifp, macaddr); sc->is_attached = true; return (0); } static int dwc_detach(device_t dev) { struct dwc_softc *sc; sc = device_get_softc(dev); /* * Disable and tear down interrupts before anything else, so we don't * race with the handler. */ dwc1000_intr_disable(sc); if (sc->intr_cookie != NULL) { bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); } if (sc->is_attached) { DWC_LOCK(sc); sc->is_detaching = true; dwc_stop_locked(sc); DWC_UNLOCK(sc); callout_drain(&sc->dwc_callout); ether_ifdetach(sc->ifp); } if (sc->miibus != NULL) { device_delete_child(dev, sc->miibus); sc->miibus = NULL; } bus_generic_detach(dev); /* Free DMA descriptors */ dma1000_free(sc); if (sc->ifp != NULL) { if_free(sc->ifp); sc->ifp = NULL; } bus_release_resources(dev, dwc_spec, sc->res); mtx_destroy(&sc->mtx); return (0); } static device_method_t dwc_methods[] = { DEVMETHOD(device_probe, dwc_probe), DEVMETHOD(device_attach, dwc_attach), DEVMETHOD(device_detach, dwc_detach), /* MII Interface */ DEVMETHOD(miibus_readreg, dwc1000_miibus_read_reg), DEVMETHOD(miibus_writereg, dwc1000_miibus_write_reg), DEVMETHOD(miibus_statchg, dwc1000_miibus_statchg), { 0, 0 } }; driver_t dwc_driver = { "dwc", dwc_methods, sizeof(struct dwc_softc), }; DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0); DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0); MODULE_DEPEND(dwc, ether, 1, 1, 1); MODULE_DEPEND(dwc, miibus, 1, 1, 1); diff --git a/sys/dev/dwc/if_dwc_aw.c b/sys/dev/dwc/if_dwc_aw.c index 981f621e3f27..2a0dfaa33715 100644 --- a/sys/dev/dwc/if_dwc_aw.c +++ b/sys/dev/dwc/if_dwc_aw.c @@ -1,147 +1,147 @@ /*- * Copyright (c) 2015 Luiz Otavio O Souza * 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 #include #include -#include +#include #include #include #include #include #include "if_dwc_if.h" static int a20_if_dwc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-gmac")) return (ENXIO); device_set_desc(dev, "A20 Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int a20_if_dwc_init(device_t dev) { struct dwc_softc *sc; const char *tx_parent_name; clk_t clk_tx, clk_tx_parent; regulator_t reg; int error; sc = device_get_softc(dev); /* Configure PHY for MII or RGMII mode */ switch(sc->phy_mode) { case MII_CONTYPE_RGMII: case MII_CONTYPE_RGMII_ID: case MII_CONTYPE_RGMII_RXID: case MII_CONTYPE_RGMII_TXID: tx_parent_name = "gmac_int_tx"; break; case MII_CONTYPE_MII: tx_parent_name = "mii_phy_tx"; break; default: device_printf(dev, "unsupported PHY connection type: %d", sc->phy_mode); return (ENXIO); } error = clk_get_by_ofw_name(dev, 0, "allwinner_gmac_tx", &clk_tx); if (error != 0) { device_printf(dev, "could not get tx clk\n"); return (error); } error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); if (error != 0) { device_printf(dev, "could not get clock '%s'\n", tx_parent_name); return (error); } error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); if (error != 0) { device_printf(dev, "could not set tx clk parent\n"); return (error); } /* Enable PHY regulator if applicable */ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { error = regulator_enable(reg); if (error != 0) { device_printf(dev, "could not enable PHY regulator\n"); return (error); } } return (0); } static int a20_if_dwc_mii_clk(device_t dev) { return (GMAC_MII_CLK_150_250M_DIV102); } static device_method_t a20_dwc_methods[] = { DEVMETHOD(device_probe, a20_if_dwc_probe), DEVMETHOD(if_dwc_init, a20_if_dwc_init), DEVMETHOD(if_dwc_mii_clk, a20_if_dwc_mii_clk), DEVMETHOD_END }; extern driver_t dwc_driver; DEFINE_CLASS_1(dwc, a20_dwc_driver, a20_dwc_methods, sizeof(struct dwc_softc), dwc_driver); DRIVER_MODULE(a20_dwc, simplebus, a20_dwc_driver, 0, 0); MODULE_DEPEND(a20_dwc, dwc, 1, 1, 1); diff --git a/sys/dev/dwc/if_dwc_rk.c b/sys/dev/dwc/if_dwc_rk.c index 76fd11dfd109..c78fb447d6d3 100644 --- a/sys/dev/dwc/if_dwc_rk.c +++ b/sys/dev/dwc/if_dwc_rk.c @@ -1,636 +1,636 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 Emmanuel Vadot * * 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 #include #include -#include +#include #include #include #include #include #include "if_dwc_if.h" #include "syscon_if.h" #define RK3328_GRF_MAC_CON0 0x0900 #define MAC_CON0_GMAC2IO_TX_DL_CFG_MASK 0x7F #define MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT 0 #define MAC_CON0_GMAC2IO_RX_DL_CFG_MASK 0x7F #define MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT 7 #define RK3328_GRF_MAC_CON1 0x0904 #define MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA (1 << 0) #define MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA (1 << 1) #define MAC_CON1_GMAC2IO_GMII_CLK_SEL_MASK (3 << 11) #define MAC_CON1_GMAC2IO_GMII_CLK_SEL_125 (0 << 11) #define MAC_CON1_GMAC2IO_GMII_CLK_SEL_25 (3 << 11) #define MAC_CON1_GMAC2IO_GMII_CLK_SEL_2_5 (2 << 11) #define MAC_CON1_GMAC2IO_RMII_MODE_MASK (1 << 9) #define MAC_CON1_GMAC2IO_RMII_MODE (1 << 9) #define MAC_CON1_GMAC2IO_INTF_SEL_MASK (7 << 4) #define MAC_CON1_GMAC2IO_INTF_RMII (4 << 4) #define MAC_CON1_GMAC2IO_INTF_RGMII (1 << 4) #define MAC_CON1_GMAC2IO_RMII_CLK_SEL_MASK (1 << 7) #define MAC_CON1_GMAC2IO_RMII_CLK_SEL_25 (1 << 7) #define MAC_CON1_GMAC2IO_RMII_CLK_SEL_2_5 (0 << 7) #define MAC_CON1_GMAC2IO_MAC_SPEED_MASK (1 << 2) #define MAC_CON1_GMAC2IO_MAC_SPEED_100 (1 << 2) #define MAC_CON1_GMAC2IO_MAC_SPEED_10 (0 << 2) #define RK3328_GRF_MAC_CON2 0x0908 #define RK3328_GRF_MACPHY_CON0 0x0B00 #define MACPHY_CON0_CLK_50M_MASK (1 << 14) #define MACPHY_CON0_CLK_50M (1 << 14) #define MACPHY_CON0_RMII_MODE_MASK (3 << 6) #define MACPHY_CON0_RMII_MODE (1 << 6) #define RK3328_GRF_MACPHY_CON1 0x0B04 #define MACPHY_CON1_RMII_MODE_MASK (1 << 9) #define MACPHY_CON1_RMII_MODE (1 << 9) #define RK3328_GRF_MACPHY_CON2 0x0B08 #define RK3328_GRF_MACPHY_CON3 0x0B0C #define RK3328_GRF_MACPHY_STATUS 0x0B10 #define RK3399_GRF_SOC_CON5 0xc214 #define SOC_CON5_GMAC_CLK_SEL_MASK (3 << 4) #define SOC_CON5_GMAC_CLK_SEL_125 (0 << 4) #define SOC_CON5_GMAC_CLK_SEL_25 (3 << 4) #define SOC_CON5_GMAC_CLK_SEL_2_5 (2 << 4) #define RK3399_GRF_SOC_CON6 0xc218 #define SOC_CON6_GMAC_TXCLK_DLY_ENA (1 << 7) #define SOC_CON6_TX_DL_CFG_MASK 0x7F #define SOC_CON6_TX_DL_CFG_SHIFT 0 #define SOC_CON6_RX_DL_CFG_MASK 0x7F #define SOC_CON6_GMAC_RXCLK_DLY_ENA (1 << 15) #define SOC_CON6_RX_DL_CFG_SHIFT 8 struct if_dwc_rk_softc; typedef void (*if_dwc_rk_set_delaysfn_t)(struct if_dwc_rk_softc *); typedef int (*if_dwc_rk_set_speedfn_t)(struct if_dwc_rk_softc *, int); typedef void (*if_dwc_rk_set_phy_modefn_t)(struct if_dwc_rk_softc *); typedef void (*if_dwc_rk_phy_powerupfn_t)(struct if_dwc_rk_softc *); struct if_dwc_rk_ops { if_dwc_rk_set_delaysfn_t set_delays; if_dwc_rk_set_speedfn_t set_speed; if_dwc_rk_set_phy_modefn_t set_phy_mode; if_dwc_rk_phy_powerupfn_t phy_powerup; }; struct if_dwc_rk_softc { struct dwc_softc base; uint32_t tx_delay; uint32_t rx_delay; bool integrated_phy; bool clock_in; phandle_t phy_node; struct syscon *grf; struct if_dwc_rk_ops *ops; /* Common clocks */ clk_t mac_clk_rx; clk_t mac_clk_tx; clk_t aclk_mac; clk_t pclk_mac; clk_t clk_stmmaceth; clk_t clk_mac_speed; /* RMII clocks */ clk_t clk_mac_ref; clk_t clk_mac_refout; /* PHY clock */ clk_t clk_phy; }; static void rk3328_set_delays(struct if_dwc_rk_softc *sc); static int rk3328_set_speed(struct if_dwc_rk_softc *sc, int speed); static void rk3328_set_phy_mode(struct if_dwc_rk_softc *sc); static void rk3328_phy_powerup(struct if_dwc_rk_softc *sc); static void rk3399_set_delays(struct if_dwc_rk_softc *sc); static int rk3399_set_speed(struct if_dwc_rk_softc *sc, int speed); static struct if_dwc_rk_ops rk3288_ops = { }; static struct if_dwc_rk_ops rk3328_ops = { .set_delays = rk3328_set_delays, .set_speed = rk3328_set_speed, .set_phy_mode = rk3328_set_phy_mode, .phy_powerup = rk3328_phy_powerup, }; static struct if_dwc_rk_ops rk3399_ops = { .set_delays = rk3399_set_delays, .set_speed = rk3399_set_speed, }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-gmac", (uintptr_t)&rk3288_ops}, {"rockchip,rk3328-gmac", (uintptr_t)&rk3328_ops}, {"rockchip,rk3399-gmac", (uintptr_t)&rk3399_ops}, {NULL, 0} }; static void rk3328_set_delays(struct if_dwc_rk_softc *sc) { uint32_t reg; uint32_t tx, rx; if (!mii_contype_is_rgmii(sc->base.phy_mode)) return; reg = SYSCON_READ_4(sc->grf, RK3328_GRF_MAC_CON0); tx = ((reg >> MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT) & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK); rx = ((reg >> MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT) & MAC_CON0_GMAC2IO_RX_DL_CFG_MASK); reg = SYSCON_READ_4(sc->grf, RK3328_GRF_MAC_CON1); if (bootverbose) { device_printf(sc->base.dev, "current delays settings: tx=%u(%s) rx=%u(%s)\n", tx, ((reg & MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA) ? "enabled" : "disabled"), rx, ((reg & MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA) ? "enabled" : "disabled")); device_printf(sc->base.dev, "setting new RK3328 RX/TX delays: %d/%d\n", sc->tx_delay, sc->rx_delay); } reg = (MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA | MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA) << 16; reg |= (MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA | MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1, reg); reg = 0xffff << 16; reg |= ((sc->tx_delay & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK) << MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT); reg |= ((sc->rx_delay & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK) << MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON0, reg); } static int rk3328_set_speed(struct if_dwc_rk_softc *sc, int speed) { uint32_t reg; switch (sc->base.phy_mode) { case MII_CONTYPE_RGMII: case MII_CONTYPE_RGMII_ID: case MII_CONTYPE_RGMII_RXID: case MII_CONTYPE_RGMII_TXID: switch (speed) { case IFM_1000_T: case IFM_1000_SX: reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_125; break; case IFM_100_TX: reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_25; break; case IFM_10_T: reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_2_5; break; default: device_printf(sc->base.dev, "unsupported RGMII media %u\n", speed); return (-1); } SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1, ((MAC_CON1_GMAC2IO_GMII_CLK_SEL_MASK << 16) | reg)); break; case MII_CONTYPE_RMII: switch (speed) { case IFM_100_TX: reg = MAC_CON1_GMAC2IO_RMII_CLK_SEL_25 | MAC_CON1_GMAC2IO_MAC_SPEED_100; break; case IFM_10_T: reg = MAC_CON1_GMAC2IO_RMII_CLK_SEL_2_5 | MAC_CON1_GMAC2IO_MAC_SPEED_10; break; default: device_printf(sc->base.dev, "unsupported RMII media %u\n", speed); return (-1); } SYSCON_WRITE_4(sc->grf, sc->integrated_phy ? RK3328_GRF_MAC_CON2 : RK3328_GRF_MAC_CON1, reg | ((MAC_CON1_GMAC2IO_RMII_CLK_SEL_MASK | MAC_CON1_GMAC2IO_MAC_SPEED_MASK) << 16)); break; } return (0); } static void rk3328_set_phy_mode(struct if_dwc_rk_softc *sc) { switch (sc->base.phy_mode) { case MII_CONTYPE_RGMII: case MII_CONTYPE_RGMII_ID: case MII_CONTYPE_RGMII_RXID: case MII_CONTYPE_RGMII_TXID: SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1, ((MAC_CON1_GMAC2IO_INTF_SEL_MASK | MAC_CON1_GMAC2IO_RMII_MODE_MASK) << 16) | MAC_CON1_GMAC2IO_INTF_RGMII); break; case MII_CONTYPE_RMII: SYSCON_WRITE_4(sc->grf, sc->integrated_phy ? RK3328_GRF_MAC_CON2 : RK3328_GRF_MAC_CON1, ((MAC_CON1_GMAC2IO_INTF_SEL_MASK | MAC_CON1_GMAC2IO_RMII_MODE_MASK) << 16) | MAC_CON1_GMAC2IO_INTF_RMII | MAC_CON1_GMAC2IO_RMII_MODE); break; } } static void rk3328_phy_powerup(struct if_dwc_rk_softc *sc) { SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON1, (MACPHY_CON1_RMII_MODE_MASK << 16) | MACPHY_CON1_RMII_MODE); } static void rk3399_set_delays(struct if_dwc_rk_softc *sc) { uint32_t reg, tx, rx; if (!mii_contype_is_rgmii(sc->base.phy_mode)) return; reg = SYSCON_READ_4(sc->grf, RK3399_GRF_SOC_CON6); tx = ((reg >> SOC_CON6_TX_DL_CFG_SHIFT) & SOC_CON6_TX_DL_CFG_MASK); rx = ((reg >> SOC_CON6_RX_DL_CFG_SHIFT) & SOC_CON6_RX_DL_CFG_MASK); if (bootverbose) { device_printf(sc->base.dev, "current delays settings: tx=%u(%s) rx=%u(%s)\n", tx, ((reg & SOC_CON6_GMAC_TXCLK_DLY_ENA) ? "enabled" : "disabled"), rx, ((reg & SOC_CON6_GMAC_RXCLK_DLY_ENA) ? "enabled" : "disabled")); device_printf(sc->base.dev, "setting new RK3399 RX/TX delays: %d/%d\n", sc->rx_delay, sc->tx_delay); } reg = 0xFFFF << 16; reg |= ((sc->tx_delay & SOC_CON6_TX_DL_CFG_MASK) << SOC_CON6_TX_DL_CFG_SHIFT); reg |= ((sc->rx_delay & SOC_CON6_RX_DL_CFG_MASK) << SOC_CON6_RX_DL_CFG_SHIFT); reg |= SOC_CON6_GMAC_TXCLK_DLY_ENA | SOC_CON6_GMAC_RXCLK_DLY_ENA; SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON6, reg); } static int rk3399_set_speed(struct if_dwc_rk_softc *sc, int speed) { uint32_t reg; switch (speed) { case IFM_1000_T: case IFM_1000_SX: reg = SOC_CON5_GMAC_CLK_SEL_125; break; case IFM_100_TX: reg = SOC_CON5_GMAC_CLK_SEL_25; break; case IFM_10_T: reg = SOC_CON5_GMAC_CLK_SEL_2_5; break; default: device_printf(sc->base.dev, "unsupported media %u\n", speed); return (-1); } SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON5, ((SOC_CON5_GMAC_CLK_SEL_MASK << 16) | reg)); return (0); } static int if_dwc_rk_sysctl_delays(SYSCTL_HANDLER_ARGS) { struct if_dwc_rk_softc *sc; int rv; uint32_t rxtx; sc = arg1; rxtx = ((sc->rx_delay << 8) | sc->tx_delay); rv = sysctl_handle_int(oidp, &rxtx, 0, req); if (rv != 0 || req->newptr == NULL) return (rv); sc->tx_delay = rxtx & 0xff; sc->rx_delay = (rxtx >> 8) & 0xff; if (sc->ops->set_delays) sc->ops->set_delays(sc); return (0); } static int if_dwc_rk_init_sysctl(struct if_dwc_rk_softc *sc) { struct sysctl_oid *child; struct sysctl_ctx_list *ctx_list; ctx_list = device_get_sysctl_ctx(sc->base.dev); child = device_get_sysctl_tree(sc->base.dev); SYSCTL_ADD_PROC(ctx_list, SYSCTL_CHILDREN(child), OID_AUTO, "delays", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, sc, 0, if_dwc_rk_sysctl_delays, "", "RGMII RX/TX delays: ((rx << 8) | tx)"); return (0); } static int if_dwc_rk_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Rockchip Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int if_dwc_rk_init_clocks(device_t dev) { struct if_dwc_rk_softc *sc; sc = device_get_softc(dev); /* Enable clocks */ if (clk_get_by_ofw_name(dev, 0, "mac_clk_tx", &sc->mac_clk_tx) != 0) { device_printf(sc->base.dev, "could not get mac_clk_tx clock\n"); sc->mac_clk_tx = NULL; } if (clk_get_by_ofw_name(dev, 0, "aclk_mac", &sc->aclk_mac) != 0) { device_printf(sc->base.dev, "could not get aclk_mac clock\n"); sc->aclk_mac = NULL; } if (clk_get_by_ofw_name(dev, 0, "pclk_mac", &sc->pclk_mac) != 0) { device_printf(sc->base.dev, "could not get pclk_mac clock\n"); sc->pclk_mac = NULL; } /* Optional clock */ clk_get_by_ofw_name(dev, 0, "clk_mac_speed", &sc->clk_mac_speed); if (sc->base.phy_mode == MII_CONTYPE_RMII) { if (clk_get_by_ofw_name(dev, 0, "mac_clk_rx", &sc->mac_clk_rx) != 0) { device_printf(sc->base.dev, "could not get mac_clk_rx clock\n"); sc->mac_clk_rx = NULL; } if (clk_get_by_ofw_name(dev, 0, "clk_mac_ref", &sc->clk_mac_ref) != 0) { device_printf(sc->base.dev, "could not get clk_mac_ref clock\n"); sc->clk_mac_ref = NULL; } if (!sc->clock_in) { if (clk_get_by_ofw_name(dev, 0, "clk_mac_refout", &sc->clk_mac_refout) != 0) { device_printf(sc->base.dev, "could not get clk_mac_refout clock\n"); sc->clk_mac_refout = NULL; } clk_set_freq(sc->clk_stmmaceth, 50000000, 0); } } if ((sc->phy_node != 0) && sc->integrated_phy) { if (clk_get_by_ofw_index(dev, sc->phy_node, 0, &sc->clk_phy) != 0) { device_printf(sc->base.dev, "could not get PHY clock\n"); sc->clk_phy = NULL; } if (sc->clk_phy) { clk_set_freq(sc->clk_phy, 50000000, 0); } } if (sc->base.phy_mode == MII_CONTYPE_RMII) { if (sc->mac_clk_rx) clk_enable(sc->mac_clk_rx); if (sc->clk_mac_ref) clk_enable(sc->clk_mac_ref); if (sc->clk_mac_refout) clk_enable(sc->clk_mac_refout); } if (sc->clk_phy) clk_enable(sc->clk_phy); if (sc->aclk_mac) clk_enable(sc->aclk_mac); if (sc->pclk_mac) clk_enable(sc->pclk_mac); if (sc->mac_clk_tx) clk_enable(sc->mac_clk_tx); if (sc->clk_mac_speed) clk_enable(sc->clk_mac_speed); DELAY(50); return (0); } static int if_dwc_rk_init(device_t dev) { struct if_dwc_rk_softc *sc; phandle_t node; uint32_t rx, tx; int err; pcell_t phy_handle; char *clock_in_out; hwreset_t phy_reset; regulator_t phy_supply; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->ops = (struct if_dwc_rk_ops *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } if (OF_getencprop(node, "tx_delay", &tx, sizeof(tx)) <= 0) tx = 0x30; if (OF_getencprop(node, "rx_delay", &rx, sizeof(rx)) <= 0) rx = 0x10; sc->tx_delay = tx; sc->rx_delay = rx; sc->clock_in = true; if (OF_getprop_alloc(node, "clock_in_out", (void **)&clock_in_out)) { if (strcmp(clock_in_out, "input") == 0) sc->clock_in = true; else sc->clock_in = false; OF_prop_free(clock_in_out); } if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, sizeof(phy_handle)) > 0) sc->phy_node = OF_node_from_xref(phy_handle); if (sc->phy_node) sc->integrated_phy = OF_hasprop(sc->phy_node, "phy-is-integrated"); if (sc->integrated_phy) device_printf(sc->base.dev, "PHY is integrated\n"); if_dwc_rk_init_clocks(dev); if (sc->ops->set_phy_mode) sc->ops->set_phy_mode(sc); if (sc->ops->set_delays) sc->ops->set_delays(sc); /* * this also sets delays if tunable is defined */ err = if_dwc_rk_init_sysctl(sc); if (err != 0) return (err); if (regulator_get_by_ofw_property(sc->base.dev, 0, "phy-supply", &phy_supply) == 0) { if (regulator_enable(phy_supply)) { device_printf(sc->base.dev, "cannot enable 'phy' regulator\n"); } } else device_printf(sc->base.dev, "no phy-supply property\n"); /* Power up */ if (sc->integrated_phy) { if (sc->ops->phy_powerup) sc->ops->phy_powerup(sc); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON0, (MACPHY_CON0_CLK_50M_MASK << 16) | MACPHY_CON0_CLK_50M); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON0, (MACPHY_CON0_RMII_MODE_MASK << 16) | MACPHY_CON0_RMII_MODE); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON2, 0xffff1234); SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON3, 0x003f0035); if (hwreset_get_by_ofw_idx(dev, sc->phy_node, 0, &phy_reset) == 0) { hwreset_assert(phy_reset); DELAY(20); hwreset_deassert(phy_reset); DELAY(20); } } return (0); } static int if_dwc_rk_mii_clk(device_t dev) { struct if_dwc_rk_softc *sc; uint64_t freq; int rv; sc = device_get_softc(dev); if ((rv = clk_get_freq(sc->pclk_mac, &freq)) != 0) return (-rv); freq = freq / 1000 / 1000; if (freq >= 60 && freq <= 100) return (GMAC_MII_CLK_60_100M_DIV42); else if (freq >= 100 && freq <= 150) return (GMAC_MII_CLK_100_150M_DIV62); else if (freq >= 20 && freq <= 35) return (GMAC_MII_CLK_25_35M_DIV16); else if (freq >= 35 && freq <= 60) return (GMAC_MII_CLK_35_60M_DIV26); else if (freq >= 150 && freq <= 250) return (GMAC_MII_CLK_150_250M_DIV102); else if (freq >= 250 && freq <= 300) return (GMAC_MII_CLK_250_300M_DIV124); return (-ERANGE); } static int if_dwc_rk_set_speed(device_t dev, int speed) { struct if_dwc_rk_softc *sc; sc = device_get_softc(dev); if (sc->ops->set_speed) return sc->ops->set_speed(sc, speed); return (0); } static device_method_t if_dwc_rk_methods[] = { DEVMETHOD(device_probe, if_dwc_rk_probe), DEVMETHOD(if_dwc_init, if_dwc_rk_init), DEVMETHOD(if_dwc_mii_clk, if_dwc_rk_mii_clk), DEVMETHOD(if_dwc_set_speed, if_dwc_rk_set_speed), DEVMETHOD_END }; extern driver_t dwc_driver; DEFINE_CLASS_1(dwc, dwc_rk_driver, if_dwc_rk_methods, sizeof(struct if_dwc_rk_softc), dwc_driver); DRIVER_MODULE(dwc_rk, simplebus, dwc_rk_driver, 0, 0); MODULE_DEPEND(dwc_rk, dwc, 1, 1, 1); diff --git a/sys/dev/dwc/if_dwc_socfpga.c b/sys/dev/dwc/if_dwc_socfpga.c index ae2bcac54a95..5a95d133c197 100644 --- a/sys/dev/dwc/if_dwc_socfpga.c +++ b/sys/dev/dwc/if_dwc_socfpga.c @@ -1,107 +1,107 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Ruslan Bukin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory (Department of Computer Science and * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the * DARPA SSITH research programme. * * 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 -#include +#include #include #include #include "if_dwc_if.h" static int if_dwc_socfpga_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "altr,socfpga-stmmac")) return (ENXIO); device_set_desc(dev, "Altera SOCFPGA Ethernet MAC"); return (BUS_PROBE_DEFAULT); } static int if_dwc_socfpga_init(device_t dev) { return (0); } static int if_dwc_socfpga_mii_clk(device_t dev) { phandle_t root; root = OF_finddevice("/"); if (ofw_bus_node_is_compatible(root, "altr,socfpga-stratix10")) return (GMAC_MII_CLK_35_60M_DIV26); /* Default value. */ return (GMAC_MII_CLK_25_35M_DIV16); } static device_method_t dwc_socfpga_methods[] = { DEVMETHOD(device_probe, if_dwc_socfpga_probe), DEVMETHOD(if_dwc_init, if_dwc_socfpga_init), DEVMETHOD(if_dwc_mii_clk, if_dwc_socfpga_mii_clk), DEVMETHOD_END }; extern driver_t dwc_driver; DEFINE_CLASS_1(dwc, dwc_socfpga_driver, dwc_socfpga_methods, sizeof(struct dwc_softc), dwc_driver); EARLY_DRIVER_MODULE(dwc_socfpga, simplebus, dwc_socfpga_driver, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); MODULE_DEPEND(dwc_socfpga, dwc, 1, 1, 1); diff --git a/sys/dev/eqos/if_eqos_fdt.c b/sys/dev/eqos/if_eqos_fdt.c index 9c36f658bad1..5601c8b778e2 100644 --- a/sys/dev/eqos/if_eqos_fdt.c +++ b/sys/dev/eqos/if_eqos_fdt.c @@ -1,307 +1,307 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Soren Schmidt * 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. * * $Id: eqos_fdt.c 1049 2022-12-03 14:25:46Z sos $ */ #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 #include #include #include "if_eqos_if.h" #include "syscon_if.h" #include "gpio_if.h" #include "rk_otp_if.h" #define RK356XGMAC0 0xfe2a0000 #define RK356XGMAC1 0xfe010000 #define RK3588GMAC0 0xfe1b0000 #define RK3588GMAC1 0xfe1c0000 #define EQOS_GRF_GMAC0 0x0380 #define EQOS_GRF_GMAC1 0x0388 #define EQOS_CON0_OFFSET 0 #define EQOS_CON1_OFFSET 4 #define EQOS_GMAC_PHY_INTF_SEL_RGMII 0x00fc0010 #define EQOS_GMAC_PHY_INTF_SEL_RMII 0x00fc0040 #define EQOS_GMAC_RXCLK_DLY_ENABLE 0x00020002 #define EQOS_GMAC_RXCLK_DLY_DISABLE 0x00020000 #define EQOS_GMAC_TXCLK_DLY_ENABLE 0x00010001 #define EQOS_GMAC_TXCLK_DLY_DISABLE 0x00010000 #define EQOS_GMAC_CLK_RX_DL_CFG(val) (0x7f000000 | val << 8) #define EQOS_GMAC_CLK_TX_DL_CFG(val) (0x007f0000 | val) #define WR4(sc, o, v) bus_write_4(sc->res[EQOS_RES_MEM], (o), (v)) static const struct ofw_compat_data compat_data[] = { {"snps,dwmac-4.20a", 1}, { NULL, 0 } }; static int eqos_phy_reset(device_t dev) { pcell_t gpio_prop[4]; pcell_t delay_prop[3]; phandle_t node, gpio_node; device_t gpio; uint32_t pin, flags; uint32_t pin_value; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "snps,reset-gpio", gpio_prop, sizeof(gpio_prop)) <= 0) return (0); if (OF_getencprop(node, "snps,reset-delays-us", delay_prop, sizeof(delay_prop)) <= 0) { device_printf(dev, "Wrong property for snps,reset-delays-us"); return (ENXIO); } gpio_node = OF_node_from_xref(gpio_prop[0]); if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { device_printf(dev, "Can't find gpio controller for phy reset\n"); return (ENXIO); } if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1, gpio_prop + 1, &pin, &flags) != 0) { device_printf(dev, "Can't map gpio for phy reset\n"); return (ENXIO); } pin_value = GPIO_PIN_LOW; if (OF_hasprop(node, "snps,reset-active-low")) pin_value = GPIO_PIN_HIGH; GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[0]); GPIO_PIN_SET(gpio, pin, !pin_value); DELAY(delay_prop[1]); GPIO_PIN_SET(gpio, pin, pin_value); DELAY(delay_prop[2]); return (0); } static int eqos_fdt_init(device_t dev) { struct eqos_softc *sc = device_get_softc(dev); phandle_t node = ofw_bus_get_node(dev); hwreset_t eqos_reset; regulator_t eqos_supply; uint32_t rx_delay, tx_delay; uint8_t buffer[16]; clk_t stmmaceth, mac_clk_rx, mac_clk_tx, aclk_mac, pclk_mac; uint64_t freq; int error; if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf)) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } /* figure out if gmac0 or gmac1 offset */ switch (rman_get_start(sc->res[EQOS_RES_MEM])) { case RK356XGMAC0: /* RK356X gmac0 */ sc->grf_offset = EQOS_GRF_GMAC0; break; case RK356XGMAC1: /* RK356X gmac1 */ sc->grf_offset = EQOS_GRF_GMAC1; break; case RK3588GMAC0: /* RK3588 gmac0 */ case RK3588GMAC1: /* RK3588 gmac1 */ default: device_printf(dev, "Unknown eqos address\n"); return (ENXIO); } if (hwreset_get_by_ofw_idx(dev, node, 0, &eqos_reset)) { device_printf(dev, "cannot get reset\n"); return (ENXIO); } hwreset_assert(eqos_reset); error = clk_set_assigned(dev, ofw_bus_get_node(dev)); if (error != 0) { device_printf(dev, "clk_set_assigned failed\n"); return (error); } if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &stmmaceth) == 0) { error = clk_enable(stmmaceth); if (error != 0) { device_printf(dev, "could not enable main clock\n"); return (error); } if (bootverbose) { clk_get_freq(stmmaceth, &freq); device_printf(dev, "MAC clock(%s) freq: %jd\n", clk_get_name(stmmaceth), (intmax_t)freq); } } else { device_printf(dev, "could not find clock stmmaceth\n"); } if (clk_get_by_ofw_name(dev, 0, "mac_clk_rx", &mac_clk_rx) != 0) { device_printf(dev, "could not get mac_clk_rx clock\n"); mac_clk_rx = NULL; } if (clk_get_by_ofw_name(dev, 0, "mac_clk_tx", &mac_clk_tx) != 0) { device_printf(dev, "could not get mac_clk_tx clock\n"); mac_clk_tx = NULL; } if (clk_get_by_ofw_name(dev, 0, "aclk_mac", &aclk_mac) != 0) { device_printf(dev, "could not get aclk_mac clock\n"); aclk_mac = NULL; } if (clk_get_by_ofw_name(dev, 0, "pclk_mac", &pclk_mac) != 0) { device_printf(dev, "could not get pclk_mac clock\n"); pclk_mac = NULL; } if (aclk_mac) clk_enable(aclk_mac); if (pclk_mac) clk_enable(pclk_mac); if (mac_clk_tx) clk_enable(mac_clk_tx); sc->csr_clock = 125000000; sc->csr_clock_range = GMAC_MAC_MDIO_ADDRESS_CR_100_150; if (OF_getencprop(node, "tx_delay", &tx_delay, sizeof(tx_delay)) <= 0) tx_delay = 0x30; if (OF_getencprop(node, "rx_delay", &rx_delay, sizeof(rx_delay)) <= 0) rx_delay = 0x10; SYSCON_WRITE_4(sc->grf, sc->grf_offset + EQOS_CON0_OFFSET, EQOS_GMAC_CLK_RX_DL_CFG(rx_delay) | EQOS_GMAC_CLK_TX_DL_CFG(tx_delay)); SYSCON_WRITE_4(sc->grf, sc->grf_offset + EQOS_CON1_OFFSET, EQOS_GMAC_PHY_INTF_SEL_RGMII | EQOS_GMAC_RXCLK_DLY_ENABLE | EQOS_GMAC_TXCLK_DLY_ENABLE); if (!regulator_get_by_ofw_property(dev, 0, "phy-supply", &eqos_supply)) { if (regulator_enable(eqos_supply)) device_printf(dev, "cannot enable 'phy' regulator\n"); } else device_printf(dev, "no phy-supply property\n"); if (eqos_phy_reset(dev)) return (ENXIO); if (eqos_reset) hwreset_deassert(eqos_reset); /* set the MAC address if we have OTP data handy */ if (!RK_OTP_READ(dev, buffer, 0, sizeof(buffer))) { uint32_t mac; mac = hash32_buf(buffer, sizeof(buffer), HASHINIT); WR4(sc, GMAC_MAC_ADDRESS0_LOW, htobe32((mac & 0xffffff00) | 0x22)); mac = hash32_buf(buffer, sizeof(buffer), mac); WR4(sc, GMAC_MAC_ADDRESS0_HIGH, htobe16((mac & 0x0000ffff) + (device_get_unit(dev) << 8))); } return (0); } static int eqos_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "DesignWare EQOS Gigabit ethernet"); return (BUS_PROBE_DEFAULT); } static device_method_t eqos_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, eqos_fdt_probe), /* EQOS interface */ DEVMETHOD(if_eqos_init, eqos_fdt_init), DEVMETHOD_END }; DEFINE_CLASS_1(eqos, eqos_fdt_driver, eqos_fdt_methods, sizeof(struct eqos_softc), eqos_driver); DRIVER_MODULE(eqos, simplebus, eqos_fdt_driver, 0, 0); MODULE_DEPEND(eqos, ether, 1, 1, 1); MODULE_DEPEND(eqos, miibus, 1, 1, 1); diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw.c index c017419be0ff..cdc13366eef3 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw.c @@ -1,357 +1,357 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include /* * XXX these are here for now; move the code using these * into main.c once this is all done! */ #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" /* * Reset the ESS switch. This also resets the ESS ethernet * and PSGMII block. */ int ar40xx_hw_ess_reset(struct ar40xx_softc *sc) { int ret; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_RESET, "%s: called\n", __func__); ret = hwreset_assert(sc->sc_ess_rst); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to assert reset\n"); return ret; } DELAY(10*1000); ret = hwreset_deassert(sc->sc_ess_rst); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to deassert reset\n"); return ret; } DELAY(10*1000); return (0); } int ar40xx_hw_init_globals(struct ar40xx_softc *sc) { uint32_t reg; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); /* enable CPU port and disable mirror port */ reg = AR40XX_FWD_CTRL0_CPU_PORT_EN | AR40XX_FWD_CTRL0_MIRROR_PORT; AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); /* forward multicast and broadcast frames to CPU */ reg = (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_UC_FLOOD_S) | (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_MC_FLOOD_S) | (AR40XX_PORTS_ALL << AR40XX_FWD_CTRL1_BC_FLOOD_S); AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL1, reg); /* enable jumbo frames */ reg = AR40XX_REG_READ(sc, AR40XX_REG_MAX_FRAME_SIZE); reg &= ~AR40XX_MAX_FRAME_SIZE_MTU; reg |= 9018 + 8 + 2; AR40XX_REG_WRITE(sc, AR40XX_REG_MAX_FRAME_SIZE, reg); /* Enable MIB counters */ reg = AR40XX_REG_READ(sc, AR40XX_REG_MODULE_EN); reg |= AR40XX_MODULE_EN_MIB; AR40XX_REG_WRITE(sc, AR40XX_REG_MODULE_EN, reg); /* Disable AZ */ AR40XX_REG_WRITE(sc, AR40XX_REG_EEE_CTRL, 0); /* set flowctrl thershold for cpu port */ reg = (AR40XX_PORT0_FC_THRESH_ON_DFLT << 16) | AR40XX_PORT0_FC_THRESH_OFF_DFLT; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(0), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } int ar40xx_hw_vlan_init(struct ar40xx_softc *sc) { int i; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); /* Enable VLANs by default */ sc->sc_vlan.vlan = 1; /* Configure initial LAN/WAN bitmap and include CPU port as tagged */ sc->sc_vlan.vlan_id[AR40XX_LAN_VLAN] = AR40XX_LAN_VLAN | ETHERSWITCH_VID_VALID; sc->sc_vlan.vlan_id[AR40XX_WAN_VLAN] = AR40XX_WAN_VLAN | ETHERSWITCH_VID_VALID; sc->sc_vlan.vlan_ports[AR40XX_LAN_VLAN] = sc->sc_config.switch_cpu_bmp | sc->sc_config.switch_lan_bmp; sc->sc_vlan.vlan_untagged[AR40XX_LAN_VLAN] = sc->sc_config.switch_lan_bmp; sc->sc_vlan.vlan_ports[AR40XX_WAN_VLAN] = sc->sc_config.switch_cpu_bmp | sc->sc_config.switch_wan_bmp; sc->sc_vlan.vlan_untagged[AR40XX_WAN_VLAN] = sc->sc_config.switch_wan_bmp; /* Populate the per-port PVID - pvid[] is an index into vlan_id[] */ for (i = 0; i < AR40XX_NUM_PORTS; i++) { if (sc->sc_config.switch_lan_bmp & (1U << i)) sc->sc_vlan.pvid[i] = AR40XX_LAN_VLAN; if (sc->sc_config.switch_wan_bmp & (1U << i)) sc->sc_vlan.pvid[i] = AR40XX_WAN_VLAN; } return (0); } /* * Apply the per-port and global configuration from software. * * This is useful if we ever start doing the linux switch framework * thing of updating the config in one hit and pushing it to the * hardware. For now it's just used in the reset path. */ int ar40xx_hw_sw_hw_apply(struct ar40xx_softc *sc) { uint8_t portmask[AR40XX_NUM_PORTS]; int i, j, ret; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); /* * Flush the VTU configuration. */ ret = ar40xx_hw_vtu_flush(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: couldn't apply config; vtu flush failed (%d)\n", ret); return (ret); } memset(portmask, 0, sizeof(portmask)); /* * Configure the ports based on whether it's 802.1q * VLANs, or just straight up per-port VLANs. */ if (sc->sc_vlan.vlan) { device_printf(sc->sc_dev, "%s: configuring 802.1q VLANs\n", __func__); for (j = 0; j < AR40XX_NUM_VTU_ENTRIES; j++) { uint8_t vp = sc->sc_vlan.vlan_ports[j]; if (!vp) continue; if ((sc->sc_vlan.vlan_id[j] & ETHERSWITCH_VID_VALID) == 0) continue; for (i = 0; i < AR40XX_NUM_PORTS; i++) { uint8_t mask = (1U << i); if (vp & mask) portmask[i] |= vp & ~mask; } ar40xx_hw_vtu_load_vlan(sc, sc->sc_vlan.vlan_id[j] & ETHERSWITCH_VID_MASK, sc->sc_vlan.vlan_ports[j], sc->sc_vlan.vlan_untagged[j]); } } else { device_printf(sc->sc_dev, "%s: configuring per-port VLANs\n", __func__); for (i = 0; i < AR40XX_NUM_PORTS; i++) { if (i == AR40XX_PORT_CPU) continue; portmask[i] = (1U << AR40XX_PORT_CPU); portmask[AR40XX_PORT_CPU] |= (1U << i); } } /* * Update per-port destination mask, vlan tag settings */ for (i = 0; i < AR40XX_NUM_PORTS; i++) (void) ar40xx_hw_port_setup(sc, i, portmask[i]); /* Set the mirror register config */ ret = ar40xx_hw_mirror_set_registers(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: couldn't apply config; mirror config failed" " (%d)\n", ret); return (ret); } return (0); } int ar40xx_hw_wait_bit(struct ar40xx_softc *sc, int reg, uint32_t mask, uint32_t val) { int timeout = 20; uint32_t t; while (true) { AR40XX_REG_BARRIER_READ(sc); t = AR40XX_REG_READ(sc, reg); if ((t & mask) == val) return 0; if (timeout-- <= 0) break; DELAY(20); } device_printf(sc->sc_dev, "ERROR: timeout for reg " "%08x: %08x & %08x != %08x\n", (unsigned int)reg, t, mask, val); return (ETIMEDOUT); } /* * Read the switch MAC address. */ int ar40xx_hw_read_switch_mac_address(struct ar40xx_softc *sc, struct ether_addr *ea) { uint32_t ret0, ret1; char *s; s = (void *) ea; AR40XX_LOCK_ASSERT(sc); AR40XX_REG_BARRIER_READ(sc); ret0 = AR40XX_REG_READ(sc, AR40XX_REG_SW_MAC_ADDR0); ret1 = AR40XX_REG_READ(sc, AR40XX_REG_SW_MAC_ADDR1); s[5] = MS(ret0, AR40XX_REG_SW_MAC_ADDR0_BYTE5); s[4] = MS(ret0, AR40XX_REG_SW_MAC_ADDR0_BYTE4); s[3] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE3); s[2] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE2); s[1] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE1); s[0] = MS(ret1, AR40XX_REG_SW_MAC_ADDR1_BYTE0); return (0); } /* * Set the switch MAC address. */ int ar40xx_hw_write_switch_mac_address(struct ar40xx_softc *sc, struct ether_addr *ea) { uint32_t ret0 = 0, ret1 = 0; char *s; s = (void *) ea; AR40XX_LOCK_ASSERT(sc); ret0 |= SM(s[5], AR40XX_REG_SW_MAC_ADDR0_BYTE5); ret0 |= SM(s[4], AR40XX_REG_SW_MAC_ADDR0_BYTE4); ret1 |= SM(s[3], AR40XX_REG_SW_MAC_ADDR1_BYTE3); ret1 |= SM(s[2], AR40XX_REG_SW_MAC_ADDR1_BYTE2); ret1 |= SM(s[1], AR40XX_REG_SW_MAC_ADDR1_BYTE1); ret1 |= SM(s[0], AR40XX_REG_SW_MAC_ADDR1_BYTE0); AR40XX_REG_WRITE(sc, AR40XX_REG_SW_MAC_ADDR0, ret0); AR40XX_REG_WRITE(sc, AR40XX_REG_SW_MAC_ADDR1, ret1); AR40XX_REG_BARRIER_WRITE(sc); return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c index a3facf4a6199..4ddcc58a2cfc 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_atu.c @@ -1,216 +1,216 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" int ar40xx_hw_atu_wait_busy(struct ar40xx_softc *sc) { int ret; ret = ar40xx_hw_wait_bit(sc, AR40XX_REG_ATU_FUNC, AR40XX_ATU_FUNC_BUSY, 0); return (ret); } int ar40xx_hw_atu_flush_all(struct ar40xx_softc *sc) { int ret; AR40XX_LOCK_ASSERT(sc); AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: called\n", __func__); ret = ar40xx_hw_atu_wait_busy(sc); if (ret != 0) return (ret); AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, AR40XX_ATU_FUNC_OP_FLUSH | AR40XX_ATU_FUNC_BUSY); AR40XX_REG_BARRIER_WRITE(sc); return (ret); } int ar40xx_hw_atu_flush_port(struct ar40xx_softc *sc, int port) { uint32_t val; int ret; AR40XX_LOCK_ASSERT(sc); AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: called, port=%d\n", __func__, port); if (port >= AR40XX_NUM_PORTS) { return (EINVAL); } ret = ar40xx_hw_atu_wait_busy(sc); if (ret != 0) return (ret); val = AR40XX_ATU_FUNC_OP_FLUSH_UNICAST; val |= (port << AR40XX_ATU_FUNC_PORT_NUM_S) & AR40XX_ATU_FUNC_PORT_NUM; AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, val | AR40XX_ATU_FUNC_BUSY); AR40XX_REG_BARRIER_WRITE(sc); return (0); } int ar40xx_hw_atu_fetch_entry(struct ar40xx_softc *sc, etherswitch_atu_entry_t *e, int atu_fetch_op) { uint32_t ret0, ret1, ret2, val; int ret; AR40XX_LOCK_ASSERT(sc); switch (atu_fetch_op) { case 0: /* Initialise things for the first fetch */ AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: initializing\n", __func__); ret = ar40xx_hw_atu_wait_busy(sc); if (ret != 0) return (ret); AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, AR40XX_ATU_FUNC_OP_GET_NEXT); AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA0, 0); AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA1, 0); AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_DATA2, 0); AR40XX_REG_BARRIER_WRITE(sc); return (0); case 1: AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: reading next\n", __func__); /* * Attempt to read the next address entry; don't modify what * is there in these registers as its used for the next fetch */ ret = ar40xx_hw_atu_wait_busy(sc); if (ret != 0) return (ret); /* Begin the next read event; not modifying anything */ AR40XX_REG_BARRIER_READ(sc); val = AR40XX_REG_READ(sc, AR40XX_REG_ATU_FUNC); val |= AR40XX_ATU_FUNC_BUSY; AR40XX_REG_WRITE(sc, AR40XX_REG_ATU_FUNC, val); AR40XX_REG_BARRIER_WRITE(sc); /* Wait for it to complete */ ret = ar40xx_hw_atu_wait_busy(sc); if (ret != 0) return (ret); /* Fetch the ethernet address and ATU status */ AR40XX_REG_BARRIER_READ(sc); ret0 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA0); ret1 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA1); ret2 = AR40XX_REG_READ(sc, AR40XX_REG_ATU_DATA2); /* If the status is zero, then we're done */ if (MS(ret2, AR40XX_ATU_FUNC_DATA2_STATUS) == 0) return (ENOENT); /* MAC address */ e->es_macaddr[5] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR3); e->es_macaddr[4] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR2); e->es_macaddr[3] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR1); e->es_macaddr[2] = MS(ret0, AR40XX_ATU_DATA0_MAC_ADDR0); e->es_macaddr[0] = MS(ret1, AR40XX_ATU_DATA1_MAC_ADDR5); e->es_macaddr[1] = MS(ret1, AR40XX_ATU_DATA1_MAC_ADDR4); /* Bitmask of ports this entry is for */ e->es_portmask = MS(ret1, AR40XX_ATU_DATA1_DEST_PORT); /* TODO: other flags that are interesting */ AR40XX_DPRINTF(sc, AR40XX_DBG_ATU_OP, "%s: MAC %6D portmask 0x%08x\n", __func__, e->es_macaddr, ":", e->es_portmask); return (0); default: return (EINVAL); } return (EINVAL); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c index 43c2d8744054..793507b9aaa2 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mdio.c @@ -1,129 +1,129 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" int ar40xx_hw_phy_dbg_write(struct ar40xx_softc *sc, int phy, uint16_t dbg, uint16_t data) { AR40XX_LOCK_ASSERT(sc); device_printf(sc->sc_dev, "%s: TODO\n", __func__); return (0); } int ar40xx_hw_phy_dbg_read(struct ar40xx_softc *sc, int phy, uint16_t dbg) { AR40XX_LOCK_ASSERT(sc); device_printf(sc->sc_dev, "%s: TODO\n", __func__); return (-1); } int ar40xx_hw_phy_mmd_write(struct ar40xx_softc *sc, uint32_t phy_id, uint16_t mmd_num, uint16_t reg_id, uint16_t reg_val) { AR40XX_LOCK_ASSERT(sc); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, mmd_num); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, reg_id); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, 0x4000 | mmd_num); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, reg_val); return (0); } int ar40xx_hw_phy_mmd_read(struct ar40xx_softc *sc, uint32_t phy_id, uint16_t mmd_num, uint16_t reg_id) { uint16_t value; AR40XX_LOCK_ASSERT(sc); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, mmd_num); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA, reg_id); MDIO_WRITEREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_ADDR, 0x4000 | mmd_num); value = MDIO_READREG(sc->sc_mdio_dev, phy_id, AR40XX_MII_ATH_MMD_DATA); return value; } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c index 6fdc9e96fd81..73110753c915 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mib.c @@ -1,194 +1,194 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" #define MIB_DESC(_s , _o, _n) \ { \ .size = (_s), \ .offset = (_o), \ .name = (_n), \ } static const struct ar40xx_mib_desc ar40xx_mibs[] = { MIB_DESC(1, AR40XX_STATS_RXBROAD, "RxBroad"), MIB_DESC(1, AR40XX_STATS_RXPAUSE, "RxPause"), MIB_DESC(1, AR40XX_STATS_RXMULTI, "RxMulti"), MIB_DESC(1, AR40XX_STATS_RXFCSERR, "RxFcsErr"), MIB_DESC(1, AR40XX_STATS_RXALIGNERR, "RxAlignErr"), MIB_DESC(1, AR40XX_STATS_RXRUNT, "RxRunt"), MIB_DESC(1, AR40XX_STATS_RXFRAGMENT, "RxFragment"), MIB_DESC(1, AR40XX_STATS_RX64BYTE, "Rx64Byte"), MIB_DESC(1, AR40XX_STATS_RX128BYTE, "Rx128Byte"), MIB_DESC(1, AR40XX_STATS_RX256BYTE, "Rx256Byte"), MIB_DESC(1, AR40XX_STATS_RX512BYTE, "Rx512Byte"), MIB_DESC(1, AR40XX_STATS_RX1024BYTE, "Rx1024Byte"), MIB_DESC(1, AR40XX_STATS_RX1518BYTE, "Rx1518Byte"), MIB_DESC(1, AR40XX_STATS_RXMAXBYTE, "RxMaxByte"), MIB_DESC(1, AR40XX_STATS_RXTOOLONG, "RxTooLong"), MIB_DESC(2, AR40XX_STATS_RXGOODBYTE, "RxGoodByte"), MIB_DESC(2, AR40XX_STATS_RXBADBYTE, "RxBadByte"), MIB_DESC(1, AR40XX_STATS_RXOVERFLOW, "RxOverFlow"), MIB_DESC(1, AR40XX_STATS_FILTERED, "Filtered"), MIB_DESC(1, AR40XX_STATS_TXBROAD, "TxBroad"), MIB_DESC(1, AR40XX_STATS_TXPAUSE, "TxPause"), MIB_DESC(1, AR40XX_STATS_TXMULTI, "TxMulti"), MIB_DESC(1, AR40XX_STATS_TXUNDERRUN, "TxUnderRun"), MIB_DESC(1, AR40XX_STATS_TX64BYTE, "Tx64Byte"), MIB_DESC(1, AR40XX_STATS_TX128BYTE, "Tx128Byte"), MIB_DESC(1, AR40XX_STATS_TX256BYTE, "Tx256Byte"), MIB_DESC(1, AR40XX_STATS_TX512BYTE, "Tx512Byte"), MIB_DESC(1, AR40XX_STATS_TX1024BYTE, "Tx1024Byte"), MIB_DESC(1, AR40XX_STATS_TX1518BYTE, "Tx1518Byte"), MIB_DESC(1, AR40XX_STATS_TXMAXBYTE, "TxMaxByte"), MIB_DESC(1, AR40XX_STATS_TXOVERSIZE, "TxOverSize"), MIB_DESC(2, AR40XX_STATS_TXBYTE, "TxByte"), MIB_DESC(1, AR40XX_STATS_TXCOLLISION, "TxCollision"), MIB_DESC(1, AR40XX_STATS_TXABORTCOL, "TxAbortCol"), MIB_DESC(1, AR40XX_STATS_TXMULTICOL, "TxMultiCol"), MIB_DESC(1, AR40XX_STATS_TXSINGLECOL, "TxSingleCol"), MIB_DESC(1, AR40XX_STATS_TXEXCDEFER, "TxExcDefer"), MIB_DESC(1, AR40XX_STATS_TXDEFER, "TxDefer"), MIB_DESC(1, AR40XX_STATS_TXLATECOL, "TxLateCol"), }; int ar40xx_hw_mib_op(struct ar40xx_softc *sc, uint32_t op) { uint32_t reg; int ret; AR40XX_LOCK_ASSERT(sc); /* Trigger capturing statistics on all ports */ AR40XX_REG_BARRIER_READ(sc); reg = AR40XX_REG_READ(sc, AR40XX_REG_MIB_FUNC); reg &= ~AR40XX_MIB_FUNC; reg |= (op << AR40XX_MIB_FUNC_S); AR40XX_REG_WRITE(sc, AR40XX_REG_MIB_FUNC, reg); AR40XX_REG_BARRIER_WRITE(sc); /* Now wait */ ret = ar40xx_hw_wait_bit(sc, AR40XX_REG_MIB_FUNC, AR40XX_MIB_BUSY, 0); if (ret != 0) { device_printf(sc->sc_dev, "%s: ERROR: timeout waiting for MIB load\n", __func__); } return ret; } int ar40xx_hw_mib_capture(struct ar40xx_softc *sc) { int ret; ret = ar40xx_hw_mib_op(sc, AR40XX_MIB_FUNC_CAPTURE); return (ret); } int ar40xx_hw_mib_flush(struct ar40xx_softc *sc) { int ret; ret = ar40xx_hw_mib_op(sc, AR40XX_MIB_FUNC_FLUSH); return (ret); } int ar40xx_hw_mib_fetch(struct ar40xx_softc *sc, int port) { uint64_t val; uint32_t base, reg; int i; base = AR40XX_REG_PORT_STATS_START + (AR40XX_REG_PORT_STATS_LEN * port); /* For now just print them out, we'll store them later */ AR40XX_REG_BARRIER_READ(sc); for (i = 0; i < nitems(ar40xx_mibs); i++) { val = 0; val = AR40XX_REG_READ(sc, base + ar40xx_mibs[i].offset); if (ar40xx_mibs[i].size == 2) { reg = AR40XX_REG_READ(sc, base + ar40xx_mibs[i].offset + 4); val |= ((uint64_t) reg << 32); } device_printf(sc->sc_dev, "%s[%d] = %llu\n", ar40xx_mibs[i].name, port, val); } return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c index 7e440f54e3ac..fff97147d878 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_mirror.c @@ -1,132 +1,132 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" int ar40xx_hw_mirror_set_registers(struct ar40xx_softc *sc) { uint32_t reg; int port; /* Reset the mirror registers before configuring */ reg = AR40XX_REG_READ(sc, AR40XX_REG_FWD_CTRL0); reg &= ~(AR40XX_FWD_CTRL0_MIRROR_PORT); reg |= (0xF << AR40XX_FWD_CTRL0_MIRROR_PORT_S); AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); AR40XX_REG_BARRIER_WRITE(sc); for (port = 0; port < AR40XX_NUM_PORTS; port++) { reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(port)); reg &= ~AR40XX_PORT_LOOKUP_ING_MIRROR_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(port)); reg &= ~AR40XX_PORT_HOL_CTRL1_EG_MIRROR_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HOL_CTRL1(port), reg); AR40XX_REG_BARRIER_WRITE(sc); } /* Now, enable mirroring if requested */ if (sc->sc_monitor.source_port >= AR40XX_NUM_PORTS || sc->sc_monitor.monitor_port >= AR40XX_NUM_PORTS || sc->sc_monitor.source_port == sc->sc_monitor.monitor_port) { return (0); } reg = AR40XX_REG_READ(sc, AR40XX_REG_FWD_CTRL0); reg &= ~AR40XX_FWD_CTRL0_MIRROR_PORT; reg |= (sc->sc_monitor.monitor_port << AR40XX_FWD_CTRL0_MIRROR_PORT_S); AR40XX_REG_WRITE(sc, AR40XX_REG_FWD_CTRL0, reg); if (sc->sc_monitor.mirror_rx) { reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(sc->sc_monitor.source_port)); reg |= AR40XX_PORT_LOOKUP_ING_MIRROR_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(sc->sc_monitor.source_port), reg); AR40XX_REG_BARRIER_WRITE(sc); } if (sc->sc_monitor.mirror_tx) { reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(sc->sc_monitor.source_port)); reg |= AR40XX_PORT_HOL_CTRL1_EG_MIRROR_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HOL_CTRL1(sc->sc_monitor.source_port), reg); AR40XX_REG_BARRIER_WRITE(sc); } return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c index e701f3ae28ba..a540f9b7498e 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_port.c @@ -1,287 +1,287 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" int ar40xx_hw_port_init(struct ar40xx_softc *sc, int port) { uint32_t reg; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called; port %d\n", __func__, port); AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_HEADER(port), 0); AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), 0); AR40XX_REG_BARRIER_WRITE(sc); DELAY(20); /* * Ok! Here is where things get super fun in the AR40xx * driver in uboot/linux. * * The earlier chipset switch drivers enable auto link enable here. * The switch will poll the PHYs too, and configure appropriately. * * The ar40xx code in linux/u-boot instead has a whole workaround * path that polls things directly and does some weird hijinx. * NOTABLY - they do NOT enable the TX/RX MAC here or autoneg - * it's done in the work around path. * * SO - for now the port is left off until the PHY state changes. * And then we flip it on and off based on the PHY state. */ #if 0 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), AR40XX_PORT_AUTO_LINK_EN); #endif /* * Configure the VLAN egress mode (don't touch them) and * learning state for STP/ATU. This isn't currently * configurable so it's just nailed up here and left alone. */ reg = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH << AR40XX_PORT_VLAN1_OUT_MODE_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); reg = AR40XX_PORT_LOOKUP_LEARN; reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Call when the link for a non-CPU port is down. * * This will turn off the MAC/forwarding path for this port. */ int ar40xx_hw_port_link_down(struct ar40xx_softc *sc, int port) { AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called; port %d\n", __func__, port); AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), 0); return (0); } /* * Call when the link for a non-CPU port is up. * * This will turn on the default auto-link checking and * eventually enable the TX/RX MAC. */ int ar40xx_hw_port_link_up(struct ar40xx_softc *sc, int port) { uint32_t reg; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called; port %d\n", __func__, port); /* Auto-link enable */ AR40XX_REG_BARRIER_READ(sc); reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(port)); reg |= AR40XX_PORT_AUTO_LINK_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(port), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Setup the CPU facing port. For this device it'll only * be port 0. */ int ar40xx_hw_port_cpuport_setup(struct ar40xx_softc *sc) { uint32_t reg; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_PORT_INIT, "%s: called\n", __func__); reg = AR40XX_PORT_STATUS_TXFLOW | AR40XX_PORT_STATUS_RXFLOW | AR40XX_PORT_TXHALF_FLOW | AR40XX_PORT_DUPLEX | AR40XX_PORT_SPEED_1000M; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); DELAY(20); reg |= AR40XX_PORT_TX_EN | AR40XX_PORT_RX_EN; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_STATUS(0), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Fetch the port PVID. * * For 802.1q mode this is the default VLAN ID for the port. * Frames without an 802.1q VLAN will assume this VLAN ID for * transmit/receive. */ int ar40xx_hw_get_port_pvid(struct ar40xx_softc *sc, int port, int *pvid) { uint32_t reg; AR40XX_LOCK_ASSERT(sc); AR40XX_REG_BARRIER_READ(sc); reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(port)); reg = reg >> AR40XX_PORT_VLAN0_DEF_CVID_S; reg = reg & 0x0fff; /* XXX */ *pvid = reg; return (0); } /* * Set the port PVID. * * For now, since double-tagged frames aren't currently supported, * CVID=SVID here. */ int ar40xx_hw_set_port_pvid(struct ar40xx_softc *sc, int port, int pvid) { uint32_t reg; AR40XX_LOCK_ASSERT(sc); pvid &= ETHERSWITCH_VID_MASK; reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Setup the default port membership configuration. * * This configures the PVID for the port in the sc_vlan config, * along with a set of ports that constitute the "membership" * of this particular VID. * * For 802.1q mode the membership can be viewed as the default * learning port group, but this can be added to via VLAN membership. * (Eg you could in theory split two LAN ports into separate "member" * groups and they'd not learn MAC addresses from each other even * inside a VLAN; you'd then end up with the traffic being flooded to * the CPU port.) */ int ar40xx_hw_port_setup(struct ar40xx_softc *sc, int port, uint32_t members) { uint32_t egress, ingress, reg; uint32_t pvid = sc->sc_vlan.vlan_id[sc->sc_vlan.pvid[port]] & ETHERSWITCH_VID_MASK; if (sc->sc_vlan.vlan) { egress = AR40XX_PORT_VLAN1_OUT_MODE_UNMOD; ingress = AR40XX_IN_SECURE; } else { egress = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH; ingress = AR40XX_IN_PORT_ONLY; } reg = pvid << AR40XX_PORT_VLAN0_DEF_SVID_S; reg |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN0(port), reg); AR40XX_REG_BARRIER_WRITE(sc); reg = AR40XX_PORT_VLAN1_PORT_VLAN_PROP; reg |= egress << AR40XX_PORT_VLAN1_OUT_MODE_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_VLAN1(port), reg); AR40XX_REG_BARRIER_WRITE(sc); reg = members; reg |= AR40XX_PORT_LOOKUP_LEARN; reg |= ingress << AR40XX_PORT_LOOKUP_IN_MODE_S; reg |= AR40XX_PORT_STATE_FORWARD << AR40XX_PORT_LOOKUP_STATE_S; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(port), reg); AR40XX_REG_BARRIER_WRITE(sc); return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c index 0f0704c41aba..67a2bcbc7a6c 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c @@ -1,437 +1,437 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" /* * Routines that control the ess-psgmii block - the interconnect * between the ess-switch and the external multi-port PHY * (eg Maple.) */ static void ar40xx_hw_psgmii_reg_write(struct ar40xx_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, reg, val); bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, 0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_WRITE); } static int ar40xx_hw_psgmii_reg_read(struct ar40xx_softc *sc, uint32_t reg) { int ret; bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, 0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_READ); ret = bus_space_read_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle, reg); return (ret); } int ar40xx_hw_psgmii_set_mac_mode(struct ar40xx_softc *sc, uint32_t mac_mode) { if (mac_mode == PORT_WRAPPER_PSGMII) { ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMII_MODE_CONTROL, 0x2200); ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMIIPHY_TX_CONTROL, 0x8380); } else { device_printf(sc->sc_dev, "WARNING: unknown MAC_MODE=%u\n", mac_mode); } return (0); } int ar40xx_hw_psgmii_single_phy_testing(struct ar40xx_softc *sc, int phy) { int j; uint32_t tx_ok, tx_error; uint32_t rx_ok, rx_error; uint32_t tx_ok_high16; uint32_t rx_ok_high16; uint32_t tx_all_ok, rx_all_ok; MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x9000); MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x4140); for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) { uint16_t status; status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11); if (status & AR40XX_PHY_SPEC_STATUS_LINK) break; /* * the polling interval to check if the PHY link up * or not * maxwait_timer: 750 ms +/-10 ms * minwait_timer : 1 us +/- 0.1us * time resides in minwait_timer ~ maxwait_timer * see IEEE 802.3 section 40.4.5.2 */ DELAY(8 * 1000); } /* enable check */ ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0000); ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0003); /* start traffic */ ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8020, 0xa000); /* *wait for all traffic end * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms */ DELAY(60 * 1000); /* check counter */ tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e); tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d); tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f); rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b); rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a); rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c); tx_all_ok = tx_ok + (tx_ok_high16 << 16); rx_all_ok = rx_ok + (rx_ok_high16 << 16); if (tx_all_ok == 0x1000 && tx_error == 0) { /* success */ sc->sc_psgmii.phy_t_status &= ~(1U << phy); } else { device_printf(sc->sc_dev, "TX_OK=%d, tx_error=%d RX_OK=%d" " rx_error=%d\n", tx_all_ok, tx_error, rx_all_ok, rx_error); device_printf(sc->sc_dev, "PHY %d single test PSGMII issue happen!\n", phy); sc->sc_psgmii.phy_t_status |= BIT(phy); } MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x1840); return (0); } int ar40xx_hw_psgmii_all_phy_testing(struct ar40xx_softc *sc) { int phy, j; MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9000); MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x4140); for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) { for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { uint16_t status; status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11); if (!(status & (1U << 10))) break; } if (phy >= (AR40XX_NUM_PORTS - 1)) break; /* The polling interval to check if the PHY link up or not */ DELAY(8*1000); } /* enable check */ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0000); ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0003); /* start traffic */ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0xa000); /* * wait for all traffic end * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms */ DELAY(60*1000); /* was 50ms */ for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { uint32_t tx_ok, tx_error; uint32_t rx_ok, rx_error; uint32_t tx_ok_high16; uint32_t rx_ok_high16; uint32_t tx_all_ok, rx_all_ok; /* check counter */ tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e); tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d); tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f); rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b); rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a); rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c); tx_all_ok = tx_ok + (tx_ok_high16<<16); rx_all_ok = rx_ok + (rx_ok_high16<<16); if (tx_all_ok == 0x1000 && tx_error == 0) { /* success */ sc->sc_psgmii.phy_t_status &= ~(1U << (phy + 8)); } else { device_printf(sc->sc_dev, "PHY%d test see issue! (tx_all_ok=%u," " rx_all_ok=%u, tx_error=%u, rx_error=%u)\n", phy, tx_all_ok, rx_all_ok, tx_error, rx_error); sc->sc_psgmii.phy_t_status |= (1U << (phy + 8)); } } device_printf(sc->sc_dev, "PHY all test 0x%x\n", sc->sc_psgmii.phy_t_status); return (0); } /* * Reset PSGMII in the Malibu PHY. */ int ar40xx_hw_malibu_psgmii_ess_reset(struct ar40xx_softc *sc) { device_printf(sc->sc_dev, "%s: called\n", __func__); uint32_t i; /* reset phy psgmii */ /* fix phy psgmii RX 20bit */ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b); /* reset phy psgmii */ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x001b); /* release reset phy psgmii */ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b); for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { uint32_t status; status = ar40xx_hw_phy_mmd_read(sc, 5, 1, 0x28); if (status & (1U << 0)) break; /* * Polling interval to check PSGMII PLL in malibu is ready * the worst time is 8.67ms * for 25MHz reference clock * [512+(128+2048)*49]*80ns+100us */ DELAY(2000); } /* XXX TODO ;see if it timed out? */ /*check malibu psgmii calibration done end..*/ /*freeze phy psgmii RX CDR*/ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x2230); ar40xx_hw_ess_reset(sc); /*check psgmii calibration done start*/ for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { uint32_t status; status = ar40xx_hw_psgmii_reg_read(sc, 0xa0); if (status & (1U << 0)) break; /* Polling interval to check PSGMII PLL in ESS is ready */ DELAY(2000); } /* XXX TODO ;see if it timed out? */ /* check dakota psgmii calibration done end..*/ /* release phy psgmii RX CDR */ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x3230); /* release phy psgmii RX 20bit */ MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005f); return (0); } int ar40xx_hw_psgmii_self_test(struct ar40xx_softc *sc) { uint32_t i, phy, reg; device_printf(sc->sc_dev, "%s: called\n", __func__); ar40xx_hw_malibu_psgmii_ess_reset(sc); /* switch to access MII reg for copper */ MDIO_WRITEREG(sc->sc_mdio_dev, 4, 0x1f, 0x8500); for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { /*enable phy mdio broadcast write*/ ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x801f); } /* force no link by power down */ MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x1840); /* packet number*/ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x1000); ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8062, 0x05e0); /* fix mdi status */ MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6800); for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) { sc->sc_psgmii.phy_t_status = 0; for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { /* Enable port loopback for testing */ AR40XX_REG_BARRIER_READ(sc); reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(phy + 1)); reg |= AR40XX_PORT_LOOKUP_LOOPBACK; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(phy + 1), reg); AR40XX_REG_BARRIER_WRITE(sc); } for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) ar40xx_hw_psgmii_single_phy_testing(sc, phy); ar40xx_hw_psgmii_all_phy_testing(sc); if (sc->sc_psgmii.phy_t_status) ar40xx_hw_malibu_psgmii_ess_reset(sc); else break; } if (i >= AR40XX_PSGMII_CALB_NUM) device_printf(sc->sc_dev, "PSGMII cannot recover\n"); else device_printf(sc->sc_dev, "PSGMII recovered after %d times reset\n", i); /* configuration recover */ /* packet number */ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x0); /* disable check */ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0); /* disable traffic */ ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0x0); return (0); } int ar40xx_hw_psgmii_self_test_clean(struct ar40xx_softc *sc) { uint32_t reg; int phy; device_printf(sc->sc_dev, "%s: called\n", __func__); /* disable phy internal loopback */ MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6860); MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9040); for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) { /* disable mac loop back */ reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(phy + 1)); reg &= ~AR40XX_PORT_LOOKUP_LOOPBACK; AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(phy + 1), reg); AR40XX_REG_BARRIER_WRITE(sc); /* disable phy mdio broadcast write */ ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x001f); } /* clear fdb entry */ ar40xx_hw_atu_flush_all(sc); return (0); } int ar40xx_hw_psgmii_init_config(struct ar40xx_softc *sc) { uint32_t reg; /* * This is based on what I found in uboot - it configures * the initial ESS interconnect to either be PSGMII * or RGMII. */ /* For now, just assume PSGMII and fix it in post. */ /* PSGMIIPHY_PLL_VCO_RELATED_CTRL */ reg = ar40xx_hw_psgmii_reg_read(sc, 0x78c); device_printf(sc->sc_dev, "%s: PSGMIIPHY_PLL_VCO_RELATED_CTRL=0x%08x\n", __func__, reg); /* PSGMIIPHY_VCO_CALIBRATION_CTRL */ reg = ar40xx_hw_psgmii_reg_read(sc, 0x09c); device_printf(sc->sc_dev, "%s: PSGMIIPHY_VCO_CALIBRATION_CTRL=0x%08x\n", __func__, reg); return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c b/sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c index 3c3800847438..15f5f61f8b2d 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_hw_vtu.c @@ -1,196 +1,196 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" /* * Perform a VTU (vlan table unit) operation. */ int ar40xx_hw_vtu_op(struct ar40xx_softc *sc, uint32_t op, uint32_t val) { int ret; AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, "%s: called; op=0x%08x, val=0x%08x\n", __func__, op, val); ret = (ar40xx_hw_wait_bit(sc, AR40XX_REG_VTU_FUNC1, AR40XX_VTU_FUNC1_BUSY, 0)); if (ret != 0) return (ret); if ((op & AR40XX_VTU_FUNC1_OP) == AR40XX_VTU_FUNC1_OP_LOAD) { AR40XX_REG_WRITE(sc, AR40XX_REG_VTU_FUNC0, val); AR40XX_REG_BARRIER_WRITE(sc); } op |= AR40XX_VTU_FUNC1_BUSY; AR40XX_REG_WRITE(sc, AR40XX_REG_VTU_FUNC1, op); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Load in a VLAN table map / port configuration for the given * vlan ID. */ int ar40xx_hw_vtu_load_vlan(struct ar40xx_softc *sc, uint32_t vid, uint32_t port_mask, uint32_t untagged_mask) { uint32_t op, val, mode; int i, ret; AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, "%s: called; vid=%d port_mask=0x%08x, untagged_mask=0x%08x\n", __func__, vid, port_mask, untagged_mask); op = AR40XX_VTU_FUNC1_OP_LOAD | (vid << AR40XX_VTU_FUNC1_VID_S); val = AR40XX_VTU_FUNC0_VALID | AR40XX_VTU_FUNC0_IVL; for (i = 0; i < AR40XX_NUM_PORTS; i++) { if ((port_mask & (1U << i)) == 0) /* Not in the VLAN at all */ mode = AR40XX_VTU_FUNC0_EG_MODE_NOT; else if (sc->sc_vlan.vlan == 0) /* VLAN mode disabled; keep the provided VLAN tag */ mode = AR40XX_VTU_FUNC0_EG_MODE_KEEP; else if (untagged_mask & (1U << i)) /* Port in the VLAN; is untagged */ mode = AR40XX_VTU_FUNC0_EG_MODE_UNTAG; else /* Port is in the VLAN; is tagged */ mode = AR40XX_VTU_FUNC0_EG_MODE_TAG; val |= mode << AR40XX_VTU_FUNC0_EG_MODE_S(i); } ret = ar40xx_hw_vtu_op(sc, op, val); return (ret); } /* * Flush all VLAN port entries. */ int ar40xx_hw_vtu_flush(struct ar40xx_softc *sc) { int ret; AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, "%s: called\n", __func__); ret = ar40xx_hw_vtu_op(sc, AR40XX_VTU_FUNC1_OP_FLUSH, 0); return (ret); } /* * Get the VLAN port map for the given vlan ID. */ int ar40xx_hw_vtu_get_vlan(struct ar40xx_softc *sc, int vid, uint32_t *ports, uint32_t *untagged_ports) { uint32_t op, reg, val; int i, r; op = AR40XX_VTU_FUNC1_OP_GET_ONE; /* Filter out any etherswitch VID flags; only grab the VLAN ID */ vid &= ETHERSWITCH_VID_MASK; /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */ op |= (vid << AR40XX_VTU_FUNC1_VID_S); r = ar40xx_hw_vtu_op(sc, op, 0); if (r != 0) { device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid); return (r); } AR40XX_REG_BARRIER_READ(sc); reg = AR40XX_REG_READ(sc, AR40XX_REG_VTU_FUNC0); *ports = 0; for (i = 0; i < AR40XX_NUM_PORTS; i++) { val = reg >> AR40XX_VTU_FUNC0_EG_MODE_S(i); val = val & 0x3; /* XXX KEEP (unmodified? For non-dot1q operation?) */ if (val == AR40XX_VTU_FUNC0_EG_MODE_TAG) { *ports |= (1 << i); } else if (val == AR40XX_VTU_FUNC0_EG_MODE_UNTAG) { *ports |= (1 << i); *untagged_ports |= (1 << i); } } return (0); } diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_main.c b/sys/dev/etherswitch/ar40xx/ar40xx_main.c index d8cbfd836a9b..757dbe23071e 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_main.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_main.c @@ -1,967 +1,967 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" static struct ofw_compat_data compat_data[] = { { "qcom,ess-switch", 1 }, { NULL, 0 }, }; static int ar40xx_probe(device_t dev) { if (! ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "IPQ4018 ESS Switch fabric / PSGMII PHY"); return (BUS_PROBE_DEFAULT); } static void ar40xx_tick(void *arg) { struct ar40xx_softc *sc = arg; (void) ar40xx_phy_tick(sc); callout_reset(&sc->sc_phy_callout, hz, ar40xx_tick, sc); } static void ar40xx_statchg(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s\n", __func__); } static int ar40xx_readphy(device_t dev, int phy, int reg) { struct ar40xx_softc *sc = device_get_softc(dev); return MDIO_READREG(sc->sc_mdio_dev, phy, reg); } static int ar40xx_writephy(device_t dev, int phy, int reg, int val) { struct ar40xx_softc *sc = device_get_softc(dev); return MDIO_WRITEREG(sc->sc_mdio_dev, phy, reg, val); } /* * Do the initial switch configuration. */ static int ar40xx_reset_switch(struct ar40xx_softc *sc) { int ret, i; AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); /* blank the VLAN config */ memset(&sc->sc_vlan, 0, sizeof(sc->sc_vlan)); /* initial vlan port mapping */ for (i = 0; i < AR40XX_NUM_VTU_ENTRIES; i++) sc->sc_vlan.vlan_id[i] = 0; /* init vlan config */ ret = ar40xx_hw_vlan_init(sc); /* init monitor config */ sc->sc_monitor.mirror_tx = false; sc->sc_monitor.mirror_rx = false; sc->sc_monitor.source_port = 0; sc->sc_monitor.monitor_port = 0; /* apply switch config */ ret = ar40xx_hw_sw_hw_apply(sc); return (ret); } static int ar40xx_sysctl_dump_port_state(SYSCTL_HANDLER_ARGS) { struct ar40xx_softc *sc = arg1; int val = 0; int error; int i; (void) i; (void) sc; error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return (error); if (val < 0 || val > 5) { return (EINVAL); } AR40XX_LOCK(sc); device_printf(sc->sc_dev, "port %d: PORT_STATUS=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(val))); device_printf(sc->sc_dev, "port %d: PORT_HEADER=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_HEADER(val))); device_printf(sc->sc_dev, "port %d: PORT_VLAN0=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(val))); device_printf(sc->sc_dev, "port %d: PORT_VLAN1=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN1(val))); device_printf(sc->sc_dev, "port %d: PORT_LOOKUP=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(val))); device_printf(sc->sc_dev, "port %d: PORT_HOL_CTRL1=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(val))); device_printf(sc->sc_dev, "port %d: PORT_FLOWCTRL_THRESH=0x%08x\n", val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(val))); AR40XX_UNLOCK(sc); return (0); } static int ar40xx_sysctl_dump_port_mibstats(SYSCTL_HANDLER_ARGS) { struct ar40xx_softc *sc = arg1; int val = 0; int error; int i; (void) i; (void) sc; error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return (error); if (val < 0 || val > 5) { return (EINVAL); } AR40XX_LOCK(sc); /* Yes, this snapshots all ports */ (void) ar40xx_hw_mib_capture(sc); (void) ar40xx_hw_mib_fetch(sc, val); AR40XX_UNLOCK(sc); return (0); } static int ar40xx_sysctl_attach(struct ar40xx_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debugging flags"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "port_state", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ar40xx_sysctl_dump_port_state, "I", ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "port_mibstats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ar40xx_sysctl_dump_port_mibstats, "I", ""); return (0); } static int ar40xx_detach(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); int i; device_printf(sc->sc_dev, "%s: called\n", __func__); callout_drain(&sc->sc_phy_callout); /* Free PHYs */ for (i = 0; i < AR40XX_NUM_PHYS; i++) { if (sc->sc_phys.miibus[i] != NULL) device_delete_child(dev, sc->sc_phys.miibus[i]); if (sc->sc_phys.ifp[i] != NULL) if_free(sc->sc_phys.ifp[i]); free(sc->sc_phys.ifname[i], M_DEVBUF); } bus_generic_detach(dev); mtx_destroy(&sc->sc_mtx); return (0); } static int ar40xx_attach(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); phandle_t psgmii_p, root_p, mdio_p; int ret, i; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF); psgmii_p = OF_finddevice("/soc/ess-psgmii"); if (psgmii_p == -1) { device_printf(dev, "%s: couldn't find /soc/ess-psgmii DT node\n", __func__); goto error; } /* * Get the ipq4019-mdio node here, to talk to our local PHYs * if needed */ root_p = OF_finddevice("/soc"); mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio"); if (mdio_p == -1) { device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n", __func__); goto error; } sc->sc_mdio_phandle = mdio_p; sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p)); if (sc->sc_mdio_dev == NULL) { device_printf(dev, "%s: couldn't get mdio device (mdio_p=%u)\n", __func__, mdio_p); goto error; } /* get psgmii base address from psgmii node */ ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag, &sc->sc_psgmii_mem_handle, &sc->sc_psgmii_mem_size); if (ret != 0) { device_printf(dev, "%s: couldn't map psgmii mem (%d)\n", __func__, ret); goto error; } /* get switch base address */ sc->sc_ess_mem_rid = 0; sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_ess_mem_rid, RF_ACTIVE); if (sc->sc_ess_mem_res == NULL) { device_printf(dev, "%s: failed to find memory resource\n", __func__); goto error; } sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev, SYS_RES_MEMORY, sc->sc_ess_mem_rid); if (sc->sc_ess_mem_size == 0) { device_printf(dev, "%s: failed to get device memory size\n", __func__); goto error; } ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode", &sc->sc_config.switch_mac_mode, sizeof(sc->sc_config.switch_mac_mode)); if (ret < 0) { device_printf(dev, "%s: missing switch_mac_mode property\n", __func__); goto error; } ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp", &sc->sc_config.switch_cpu_bmp, sizeof(sc->sc_config.switch_cpu_bmp)); if (ret < 0) { device_printf(dev, "%s: missing switch_cpu_bmp property\n", __func__); goto error; } ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp", &sc->sc_config.switch_lan_bmp, sizeof(sc->sc_config.switch_lan_bmp)); if (ret < 0) { device_printf(dev, "%s: missing switch_lan_bmp property\n", __func__); goto error; } ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp", &sc->sc_config.switch_wan_bmp, sizeof(sc->sc_config.switch_wan_bmp)); if (ret < 0) { device_printf(dev, "%s: missing switch_wan_bmp property\n", __func__); goto error; } ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk); if (ret != 0) { device_printf(dev, "%s: failed to find ess_clk (%d)\n", __func__, ret); goto error; } ret = clk_enable(sc->sc_ess_clk); if (ret != 0) { device_printf(dev, "%s: failed to enable clock (%d)\n", __func__, ret); goto error; } ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst); if (ret != 0) { device_printf(dev, "%s: failed to find ess_rst (%d)\n", __func__, ret); goto error; } /* * Ok, at this point we have enough resources to do an initial * reset and configuration. */ AR40XX_LOCK(sc); /* Initial PSGMII/RGMII port configuration */ ret = ar40xx_hw_psgmii_init_config(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to init PSGMII (%d)\n", ret); goto error_locked; } /* * ESS reset - this resets both the ethernet switch * AND the ethernet block. */ ret = ar40xx_hw_ess_reset(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to reset ESS block (%d)\n", ret); goto error_locked; } /* * Check the PHY IDs for each of the PHYs from 0..4; * this is useful to make sure that we can SEE the external * PHY(s). */ if (bootverbose) { ret = ar40xx_hw_phy_get_ids(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to check PHY IDs (%d)\n", ret); goto error_locked; } } /* * Do PSGMII PHY self-test; work-around issues. */ ret = ar40xx_hw_psgmii_self_test(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to do PSGMII self-test (%d)\n", ret); goto error_locked; } /* Return port config to runtime state */ ret = ar40xx_hw_psgmii_self_test_clean(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to do PSGMII runtime config (%d)\n", ret); goto error_locked; } /* mac_mode_init */ ret = ar40xx_hw_psgmii_set_mac_mode(sc, sc->sc_config.switch_mac_mode); /* Initialise each hardware port */ for (i = 0; i < AR40XX_NUM_PORTS; i++) { ret = ar40xx_hw_port_init(sc, i); } /* initialise the global switch configuration */ ret = ar40xx_hw_init_globals(sc); /* reset the switch vlan/port learning config */ ret = ar40xx_reset_switch(sc); /* cpuport setup */ ret = ar40xx_hw_port_cpuport_setup(sc); AR40XX_UNLOCK(sc); #if 0 /* We may end up needing the QM workaround code here.. */ device_printf(dev, "%s: TODO: QM error check\n", __func__); #endif /* Attach PHYs */ ret = ar40xx_attach_phys(sc); ret = bus_generic_probe(dev); bus_enumerate_hinted_children(dev); ret = bus_generic_attach(dev); /* Start timer */ callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0); /* * Setup the etherswitch info block. */ strlcpy(sc->sc_info.es_name, device_get_desc(dev), sizeof(sc->sc_info.es_name)); sc->sc_info.es_nports = AR40XX_NUM_PORTS; sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q; /* XXX TODO: double-tag / 802.1ad */ sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES; /* * Fetch the initial port configuration. */ AR40XX_LOCK(sc); ar40xx_tick(sc); AR40XX_UNLOCK(sc); ar40xx_sysctl_attach(sc); return (0); error_locked: AR40XX_UNLOCK(sc); error: ar40xx_detach(dev); return (ENXIO); } static void ar40xx_lock(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); AR40XX_LOCK(sc); } static void ar40xx_unlock(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); AR40XX_LOCK_ASSERT(sc); AR40XX_UNLOCK(sc); } static etherswitch_info_t * ar40xx_getinfo(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); return (&sc->sc_info); } static int ar40xx_readreg(device_t dev, int addr) { struct ar40xx_softc *sc = device_get_softc(dev); if (addr >= sc->sc_ess_mem_size - 1) return (-1); AR40XX_REG_BARRIER_READ(sc); return AR40XX_REG_READ(sc, addr); } static int ar40xx_writereg(device_t dev, int addr, int value) { struct ar40xx_softc *sc = device_get_softc(dev); if (addr >= sc->sc_ess_mem_size - 1) return (-1); AR40XX_REG_WRITE(sc, addr, value); AR40XX_REG_BARRIER_WRITE(sc); return (0); } /* * Get the port configuration and status. */ static int ar40xx_getport(device_t dev, etherswitch_port_t *p) { struct ar40xx_softc *sc = device_get_softc(dev); struct mii_data *mii = NULL; struct ifmediareq *ifmr; int err; if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) return (ENXIO); AR40XX_LOCK(sc); /* Fetch the current VLAN configuration for this port */ /* PVID */ ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid); /* * The VLAN egress aren't appropriate to the ports; * instead it's part of the VLAN group config. */ /* Get MII config */ mii = ar40xx_phy_miiforport(sc, p->es_port); AR40XX_UNLOCK(sc); if (p->es_port == 0) { /* CPU port */ p->es_flags |= ETHERSWITCH_PORT_CPU; ifmr = &p->es_ifmr; ifmr->ifm_count = 0; ifmr->ifm_current = ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX; ifmr->ifm_mask = 0; ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; } else if (mii != NULL) { /* non-CPU port */ err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media, SIOCGIFMEDIA); if (err) return (err); } else { return (ENXIO); } return (0); } /* * Set the port configuration and status. */ static int ar40xx_setport(device_t dev, etherswitch_port_t *p) { struct ar40xx_softc *sc = device_get_softc(dev); struct ifmedia *ifm; struct mii_data *mii; if_t ifp; int ret; if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) return (EINVAL); /* Port flags */ AR40XX_LOCK(sc); ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid); if (ret != 0) { AR40XX_UNLOCK(sc); return (ret); } /* XXX TODO: tag strip/unstrip, double-tag, etc */ AR40XX_UNLOCK(sc); /* Don't change media config on CPU port */ if (p->es_port == 0) return (0); mii = ar40xx_phy_miiforport(sc, p->es_port); if (mii == NULL) return (ENXIO); ifp = ar40xx_phy_ifpforport(sc, p->es_port); ifm = &mii->mii_media; return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); return (0); } /* * Get the current VLAN group (per-port, ISL, dot1q) configuration. * * For now the only supported operating mode is dot1q. */ static int ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) { struct ar40xx_softc *sc = device_get_softc(dev); int vid, ret; if (vg->es_vlangroup > sc->sc_info.es_nvlangroups) return (EINVAL); vg->es_untagged_ports = 0; vg->es_member_ports = 0; vg->es_fid = 0; AR40XX_LOCK(sc); /* Note: only supporting 802.1q VLAN config for now */ if (sc->sc_vlan.vlan != 1) { vg->es_member_ports = 0; vg->es_untagged_ports = 0; AR40XX_UNLOCK(sc); return (-1); } /* Get vlangroup mapping to VLAN id */ vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; if ((vid & ETHERSWITCH_VID_VALID) == 0) { /* Not an active vgroup; bail */ AR40XX_UNLOCK(sc); return (0); } vg->es_vid = vid; ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports, &vg->es_untagged_ports); AR40XX_UNLOCK(sc); if (ret == 0) { vg->es_vid |= ETHERSWITCH_VID_VALID; } return (ret); } /* * Set the current VLAN group (per-port, ISL, dot1q) configuration. * * For now the only supported operating mode is dot1q. */ static int ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) { struct ar40xx_softc *sc = device_get_softc(dev); int err, vid; /* For now we only support 802.1q mode */ if (sc->sc_vlan.vlan == 0) return (EINVAL); AR40XX_LOCK(sc); vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; /* * If we have an 802.1q VID and it's different to the current one, * purge the current VTU entry. */ if ((vid != 0) && ((vid & ETHERSWITCH_VID_VALID) != 0) && ((vid & ETHERSWITCH_VID_MASK) != (vg->es_vid & ETHERSWITCH_VID_MASK))) { AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, "%s: purging VID %d first\n", __func__, vid); err = ar40xx_hw_vtu_flush(sc); if (err != 0) { AR40XX_UNLOCK(sc); return (err); } } /* Update VLAN ID */ vid = vg->es_vid & ETHERSWITCH_VID_MASK; sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid; if (vid == 0) { /* Setting it to 0 disables the group */ AR40XX_UNLOCK(sc); return (0); } /* Add valid bit for this entry */ sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID; /* Update hardware */ err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports, vg->es_untagged_ports); if (err != 0) { AR40XX_UNLOCK(sc); return (err); } /* Update the config for the given entry */ sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports; sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports; AR40XX_UNLOCK(sc); return (0); } /* * Get the current configuration mode. */ static int ar40xx_getconf(device_t dev, etherswitch_conf_t *conf) { struct ar40xx_softc *sc = device_get_softc(dev); int ret; AR40XX_LOCK(sc); /* Only support dot1q VLAN for now */ conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; /* Switch MAC address */ ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr); if (ret == 0) conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; AR40XX_UNLOCK(sc); return (0); } /* * Set the current configuration and do a switch reset. * * For now the only supported operating mode is dot1q, don't * allow it to be set to non-dot1q. */ static int ar40xx_setconf(device_t dev, etherswitch_conf_t *conf) { struct ar40xx_softc *sc = device_get_softc(dev); int ret = 0; if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { /* Only support dot1q VLAN for now */ if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) return (EINVAL); } if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) { AR40XX_LOCK(sc); ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr); AR40XX_UNLOCK(sc); } return (ret); } /* * Flush all ATU entries. */ static int ar40xx_atu_flush_all(device_t dev) { struct ar40xx_softc *sc = device_get_softc(dev); int ret; AR40XX_LOCK(sc); ret = ar40xx_hw_atu_flush_all(sc); AR40XX_UNLOCK(sc); return (ret); } /* * Flush all ATU entries for the given port. */ static int ar40xx_atu_flush_port(device_t dev, int port) { struct ar40xx_softc *sc = device_get_softc(dev); int ret; AR40XX_LOCK(sc); ret = ar40xx_hw_atu_flush_port(sc, port); AR40XX_UNLOCK(sc); return (ret); } /* * Load the ATU table into local storage so it can be iterated * over. */ static int ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) { struct ar40xx_softc *sc = device_get_softc(dev); int err, nitems; memset(&sc->atu.entries, 0, sizeof(sc->atu.entries)); table->es_nitems = 0; nitems = 0; AR40XX_LOCK(sc); sc->atu.count = 0; err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0); if (err != 0) goto done; while (nitems < AR40XX_NUM_ATU_ENTRIES) { err = ar40xx_hw_atu_fetch_entry(sc, &sc->atu.entries[nitems], 1); if (err != 0) goto done; sc->atu.entries[nitems].id = nitems; nitems++; } done: sc->atu.count = nitems; table->es_nitems = nitems; AR40XX_UNLOCK(sc); return (0); } /* * Iterate over the ATU table entries that have been previously * fetched. */ static int ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) { struct ar40xx_softc *sc = device_get_softc(dev); int id, err = 0; id = e->id; AR40XX_LOCK(sc); if (id > sc->atu.count) { err = ENOENT; goto done; } memcpy(e, &sc->atu.entries[id], sizeof(*e)); done: AR40XX_UNLOCK(sc); return (err); } static device_method_t ar40xx_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar40xx_probe), DEVMETHOD(device_attach, ar40xx_attach), DEVMETHOD(device_detach, ar40xx_detach), /* bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), /* MII interface */ DEVMETHOD(miibus_readreg, ar40xx_readphy), DEVMETHOD(miibus_writereg, ar40xx_writephy), DEVMETHOD(miibus_statchg, ar40xx_statchg), /* MDIO interface */ DEVMETHOD(mdio_readreg, ar40xx_readphy), DEVMETHOD(mdio_writereg, ar40xx_writephy), /* etherswitch interface */ DEVMETHOD(etherswitch_lock, ar40xx_lock), DEVMETHOD(etherswitch_unlock, ar40xx_unlock), DEVMETHOD(etherswitch_getinfo, ar40xx_getinfo), DEVMETHOD(etherswitch_readreg, ar40xx_readreg), DEVMETHOD(etherswitch_writereg, ar40xx_writereg), DEVMETHOD(etherswitch_readphyreg, ar40xx_readphy), DEVMETHOD(etherswitch_writephyreg, ar40xx_writephy), DEVMETHOD(etherswitch_getport, ar40xx_getport), DEVMETHOD(etherswitch_setport, ar40xx_setport), DEVMETHOD(etherswitch_getvgroup, ar40xx_getvgroup), DEVMETHOD(etherswitch_setvgroup, ar40xx_setvgroup), DEVMETHOD(etherswitch_getconf, ar40xx_getconf), DEVMETHOD(etherswitch_setconf, ar40xx_setconf), DEVMETHOD(etherswitch_flush_all, ar40xx_atu_flush_all), DEVMETHOD(etherswitch_flush_port, ar40xx_atu_flush_port), DEVMETHOD(etherswitch_fetch_table, ar40xx_atu_fetch_table), DEVMETHOD(etherswitch_fetch_table_entry, ar40xx_atu_fetch_table_entry), DEVMETHOD_END }; DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods, sizeof(struct ar40xx_softc)); DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, 0, 0); DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, 0, 0); DRIVER_MODULE(miibus, ar40xx, miibus_driver, 0, 0); DRIVER_MODULE(mdio, ar40xx, mdio_driver, 0, 0); DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, 0, 0); MODULE_DEPEND(ar40xx, mdio, 1, 1, 1); MODULE_DEPEND(ar40xx, miibus, 1, 1, 1); MODULE_DEPEND(ar40xx, etherswitch, 1, 1, 1); MODULE_VERSION(ar40xx, 1); diff --git a/sys/dev/etherswitch/ar40xx/ar40xx_phy.c b/sys/dev/etherswitch/ar40xx/ar40xx_phy.c index 079a92983503..b9a308e3620d 100644 --- a/sys/dev/etherswitch/ar40xx/ar40xx_phy.c +++ b/sys/dev/etherswitch/ar40xx/ar40xx_phy.c @@ -1,252 +1,252 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Adrian Chadd . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "mdio_if.h" #include "miibus_if.h" #include "etherswitch_if.h" int ar40xx_phy_tick(struct ar40xx_softc *sc) { struct mii_softc *miisc; struct mii_data *mii; int phy; uint32_t reg; AR40XX_LOCK_ASSERT(sc); AR40XX_REG_BARRIER_READ(sc); /* * Loop over; update phy port status here */ for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { /* * Port here is PHY, not port! */ reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(phy + 1)); mii = device_get_softc(sc->sc_phys.miibus[phy]); /* * Compare the current link status to the previous link * status. We may need to clear ATU / change phy config. */ if (((reg & AR40XX_PORT_STATUS_LINK_UP) != 0) && (mii->mii_media_status & IFM_ACTIVE) == 0) { AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: PHY %d: down -> up\n", __func__, phy); ar40xx_hw_port_link_up(sc, phy + 1); ar40xx_hw_atu_flush_port(sc, phy + 1); } if (((reg & AR40XX_PORT_STATUS_LINK_UP) == 0) && (mii->mii_media_status & IFM_ACTIVE) != 0) { AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: PHY %d: up -> down\n", __func__, phy); ar40xx_hw_port_link_down(sc, phy + 1); ar40xx_hw_atu_flush_port(sc, phy + 1); } mii_tick(mii); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) continue; ukphy_status(miisc); mii_phy_update(miisc, MII_POLLSTAT); } } return (0); } static inline int ar40xx_portforphy(int phy) { return (phy+1); } struct mii_data * ar40xx_phy_miiforport(struct ar40xx_softc *sc, int port) { int phy; phy = port-1; if (phy < 0 || phy >= AR40XX_NUM_PHYS) return (NULL); return (device_get_softc(sc->sc_phys.miibus[phy])); } if_t ar40xx_phy_ifpforport(struct ar40xx_softc *sc, int port) { int phy; phy = port-1; if (phy < 0 || phy >= AR40XX_NUM_PHYS) return (NULL); return (sc->sc_phys.ifp[phy]); } static int ar40xx_ifmedia_upd(if_t ifp) { struct ar40xx_softc *sc = if_getsoftc(ifp); struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp)); AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", __func__, if_getdunit(ifp)); if (mii == NULL) return (ENXIO); mii_mediachg(mii); return (0); } static void ar40xx_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) { struct ar40xx_softc *sc = if_getsoftc(ifp); struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp)); AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", __func__, if_getdunit(ifp)); if (mii == NULL) return; mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } int ar40xx_attach_phys(struct ar40xx_softc *sc) { int phy, err = 0; char name[IFNAMSIZ]; /* PHYs need an interface, so we generate a dummy one */ snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { sc->sc_phys.ifp[phy] = if_alloc(IFT_ETHER); if (sc->sc_phys.ifp[phy] == NULL) { device_printf(sc->sc_dev, "PHY %d: couldn't allocate ifnet structure\n", phy); err = ENOMEM; break; } sc->sc_phys.ifp[phy]->if_softc = sc; sc->sc_phys.ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING | IFF_SIMPLEX; sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1); if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy], ar40xx_portforphy(phy)); err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy], sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd, ar40xx_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); device_printf(sc->sc_dev, "%s attached to pseudo interface %s\n", device_get_nameunit(sc->sc_phys.miibus[phy]), sc->sc_phys.ifp[phy]->if_xname); if (err != 0) { device_printf(sc->sc_dev, "attaching PHY %d failed\n", phy); return (err); } } return (0); } int ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc) { int phy; uint32_t id1, id2; for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2); id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3); device_printf(sc->sc_dev, "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n", __func__, phy, id1, id2); } return (0); } diff --git a/sys/dev/extres/hwreset/hwreset.c b/sys/dev/hwreset/hwreset.c similarity index 99% rename from sys/dev/extres/hwreset/hwreset.c rename to sys/dev/hwreset/hwreset.c index eda8878229d4..82ffa3491a11 100644 --- a/sys/dev/extres/hwreset/hwreset.c +++ b/sys/dev/hwreset/hwreset.c @@ -1,187 +1,187 @@ /*- * Copyright 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 "opt_platform.h" #include #include #include #include #include #ifdef FDT #include #include #endif -#include +#include #include "hwreset_if.h" struct hwreset { device_t consumer_dev; /* consumer device*/ device_t provider_dev; /* provider device*/ int rst_id; /* reset id */ }; MALLOC_DEFINE(M_HWRESET, "hwreset", "Reset framework"); int hwreset_assert(hwreset_t rst) { return (HWRESET_ASSERT(rst->provider_dev, rst->rst_id, true)); } int hwreset_deassert(hwreset_t rst) { return (HWRESET_ASSERT(rst->provider_dev, rst->rst_id, false)); } int hwreset_is_asserted(hwreset_t rst, bool *value) { return (HWRESET_IS_ASSERTED(rst->provider_dev, rst->rst_id, value)); } void hwreset_release(hwreset_t rst) { free(rst, M_HWRESET); } int hwreset_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, hwreset_t *rst_out) { hwreset_t rst; /* Create handle */ rst = malloc(sizeof(struct hwreset), M_HWRESET, M_WAITOK | M_ZERO); rst->consumer_dev = consumer_dev; rst->provider_dev = provider_dev; rst->rst_id = id; *rst_out = rst; return (0); } #ifdef FDT int hwreset_default_ofw_map(device_t provider_dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { if (ncells == 0) *id = 1; else if (ncells == 1) *id = cells[0]; else return (ERANGE); return (0); } int hwreset_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, hwreset_t *rst) { phandle_t xnode; pcell_t *cells; device_t rstdev; int ncells, rv; intptr_t id; if (cnode <= 0) cnode = ofw_bus_get_node(consumer_dev); if (cnode <= 0) { device_printf(consumer_dev, "%s called on not ofw based device\n", __func__); return (ENXIO); } rv = ofw_bus_parse_xref_list_alloc(cnode, "resets", "#reset-cells", idx, &xnode, &ncells, &cells); if (rv != 0) return (rv); /* Tranlate provider to device */ rstdev = OF_device_from_xref(xnode); if (rstdev == NULL) { OF_prop_free(cells); return (ENODEV); } /* Map reset to number */ rv = HWRESET_MAP(rstdev, xnode, ncells, cells, &id); OF_prop_free(cells); if (rv != 0) return (rv); return (hwreset_get_by_id(consumer_dev, rstdev, id, rst)); } int hwreset_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name, hwreset_t *rst) { int rv, idx; if (cnode <= 0) cnode = ofw_bus_get_node(consumer_dev); if (cnode <= 0) { device_printf(consumer_dev, "%s called on not ofw based device\n", __func__); return (ENXIO); } rv = ofw_bus_find_string_index(cnode, "reset-names", name, &idx); if (rv != 0) return (rv); return (hwreset_get_by_ofw_idx(consumer_dev, cnode, idx, rst)); } void hwreset_register_ofw_provider(device_t provider_dev) { phandle_t xref, node; node = ofw_bus_get_node(provider_dev); if (node <= 0) panic("%s called on not ofw based device.\n", __func__); xref = OF_xref_from_node(node); OF_device_register_xref(xref, provider_dev); } void hwreset_unregister_ofw_provider(device_t provider_dev) { phandle_t xref; xref = OF_xref_from_device(provider_dev); OF_device_register_xref(xref, NULL); } #endif diff --git a/sys/dev/extres/hwreset/hwreset.h b/sys/dev/hwreset/hwreset.h similarity index 95% rename from sys/dev/extres/hwreset/hwreset.h rename to sys/dev/hwreset/hwreset.h index ea86d20a69ab..e5cf51ce3ace 100644 --- a/sys/dev/extres/hwreset/hwreset.h +++ b/sys/dev/hwreset/hwreset.h @@ -1,73 +1,73 @@ /*- * Copyright 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 ``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 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. */ -#ifndef DEV_EXTRES_HWRESET_HWRESET_H -#define DEV_EXTRES_HWRESET_HWRESET_H +#ifndef _DEV_HWRESET_HWRESET_H_ +#define _DEV_HWRESET_HWRESET_H_ #include "opt_platform.h" #include #ifdef FDT #include #endif typedef struct hwreset *hwreset_t; typedef struct hwreset_array *hwreset_array_t; /* * Provider interface */ #ifdef FDT void hwreset_register_ofw_provider(device_t provider_dev); void hwreset_unregister_ofw_provider(device_t provider_dev); #endif /* * Consumer interface */ int hwreset_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, hwreset_t *rst); void hwreset_release(hwreset_t rst); int hwreset_assert(hwreset_t rst); int hwreset_deassert(hwreset_t rst); int hwreset_is_asserted(hwreset_t rst, bool *value); #ifdef FDT int hwreset_get_by_ofw_name(device_t consumer_dev, phandle_t node, char *name, hwreset_t *rst); int hwreset_get_by_ofw_idx(device_t consumer_dev, phandle_t node, int idx, hwreset_t *rst); #endif void hwreset_array_release(hwreset_array_t rsts); int hwreset_array_assert(hwreset_array_t rsts); int hwreset_array_deassert(hwreset_array_t rsts); #ifdef FDT int hwreset_array_get_ofw(device_t consumer_dev, phandle_t cnode, hwreset_array_t *rsts); #endif -#endif /* DEV_EXTRES_HWRESET_HWRESET_H */ +#endif /* _DEV_HWRESET_HWRESET_H_ */ diff --git a/sys/dev/extres/hwreset/hwreset_array.c b/sys/dev/hwreset/hwreset_array.c similarity index 98% rename from sys/dev/extres/hwreset/hwreset_array.c rename to sys/dev/hwreset/hwreset_array.c index 40893a1d118f..25b73ad4d74a 100644 --- a/sys/dev/extres/hwreset/hwreset_array.c +++ b/sys/dev/hwreset/hwreset_array.c @@ -1,139 +1,139 @@ /*- * Copyright (c) 2022 The FreeBSD Foundation * * This software was developed by Andrew Turner under sponsorship from * the FreeBSD Foundation. * * 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. */ /* * This manages all hwresets for a device and asserts/deasserts them in * an undefined order. */ #include "opt_platform.h" #include #include #ifdef FDT #include #include #endif -#include +#include MALLOC_DECLARE(M_HWRESET); struct hwreset_array { hwreset_t *rst_array; int count; }; int hwreset_array_assert(hwreset_array_t rsts) { int i, rv; for (i = 0; i < rsts->count; i++) { rv = hwreset_assert(rsts->rst_array[i]); if (rv != 0) return (rv); } return (0); } int hwreset_array_deassert(hwreset_array_t rsts) { int i, rv; for (i = 0; i < rsts->count; i++) { rv = hwreset_deassert(rsts->rst_array[i]); if (rv != 0) return (rv); } return (0); } void hwreset_array_release(hwreset_array_t rsts) { int i; for (i = 0; i < rsts->count; i++) { hwreset_release(rsts->rst_array[i]); } free(rsts->rst_array, M_HWRESET); free(rsts, M_HWRESET); } #ifdef FDT int hwreset_array_get_ofw(device_t consumer_dev, phandle_t cnode, hwreset_array_t *rsts) { hwreset_array_t resets; int count, i, rv; if (cnode <= 0) cnode = ofw_bus_get_node(consumer_dev); if (cnode <= 0) { device_printf(consumer_dev, "%s called on not ofw based device\n", __func__); return (ENXIO); } rv = ofw_bus_parse_xref_list_get_length(cnode, "resets", "#reset-cells", &count); if (rv != 0) return (rv); resets = malloc(sizeof(struct hwreset_array), M_HWRESET, M_WAITOK | M_ZERO); resets->rst_array = mallocarray(count, sizeof(hwreset_t), M_HWRESET, M_WAITOK | M_ZERO); for (i = 0; i < count; i++) { rv = hwreset_get_by_ofw_idx(consumer_dev, cnode, i, &resets->rst_array[i]); if (rv != 0) break; } if (rv != 0) { count = i; for (i = 0; i < count; i++) { hwreset_release(resets->rst_array[i]); } free(resets->rst_array, M_HWRESET); free(resets, M_HWRESET); } else { resets->count = count; *rsts = resets; } return (rv); } #endif diff --git a/sys/dev/extres/hwreset/hwreset_if.m b/sys/dev/hwreset/hwreset_if.m similarity index 100% rename from sys/dev/extres/hwreset/hwreset_if.m rename to sys/dev/hwreset/hwreset_if.m diff --git a/sys/dev/iicbus/controller/twsi/a10_twsi.c b/sys/dev/iicbus/controller/twsi/a10_twsi.c index 40d75f64e3e1..4d370674fee3 100644 --- a/sys/dev/iicbus/controller/twsi/a10_twsi.c +++ b/sys/dev/iicbus/controller/twsi/a10_twsi.c @@ -1,152 +1,152 @@ /*- * Copyright (c) 2016-2019 Emmanuel Vadot * * 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 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 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 #include #include #include #include #include #include -#include +#include #include "iicbus_if.h" #define TWI_ADDR 0x0 #define TWI_XADDR 0x4 #define TWI_DATA 0x8 #define TWI_CNTR 0xC #define TWI_STAT 0x10 #define TWI_CCR 0x14 #define TWI_SRST 0x18 #define TWI_EFR 0x1C #define TWI_LCR 0x20 static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-i2c", 1}, {"allwinner,sun6i-a31-i2c", 1}, {"allwinner,sun8i-a83t-i2c", 1}, {NULL, 0}, }; static int a10_twsi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Integrated I2C Bus Controller"); return (BUS_PROBE_DEFAULT); } static int a10_twsi_attach(device_t dev) { struct twsi_softc *sc; hwreset_t rst; int error; sc = device_get_softc(dev); /* De-assert reset */ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "could not de-assert reset\n"); return (error); } } /* Activate clock */ error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_core); if (error != 0) { device_printf(dev, "could not find clock\n"); return (error); } error = clk_enable(sc->clk_core); if (error != 0) { device_printf(dev, "could not enable clock\n"); return (error); } sc->reg_data = TWI_DATA; sc->reg_slave_addr = TWI_ADDR; sc->reg_slave_ext_addr = TWI_XADDR; sc->reg_control = TWI_CNTR; sc->reg_status = TWI_STAT; sc->reg_baud_rate = TWI_CCR; sc->reg_soft_reset = TWI_SRST; if (ofw_bus_is_compatible(dev, "allwinner,sun6i-a31-i2c") || ofw_bus_is_compatible(dev, "allwinner,sun6i-a83t-i2c")) sc->iflag_w1c = true; return (twsi_attach(dev)); } static phandle_t a10_twsi_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static device_method_t a10_twsi_methods[] = { /* device interface */ DEVMETHOD(device_probe, a10_twsi_probe), DEVMETHOD(device_attach, a10_twsi_attach), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, a10_twsi_get_node), { 0, 0 } }; DEFINE_CLASS_1(iichb, a10_twsi_driver, a10_twsi_methods, sizeof(struct twsi_softc), twsi_driver); EARLY_DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); EARLY_DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); MODULE_DEPEND(a10_twsi, iicbus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/dev/mmc/host/dwmmc_var.h b/sys/dev/mmc/host/dwmmc_var.h index 16893b110804..ebfc738eb8af 100644 --- a/sys/dev/mmc/host/dwmmc_var.h +++ b/sys/dev/mmc/host/dwmmc_var.h @@ -1,103 +1,103 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ #ifndef DEV_MMC_HOST_DWMMC_VAR_H #define DEV_MMC_HOST_DWMMC_VAR_H #include -#include +#include #include #include "opt_mmccam.h" #include enum { HWTYPE_NONE, HWTYPE_ALTERA, HWTYPE_EXYNOS, HWTYPE_HISILICON, HWTYPE_ROCKCHIP, }; struct dwmmc_softc { struct resource *res[2]; device_t dev; void *intr_cookie; struct mmc_host host; struct mmc_helper mmc_helper; struct mtx sc_mtx; #ifdef MMCCAM union ccb * ccb; struct mmc_sim mmc_sim; #else struct mmc_request *req; #endif struct mmc_command *curcmd; uint32_t flags; uint32_t hwtype; uint32_t use_auto_stop; uint32_t use_pio; device_t child; struct task card_task; /* Card presence check task */ struct timeout_task card_delayed_task;/* Card insert delayed task */ int (*update_ios)(struct dwmmc_softc *sc, struct mmc_ios *ios); bus_dma_tag_t desc_tag; bus_dmamap_t desc_map; struct idmac_desc *desc_ring; bus_addr_t desc_ring_paddr; bus_dma_tag_t buf_tag; bus_dmamap_t buf_map; uint32_t bus_busy; uint32_t dto_rcvd; uint32_t acd_rcvd; uint32_t cmd_done; uint64_t bus_hz; uint32_t fifo_depth; uint32_t num_slots; uint32_t sdr_timing; uint32_t ddr_timing; clk_t biu; clk_t ciu; hwreset_t hwreset; regulator_t vmmc; regulator_t vqmmc; }; DECLARE_CLASS(dwmmc_driver); int dwmmc_attach(device_t); int dwmmc_detach(device_t); #endif diff --git a/sys/dev/qcom_dwc3/qcom_dwc3.c b/sys/dev/qcom_dwc3/qcom_dwc3.c index d9f01e714867..71c95096e5cf 100644 --- a/sys/dev/qcom_dwc3/qcom_dwc3.c +++ b/sys/dev/qcom_dwc3/qcom_dwc3.c @@ -1,174 +1,174 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021 Adrian Chadd * * 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. */ /* * Qualcomm DWC3 glue */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include static struct ofw_compat_data compat_data[] = { { "qcom,dwc3", 1}, { NULL, 0 } }; struct qcom_dwc3_softc { struct simplebus_softc sc; device_t dev; clk_t clk_master; clk_t clk_sleep; clk_t clk_mock_utmi; int type; }; static int qcom_dwc3_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* Binding says that we need a child node for the actual dwc3 controller */ node = ofw_bus_get_node(dev); if (OF_child(node) <= 0) return (ENXIO); device_set_desc(dev, "Qualcomm DWC3"); return (BUS_PROBE_DEFAULT); } static int qcom_dwc3_attach(device_t dev) { struct qcom_dwc3_softc *sc; device_t cdev; phandle_t node, child; int err; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Mandatory clocks */ if (clk_get_by_ofw_name(dev, 0, "master", &sc->clk_master) != 0) { device_printf(dev, "Cannot get master clock\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "sleep", &sc->clk_sleep) != 0) { device_printf(dev, "Cannot get sleep clock\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "mock_utmi", &sc->clk_mock_utmi) != 0) { device_printf(dev, "Cannot get mock_utmi clock\n"); return (ENXIO); } /* * TODO: when we support optional reset blocks, take things * out of reset (well, put them into reset, then take out of reset.) */ /* * Now, iterate over the clocks and enable them. */ err = clk_enable(sc->clk_master); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_master)); return (ENXIO); } err = clk_enable(sc->clk_sleep); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_sleep)); return (ENXIO); } err = clk_enable(sc->clk_mock_utmi); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_mock_utmi)); return (ENXIO); } /* * Rest is glue code. */ simplebus_init(dev, node); if (simplebus_fill_ranges(node, &sc->sc) < 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } for (child = OF_child(node); child > 0; child = OF_peer(child)) { cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); if (cdev != NULL) device_probe_and_attach(cdev); } return (bus_generic_attach(dev)); } static device_method_t qcom_dwc3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, qcom_dwc3_probe), DEVMETHOD(device_attach, qcom_dwc3_attach), /* XXX TODO suspend */ /* XXX TODO resume */ DEVMETHOD_END }; DEFINE_CLASS_1(qcom_dwc3, qcom_dwc3_driver, qcom_dwc3_methods, sizeof(struct qcom_dwc3_softc), simplebus_driver); DRIVER_MODULE(qcom_dwc3, simplebus, qcom_dwc3_driver, 0, 0); diff --git a/sys/dev/qcom_gcc/qcom_gcc_ipq4018.c b/sys/dev/qcom_gcc/qcom_gcc_ipq4018.c index c56a5f93b939..5980d8ebe893 100644 --- a/sys/dev/qcom_gcc/qcom_gcc_ipq4018.c +++ b/sys/dev/qcom_gcc/qcom_gcc_ipq4018.c @@ -1,178 +1,178 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, Adrian Chadd * * 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 unmodified, 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 ``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 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. */ /* Driver for Qualcomm IPQ4018 clock and reset device */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "clkdev_if.h" #include "hwreset_if.h" #include #include "qcom_gcc_ipq4018_var.h" static int qcom_gcc_ipq4018_modevent(module_t, int, void *); static int qcom_gcc_ipq4018_probe(device_t); static int qcom_gcc_ipq4018_attach(device_t); static int qcom_gcc_ipq4018_detach(device_t); static int qcom_gcc_ipq4018_modevent(module_t mod, int type, void *unused) { int error; switch (type) { case MOD_LOAD: case MOD_QUIESCE: case MOD_UNLOAD: case MOD_SHUTDOWN: error = 0; break; default: error = EOPNOTSUPP; break; } return (error); } static int qcom_gcc_ipq4018_probe(device_t dev) { if (! ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "qcom,gcc-ipq4019") == 0) return (ENXIO); return (0); } static int qcom_gcc_ipq4018_attach(device_t dev) { struct qcom_gcc_ipq4018_softc *sc; sc = device_get_softc(dev); /* Found a compatible device! */ sc->dev = dev; sc->reg_rid = 0; sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY, &sc->reg_rid, 0x60000, RF_ACTIVE); if (sc->reg == NULL) { device_printf(dev, "Couldn't allocate memory resource!\n"); return (ENXIO); } device_set_desc(dev, "Qualcomm IPQ4018 Clock/Reset Controller"); mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* * Register as a reset provider. */ hwreset_register_ofw_provider(dev); /* * Setup and register as a clock provider. */ qcom_gcc_ipq4018_clock_setup(sc); return (0); } static int qcom_gcc_ipq4018_detach(device_t dev) { struct qcom_gcc_ipq4018_softc *sc; sc = device_get_softc(dev); /* * TBD - deregistering reset/clock resources. */ if (sc->reg != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg); } return (0); } static device_method_t qcom_gcc_ipq4018_methods[] = { /* Device methods. */ DEVMETHOD(device_probe, qcom_gcc_ipq4018_probe), DEVMETHOD(device_attach, qcom_gcc_ipq4018_attach), DEVMETHOD(device_detach, qcom_gcc_ipq4018_detach), /* Reset interface */ DEVMETHOD(hwreset_assert, qcom_gcc_ipq4018_hwreset_assert), DEVMETHOD(hwreset_is_asserted, qcom_gcc_ipq4018_hwreset_is_asserted), /* Clock interface */ DEVMETHOD(clkdev_read_4, qcom_gcc_ipq4018_clock_read), DEVMETHOD(clkdev_write_4, qcom_gcc_ipq4018_clock_write), DEVMETHOD(clkdev_modify_4, qcom_gcc_ipq4018_clock_modify), DEVMETHOD(clkdev_device_lock, qcom_gcc_ipq4018_clock_lock), DEVMETHOD(clkdev_device_unlock, qcom_gcc_ipq4018_clock_unlock), DEVMETHOD_END }; static driver_t qcom_gcc_ipq4018_driver = { "qcom_gcc", qcom_gcc_ipq4018_methods, sizeof(struct qcom_gcc_ipq4018_softc) }; EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, simplebus, qcom_gcc_ipq4018_driver, qcom_gcc_ipq4018_modevent, NULL, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, ofwbus, qcom_gcc_ipq4018_driver, qcom_gcc_ipq4018_modevent, NULL, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); MODULE_VERSION(qcom_gcc_ipq4018, 1); diff --git a/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c b/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c index 2df524d81b5c..ae2236d7fca7 100644 --- a/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c +++ b/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c @@ -1,177 +1,177 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, Adrian Chadd * * 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 unmodified, 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 ``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 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. */ /* Driver for Qualcomm IPQ4018 clock and reset device */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "hwreset_if.h" #include #include "qcom_gcc_ipq4018_var.h" static const struct qcom_gcc_ipq4018_reset_entry gcc_ipq4019_reset_list[] = { [WIFI0_CPU_INIT_RESET] = { 0x1f008, 5 }, [WIFI0_RADIO_SRIF_RESET] = { 0x1f008, 4 }, [WIFI0_RADIO_WARM_RESET] = { 0x1f008, 3 }, [WIFI0_RADIO_COLD_RESET] = { 0x1f008, 2 }, [WIFI0_CORE_WARM_RESET] = { 0x1f008, 1 }, [WIFI0_CORE_COLD_RESET] = { 0x1f008, 0 }, [WIFI1_CPU_INIT_RESET] = { 0x20008, 5 }, [WIFI1_RADIO_SRIF_RESET] = { 0x20008, 4 }, [WIFI1_RADIO_WARM_RESET] = { 0x20008, 3 }, [WIFI1_RADIO_COLD_RESET] = { 0x20008, 2 }, [WIFI1_CORE_WARM_RESET] = { 0x20008, 1 }, [WIFI1_CORE_COLD_RESET] = { 0x20008, 0 }, [USB3_UNIPHY_PHY_ARES] = { 0x1e038, 5 }, [USB3_HSPHY_POR_ARES] = { 0x1e038, 4 }, [USB3_HSPHY_S_ARES] = { 0x1e038, 2 }, [USB2_HSPHY_POR_ARES] = { 0x1e01c, 4 }, [USB2_HSPHY_S_ARES] = { 0x1e01c, 2 }, [PCIE_PHY_AHB_ARES] = { 0x1d010, 11 }, [PCIE_AHB_ARES] = { 0x1d010, 10 }, [PCIE_PWR_ARES] = { 0x1d010, 9 }, [PCIE_PIPE_STICKY_ARES] = { 0x1d010, 8 }, [PCIE_AXI_M_STICKY_ARES] = { 0x1d010, 7 }, [PCIE_PHY_ARES] = { 0x1d010, 6 }, [PCIE_PARF_XPU_ARES] = { 0x1d010, 5 }, [PCIE_AXI_S_XPU_ARES] = { 0x1d010, 4 }, [PCIE_AXI_M_VMIDMT_ARES] = { 0x1d010, 3 }, [PCIE_PIPE_ARES] = { 0x1d010, 2 }, [PCIE_AXI_S_ARES] = { 0x1d010, 1 }, [PCIE_AXI_M_ARES] = { 0x1d010, 0 }, [ESS_RESET] = { 0x12008, 0}, [GCC_BLSP1_BCR] = {0x01000, 0}, [GCC_BLSP1_QUP1_BCR] = {0x02000, 0}, [GCC_BLSP1_UART1_BCR] = {0x02038, 0}, [GCC_BLSP1_QUP2_BCR] = {0x03008, 0}, [GCC_BLSP1_UART2_BCR] = {0x03028, 0}, [GCC_BIMC_BCR] = {0x04000, 0}, [GCC_TLMM_BCR] = {0x05000, 0}, [GCC_IMEM_BCR] = {0x0E000, 0}, [GCC_ESS_BCR] = {0x12008, 0}, [GCC_PRNG_BCR] = {0x13000, 0}, [GCC_BOOT_ROM_BCR] = {0x13008, 0}, [GCC_CRYPTO_BCR] = {0x16000, 0}, [GCC_SDCC1_BCR] = {0x18000, 0}, [GCC_SEC_CTRL_BCR] = {0x1A000, 0}, [GCC_AUDIO_BCR] = {0x1B008, 0}, [GCC_QPIC_BCR] = {0x1C000, 0}, [GCC_PCIE_BCR] = {0x1D000, 0}, [GCC_USB2_BCR] = {0x1E008, 0}, [GCC_USB2_PHY_BCR] = {0x1E018, 0}, [GCC_USB3_BCR] = {0x1E024, 0}, [GCC_USB3_PHY_BCR] = {0x1E034, 0}, [GCC_SYSTEM_NOC_BCR] = {0x21000, 0}, [GCC_PCNOC_BCR] = {0x2102C, 0}, [GCC_DCD_BCR] = {0x21038, 0}, [GCC_SNOC_BUS_TIMEOUT0_BCR] = {0x21064, 0}, [GCC_SNOC_BUS_TIMEOUT1_BCR] = {0x2106C, 0}, [GCC_SNOC_BUS_TIMEOUT2_BCR] = {0x21074, 0}, [GCC_SNOC_BUS_TIMEOUT3_BCR] = {0x2107C, 0}, [GCC_PCNOC_BUS_TIMEOUT0_BCR] = {0x21084, 0}, [GCC_PCNOC_BUS_TIMEOUT1_BCR] = {0x2108C, 0}, [GCC_PCNOC_BUS_TIMEOUT2_BCR] = {0x21094, 0}, [GCC_PCNOC_BUS_TIMEOUT3_BCR] = {0x2109C, 0}, [GCC_PCNOC_BUS_TIMEOUT4_BCR] = {0x210A4, 0}, [GCC_PCNOC_BUS_TIMEOUT5_BCR] = {0x210AC, 0}, [GCC_PCNOC_BUS_TIMEOUT6_BCR] = {0x210B4, 0}, [GCC_PCNOC_BUS_TIMEOUT7_BCR] = {0x210BC, 0}, [GCC_PCNOC_BUS_TIMEOUT8_BCR] = {0x210C4, 0}, [GCC_PCNOC_BUS_TIMEOUT9_BCR] = {0x210CC, 0}, [GCC_TCSR_BCR] = {0x22000, 0}, [GCC_MPM_BCR] = {0x24000, 0}, [GCC_SPDM_BCR] = {0x25000, 0}, }; int qcom_gcc_ipq4018_hwreset_assert(device_t dev, intptr_t id, bool reset) { struct qcom_gcc_ipq4018_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (id > nitems(gcc_ipq4019_reset_list)) { device_printf(dev, "%s: invalid id (%d)\n", __func__, id); return (EINVAL); } mtx_lock(&sc->mtx); reg = bus_read_4(sc->reg, gcc_ipq4019_reset_list[id].reg); if (reset) reg |= (1U << gcc_ipq4019_reset_list[id].bit); else reg &= ~(1U << gcc_ipq4019_reset_list[id].bit); bus_write_4(sc->reg, gcc_ipq4019_reset_list[id].reg, reg); mtx_unlock(&sc->mtx); return (0); } int qcom_gcc_ipq4018_hwreset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct qcom_gcc_ipq4018_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (id > nitems(gcc_ipq4019_reset_list)) { device_printf(dev, "%s: invalid id (%d)\n", __func__, id); return (EINVAL); } mtx_lock(&sc->mtx); reg = bus_read_4(sc->reg, gcc_ipq4019_reset_list[id].reg); if (reg & ((1U << gcc_ipq4019_reset_list[id].bit))) *reset = true; else *reset = false; mtx_unlock(&sc->mtx); device_printf(dev, "called; id=%d\n", id); return (0); } diff --git a/sys/dev/qcom_qup/qcom_spi.c b/sys/dev/qcom_qup/qcom_spi.c index f7163fa1dcab..b7e4d6519a36 100644 --- a/sys/dev/qcom_qup/qcom_spi.c +++ b/sys/dev/qcom_qup/qcom_spi.c @@ -1,905 +1,905 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, Adrian Chadd * * 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 unmodified, 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 #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "spibus_if.h" #include #include #include #include static struct ofw_compat_data compat_data[] = { { "qcom,spi-qup-v1.1.1", QCOM_SPI_HW_QPI_V1_1 }, { "qcom,spi-qup-v2.1.1", QCOM_SPI_HW_QPI_V2_1 }, { "qcom,spi-qup-v2.2.1", QCOM_SPI_HW_QPI_V2_2 }, { NULL, 0 } }; /* * Flip the CS GPIO line either active or inactive. * * Actually listen to the CS polarity. */ static void qcom_spi_set_chipsel(struct qcom_spi_softc *sc, int cs, bool active) { bool pinactive; bool invert = !! (cs & SPIBUS_CS_HIGH); cs = cs & ~SPIBUS_CS_HIGH; if (sc->cs_pins[cs] == NULL) { device_printf(sc->sc_dev, "%s: cs=%u, active=%u, invert=%u, no gpio?\n", __func__, cs, active, invert); return; } QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_CHIPSELECT, "%s: cs=%u active=%u\n", __func__, cs, active); /* * Default rule here is CS is active low. */ if (active) pinactive = false; else pinactive = true; /* * Invert the CS line if required. */ if (invert) pinactive = !! pinactive; gpio_pin_set_active(sc->cs_pins[cs], pinactive); gpio_pin_is_active(sc->cs_pins[cs], &pinactive); } static void qcom_spi_intr(void *arg) { struct qcom_spi_softc *sc = arg; int ret; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_INTR, "%s: called\n", __func__); QCOM_SPI_LOCK(sc); ret = qcom_spi_hw_interrupt_handle(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to read intr status\n"); goto done; } /* * Handle spurious interrupts outside of an actual * transfer. */ if (sc->transfer.active == false) { device_printf(sc->sc_dev, "ERROR: spurious interrupt\n"); qcom_spi_hw_ack_opmode(sc); goto done; } /* Now, handle interrupts */ if (sc->intr.error) { sc->intr.error = false; device_printf(sc->sc_dev, "ERROR: intr\n"); } if (sc->intr.do_rx) { sc->intr.do_rx = false; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_INTR, "%s: PIO_READ\n", __func__); if (sc->state.transfer_mode == QUP_IO_M_MODE_FIFO) ret = qcom_spi_hw_read_pio_fifo(sc); else ret = qcom_spi_hw_read_pio_block(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_read failed (%u)\n", ret); goto done; } } if (sc->intr.do_tx) { sc->intr.do_tx = false; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_INTR, "%s: PIO_WRITE\n", __func__); /* * For FIFO operations we do not do a write here, we did * it at the beginning of the transfer. * * For BLOCK operations yes, we call the routine. */ if (sc->state.transfer_mode == QUP_IO_M_MODE_FIFO) ret = qcom_spi_hw_ack_write_pio_fifo(sc); else ret = qcom_spi_hw_write_pio_block(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_write failed (%u)\n", ret); goto done; } } /* * Do this last. We may actually have completed the * transfer in the PIO receive path above and it will * set the done flag here. */ if (sc->intr.done) { sc->intr.done = false; sc->transfer.done = true; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_INTR, "%s: transfer done\n", __func__); wakeup(sc); } done: QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_INTR, "%s: done\n", __func__); QCOM_SPI_UNLOCK(sc); } static int qcom_spi_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(dev, "Qualcomm SPI Interface"); return (BUS_PROBE_DEFAULT); } /* * Allocate GPIOs if provided in the SPI controller block. * * Some devices will use GPIO lines for chip select. * It's also quite annoying because some devices will want to use * the hardware provided CS gating for say, the first chipselect block, * and then use GPIOs for the later ones. * * So here we just assume for now that SPI index 0 uses the hardware * lines, and >0 use GPIO lines. Revisit this if better hardware * shows up. * * And finally, iterating over the cs-gpios list to allocate GPIOs * doesn't actually tell us what the polarity is. For that we need * to actually iterate over the list of child nodes and check what * their properties are (and look for "spi-cs-high".) */ static void qcom_spi_attach_gpios(struct qcom_spi_softc *sc) { phandle_t node; int idx, err; /* Allocate gpio pins for configured chip selects. */ node = ofw_bus_get_node(sc->sc_dev); for (idx = 0; idx < nitems(sc->cs_pins); idx++) { err = gpio_pin_get_by_ofw_propidx(sc->sc_dev, node, "cs-gpios", idx, &sc->cs_pins[idx]); if (err == 0) { err = gpio_pin_setflags(sc->cs_pins[idx], GPIO_PIN_OUTPUT); if (err != 0) { device_printf(sc->sc_dev, "error configuring gpio for" " cs %u (%d)\n", idx, err); } /* * We can't set this HIGH right now because * we don't know if it needs to be set to * high for inactive or low for inactive * based on the child SPI device flags. */ #if 0 gpio_pin_set_active(sc->cs_pins[idx], 1); gpio_pin_is_active(sc->cs_pins[idx], &tmp); #endif } else { device_printf(sc->sc_dev, "cannot configure gpio for chip select %u\n", idx); sc->cs_pins[idx] = NULL; } } } static void qcom_spi_sysctl_attach(struct qcom_spi_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); } static int qcom_spi_attach(device_t dev) { struct qcom_spi_softc *sc = device_get_softc(dev); int rid, ret, i, val; sc->sc_dev = dev; /* * Hardware version is stored in the ofw_compat_data table. */ sc->hw_version = ofw_bus_search_compatible(dev, compat_data)->ocd_data; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "ERROR: Could not map memory\n"); ret = ENXIO; goto error; } rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { device_printf(dev, "ERROR: Could not map interrupt\n"); ret = ENXIO; goto error; } ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, qcom_spi_intr, sc, &sc->sc_irq_h); if (ret != 0) { device_printf(dev, "ERROR: could not configure interrupt " "(%d)\n", ret); goto error; } qcom_spi_attach_gpios(sc); ret = clk_get_by_ofw_name(dev, 0, "core", &sc->clk_core); if (ret != 0) { device_printf(dev, "ERROR: could not get %s clock (%d)\n", "core", ret); goto error; } ret = clk_get_by_ofw_name(dev, 0, "iface", &sc->clk_iface); if (ret != 0) { device_printf(dev, "ERROR: could not get %s clock (%d)\n", "iface", ret); goto error; } /* Bring up initial clocks if they're off */ ret = clk_enable(sc->clk_core); if (ret != 0) { device_printf(dev, "ERROR: couldn't enable core clock (%u)\n", ret); goto error; } ret = clk_enable(sc->clk_iface); if (ret != 0) { device_printf(dev, "ERROR: couldn't enable iface clock (%u)\n", ret); goto error; } /* * Read optional spi-max-frequency */ if (OF_getencprop(ofw_bus_get_node(dev), "spi-max-frequency", &val, sizeof(val)) > 0) sc->config.max_frequency = val; else sc->config.max_frequency = SPI_MAX_RATE; /* * Read optional cs-select */ if (OF_getencprop(ofw_bus_get_node(dev), "cs-select", &val, sizeof(val)) > 0) sc->config.cs_select = val; else sc->config.cs_select = 0; /* * Read optional num-cs */ if (OF_getencprop(ofw_bus_get_node(dev), "num-cs", &val, sizeof(val)) > 0) sc->config.num_cs = val; else sc->config.num_cs = SPI_NUM_CHIPSELECTS; ret = fdt_pinctrl_configure_by_name(dev, "default"); if (ret != 0) { device_printf(dev, "ERROR: could not configure default pinmux\n"); goto error; } ret = qcom_spi_hw_read_controller_transfer_sizes(sc); if (ret != 0) { device_printf(dev, "ERROR: Could not read transfer config\n"); goto error; } device_printf(dev, "BLOCK: input=%u bytes, output=%u bytes\n", sc->config.input_block_size, sc->config.output_block_size); device_printf(dev, "FIFO: input=%u bytes, output=%u bytes\n", sc->config.input_fifo_size, sc->config.output_fifo_size); /* QUP config */ QCOM_SPI_LOCK(sc); ret = qcom_spi_hw_qup_init_locked(sc); if (ret != 0) { device_printf(dev, "ERROR: QUP init failed (%d)\n", ret); QCOM_SPI_UNLOCK(sc); goto error; } /* Initial SPI config */ ret = qcom_spi_hw_spi_init_locked(sc); if (ret != 0) { device_printf(dev, "ERROR: SPI init failed (%d)\n", ret); QCOM_SPI_UNLOCK(sc); goto error; } QCOM_SPI_UNLOCK(sc); sc->spibus = device_add_child(dev, "spibus", -1); /* We're done, so shut down the interface clock for now */ device_printf(dev, "DONE: shutting down interface clock for now\n"); clk_disable(sc->clk_iface); /* Register for debug sysctl */ qcom_spi_sysctl_attach(sc); return (bus_generic_attach(dev)); error: if (sc->sc_irq_h) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->clk_core) { clk_disable(sc->clk_core); clk_release(sc->clk_core); } if (sc->clk_iface) { clk_disable(sc->clk_iface); clk_release(sc->clk_iface); } for (i = 0; i < CS_MAX; i++) { if (sc->cs_pins[i] != NULL) gpio_pin_release(sc->cs_pins[i]); } mtx_destroy(&sc->sc_mtx); return (ret); } /* * Do a PIO transfer. * * Note that right now the TX/RX lens need to match, I'm not doing * dummy reads / dummy writes as required if they're not the same * size. The QUP hardware supports doing multi-phase transactions * where the FIFO isn't engaged for transmit or receive, but it's * not yet being done here. */ static int qcom_spi_transfer_pio_block(struct qcom_spi_softc *sc, int mode, char *tx_buf, int tx_len, char *rx_buf, int rx_len) { int ret = 0; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_TRANSFER, "%s: start\n", __func__); if (rx_len != tx_len) { device_printf(sc->sc_dev, "ERROR: tx/rx len doesn't match (%d/%d)\n", tx_len, rx_len); return (ENXIO); } QCOM_SPI_ASSERT_LOCKED(sc); /* * Make initial choices for transfer configuration. */ ret = qcom_spi_hw_setup_transfer_selection(sc, tx_len); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to setup transfer selection (%d)\n", ret); return (ret); } /* Now set suitable buffer/lengths */ sc->transfer.tx_buf = tx_buf; sc->transfer.tx_len = tx_len; sc->transfer.rx_buf = rx_buf; sc->transfer.rx_len = rx_len; sc->transfer.done = false; sc->transfer.active = false; /* * Loop until the full transfer set is done. * * qcom_spi_hw_setup_current_transfer() will take care of * setting a maximum transfer size for the hardware and choose * a suitable operating mode. */ while (sc->transfer.tx_offset < sc->transfer.tx_len) { /* * Set transfer to false early; this covers * it also finishing a sub-transfer and we're * about the put the block into RESET state before * starting a new transfer. */ sc->transfer.active = false; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_TRANSFER, "%s: tx=%d of %d bytes, rx=%d of %d bytes\n", __func__, sc->transfer.tx_offset, sc->transfer.tx_len, sc->transfer.rx_offset, sc->transfer.rx_len); /* * Set state to RESET before doing anything. * * Otherwise the second sub-transfer that we queue up * will generate interrupts immediately when we start * configuring it here and it'll start underflowing. */ ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RESET); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: can't transition to RESET (%u)\n", ret); goto done; } /* blank interrupt state; we'll do a RESET below */ bzero(&sc->intr, sizeof(sc->intr)); sc->transfer.done = false; /* * Configure what the transfer configuration for this * sub-transfer will be. */ ret = qcom_spi_hw_setup_current_transfer(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to setup sub transfer (%d)\n", ret); goto done; } /* * For now since we're configuring up PIO, we only setup * the PIO transfer size. */ ret = qcom_spi_hw_setup_pio_transfer_cnt(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_pio_transfer_cnt failed" " (%u)\n", ret); goto done; } #if 0 /* * This is what we'd do to setup the block transfer sizes. */ ret = qcom_spi_hw_setup_block_transfer_cnt(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_block_transfer_cnt failed" " (%u)\n", ret); goto done; } #endif ret = qcom_spi_hw_setup_io_modes(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_io_modes failed" " (%u)\n", ret); goto done; } ret = qcom_spi_hw_setup_spi_io_clock_polarity(sc, !! (mode & SPIBUS_MODE_CPOL)); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_spi_io_clock_polarity" " failed (%u)\n", ret); goto done; } ret = qcom_spi_hw_setup_spi_config(sc, sc->state.frequency, !! (mode & SPIBUS_MODE_CPHA)); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_spi_config failed" " (%u)\n", ret); goto done; } ret = qcom_spi_hw_setup_qup_config(sc, !! (tx_len > 0), !! (rx_len > 0)); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_qup_config failed" " (%u)\n", ret); goto done; } ret = qcom_spi_hw_setup_operational_mask(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_setup_operational_mask failed" " (%u)\n", ret); goto done; } /* * Setup is done; reset the controller and start the PIO * write. */ /* * Set state to RUN; we may start getting interrupts that * are valid and we need to handle. */ sc->transfer.active = true; ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RUN); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: can't transition to RUN (%u)\n", ret); goto done; } /* * Set state to PAUSE */ ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_PAUSE); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: can't transition to PAUSE (%u)\n", ret); goto done; } /* * If FIFO mode, write data now. Else, we'll get an * interrupt when it's time to populate more data * in BLOCK mode. */ if (sc->state.transfer_mode == QUP_IO_M_MODE_FIFO) ret = qcom_spi_hw_write_pio_fifo(sc); else ret = qcom_spi_hw_write_pio_block(sc); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: qcom_spi_hw_write failed (%u)\n", ret); goto done; } /* * Set state to RUN */ ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RUN); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: can't transition to RUN (%u)\n", ret); goto done; } /* * Wait for an interrupt notification (which will * continue to drive the state machine for this * sub-transfer) or timeout. */ ret = 0; while (ret == 0 && sc->transfer.done == false) { QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_TRANSFER, "%s: waiting\n", __func__); ret = msleep(sc, &sc->sc_mtx, 0, "qcom_spi", 0); } } done: /* * Complete; put controller into reset. * * Don't worry about return value here; if we errored out above then * we want to communicate that value to the caller. */ (void) qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RESET); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_TRANSFER, "%s: completed\n", __func__); /* * Blank the transfer state so we don't use an old transfer * state in a subsequent interrupt. */ (void) qcom_spi_hw_complete_transfer(sc); sc->transfer.active = false; return (ret); } static int qcom_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct qcom_spi_softc *sc = device_get_softc(dev); uint32_t cs_val, mode_val, clock_val; uint32_t ret = 0; spibus_get_cs(child, &cs_val); spibus_get_clock(child, &clock_val); spibus_get_mode(child, &mode_val); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_TRANSFER, "%s: called; child cs=0x%08x, clock=%u, mode=0x%08x, " "cmd=%u/%u bytes; data=%u/%u bytes\n", __func__, cs_val, clock_val, mode_val, cmd->tx_cmd_sz, cmd->rx_cmd_sz, cmd->tx_data_sz, cmd->rx_data_sz); QCOM_SPI_LOCK(sc); /* * wait until the controller isn't busy */ while (sc->sc_busy == true) mtx_sleep(sc, &sc->sc_mtx, 0, "qcom_spi_wait", 0); /* * it's ours now! */ sc->sc_busy = true; sc->state.cs_high = !! (cs_val & SPIBUS_CS_HIGH); sc->state.frequency = clock_val; /* * We can't set the clock frequency and enable it * with the driver lock held, as the SPI lock is non-sleepable * and the clock framework is sleepable. * * No other transaction is going on right now, so we can * unlock here and do the clock related work. */ QCOM_SPI_UNLOCK(sc); /* * Set the clock frequency */ ret = clk_set_freq(sc->clk_iface, sc->state.frequency, 0); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to set frequency to %u\n", sc->state.frequency); goto done2; } clk_enable(sc->clk_iface); QCOM_SPI_LOCK(sc); /* * Set state to RESET */ ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RESET); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: can't transition to RESET (%u)\n", ret); goto done; } /* Assert hardware CS if set, else GPIO */ if (sc->cs_pins[cs_val & ~SPIBUS_CS_HIGH] == NULL) qcom_spi_hw_spi_cs_force(sc, cs_val & SPIBUS_CS_HIGH, true); else qcom_spi_set_chipsel(sc, cs_val & ~SPIBUS_CS_HIGH, true); /* * cmd buffer transfer */ ret = qcom_spi_transfer_pio_block(sc, mode_val, cmd->tx_cmd, cmd->tx_cmd_sz, cmd->rx_cmd, cmd->rx_cmd_sz); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to transfer cmd payload (%u)\n", ret); goto done; } /* * data buffer transfer */ if (cmd->tx_data_sz > 0) { ret = qcom_spi_transfer_pio_block(sc, mode_val, cmd->tx_data, cmd->tx_data_sz, cmd->rx_data, cmd->rx_data_sz); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: failed to transfer data payload (%u)\n", ret); goto done; } } done: /* De-assert GPIO/CS */ if (sc->cs_pins[cs_val & ~SPIBUS_CS_HIGH] == NULL) qcom_spi_hw_spi_cs_force(sc, cs_val & ~SPIBUS_CS_HIGH, false); else qcom_spi_set_chipsel(sc, cs_val & ~SPIBUS_CS_HIGH, false); /* * Similarly to when we enabled the clock, we can't hold it here * across a clk API as that's a sleep lock and we're non-sleepable. * So instead we unlock/relock here, but we still hold the busy flag. */ QCOM_SPI_UNLOCK(sc); clk_disable(sc->clk_iface); QCOM_SPI_LOCK(sc); done2: /* * We're done; so mark the bus as not busy and wakeup * the next caller. */ sc->sc_busy = false; wakeup_one(sc); QCOM_SPI_UNLOCK(sc); return (ret); } static int qcom_spi_detach(device_t dev) { struct qcom_spi_softc *sc = device_get_softc(dev); int i; bus_generic_detach(sc->sc_dev); if (sc->spibus != NULL) device_delete_child(dev, sc->spibus); if (sc->sc_irq_h) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); if (sc->clk_iface) { clk_disable(sc->clk_iface); clk_release(sc->clk_iface); } if (sc->clk_core) { clk_disable(sc->clk_core); clk_release(sc->clk_core); } for (i = 0; i < CS_MAX; i++) { if (sc->cs_pins[i] != NULL) gpio_pin_release(sc->cs_pins[i]); } if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); mtx_destroy(&sc->sc_mtx); return (0); } static phandle_t qcom_spi_get_node(device_t bus, device_t dev) { return ofw_bus_get_node(bus); } static device_method_t qcom_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, qcom_spi_probe), DEVMETHOD(device_attach, qcom_spi_attach), DEVMETHOD(device_detach, qcom_spi_detach), /* TODO: suspend */ /* TODO: resume */ DEVMETHOD(spibus_transfer, qcom_spi_transfer), /* ofw_bus_if */ DEVMETHOD(ofw_bus_get_node, qcom_spi_get_node), DEVMETHOD_END }; static driver_t qcom_spi_driver = { "qcom_spi", qcom_spi_methods, sizeof(struct qcom_spi_softc), }; DRIVER_MODULE(qcom_spi, simplebus, qcom_spi_driver, 0, 0); DRIVER_MODULE(ofw_spibus, qcom_spi, ofw_spibus_driver, 0, 0); MODULE_DEPEND(qcom_spi, ofw_spibus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/dev/qcom_qup/qcom_spi_hw.c b/sys/dev/qcom_qup/qcom_spi_hw.c index 6efbedf0892a..910bd8aa3a0e 100644 --- a/sys/dev/qcom_qup/qcom_spi_hw.c +++ b/sys/dev/qcom_qup/qcom_spi_hw.c @@ -1,982 +1,982 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021, Adrian Chadd * * 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 unmodified, 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 #include #include #include #include #include #include #include #include -#include +#include #include #include #include "spibus_if.h" #include #include #include #include int qcom_spi_hw_read_controller_transfer_sizes(struct qcom_spi_softc *sc) { uint32_t reg, val; reg = QCOM_SPI_READ_4(sc, QUP_IO_M_MODES); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: QUP_IO_M_MODES=0x%08x\n", __func__, reg); /* Input block size */ val = (reg >> QUP_IO_M_INPUT_BLOCK_SIZE_SHIFT) & QUP_IO_M_INPUT_BLOCK_SIZE_MASK; if (val == 0) sc->config.input_block_size = 4; else sc->config.input_block_size = val * 16; /* Output block size */ val = (reg >> QUP_IO_M_OUTPUT_BLOCK_SIZE_SHIFT) & QUP_IO_M_OUTPUT_BLOCK_SIZE_MASK; if (val == 0) sc->config.output_block_size = 4; else sc->config.output_block_size = val * 16; /* Input FIFO size */ val = (reg >> QUP_IO_M_INPUT_FIFO_SIZE_SHIFT) & QUP_IO_M_INPUT_FIFO_SIZE_MASK; sc->config.input_fifo_size = sc->config.input_block_size * (2 << val); /* Output FIFO size */ val = (reg >> QUP_IO_M_OUTPUT_FIFO_SIZE_SHIFT) & QUP_IO_M_OUTPUT_FIFO_SIZE_MASK; sc->config.output_fifo_size = sc->config.output_block_size * (2 << val); return (0); } static bool qcom_spi_hw_qup_is_state_valid_locked(struct qcom_spi_softc *sc) { uint32_t reg; QCOM_SPI_ASSERT_LOCKED(sc); reg = QCOM_SPI_READ_4(sc, QUP_STATE); QCOM_SPI_BARRIER_READ(sc); return !! (reg & QUP_STATE_VALID); } static int qcom_spi_hw_qup_wait_state_valid_locked(struct qcom_spi_softc *sc) { int i; for (i = 0; i < 10; i++) { if (qcom_spi_hw_qup_is_state_valid_locked(sc)) break; } if (i >= 10) { device_printf(sc->sc_dev, "ERROR: timeout waiting for valid state\n"); return (ENXIO); } return (0); } static bool qcom_spi_hw_is_opmode_dma_locked(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); if (sc->state.transfer_mode == QUP_IO_M_MODE_DMOV) return (true); if (sc->state.transfer_mode == QUP_IO_M_MODE_BAM) return (true); return (false); } int qcom_spi_hw_qup_set_state_locked(struct qcom_spi_softc *sc, uint32_t state) { uint32_t cur_state; int ret; QCOM_SPI_ASSERT_LOCKED(sc); /* Wait until the state becomes valid */ ret = qcom_spi_hw_qup_wait_state_valid_locked(sc); if (ret != 0) { return (ret); } cur_state = QCOM_SPI_READ_4(sc, QUP_STATE); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_STATE_CHANGE, "%s: target state=%d, cur_state=0x%08x\n", __func__, state, cur_state); /* * According to the QUP specification, when going * from PAUSE to RESET, two writes are required. */ if ((state == QUP_STATE_RESET) && ((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE)) { QCOM_SPI_WRITE_4(sc, QUP_STATE, QUP_STATE_CLEAR); QCOM_SPI_BARRIER_WRITE(sc); QCOM_SPI_WRITE_4(sc, QUP_STATE, QUP_STATE_CLEAR); QCOM_SPI_BARRIER_WRITE(sc); } else { cur_state &= ~QUP_STATE_MASK; cur_state |= state; QCOM_SPI_WRITE_4(sc, QUP_STATE, cur_state); QCOM_SPI_BARRIER_WRITE(sc); } /* Wait until the state becomes valid */ ret = qcom_spi_hw_qup_wait_state_valid_locked(sc); if (ret != 0) { return (ret); } cur_state = QCOM_SPI_READ_4(sc, QUP_STATE); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_STATE_CHANGE, "%s: FINISH: target state=%d, cur_state=0x%08x\n", __func__, state, cur_state); return (0); } /* * Do initial QUP setup. * * This is initially for the SPI driver; it would be interesting to see how * much of this is the same with the I2C/HSUART paths. */ int qcom_spi_hw_qup_init_locked(struct qcom_spi_softc *sc) { int ret; QCOM_SPI_ASSERT_LOCKED(sc); /* Full hardware reset */ (void) qcom_spi_hw_do_full_reset(sc); ret = qcom_spi_hw_qup_set_state_locked(sc, QUP_STATE_RESET); if (ret != 0) { device_printf(sc->sc_dev, "ERROR: %s: couldn't reset\n", __func__); goto error; } QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, 0); QCOM_SPI_WRITE_4(sc, QUP_IO_M_MODES, 0); /* Note: no QUP_OPERATIONAL_MASK in QUP v1 */ if (! QCOM_SPI_QUP_VERSION_V1(sc)) QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL_MASK, 0); /* Explicitly disable input overrun in QUP v1 */ if (QCOM_SPI_QUP_VERSION_V1(sc)) QCOM_SPI_WRITE_4(sc, QUP_ERROR_FLAGS_EN, QUP_ERROR_OUTPUT_OVER_RUN | QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN); QCOM_SPI_BARRIER_WRITE(sc); return (0); error: return (ret); } /* * Do initial SPI setup. */ int qcom_spi_hw_spi_init_locked(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); /* Initial SPI error flags */ QCOM_SPI_WRITE_4(sc, SPI_ERROR_FLAGS_EN, QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN); QCOM_SPI_BARRIER_WRITE(sc); /* Initial SPI config */ QCOM_SPI_WRITE_4(sc, SPI_CONFIG, 0); QCOM_SPI_BARRIER_WRITE(sc); /* Initial CS/tri-state io control config */ QCOM_SPI_WRITE_4(sc, SPI_IO_CONTROL, SPI_IO_C_NO_TRI_STATE | SPI_IO_C_CS_SELECT(sc->config.cs_select)); QCOM_SPI_BARRIER_WRITE(sc); return (0); } /* * Force the currently selected device CS line to be active * or inactive. * * This forces it to be active or inactive rather than letting * the SPI transfer machine do its thing. If you want to be able * break up a big transaction into a handful of smaller ones, * without toggling /CS_n for that device, then you need it forced. * (If you toggle the /CS_n to the device to inactive then active, * NOR/NAND devices tend to stop a block transfer.) */ int qcom_spi_hw_spi_cs_force(struct qcom_spi_softc *sc, int cs, bool enable) { uint32_t reg; QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_CHIPSELECT, "%s: called, enable=%u\n", __func__, enable); reg = QCOM_SPI_READ_4(sc, SPI_IO_CONTROL); if (enable) reg |= SPI_IO_C_FORCE_CS; else reg &= ~SPI_IO_C_FORCE_CS; reg &= ~SPI_IO_C_CS_SELECT_MASK; reg |= SPI_IO_C_CS_SELECT(cs); QCOM_SPI_WRITE_4(sc, SPI_IO_CONTROL, reg); QCOM_SPI_BARRIER_WRITE(sc); return (0); } /* * ACK/store current interrupt flag state. */ int qcom_spi_hw_interrupt_handle(struct qcom_spi_softc *sc) { uint32_t qup_error, spi_error, op_flags; QCOM_SPI_ASSERT_LOCKED(sc); /* Get QUP/SPI state */ qup_error = QCOM_SPI_READ_4(sc, QUP_ERROR_FLAGS); spi_error = QCOM_SPI_READ_4(sc, SPI_ERROR_FLAGS); op_flags = QCOM_SPI_READ_4(sc, QUP_OPERATIONAL); /* ACK state */ QCOM_SPI_WRITE_4(sc, QUP_ERROR_FLAGS, qup_error); QCOM_SPI_WRITE_4(sc, SPI_ERROR_FLAGS, spi_error); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_INTR, "%s: called; qup=0x%08x, spi=0x%08x, op=0x%08x\n", __func__, qup_error, spi_error, op_flags); /* handle error flags */ if (qup_error != 0) { device_printf(sc->sc_dev, "ERROR: (QUP) mask=0x%08x\n", qup_error); sc->intr.error = true; } if (spi_error != 0) { device_printf(sc->sc_dev, "ERROR: (SPI) mask=0x%08x\n", spi_error); sc->intr.error = true; } /* handle operational state */ if (qcom_spi_hw_is_opmode_dma_locked(sc)) { /* ACK interrupts now */ QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, op_flags); if ((op_flags & QUP_OP_IN_SERVICE_FLAG) && (op_flags & QUP_OP_MAX_INPUT_DONE_FLAG)) sc->intr.rx_dma_done = true; if ((op_flags & QUP_OP_OUT_SERVICE_FLAG) && (op_flags & QUP_OP_MAX_OUTPUT_DONE_FLAG)) sc->intr.tx_dma_done = true; } else { /* FIFO/Block */ if (op_flags & QUP_OP_IN_SERVICE_FLAG) sc->intr.do_rx = true; if (op_flags & QUP_OP_OUT_SERVICE_FLAG) sc->intr.do_tx = true; } /* Check if we've finished transfers */ if (op_flags & QUP_OP_MAX_INPUT_DONE_FLAG) sc->intr.done = true; if (sc->intr.error) sc->intr.done = true; return (0); } /* * Make initial transfer selections based on the transfer sizes * and alignment. * * For now this'll just default to FIFO until that works, and then * will grow to include BLOCK / DMA as appropriate. */ int qcom_spi_hw_setup_transfer_selection(struct qcom_spi_softc *sc, uint32_t len) { QCOM_SPI_ASSERT_LOCKED(sc); /* * For now only support doing a single FIFO transfer. * The main PIO transfer routine loop will break it up for us. */ sc->state.transfer_mode = QUP_IO_M_MODE_FIFO; sc->transfer.tx_offset = 0; sc->transfer.rx_offset = 0; sc->transfer.tx_len = 0; sc->transfer.rx_len = 0; sc->transfer.tx_buf = NULL; sc->transfer.rx_buf = NULL; /* * If we're sending a DWORD multiple sized block (like IO buffers) * then we can totally just use the DWORD size transfers. * * This is really only valid for PIO/block modes; I'm not yet * sure what we should do for DMA modes. */ if (len > 0 && len % 4 == 0) sc->state.transfer_word_size = 4; else sc->state.transfer_word_size = 1; return (0); } /* * Blank the transfer state after a full transfer is completed. */ int qcom_spi_hw_complete_transfer(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); sc->state.transfer_mode = QUP_IO_M_MODE_FIFO; sc->transfer.tx_offset = 0; sc->transfer.rx_offset = 0; sc->transfer.tx_len = 0; sc->transfer.rx_len = 0; sc->transfer.tx_buf = NULL; sc->transfer.rx_buf = NULL; sc->state.transfer_word_size = 0; return (0); } /* * Configure up the transfer selection for the current transfer. * * This calculates how many words we can transfer in the current * transfer and what's left to transfer. */ int qcom_spi_hw_setup_current_transfer(struct qcom_spi_softc *sc) { uint32_t bytes_left; QCOM_SPI_ASSERT_LOCKED(sc); /* * XXX For now, base this on the TX side buffer size, not both. * Later on we'll want to configure it based on the MAX of * either and just eat up the dummy values in the PIO * routines. (For DMA it's .. more annoyingly complicated * if the transfer sizes are not symmetrical.) */ bytes_left = sc->transfer.tx_len - sc->transfer.tx_offset; if (sc->state.transfer_mode == QUP_IO_M_MODE_FIFO) { /* * For FIFO transfers the num_words limit depends upon * the word size, FIFO size and how many bytes are left. * It definitely will be under SPI_MAX_XFER so don't * worry about that here. */ sc->transfer.num_words = bytes_left / sc->state.transfer_word_size; sc->transfer.num_words = MIN(sc->transfer.num_words, sc->config.input_fifo_size / sizeof(uint32_t)); } else if (sc->state.transfer_mode == QUP_IO_M_MODE_BLOCK) { /* * For BLOCK transfers the logic will be a little different. * Instead of it being based on the maximum input_fifo_size, * it'll be broken down into the 'words per block" size but * our maximum transfer size will ACTUALLY be capped by * SPI_MAX_XFER (65536-64 bytes.) Each transfer * will end up being in multiples of a block until the * last transfer. */ sc->transfer.num_words = bytes_left / sc->state.transfer_word_size; sc->transfer.num_words = MIN(sc->transfer.num_words, SPI_MAX_XFER); } QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: transfer.tx_len=%u," "transfer.tx_offset=%u," " transfer_word_size=%u," " bytes_left=%u, num_words=%u, fifo_word_max=%u\n", __func__, sc->transfer.tx_len, sc->transfer.tx_offset, sc->state.transfer_word_size, bytes_left, sc->transfer.num_words, sc->config.input_fifo_size / sizeof(uint32_t)); return (0); } /* * Setup the PIO FIFO transfer count. * * Note that we get a /single/ TX/RX phase up to these num_words * transfers. */ int qcom_spi_hw_setup_pio_transfer_cnt(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_MX_READ_CNT, sc->transfer.num_words); QCOM_SPI_WRITE_4(sc, QUP_MX_WRITE_CNT, sc->transfer.num_words); QCOM_SPI_WRITE_4(sc, QUP_MX_INPUT_CNT, 0); QCOM_SPI_WRITE_4(sc, QUP_MX_OUTPUT_CNT, 0); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: num_words=%u\n", __func__, sc->transfer.num_words); QCOM_SPI_BARRIER_WRITE(sc); return (0); } /* * Setup the PIO BLOCK transfer count. * * This sets up the total transfer size, in TX/RX FIFO block size * chunks. We will get multiple notifications when a block sized * chunk of data is avaliable or required. */ int qcom_spi_hw_setup_block_transfer_cnt(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_MX_READ_CNT, 0); QCOM_SPI_WRITE_4(sc, QUP_MX_WRITE_CNT, 0); QCOM_SPI_WRITE_4(sc, QUP_MX_INPUT_CNT, sc->transfer.num_words); QCOM_SPI_WRITE_4(sc, QUP_MX_OUTPUT_CNT, sc->transfer.num_words); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_setup_io_modes(struct qcom_spi_softc *sc) { uint32_t reg; QCOM_SPI_ASSERT_LOCKED(sc); reg = QCOM_SPI_READ_4(sc, QUP_IO_M_MODES); reg &= ~((QUP_IO_M_INPUT_MODE_MASK << QUP_IO_M_INPUT_MODE_SHIFT) | (QUP_IO_M_OUTPUT_MODE_MASK << QUP_IO_M_OUTPUT_MODE_SHIFT)); /* * If it's being done using DMA then the hardware will * need to pack and unpack the byte stream into the word/dword * stream being expected by the SPI/QUP micro engine. * * For PIO modes we're doing the pack/unpack in software, * see the pio/block transfer routines. */ if (qcom_spi_hw_is_opmode_dma_locked(sc)) reg |= (QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); else reg &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); /* Transfer mode */ reg |= ((sc->state.transfer_mode & QUP_IO_M_INPUT_MODE_MASK) << QUP_IO_M_INPUT_MODE_SHIFT); reg |= ((sc->state.transfer_mode & QUP_IO_M_OUTPUT_MODE_MASK) << QUP_IO_M_OUTPUT_MODE_SHIFT); QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: QUP_IO_M_MODES=0x%08x\n", __func__, reg); QCOM_SPI_WRITE_4(sc, QUP_IO_M_MODES, reg); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_setup_spi_io_clock_polarity(struct qcom_spi_softc *sc, bool cpol) { uint32_t reg; QCOM_SPI_ASSERT_LOCKED(sc); reg = QCOM_SPI_READ_4(sc, SPI_IO_CONTROL); if (cpol) reg |= SPI_IO_C_CLK_IDLE_HIGH; else reg &= ~SPI_IO_C_CLK_IDLE_HIGH; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: SPI_IO_CONTROL=0x%08x\n", __func__, reg); QCOM_SPI_WRITE_4(sc, SPI_IO_CONTROL, reg); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_setup_spi_config(struct qcom_spi_softc *sc, uint32_t clock_val, bool cpha) { uint32_t reg; /* * For now we don't have a way to configure loopback SPI for testing, * or the clock/transfer phase. When we do then here's where we * would put that. */ QCOM_SPI_ASSERT_LOCKED(sc); reg = QCOM_SPI_READ_4(sc, SPI_CONFIG); reg &= ~SPI_CONFIG_LOOPBACK; if (cpha) reg &= ~SPI_CONFIG_INPUT_FIRST; else reg |= SPI_CONFIG_INPUT_FIRST; /* * If the frequency is above SPI_HS_MIN_RATE then enable high speed. * This apparently improves stability. * * Note - don't do this if SPI loopback is enabled! */ if (clock_val >= SPI_HS_MIN_RATE) reg |= SPI_CONFIG_HS_MODE; else reg &= ~SPI_CONFIG_HS_MODE; QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: SPI_CONFIG=0x%08x\n", __func__, reg); QCOM_SPI_WRITE_4(sc, SPI_CONFIG, reg); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_setup_qup_config(struct qcom_spi_softc *sc, bool is_tx, bool is_rx) { uint32_t reg; QCOM_SPI_ASSERT_LOCKED(sc); reg = QCOM_SPI_READ_4(sc, QUP_CONFIG); reg &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); /* SPI mode */ reg |= QUP_CONFIG_SPI_MODE; /* bitmask for number of bits per word being used in each FIFO slot */ reg |= ((sc->state.transfer_word_size * 8) - 1) & QUP_CONFIG_N; /* * When doing DMA we need to configure whether we are shifting * data in, out, and/or both. For PIO/block modes it must stay * unset. */ if (qcom_spi_hw_is_opmode_dma_locked(sc)) { if (is_rx == false) reg |= QUP_CONFIG_NO_INPUT; if (is_tx == false) reg |= QUP_CONFIG_NO_OUTPUT; } QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: QUP_CONFIG=0x%08x\n", __func__, reg); QCOM_SPI_WRITE_4(sc, QUP_CONFIG, reg); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_setup_operational_mask(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); if (QCOM_SPI_QUP_VERSION_V1(sc)) { QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TRANSFER_SETUP, "%s: skipping, qupv1\n", __func__); return (0); } if (qcom_spi_hw_is_opmode_dma_locked(sc)) QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL_MASK, QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG); else QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL_MASK, 0); QCOM_SPI_BARRIER_WRITE(sc); return (0); } /* * ACK that we already have serviced the output FIFO. */ int qcom_spi_hw_ack_write_pio_fifo(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, QUP_OP_OUT_SERVICE_FLAG); QCOM_SPI_BARRIER_WRITE(sc); return (0); } int qcom_spi_hw_ack_opmode(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_BARRIER_READ(sc); QCOM_SPI_READ_4(sc, QUP_OPERATIONAL); QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, QUP_OP_OUT_SERVICE_FLAG); QCOM_SPI_BARRIER_WRITE(sc); return (0); } /* * Read the value from the TX buffer into the given 32 bit DWORD, * pre-shifting it into the place requested. * * Returns true if there was a byte available, false otherwise. */ static bool qcom_spi_hw_write_from_tx_buf(struct qcom_spi_softc *sc, int shift, uint32_t *val) { QCOM_SPI_ASSERT_LOCKED(sc); if (sc->transfer.tx_buf == NULL) return false; if (sc->transfer.tx_offset < sc->transfer.tx_len) { *val |= (sc->transfer.tx_buf[sc->transfer.tx_offset] & 0xff) << shift; sc->transfer.tx_offset++; return true; } return false; } int qcom_spi_hw_write_pio_fifo(struct qcom_spi_softc *sc) { uint32_t i; int num_bytes = 0; QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, QUP_OP_OUT_SERVICE_FLAG); QCOM_SPI_BARRIER_WRITE(sc); /* * Loop over the transfer num_words, do complain if we are full. */ for (i = 0; i < sc->transfer.num_words; i++) { uint32_t reg; /* Break if FIFO is full */ if ((QCOM_SPI_READ_4(sc, QUP_OPERATIONAL) & QUP_OP_OUT_FIFO_FULL) != 0) { device_printf(sc->sc_dev, "%s: FIFO full\n", __func__); break; } /* * Handle 1, 2, 4 byte transfer packing rules. * * Unlike read, where the shifting is done towards the MSB * for us by default, we have to do it ourselves for transmit. * There's a bit that one can set to do the preshifting * (and u-boot uses it!) but I'll stick with what Linux is * doing to make it easier for future maintenance. * * The format is the same as 4 byte RX - 0xaabbccdd; * the byte ordering on the wire being aa, bb, cc, dd. */ reg = 0; if (sc->state.transfer_word_size == 1) { if (qcom_spi_hw_write_from_tx_buf(sc, 24, ®)) num_bytes++; } else if (sc->state.transfer_word_size == 2) { if (qcom_spi_hw_write_from_tx_buf(sc, 24, ®)) num_bytes++; if (qcom_spi_hw_write_from_tx_buf(sc, 16, ®)) num_bytes++; } else if (sc->state.transfer_word_size == 4) { if (qcom_spi_hw_write_from_tx_buf(sc, 24, ®)) num_bytes++; if (qcom_spi_hw_write_from_tx_buf(sc, 16, ®)) num_bytes++; if (qcom_spi_hw_write_from_tx_buf(sc, 8, ®)) num_bytes++; if (qcom_spi_hw_write_from_tx_buf(sc, 0, ®)) num_bytes++; } /* * always shift out something in case we need phantom * writes to finish things up whilst we read a reply * payload. */ QCOM_SPI_WRITE_4(sc, QUP_OUTPUT_FIFO, reg); QCOM_SPI_BARRIER_WRITE(sc); } QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TX_FIFO, "%s: wrote %d bytes (%d fifo slots)\n", __func__, num_bytes, sc->transfer.num_words); return (0); } int qcom_spi_hw_write_pio_block(struct qcom_spi_softc *sc) { /* Not yet implemented */ return (ENXIO); } /* * Read data into the RX buffer and increment the RX offset. * * Return true if the byte was saved into the RX buffer, else * return false. */ static bool qcom_spi_hw_read_into_rx_buf(struct qcom_spi_softc *sc, uint8_t val) { QCOM_SPI_ASSERT_LOCKED(sc); if (sc->transfer.rx_buf == NULL) return false; /* Make sure we aren't overflowing the receive buffer */ if (sc->transfer.rx_offset < sc->transfer.rx_len) { sc->transfer.rx_buf[sc->transfer.rx_offset] = val; sc->transfer.rx_offset++; return true; } return false; } /* * Read "n_words" transfers, and push those bytes into the receive buffer. * Make sure we have enough space, and make sure we don't overflow the * read buffer size too! */ int qcom_spi_hw_read_pio_fifo(struct qcom_spi_softc *sc) { uint32_t i; uint32_t reg; int num_bytes = 0; QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_OPERATIONAL, QUP_OP_IN_SERVICE_FLAG); QCOM_SPI_BARRIER_WRITE(sc); for (i = 0; i < sc->transfer.num_words; i++) { /* Break if FIFO is empty */ QCOM_SPI_BARRIER_READ(sc); reg = QCOM_SPI_READ_4(sc, QUP_OPERATIONAL); if ((reg & QUP_OP_IN_FIFO_NOT_EMPTY) == 0) { device_printf(sc->sc_dev, "%s: FIFO empty\n", __func__); break; } /* * Always read num_words up to FIFO being non-empty; that way * if we have mis-matching TX/RX buffer sizes for some reason * we will read the needed phantom bytes. */ reg = QCOM_SPI_READ_4(sc, QUP_INPUT_FIFO); /* * Unpack the receive buffer based on whether we are * doing 1, 2, or 4 byte transfer words. */ if (sc->state.transfer_word_size == 1) { if (qcom_spi_hw_read_into_rx_buf(sc, reg & 0xff)) num_bytes++; } else if (sc->state.transfer_word_size == 2) { if (qcom_spi_hw_read_into_rx_buf(sc, (reg >> 8) & 0xff)) num_bytes++; if (qcom_spi_hw_read_into_rx_buf(sc, reg & 0xff)) num_bytes++; } else if (sc->state.transfer_word_size == 4) { if (qcom_spi_hw_read_into_rx_buf(sc, (reg >> 24) & 0xff)) num_bytes++; if (qcom_spi_hw_read_into_rx_buf(sc, (reg >> 16) & 0xff)) num_bytes++; if (qcom_spi_hw_read_into_rx_buf(sc, (reg >> 8) & 0xff)) num_bytes++; if (qcom_spi_hw_read_into_rx_buf(sc, reg & 0xff)) num_bytes++; } } QCOM_SPI_DPRINTF(sc, QCOM_SPI_DEBUG_HW_TX_FIFO, "%s: read %d bytes (%d transfer words)\n", __func__, num_bytes, sc->transfer.num_words); #if 0 /* * This is a no-op for FIFO mode, it's only a thing for BLOCK * transfers. */ QCOM_SPI_BARRIER_READ(sc); reg = QCOM_SPI_READ_4(sc, QUP_OPERATIONAL); if (reg & QUP_OP_MAX_INPUT_DONE_FLAG) { device_printf(sc->sc_dev, "%s: read complete (DONE)\n" , __func__); sc->intr.done = true; } #endif #if 0 /* * And see if we've finished the transfer and won't be getting * any more. Then treat it as done as well. * * In FIFO only mode we don't get a completion interrupt; * we get an interrupt when the FIFO has enough data present. */ if ((sc->state.transfer_mode == QUP_IO_M_MODE_FIFO) && (sc->transfer.rx_offset >= sc->transfer.rx_len)) { device_printf(sc->sc_dev, "%s: read complete (rxlen)\n", __func__); sc->intr.done = true; } #endif /* * For FIFO transfers we get a /single/ result that complete * the FIFO transfer. We won't get any subsequent transfers; * we'll need to schedule a new FIFO transfer. */ sc->intr.done = true; return (0); } int qcom_spi_hw_read_pio_block(struct qcom_spi_softc *sc) { /* Not yet implemented */ return (ENXIO); } int qcom_spi_hw_do_full_reset(struct qcom_spi_softc *sc) { QCOM_SPI_ASSERT_LOCKED(sc); QCOM_SPI_WRITE_4(sc, QUP_SW_RESET, 1); QCOM_SPI_BARRIER_WRITE(sc); DELAY(100); return (0); } diff --git a/sys/dev/spibus/controller/allwinner/aw_spi.c b/sys/dev/spibus/controller/allwinner/aw_spi.c index fe6f5c21fcd7..382f44345250 100644 --- a/sys/dev/spibus/controller/allwinner/aw_spi.c +++ b/sys/dev/spibus/controller/allwinner/aw_spi.c @@ -1,610 +1,610 @@ /*- * Copyright (c) 2018 Emmanuel Vadot * * 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 ``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 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 #include #include #include #include -#include +#include #include "spibus_if.h" #define AW_SPI_GCR 0x04 /* Global Control Register */ #define AW_SPI_GCR_EN (1 << 0) /* ENable */ #define AW_SPI_GCR_MODE_MASTER (1 << 1) /* 1 = Master, 0 = Slave */ #define AW_SPI_GCR_TP_EN (1 << 7) /* 1 = Stop transmit when FIFO is full */ #define AW_SPI_GCR_SRST (1 << 31) /* Soft Reset */ #define AW_SPI_TCR 0x08 /* Transfer Control register */ #define AW_SPI_TCR_XCH (1 << 31) /* Initiate transfer */ #define AW_SPI_TCR_SDDM (1 << 14) /* Sending Delay Data Mode */ #define AW_SPI_TCR_SDM (1 << 13) /* Master Sample Data Mode */ #define AW_SPI_TCR_FBS (1 << 12) /* First Transmit Bit Select (1 == LSB) */ #define AW_SPI_TCR_SDC (1 << 11) /* Master Sample Data Control */ #define AW_SPI_TCR_RPSM (1 << 10) /* Rapid Mode Select */ #define AW_SPI_TCR_DDB (1 << 9) /* Dummy Burst Type */ #define AW_SPI_TCR_SSSEL_MASK 0x30 /* Chip select */ #define AW_SPI_TCR_SSSEL_SHIFT 4 #define AW_SPI_TCR_SS_LEVEL (1 << 7) /* 1 == CS High */ #define AW_SPI_TCR_SS_OWNER (1 << 6) /* 1 == Software controlled */ #define AW_SPI_TCR_SPOL (1 << 2) /* 1 == Active low */ #define AW_SPI_TCR_CPOL (1 << 1) /* 1 == Active low */ #define AW_SPI_TCR_CPHA (1 << 0) /* 1 == Phase 1 */ #define AW_SPI_IER 0x10 /* Interrupt Control Register */ #define AW_SPI_IER_SS (1 << 13) /* Chip select went from valid to invalid */ #define AW_SPI_IER_TC (1 << 12) /* Transfer complete */ #define AW_SPI_IER_TF_UDR (1 << 11) /* TXFIFO underrun */ #define AW_SPI_IER_TF_OVF (1 << 10) /* TXFIFO overrun */ #define AW_SPI_IER_RF_UDR (1 << 9) /* RXFIFO underrun */ #define AW_SPI_IER_RF_OVF (1 << 8) /* RXFIFO overrun */ #define AW_SPI_IER_TF_FULL (1 << 6) /* TXFIFO Full */ #define AW_SPI_IER_TF_EMP (1 << 5) /* TXFIFO Empty */ #define AW_SPI_IER_TF_ERQ (1 << 4) /* TXFIFO Empty Request */ #define AW_SPI_IER_RF_FULL (1 << 2) /* RXFIFO Full */ #define AW_SPI_IER_RF_EMP (1 << 1) /* RXFIFO Empty */ #define AW_SPI_IER_RF_RDY (1 << 0) /* RXFIFO Ready Request */ #define AW_SPI_ISR 0x14 /* Interrupt Status Register */ #define AW_SPI_FCR 0x18 /* FIFO Control Register */ #define AW_SPI_FCR_TX_RST (1 << 31) /* Reset TX FIFO */ #define AW_SPI_FCR_TX_TRIG_MASK 0xFF0000 /* TX FIFO Trigger level */ #define AW_SPI_FCR_TX_TRIG_SHIFT 16 #define AW_SPI_FCR_RX_RST (1 << 15) /* Reset RX FIFO */ #define AW_SPI_FCR_RX_TRIG_MASK 0xFF /* RX FIFO Trigger level */ #define AW_SPI_FCR_RX_TRIG_SHIFT 0 #define AW_SPI_FSR 0x1C /* FIFO Status Register */ #define AW_SPI_FSR_TB_WR (1 << 31) #define AW_SPI_FSR_TB_CNT_MASK 0x70000000 #define AW_SPI_FSR_TB_CNT_SHIFT 28 #define AW_SPI_FSR_TF_CNT_MASK 0xFF0000 #define AW_SPI_FSR_TF_CNT_SHIFT 16 #define AW_SPI_FSR_RB_WR (1 << 15) #define AW_SPI_FSR_RB_CNT_MASK 0x7000 #define AW_SPI_FSR_RB_CNT_SHIFT 12 #define AW_SPI_FSR_RF_CNT_MASK 0xFF #define AW_SPI_FSR_RF_CNT_SHIFT 0 #define AW_SPI_WCR 0x20 /* Wait Clock Counter Register */ #define AW_SPI_CCR 0x24 /* Clock Rate Control Register */ #define AW_SPI_CCR_DRS (1 << 12) /* Clock divider select */ #define AW_SPI_CCR_CDR1_MASK 0xF00 #define AW_SPI_CCR_CDR1_SHIFT 8 #define AW_SPI_CCR_CDR2_MASK 0xFF #define AW_SPI_CCR_CDR2_SHIFT 0 #define AW_SPI_MBC 0x30 /* Burst Counter Register */ #define AW_SPI_MTC 0x34 /* Transmit Counter Register */ #define AW_SPI_BCC 0x38 /* Burst Control Register */ #define AW_SPI_MDMA_CTL 0x88 /* Normal DMA Control Register */ #define AW_SPI_TXD 0x200 /* TX Data Register */ #define AW_SPI_RDX 0x300 /* RX Data Register */ #define AW_SPI_MAX_CS 4 #define AW_SPI_FIFO_SIZE 64 static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-h3-spi", 1 }, { NULL, 0 } }; static struct resource_spec aw_spi_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct aw_spi_softc { device_t dev; device_t spibus; struct resource *res[2]; struct mtx mtx; clk_t clk_ahb; clk_t clk_mod; uint64_t mod_freq; hwreset_t rst_ahb; void * intrhand; int transfer; uint8_t *rxbuf; uint32_t rxcnt; uint8_t *txbuf; uint32_t txcnt; uint32_t txlen; uint32_t rxlen; }; #define AW_SPI_LOCK(sc) mtx_lock(&(sc)->mtx) #define AW_SPI_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define AW_SPI_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) #define AW_SPI_READ_1(sc, reg) bus_read_1((sc)->res[0], (reg)) #define AW_SPI_WRITE_1(sc, reg, val) bus_write_1((sc)->res[0], (reg), (val)) #define AW_SPI_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define AW_SPI_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int aw_spi_probe(device_t dev); static int aw_spi_attach(device_t dev); static int aw_spi_detach(device_t dev); static int aw_spi_intr(void *arg); static int aw_spi_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(dev, "Allwinner SPI"); return (BUS_PROBE_DEFAULT); } static int aw_spi_attach(device_t dev) { struct aw_spi_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, aw_spi_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, aw_spi_intr, NULL, sc, &sc->intrhand)) { bus_release_resources(dev, aw_spi_spec, sc->res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } /* De-assert reset */ if (hwreset_get_by_ofw_idx(dev, 0, 0, &sc->rst_ahb) == 0) { error = hwreset_deassert(sc->rst_ahb); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } /* Activate the module clock. */ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb); if (error != 0) { device_printf(dev, "cannot get ahb clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_mod); if (error != 0) { device_printf(dev, "cannot get mod clock\n"); goto fail; } error = clk_enable(sc->clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } error = clk_enable(sc->clk_mod); if (error != 0) { device_printf(dev, "cannot enable mod clock\n"); goto fail; } sc->spibus = device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); fail: aw_spi_detach(dev); return (error); } static int aw_spi_detach(device_t dev) { struct aw_spi_softc *sc; sc = device_get_softc(dev); bus_generic_detach(sc->dev); if (sc->spibus != NULL) device_delete_child(dev, sc->spibus); if (sc->clk_mod != NULL) clk_release(sc->clk_mod); if (sc->clk_ahb) clk_release(sc->clk_ahb); if (sc->rst_ahb) hwreset_assert(sc->rst_ahb); if (sc->intrhand != NULL) bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); bus_release_resources(dev, aw_spi_spec, sc->res); mtx_destroy(&sc->mtx); return (0); } static phandle_t aw_spi_get_node(device_t bus, device_t dev) { return ofw_bus_get_node(bus); } static void aw_spi_setup_mode(struct aw_spi_softc *sc, uint32_t mode) { uint32_t reg; /* We only support master mode */ reg = AW_SPI_READ_4(sc, AW_SPI_GCR); reg |= AW_SPI_GCR_MODE_MASTER; AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg); /* Setup the modes */ reg = AW_SPI_READ_4(sc, AW_SPI_TCR); if (mode & SPIBUS_MODE_CPHA) reg |= AW_SPI_TCR_CPHA; if (mode & SPIBUS_MODE_CPOL) reg |= AW_SPI_TCR_CPOL; AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg); } static void aw_spi_setup_cs(struct aw_spi_softc *sc, uint32_t cs, bool low) { uint32_t reg; /* Setup CS */ reg = AW_SPI_READ_4(sc, AW_SPI_TCR); reg &= ~(AW_SPI_TCR_SSSEL_MASK); reg |= cs << AW_SPI_TCR_SSSEL_SHIFT; reg |= AW_SPI_TCR_SS_OWNER; if (low) reg &= ~(AW_SPI_TCR_SS_LEVEL); else reg |= AW_SPI_TCR_SS_LEVEL; AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg); } static uint64_t aw_spi_clock_test_cdr1(struct aw_spi_softc *sc, uint64_t clock, uint32_t *ccr) { uint64_t cur, best = 0; int i, max, best_div; max = AW_SPI_CCR_CDR1_MASK >> AW_SPI_CCR_CDR1_SHIFT; for (i = 0; i < max; i++) { cur = sc->mod_freq / (1 << i); if ((clock - cur) < (clock - best)) { best = cur; best_div = i; } } *ccr = (best_div << AW_SPI_CCR_CDR1_SHIFT); return (best); } static uint64_t aw_spi_clock_test_cdr2(struct aw_spi_softc *sc, uint64_t clock, uint32_t *ccr) { uint64_t cur, best = 0; int i, max, best_div; max = ((AW_SPI_CCR_CDR2_MASK) >> AW_SPI_CCR_CDR2_SHIFT); for (i = 0; i < max; i++) { cur = sc->mod_freq / (2 * i + 1); if ((clock - cur) < (clock - best)) { best = cur; best_div = i; } } *ccr = AW_SPI_CCR_DRS | (best_div << AW_SPI_CCR_CDR2_SHIFT); return (best); } static void aw_spi_setup_clock(struct aw_spi_softc *sc, uint64_t clock) { uint64_t best_ccr1, best_ccr2; uint32_t ccr, ccr1, ccr2; best_ccr1 = aw_spi_clock_test_cdr1(sc, clock, &ccr1); best_ccr2 = aw_spi_clock_test_cdr2(sc, clock, &ccr2); if (best_ccr1 == clock) { ccr = ccr1; } else if (best_ccr2 == clock) { ccr = ccr2; } else { if ((clock - best_ccr1) < (clock - best_ccr2)) ccr = ccr1; else ccr = ccr2; } AW_SPI_WRITE_4(sc, AW_SPI_CCR, ccr); } static inline void aw_spi_fill_txfifo(struct aw_spi_softc *sc) { uint32_t reg, txcnt; int i; if (sc->txcnt == sc->txlen) return; reg = AW_SPI_READ_4(sc, AW_SPI_FSR); reg &= AW_SPI_FSR_TF_CNT_MASK; txcnt = reg >> AW_SPI_FSR_TF_CNT_SHIFT; for (i = 0; i < (AW_SPI_FIFO_SIZE - txcnt); i++) { AW_SPI_WRITE_1(sc, AW_SPI_TXD, sc->txbuf[sc->txcnt++]); if (sc->txcnt == sc->txlen) break; } return; } static inline void aw_spi_read_rxfifo(struct aw_spi_softc *sc) { uint32_t reg; uint8_t val; int i; if (sc->rxcnt == sc->rxlen) return; reg = AW_SPI_READ_4(sc, AW_SPI_FSR); reg = (reg & AW_SPI_FSR_RF_CNT_MASK) >> AW_SPI_FSR_RF_CNT_SHIFT; for (i = 0; i < reg; i++) { val = AW_SPI_READ_1(sc, AW_SPI_RDX); if (sc->rxcnt < sc->rxlen) sc->rxbuf[sc->rxcnt++] = val; } } static int aw_spi_intr(void *arg) { struct aw_spi_softc *sc; uint32_t intr; sc = (struct aw_spi_softc *)arg; intr = AW_SPI_READ_4(sc, AW_SPI_ISR); if (intr & AW_SPI_IER_RF_RDY) aw_spi_read_rxfifo(sc); if (intr & AW_SPI_IER_TF_ERQ) { aw_spi_fill_txfifo(sc); /* * If we don't have anything else to write * disable TXFifo interrupts */ if (sc->txcnt == sc->txlen) AW_SPI_WRITE_4(sc, AW_SPI_IER, AW_SPI_IER_TC | AW_SPI_IER_RF_RDY); } if (intr & AW_SPI_IER_TC) { /* read the rest of the data from the fifo */ aw_spi_read_rxfifo(sc); /* Disable the interrupts */ AW_SPI_WRITE_4(sc, AW_SPI_IER, 0); sc->transfer = 0; wakeup(sc); } /* Clear Interrupts */ AW_SPI_WRITE_4(sc, AW_SPI_ISR, intr); return (intr != 0 ? FILTER_HANDLED : FILTER_STRAY); } static int aw_spi_xfer(struct aw_spi_softc *sc, void *rxbuf, void *txbuf, uint32_t txlen, uint32_t rxlen) { uint32_t reg; int error = 0, timeout; sc->rxbuf = rxbuf; sc->rxcnt = 0; sc->txbuf = txbuf; sc->txcnt = 0; sc->txlen = txlen; sc->rxlen = rxlen; /* Reset the FIFOs */ AW_SPI_WRITE_4(sc, AW_SPI_FCR, AW_SPI_FCR_TX_RST | AW_SPI_FCR_RX_RST); for (timeout = 1000; timeout > 0; timeout--) { reg = AW_SPI_READ_4(sc, AW_SPI_FCR); if (reg == 0) break; } if (timeout == 0) { device_printf(sc->dev, "Cannot reset the FIFOs\n"); return (EIO); } /* * Set the TX FIFO threshold to 3/4-th the size and * the RX FIFO one to 1/4-th. */ AW_SPI_WRITE_4(sc, AW_SPI_FCR, ((3 * AW_SPI_FIFO_SIZE / 4) << AW_SPI_FCR_TX_TRIG_SHIFT) | ((AW_SPI_FIFO_SIZE / 4) << AW_SPI_FCR_RX_TRIG_SHIFT)); /* Write the counters */ AW_SPI_WRITE_4(sc, AW_SPI_MBC, txlen); AW_SPI_WRITE_4(sc, AW_SPI_MTC, txlen); AW_SPI_WRITE_4(sc, AW_SPI_BCC, txlen); /* First fill */ aw_spi_fill_txfifo(sc); /* Start transmit */ reg = AW_SPI_READ_4(sc, AW_SPI_TCR); reg |= AW_SPI_TCR_XCH; AW_SPI_WRITE_4(sc, AW_SPI_TCR, reg); /* * Enable interrupts for : * Transmit complete * TX Fifo is below its trigger threshold * RX Fifo is above its trigger threshold */ AW_SPI_WRITE_4(sc, AW_SPI_IER, AW_SPI_IER_TC | AW_SPI_IER_TF_ERQ | AW_SPI_IER_RF_RDY); sc->transfer = 1; while (error == 0 && sc->transfer != 0) error = msleep(sc, &sc->mtx, 0, "aw_spi", 10 * hz); return (0); } static int aw_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct aw_spi_softc *sc; uint32_t cs, mode, clock, reg; int err = 0; sc = device_get_softc(dev); spibus_get_cs(child, &cs); spibus_get_clock(child, &clock); spibus_get_mode(child, &mode); /* The minimum divider is 2 so set the clock at twice the needed speed */ clk_set_freq(sc->clk_mod, 2 * clock, CLK_SET_ROUND_DOWN); clk_get_freq(sc->clk_mod, &sc->mod_freq); if (cs >= AW_SPI_MAX_CS) { device_printf(dev, "Invalid cs %d\n", cs); return (EINVAL); } mtx_lock(&sc->mtx); /* Enable and reset the module */ reg = AW_SPI_READ_4(sc, AW_SPI_GCR); reg |= AW_SPI_GCR_EN | AW_SPI_GCR_SRST; AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg); /* Setup clock, CS and mode */ aw_spi_setup_clock(sc, clock); aw_spi_setup_mode(sc, mode); if (cs & SPIBUS_CS_HIGH) aw_spi_setup_cs(sc, cs, false); else aw_spi_setup_cs(sc, cs, true); /* xfer */ err = 0; if (cmd->tx_cmd_sz > 0) err = aw_spi_xfer(sc, cmd->rx_cmd, cmd->tx_cmd, cmd->tx_cmd_sz, cmd->rx_cmd_sz); if (cmd->tx_data_sz > 0 && err == 0) err = aw_spi_xfer(sc, cmd->rx_data, cmd->tx_data, cmd->tx_data_sz, cmd->rx_data_sz); if (cs & SPIBUS_CS_HIGH) aw_spi_setup_cs(sc, cs, true); else aw_spi_setup_cs(sc, cs, false); /* Disable the module */ reg = AW_SPI_READ_4(sc, AW_SPI_GCR); reg &= ~AW_SPI_GCR_EN; AW_SPI_WRITE_4(sc, AW_SPI_GCR, reg); mtx_unlock(&sc->mtx); return (err); } static device_method_t aw_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_spi_probe), DEVMETHOD(device_attach, aw_spi_attach), DEVMETHOD(device_detach, aw_spi_detach), /* spibus_if */ DEVMETHOD(spibus_transfer, aw_spi_transfer), /* ofw_bus_if */ DEVMETHOD(ofw_bus_get_node, aw_spi_get_node), DEVMETHOD_END }; static driver_t aw_spi_driver = { "aw_spi", aw_spi_methods, sizeof(struct aw_spi_softc), }; DRIVER_MODULE(aw_spi, simplebus, aw_spi_driver, 0, 0); DRIVER_MODULE(ofw_spibus, aw_spi, ofw_spibus_driver, 0, 0); MODULE_DEPEND(aw_spi, ofw_spibus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/dev/spibus/controller/rockchip/rk_spi.c b/sys/dev/spibus/controller/rockchip/rk_spi.c index 42f12e6ddaee..f25ec77ead5b 100644 --- a/sys/dev/spibus/controller/rockchip/rk_spi.c +++ b/sys/dev/spibus/controller/rockchip/rk_spi.c @@ -1,476 +1,476 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Oleksandr Tymoshenko * * 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 ``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 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 #include #include #include #include -#include +#include #include "spibus_if.h" #define RK_SPI_CTRLR0 0x0000 #define CTRLR0_OPM_MASTER (0 << 20) #define CTRLR0_XFM_TR (0 << 18) #define CTRLR0_FRF_MOTO (0 << 16) #define CTRLR0_BHT_8BIT (1 << 13) #define CTRLR0_EM_BIG (1 << 11) #define CTRLR0_SSD_ONE (1 << 10) #define CTRLR0_SCPOL (1 << 7) #define CTRLR0_SCPH (1 << 6) #define CTRLR0_DFS_8BIT (1 << 0) #define RK_SPI_CTRLR1 0x0004 #define RK_SPI_ENR 0x0008 #define RK_SPI_SER 0x000c #define RK_SPI_BAUDR 0x0010 #define RK_SPI_TXFTLR 0x0014 #define RK_SPI_RXFTLR 0x0018 #define RK_SPI_TXFLR 0x001c #define RK_SPI_RXFLR 0x0020 #define RK_SPI_SR 0x0024 #define SR_BUSY (1 << 0) #define RK_SPI_IPR 0x0028 #define RK_SPI_IMR 0x002c #define IMR_RFFIM (1 << 4) #define IMR_TFEIM (1 << 0) #define RK_SPI_ISR 0x0030 #define ISR_RFFIS (1 << 4) #define ISR_TFEIS (1 << 0) #define RK_SPI_RISR 0x0034 #define RK_SPI_ICR 0x0038 #define RK_SPI_DMACR 0x003c #define RK_SPI_DMATDLR 0x0040 #define RK_SPI_DMARDLR 0x0044 #define RK_SPI_TXDR 0x0400 #define RK_SPI_RXDR 0x0800 #define CS_MAX 1 static struct ofw_compat_data compat_data[] = { { "rockchip,rk3328-spi", 1 }, { "rockchip,rk3399-spi", 1 }, { "rockchip,rk3568-spi", 1 }, { NULL, 0 } }; static struct resource_spec rk_spi_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct rk_spi_softc { device_t dev; device_t spibus; struct resource *res[2]; struct mtx mtx; clk_t clk_apb; clk_t clk_spi; void * intrhand; int transfer; uint32_t fifo_size; uint64_t max_freq; uint32_t intreg; uint8_t *rxbuf; uint32_t rxidx; uint8_t *txbuf; uint32_t txidx; uint32_t txlen; uint32_t rxlen; }; #define RK_SPI_LOCK(sc) mtx_lock(&(sc)->mtx) #define RK_SPI_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RK_SPI_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define RK_SPI_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int rk_spi_probe(device_t dev); static int rk_spi_attach(device_t dev); static int rk_spi_detach(device_t dev); static void rk_spi_intr(void *arg); static void rk_spi_enable_chip(struct rk_spi_softc *sc, int enable) { RK_SPI_WRITE_4(sc, RK_SPI_ENR, enable ? 1 : 0); } static int rk_spi_set_cs(struct rk_spi_softc *sc, uint32_t cs, bool active) { uint32_t reg; if (cs & SPIBUS_CS_HIGH) { device_printf(sc->dev, "SPIBUS_CS_HIGH is not supported\n"); return (EINVAL); } if (cs > CS_MAX) return (EINVAL); reg = RK_SPI_READ_4(sc, RK_SPI_SER); if (active) reg |= (1 << cs); else reg &= ~(1 << cs); RK_SPI_WRITE_4(sc, RK_SPI_SER, reg); return (0); } static void rk_spi_hw_setup(struct rk_spi_softc *sc, uint32_t mode, uint32_t freq) { uint32_t cr0; uint32_t div; cr0 = CTRLR0_OPM_MASTER | CTRLR0_XFM_TR | CTRLR0_FRF_MOTO | CTRLR0_BHT_8BIT | CTRLR0_EM_BIG | CTRLR0_SSD_ONE | CTRLR0_DFS_8BIT; if (mode & SPIBUS_MODE_CPHA) cr0 |= CTRLR0_SCPH; if (mode & SPIBUS_MODE_CPOL) cr0 |= CTRLR0_SCPOL; /* minimum divider is 2 */ if (sc->max_freq < freq*2) { clk_set_freq(sc->clk_spi, 2 * freq, CLK_SET_ROUND_DOWN); clk_get_freq(sc->clk_spi, &sc->max_freq); } div = ((sc->max_freq + freq - 1) / freq); div = (div + 1) & 0xfffe; RK_SPI_WRITE_4(sc, RK_SPI_BAUDR, div); RK_SPI_WRITE_4(sc, RK_SPI_CTRLR0, cr0); } static uint32_t rk_spi_fifo_size(struct rk_spi_softc *sc) { uint32_t txftlr, reg; for (txftlr = 2; txftlr < 32; txftlr++) { RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, txftlr); reg = RK_SPI_READ_4(sc, RK_SPI_TXFTLR); if (reg != txftlr) break; } RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, 0); if (txftlr == 31) return 0; return txftlr; } static void rk_spi_empty_rxfifo(struct rk_spi_softc *sc) { uint32_t rxlevel; rxlevel = RK_SPI_READ_4(sc, RK_SPI_RXFLR); while (sc->rxidx < sc->rxlen && (rxlevel-- > 0)) { sc->rxbuf[sc->rxidx++] = (uint8_t)RK_SPI_READ_4(sc, RK_SPI_RXDR); } } static void rk_spi_fill_txfifo(struct rk_spi_softc *sc) { uint32_t txlevel; txlevel = RK_SPI_READ_4(sc, RK_SPI_TXFLR); while (sc->txidx < sc->txlen && txlevel < sc->fifo_size) { RK_SPI_WRITE_4(sc, RK_SPI_TXDR, sc->txbuf[sc->txidx++]); txlevel++; } if (sc->txidx != sc->txlen) sc->intreg |= (IMR_TFEIM | IMR_RFFIM); } static int rk_spi_xfer_buf(struct rk_spi_softc *sc, void *rxbuf, void *txbuf, uint32_t len) { int err; if (len == 0) return (0); sc->rxbuf = rxbuf; sc->rxlen = len; sc->rxidx = 0; sc->txbuf = txbuf; sc->txlen = len; sc->txidx = 0; sc->intreg = 0; rk_spi_fill_txfifo(sc); RK_SPI_WRITE_4(sc, RK_SPI_IMR, sc->intreg); err = 0; while (err == 0 && sc->intreg != 0) err = msleep(sc, &sc->mtx, 0, "rk_spi", 10 * hz); while (err == 0 && sc->rxidx != sc->txidx) { /* read residual data from RX fifo */ rk_spi_empty_rxfifo(sc); } if (sc->rxidx != sc->rxlen || sc->txidx != sc->txlen) err = EIO; return (err); } static int rk_spi_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(dev, "Rockchip SPI"); return (BUS_PROBE_DEFAULT); } static int rk_spi_attach(device_t dev) { struct rk_spi_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, rk_spi_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_spi_intr, sc, &sc->intrhand)) { bus_release_resources(dev, rk_spi_spec, sc->res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } /* Activate the module clock. */ error = clk_get_by_ofw_name(dev, 0, "apb_pclk", &sc->clk_apb); if (error != 0) { device_printf(dev, "cannot get apb_pclk clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "spiclk", &sc->clk_spi); if (error != 0) { device_printf(dev, "cannot get spiclk clock\n"); goto fail; } error = clk_enable(sc->clk_apb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } error = clk_enable(sc->clk_spi); if (error != 0) { device_printf(dev, "cannot enable spiclk clock\n"); goto fail; } clk_get_freq(sc->clk_spi, &sc->max_freq); sc->fifo_size = rk_spi_fifo_size(sc); if (sc->fifo_size == 0) { device_printf(dev, "failed to get fifo size\n"); goto fail; } sc->spibus = device_add_child(dev, "spibus", -1); RK_SPI_WRITE_4(sc, RK_SPI_IMR, 0); RK_SPI_WRITE_4(sc, RK_SPI_TXFTLR, sc->fifo_size/2 - 1); RK_SPI_WRITE_4(sc, RK_SPI_RXFTLR, sc->fifo_size/2 - 1); return (bus_generic_attach(dev)); fail: rk_spi_detach(dev); return (error); } static int rk_spi_detach(device_t dev) { struct rk_spi_softc *sc; sc = device_get_softc(dev); bus_generic_detach(sc->dev); if (sc->spibus != NULL) device_delete_child(dev, sc->spibus); if (sc->clk_spi != NULL) clk_release(sc->clk_spi); if (sc->clk_apb) clk_release(sc->clk_apb); if (sc->intrhand != NULL) bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); bus_release_resources(dev, rk_spi_spec, sc->res); mtx_destroy(&sc->mtx); return (0); } static void rk_spi_intr(void *arg) { struct rk_spi_softc *sc; uint32_t intreg, isr; sc = arg; RK_SPI_LOCK(sc); intreg = RK_SPI_READ_4(sc, RK_SPI_IMR); isr = RK_SPI_READ_4(sc, RK_SPI_ISR); RK_SPI_WRITE_4(sc, RK_SPI_ICR, isr); if (isr & ISR_RFFIS) rk_spi_empty_rxfifo(sc); if (isr & ISR_TFEIS) rk_spi_fill_txfifo(sc); /* no bytes left, disable interrupt */ if (sc->txidx == sc->txlen) { sc->intreg = 0; wakeup(sc); } if (sc->intreg != intreg) { (void)RK_SPI_WRITE_4(sc, RK_SPI_IMR, sc->intreg); (void)RK_SPI_READ_4(sc, RK_SPI_IMR); } RK_SPI_UNLOCK(sc); } static phandle_t rk_spi_get_node(device_t bus, device_t dev) { return ofw_bus_get_node(bus); } static int rk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct rk_spi_softc *sc; uint32_t cs, mode, clock; int err = 0; sc = device_get_softc(dev); spibus_get_cs(child, &cs); spibus_get_clock(child, &clock); spibus_get_mode(child, &mode); RK_SPI_LOCK(sc); rk_spi_hw_setup(sc, mode, clock); rk_spi_enable_chip(sc, 1); err = rk_spi_set_cs(sc, cs, true); if (err != 0) { rk_spi_enable_chip(sc, 0); RK_SPI_UNLOCK(sc); return (err); } /* Transfer command then data bytes. */ err = 0; if (cmd->tx_cmd_sz > 0) err = rk_spi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd, cmd->tx_cmd_sz); if (cmd->tx_data_sz > 0 && err == 0) err = rk_spi_xfer_buf(sc, cmd->rx_data, cmd->tx_data, cmd->tx_data_sz); rk_spi_set_cs(sc, cs, false); rk_spi_enable_chip(sc, 0); RK_SPI_UNLOCK(sc); return (err); } static device_method_t rk_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_spi_probe), DEVMETHOD(device_attach, rk_spi_attach), DEVMETHOD(device_detach, rk_spi_detach), /* spibus_if */ DEVMETHOD(spibus_transfer, rk_spi_transfer), /* ofw_bus_if */ DEVMETHOD(ofw_bus_get_node, rk_spi_get_node), DEVMETHOD_END }; static driver_t rk_spi_driver = { "spi", rk_spi_methods, sizeof(struct rk_spi_softc), }; DRIVER_MODULE(rk_spi, simplebus, rk_spi_driver, 0, 0); DRIVER_MODULE(ofw_spibus, rk_spi, ofw_spibus_driver, 0, 0); MODULE_DEPEND(rk_spi, ofw_spibus, 1, 1, 1); OFWBUS_PNP_INFO(compat_data); diff --git a/sys/dev/uart/uart_dev_snps.c b/sys/dev/uart/uart_dev_snps.c index fb5894344927..c3ada581c7cd 100644 --- a/sys/dev/uart/uart_dev_snps.c +++ b/sys/dev/uart/uart_dev_snps.c @@ -1,285 +1,285 @@ /*- * Copyright (c) 2016 Jared McNeill * * 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 ``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 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 #include #include -#include +#include #include "uart_if.h" struct snps_softc { struct ns8250_softc ns8250; clk_t baudclk; clk_t apb_pclk; hwreset_t reset; }; /* * To use early printf on 64 bits Allwinner SoC, add to kernel config * options SOCDEV_PA=0x0 * options SOCDEV_VA=0x40000000 * options EARLY_PRINTF * * To use early printf on 32 bits Allwinner SoC, add to kernel config * options SOCDEV_PA=0x01C00000 * options SOCDEV_VA=0x10000000 * options EARLY_PRINTF * * remove the if 0 */ #if 0 #ifdef EARLY_PRINTF static void uart_snps_early_putc(int c) { volatile uint32_t *stat; volatile uint32_t *tx; #ifdef ALLWINNER_64 stat = (uint32_t *) (SOCDEV_VA + 0x1C2807C); tx = (uint32_t *) (SOCDEV_VA + 0x1C28000); #endif #ifdef ALLWINNER_32 stat = (uint32_t *) (SOCDEV_VA + 0x2807C); tx = (uint32_t *) (SOCDEV_VA + 0x28000); #endif while ((*stat & (1 << 2)) == 0) continue; *tx = c; } early_putc_t *early_putc = uart_snps_early_putc; #endif /* EARLY_PRINTF */ #endif static kobj_method_t snps_methods[] = { KOBJMETHOD(uart_probe, ns8250_bus_probe), KOBJMETHOD(uart_attach, ns8250_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, ns8250_bus_grab), KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), KOBJMETHOD_END }; struct uart_class uart_snps_class = { "snps", snps_methods, sizeof(struct snps_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = 0, }; static struct ofw_compat_data compat_data[] = { { "snps,dw-apb-uart", (uintptr_t)&uart_snps_class }, { "marvell,armada-38x-uart", (uintptr_t)&uart_snps_class }, { NULL, (uintptr_t)NULL } }; UART_FDT_CLASS(compat_data); static int snps_get_clocks(device_t dev, clk_t *baudclk, clk_t *apb_pclk) { *baudclk = NULL; *apb_pclk = NULL; /* Baud clock is either named "baudclk", or there is a single * unnamed clock. */ if (clk_get_by_ofw_name(dev, 0, "baudclk", baudclk) != 0 && clk_get_by_ofw_index(dev, 0, 0, baudclk) != 0) return (ENOENT); /* APB peripheral clock is optional */ (void)clk_get_by_ofw_name(dev, 0, "apb_pclk", apb_pclk); return (0); } static int snps_probe(device_t dev) { struct snps_softc *sc; struct uart_class *uart_class; phandle_t node; uint32_t shift, iowidth, clock; uint64_t freq; int error; clk_t baudclk, apb_pclk; hwreset_t reset; if (!ofw_bus_status_okay(dev)) return (ENXIO); uart_class = (struct uart_class *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (uart_class == NULL) return (ENXIO); freq = 0; sc = device_get_softc(dev); sc->ns8250.base.sc_class = uart_class; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "reg-shift", &shift, sizeof(shift)) <= 0) shift = 0; if (OF_getencprop(node, "reg-io-width", &iowidth, sizeof(iowidth)) <= 0) iowidth = 1; if (OF_getencprop(node, "clock-frequency", &clock, sizeof(clock)) <= 0) clock = 0; if (hwreset_get_by_ofw_idx(dev, 0, 0, &reset) == 0) { error = hwreset_deassert(reset); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); return (error); } } if (snps_get_clocks(dev, &baudclk, &apb_pclk) == 0) { error = clk_enable(baudclk); if (error != 0) { device_printf(dev, "cannot enable baud clock\n"); return (error); } if (apb_pclk != NULL) { error = clk_enable(apb_pclk); if (error != 0) { device_printf(dev, "cannot enable peripheral clock\n"); return (error); } } if (clock == 0) { error = clk_get_freq(baudclk, &freq); if (error != 0) { device_printf(dev, "cannot get frequency\n"); return (error); } clock = (uint32_t)freq; } } if (bootverbose && clock == 0) device_printf(dev, "could not determine frequency\n"); error = uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, UART_F_BUSY_DETECT); if (error > 0) return (error); /* XXX uart_bus_probe has changed the softc, so refresh it */ sc = device_get_softc(dev); /* Store clock and reset handles for detach */ sc->baudclk = baudclk; sc->apb_pclk = apb_pclk; sc->reset = reset; return (BUS_PROBE_VENDOR); } static int snps_detach(device_t dev) { struct snps_softc *sc; clk_t baudclk, apb_pclk; hwreset_t reset; int error; sc = device_get_softc(dev); baudclk = sc->baudclk; apb_pclk = sc->apb_pclk; reset = sc->reset; error = uart_bus_detach(dev); if (error != 0) return (error); if (reset != NULL) { error = hwreset_assert(reset); if (error != 0) { device_printf(dev, "cannot assert reset\n"); return (error); } hwreset_release(reset); } if (apb_pclk != NULL) { error = clk_release(apb_pclk); if (error != 0) { device_printf(dev, "cannot release peripheral clock\n"); return (error); } } if (baudclk != NULL) { error = clk_release(baudclk); if (error != 0) { device_printf(dev, "cannot release baud clock\n"); return (error); } } return (0); } static device_method_t snps_bus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, snps_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, snps_detach), DEVMETHOD_END }; static driver_t snps_uart_driver = { uart_driver_name, snps_bus_methods, sizeof(struct snps_softc) }; DRIVER_MODULE(uart_snps, simplebus, snps_uart_driver, 0, 0); diff --git a/sys/dev/usb/controller/dwc3/aw_dwc3.c b/sys/dev/usb/controller/dwc3/aw_dwc3.c index 7f2869933ee5..67331f2f1be9 100644 --- a/sys/dev/usb/controller/dwc3/aw_dwc3.c +++ b/sys/dev/usb/controller/dwc3/aw_dwc3.c @@ -1,141 +1,141 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Emmanuel Vadot * * 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. */ /* * Rockchip DWC3 glue */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include static struct ofw_compat_data compat_data[] = { { "allwinner,sun50i-h6-dwc3", 1 }, { NULL, 0 } }; struct aw_dwc3_softc { struct simplebus_softc sc; device_t dev; clk_t clk_bus; hwreset_t rst_bus; }; static int aw_dwc3_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* Binding says that we need a child node for the actual dwc3 controller */ node = ofw_bus_get_node(dev); if (OF_child(node) <= 0) return (ENXIO); device_set_desc(dev, "Allwinner H6 DWC3"); return (BUS_PROBE_DEFAULT); } static int aw_dwc3_attach(device_t dev) { struct aw_dwc3_softc *sc; device_t cdev; phandle_t node, child; int err; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); /* Enable the clocks */ if (clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_bus) != 0) { device_printf(dev, "Cannot get bus clock\n"); return (ENXIO); } err = clk_enable(sc->clk_bus); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_bus)); return (ENXIO); } /* Put module out of reset */ if (hwreset_get_by_ofw_name(dev, node, "bus", &sc->rst_bus) == 0) { if (hwreset_deassert(sc->rst_bus) != 0) { device_printf(dev, "Cannot deassert reset\n"); return (ENXIO); } } simplebus_init(dev, node); if (simplebus_fill_ranges(node, &sc->sc) < 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } for (child = OF_child(node); child > 0; child = OF_peer(child)) { cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); if (cdev != NULL) device_probe_and_attach(cdev); } return (bus_generic_attach(dev)); } static device_method_t aw_dwc3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_dwc3_probe), DEVMETHOD(device_attach, aw_dwc3_attach), DEVMETHOD_END }; DEFINE_CLASS_1(aw_dwc3, aw_dwc3_driver, aw_dwc3_methods, sizeof(struct aw_dwc3_softc), simplebus_driver); DRIVER_MODULE(aw_dwc3, simplebus, aw_dwc3_driver, 0, 0); diff --git a/sys/dev/usb/controller/dwc3/rk_dwc3.c b/sys/dev/usb/controller/dwc3/rk_dwc3.c index f0cec78a6f22..b34ec4880895 100644 --- a/sys/dev/usb/controller/dwc3/rk_dwc3.c +++ b/sys/dev/usb/controller/dwc3/rk_dwc3.c @@ -1,198 +1,198 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Emmanuel Vadot * * 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. */ /* * Rockchip DWC3 glue */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include enum rk_dwc3_type { RK3399 = 1, }; static struct ofw_compat_data compat_data[] = { { "rockchip,rk3399-dwc3", RK3399 }, { NULL, 0 } }; struct rk_dwc3_softc { struct simplebus_softc sc; device_t dev; clk_t clk_ref; clk_t clk_suspend; clk_t clk_bus; clk_t clk_axi_perf; clk_t clk_usb3; clk_t clk_grf; hwreset_t rst_usb3; enum rk_dwc3_type type; }; static int rk_dwc3_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* Binding says that we need a child node for the actual dwc3 controller */ node = ofw_bus_get_node(dev); if (OF_child(node) <= 0) return (ENXIO); device_set_desc(dev, "Rockchip RK3399 DWC3"); return (BUS_PROBE_DEFAULT); } static int rk_dwc3_attach(device_t dev) { struct rk_dwc3_softc *sc; device_t cdev; phandle_t node, child; int err; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Mandatory clocks */ if (clk_get_by_ofw_name(dev, 0, "ref_clk", &sc->clk_ref) != 0) { device_printf(dev, "Cannot get ref_clk clock\n"); return (ENXIO); } err = clk_enable(sc->clk_ref); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_ref)); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "suspend_clk", &sc->clk_suspend) != 0) { device_printf(dev, "Cannot get suspend_clk clock\n"); return (ENXIO); } err = clk_enable(sc->clk_suspend); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_suspend)); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "bus_clk", &sc->clk_bus) != 0) { device_printf(dev, "Cannot get bus_clk clock\n"); return (ENXIO); } err = clk_enable(sc->clk_bus); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_bus)); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "grf_clk", &sc->clk_grf) == 0) { err = clk_enable(sc->clk_grf); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_grf)); return (ENXIO); } } /* Optional clocks */ if (clk_get_by_ofw_name(dev, 0, "aclk_usb3_rksoc_axi_perf", &sc->clk_axi_perf) == 0) { err = clk_enable(sc->clk_axi_perf); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_axi_perf)); return (ENXIO); } } if (clk_get_by_ofw_name(dev, 0, "aclk_usb3", &sc->clk_usb3) == 0) { err = clk_enable(sc->clk_usb3); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk_usb3)); return (ENXIO); } } /* Put module out of reset */ if (hwreset_get_by_ofw_name(dev, node, "usb3-otg", &sc->rst_usb3) == 0) { if (hwreset_deassert(sc->rst_usb3) != 0) { device_printf(dev, "Cannot deassert reset\n"); return (ENXIO); } } simplebus_init(dev, node); if (simplebus_fill_ranges(node, &sc->sc) < 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } for (child = OF_child(node); child > 0; child = OF_peer(child)) { cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); if (cdev != NULL) device_probe_and_attach(cdev); } return (bus_generic_attach(dev)); } static device_method_t rk_dwc3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_dwc3_probe), DEVMETHOD(device_attach, rk_dwc3_attach), DEVMETHOD_END }; DEFINE_CLASS_1(rk_dwc3, rk_dwc3_driver, rk_dwc3_methods, sizeof(struct rk_dwc3_softc), simplebus_driver); DRIVER_MODULE(rk_dwc3, simplebus, rk_dwc3_driver, 0, 0); diff --git a/sys/dev/usb/controller/generic_ehci_fdt.c b/sys/dev/usb/controller/generic_ehci_fdt.c index 8f9558c6636a..e88895d20664 100644 --- a/sys/dev/usb/controller/generic_ehci_fdt.c +++ b/sys/dev/usb/controller/generic_ehci_fdt.c @@ -1,239 +1,239 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * Copyright (c) 2016 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * 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 "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "generic_ehci.h" struct clk_list { TAILQ_ENTRY(clk_list) next; clk_t clk; }; struct hwrst_list { TAILQ_ENTRY(hwrst_list) next; hwreset_t rst; }; struct phy_list { TAILQ_ENTRY(phy_list) next; phy_t phy; }; struct generic_ehci_fdt_softc { ehci_softc_t ehci_sc; TAILQ_HEAD(, clk_list) clk_list; TAILQ_HEAD(, hwrst_list) rst_list; TAILQ_HEAD(, phy_list) phy_list; }; static device_probe_t generic_ehci_fdt_probe; static device_attach_t generic_ehci_fdt_attach; static device_detach_t generic_ehci_fdt_detach; static int generic_ehci_fdt_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); if (!ofw_bus_is_compatible(self, "generic-ehci")) return (ENXIO); device_set_desc(self, "Generic EHCI Controller"); return (BUS_PROBE_DEFAULT); } static int generic_ehci_fdt_attach(device_t dev) { int err; struct generic_ehci_fdt_softc *sc; struct clk_list *clkp; clk_t clk; struct hwrst_list *rstp; hwreset_t rst; struct phy_list *phyp; phy_t phy; int off; sc = device_get_softc(dev); TAILQ_INIT(&sc->clk_list); /* Enable clock */ for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) { err = clk_enable(clk); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(clk)); goto error; } clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); clkp->clk = clk; TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); } /* De-assert reset */ TAILQ_INIT(&sc->rst_list); for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) { err = hwreset_deassert(rst); if (err != 0) { device_printf(dev, "Could not de-assert reset\n"); goto error; } rstp = malloc(sizeof(*rstp), M_DEVBUF, M_WAITOK | M_ZERO); rstp->rst = rst; TAILQ_INSERT_TAIL(&sc->rst_list, rstp, next); } /* Enable USB PHY */ TAILQ_INIT(&sc->phy_list); for (off = 0; phy_get_by_ofw_idx(dev, 0, off, &phy) == 0; off++) { err = phy_usb_set_mode(phy, PHY_USB_MODE_HOST); if (err != 0) { device_printf(dev, "Could not set phy to host mode\n"); goto error; } err = phy_enable(phy); if (err != 0) { device_printf(dev, "Could not enable phy\n"); goto error; } phyp = malloc(sizeof(*phyp), M_DEVBUF, M_WAITOK | M_ZERO); phyp->phy = phy; TAILQ_INSERT_TAIL(&sc->phy_list, phyp, next); } err = generic_ehci_attach(dev); if (err != 0) goto error; return (0); error: generic_ehci_fdt_detach(dev); return (err); } static int generic_ehci_fdt_detach(device_t dev) { struct generic_ehci_fdt_softc *sc; struct clk_list *clk, *clk_tmp; struct hwrst_list *rst, *rst_tmp; struct phy_list *phy, *phy_tmp; int err; err = generic_ehci_detach(dev); if (err != 0) return (err); sc = device_get_softc(dev); /* Disable clock */ TAILQ_FOREACH_SAFE(clk, &sc->clk_list, next, clk_tmp) { err = clk_disable(clk->clk); if (err != 0) device_printf(dev, "Could not disable clock %s\n", clk_get_name(clk->clk)); err = clk_release(clk->clk); if (err != 0) device_printf(dev, "Could not release clock %s\n", clk_get_name(clk->clk)); TAILQ_REMOVE(&sc->clk_list, clk, next); free(clk, M_DEVBUF); } /* Assert reset */ TAILQ_FOREACH_SAFE(rst, &sc->rst_list, next, rst_tmp) { hwreset_assert(rst->rst); hwreset_release(rst->rst); TAILQ_REMOVE(&sc->rst_list, rst, next); free(rst, M_DEVBUF); } /* Disable phys */ TAILQ_FOREACH_SAFE(phy, &sc->phy_list, next, phy_tmp) { err = phy_disable(phy->phy); if (err != 0) device_printf(dev, "Could not disable phy\n"); phy_release(phy->phy); TAILQ_REMOVE(&sc->phy_list, phy, next); free(phy, M_DEVBUF); } return (0); } static device_method_t ehci_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, generic_ehci_fdt_probe), DEVMETHOD(device_attach, generic_ehci_fdt_attach), DEVMETHOD(device_detach, generic_ehci_fdt_detach), DEVMETHOD_END }; DEFINE_CLASS_1(ehci, ehci_fdt_driver, ehci_fdt_methods, sizeof(ehci_softc_t), generic_ehci_driver); DRIVER_MODULE(generic_ehci, simplebus, ehci_fdt_driver, 0, 0); MODULE_DEPEND(generic_ehci, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/generic_ohci.c b/sys/dev/usb/controller/generic_ohci.c index efedc92ebfb9..b71c269cfdcd 100644 --- a/sys/dev/usb/controller/generic_ohci.c +++ b/sys/dev/usb/controller/generic_ohci.c @@ -1,328 +1,328 @@ /*- * Copyright (c) 2016 Emmanuel Vadot All rights reserved. * Copyright (c) 2006 M. Warner Losh * * 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. */ /* * Generic OHCI driver based on AT91 OHCI */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "generic_usb_if.h" struct clk_list { TAILQ_ENTRY(clk_list) next; clk_t clk; }; struct phy_list { TAILQ_ENTRY(phy_list) next; phy_t phy; }; struct hwrst_list { TAILQ_ENTRY(hwrst_list) next; hwreset_t rst; }; struct generic_ohci_softc { ohci_softc_t ohci_sc; TAILQ_HEAD(, clk_list) clk_list; TAILQ_HEAD(, phy_list) phy_list; TAILQ_HEAD(, hwrst_list) rst_list; }; static int generic_ohci_detach(device_t); static int generic_ohci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "generic-ohci")) return (ENXIO); device_set_desc(dev, "Generic OHCI Controller"); return (BUS_PROBE_DEFAULT); } static int generic_ohci_attach(device_t dev) { struct generic_ohci_softc *sc = device_get_softc(dev); int err, rid; int off; struct clk_list *clkp; struct phy_list *phyp; struct hwrst_list *rstp; clk_t clk; phy_t phy; hwreset_t rst; sc->ohci_sc.sc_bus.parent = dev; sc->ohci_sc.sc_bus.devices = sc->ohci_sc.sc_devices; sc->ohci_sc.sc_bus.devices_max = OHCI_MAX_DEVICES; sc->ohci_sc.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->ohci_sc.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } rid = 0; sc->ohci_sc.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->ohci_sc.sc_io_res == 0) { err = ENOMEM; goto error; } sc->ohci_sc.sc_io_tag = rman_get_bustag(sc->ohci_sc.sc_io_res); sc->ohci_sc.sc_io_hdl = rman_get_bushandle(sc->ohci_sc.sc_io_res); sc->ohci_sc.sc_io_size = rman_get_size(sc->ohci_sc.sc_io_res); rid = 0; sc->ohci_sc.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->ohci_sc.sc_irq_res == 0) { err = ENXIO; goto error; } sc->ohci_sc.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->ohci_sc.sc_bus.bdev == 0) { err = ENXIO; goto error; } device_set_ivars(sc->ohci_sc.sc_bus.bdev, &sc->ohci_sc.sc_bus); strlcpy(sc->ohci_sc.sc_vendor, "Generic", sizeof(sc->ohci_sc.sc_vendor)); err = bus_setup_intr(dev, sc->ohci_sc.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->ohci_sc.sc_intr_hdl); if (err) { sc->ohci_sc.sc_intr_hdl = NULL; goto error; } TAILQ_INIT(&sc->clk_list); /* Enable clock */ for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) { err = clk_enable(clk); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(clk)); goto error; } clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); clkp->clk = clk; TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); } /* De-assert reset */ TAILQ_INIT(&sc->rst_list); for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) { err = hwreset_deassert(rst); if (err != 0) { device_printf(dev, "Could not de-assert reset\n"); goto error; } rstp = malloc(sizeof(*rstp), M_DEVBUF, M_WAITOK | M_ZERO); rstp->rst = rst; TAILQ_INSERT_TAIL(&sc->rst_list, rstp, next); } /* Enable phy */ TAILQ_INIT(&sc->phy_list); for (off = 0; phy_get_by_ofw_idx(dev, 0, off, &phy) == 0; off++) { err = phy_usb_set_mode(phy, PHY_USB_MODE_HOST); if (err != 0) { device_printf(dev, "Could not set phy to host mode\n"); goto error; } err = phy_enable(phy); if (err != 0) { device_printf(dev, "Could not enable phy\n"); goto error; } phyp = malloc(sizeof(*phyp), M_DEVBUF, M_WAITOK | M_ZERO); phyp->phy = phy; TAILQ_INSERT_TAIL(&sc->phy_list, phyp, next); } if (GENERIC_USB_INIT(dev) != 0) { err = ENXIO; goto error; } err = ohci_init(&sc->ohci_sc); if (err == 0) err = device_probe_and_attach(sc->ohci_sc.sc_bus.bdev); if (err) goto error; return (0); error: generic_ohci_detach(dev); return (err); } static int generic_ohci_detach(device_t dev) { struct generic_ohci_softc *sc = device_get_softc(dev); int err; struct clk_list *clk, *clk_tmp; struct phy_list *phy, *phy_tmp; struct hwrst_list *rst, *rst_tmp; /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->ohci_sc.sc_io_tag, sc->ohci_sc.sc_io_hdl, OHCI_CONTROL, 0); if (sc->ohci_sc.sc_irq_res && sc->ohci_sc.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->ohci_sc); err = bus_teardown_intr(dev, sc->ohci_sc.sc_irq_res, sc->ohci_sc.sc_intr_hdl); sc->ohci_sc.sc_intr_hdl = NULL; } if (sc->ohci_sc.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ohci_sc.sc_irq_res); sc->ohci_sc.sc_irq_res = NULL; } if (sc->ohci_sc.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->ohci_sc.sc_io_res); sc->ohci_sc.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->ohci_sc.sc_bus, &ohci_iterate_hw_softc); /* Disable phy */ TAILQ_FOREACH_SAFE(phy, &sc->phy_list, next, phy_tmp) { err = phy_disable(phy->phy); if (err != 0) device_printf(dev, "Could not disable phy\n"); phy_release(phy->phy); TAILQ_REMOVE(&sc->phy_list, phy, next); free(phy, M_DEVBUF); } /* Assert reset */ TAILQ_FOREACH_SAFE(rst, &sc->rst_list, next, rst_tmp) { hwreset_assert(rst->rst); hwreset_release(rst->rst); TAILQ_REMOVE(&sc->rst_list, rst, next); free(rst, M_DEVBUF); } /* Disable clock */ TAILQ_FOREACH_SAFE(clk, &sc->clk_list, next, clk_tmp) { err = clk_disable(clk->clk); if (err != 0) device_printf(dev, "Could not disable clock %s\n", clk_get_name(clk->clk)); err = clk_release(clk->clk); if (err != 0) device_printf(dev, "Could not release clock %s\n", clk_get_name(clk->clk)); TAILQ_REMOVE(&sc->clk_list, clk, next); free(clk, M_DEVBUF); } if (GENERIC_USB_DEINIT(dev) != 0) return (ENXIO); return (0); } static device_method_t generic_ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, generic_ohci_probe), DEVMETHOD(device_attach, generic_ohci_attach), DEVMETHOD(device_detach, generic_ohci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; driver_t generic_ohci_driver = { .name = "ohci", .methods = generic_ohci_methods, .size = sizeof(struct generic_ohci_softc), }; DRIVER_MODULE(ohci, simplebus, generic_ohci_driver, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/musb_otg_allwinner.c b/sys/dev/usb/controller/musb_otg_allwinner.c index 4e630ac431e8..a8961bed6385 100644 --- a/sys/dev/usb/controller/musb_otg_allwinner.c +++ b/sys/dev/usb/controller/musb_otg_allwinner.c @@ -1,621 +1,621 @@ /*- * Copyright (c) 2016 Jared McNeill * Copyright (c) 2018 Andrew Turner * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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 ``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 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. */ /* * Allwinner USB Dual-Role Device (DRD) controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #ifdef __arm__ #include #include #endif #define DRD_EP_MAX 5 #define DRD_EP_MAX_H3 4 #define MUSB2_REG_AWIN_VEND0 0x0043 #define VEND0_PIO_MODE 0 #if defined(__arm__) #define bs_parent_space(bs) ((bs)->bs_parent) typedef bus_space_tag_t awusb_bs_tag; #elif defined(__aarch64__) #define bs_parent_space(bs) (bs) typedef void * awusb_bs_tag; #endif #define AWUSB_OKAY 0x01 #define AWUSB_NO_CONFDATA 0x02 static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-musb", AWUSB_OKAY }, { "allwinner,sun6i-a31-musb", AWUSB_OKAY }, { "allwinner,sun8i-a33-musb", AWUSB_OKAY | AWUSB_NO_CONFDATA }, { "allwinner,sun8i-h3-musb", AWUSB_OKAY | AWUSB_NO_CONFDATA }, { NULL, 0 } }; static const struct musb_otg_ep_cfg musbotg_ep_allwinner[] = { { .ep_end = DRD_EP_MAX, .ep_fifosz_shift = 9, .ep_fifosz_reg = MUSB2_VAL_FIFOSZ_512, }, { .ep_end = -1, }, }; static const struct musb_otg_ep_cfg musbotg_ep_allwinner_h3[] = { { .ep_end = DRD_EP_MAX_H3, .ep_fifosz_shift = 9, .ep_fifosz_reg = MUSB2_VAL_FIFOSZ_512, }, { .ep_end = -1, }, }; struct awusbdrd_softc { struct musbotg_softc sc; struct resource *res[2]; clk_t clk; hwreset_t reset; phy_t phy; struct bus_space bs; int flags; }; static struct resource_spec awusbdrd_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; #define REMAPFLAG 0x8000 #define REGDECL(a, b) [(a)] = ((b) | REMAPFLAG) /* Allwinner USB DRD register mappings */ static const uint16_t awusbdrd_regmap[] = { REGDECL(MUSB2_REG_EPFIFO(0), 0x0000), REGDECL(MUSB2_REG_EPFIFO(1), 0x0004), REGDECL(MUSB2_REG_EPFIFO(2), 0x0008), REGDECL(MUSB2_REG_EPFIFO(3), 0x000c), REGDECL(MUSB2_REG_EPFIFO(4), 0x0010), REGDECL(MUSB2_REG_EPFIFO(5), 0x0014), REGDECL(MUSB2_REG_POWER, 0x0040), REGDECL(MUSB2_REG_DEVCTL, 0x0041), REGDECL(MUSB2_REG_EPINDEX, 0x0042), REGDECL(MUSB2_REG_INTTX, 0x0044), REGDECL(MUSB2_REG_INTRX, 0x0046), REGDECL(MUSB2_REG_INTTXE, 0x0048), REGDECL(MUSB2_REG_INTRXE, 0x004a), REGDECL(MUSB2_REG_INTUSB, 0x004c), REGDECL(MUSB2_REG_INTUSBE, 0x0050), REGDECL(MUSB2_REG_FRAME, 0x0054), REGDECL(MUSB2_REG_TESTMODE, 0x007c), REGDECL(MUSB2_REG_TXMAXP, 0x0080), REGDECL(MUSB2_REG_TXCSRL, 0x0082), REGDECL(MUSB2_REG_TXCSRH, 0x0083), REGDECL(MUSB2_REG_RXMAXP, 0x0084), REGDECL(MUSB2_REG_RXCSRL, 0x0086), REGDECL(MUSB2_REG_RXCSRH, 0x0087), REGDECL(MUSB2_REG_RXCOUNT, 0x0088), REGDECL(MUSB2_REG_TXTI, 0x008c), REGDECL(MUSB2_REG_TXNAKLIMIT, 0x008d), REGDECL(MUSB2_REG_RXNAKLIMIT, 0x008f), REGDECL(MUSB2_REG_RXTI, 0x008e), REGDECL(MUSB2_REG_TXFIFOSZ, 0x0090), REGDECL(MUSB2_REG_TXFIFOADD, 0x0092), REGDECL(MUSB2_REG_RXFIFOSZ, 0x0094), REGDECL(MUSB2_REG_RXFIFOADD, 0x0096), REGDECL(MUSB2_REG_FADDR, 0x0098), REGDECL(MUSB2_REG_TXFADDR(0), 0x0098), REGDECL(MUSB2_REG_TXHADDR(0), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(0), 0x009b), REGDECL(MUSB2_REG_RXFADDR(0), 0x009c), REGDECL(MUSB2_REG_RXHADDR(0), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(0), 0x009f), REGDECL(MUSB2_REG_TXFADDR(1), 0x0098), REGDECL(MUSB2_REG_TXHADDR(1), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(1), 0x009b), REGDECL(MUSB2_REG_RXFADDR(1), 0x009c), REGDECL(MUSB2_REG_RXHADDR(1), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(1), 0x009f), REGDECL(MUSB2_REG_TXFADDR(2), 0x0098), REGDECL(MUSB2_REG_TXHADDR(2), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(2), 0x009b), REGDECL(MUSB2_REG_RXFADDR(2), 0x009c), REGDECL(MUSB2_REG_RXHADDR(2), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(2), 0x009f), REGDECL(MUSB2_REG_TXFADDR(3), 0x0098), REGDECL(MUSB2_REG_TXHADDR(3), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(3), 0x009b), REGDECL(MUSB2_REG_RXFADDR(3), 0x009c), REGDECL(MUSB2_REG_RXHADDR(3), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(3), 0x009f), REGDECL(MUSB2_REG_TXFADDR(4), 0x0098), REGDECL(MUSB2_REG_TXHADDR(4), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(4), 0x009b), REGDECL(MUSB2_REG_RXFADDR(4), 0x009c), REGDECL(MUSB2_REG_RXHADDR(4), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(4), 0x009f), REGDECL(MUSB2_REG_TXFADDR(5), 0x0098), REGDECL(MUSB2_REG_TXHADDR(5), 0x009a), REGDECL(MUSB2_REG_TXHUBPORT(5), 0x009b), REGDECL(MUSB2_REG_RXFADDR(5), 0x009c), REGDECL(MUSB2_REG_RXHADDR(5), 0x009e), REGDECL(MUSB2_REG_RXHUBPORT(5), 0x009f), REGDECL(MUSB2_REG_CONFDATA, 0x00c0), }; static bus_size_t awusbdrd_reg(bus_size_t o) { bus_size_t v; KASSERT(o < nitems(awusbdrd_regmap), ("%s: Invalid register %#lx", __func__, o)); if (o >= nitems(awusbdrd_regmap)) return (o); v = awusbdrd_regmap[o]; KASSERT((v & REMAPFLAG) != 0, ("%s: reg %#lx not in regmap", __func__, o)); return (v & ~REMAPFLAG); } static int awusbdrd_filt(bus_size_t o) { switch (o) { case MUSB2_REG_MISC: case MUSB2_REG_RXDBDIS: case MUSB2_REG_TXDBDIS: return (1); default: return (0); } } static uint8_t awusbdrd_bs_r_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) { struct bus_space *bs = t; switch (o) { case MUSB2_REG_HWVERS: return (0); /* no known equivalent */ } return (bus_space_read_1(bs_parent_space(bs), h, awusbdrd_reg(o))); } static uint8_t awusbdrd_bs_r_1_noconf(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) { /* * There is no confdata register on some SoCs, return the same * magic value as Linux. */ if (o == MUSB2_REG_CONFDATA) return (0xde); return (awusbdrd_bs_r_1(t, h, o)); } static uint16_t awusbdrd_bs_r_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) { struct bus_space *bs = t; if (awusbdrd_filt(o) != 0) return (0); return bus_space_read_2(bs_parent_space(bs), h, awusbdrd_reg(o)); } static void awusbdrd_bs_w_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, uint8_t v) { struct bus_space *bs = t; if (awusbdrd_filt(o) != 0) return; bus_space_write_1(bs_parent_space(bs), h, awusbdrd_reg(o), v); } static void awusbdrd_bs_w_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, uint16_t v) { struct bus_space *bs = t; if (awusbdrd_filt(o) != 0) return; bus_space_write_2(bs_parent_space(bs), h, awusbdrd_reg(o), v); } static void awusbdrd_bs_rm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, uint8_t *d, bus_size_t c) { struct bus_space *bs = t; bus_space_read_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); } static void awusbdrd_bs_rm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, uint32_t *d, bus_size_t c) { struct bus_space *bs = t; bus_space_read_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); } static void awusbdrd_bs_wm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, const uint8_t *d, bus_size_t c) { struct bus_space *bs = t; if (awusbdrd_filt(o) != 0) return; bus_space_write_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); } static void awusbdrd_bs_wm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, const uint32_t *d, bus_size_t c) { struct bus_space *bs = t; if (awusbdrd_filt(o) != 0) return; bus_space_write_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); } static void awusbdrd_intr(void *arg) { struct awusbdrd_softc *sc = arg; uint8_t intusb; uint16_t inttx, intrx; intusb = MUSB2_READ_1(&sc->sc, MUSB2_REG_INTUSB); inttx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTTX); intrx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTRX); if (intusb == 0 && inttx == 0 && intrx == 0) return; if (intusb) MUSB2_WRITE_1(&sc->sc, MUSB2_REG_INTUSB, intusb); if (inttx) MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTTX, inttx); if (intrx) MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTRX, intrx); musbotg_interrupt(arg, intrx, inttx, intusb); } static int awusbdrd_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner USB DRD"); return (BUS_PROBE_DEFAULT); } static int awusbdrd_attach(device_t dev) { char usb_mode[24]; struct awusbdrd_softc *sc; uint8_t musb_mode; int phy_mode; int error; sc = device_get_softc(dev); sc->flags = ofw_bus_search_compatible(dev, compat_data)->ocd_data; error = bus_alloc_resources(dev, awusbdrd_spec, sc->res); if (error != 0) return (error); musb_mode = MUSB2_HOST_MODE; /* default */ phy_mode = PHY_USB_MODE_HOST; if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", &usb_mode, sizeof(usb_mode)) > 0) { usb_mode[sizeof(usb_mode) - 1] = 0; if (strcasecmp(usb_mode, "host") == 0) { musb_mode = MUSB2_HOST_MODE; phy_mode = PHY_USB_MODE_HOST; } else if (strcasecmp(usb_mode, "peripheral") == 0) { musb_mode = MUSB2_DEVICE_MODE; phy_mode = PHY_USB_MODE_DEVICE; } else if (strcasecmp(usb_mode, "otg") == 0) { /* * XXX phy has PHY_USB_MODE_OTG, but MUSB does not have * it. It's not clear how to propagate mode changes * from phy layer (that detects them) to MUSB. */ musb_mode = MUSB2_DEVICE_MODE; phy_mode = PHY_USB_MODE_DEVICE; } else { device_printf(dev, "Invalid FDT dr_mode: %s\n", usb_mode); } } /* AHB gate clock is required */ error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (error != 0) goto fail; /* AHB reset is only present on some SoCs */ (void)hwreset_get_by_ofw_idx(dev, 0, 0, &sc->reset); /* Enable clocks */ error = clk_enable(sc->clk); if (error != 0) { device_printf(dev, "failed to enable clock: %d\n", error); goto fail; } if (sc->reset != NULL) { error = hwreset_deassert(sc->reset); if (error != 0) { device_printf(dev, "failed to de-assert reset: %d\n", error); goto fail; } } /* XXX not sure if this is universally needed. */ (void)phy_get_by_ofw_name(dev, 0, "usb", &sc->phy); if (sc->phy != NULL) { device_printf(dev, "setting phy mode %d\n", phy_mode); if (musb_mode == MUSB2_HOST_MODE) { error = phy_enable(sc->phy); if (error != 0) { device_printf(dev, "Could not enable phy\n"); goto fail; } } error = phy_usb_set_mode(sc->phy, phy_mode); if (error != 0) { device_printf(dev, "Could not set phy mode\n"); goto fail; } } sc->sc.sc_bus.parent = dev; sc->sc.sc_bus.devices = sc->sc.sc_devices; sc->sc.sc_bus.devices_max = MUSB2_MAX_DEVICES; sc->sc.sc_bus.dma_bits = 32; error = usb_bus_mem_alloc_all(&sc->sc.sc_bus, USB_GET_DMA_TAG(dev), NULL); if (error != 0) { error = ENOMEM; goto fail; } #if defined(__arm__) sc->bs.bs_parent = rman_get_bustag(sc->res[0]); #elif defined(__aarch64__) sc->bs.bs_cookie = rman_get_bustag(sc->res[0]); #endif if ((sc->flags & AWUSB_NO_CONFDATA) == AWUSB_NO_CONFDATA) sc->bs.bs_r_1 = awusbdrd_bs_r_1_noconf; else sc->bs.bs_r_1 = awusbdrd_bs_r_1; sc->bs.bs_r_2 = awusbdrd_bs_r_2; sc->bs.bs_w_1 = awusbdrd_bs_w_1; sc->bs.bs_w_2 = awusbdrd_bs_w_2; sc->bs.bs_rm_1 = awusbdrd_bs_rm_1; sc->bs.bs_rm_4 = awusbdrd_bs_rm_4; sc->bs.bs_wm_1 = awusbdrd_bs_wm_1; sc->bs.bs_wm_4 = awusbdrd_bs_wm_4; sc->sc.sc_io_tag = &sc->bs; sc->sc.sc_io_hdl = rman_get_bushandle(sc->res[0]); sc->sc.sc_io_size = rman_get_size(sc->res[0]); sc->sc.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc.sc_bus.bdev == NULL) { error = ENXIO; goto fail; } device_set_ivars(sc->sc.sc_bus.bdev, &sc->sc.sc_bus); sc->sc.sc_id = 0; sc->sc.sc_platform_data = sc; sc->sc.sc_mode = musb_mode; if (ofw_bus_is_compatible(dev, "allwinner,sun8i-h3-musb")) { sc->sc.sc_ep_cfg = musbotg_ep_allwinner_h3; sc->sc.sc_ep_max = DRD_EP_MAX_H3; } else { sc->sc.sc_ep_cfg = musbotg_ep_allwinner; sc->sc.sc_ep_max = DRD_EP_MAX; } error = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_BIO, NULL, awusbdrd_intr, sc, &sc->sc.sc_intr_hdl); if (error != 0) goto fail; /* Enable PIO mode */ bus_write_1(sc->res[0], MUSB2_REG_AWIN_VEND0, VEND0_PIO_MODE); #ifdef __arm__ /* Map SRAMD area to USB0 (sun4i/sun7i only) */ switch (allwinner_soc_family()) { case ALLWINNERSOC_SUN4I: case ALLWINNERSOC_SUN7I: a10_map_to_otg(); break; } #endif error = musbotg_init(&sc->sc); if (error != 0) goto fail; error = device_probe_and_attach(sc->sc.sc_bus.bdev); if (error != 0) goto fail; musbotg_vbus_interrupt(&sc->sc, 1); /* XXX VBUS */ return (0); fail: if (sc->phy != NULL) { if (musb_mode == MUSB2_HOST_MODE) (void)phy_disable(sc->phy); phy_release(sc->phy); } if (sc->reset != NULL) { hwreset_assert(sc->reset); hwreset_release(sc->reset); } if (sc->clk != NULL) clk_release(sc->clk); bus_release_resources(dev, awusbdrd_spec, sc->res); return (error); } static int awusbdrd_detach(device_t dev) { struct awusbdrd_softc *sc; device_t bdev; int error; sc = device_get_softc(dev); if (sc->sc.sc_bus.bdev != NULL) { bdev = sc->sc.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } musbotg_uninit(&sc->sc); error = bus_teardown_intr(dev, sc->res[1], sc->sc.sc_intr_hdl); if (error != 0) return (error); usb_bus_mem_free_all(&sc->sc.sc_bus, NULL); if (sc->phy != NULL) { if (sc->sc.sc_mode == MUSB2_HOST_MODE) phy_disable(sc->phy); phy_release(sc->phy); } if (sc->reset != NULL) { if (hwreset_assert(sc->reset) != 0) device_printf(dev, "failed to assert reset\n"); hwreset_release(sc->reset); } if (sc->clk != NULL) clk_release(sc->clk); bus_release_resources(dev, awusbdrd_spec, sc->res); device_delete_children(dev); return (0); } static device_method_t awusbdrd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awusbdrd_probe), DEVMETHOD(device_attach, awusbdrd_attach), DEVMETHOD(device_detach, awusbdrd_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t awusbdrd_driver = { .name = "musbotg", .methods = awusbdrd_methods, .size = sizeof(struct awusbdrd_softc), }; DRIVER_MODULE(musbotg, simplebus, awusbdrd_driver, 0, 0); MODULE_DEPEND(musbotg, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/xlnx_dwc3.c b/sys/dev/usb/controller/xlnx_dwc3.c index df91d5e5bc3b..facc44823523 100644 --- a/sys/dev/usb/controller/xlnx_dwc3.c +++ b/sys/dev/usb/controller/xlnx_dwc3.c @@ -1,149 +1,149 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG * * 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. */ /* * Xilinx DWC3 glue */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include static struct ofw_compat_data compat_data[] = { { "xlnx,zynqmp-dwc3", 1 }, { NULL, 0 } }; struct xlnx_dwc3_softc { struct simplebus_softc sc; device_t dev; hwreset_t rst_crst; hwreset_t rst_hibrst; hwreset_t rst_apbrst; }; static int xlnx_dwc3_probe(device_t dev) { phandle_t node; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* Binding says that we need a child node for the actual dwc3 controller */ node = ofw_bus_get_node(dev); if (OF_child(node) <= 0) return (ENXIO); device_set_desc(dev, "Xilinx ZYNQMP DWC3"); return (BUS_PROBE_DEFAULT); } static int xlnx_dwc3_attach(device_t dev) { struct xlnx_dwc3_softc *sc; device_t cdev; phandle_t node, child; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); /* * Put module out of reset * Based on the bindings this should be mandatory to have * but reality shows that they aren't always there. * This is the case on the DTB in the AVnet Ultra96 */ if (hwreset_get_by_ofw_name(dev, node, "usb_crst", &sc->rst_crst) == 0) { if (hwreset_deassert(sc->rst_crst) != 0) { device_printf(dev, "Cannot deassert reset\n"); return (ENXIO); } } if (hwreset_get_by_ofw_name(dev, node, "usb_hibrst", &sc->rst_hibrst) == 0) { if (hwreset_deassert(sc->rst_hibrst) != 0) { device_printf(dev, "Cannot deassert reset\n"); return (ENXIO); } } if (hwreset_get_by_ofw_name(dev, node, "usb_apbrst", &sc->rst_apbrst) == 0) { if (hwreset_deassert(sc->rst_apbrst) != 0) { device_printf(dev, "Cannot deassert reset\n"); return (ENXIO); } } simplebus_init(dev, node); if (simplebus_fill_ranges(node, &sc->sc) < 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } for (child = OF_child(node); child > 0; child = OF_peer(child)) { cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); if (cdev != NULL) device_probe_and_attach(cdev); } return (bus_generic_attach(dev)); } static device_method_t xlnx_dwc3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xlnx_dwc3_probe), DEVMETHOD(device_attach, xlnx_dwc3_attach), DEVMETHOD_END }; DEFINE_CLASS_1(xlnx_dwc3, xlnx_dwc3_driver, xlnx_dwc3_methods, sizeof(struct xlnx_dwc3_softc), simplebus_driver); DRIVER_MODULE(xlnx_dwc3, simplebus, xlnx_dwc3_driver, 0, 0); diff --git a/sys/riscv/sifive/fu740_pci_dw.c b/sys/riscv/sifive/fu740_pci_dw.c index ff2078464379..13937e283042 100644 --- a/sys/riscv/sifive/fu740_pci_dw.c +++ b/sys/riscv/sifive/fu740_pci_dw.c @@ -1,460 +1,460 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2021 Jessica Clarke * * 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. * */ /* SiFive FU740 DesignWare PCIe driver */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pci_dw_if.h" #define FUDW_PHYS 2 #define FUDW_LANES_PER_PHY 4 #define FUDW_MGMT_PERST_N 0x0 #define FUDW_MGMT_LTSSM_EN 0x10 #define FUDW_MGMT_HOLD_PHY_RST 0x18 #define FUDW_MGMT_DEVICE_TYPE 0x708 #define FUDW_MGMT_DEVICE_TYPE_RC 0x4 #define FUDW_MGMT_PHY_CR_PARA_REG(_n, _r) \ (0x860 + (_n) * 0x40 + FUDW_MGMT_PHY_CR_PARA_##_r) #define FUDW_MGMT_PHY_CR_PARA_ADDR 0x0 #define FUDW_MGMT_PHY_CR_PARA_READ_EN 0x10 #define FUDW_MGMT_PHY_CR_PARA_READ_DATA 0x18 #define FUDW_MGMT_PHY_CR_PARA_SEL 0x20 #define FUDW_MGMT_PHY_CR_PARA_WRITE_DATA 0x28 #define FUDW_MGMT_PHY_CR_PARA_WRITE_EN 0x30 #define FUDW_MGMT_PHY_CR_PARA_ACK 0x38 #define FUDW_MGMT_PHY_LANE(_n) (0x1008 + (_n) * 0x100) #define FUDW_MGMT_PHY_LANE_CDR_TRACK_EN (1 << 0) #define FUDW_MGMT_PHY_LANE_LOS_THRESH (1 << 5) #define FUDW_MGMT_PHY_LANE_TERM_EN (1 << 9) #define FUDW_MGMT_PHY_LANE_TERM_ACDC (1 << 10) #define FUDW_MGMT_PHY_LANE_EN (1 << 11) #define FUDW_MGMT_PHY_LANE_INIT \ (FUDW_MGMT_PHY_LANE_CDR_TRACK_EN | FUDW_MGMT_PHY_LANE_LOS_THRESH | \ FUDW_MGMT_PHY_LANE_TERM_EN | FUDW_MGMT_PHY_LANE_TERM_ACDC | \ FUDW_MGMT_PHY_LANE_EN) #define FUDW_DBI_PORT_DBG1 0x72c #define FUDW_DBI_PORT_DBG1_LINK_UP (1 << 4) #define FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING (1 << 29) struct fupci_softc { struct pci_dw_softc dw_sc; device_t dev; struct resource *mgmt_res; gpio_pin_t porst_pin; gpio_pin_t pwren_pin; clk_t pcie_aux_clk; hwreset_t pcie_aux_rst; }; #define FUDW_MGMT_READ(_sc, _o) bus_read_4((_sc)->mgmt_res, (_o)) #define FUDW_MGMT_WRITE(_sc, _o, _v) bus_write_4((_sc)->mgmt_res, (_o), (_v)) static struct ofw_compat_data compat_data[] = { { "sifive,fu740-pcie", 1 }, { NULL, 0 }, }; /* Currently unused; included for completeness */ static int __unused fupci_phy_read(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t *val) { unsigned timeout; uint32_t ack; FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 1); timeout = 10; do { ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); if (ack != 0) break; DELAY(10); } while (--timeout > 0); if (timeout == 0) { device_printf(sc->dev, "Timeout waiting for read ACK\n"); return (ETIMEDOUT); } *val = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_DATA)); FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 0); timeout = 10; do { ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); if (ack == 0) break; DELAY(10); } while (--timeout > 0); if (timeout == 0) { device_printf(sc->dev, "Timeout waiting for read un-ACK\n"); return (ETIMEDOUT); } return (0); } static int fupci_phy_write(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t val) { unsigned timeout; uint32_t ack; FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_DATA), val); FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 1); timeout = 10; do { ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); if (ack != 0) break; DELAY(10); } while (--timeout > 0); if (timeout == 0) { device_printf(sc->dev, "Timeout waiting for write ACK\n"); return (ETIMEDOUT); } FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 0); timeout = 10; do { ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); if (ack == 0) break; DELAY(10); } while (--timeout > 0); if (timeout == 0) { device_printf(sc->dev, "Timeout waiting for write un-ACK\n"); return (ETIMEDOUT); } return (0); } static int fupci_phy_init(struct fupci_softc *sc) { device_t dev; int error, phy, lane; dev = sc->dev; /* Assert core power-on reset (active low) */ error = gpio_pin_set_active(sc->porst_pin, false); if (error != 0) { device_printf(dev, "Cannot assert power-on reset: %d\n", error); return (error); } /* Assert PERST_N */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 0); /* Enable power */ error = gpio_pin_set_active(sc->pwren_pin, true); if (error != 0) { device_printf(dev, "Cannot enable power: %d\n", error); return (error); } /* Hold PERST for 100ms as per the PCIe spec */ DELAY(100); /* Deassert PERST_N */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 1); /* Deassert core power-on reset (active low) */ error = gpio_pin_set_active(sc->porst_pin, true); if (error != 0) { device_printf(dev, "Cannot deassert power-on reset: %d\n", error); return (error); } /* Enable the aux clock */ error = clk_enable(sc->pcie_aux_clk); if (error != 0) { device_printf(dev, "Cannot enable aux clock: %d\n", error); return (error); } /* Hold LTSSM in reset whilst initialising the PHYs */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 1); /* Deassert the aux reset */ error = hwreset_deassert(sc->pcie_aux_rst); if (error != 0) { device_printf(dev, "Cannot deassert aux reset: %d\n", error); return (error); } /* Enable control register interface */ for (phy = 0; phy < FUDW_PHYS; ++phy) FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, SEL), 1); /* Wait for enable to take effect */ DELAY(1); /* Initialise lane configuration */ for (phy = 0; phy < FUDW_PHYS; ++phy) { for (lane = 0; lane < FUDW_LANES_PER_PHY; ++lane) fupci_phy_write(sc, phy, FUDW_MGMT_PHY_LANE(lane), FUDW_MGMT_PHY_LANE_INIT); } /* Disable the aux clock whilst taking the LTSSM out of reset */ error = clk_disable(sc->pcie_aux_clk); if (error != 0) { device_printf(dev, "Cannot disable aux clock: %d\n", error); return (error); } /* Take LTSSM out of reset */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 0); /* Enable the aux clock again */ error = clk_enable(sc->pcie_aux_clk); if (error != 0) { device_printf(dev, "Cannot re-enable aux clock: %d\n", error); return (error); } /* Put the controller in Root Complex mode */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_DEVICE_TYPE, FUDW_MGMT_DEVICE_TYPE_RC); return (0); } static void fupci_dbi_protect(struct fupci_softc *sc, bool protect) { uint32_t reg; reg = pci_dw_dbi_rd4(sc->dev, DW_MISC_CONTROL_1); if (protect) reg &= ~DBI_RO_WR_EN; else reg |= DBI_RO_WR_EN; pci_dw_dbi_wr4(sc->dev, DW_MISC_CONTROL_1, reg); } static int fupci_init(struct fupci_softc *sc) { /* Enable 32-bit I/O window */ fupci_dbi_protect(sc, false); pci_dw_dbi_wr2(sc->dev, PCIR_IOBASEL_1, (PCIM_BRIO_32 << 8) | PCIM_BRIO_32); fupci_dbi_protect(sc, true); /* Enable LTSSM */ FUDW_MGMT_WRITE(sc, FUDW_MGMT_LTSSM_EN, 1); return (0); } static int fupci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "SiFive FU740 PCIe Controller"); return (BUS_PROBE_DEFAULT); } static int fupci_attach(device_t dev) { struct fupci_softc *sc; phandle_t node; int error, rid; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->dev = dev; rid = 0; error = ofw_bus_find_string_index(node, "reg-names", "dbi", &rid); if (error != 0) { device_printf(dev, "Cannot get DBI memory: %d\n", error); goto fail; } sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->dw_sc.dbi_res == NULL) { device_printf(dev, "Cannot allocate DBI memory\n"); error = ENXIO; goto fail; } rid = 0; error = ofw_bus_find_string_index(node, "reg-names", "mgmt", &rid); if (error != 0) { device_printf(dev, "Cannot get management space memory: %d\n", error); goto fail; } sc->mgmt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mgmt_res == NULL) { device_printf(dev, "Cannot allocate management space memory\n"); error = ENXIO; goto fail; } error = gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", &sc->porst_pin); /* Old U-Boot device tree uses perstn-gpios */ if (error == ENOENT) error = gpio_pin_get_by_ofw_property(dev, node, "perstn-gpios", &sc->porst_pin); if (error != 0) { device_printf(dev, "Cannot get power-on reset GPIO: %d\n", error); goto fail; } error = gpio_pin_setflags(sc->porst_pin, GPIO_PIN_OUTPUT); if (error != 0) { device_printf(dev, "Cannot configure power-on reset GPIO: %d\n", error); goto fail; } error = gpio_pin_get_by_ofw_property(dev, node, "pwren-gpios", &sc->pwren_pin); if (error != 0) { device_printf(dev, "Cannot get power enable GPIO: %d\n", error); goto fail; } error = gpio_pin_setflags(sc->pwren_pin, GPIO_PIN_OUTPUT); if (error != 0) { device_printf(dev, "Cannot configure power enable GPIO: %d\n", error); goto fail; } error = clk_get_by_ofw_name(dev, node, "pcie_aux", &sc->pcie_aux_clk); /* Old U-Boot device tree uses pcieaux */ if (error == ENOENT) error = clk_get_by_ofw_name(dev, node, "pcieaux", &sc->pcie_aux_clk); if (error != 0) { device_printf(dev, "Cannot get aux clock: %d\n", error); goto fail; } error = hwreset_get_by_ofw_idx(dev, node, 0, &sc->pcie_aux_rst); if (error != 0) { device_printf(dev, "Cannot get aux reset: %d\n", error); goto fail; } error = fupci_phy_init(sc); if (error != 0) goto fail; error = pci_dw_init(dev); if (error != 0) goto fail; error = fupci_init(sc); if (error != 0) goto fail; return (bus_generic_attach(dev)); fail: /* XXX Cleanup */ return (error); } static int fupci_get_link(device_t dev, bool *status) { uint32_t reg; reg = pci_dw_dbi_rd4(dev, FUDW_DBI_PORT_DBG1); *status = (reg & FUDW_DBI_PORT_DBG1_LINK_UP) != 0 && (reg & FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING) == 0; return (0); } static device_method_t fupci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fupci_probe), DEVMETHOD(device_attach, fupci_attach), /* PCI DW interface */ DEVMETHOD(pci_dw_get_link, fupci_get_link), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, fupci_driver, fupci_methods, sizeof(struct fupci_softc), pci_dw_driver); DRIVER_MODULE(fu740_pci_dw, simplebus, fupci_driver, NULL, NULL);