diff options
| -rw-r--r-- | src/lpc/ao_arch.h | 15 | ||||
| -rw-r--r-- | src/lpc/ao_arch_funcs.h | 51 | ||||
| -rw-r--r-- | src/lpc/ao_spi_lpc.c | 194 | 
3 files changed, 260 insertions, 0 deletions
| diff --git a/src/lpc/ao_arch.h b/src/lpc/ao_arch.h index 99c646f9..d9f72e1a 100644 --- a/src/lpc/ao_arch.h +++ b/src/lpc/ao_arch.h @@ -28,6 +28,8 @@  #define AO_LED_TYPE	uint16_t +#define AO_PORT_TYPE	uint32_t +  #ifndef AO_TICK_TYPE  #define AO_TICK_TYPE	uint16_t  #define AO_TICK_SIGNED	int16_t @@ -123,4 +125,17 @@ ao_adc_init(void);  void  ao_serial_init(void); +/* SPI definitions */ + +#define AO_SPI_SPEED_12MHz		2 +#define AO_SPI_SPEED_6MHz		4 +#define AO_SPI_SPEED_4MHz		6 +#define AO_SPI_SPEED_2MHz		12 +#define AO_SPI_SPEED_1MHz		24 +#define AO_SPI_SPEED_500kHz		48 +#define AO_SPI_SPEED_250kHz		96 +#define AO_SPI_SPEED_125kHz		192 + +#define AO_SPI_SPEED_FAST	AO_SPI_SPEED_12MHz +  #endif /* _AO_ARCH_H_ */ diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h index 96ae0366..94d876f6 100644 --- a/src/lpc/ao_arch_funcs.h +++ b/src/lpc/ao_arch_funcs.h @@ -144,4 +144,55 @@ static inline void ao_arch_restore_stack(void) {  		ao_arch_release_interrupts();			\  	} while (0) +/* + * SPI + */ + +#define ao_spi_set_cs(port,mask) (lpc_gpio.clr[port] = (mask)) +#define ao_spi_clr_cs(port,mask) (lpc_gpio.set[port] = (mask)) + +#define ao_spi_get_mask(port,mask,bus,speed) do {	\ +		ao_spi_get(bus, speed);			\ +		ao_spi_set_cs(port, mask);		\ +	} while (0) + +#define ao_spi_put_mask(reg,mask,bus) do {	\ +		ao_spi_clr_cs(reg,mask);	\ +		ao_spi_put(bus);		\ +	} while (0) + +#define ao_spi_get_bit(reg,bit,pin,bus,speed) ao_spi_get_mask(reg,(1<<bit),bus,speed) +#define ao_spi_put_bit(reg,bit,pin,bus) ao_spi_put_mask(reg,(1<<bit),bus) + +void +ao_spi_get(uint8_t spi_index, uint32_t speed); + +void +ao_spi_put(uint8_t spi_index); + +void +ao_spi_send(void *block, uint16_t len, uint8_t spi_index); + +void +ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index); + +void +ao_spi_recv(void *block, uint16_t len, uint8_t spi_index); + +void +ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index); + +extern uint16_t	ao_spi_speed[LPC_NUM_SPI]; + +void +ao_spi_init(void); + +#define ao_spi_init_cs(port, mask) do {					\ +		uint8_t __bit__;					\ +		for (__bit__ = 0; __bit__ < 32; __bit__++) {		\ +			if (mask & (1 << __bit__))			\ +				ao_enable_output(port, __bit__, PIN, 1); \ +		}							\ +	} while (0) +  #endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/lpc/ao_spi_lpc.c b/src/lpc/ao_spi_lpc.c new file mode 100644 index 00000000..05688f52 --- /dev/null +++ b/src/lpc/ao_spi_lpc.c @@ -0,0 +1,194 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> + +static uint8_t		ao_spi_mutex[LPC_NUM_SPI]; + +static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 }; + +static uint8_t	spi_dev_null; + +#define spi_loop(len, put, get) do {					\ +		while (len--) {						\ +			/* Wait for space in the fifo */		\ +			while ((lpc_ssp->sr & (1 << LPC_SSP_SR_TNF)) == 0) \ +				;					\ +			/* send a byte */				\ +			lpc_ssp->dr = put;				\ +									\ +			/* recv a byte */				\ +			get lpc_ssp->dr;				\ +		}							\ +									\ +		/* Wait for the fifo to drain */			\ +		while ((lpc_ssp->sr & (1 << LPC_SSP_SR_BSY)))		\ +			;						\ +	} while (0); + +void +ao_spi_send(void *block, uint16_t len, uint8_t id) +{ +	uint8_t	*b = block; +	struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id]; + +	spi_loop(len, *b++, (void)); +} + +void +ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id) +{ +	struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id]; + +	spi_loop(len, value, (void)); +} + +void +ao_spi_recv(void *block, uint16_t len, uint8_t id) +{ +	uint8_t	*b = block; +	struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id]; + +	spi_loop(len, 0xff, *b++ =); +} + +void +ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t id) +{ +	uint8_t	*o = out; +	uint8_t	*i = in; +	struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id]; + +	spi_loop(len, *o++, *i++ =); +} + +void +ao_spi_get(uint8_t id, uint32_t speed) +{ +	struct lpc_ssp	*lpc_ssp = ao_lpc_ssp[id]; + +	ao_mutex_get(&ao_spi_mutex[id]); +	 +	/* Set the clock prescale */ +	lpc_ssp->cpsr = speed; + +	/* Enable the device */ +	lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) | +			(1 << LPC_SSP_CR1_SSE) | +			(LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) | +			(0 << LPC_SSP_CR1_SOD)); +} + +void +ao_spi_put(uint8_t id) +{ +	struct lpc_ssp	*lpc_ssp = ao_lpc_ssp[id]; + +	/* Disable the device */ +	lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) | +			(0 << LPC_SSP_CR1_SSE) | +			(LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) | +			(0 << LPC_SSP_CR1_SOD)); +	ao_mutex_put(&ao_spi_mutex[id]); +} + +static void +ao_spi_channel_init(uint8_t id) +{ +	struct lpc_ssp	*lpc_ssp = ao_lpc_ssp[id]; +	uint8_t	d; + +	lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) | +			(LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) | +			(0 << LPC_SSP_CR0_CPOL) | +			(0 << LPC_SSP_CR0_CPHA) | +			(0 << LPC_SSP_CR0_SCR)); +	/* Drain the receive fifo */ +	for (d = 0; d < LPC_SSP_FIFOSIZE; d++) +		(void) lpc_ssp->dr; +} + +void +ao_spi_init(void) +{ +#if HAS_SPI_0 +	/* Configure pins */ +	lpc_ioconf.pio0_6 = ao_lpc_alternate(LPC_IOCONF_FUNC_SCK0); +	lpc_ioconf.pio0_8 = ao_lpc_alternate(LPC_IOCONF_FUNC_MISO0); +	lpc_ioconf.pio0_9 = ao_lpc_alternate(LPC_IOCONF_FUNC_MOSI0); + +	/* Enable the device */ +	lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP0); + +	/* Turn on the clock */ +	lpc_scb.ssp0clkdiv = 1; + +	/* Reset the device */ +	lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N); +	lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N); +	ao_spi_channel_init(0); +#endif			    + +#if HAS_SPI_1 + +#if SPI_SCK1_P1_15 +	lpc_ioconf.pio1_15 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_15_SCK1); +#define HAS_SCK1 +#endif +#if SPI_SCK1_P1_20 +	lpc_ioconf.pio1_20 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_20_SCK1); +#define HAS_SCK1 +#endif +#ifndef HAS_SCK1 +#error "No pin specified for SCK1" +#endif + +#if SPI_MISO1_P0_22 +	lpc_ioconf.pio0_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_22_MISO1); +#define HAS_MISO1 +#endif +#if SPI_MISO1_P1_21 +	lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_21_MISO1); +#define HAS_MISO1 +#endif +#ifndef HAS_MISO1 +#error "No pin specified for MISO1" +#endif + +#if SPI_MOSI1_P0_21 +	lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_21_MOSI1); +#define HAS_MOSI1 +#endif +#if SPI_MOSI1_P1_22 +	lpc_ioconf.pio1_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_22_MOSI1); +#define HAS_MOSI1 +#endif +#ifndef HAS_MOSI1 +#error "No pin specified for MOSI1" +#endif +		 +	/* Enable the device */ +	lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1); + +	/* Turn on the clock */ +	lpc_scb.ssp1clkdiv = 1; + +	/* De-assert reset */ +	lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N); +	ao_spi_channel_init(1); +#endif /* HAS_SPI_1 */ +} | 
