1 /*
   2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 #include <sys/conf.h>
   7 #include <sys/ddi.h>
   8 #include <sys/sunddi.h>
   9 #include <sys/modctl.h>
  10 #include <sys/stat.h>
  11 #include <sys/sunldi.h>
  12 #include <sys/file.h>
  13 #include <sys/agpgart.h>
  14 #include <sys/agp/agpdefs.h>
  15 #include <sys/agp/agpamd64gart_io.h>
  16 
  17 #define MAX_GART_INSTS          8
  18 #define GETSOFTC(instance)      ((amd64_gart_softstate_t *)     \
  19     ddi_get_soft_state(amd64_gart_glob_soft_handle, (instance)));
  20 #define DEV2INST(dev)           (getminor(dev))
  21 #define INST2NODENUM(inst)      (inst)
  22 
  23 int amd64_debug_var = 0;
  24 #define AMD64DB_PRINT1(fmt)     if (amd64_debug_var == 1) cmn_err fmt
  25 #define AMD64DB_PRINT2(fmt)     if (amd64_debug_var >= 1) cmn_err fmt
  26 
  27 typedef struct amd64_gart_softstate {
  28         dev_info_t              *gsoft_dip;
  29         ddi_acc_handle_t        gsoft_pcihdl;
  30         kmutex_t                gsoft_lock;
  31 }amd64_gart_softstate_t;
  32 
  33 static void *amd64_gart_glob_soft_handle;
  34 
  35 static uint64_t
  36 amd64_get_aperbase(amd64_gart_softstate_t *sc)
  37 {
  38         uint32_t        value;
  39         uint64_t        aper_base;
  40 
  41         /* amd64 aperture base support 40 bits and 32M aligned */
  42         value = pci_config_get32(sc->gsoft_pcihdl,
  43             AMD64_APERTURE_BASE) & AMD64_APERBASE_MASK;
  44         aper_base = (uint64_t)value << AMD64_APERBASE_SHIFT;
  45         return (aper_base);
  46 }
  47 
  48 static size_t
  49 amd64_get_apersize(amd64_gart_softstate_t *sc)
  50 {
  51         uint32_t        value;
  52         size_t          size;
  53 
  54         value = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL);
  55 
  56         value = (value & AMD64_APERSIZE_MASK) >> 1;
  57 
  58         /* aper size = 2^value x 32 */
  59         switch (value) {
  60                 case  0x0:
  61                         size = 32;
  62                         break;
  63                 case  0x1:
  64                         size = 64;
  65                         break;
  66                 case  0x2:
  67                         size = 128;
  68                         break;
  69                 case  0x3:
  70                         size = 256;
  71                         break;
  72                 case  0x4:
  73                         size = 512;
  74                         break;
  75                 case  0x5:
  76                         size = 1024;
  77                         break;
  78                 case  0x6:
  79                         size = 2048;
  80                         break;
  81                 default:                /* reserved */
  82                         size = 0;
  83         };
  84 
  85         return (size);
  86 }
  87 
  88 static void
  89 amd64_invalidate_gtlb(amd64_gart_softstate_t *sc)
  90 {
  91         uint32_t value;
  92 
  93         value = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL);
  94         value |= AMD64_INVALID_CACHE;
  95 
  96         pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL, value);
  97 }
  98 
  99 static void
 100 amd64_enable_gart(amd64_gart_softstate_t *sc, int enable)
 101 {
 102         uint32_t aper_ctl;
 103         uint32_t aper_base;
 104         uint32_t gart_ctl;
 105         uint32_t gart_base;
 106 
 107         aper_ctl = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL);
 108         AMD64DB_PRINT1((CE_NOTE, "before: aper_ctl = %x", aper_ctl));
 109         aper_base = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_BASE);
 110         gart_ctl = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL);
 111         gart_base = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_BASE);
 112 #ifdef lint
 113         aper_base = aper_base;
 114         gart_ctl = gart_ctl;
 115         gart_base = gart_base;
 116 #endif /* lint */
 117         AMD64DB_PRINT1((CE_NOTE, "before: aper_base = %x", aper_base));
 118         AMD64DB_PRINT1((CE_NOTE, "before: gart_ctl = %x", gart_ctl));
 119         AMD64DB_PRINT1((CE_NOTE, "before: gart_base = %x", gart_base));
 120         if (enable) {
 121                 aper_ctl |= AMD64_GARTEN;
 122                 aper_ctl &= ~(AMD64_DISGARTCPU | AMD64_DISGARTIO);
 123         } else
 124                 aper_ctl &= (~AMD64_GARTEN);
 125 
 126         pci_config_put32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL, aper_ctl);
 127 }
 128 
 129 /*ARGSUSED*/
 130 static int
 131 amd64_gart_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
 132     void *arg, void **resultp)
 133 {
 134         amd64_gart_softstate_t *st;
 135         int instance, rval = DDI_FAILURE;
 136         dev_t dev;
 137 
 138         switch (cmd) {
 139         case DDI_INFO_DEVT2DEVINFO:
 140                 dev = (dev_t)arg;
 141                 instance = DEV2INST(dev);
 142                 st = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
 143                 if (st != NULL) {
 144                         mutex_enter(&st->gsoft_lock);
 145                         *resultp = st->gsoft_dip;
 146                         mutex_exit(&st->gsoft_lock);
 147                         rval = DDI_SUCCESS;
 148                 } else {
 149                         *resultp = NULL;
 150                 }
 151 
 152                 break;
 153         case DDI_INFO_DEVT2INSTANCE:
 154                 dev = (dev_t)arg;
 155                 instance = DEV2INST(dev);
 156                 *resultp = (void *)(uintptr_t)instance;
 157                 rval = DDI_SUCCESS;
 158                 break;
 159         default:
 160                 break;
 161         }
 162 
 163         return (rval);
 164 }
 165 
 166 static int
 167 amd64_gart_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 168 {
 169         int                     instance;
 170         amd64_gart_softstate_t  *sc;
 171         int                     status;
 172         char                    buf[80];
 173 
 174         switch (cmd) {
 175         default:
 176                 return (DDI_FAILURE);
 177 
 178         case DDI_RESUME:
 179                 /* Nothing special is needed for resume. */
 180                 return (DDI_SUCCESS);
 181 
 182         case DDI_ATTACH:
 183                 break;
 184         }
 185 
 186         instance = ddi_get_instance(dip);
 187 
 188         if (ddi_soft_state_zalloc(amd64_gart_glob_soft_handle, instance) !=
 189             DDI_SUCCESS)
 190                 return (DDI_FAILURE);
 191 
 192         sc = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
 193         mutex_init(&sc->gsoft_lock, NULL, MUTEX_DRIVER, NULL);
 194         sc->gsoft_dip = dip;
 195         status = pci_config_setup(dip, &sc->gsoft_pcihdl);
 196         if (status != DDI_SUCCESS) {
 197                 ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
 198                 return (DDI_FAILURE);
 199         }
 200         (void) sprintf(buf, "%s-%d", AMD64GART_NAME, instance);
 201         status = ddi_create_minor_node(dip, buf, S_IFCHR,
 202             INST2NODENUM(instance), DDI_NT_AGP_CPUGART, 0);
 203         if (status != DDI_SUCCESS) {
 204                 pci_config_teardown(&sc->gsoft_pcihdl);
 205                 ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
 206                 return (DDI_FAILURE);
 207         }
 208         return (DDI_SUCCESS);
 209 }
 210 
 211 /*ARGSUSED*/
 212 static int
 213 amd64_gart_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 214 {
 215         int                     instance;
 216         amd64_gart_softstate_t  *sc;
 217         char                    buf[80];
 218 
 219         switch (cmd) {
 220         default:
 221                 return (DDI_FAILURE);
 222 
 223         case DDI_SUSPEND:
 224                 /* Nothing special is needed for suspend */
 225                 return (DDI_SUCCESS);
 226 
 227         case DDI_DETACH:
 228                 break;
 229         }
 230 
 231         instance = ddi_get_instance(dip);
 232         sc = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
 233 
 234         (void) sprintf(buf, "%s-%d", AMD64GART_NAME, instance);
 235         ddi_remove_minor_node(dip, buf);
 236         pci_config_teardown(&sc->gsoft_pcihdl);
 237         mutex_destroy(&sc->gsoft_lock);
 238         ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
 239 
 240         return (DDI_SUCCESS);
 241 }
 242 
 243 /*ARGSUSED*/
 244 static int
 245 amd64_gart_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
 246     cred_t *cred, int *rval)
 247 {
 248         int instance;
 249         amd64_gart_softstate_t *sc;
 250         static char kernel_only[] =
 251             "amd64_gart_ioctl: is a kernel only ioctl";
 252 
 253         if (!(mode & FKIOCTL)) {
 254                 AMD64DB_PRINT2((CE_CONT, kernel_only));
 255                 return (ENXIO);
 256         }
 257         instance = DEV2INST(dev);
 258         sc = GETSOFTC(instance);
 259 
 260         if (sc == NULL)
 261                 return (ENXIO);
 262         mutex_enter(&sc->gsoft_lock);
 263 
 264         switch (cmd) {
 265         case AMD64_GET_INFO:
 266         {
 267                 amdgart_info_t info;
 268 
 269                 info.cgart_aperbase = amd64_get_aperbase(sc);
 270                 info.cgart_apersize = amd64_get_apersize(sc);
 271 
 272                 if (ddi_copyout(&info, (void *)data,
 273                     sizeof (amdgart_info_t), mode)) {
 274                         mutex_exit(&sc->gsoft_lock);
 275                         return (EFAULT);
 276                 }
 277                 break;
 278         }
 279         case AMD64_SET_GART_ADDR:
 280         {
 281                 uint32_t addr;
 282 
 283                 if (ddi_copyin((void *)data, &addr, sizeof (uint32_t), mode)) {
 284                         mutex_exit(&sc->gsoft_lock);
 285                         return (EFAULT);
 286                 }
 287 
 288                 pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_BASE, addr);
 289                 amd64_enable_gart(sc, 1);
 290 
 291                 break;
 292         }
 293         case AMD64_FLUSH_GTLB:
 294         {
 295                 amd64_invalidate_gtlb(sc);
 296 
 297                 break;
 298         }
 299         case AMD64_CONFIGURE:
 300         {
 301                 /* reserved */
 302                 break;
 303         }
 304         case AMD64_UNCONFIG:
 305         {
 306                 amd64_enable_gart(sc, 0);
 307                 pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_BASE, 0x00000000);
 308 
 309                 break;
 310         }
 311         default:
 312                 mutex_exit(&sc->gsoft_lock);
 313                 return (ENXIO);
 314 
 315         }
 316 
 317         mutex_exit(&sc->gsoft_lock);
 318 
 319         return (0);
 320 }
 321 
 322 /*ARGSUSED*/
 323 static int
 324 amd64_gart_open(dev_t *dev, int flag, int otyp, cred_t *cred)
 325 {
 326         int                     instance;
 327         amd64_gart_softstate_t  *sc;
 328 
 329         if (!(flag & FKLYR))
 330                 return (ENXIO);
 331 
 332         instance = DEV2INST(*dev);
 333         sc = GETSOFTC(instance);
 334 
 335         if (sc == NULL)
 336                 return (ENXIO);
 337 
 338         return (0);
 339 }
 340 
 341 /*ARGSUSED*/
 342 static int
 343 amd64_gart_close(dev_t dev, int flag, int otyp, cred_t *cred)
 344 {
 345         int                     instance;
 346         amd64_gart_softstate_t  *sc;
 347 
 348         instance = DEV2INST(dev);
 349         sc = GETSOFTC(instance);
 350 
 351         if (sc == NULL)
 352                 return (ENXIO);
 353 
 354         return (0);
 355 }
 356 
 357 static  struct  cb_ops  amd64_gart_cb_ops = {
 358         amd64_gart_open,        /* cb_open() */
 359         amd64_gart_close,       /* cb_close() */
 360         nodev,                  /* cb_strategy() */
 361         nodev,                  /* cb_print */
 362         nodev,                  /* cb_dump */
 363         nodev,                  /* cb_read() */
 364         nodev,                  /* cb_write() */
 365         amd64_gart_ioctl,       /* cb_ioctl */
 366         nodev,                  /* cb_devmap */
 367         nodev,                  /* cb_mmap */
 368         nodev,                  /* cb_segmap */
 369         nochpoll,               /* cb_chpoll */
 370         ddi_prop_op,            /* cb_prop_op */
 371         0,                      /* cb_stream */
 372         D_NEW | D_MP,           /* cb_flag */
 373         CB_REV,                 /* cb_ops version? */
 374         nodev,                  /* cb_aread() */
 375         nodev,                  /* cb_awrite() */
 376 };
 377 
 378 /* device operations */
 379 static struct dev_ops amd64_gart_ops = {
 380         DEVO_REV,               /* devo_rev */
 381         0,                      /* devo_refcnt */
 382         amd64_gart_getinfo,     /* devo_getinfo */
 383         nulldev,                /* devo_identify */
 384         nulldev,                /* devo_probe */
 385         amd64_gart_attach,      /* devo_attach */
 386         amd64_gart_detach,      /* devo_detach */
 387         nodev,                  /* devo_reset */
 388         &amd64_gart_cb_ops, /* devo_cb_ops */
 389         0,                      /* devo_bus_ops */
 390         0,                      /* devo_power */
 391         ddi_quiesce_not_needed, /* devo_quiesce */
 392 };
 393 
 394 static  struct modldrv modldrv = {
 395         &mod_driverops,
 396         "AGP AMD gart driver",
 397         &amd64_gart_ops,
 398 };
 399 
 400 static  struct modlinkage modlinkage = {
 401         MODREV_1,               /* MODREV_1 is indicated by manual */
 402         &modldrv,
 403         NULL
 404 };
 405 
 406 
 407 int
 408 _init(void)
 409 {
 410         int ret = DDI_SUCCESS;
 411 
 412         ret = ddi_soft_state_init(&amd64_gart_glob_soft_handle,
 413             sizeof (amd64_gart_softstate_t),
 414             MAX_GART_INSTS);
 415 
 416         if (ret)
 417                 return (ret);
 418         if ((ret = mod_install(&modlinkage)) != 0) {
 419                 ddi_soft_state_fini(&amd64_gart_glob_soft_handle);
 420                 return (ret);
 421         }
 422         return (DDI_SUCCESS);
 423 }
 424 
 425 int
 426 _info(struct  modinfo *modinfop)
 427 {
 428         return (mod_info(&modlinkage, modinfop));
 429 }
 430 
 431 int
 432 _fini(void)
 433 {
 434         int ret;
 435         if ((ret = mod_remove(&modlinkage)) == 0) {
 436                 ddi_soft_state_fini(&amd64_gart_glob_soft_handle);
 437         }
 438         return (ret);
 439 }