// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2019 Google Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Provides a simple driver to control the ASPEED P2A interface which allows * the host to read and write to various regions of the BMC's memory.
*/
/* SCU2C is a Misc. Control Register. */ #define SCU2C 0x2c /* SCU180 is the PCIe Configuration Setting Control Register. */ #define SCU180 0x180 /* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device * on the PCI bus.
*/ #define SCU180_ENP2A BIT(1)
/* The ast2400/2500 both have six ranges. */ #define P2A_REGION_COUNT 6
struct region {
u64 min;
u64 max;
u32 bit;
};
struct aspeed_p2a_model_data { /* min, max, bit */ struct region regions[P2A_REGION_COUNT];
};
/* Access to these needs to be locked, held via probe, mapping ioctl, * and release, remove.
*/ struct mutex tracking;
u32 readers;
u32 readerwriters[P2A_REGION_COUNT];
/* The entire memory space is opened for reading once the bridge is * enabled, therefore this needs only to be tracked once per user. * If any user has it open for read, the bridge must stay enabled.
*/
u32 read;
/* Each entry of the array corresponds to a P2A Region. If the user * opens for read or readwrite, the reference goes up here. On * release, this array is walked and references adjusted accordingly.
*/
u32 readwrite[P2A_REGION_COUNT];
};
base = map->addr;
end = map->addr + (map->length - 1);
/* If the value is a legal u32, it will find a match. */ for (i = 0; i < P2A_REGION_COUNT; i++) { conststruct region *curr = &ctrl->config->regions[i];
/* If the top of this region is lower than your base, skip it.
*/ if (curr->max < base) continue;
/* If the bottom of this region is higher than your end, bail.
*/ if (curr->min > end) break;
/* Lock this and update it, therefore it someone else is * closing their file out, this'll preserve the increment.
*/
mutex_lock(&ctrl->tracking);
ctrl->readerwriters[i] += 1;
mutex_unlock(&ctrl->tracking);
/* Track with the user, so when they close their file, we can * decrement properly.
*/
priv->readwrite[i] += 1;
/* Enable the region as read-write. */
regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
matched = true;
}
if (copy_from_user(&map, arg, sizeof(map))) return -EFAULT;
switch (cmd) { case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW: /* If they want a region to be read-only, since the entire * region is read-only once enabled, we just need to track this * user wants to read from the bridge, and if it's not enabled. * Enable it.
*/ if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
mutex_lock(&ctrl->tracking);
ctrl->readers += 1;
mutex_unlock(&ctrl->tracking);
/* Track with the user, so when they close their file, * we can decrement properly.
*/
priv->read += 1;
} elseif (map.flags == ASPEED_P2A_CTRL_READWRITE) { /* If we don't acquire any region return error. */ if (!aspeed_p2a_region_acquire(priv, ctrl, &map)) { return -EINVAL;
}
} else { /* Invalid map flags. */ return -EINVAL;
}
aspeed_p2a_enable_bridge(ctrl); return 0; case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG: /* This is a request for the memory-region and corresponding * length that is used by the driver for mmap.
*/
/* * When a user opens this file, we create a structure to track their mappings. * * A user can map a region as read-only (bridge enabled), or read-write (bit * flipped, and bridge enabled). Either way, this tracking is used, s.t. when * they release the device references are handled. * * The bridge is not enabled until a user calls an ioctl to map a region, * simply opening the device does not enable it.
*/ staticint aspeed_p2a_open(struct inode *inode, struct file *file)
{ struct aspeed_p2a_user *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
/* The file's private_data is initialized to the p2a_ctrl. */
priv->parent = file->private_data;
/* Set the file's private_data to the user's data. */
file->private_data = priv;
return 0;
}
/* * This will close the users mappings. It will go through what they had opened * for readwrite, and decrement those counts. If at the end, this is the last * user, it'll close the bridge.
*/ staticint aspeed_p2a_release(struct inode *inode, struct file *file)
{ int i;
u32 bits = 0; bool open_regions = false; struct aspeed_p2a_user *priv = file->private_data;
/* Lock others from changing these values until everything is updated * in one pass.
*/
mutex_lock(&priv->parent->tracking);
priv->parent->readers -= priv->read;
for (i = 0; i < P2A_REGION_COUNT; i++) {
priv->parent->readerwriters[i] -= priv->readwrite[i];
/* Setting a bit to 1 disables the region, so let's just OR with the * above to disable any.
*/
/* Note, if another user is trying to ioctl, they can't grab tracking, * and therefore can't grab either register mutex. * If another user is trying to close, they can't grab tracking either.
*/
regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
/* If parent->readers is zero and open windows is 0, disable the * bridge.
*/ if (!open_regions && priv->parent->readers == 0)
aspeed_p2a_disable_bridge(priv->parent);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.