1/* $OpenBSD: com_pci.c,v 1.4 2024/05/24 06:02:53 jsg Exp $ */ 2/* 3 * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/tty.h> 21 22#include <dev/pci/pcidevs.h> 23#include <dev/pci/pcireg.h> 24#include <dev/pci/pcivar.h> 25 26#include <dev/ic/comreg.h> 27#include <dev/ic/comvar.h> 28 29#define com_usr 31 /* Synopsys DesignWare UART */ 30 31/* Intel Low Power Subsystem */ 32#define LPSS_CLK 0x200 33#define LPSS_CLK_GATE (1 << 0) 34#define LPSS_CLK_MDIV_SHIFT 1 35#define LPSS_CLK_MDIV_MASK 0x3fff 36#define LPSS_CLK_NDIV_SHIFT 16 37#define LPSS_CLK_NDIV_MASK 0x3fff 38#define LPSS_CLK_UPDATE (1U << 31) 39#define LPSS_RESETS 0x204 40#define LPSS_RESETS_FUNC (3 << 0) 41#define LPSS_RESETS_IDMA (1 << 2) 42#define LPSS_ACTIVELTR 0x210 43#define LPSS_IDLELTR 0x214 44#define LPSS_LTR_VALUE_MASK (0x3ff << 0) 45#define LPSS_LTR_SCALE_MASK (0x3 << 10) 46#define LPSS_LTR_SCALE_1US (2 << 10) 47#define LPSS_LTR_SCALE_32US (3 << 10) 48#define LPSS_LTR_REQ (1 << 15) 49#define LPSS_SSP 0x220 50#define LPSS_SSP_DIS_DMA_FIN (1 << 0) 51#define LPSS_REMAP_ADDR 0x240 52#define LPSS_CAPS 0x2fc 53#define LPSS_CAPS_TYPE_I2C (0 << 4) 54#define LPSS_CAPS_TYPE_UART (1 << 4) 55#define LPSS_CAPS_TYPE_SPI (2 << 4) 56#define LPSS_CAPS_TYPE_MASK (0xf << 4) 57#define LPSS_CAPS_NO_IDMA (1 << 8) 58 59#define LPSS_REG_OFF 0x200 60#define LPSS_REG_SIZE 0x100 61#define LPSS_REG_NUM (LPSS_REG_SIZE / sizeof(uint32_t)) 62 63#define HREAD4(sc, reg) \ 64 (bus_space_read_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg))) 65#define HWRITE4(sc, reg, val) \ 66 bus_space_write_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg), (val)) 67#define HSET4(sc, reg, bits) \ 68 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 69#define HCLR4(sc, reg, bits) \ 70 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 71 72int com_pci_match(struct device *, void *, void *); 73void com_pci_attach(struct device *, struct device *, void *); 74int com_pci_detach(struct device *, int); 75int com_pci_activate(struct device *, int); 76int com_pci_intr_designware(void *); 77 78struct com_pci_softc { 79 struct com_softc sc; 80 pci_chipset_tag_t sc_pc; 81 pcireg_t sc_id; 82 83 bus_size_t sc_ios; 84 void *sc_ih; 85 86 uint32_t sc_priv[LPSS_REG_NUM]; 87}; 88 89const struct cfattach com_pci_ca = { 90 sizeof(struct com_pci_softc), com_pci_match, 91 com_pci_attach, com_pci_detach, com_pci_activate, 92}; 93 94const struct pci_matchid com_pci_ids[] = { 95 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_1 }, 96 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_2 }, 97 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_3 }, 98 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_4 }, 99}; 100 101int 102com_pci_match(struct device *parent, void *match, void *aux) 103{ 104 return (pci_matchbyid(aux, com_pci_ids, nitems(com_pci_ids))); 105} 106 107void 108com_pci_attach(struct device *parent, struct device *self, void *aux) 109{ 110 struct com_pci_softc *sc = (struct com_pci_softc *)self; 111 struct pci_attach_args *pa = aux; 112 pci_intr_handle_t ih; 113 const char *intrstr; 114 uint64_t freq, m, n; 115 uint32_t caps; 116 117 sc->sc_pc = pa->pa_pc; 118 sc->sc_id = pa->pa_id; 119 sc->sc.sc_frequency = COM_FREQ; 120 sc->sc.sc_uarttype = COM_UART_16550; 121 sc->sc.sc_reg_width = 4; 122 sc->sc.sc_reg_shift = 2; 123 124 pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0); 125 126 if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT, 0, 127 &sc->sc.sc_iot, &sc->sc.sc_ioh, &sc->sc.sc_iobase, &sc->sc_ios, 0)) { 128 printf(": can't map mem space\n"); 129 return; 130 } 131 132 /* 133 * Once we are adding non-Intel and non-LPSS device it will make 134 * sense to add a second table and use pci_matchbyid(). 135 */ 136 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) { 137 caps = HREAD4(sc, LPSS_CAPS); 138 if ((caps & LPSS_CAPS_TYPE_MASK) != LPSS_CAPS_TYPE_UART) { 139 bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, 140 sc->sc_ios); 141 printf(": not a UART\n"); 142 return; 143 } 144 145 HWRITE4(sc, LPSS_RESETS, 0); 146 HWRITE4(sc, LPSS_RESETS, LPSS_RESETS_FUNC | LPSS_RESETS_IDMA); 147 HWRITE4(sc, LPSS_REMAP_ADDR, sc->sc.sc_iobase); 148 149 /* 100 MHz base clock */ 150 freq = 100 * 1000 * 1000; 151 m = n = HREAD4(sc, LPSS_CLK); 152 m = (m >> LPSS_CLK_MDIV_SHIFT) & LPSS_CLK_MDIV_MASK; 153 n = (n >> LPSS_CLK_NDIV_SHIFT) & LPSS_CLK_NDIV_MASK; 154 if (m && n) { 155 freq *= m; 156 freq /= n; 157 } 158 sc->sc.sc_frequency = freq; 159 } 160 161 if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { 162 bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios); 163 printf(": unable to map interrupt\n"); 164 return; 165 } 166 167 intrstr = pci_intr_string(pa->pa_pc, ih); 168 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY, 169 com_pci_intr_designware, &sc->sc, sc->sc.sc_dev.dv_xname); 170 if (sc->sc_ih == NULL) { 171 bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios); 172 printf(": can't establish interrupt"); 173 if (intrstr != NULL) 174 printf(" at %s", intrstr); 175 printf("\n"); 176 return; 177 } 178 179 com_attach_subr(&sc->sc); 180} 181 182int 183com_pci_detach(struct device *self, int flags) 184{ 185 struct com_pci_softc *sc = (struct com_pci_softc *)self; 186 int rv; 187 188 rv = com_detach(self, flags); 189 if (rv != 0) 190 return (rv); 191 if (sc->sc_ih != NULL) { 192 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 193 sc->sc_ih = NULL; 194 } 195 if (sc->sc_ios != 0) { 196 bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios); 197 sc->sc_ios = 0; 198 } 199 200 return (rv); 201} 202 203int 204com_pci_activate(struct device *self, int act) 205{ 206 struct com_pci_softc *sc = (struct com_pci_softc *)self; 207 int i, rv = 0; 208 209 if (PCI_VENDOR(sc->sc_id) != PCI_VENDOR_INTEL) 210 return com_activate(self, act); 211 212 switch (act) { 213 case DVACT_RESUME: 214 for (i = 0; i < LPSS_REG_NUM; i++) 215 HWRITE4(sc, i * sizeof(uint32_t), sc->sc_priv[i]); 216 rv = com_activate(self, act); 217 break; 218 case DVACT_SUSPEND: 219 rv = com_activate(self, act); 220 for (i = 0; i < LPSS_REG_NUM; i++) 221 sc->sc_priv[i] = HREAD4(sc, i * sizeof(uint32_t)); 222 break; 223 default: 224 rv = com_activate(self, act); 225 break; 226 } 227 228 return (rv); 229} 230 231int 232com_pci_intr_designware(void *cookie) 233{ 234 struct com_softc *sc = cookie; 235 236 com_read_reg(sc, com_usr); 237 238 return comintr(sc); 239} 240