zephyr/drivers/pwm/pwm_qmsi.c
Leandro Pereira 732424f065 drivers, net: Clean up semaphore initialization
Change the common "init with 0" + "give" idiom to "init with 1".  This
won't change the behavior or performance, but should decrease the size
ever so slightly.

This change has been performed mechanically with the following
Coccinelle script:

    @@
    expression SEM;
    expression LIMIT;
    expression TIMEOUT;
    @@

    - k_sem_init(SEM, 0, LIMIT);
    - k_sem_give(SEM);
    + k_sem_init(SEM, 1, LIMIT);

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
2017-07-27 15:23:07 -04:00

271 lines
6.3 KiB
C

/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <pwm.h>
#include <device.h>
#include <kernel.h>
#include <init.h>
#include <power.h>
#include <misc/util.h>
#include "qm_pwm.h"
#include "clk.h"
#define HW_CLOCK_CYCLES_PER_USEC (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / \
USEC_PER_SEC)
/* pwm uses 32 bits counter to control low and high period */
#define MAX_LOW_PERIOD_IN_HW_CLOCK_CYCLES (((u64_t)1) << 32)
#define MAX_HIGH_PERIOD_IN_HW_CLOCK_CYCLES (((u64_t)1) << 32)
#define MAX_PERIOD_IN_HW_CLOCK_CYCLES (MAX_LOW_PERIOD_IN_HW_CLOCK_CYCLES + \
MAX_HIGH_PERIOD_IN_HW_CLOCK_CYCLES)
/* in micro seconds. */
#define MAX_PERIOD (MAX_PERIOD_IN_HW_CLOCK_CYCLES / HW_CLOCK_CYCLES_PER_USEC)
/**
* in micro seconds. To be able to get 1% granularity, MIN_PERIOD should
* have at least 100 HW clock cycles.
*/
#define MIN_PERIOD ((100 + (HW_CLOCK_CYCLES_PER_USEC - 1)) / \
HW_CLOCK_CYCLES_PER_USEC)
/* in micro seconds */
#define DEFAULT_PERIOD 2000
struct pwm_data {
#ifdef CONFIG_PWM_QMSI_API_REENTRANCY
struct k_sem sem;
#endif
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
u32_t device_power_state;
#endif
u32_t channel_period[CONFIG_PWM_QMSI_NUM_PORTS];
};
static struct pwm_data pwm_context;
#ifdef CONFIG_PWM_QMSI_API_REENTRANCY
#define RP_GET(dev) (&((struct pwm_data *)(dev->driver_data))->sem)
#else
#define RP_GET(dev) (NULL)
#endif
static int __set_one_port(struct device *dev, qm_pwm_t id, u32_t pwm,
u32_t on, u32_t off)
{
qm_pwm_config_t cfg;
int ret_val = 0;
if (IS_ENABLED(CONFIG_PWM_QMSI_API_REENTRANCY)) {
k_sem_take(RP_GET(dev), K_FOREVER);
}
/* Disable timer to prevent any output */
qm_pwm_stop(id, pwm);
if (on == 0) {
/* stop PWM if so specified */
goto pwm_set_port_return;
}
/**
* off period must be more than zero. Otherwise, the PWM pin will be
* turned off. Let's use the minimum value which is 1 for this case.
*/
if (off == 0) {
off = 1;
}
/* PWM mode, user-defined count mode, timer disabled */
cfg.mode = QM_PWM_MODE_PWM;
/* No interrupts */
cfg.mask_interrupt = true;
cfg.callback = NULL;
cfg.callback_data = NULL;
/* Data for the timer to stay high and low */
cfg.hi_count = on;
cfg.lo_count = off;
if (qm_pwm_set_config(id, pwm, &cfg) != 0) {
ret_val = -EIO;
goto pwm_set_port_return;
}
/* Enable timer so it starts running and counting */
qm_pwm_start(id, pwm);
pwm_set_port_return:
if (IS_ENABLED(CONFIG_PWM_QMSI_API_REENTRANCY)) {
k_sem_give(RP_GET(dev));
}
return ret_val;
}
/*
* Set the period and pulse width for a PWM pin.
*
* For example, with a nominal system clock of 32MHz, each count represents
* 31.25ns (e.g. period = 100 means the pulse is to repeat every 3125ns). The
* duration of one count depends on system clock. Refer to the hardware manual
* for more information.
*
* Parameters
* dev: Pointer to PWM device structure
* pwm: PWM port number to set
* period_cycles: Period (in timer count)
* pulse_cycles: Pulse width (in timer count).
*
* return 0, or negative errno code
*/
static int pwm_qmsi_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
{
u32_t high, low;
if (pwm >= CONFIG_PWM_QMSI_NUM_PORTS) {
return -EINVAL;
}
if (period_cycles == 0 || pulse_cycles > period_cycles) {
return -EINVAL;
}
high = pulse_cycles;
low = period_cycles - pulse_cycles;
/*
* low must be more than zero. Otherwise, the PWM pin will be
* turned off. Let's make sure low is always more than zero.
*/
if (low == 0) {
high--;
low = 1;
}
return __set_one_port(dev, QM_PWM_0, pwm, high, low);
}
/*
* Get the clock rate (cycles per second) for a PWM pin.
*
* Parameters
* dev: Pointer to PWM device structure
* pwm: PWM port number
* cycles: Pointer to the memory to store clock rate (cycles per second)
*
* return 0, or negative errno code
*/
static int pwm_qmsi_get_cycles_per_sec(struct device *dev, u32_t pwm,
u64_t *cycles)
{
if (cycles == NULL) {
return -EINVAL;
}
*cycles = (u64_t)clk_sys_get_ticks_per_us() * USEC_PER_SEC;
return 0;
}
static const struct pwm_driver_api pwm_qmsi_drv_api_funcs = {
.pin_set = pwm_qmsi_pin_set,
.get_cycles_per_sec = pwm_qmsi_get_cycles_per_sec,
};
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static void pwm_qmsi_set_power_state(struct device *dev, u32_t power_state)
{
struct pwm_data *context = dev->driver_data;
context->device_power_state = power_state;
}
#else
#define pwm_qmsi_set_power_state(...)
#endif
static int pwm_qmsi_init(struct device *dev)
{
struct pwm_data *context = dev->driver_data;
u32_t *channel_period = context->channel_period;
for (int i = 0; i < CONFIG_PWM_QMSI_NUM_PORTS; i++) {
channel_period[i] = DEFAULT_PERIOD *
HW_CLOCK_CYCLES_PER_USEC;
}
clk_periph_enable(CLK_PERIPH_PWM_REGISTER | CLK_PERIPH_CLK);
if (IS_ENABLED(CONFIG_PWM_QMSI_API_REENTRANCY)) {
k_sem_init(RP_GET(dev), 1, UINT_MAX);
}
pwm_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static qm_pwm_context_t pwm_ctx;
static u32_t pwm_qmsi_get_power_state(struct device *dev)
{
struct pwm_data *context = dev->driver_data;
return context->device_power_state;
}
static int pwm_qmsi_suspend(struct device *dev)
{
qm_pwm_save_context(QM_PWM_0, &pwm_ctx);
pwm_qmsi_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
return 0;
}
static int pwm_qmsi_resume_from_suspend(struct device *dev)
{
qm_pwm_restore_context(QM_PWM_0, &pwm_ctx);
pwm_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
/*
* Implements the driver control management functionality
* the *context may include IN data or/and OUT data
*/
static int pwm_qmsi_device_ctrl(struct device *dev, u32_t ctrl_command,
void *context)
{
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
return pwm_qmsi_suspend(dev);
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
return pwm_qmsi_resume_from_suspend(dev);
}
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
*((u32_t *)context) = pwm_qmsi_get_power_state(dev);
return 0;
}
return 0;
}
#endif
DEVICE_DEFINE(pwm_qmsi_0, CONFIG_PWM_QMSI_DEV_NAME, pwm_qmsi_init,
pwm_qmsi_device_ctrl, &pwm_context, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&pwm_qmsi_drv_api_funcs);