diff --git a/arch/arm/src/tiva/tiva_pwm.c b/arch/arm/src/tiva/tiva_pwm.c index 9e1f660be51..8ccaf73837e 100644 --- a/arch/arm/src/tiva/tiva_pwm.c +++ b/arch/arm/src/tiva/tiva_pwm.c @@ -1,49 +1,52 @@ /************************************************************************************ -* arch/arm/src/tiva/tiva_pwm.c -* -* Copyright (C) 2016 Young Mu. All rights reserved. -* Author: Young Mu -* -* The basic structure of this driver derives in spirit (if nothing more) from the -* NuttX SAM PWM driver which has: -* -* Copyright (C) 2013 Gregory Nutt. All rights reserved. -* Author: Gregory Nutt -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* 3. Neither the name NuttX nor the names of its contributors may be -* used to endorse or promote products derived from this software -* without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -************************************************************************************/ + * arch/arm/src/tiva/tiva_pwm.c + * + * Copyright (C) 2016 Young Mu. All rights reserved. + * Author: Young Mu + * + * The basic structure of this driver derives in spirit (if nothing more) from the + * NuttX SAM PWM driver which has: + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ /************************************************************************************ -* Included Files -************************************************************************************/ + * Included Files + ************************************************************************************/ + +#include #include +#include #include #include @@ -59,60 +62,39 @@ #include "chip/tm4c_memorymap.h" /************************************************************************************ -* Pre-processor Definitions -************************************************************************************/ - -#define pwmerr(fmt, args...) printf("%s(%d): " fmt, __FUNCTION__, __LINE__, ##args); - -#ifndef CONFIG_DEBUG -# undef CONFIG_DEBUG_PWM -#endif - -#ifdef CONFIG_DEBUG_PWM -# define pwmdbg dbg -# ifdef CONFIG_DEBUG_VERBOSE -# define pwmvdbg vdbg -# else -# define pwmvdbg(x...) -# endif -#else -# define pwmdbg(x...) -# define pwmvdbg(x...) -#endif - -/************************************************************************************ -* Private Types -************************************************************************************/ + * Private Types + ************************************************************************************/ uint32_t g_pwm_pinset[] = { - GPIO_M0_PWM0, - GPIO_M0_PWM1, - GPIO_M0_PWM2, - GPIO_M0_PWM3, - GPIO_M0_PWM4, - GPIO_M0_PWM5, - GPIO_M0_PWM6, - GPIO_M0_PWM7, + GPIO_M0_PWM0, + GPIO_M0_PWM1, + GPIO_M0_PWM2, + GPIO_M0_PWM3, + GPIO_M0_PWM4, + GPIO_M0_PWM5, + GPIO_M0_PWM6, + GPIO_M0_PWM7, }; struct tiva_pwm_chan_s { - const struct pwm_ops_s *ops; - uint8_t controller_id; - uintptr_t controller_base; - uint8_t generator_id; - uintptr_t generator_base; - uint8_t channel_id; + const struct pwm_ops_s *ops; + uint8_t controller_id; + uintptr_t controller_base; + uint8_t generator_id; + uintptr_t generator_base; + uint8_t channel_id; }; - /************************************************************************************ -* Private Function Prototypes -************************************************************************************/ + * Private Function Prototypes + ************************************************************************************/ -static inline void tiva_pwm_putreg(struct tiva_pwm_chan_s *chan, unsigned int offset, uint32_t regval); -static inline uint32_t tiva_pwm_getreg(struct tiva_pwm_chan_s *chan, unsigned int offset); +static inline void tiva_pwm_putreg(struct tiva_pwm_chan_s *chan, + unsigned int offset, uint32_t regval); +static inline uint32_t tiva_pwm_getreg(struct tiva_pwm_chan_s *chan, + unsigned int offset); static int tiva_pwm_setup(FAR struct pwm_lowerhalf_s *dev); static int tiva_pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); @@ -123,236 +105,246 @@ static int tiva_pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg); /************************************************************************************ -* Private Data -************************************************************************************/ + * Private Data + ************************************************************************************/ -uint32_t g_pwm_freq = 15000000; -uint32_t g_pwm_counter = (1 << 16); +static uint32_t g_pwm_freq = 15000000; +static uint32_t g_pwm_counter = (1 << 16); static const struct pwm_ops_s g_pwm_ops = { - .setup = tiva_pwm_setup, - .shutdown = tiva_pwm_shutdown, - .start = tiva_pwm_start, - .stop = tiva_pwm_stop, - .ioctl = tiva_pwm_ioctl, + .setup = tiva_pwm_setup, + .shutdown = tiva_pwm_shutdown, + .start = tiva_pwm_start, + .stop = tiva_pwm_stop, + .ioctl = tiva_pwm_ioctl, }; #ifdef CONFIG_TIVA_PWM0_CHAN0 static struct tiva_pwm_chan_s g_pwm_chan0 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 0, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 0, - .channel_id = 0, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 0, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 0, + .channel_id = 0, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN1 static struct tiva_pwm_chan_s g_pwm_chan1 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 0, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 0, - .channel_id = 1, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 0, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 0, + .channel_id = 1, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN2 static struct tiva_pwm_chan_s g_pwm_chan2 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 1, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 1, - .channel_id = 2, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 1, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 1, + .channel_id = 2, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN3 static struct tiva_pwm_chan_s g_pwm_chan3 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 1, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 1, - .channel_id = 3, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 1, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 1, + .channel_id = 3, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN4 static struct tiva_pwm_chan_s g_pwm_chan4 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 2, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 2, - .channel_id = 4, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 2, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 2, + .channel_id = 4, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN5 static struct tiva_pwm_chan_s g_pwm_chan5 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 2, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 2, - .channel_id = 5, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 2, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 2, + .channel_id = 5, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN6 static struct tiva_pwm_chan_s g_pwm_chan6 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 3, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 3, - .channel_id = 6, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 3, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 3, + .channel_id = 6, }; #endif #ifdef CONFIG_TIVA_PWM0_CHAN7 static struct tiva_pwm_chan_s g_pwm_chan7 = { - .ops = &g_pwm_ops, - .controller_id = 0, - .controller_base = TIVA_PWM0_BASE, - .generator_id = 3, - .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 3, - .channel_id = 7, + .ops = &g_pwm_ops, + .controller_id = 0, + .controller_base = TIVA_PWM0_BASE, + .generator_id = 3, + .generator_base = TIVA_PWM0_BASE + TIVA_PWMn_BASE + TIVA_PWMn_INTERVAL * 3, + .channel_id = 7, }; #endif /************************************************************************************ -* Private Functions -************************************************************************************/ + * Private Functions + ************************************************************************************/ /************************************************************************************ -* Name: tiva_pwm_getreg -* -* Description: -* Get a 32-bit register value by offset -* -************************************************************************************/ + * Name: tiva_pwm_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ************************************************************************************/ -static inline uint32_t tiva_pwm_getreg(struct tiva_pwm_chan_s *chan, unsigned int offset) +static inline uint32_t tiva_pwm_getreg(struct tiva_pwm_chan_s *chan, + unsigned int offset) { uintptr_t regaddr = chan->generator_base + offset; return getreg32(regaddr); } /************************************************************************************ -* Name: tiva_pwm_putreg -* -* Description: -* Put a 32-bit register value by offset -* -************************************************************************************/ + * Name: tiva_pwm_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ************************************************************************************/ -static inline void tiva_pwm_putreg(struct tiva_pwm_chan_s *chan, unsigned int offset, uint32_t regval) +static inline void tiva_pwm_putreg(struct tiva_pwm_chan_s *chan, + unsigned int offset, uint32_t regval) { uintptr_t regaddr = chan->generator_base + offset; putreg32(regval, regaddr); } /**************************************************************************** -* Name: pwm_setup -* -* Description: -* This method is called when the driver is opened. The lower half driver -* will be configured and initialized the device so that it is ready for -* use. It will not, however, output pulses until the start method is -* called. -* -* Input parameters: -* dev - A reference to the lower half PWM driver state structure -* -* Returned Value: -* Zero on success; a negated errno value on failure -* -****************************************************************************/ + * Name: pwm_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * will be configured and initialized the device so that it is ready for + * use. It will not, however, output pulses until the start method is + * called. + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ static int tiva_pwm_setup(FAR struct pwm_lowerhalf_s *dev) { FAR struct tiva_pwm_chan_s *chan = (FAR struct tiva_pwm_chan_s *)dev; - pwmdbg("setup PWM for channel %d\n", chan->channel_id); + pwminfo("setup PWM for channel %d\n", chan->channel_id); + + /* Enable GPIO port, GPIO pin type and GPIO alternate function (refer to + * TM4C1294NC 23.4.2-4) + */ - /* Enable GPIO port, GPIO pin type and GPIO alternate function (refer to TM4C1294NC 23.4.2-4) */ int ret = tiva_configgpio(g_pwm_pinset[chan->channel_id]); if (ret < 0) { - pwmerr("tiva_configgpio failed (%x)\n", g_pwm_pinset[chan->channel_id]); - return -1; + pwmerr("ERROR: tiva_configgpio failed (%x)\n", + g_pwm_pinset[chan->channel_id]); + return ret; } return OK; } /**************************************************************************** -* Name: pwm_shutdown -* -* Description: -* This method is called when the driver is closed. The lower half driver -* stop pulsed output, free any resources, disable the timer hardware, and -* put the system into the lowest possible power usage state -* -* Input parameters: -* dev - A reference to the lower half PWM driver state structure -* -* Returned Value: -* Zero on success; a negated errno value on failure -* -****************************************************************************/ + * Name: pwm_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * stop pulsed output, free any resources, disable the timer hardware, and + * put the system into the lowest possible power usage state + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ static int tiva_pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) { FAR struct tiva_pwm_chan_s *chan = (FAR struct tiva_pwm_chan_s *)dev; - pwmdbg("shutdown PWM for channel %d\n", chan->channel_id); + pwminfo("shutdown PWM for channel %d\n", chan->channel_id); /* Remove unused-variable warning */ + (void)chan; /* Ensure the PWM channel has been stopped */ + tiva_pwm_stop(dev); return OK; } /**************************************************************************** -* Name: pwm_start -* -* Description: -* (Re-)initialize the timer resources and start the pulsed output -* -* Input parameters: -* dev - A reference to the lower half PWM driver state structure -* info - A reference to the characteristics of the pulsed output -* -* Returned Value: -* Zero on success; a negated errno value on failure -* -****************************************************************************/ + * Name: pwm_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ -static int tiva_pwm_start(FAR struct pwm_lowerhalf_s *dev, FAR const struct pwm_info_s *info) +static int tiva_pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) { FAR struct tiva_pwm_chan_s *chan = (FAR struct tiva_pwm_chan_s *)dev; - pwmdbg("start PWM for channel %d\n", chan->channel_id); + pwminfo("start PWM for channel %d\n", chan->channel_id); uint16_t duty = info->duty; uint32_t frequency = info->frequency; /* Configure PWM countdown mode (refer to TM4C1294NC 23.4.6) */ + tiva_pwm_putreg(chan, TIVA_PWMn_CTL_OFFSET, 0); if (chan->channel_id % 2 == 0) { @@ -366,20 +358,27 @@ static int tiva_pwm_start(FAR struct pwm_lowerhalf_s *dev, FAR const struct pwm_ } /* Set the PWM period (refer to TM4C1294NC 23.4.7) */ + uint32_t pwm_min_freq = (uint32_t)(g_pwm_freq / g_pwm_counter) + 1; uint32_t pwm_max_freq = g_pwm_freq; uint32_t load = (uint32_t)(g_pwm_freq / frequency); - pwmdbg("channel %d: load = %u (%08x)\n", chan->channel_id, load, load); + + pwminfo("channel %d: load = %u (%08x)\n", chan->channel_id, load, load); + if (load >= g_pwm_counter || load < 1) { - pwmerr("frequency should be in [%d, %d] Hz\n", pwm_min_freq, pwm_max_freq); - return -1; + pwmerr("ERROR: frequency should be in [%d, %d] Hz\n", + pwm_min_freq, pwm_max_freq); + return -ERANGE; } + tiva_pwm_putreg(chan, TIVA_PWMn_LOAD_OFFSET, load - 1); /* Configure PWM duty (refer to TM4C1294NC 23.4.8-9) */ + uint32_t comp = (uint32_t)((1 - (float)duty / g_pwm_counter) * load); - pwmdbg("channel %d: comp = %u (%08x)\n", chan->channel_id, comp, comp); + pwminfo("channel %d: comp = %u (%08x)\n", chan->channel_id, comp, comp); + if (chan->channel_id % 2 == 0) { tiva_pwm_putreg(chan, TIVA_PWMn_CMPA_OFFSET, comp - 1); @@ -390,39 +389,41 @@ static int tiva_pwm_start(FAR struct pwm_lowerhalf_s *dev, FAR const struct pwm_ } /* Enable the PWM generator (refer to TM4C1294NC 23.4.10) */ + tiva_pwm_putreg(chan, TIVA_PWMn_CTL_OFFSET, CTL_ENABLE << TIVA_PWMn_CTL_ENABLE); /* Enable PWM channel (refer to TM4C1294NC 23.4.11) */ - putreg32((1 << chan->channel_id), chan->controller_base + TIVA_PWM_ENABLE_OFFSET); + putreg32((1 << chan->channel_id), chan->controller_base + TIVA_PWM_ENABLE_OFFSET); return OK; } /**************************************************************************** -* Name: pwm_stop -* -* Description: -* Stop the pulsed output and reset the timer resources -* -* Input parameters: -* dev - A reference to the lower half PWM driver state structure -* -* Returned Value: -* Zero on success; a negated errno value on failure -* -* Assumptions: -* This function is called to stop the pulsed output at anytime. This -* method is also called from the timer interrupt handler when a repetition -* count expires... automatically stopping the timer. -* -****************************************************************************/ + * Name: pwm_stop + * + * Description: + * Stop the pulsed output and reset the timer resources + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * This function is called to stop the pulsed output at anytime. This + * method is also called from the timer interrupt handler when a repetition + * count expires... automatically stopping the timer. + * + ****************************************************************************/ static int tiva_pwm_stop(FAR struct pwm_lowerhalf_s *dev) { FAR struct tiva_pwm_chan_s *chan = (FAR struct tiva_pwm_chan_s *)dev; - pwmdbg("stop PWM for channel %d\n", chan->channel_id); + pwminfo("stop PWM for channel %d\n", chan->channel_id); /* Disable PWM channel */ + uint32_t value = getreg32(chan->controller_base + TIVA_PWM_ENABLE_OFFSET); value &= ~(1 << chan->channel_id); putreg32(value, chan->controller_base + TIVA_PWM_ENABLE_OFFSET); @@ -431,52 +432,54 @@ static int tiva_pwm_stop(FAR struct pwm_lowerhalf_s *dev) } /**************************************************************************** -* Name: pwm_ioctl -* -* Description: -* Lower-half logic may support platform-specific ioctl commands -* -* Input parameters: -* dev - A reference to the lower half PWM driver state structure -* cmd - The ioctl command -* arg - The argument accompanying the ioctl command -* -* Returned Value: -* Zero on success; a negated errno value on failure -* -****************************************************************************/ + * Name: pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ -static int tiva_pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg) +static int tiva_pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg) { FAR struct tiva_pwm_chan_s *chan = (FAR struct tiva_pwm_chan_s *)dev; - pwmdbg("ioctl PWM for channel %d\n", chan->channel_id); + pwminfo("ioctl PWM for channel %d\n", chan->channel_id); /* Remove unused-variable warning */ + (void)chan; /* There are no platform-specific ioctl commands */ - return -1; + return -ENOTTY; } /**************************************************************************** -* Public Functions -****************************************************************************/ + * Public Functions + ****************************************************************************/ /**************************************************************************** -* Name: tiva_pwm_initialize -* -* Description: -* Initialize one PWM channel for use with the upper_level PWM driver. -* -* Input Parameters: -* channel - A number identifying the PWM channel use. -* -* Returned Value: -* On success, a pointer to the SAMA5 lower half PWM driver is returned. -* NULL is returned on any failure. -* -****************************************************************************/ + * Name: tiva_pwm_initialize + * + * Description: + * Initialize one PWM channel for use with the upper_level PWM driver. + * + * Input Parameters: + * channel - A number identifying the PWM channel use. + * + * Returned Value: + * On success, a pointer to the SAMA5 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ FAR struct pwm_lowerhalf_s *tiva_pwm_initialize(int channel) { @@ -538,39 +541,28 @@ FAR struct pwm_lowerhalf_s *tiva_pwm_initialize(int channel) return NULL; } - pwmvdbg("channel %d: channel_id=%d, ", channel, chan->channel_id); - pwmvdbg("controller_id=%d, controller_base=%08x, ", chan->controller_id, chan->controller_base); - pwmvdbg("generator_id=%d, generator_base=%08x\n", chan->generator_id, chan->generator_base); + pwminfo("channel %d: channel_id=%d, ", channel, chan->channel_id); + pwminfo("controller_id=%d, controller_base=%08x, ", + chan->controller_id, chan->controller_base); + pwminfo("generator_id=%d, generator_base=%08x\n", + chan->generator_id, chan->generator_base); /* Enable PWM controller (refer to TM4C1294NC 23.4.1) */ + assert(chan->controller_id == 0); tiva_pwm_enablepwr(chan->controller_id); tiva_pwm_enableclk(chan->controller_id); - /* Configure PWM Clock Configuration (refer to TM4C1294NC 23.4.5) */ - /* on TM4C1294NC, configure the PWM clock source as 15MHz (the system clock 120MHz divided by 8) */ - /* TODO: need an algorithm to choose the best divider and load value combo */ + /* Configure PWM Clock Configuration (refer to TM4C1294NC 23.4.5) + * + * On TM4C1294NC, configure the PWM clock source as 15MHz (the system + * clock 120MHz divided by 8) + * + * TODO: need an algorithm to choose the best divider and load value combo. + */ + putreg32(CC_USEPWM << TIVA_PWM_CC_USEPWM | CC_PWMDIV_8 << TIVA_PWM_CC_PWMDIV, chan->controller_base + TIVA_PWM_CC); return (FAR struct pwm_lowerhalf_s *)chan; } - -/**************************************************************************** -* Name: board_pwm_setup -* -* Description: -* No implementation for now, it's called by PWM tool. -* -* Input Parameters: -* None. -* -* Returned Value: -* Zero on Success. -* -****************************************************************************/ - -int board_pwm_setup(void) -{ - return OK; -} diff --git a/arch/arm/src/tiva/tiva_pwm.h b/arch/arm/src/tiva/tiva_pwm.h index d75e704c713..c60b37617b2 100644 --- a/arch/arm/src/tiva/tiva_pwm.h +++ b/arch/arm/src/tiva/tiva_pwm.h @@ -47,8 +47,6 @@ ****************************************************************************/ FAR struct pwm_lowerhalf_s *tiva_pwm_initialize(int channel); - void tm4c_pwm_register(int channel); -int board_pwm_setup(void); #endif /* __ARCH_ARM_SRC_TIVA_TIVA_PWM_H */ diff --git a/arch/sim/src/up_ioexpander.c b/arch/sim/src/up_ioexpander.c index ca2a3349100..4451e123cf3 100644 --- a/arch/sim/src/up_ioexpander.c +++ b/arch/sim/src/up_ioexpander.c @@ -701,7 +701,7 @@ static ioe_pinset_t sim_int_update(FAR struct sim_dev_s *priv) } else /* if (SIM_LEVEL_SENSITIVE(priv, pin)) */ { - /* Level triggered. Set intstat if imatch in level type. */ + /* Level triggered. Set intstat if match in level type. */ if ((pinval && SIM_LEVEL_HIGH(priv, pin)) || (!pinval && SIM_LEVEL_LOW(priv, pin))) diff --git a/configs/tm4c1294-launchpad/src/tm4c_appinit.c b/configs/tm4c1294-launchpad/src/tm4c_appinit.c index 2245ac31e9e..747c1f20df3 100644 --- a/configs/tm4c1294-launchpad/src/tm4c_appinit.c +++ b/configs/tm4c1294-launchpad/src/tm4c_appinit.c @@ -43,6 +43,8 @@ #include "tm4c1294-launchpad.h" +#ifdef CONFIG_LIB_BOARDCTL + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -84,3 +86,27 @@ int board_app_initialize(uintptr_t arg) return OK; #endif } + +/**************************************************************************** + * Name: board_pwm_setup + * + * Description: + * No implementation for now, it's called by PWM tool via boardctl(). + * See include/nuttx/board.h + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero on Success. + * + ****************************************************************************/ + +#ifdef CONFIG_BOARDCTL_PWMTEST +int board_pwm_setup(void) +{ + return OK; +} +#endif /* CONFIG_BOARDCTL_PWMTEST */ + +#endif /* CONFIG_LIB_BOARDCTL */ diff --git a/configs/tm4c1294-launchpad/src/tm4c_bringup.c b/configs/tm4c1294-launchpad/src/tm4c_bringup.c index e19d9e693d9..0cd3e2f2291 100644 --- a/configs/tm4c1294-launchpad/src/tm4c_bringup.c +++ b/configs/tm4c1294-launchpad/src/tm4c_bringup.c @@ -37,11 +37,10 @@ * Included Files ****************************************************************************/ -#include -#include - #include +#include +#include #include #include @@ -51,10 +50,6 @@ #include "tiva_pwm.h" #include "tm4c1294-launchpad.h" -#define PWM_PATH_FMT "/dev/pwm%d" -#define PWM_PATH_FMTLEN (10) - - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -67,6 +62,9 @@ # define HAVE_PWM #endif +#define PWM_PATH_FMT "/dev/pwm%d" +#define PWM_PATH_FMTLEN (10) + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -171,7 +169,7 @@ void tm4c_pwm_register(int channel) dev = tiva_pwm_initialize(channel); if (dev == NULL) { - dbg("ERROR: Failed to get PWM%d interface\n", channel); + pwmerr("ERROR: Failed to get PWM%d interface\n", channel); } else { @@ -179,7 +177,8 @@ void tm4c_pwm_register(int channel) ret = pwm_register(pwm_path, dev); if (ret < 0) { - dbg("ERROR: Failed to register PWM%d driver: %d\n", channel, ret); + pwmerr("ERROR: Failed to register PWM%d driver: %d\n", + channel, ret); } } } @@ -193,7 +192,6 @@ void tm4c_pwm_register(int channel) ****************************************************************************/ #ifdef HAVE_PWM - static void tm4c_pwm(void) { #ifdef CONFIG_TIVA_PWM0_CHAN0 @@ -221,8 +219,7 @@ static void tm4c_pwm(void) tm4c_pwm_register(7); #endif } - -#endif +#endif # HAVE_PWM /**************************************************************************** * Public Functions @@ -246,9 +243,11 @@ int tm4c_bringup(void) tm4c_i2ctool(); +#ifdef HAVE_PWM /* Register PWM drivers */ tm4c_pwm(); +#endif #ifdef HAVE_TIMER /* Initialize the timer driver */ diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 95bd1125007..b5a7399f2c0 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -30,5 +30,10 @@ config I2C_NTRACE config I2C_DRIVER bool "I2C character driver" default n + ---help--- + Build in support for a character driver at /dev/i2c[N] that may be + used to perform I2C bus transfers from applications. The intent of + this driver is to support I2C testing. It is not suitable for use + in any real driver application. endif # I2C diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ff38027e39e..b08c18fdba2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -47,13 +47,6 @@ config SPI_CALLBACK the SPI-based MMC/SD driver to get a notification of changes in the card status when an SD card is inserted or removed. -config SPI_BITBANG - bool "SPI bit-bang device" - default n - ---help--- - Enable support for a generic SPI bit-bang device. - See include/nuttx/spi/spi_bitbang.h for further information. - config SPI_HWFEATURES bool default n @@ -90,6 +83,23 @@ config SPI_CS_DELAY_CONTROL This option enables the setdelay() interface method. +config SPI_DRIVER + bool "SPI character driver" + default n + depends on SPI_EXCHANGE + ---help--- + Build in support for a character driver at /dev/spi[N] that may be + used to perform SPI bus transfers from applications. The intent of + this driver is to support SPI testing. It is not suitable for use + in any real driver application. + +config SPI_BITBANG + bool "SPI bit-bang device" + default n + ---help--- + Enable support for a generic SPI bit-bang device. + See include/nuttx/spi/spi_bitbang.h for further information. + if SPI_BITBANG config SPI_BITBANG_VARWIDTH diff --git a/drivers/spi/Make.defs b/drivers/spi/Make.defs index 43e5654faa8..e489bd600e4 100644 --- a/drivers/spi/Make.defs +++ b/drivers/spi/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # drivers/spi/Make.defs # -# Copyright (C) 2013 Gregory Nutt. All rights reserved. +# Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -37,6 +37,13 @@ ifeq ($(CONFIG_SPI),y) +ifeq ($(CONFIG_SPI_EXCHANGE),y) + CSRCS += spi_transfer.c + ifeq ($(CONFIG_SPI_DRIVER),y) + CSRCS += spi_driver.c + endif +endif + # Include the selected SPI drivers ifeq ($(CONFIG_SPI_BITBANG),y) diff --git a/drivers/spi/spi_driver.c b/drivers/spi/spi_driver.c new file mode 100644 index 00000000000..bbbfb46e685 --- /dev/null +++ b/drivers/spi/spi_driver.c @@ -0,0 +1,411 @@ +/**************************************************************************** + * drivers/spi/spi_driver.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SPI_DRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device naming ************************************************************/ + +#define DEVNAME_FMT "/dev/spi%d" +#define DEVNAME_FMTLEN (8 + 3 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Driver state structure */ + +struct spi_driver_s +{ + FAR struct spi_dev_s *spi; /* Contained SPI lower half driver */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + sem_t exclsem; /* Mutual exclusion */ + int16_t crefs; /* Number of open references */ + bool unlinked; /* True, driver has been unlinked */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int spidrvr_open(FAR struct file *filep); +static int spidrvr_close(FAR struct file *filep); +static ssize_t spidrvr_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t spidrvr_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int spidrvr_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_unlink(FAR struct inode *inode); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations spidrvr_fops = +{ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + spidrvr_open, /* open */ + spidrvr_close, /* close */ +#else + 0, /* open */ + 0, /* close */ +#endif + spidrvr_read, /* read */ + spidrvr_write, /* write */ + 0, /* seek */ + spidrvr_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , spidrvr_unlink /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spidrvr_open + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Increment the count of open references on the RTC driver */ + + priv->crefs++; + DEBUGASSERT(priv->crefs > 0); + + sem_post(&priv->exclsem); + return OK; +} +#endif + +/**************************************************************************** + * Name: spidrvr_close + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Decrement the count of open references on the RTC driver */ + + DEBUGASSERT(priv->crefs > 0); + priv->crefs--; + + /* If the count has decremented to zero and the driver has been unlinked, + * then commit Hara-Kiri now. + */ + + if (priv->crefs <= 0 && priv->unlinked) + { + sem_destroy(&priv->exclsem); + kmm_free(priv); + return OK; + } + + sem_post(&priv->exclsem); + return OK; +} +#endif + +/**************************************************************************** + * Name: spidrvr_read + ****************************************************************************/ + +static ssize_t spidrvr_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + return 0; /* Return EOF */ +} + +/**************************************************************************** + * Name: spidrvr_write + ****************************************************************************/ + +static ssize_t spidrvr_write(FAR struct file *filep, FAR const char *buffer, + size_t len) +{ + return len; /* Say that everything was written */ +} + +/**************************************************************************** + * Name: spidrvr_ioctl + ****************************************************************************/ + +static int spidrvr_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + FAR struct spi_sequence_s *seq; + int ret; + + spiinfo("cmd=%d arg=%lu\n", cmd, arg); + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Process the IOCTL command */ + + switch (cmd) + { + /* Command: SPIIOC_TRANSFER + * Description: Perform a sequence of SPI transfers + * Argument: A reference to an instance of struct spi_sequence_s. + * Dependencies: CONFIG_SPI_DRIVER + */ + + case SPIIOC_TRANSFER: + { + /* Get the reference to the spi_transfer_s structure */ + + seq = (FAR struct spi_sequence_s *)((uintptr_t)arg); + DEBUGASSERT(seq != NULL); + + /* Perform the transfer */ + + ret = spi_transfer(priv->spi, seq); + } + break; + + default: + ret = -ENOTTY; + break; + } + + sem_post(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: spidrvr_unlink + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_unlink(FAR struct inode *inode) +{ + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(inode != NULL && inode->i_private != NULL); + priv = (FAR struct spi_driver_s *)inode->i_private; + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Are there open references to the driver data structure? */ + + if (priv->crefs <= 0) + { + sem_destroy(&priv->exclsem); + kmm_free(priv); + return OK; + } + + /* No... just mark the driver as unlinked and free the resouces when the + * last client closes their reference to the driver. + */ + + priv->unlinked = true; + sem_post(&priv->exclsem); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spi_register + * + * Description: + * Create and register the SPI character driver. + * + * The SPI character driver is a simple character driver that supports SPI + * transfers. The intent of this driver is to support SPI testing. It is + * not suitable for use in any real driver application. + * + * Input Parameters: + * spi - An instance of the lower half SPI driver + * bus - The SPI bus number. This will be used as the SPI device minor + * number. The SPI character device will be registered as /dev/spiN + * where N is the minor number + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int spi_register(FAR struct spi_dev_s *spi, int bus) +{ + FAR struct spi_driver_s *priv; + char devname[DEVNAME_FMTLEN]; + int ret; + + /* Sanity check */ + + DEBUGASSERT(spi != NULL && (unsigned)bus < 1000); + + /* Allocate a SPI character device structure */ + + priv = (FAR struct spi_driver_s *)kmm_zalloc(sizeof(struct spi_driver_s)); + if (priv) + { + /* Initialize the SPI character device structure */ + + priv->spi = spi; +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + sem_init(&priv->exclsem, 0, 1); +#endif + + /* Create the character device name */ + + snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, bus); + ret = register_driver(devname, &spidrvr_fops, 0666, priv); + if (ret < 0) + { + /* Free the device structure if we failed to create the character + * device. + */ + + kmm_free(priv); + } + + /* Return the result of the registration */ + + return OK; + } + + return -ENOMEM; +} + +#endif /* CONFIG_SPI_DRIVER */ diff --git a/drivers/spi/spi_transfer.c b/drivers/spi/spi_transfer.c new file mode 100644 index 00000000000..74e312e3fe1 --- /dev/null +++ b/drivers/spi/spi_transfer.c @@ -0,0 +1,158 @@ +/**************************************************************************** + * drivers/spi/spi_transfer.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_SPI_EXCHANGE + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spi_transfer + * + * Description: + * This is a helper function that can be used to encapsulate and manage + * a sequence of SPI transfers. The SPI bus will be locked and the + * SPI device selected for the duration of the transfers. + * + * Input Parameters: + * spi - An instance of the SPI device to use for the transfer + * seq - Describes the sequence of transfers. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int spi_transfer(FAR struct spi_dev_s *spi, FAR struct spi_sequence_s *seq) +{ + FAR struct spi_trans_s *trans; + int ret = OK; + int i; + + DEBUGASSERT(spi != NULL && seq != NULL && seq->trans != NULL); + + /* Get exclusive access to the SPI bus */ + + SPI_LOCK(spi, true); + + /* Establish the fixed SPI attributes for all transfers in the sequence */ + + (void)SPI_SETFREQUENCY(spi, seq->frequency); + +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + ret = SPI_SETDELAY(spi, seq->a, seq->b, seq->c); + if (ret < 0) + { + spierr("ERROR: SPI_SETDELAY failed: %d\n", ret) + SPI_LOCK(spi, false); + return ret; + } +#endif + + SPI_SETMODE(spi, seq->mode); + SPI_SETBITS(spi, seq->nbits); + + /* Select the SPI device in preparation for the transfer */ + + SPI_SELECT(spi, seq->dev, true); + + /* Then perform each transfer is the sequence */ + + for (i = 0, trans = seq->trans; i < (int)seq->ntrans; i++, trans++) + { + /* Establish the fixed SPI attributes for unique to this transaction */ + +#ifdef CONFIG_SPI_HWFEATURES + ret = SPI_HWFEATURES(spi, trans->hwfeat); + if (ret < 0) + { + spierr("ERROR: SPI_HWFEATURES failed: %d\n", ret) + break; + } +#endif + +#ifdef CONFIG_SPI_CMDDATA + ret = SPI_CMDDATA(spi, seq->dev, trans->cmd); + if (ret < 0) + { + spierr("ERROR: SPI_CMDDATA failed: %d\n", ret) + break; + } +#endif + + /* [Re-]select the SPI device in preparation for the transfer */ + + SPI_SELECT(spi, seq->dev, true); + + /* Perform the transfer */ + + SPI_EXCHANGE(spi, trans->txbuffer, trans->rxbuffer, trans->nwords); + + /* Possibly de-select the SPI device after the transfer */ + + if (trans->deselect) + { + SPI_SELECT(spi, seq->dev, false); + } + + /* Perform any requested inter-transfer delay */ + + if (trans->delay > 0) + { + usleep(trans->delay); + } + } + + SPI_SELECT(spi, seq->dev, false); + SPI_LOCK(spi, false); + return ret; +} + +#endif /* CONFIG_SPI_EXCHANGE */ diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 63074a3a9bc..19fd8012b0f 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -83,9 +83,10 @@ #define _LOOPBASE (0x1e00) /* Loop device commands */ #define _MODEMBASE (0x1f00) /* Modem ioctl commands */ #define _I2CBASE (0x2000) /* I2C driver commands */ -#define _GPIOBASE (0x2100) /* GPIO driver commands */ +#define _SPIBASE (0x2100) /* SPI driver commands */ +#define _GPIOBASE (0x2200) /* GPIO driver commands */ -/* boardctl commands share the same number space */ +/* boardctl() commands share the same number space */ #define _BOARDBASE (0xff00) /* boardctl commands */ @@ -385,7 +386,14 @@ #define _I2CIOCVALID(c) (_IOC_TYPE(c)==_I2CBASE) #define _I2CIOC(nr) _IOC(_I2CBASE,nr) +/* SPI driver ioctl definitions **********************************************/ +/* see nuttx/include/spi/spi_transfer.h */ + +#define _SPIIOCVALID(c) (_IOC_TYPE(c)==_SPIBASE) +#define _SPIIOC(nr) _IOC(_SPIBASE,nr) + /* GPIO driver command definitions ******************************************/ +/* see nuttx/include/ioexpander/gpio.h */ #define _GPIOCVALID(c) (_IOC_TYPE(c)==_GPIOBASE) #define _GPIOC(nr) _IOC(_GPIOBASE,nr) diff --git a/include/nuttx/spi/spi.h b/include/nuttx/spi/spi.h index 45b041ffba6..53046fcc733 100644 --- a/include/nuttx/spi/spi.h +++ b/include/nuttx/spi/spi.h @@ -140,7 +140,8 @@ * csdelay - The delay between CS inactive and CS active again * * Returned Value: - * Returns the actual frequency selected + * Returns zero (OK) on success; a negated errno value is return on any + * failure. * ****************************************************************************/ diff --git a/include/nuttx/spi/spi_transfer.h b/include/nuttx/spi/spi_transfer.h new file mode 100644 index 00000000000..ef31e842650 --- /dev/null +++ b/include/nuttx/spi/spi_transfer.h @@ -0,0 +1,192 @@ +/**************************************************************************** + * include/nuttx/spi/spi_transfer.h + * + * Copyright(C) 2015-2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SPI_TRANSFER_H +#define __INCLUDE_NUTTX_SPI_TRANSFER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#ifdef CONFIG_SPI_EXCHANGE + +/* SPI Character Driver IOCTL Commands **************************************/ +/* The SPI driver is intended to support application testing of the SPI bus. + * The SPI driver simply provides a user-accessible wrapper around the + * OS internal spi_transfer() function. The following IOCTL commands to + * supported by the SPI driver to perform SPI transfers + */ + +/* Command: SPIIOC_TRANSFER + * Description: Perform a sequence of SPI transfers + * Argument: A reference to an instance of struct spi_sequence_s. + * Dependencies: CONFIG_SPI_DRIVER + */ + +#define SPIIOC_TRANSFER _SPIIOC(0x0001) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This describes one SPI transaction as handled by spi_transfer() */ + +struct spi_trans_s +{ + /* SPI attributes for unique to this transaction */ + + bool deselect; /* De-select after transfer */ +#ifdef CONFIG_SPI_CMDDATA + bool cmd; /* true=command; false=data */ +#endif +#ifdef CONFIG_SPI_HWFEATURES + spi_hwfeatures_t hwfeat; /* H/W features to enable on this transfer */ +#endif + useconds_t delay; /* Microsecond delay after transfer */ + + /* These describe the single data transfer */ + + size_t nwords; /* Number of words in transfer */ + FAR const void *txbuffer; /* Source buffer for TX transfer */ + FAR void *rxbuffer; /* Sink buffer for RX transfer */ +}; + +/* This describes a sequence of SPI transactions as handled by spi_transfer. + * + * Example usage: + * struct spi_trans_s mytrans[5]; + * struct spi_sequence_s myseq; + * ... + * myseq.ntrans = 5; + * myseq.trans = mytrans; + * ... + * int ret = spi_transfer(spi, myseq); + * ... + */ + +struct spi_sequence_s +{ + /* Properties that are fixed throughout the transfer */ + + uint8_t dev; /* See enum spi_dev_e */ + uint8_t mode; /* See enum spi_mode_e */ + uint8_t nbits; /* Number of bits */ + uint8_t ntrans; /* Number of transactions */ + uint32_t frequency; /* SPI frequency (Hz) */ +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + uint32_t a; /* Arguments to setdelay() */ + uint32_t b; + uint32_t c; +#endif + + /* A pointer to the list of transfers to be be performed. */ + + FAR struct spi_trans_s *trans; +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spi_transfer + * + * Description: + * This is a helper function that can be used to encapsulate and manage + * a sequence of SPI transfers. The SPI bus will be locked and the + * SPI device selected for the duration of the transfers. + * + * Input Parameters: + * spi - An instance of the SPI device to use for the transfer + * seq - Describes the sequence of transfers. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int spi_transfer(FAR struct spi_dev_s *spi, FAR struct spi_sequence_s *seq); + +/**************************************************************************** + * Name: spi_register + * + * Description: + * Create and register the SPI character driver. + * + * The SPI character driver is a simple character driver that supports SPI + * transfers. The intent of this driver is to support SPI testing. It is + * not suitable for use in any real driver application. + * + * Input Parameters: + * spi - An instance of the lower half SPI driver + * bus - The SPI bus number. This will be used as the SPI device minor + * number. The SPI character device will be registered as /dev/spiN + * where N is the minor number + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_DRIVER +int spi_register(FAR struct spi_dev_s *spi, int bus); +#endif + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* CONFIG_SPI_EXCHANGE */ +#endif /* __INCLUDE_NUTTX_SPI_TRANSFER_H */