lpc32xx_ssp.c 3.35 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
/*
 * LPC32xx SSP interface (SPI mode)
 *
 * (C) Copyright 2014  DENX Software Engineering GmbH
 * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
 *
 * SPDX-License-Identifier:     GPL-2.0+
 */

#include <common.h>
#include <linux/compat.h>
#include <asm/io.h>
#include <malloc.h>
#include <spi.h>
#include <asm/arch/clk.h>

/* SSP chip registers */
struct ssp_regs {
	u32 cr0;
	u32 cr1;
	u32 data;
	u32 sr;
	u32 cpsr;
	u32 imsc;
	u32 ris;
	u32 mis;
	u32 icr;
	u32 dmacr;
};

/* CR1 register defines  */
#define SSP_CR1_SSP_ENABLE 0x0002

/* SR register defines  */
#define SSP_SR_TNF 0x0002
/* SSP status RX FIFO not empty bit */
#define SSP_SR_RNE 0x0004

/* lpc32xx spi slave */
struct lpc32xx_spi_slave {
	struct spi_slave slave;
	struct ssp_regs *regs;
};

static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
	struct spi_slave *slave)
{
	return container_of(slave, struct lpc32xx_spi_slave, slave);
}

/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
void spi_init(void)
{
	/*
	 *  nothing to do: clocking was enabled in lpc32xx_ssp_enable()
	 * and configuration will be done in spi_setup_slave()
	*/
}

/* the following is called in sequence by do_spi_xfer() */

struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
{
	struct lpc32xx_spi_slave *lslave;

	/* we only set up SSP0 for now, so ignore bus */

	if (mode & SPI_3WIRE) {
		error("3-wire mode not supported");
		return NULL;
	}

	if (mode & SPI_SLAVE) {
		error("slave mode not supported\n");
		return NULL;
	}

	if (mode & SPI_PREAMBLE) {
		error("preamble byte skipping not supported\n");
		return NULL;
	}

	lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs);
	if (!lslave) {
		printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n");
		return NULL;
	}

	lslave->regs = (struct ssp_regs *)SSP0_BASE;

	/*
	 * 8 bit frame, SPI fmt, 500kbps -> clock divider is 26.
	 * Set SCR to 0 and CPSDVSR to 26.
	 */

	writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */
	writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */
	writel(0, &lslave->regs->imsc); /* do not raise any interrupts */
	writel(0, &lslave->regs->icr); /* clear any pending interrupt */
	writel(0, &lslave->regs->dmacr); /* do not do DMAs */
	writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */
	return &lslave->slave;
}

void spi_free_slave(struct spi_slave *slave)
{
	struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);

	debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave);
	free(lslave);
}

int spi_claim_bus(struct spi_slave *slave)
{
	/* only one bus and slave so far, always available */
	return 0;
}

int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
	const void *dout, void *din, unsigned long flags)
{
	struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
	int bytelen = bitlen >> 3;
	int idx_out = 0;
	int idx_in = 0;
	int start_time;

	start_time = get_timer(0);
	while ((idx_out < bytelen) || (idx_in < bytelen)) {
		int status = readl(&lslave->regs->sr);
		if ((idx_out < bytelen) && (status & SSP_SR_TNF))
			writel(((u8 *)dout)[idx_out++], &lslave->regs->data);
		if ((idx_in < bytelen) && (status & status & SSP_SR_RNE))
			((u8 *)din)[idx_in++] = readl(&lslave->regs->data);
		if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT)
			return -1;
	}
	return 0;
}

void spi_release_bus(struct spi_slave *slave)
{
	/* do nothing */
}