arch/arm/mm/cache-uniphier.c

Source file repositories/reference/linux-study-clean/arch/arm/mm/cache-uniphier.c

File Facts

System
Linux kernel
Corpus path
arch/arm/mm/cache-uniphier.c
Extension
.c
Size
14544 bytes
Lines
498
Domain
Architecture Layer
Bucket
arch/arm
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct uniphier_cache_data {
	void __iomem *ctrl_base;
	void __iomem *rev_base;
	void __iomem *op_base;
	void __iomem *way_ctrl_base;
	u32 way_mask;
	u32 nsets;
	u32 line_size;
	u32 range_op_max_size;
	struct list_head list;
};

/*
 * List of the whole outer cache hierarchy.  This list is only modified during
 * the early boot stage, so no mutex is taken for the access to the list.
 */
static LIST_HEAD(uniphier_cache_list);

/**
 * __uniphier_cache_sync - perform a sync point for a particular cache level
 *
 * @data: cache controller specific data
 */
static void __uniphier_cache_sync(struct uniphier_cache_data *data)
{
	/* This sequence need not be atomic.  Do not disable IRQ. */
	writel_relaxed(UNIPHIER_SSCOPE_CM_SYNC,
		       data->op_base + UNIPHIER_SSCOPE);
	/* need a read back to confirm */
	readl_relaxed(data->op_base + UNIPHIER_SSCOPE);
}

/**
 * __uniphier_cache_maint_common - run a queue operation for a particular level
 *
 * @data: cache controller specific data
 * @start: start address of range operation (don't care for "all" operation)
 * @size: data size of range operation (don't care for "all" operation)
 * @operation: flags to specify the desired cache operation
 */
static void __uniphier_cache_maint_common(struct uniphier_cache_data *data,
					  unsigned long start,
					  unsigned long size,
					  u32 operation)
{
	unsigned long flags;

	/*
	 * No spin lock is necessary here because:
	 *
	 * [1] This outer cache controller is able to accept maintenance
	 * operations from multiple CPUs at a time in an SMP system; if a
	 * maintenance operation is under way and another operation is issued,
	 * the new one is stored in the queue.  The controller performs one
	 * operation after another.  If the queue is full, the status register,
	 * UNIPHIER_SSCOPPQSEF, indicates that the queue registration has
	 * failed.  The status registers, UNIPHIER_{SSCOPPQSEF, SSCOLPQS}, have
	 * different instances for each CPU, i.e. each CPU can track the status
	 * of the maintenance operations triggered by itself.
	 *
	 * [2] The cache command registers, UNIPHIER_{SSCOQM, SSCOQAD, SSCOQSZ,
	 * SSCOQWN}, are shared between multiple CPUs, but the hardware still
	 * guarantees the registration sequence is atomic; the write access to
	 * them are arbitrated by the hardware.  The first accessor to the
	 * register, UNIPHIER_SSCOQM, holds the access right and it is released
	 * by reading the status register, UNIPHIER_SSCOPPQSEF.  While one CPU
	 * is holding the access right, other CPUs fail to register operations.
	 * One CPU should not hold the access right for a long time, so local
	 * IRQs should be disabled while the following sequence.
	 */
	local_irq_save(flags);

	/* clear the complete notification flag */
	writel_relaxed(UNIPHIER_SSCOLPQS_EF, data->op_base + UNIPHIER_SSCOLPQS);

	do {
		/* set cache operation */
		writel_relaxed(UNIPHIER_SSCOQM_CE | operation,
			       data->op_base + UNIPHIER_SSCOQM);

		/* set address range if needed */
		if (likely(UNIPHIER_SSCOQM_S_IS_RANGE(operation))) {
			writel_relaxed(start, data->op_base + UNIPHIER_SSCOQAD);
			writel_relaxed(size, data->op_base + UNIPHIER_SSCOQSZ);
		}
	} while (unlikely(readl_relaxed(data->op_base + UNIPHIER_SSCOPPQSEF) &
			  (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE)));

	/* wait until the operation is completed */
	while (likely(readl_relaxed(data->op_base + UNIPHIER_SSCOLPQS) !=

Annotation

Implementation Notes