root/drivers/soc/aspeed/aspeed-p2a-ctrl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. aspeed_p2a_enable_bridge
  2. aspeed_p2a_disable_bridge
  3. aspeed_p2a_mmap
  4. aspeed_p2a_region_acquire
  5. aspeed_p2a_ioctl
  6. aspeed_p2a_open
  7. aspeed_p2a_release
  8. aspeed_p2a_disable_all
  9. aspeed_p2a_ctrl_probe
  10. aspeed_p2a_ctrl_remove

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright 2019 Google Inc
   4  *
   5  * This program is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU General Public License
   7  * as published by the Free Software Foundation; either version
   8  * 2 of the License, or (at your option) any later version.
   9  *
  10  * Provides a simple driver to control the ASPEED P2A interface which allows
  11  * the host to read and write to various regions of the BMC's memory.
  12  */
  13 
  14 #include <linux/fs.h>
  15 #include <linux/io.h>
  16 #include <linux/mfd/syscon.h>
  17 #include <linux/miscdevice.h>
  18 #include <linux/mm.h>
  19 #include <linux/module.h>
  20 #include <linux/mutex.h>
  21 #include <linux/of_address.h>
  22 #include <linux/of_device.h>
  23 #include <linux/platform_device.h>
  24 #include <linux/regmap.h>
  25 #include <linux/slab.h>
  26 #include <linux/uaccess.h>
  27 
  28 #include <linux/aspeed-p2a-ctrl.h>
  29 
  30 #define DEVICE_NAME     "aspeed-p2a-ctrl"
  31 
  32 /* SCU2C is a Misc. Control Register. */
  33 #define SCU2C 0x2c
  34 /* SCU180 is the PCIe Configuration Setting Control Register. */
  35 #define SCU180 0x180
  36 /* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device
  37  * on the PCI bus.
  38  */
  39 #define SCU180_ENP2A BIT(1)
  40 
  41 /* The ast2400/2500 both have six ranges. */
  42 #define P2A_REGION_COUNT 6
  43 
  44 struct region {
  45         u64 min;
  46         u64 max;
  47         u32 bit;
  48 };
  49 
  50 struct aspeed_p2a_model_data {
  51         /* min, max, bit */
  52         struct region regions[P2A_REGION_COUNT];
  53 };
  54 
  55 struct aspeed_p2a_ctrl {
  56         struct miscdevice miscdev;
  57         struct regmap *regmap;
  58 
  59         const struct aspeed_p2a_model_data *config;
  60 
  61         /* Access to these needs to be locked, held via probe, mapping ioctl,
  62          * and release, remove.
  63          */
  64         struct mutex tracking;
  65         u32 readers;
  66         u32 readerwriters[P2A_REGION_COUNT];
  67 
  68         phys_addr_t mem_base;
  69         resource_size_t mem_size;
  70 };
  71 
  72 struct aspeed_p2a_user {
  73         struct file *file;
  74         struct aspeed_p2a_ctrl *parent;
  75 
  76         /* The entire memory space is opened for reading once the bridge is
  77          * enabled, therefore this needs only to be tracked once per user.
  78          * If any user has it open for read, the bridge must stay enabled.
  79          */
  80         u32 read;
  81 
  82         /* Each entry of the array corresponds to a P2A Region.  If the user
  83          * opens for read or readwrite, the reference goes up here.  On
  84          * release, this array is walked and references adjusted accordingly.
  85          */
  86         u32 readwrite[P2A_REGION_COUNT];
  87 };
  88 
  89 static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
  90 {
  91         regmap_update_bits(p2a_ctrl->regmap,
  92                 SCU180, SCU180_ENP2A, SCU180_ENP2A);
  93 }
  94 
  95 static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
  96 {
  97         regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
  98 }
  99 
 100 static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma)
 101 {
 102         unsigned long vsize;
 103         pgprot_t prot;
 104         struct aspeed_p2a_user *priv = file->private_data;
 105         struct aspeed_p2a_ctrl *ctrl = priv->parent;
 106 
 107         if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
 108                 return -EINVAL;
 109 
 110         vsize = vma->vm_end - vma->vm_start;
 111         prot = vma->vm_page_prot;
 112 
 113         if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
 114                 return -EINVAL;
 115 
 116         /* ast2400/2500 AHB accesses are not cache coherent */
 117         prot = pgprot_noncached(prot);
 118 
 119         if (remap_pfn_range(vma, vma->vm_start,
 120                 (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
 121                 vsize, prot))
 122                 return -EAGAIN;
 123 
 124         return 0;
 125 }
 126 
 127 static bool aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
 128                 struct aspeed_p2a_ctrl *ctrl,
 129                 struct aspeed_p2a_ctrl_mapping *map)
 130 {
 131         int i;
 132         u64 base, end;
 133         bool matched = false;
 134 
 135         base = map->addr;
 136         end = map->addr + (map->length - 1);
 137 
 138         /* If the value is a legal u32, it will find a match. */
 139         for (i = 0; i < P2A_REGION_COUNT; i++) {
 140                 const struct region *curr = &ctrl->config->regions[i];
 141 
 142                 /* If the top of this region is lower than your base, skip it.
 143                  */
 144                 if (curr->max < base)
 145                         continue;
 146 
 147                 /* If the bottom of this region is higher than your end, bail.
 148                  */
 149                 if (curr->min > end)
 150                         break;
 151 
 152                 /* Lock this and update it, therefore it someone else is
 153                  * closing their file out, this'll preserve the increment.
 154                  */
 155                 mutex_lock(&ctrl->tracking);
 156                 ctrl->readerwriters[i] += 1;
 157                 mutex_unlock(&ctrl->tracking);
 158 
 159                 /* Track with the user, so when they close their file, we can
 160                  * decrement properly.
 161                  */
 162                 priv->readwrite[i] += 1;
 163 
 164                 /* Enable the region as read-write. */
 165                 regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
 166                 matched = true;
 167         }
 168 
 169         return matched;
 170 }
 171 
 172 static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
 173                 unsigned long data)
 174 {
 175         struct aspeed_p2a_user *priv = file->private_data;
 176         struct aspeed_p2a_ctrl *ctrl = priv->parent;
 177         void __user *arg = (void __user *)data;
 178         struct aspeed_p2a_ctrl_mapping map;
 179 
 180         if (copy_from_user(&map, arg, sizeof(map)))
 181                 return -EFAULT;
 182 
 183         switch (cmd) {
 184         case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
 185                 /* If they want a region to be read-only, since the entire
 186                  * region is read-only once enabled, we just need to track this
 187                  * user wants to read from the bridge, and if it's not enabled.
 188                  * Enable it.
 189                  */
 190                 if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
 191                         mutex_lock(&ctrl->tracking);
 192                         ctrl->readers += 1;
 193                         mutex_unlock(&ctrl->tracking);
 194 
 195                         /* Track with the user, so when they close their file,
 196                          * we can decrement properly.
 197                          */
 198                         priv->read += 1;
 199                 } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
 200                         /* If we don't acquire any region return error. */
 201                         if (!aspeed_p2a_region_acquire(priv, ctrl, &map)) {
 202                                 return -EINVAL;
 203                         }
 204                 } else {
 205                         /* Invalid map flags. */
 206                         return -EINVAL;
 207                 }
 208 
 209                 aspeed_p2a_enable_bridge(ctrl);
 210                 return 0;
 211         case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
 212                 /* This is a request for the memory-region and corresponding
 213                  * length that is used by the driver for mmap.
 214                  */
 215 
 216                 map.flags = 0;
 217                 map.addr = ctrl->mem_base;
 218                 map.length = ctrl->mem_size;
 219 
 220                 return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
 221         }
 222 
 223         return -EINVAL;
 224 }
 225 
 226 
 227 /*
 228  * When a user opens this file, we create a structure to track their mappings.
 229  *
 230  * A user can map a region as read-only (bridge enabled), or read-write (bit
 231  * flipped, and bridge enabled).  Either way, this tracking is used, s.t. when
 232  * they release the device references are handled.
 233  *
 234  * The bridge is not enabled until a user calls an ioctl to map a region,
 235  * simply opening the device does not enable it.
 236  */
 237 static int aspeed_p2a_open(struct inode *inode, struct file *file)
 238 {
 239         struct aspeed_p2a_user *priv;
 240 
 241         priv = kmalloc(sizeof(*priv), GFP_KERNEL);
 242         if (!priv)
 243                 return -ENOMEM;
 244 
 245         priv->file = file;
 246         priv->read = 0;
 247         memset(priv->readwrite, 0, sizeof(priv->readwrite));
 248 
 249         /* The file's private_data is initialized to the p2a_ctrl. */
 250         priv->parent = file->private_data;
 251 
 252         /* Set the file's private_data to the user's data. */
 253         file->private_data = priv;
 254 
 255         return 0;
 256 }
 257 
 258 /*
 259  * This will close the users mappings.  It will go through what they had opened
 260  * for readwrite, and decrement those counts.  If at the end, this is the last
 261  * user, it'll close the bridge.
 262  */
 263 static int aspeed_p2a_release(struct inode *inode, struct file *file)
 264 {
 265         int i;
 266         u32 bits = 0;
 267         bool open_regions = false;
 268         struct aspeed_p2a_user *priv = file->private_data;
 269 
 270         /* Lock others from changing these values until everything is updated
 271          * in one pass.
 272          */
 273         mutex_lock(&priv->parent->tracking);
 274 
 275         priv->parent->readers -= priv->read;
 276 
 277         for (i = 0; i < P2A_REGION_COUNT; i++) {
 278                 priv->parent->readerwriters[i] -= priv->readwrite[i];
 279 
 280                 if (priv->parent->readerwriters[i] > 0)
 281                         open_regions = true;
 282                 else
 283                         bits |= priv->parent->config->regions[i].bit;
 284         }
 285 
 286         /* Setting a bit to 1 disables the region, so let's just OR with the
 287          * above to disable any.
 288          */
 289 
 290         /* Note, if another user is trying to ioctl, they can't grab tracking,
 291          * and therefore can't grab either register mutex.
 292          * If another user is trying to close, they can't grab tracking either.
 293          */
 294         regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
 295 
 296         /* If parent->readers is zero and open windows is 0, disable the
 297          * bridge.
 298          */
 299         if (!open_regions && priv->parent->readers == 0)
 300                 aspeed_p2a_disable_bridge(priv->parent);
 301 
 302         mutex_unlock(&priv->parent->tracking);
 303 
 304         kfree(priv);
 305 
 306         return 0;
 307 }
 308 
 309 static const struct file_operations aspeed_p2a_ctrl_fops = {
 310         .owner = THIS_MODULE,
 311         .mmap = aspeed_p2a_mmap,
 312         .unlocked_ioctl = aspeed_p2a_ioctl,
 313         .open = aspeed_p2a_open,
 314         .release = aspeed_p2a_release,
 315 };
 316 
 317 /* The regions are controlled by SCU2C */
 318 static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
 319 {
 320         int i;
 321         u32 value = 0;
 322 
 323         for (i = 0; i < P2A_REGION_COUNT; i++)
 324                 value |= p2a_ctrl->config->regions[i].bit;
 325 
 326         regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
 327 
 328         /* Disable the bridge. */
 329         aspeed_p2a_disable_bridge(p2a_ctrl);
 330 }
 331 
 332 static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
 333 {
 334         struct aspeed_p2a_ctrl *misc_ctrl;
 335         struct device *dev;
 336         struct resource resm;
 337         struct device_node *node;
 338         int rc = 0;
 339 
 340         dev = &pdev->dev;
 341 
 342         misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
 343         if (!misc_ctrl)
 344                 return -ENOMEM;
 345 
 346         mutex_init(&misc_ctrl->tracking);
 347 
 348         /* optional. */
 349         node = of_parse_phandle(dev->of_node, "memory-region", 0);
 350         if (node) {
 351                 rc = of_address_to_resource(node, 0, &resm);
 352                 of_node_put(node);
 353                 if (rc) {
 354                         dev_err(dev, "Couldn't address to resource for reserved memory\n");
 355                         return -ENODEV;
 356                 }
 357 
 358                 misc_ctrl->mem_size = resource_size(&resm);
 359                 misc_ctrl->mem_base = resm.start;
 360         }
 361 
 362         misc_ctrl->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node);
 363         if (IS_ERR(misc_ctrl->regmap)) {
 364                 dev_err(dev, "Couldn't get regmap\n");
 365                 return -ENODEV;
 366         }
 367 
 368         misc_ctrl->config = of_device_get_match_data(dev);
 369 
 370         dev_set_drvdata(&pdev->dev, misc_ctrl);
 371 
 372         aspeed_p2a_disable_all(misc_ctrl);
 373 
 374         misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
 375         misc_ctrl->miscdev.name = DEVICE_NAME;
 376         misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
 377         misc_ctrl->miscdev.parent = dev;
 378 
 379         rc = misc_register(&misc_ctrl->miscdev);
 380         if (rc)
 381                 dev_err(dev, "Unable to register device\n");
 382 
 383         return rc;
 384 }
 385 
 386 static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
 387 {
 388         struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
 389 
 390         misc_deregister(&p2a_ctrl->miscdev);
 391 
 392         return 0;
 393 }
 394 
 395 #define SCU2C_DRAM      BIT(25)
 396 #define SCU2C_SPI       BIT(24)
 397 #define SCU2C_SOC       BIT(23)
 398 #define SCU2C_FLASH     BIT(22)
 399 
 400 static const struct aspeed_p2a_model_data ast2400_model_data = {
 401         .regions = {
 402                 {0x00000000, 0x17FFFFFF, SCU2C_FLASH},
 403                 {0x18000000, 0x1FFFFFFF, SCU2C_SOC},
 404                 {0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
 405                 {0x30000000, 0x3FFFFFFF, SCU2C_SPI},
 406                 {0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
 407                 {0x60000000, 0xFFFFFFFF, SCU2C_SOC},
 408         }
 409 };
 410 
 411 static const struct aspeed_p2a_model_data ast2500_model_data = {
 412         .regions = {
 413                 {0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
 414                 {0x10000000, 0x1FFFFFFF, SCU2C_SOC},
 415                 {0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
 416                 {0x40000000, 0x5FFFFFFF, SCU2C_SOC},
 417                 {0x60000000, 0x7FFFFFFF, SCU2C_SPI},
 418                 {0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
 419         }
 420 };
 421 
 422 static const struct of_device_id aspeed_p2a_ctrl_match[] = {
 423         { .compatible = "aspeed,ast2400-p2a-ctrl",
 424           .data = &ast2400_model_data },
 425         { .compatible = "aspeed,ast2500-p2a-ctrl",
 426           .data = &ast2500_model_data },
 427         { },
 428 };
 429 
 430 static struct platform_driver aspeed_p2a_ctrl_driver = {
 431         .driver = {
 432                 .name           = DEVICE_NAME,
 433                 .of_match_table = aspeed_p2a_ctrl_match,
 434         },
 435         .probe = aspeed_p2a_ctrl_probe,
 436         .remove = aspeed_p2a_ctrl_remove,
 437 };
 438 
 439 module_platform_driver(aspeed_p2a_ctrl_driver);
 440 
 441 MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
 442 MODULE_LICENSE("GPL");
 443 MODULE_AUTHOR("Patrick Venture <venture@google.com>");
 444 MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");

/* [<][>][^][v][top][bottom][index][help] */