drivers/spi/spi-meson-spicc.c

Source file repositories/reference/linux-study-clean/drivers/spi/spi-meson-spicc.c

File Facts

System
Linux kernel
Corpus path
drivers/spi/spi-meson-spicc.c
Extension
.c
Size
32066 bytes
Lines
1148
Domain
Driver Families
Bucket
drivers/spi
Inferred role
Driver Families: implementation source
Status
source 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

struct meson_spicc_data {
	unsigned int			max_speed_hz;
	unsigned int			min_speed_hz;
	unsigned int			fifo_size;
	bool				has_oen;
	bool				has_enhance_clk_div;
	bool				has_pclk;
};

struct meson_spicc_device {
	struct spi_controller		*host;
	struct platform_device		*pdev;
	void __iomem			*base;
	struct clk			*core;
	struct clk			*pclk;
	struct clk_divider		pow2_div;
	struct clk			*clk;
	struct spi_message		*message;
	struct spi_transfer		*xfer;
	struct completion		done;
	const struct meson_spicc_data	*data;
	u8				*tx_buf;
	u8				*rx_buf;
	unsigned int			bytes_per_word;
	unsigned long			tx_remain;
	unsigned long			rx_remain;
	unsigned long			xfer_remain;
	struct pinctrl			*pinctrl;
	struct pinctrl_state		*pins_idle_high;
	struct pinctrl_state		*pins_idle_low;
	dma_addr_t			tx_dma;
	dma_addr_t			rx_dma;
	bool				using_dma;
};

#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)

static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
{
	u32 conf;

	if (!spicc->data->has_oen) {
		/* Try to get pinctrl states for idle high/low */
		spicc->pins_idle_high = pinctrl_lookup_state(spicc->pinctrl,
							     "idle-high");
		if (IS_ERR(spicc->pins_idle_high)) {
			dev_warn(&spicc->pdev->dev, "can't get idle-high pinctrl\n");
			spicc->pins_idle_high = NULL;
		}
		spicc->pins_idle_low = pinctrl_lookup_state(spicc->pinctrl,
							     "idle-low");
		if (IS_ERR(spicc->pins_idle_low)) {
			dev_warn(&spicc->pdev->dev, "can't get idle-low pinctrl\n");
			spicc->pins_idle_low = NULL;
		}
		return;
	}

	conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) |
		SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN;

	writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
}

static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
			       struct spi_transfer *t)
{
	struct device *dev = spicc->host->dev.parent;

	if (!(t->tx_buf && t->rx_buf))
		return -EINVAL;

	t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
	if (dma_mapping_error(dev, t->tx_dma))
		return -ENOMEM;

	t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
	if (dma_mapping_error(dev, t->rx_dma))
		return -ENOMEM;

	spicc->tx_dma = t->tx_dma;
	spicc->rx_dma = t->rx_dma;

	return 0;
}

static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
				  struct spi_transfer *t)
{
	struct device *dev = spicc->host->dev.parent;

Annotation

Implementation Notes