drivers/spi/spi-loongson-core.c

Source file repositories/reference/linux-study-clean/drivers/spi/spi-loongson-core.c

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-loongson-core.c
Extension
.c
Size
8728 bytes
Lines
280
Domain
Driver Families
Bucket
drivers/spi
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0+
// Loongson SPI Support
// Copyright (C) 2023 Loongson Technology Corporation Limited

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>

#include "spi-loongson.h"

static inline void loongson_spi_write_reg(struct loongson_spi *spi, unsigned char reg,
					  unsigned char data)
{
	writeb(data, spi->base + reg);
}

static inline char loongson_spi_read_reg(struct loongson_spi *spi, unsigned char reg)
{
	return readb(spi->base + reg);
}

static void loongson_spi_set_cs(struct spi_device *spi, bool en)
{
	int cs;
	unsigned char mask = (BIT(4) | BIT(0)) << spi_get_chipselect(spi, 0);
	unsigned char val = en ? mask :  (BIT(0) << spi_get_chipselect(spi, 0));
	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);

	cs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG) & ~mask;
	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, val | cs);
}

static void loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
{
	unsigned char val;
	unsigned int div, div_tmp;
	static const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};

	div = clamp_val(DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz), 2, 4096);
	div_tmp = rdiv[fls(div - 1)];
	loongson_spi->spcr = (div_tmp & GENMASK(1, 0)) >> 0;
	loongson_spi->sper = (div_tmp & GENMASK(3, 2)) >> 2;
	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
	val &= ~GENMASK(1, 0);
	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val |
			       loongson_spi->spcr);
	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
	val &= ~GENMASK(1, 0);
	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val |
			       loongson_spi->sper);
	loongson_spi->hz = hz;
}

static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
				  struct spi_device *spi)
{
	unsigned char val;

	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
	val &= ~(LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA);
	if (spi->mode & SPI_CPOL)
		val |= LOONGSON_SPI_SPCR_CPOL;
	if (spi->mode & SPI_CPHA)
		val |= LOONGSON_SPI_SPCR_CPHA;

	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val);
	loongson_spi->mode |= spi->mode;
}

static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
				struct spi_device *spi, struct spi_transfer *t)
{
	if (t && loongson_spi->hz != t->speed_hz)
		loongson_spi_set_clk(loongson_spi, t->speed_hz);

	if ((spi->mode ^ loongson_spi->mode) & SPI_MODE_X_MASK)
		loongson_spi_set_mode(loongson_spi, spi);

	return 0;
}

static int loongson_spi_setup(struct spi_device *spi)

Annotation

Implementation Notes