Commit e04b1bff authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'counter-updates-for-6.5a' of...

Merge tag 'counter-updates-for-6.5a' of git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter into char-misc-next

William writes:

First set of Counter updates for the 6.5 cycle

Biggest changes in this set include the introduction of a new Intel 8254
interface library module and the refactoring of the existing 104-quad-8
modules to migrate it to the regmap API. Some other minor cleanups
touching tools/counter and stm32-timer-cnt are also present.

Changes
* 104-quad-8
  - Remove reference in Kconfig to 25-bit counter value
  - Utilize bitfield access macros
  - Refactor to buffer states for CMR, IOR, and IDR
  - Utilize helper functions to handle PR, FLAG and PSC
  - Migrate to the regmap API
* i8254
  - Introduce the Intel 8254 interface library module
* stm32-timer-cnt
  - Reset TIM_TISEL to its default value in probe
* tools/counter
  - Add .gitignore
  - Remove lingering 'include' directories on make clean

* tag 'counter-updates-for-6.5a' of git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter:
  counter: i8254: Introduce the Intel 8254 interface library module
  counter: 104-quad-8: Migrate to the regmap API
  counter: 104-quad-8: Utilize helper functions to handle PR, FLAG and PSC
  counter: 104-quad-8: Refactor to buffer states for CMR, IOR, and IDR
  counter: 104-quad-8: Utilize bitfield access macros
  tools/counter: Makefile: Remove lingering 'include' directories on make clean
  tools/counter: Add .gitignore
  counter: stm32-timer-cnt: Reset TIM_TISEL to its default value in probe
  counter: 104-quad-8: Remove reference in Kconfig to 25-bit counter value
parents be2c2edf d4284874
......@@ -90,6 +90,60 @@ Description:
counter does not freeze at the boundary points, but
counts continuously throughout.
interrupt on terminal count:
The output signal is initially low, and will remain low
until the counter reaches zero. The output signal then
goes high and remains high until a new preset value is
set.
hardware retriggerable one-shot:
The output signal is initially high. The output signal
will go low by a trigger input signal, and will remain
low until the counter reaches zero. The output will then
go high and remain high until the next trigger. A
trigger results in loading the counter to the preset
value and setting the output signal low, thus starting
the one-shot pulse.
rate generator:
The output signal is initially high. When the counter
has decremented to 1, the output signal goes low for one
clock pulse. The output signal then goes high again, the
counter is reloaded to the preset value, and the process
repeats in a periodic manner as such.
square wave mode:
The output signal is initially high.
If the initial count is even, the counter is decremented
by two on succeeding clock pulses. When the count
expires, the output signal changes value and the
counter is reloaded to the preset value. The process
repeats in periodic manner as such.
If the initial count is odd, the initial count minus one
(an even number) is loaded and then is decremented by
two on succeeding clock pulses. One clock pulse after
the count expires, the output signal goes low and the
counter is reloaded to the preset value minus one.
Succeeding clock pulses decrement the count by two. When
the count expires, the output goes high again and the
counter is reloaded to the preset value minus one. The
process repeats in a periodic manner as such.
software triggered strobe:
The output signal is initially high. When the count
expires, the output will go low for one clock pulse and
then go high again. The counting sequence is "triggered"
by setting the preset value.
hardware triggered strobe:
The output signal is initially high. Counting is started
by a trigger input signal. When the count expires, the
output signal will go low for one clock pulse and then
go high again. A trigger results in loading the counter
to the preset value.
What: /sys/bus/counter/devices/counterX/countY/count_mode_available
What: /sys/bus/counter/devices/counterX/countY/error_noise_available
What: /sys/bus/counter/devices/counterX/countY/function_available
......
......@@ -10260,6 +10260,13 @@ L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/video/fbdev/i810/
INTEL 8254 COUNTER DRIVER
M: William Breathitt Gray <william.gray@linaro.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/counter/i8254.c
F: include/linux/i8254.h
INTEL 8255 GPIO DRIVER
M: William Breathitt Gray <william.gray@linaro.org>
L: linux-gpio@vger.kernel.org
......
......@@ -5,10 +5,11 @@
*
* This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/counter.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
......@@ -17,8 +18,11 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#define QUAD8_EXTENT 32
......@@ -34,118 +38,196 @@ MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
#define QUAD8_NUM_COUNTERS 8
/**
* struct channel_reg - channel register structure
* @data: Count data
* @control: Channel flags and control
*/
struct channel_reg {
u8 data;
u8 control;
};
/**
* struct quad8_reg - device register structure
* @channel: quadrature counter data and control
* @interrupt_status: channel interrupt status
* @channel_oper: enable/reset counters and interrupt functions
* @index_interrupt: enable channel interrupts
* @reserved: reserved for Factory Use
* @index_input_levels: index signal logical input level
* @cable_status: differential encoder cable status
*/
struct quad8_reg {
struct channel_reg channel[QUAD8_NUM_COUNTERS];
u8 interrupt_status;
u8 channel_oper;
u8 index_interrupt;
u8 reserved[3];
u8 index_input_levels;
u8 cable_status;
};
#define QUAD8_DATA(_channel) ((_channel) * 2)
#define QUAD8_CONTROL(_channel) (QUAD8_DATA(_channel) + 1)
#define QUAD8_INTERRUPT_STATUS 0x10
#define QUAD8_CHANNEL_OPERATION 0x11
#define QUAD8_INDEX_INTERRUPT 0x12
#define QUAD8_INDEX_INPUT_LEVELS 0x16
#define QUAD8_CABLE_STATUS 0x17
/**
* struct quad8 - device private data structure
* @lock: lock to prevent clobbering device states during R/W ops
* @counter: instance of the counter_device
* @cmr: array of Counter Mode Register states
* @ior: array of Input / Output Control Register states
* @idr: array of Index Control Register states
* @fck_prescaler: array of filter clock prescaler configurations
* @preset: array of preset values
* @count_mode: array of count mode configurations
* @quadrature_mode: array of quadrature mode configurations
* @quadrature_scale: array of quadrature mode scale configurations
* @ab_enable: array of A and B inputs enable configurations
* @preset_enable: array of set_to_preset_on_index attribute configurations
* @irq_trigger: array of current IRQ trigger function configurations
* @synchronous_mode: array of index function synchronous mode configurations
* @index_polarity: array of index function polarity configurations
* @cable_fault_enable: differential encoder cable status enable configurations
* @reg: I/O address offset for the device registers
* @map: regmap for the device
*/
struct quad8 {
spinlock_t lock;
u8 cmr[QUAD8_NUM_COUNTERS];
u8 ior[QUAD8_NUM_COUNTERS];
u8 idr[QUAD8_NUM_COUNTERS];
unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
unsigned int preset[QUAD8_NUM_COUNTERS];
unsigned int count_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
unsigned int ab_enable[QUAD8_NUM_COUNTERS];
unsigned int preset_enable[QUAD8_NUM_COUNTERS];
unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
unsigned int index_polarity[QUAD8_NUM_COUNTERS];
unsigned int cable_fault_enable;
struct quad8_reg __iomem *reg;
struct regmap *map;
};
static const struct regmap_range quad8_wr_ranges[] = {
regmap_reg_range(0x0, 0xF), regmap_reg_range(0x11, 0x12), regmap_reg_range(0x17, 0x17),
};
static const struct regmap_range quad8_rd_ranges[] = {
regmap_reg_range(0x0, 0x12), regmap_reg_range(0x16, 0x18),
};
static const struct regmap_access_table quad8_wr_table = {
.yes_ranges = quad8_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(quad8_wr_ranges),
};
static const struct regmap_access_table quad8_rd_table = {
.yes_ranges = quad8_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(quad8_rd_ranges),
};
static const struct regmap_config quad8_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
.wr_table = &quad8_wr_table,
.rd_table = &quad8_rd_table,
};
/* Error flag */
#define QUAD8_FLAG_E BIT(4)
#define FLAG_E BIT(4)
/* Up/Down flag */
#define QUAD8_FLAG_UD BIT(5)
#define FLAG_UD BIT(5)
/* Counting up */
#define UP 0x1
#define REGISTER_SELECTION GENMASK(6, 5)
/* Reset and Load Signal Decoders */
#define QUAD8_CTR_RLD 0x00
#define SELECT_RLD u8_encode_bits(0x0, REGISTER_SELECTION)
/* Counter Mode Register */
#define QUAD8_CTR_CMR 0x20
#define SELECT_CMR u8_encode_bits(0x1, REGISTER_SELECTION)
/* Input / Output Control Register */
#define QUAD8_CTR_IOR 0x40
#define SELECT_IOR u8_encode_bits(0x2, REGISTER_SELECTION)
/* Index Control Register */
#define QUAD8_CTR_IDR 0x60
#define SELECT_IDR u8_encode_bits(0x3, REGISTER_SELECTION)
/*
* Reset and Load Signal Decoders
*/
#define RESETS GENMASK(2, 1)
#define LOADS GENMASK(4, 3)
/* Reset Byte Pointer (three byte data pointer) */
#define QUAD8_RLD_RESET_BP 0x01
/* Reset Counter */
#define QUAD8_RLD_RESET_CNTR 0x02
/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
#define QUAD8_RLD_RESET_FLAGS 0x04
#define RESET_BP BIT(0)
/* Reset Borrow Toggle, Carry toggle, Compare toggle, Sign, and Index flags */
#define RESET_BT_CT_CPT_S_IDX u8_encode_bits(0x2, RESETS)
/* Reset Error flag */
#define QUAD8_RLD_RESET_E 0x06
#define RESET_E u8_encode_bits(0x3, RESETS)
/* Preset Register to Counter */
#define QUAD8_RLD_PRESET_CNTR 0x08
#define TRANSFER_PR_TO_CNTR u8_encode_bits(0x1, LOADS)
/* Transfer Counter to Output Latch */
#define QUAD8_RLD_CNTR_OUT 0x10
#define TRANSFER_CNTR_TO_OL u8_encode_bits(0x2, LOADS)
/* Transfer Preset Register LSB to FCK Prescaler */
#define QUAD8_RLD_PRESET_PSC 0x18
#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
#define QUAD8_CMR_QUADRATURE_X1 0x08
#define QUAD8_CMR_QUADRATURE_X2 0x10
#define QUAD8_CMR_QUADRATURE_X4 0x18
#define TRANSFER_PR0_TO_PSC u8_encode_bits(0x3, LOADS)
/*
* Counter Mode Registers
*/
#define COUNT_ENCODING BIT(0)
#define COUNT_MODE GENMASK(2, 1)
#define QUADRATURE_MODE GENMASK(4, 3)
/* Binary count */
#define BINARY u8_encode_bits(0x0, COUNT_ENCODING)
/* Normal count */
#define NORMAL_COUNT 0x0
/* Range Limit */
#define RANGE_LIMIT 0x1
/* Non-recycle count */
#define NON_RECYCLE_COUNT 0x2
/* Modulo-N */
#define MODULO_N 0x3
/* Non-quadrature */
#define NON_QUADRATURE 0x0
/* Quadrature X1 */
#define QUADRATURE_X1 0x1
/* Quadrature X2 */
#define QUADRATURE_X2 0x2
/* Quadrature X4 */
#define QUADRATURE_X4 0x3
/*
* Input/Output Control Register
*/
#define AB_GATE BIT(0)
#define LOAD_PIN BIT(1)
#define FLG_PINS GENMASK(4, 3)
/* Disable inputs A and B */
#define DISABLE_AB u8_encode_bits(0x0, AB_GATE)
/* Load Counter input */
#define LOAD_CNTR 0x0
/* FLG1 = CARRY(active low); FLG2 = BORROW(active low) */
#define FLG1_CARRY_FLG2_BORROW 0x0
/* FLG1 = COMPARE(active low); FLG2 = BORROW(active low) */
#define FLG1_COMPARE_FLG2_BORROW 0x1
/* FLG1 = Carry(active low)/Borrow(active low); FLG2 = U/D(active low) flag */
#define FLG1_CARRYBORROW_FLG2_UD 0x2
/* FLG1 = INDX (low pulse at INDEX pin active level); FLG2 = E flag */
#define FLG1_INDX_FLG2_E 0x3
/*
* INDEX CONTROL REGISTERS
*/
#define INDEX_MODE BIT(0)
#define INDEX_POLARITY BIT(1)
/* Disable Index mode */
#define DISABLE_INDEX_MODE 0x0
/* Enable Index mode */
#define ENABLE_INDEX_MODE 0x1
/* Negative Index Polarity */
#define NEGATIVE_INDEX_POLARITY 0x0
/* Positive Index Polarity */
#define POSITIVE_INDEX_POLARITY 0x1
/*
* Channel Operation Register
*/
#define COUNTERS_OPERATION BIT(0)
#define INTERRUPT_FUNCTION BIT(2)
/* Enable all Counters */
#define ENABLE_COUNTERS u8_encode_bits(0x0, COUNTERS_OPERATION)
/* Reset all Counters */
#define RESET_COUNTERS u8_encode_bits(0x1, COUNTERS_OPERATION)
/* Disable the interrupt function */
#define DISABLE_INTERRUPT_FUNCTION u8_encode_bits(0x0, INTERRUPT_FUNCTION)
/* Enable the interrupt function */
#define ENABLE_INTERRUPT_FUNCTION u8_encode_bits(0x1, INTERRUPT_FUNCTION)
/* Any write to the Channel Operation register clears any pending interrupts */
#define CLEAR_PENDING_INTERRUPTS (ENABLE_COUNTERS | ENABLE_INTERRUPT_FUNCTION)
/* Each Counter is 24 bits wide */
#define LS7267_CNTR_MAX GENMASK(23, 0)
static __always_inline int quad8_control_register_update(struct regmap *const map, u8 *const buf,
const size_t channel, const u8 val,
const u8 field)
{
u8p_replace_bits(&buf[channel], val, field);
return regmap_write(map, QUAD8_CONTROL(channel), buf[channel]);
}
static int quad8_signal_read(struct counter_device *counter,
struct counter_signal *signal,
enum counter_signal_level *level)
{
const struct quad8 *const priv = counter_priv(counter);
unsigned int state;
int ret;
/* Only Index signal levels can be read */
if (signal->id < 16)
return -EINVAL;
state = ioread8(&priv->reg->index_input_levels) & BIT(signal->id - 16);
ret = regmap_test_bits(priv->map, QUAD8_INDEX_INPUT_LEVELS, BIT(signal->id - 16));
if (ret < 0)
return ret;
*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
*level = (ret) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
return 0;
}
......@@ -154,65 +236,81 @@ static int quad8_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
struct quad8 *const priv = counter_priv(counter);
struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
unsigned long irqflags;
int i;
*val = 0;
u8 value[3];
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
/* Reset Byte Pointer; transfer Counter to Output Latch */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
&chan->control);
for (i = 0; i < 3; i++)
*val |= (unsigned long)ioread8(&chan->data) << (8 * i);
ret = regmap_write(priv->map, QUAD8_CONTROL(count->id),
SELECT_RLD | RESET_BP | TRANSFER_CNTR_TO_OL);
if (ret)
goto exit_unlock;
ret = regmap_noinc_read(priv->map, QUAD8_DATA(count->id), value, sizeof(value));
exit_unlock:
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
*val = get_unaligned_le24(value);
return ret;
}
static int quad8_preset_register_set(struct quad8 *const priv, const size_t id,
const unsigned long preset)
{
u8 value[3];
int ret;
put_unaligned_le24(preset, value);
ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BP);
if (ret)
return ret;
return regmap_noinc_write(priv->map, QUAD8_DATA(id), value, sizeof(value));
}
static int quad8_flag_register_reset(struct quad8 *const priv, const size_t id)
{
int ret;
ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BT_CT_CPT_S_IDX);
if (ret)
return ret;
return regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_E);
}
static int quad8_count_write(struct counter_device *counter,
struct counter_count *count, u64 val)
{
struct quad8 *const priv = counter_priv(counter);
struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
unsigned long irqflags;
int i;
int ret;
if (val > LS7267_CNTR_MAX)
return -ERANGE;
spin_lock_irqsave(&priv->lock, irqflags);
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
/* Counter can only be set via Preset Register */
for (i = 0; i < 3; i++)
iowrite8(val >> (8 * i), &chan->data);
ret = quad8_preset_register_set(priv, count->id, val);
if (ret)
goto exit_unlock;
ret = regmap_write(priv->map, QUAD8_CONTROL(count->id), SELECT_RLD | TRANSFER_PR_TO_CNTR);
if (ret)
goto exit_unlock;
/* Transfer Preset Register to Counter */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, &chan->control);
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
ret = quad8_flag_register_reset(priv, count->id);
if (ret)
goto exit_unlock;
/* Set Preset Register back to original value */
val = priv->preset[count->id];
for (i = 0; i < 3; i++)
iowrite8(val >> (8 * i), &chan->data);
/* Reset Borrow, Carry, Compare, and Sign flags */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
/* Reset Error flag */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
ret = quad8_preset_register_set(priv, count->id, priv->preset[count->id]);
exit_unlock:
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static const enum counter_function quad8_count_functions_list[] = {
......@@ -225,19 +323,17 @@ static const enum counter_function quad8_count_functions_list[] = {
static int quad8_function_get(const struct quad8 *const priv, const size_t id,
enum counter_function *const function)
{
if (!priv->quadrature_mode[id]) {
switch (u8_get_bits(priv->cmr[id], QUADRATURE_MODE)) {
case NON_QUADRATURE:
*function = COUNTER_FUNCTION_PULSE_DIRECTION;
return 0;
}
switch (priv->quadrature_scale[id]) {
case 0:
case QUADRATURE_X1:
*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
return 0;
case 1:
case QUADRATURE_X2:
*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
return 0;
case 2:
case QUADRATURE_X4:
*function = COUNTER_FUNCTION_QUADRATURE_X4;
return 0;
default:
......@@ -269,60 +365,46 @@ static int quad8_function_write(struct counter_device *counter,
{
struct quad8 *const priv = counter_priv(counter);
const int id = count->id;
unsigned int *const quadrature_mode = priv->quadrature_mode + id;
unsigned int *const scale = priv->quadrature_scale + id;
unsigned int *const synchronous_mode = priv->synchronous_mode + id;
u8 __iomem *const control = &priv->reg->channel[id].control;
unsigned long irqflags;
unsigned int mode_cfg;
unsigned int idr_cfg;
bool synchronous_mode;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
mode_cfg = priv->count_mode[id] << 1;
idr_cfg = priv->index_polarity[id] << 1;
if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
*quadrature_mode = 0;
/* Quadrature scaling only available in quadrature mode */
*scale = 0;
switch (function) {
case COUNTER_FUNCTION_PULSE_DIRECTION:
mode_cfg = NON_QUADRATURE;
break;
case COUNTER_FUNCTION_QUADRATURE_X1_A:
mode_cfg = QUADRATURE_X1;
break;
case COUNTER_FUNCTION_QUADRATURE_X2_A:
mode_cfg = QUADRATURE_X2;
break;
case COUNTER_FUNCTION_QUADRATURE_X4:
mode_cfg = QUADRATURE_X4;
break;
default:
/* should never reach this path */
return -EINVAL;
}
/* Synchronous function not supported in non-quadrature mode */
if (*synchronous_mode) {
*synchronous_mode = 0;
/* Disable synchronous function mode */
iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
}
} else {
*quadrature_mode = 1;
spin_lock_irqsave(&priv->lock, irqflags);
switch (function) {
case COUNTER_FUNCTION_QUADRATURE_X1_A:
*scale = 0;
mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
break;
case COUNTER_FUNCTION_QUADRATURE_X2_A:
*scale = 1;
mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
break;
case COUNTER_FUNCTION_QUADRATURE_X4:
*scale = 2;
mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
break;
default:
/* should never reach this path */
spin_unlock_irqrestore(&priv->lock, irqflags);
return -EINVAL;
}
/* Synchronous function not supported in non-quadrature mode */
synchronous_mode = u8_get_bits(priv->idr[id], INDEX_MODE) == ENABLE_INDEX_MODE;
if (synchronous_mode && mode_cfg == NON_QUADRATURE) {
ret = quad8_control_register_update(priv->map, priv->idr, id, DISABLE_INDEX_MODE,
INDEX_MODE);
if (ret)
goto exit_unlock;
}
/* Load mode configuration to Counter Mode Register */
iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
ret = quad8_control_register_update(priv->map, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
exit_unlock:
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_direction_read(struct counter_device *counter,
......@@ -330,13 +412,13 @@ static int quad8_direction_read(struct counter_device *counter,
enum counter_count_direction *direction)
{
const struct quad8 *const priv = counter_priv(counter);
unsigned int ud_flag;
u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
/* U/D flag: nonzero = up, zero = down */
ud_flag = ioread8(flag_addr) & QUAD8_FLAG_UD;
unsigned int flag;
int ret;
*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
ret = regmap_read(priv->map, QUAD8_CONTROL(count->id), &flag);
if (ret)
return ret;
*direction = (u8_get_bits(flag, FLAG_UD) == UP) ? COUNTER_COUNT_DIRECTION_FORWARD :
COUNTER_COUNT_DIRECTION_BACKWARD;
return 0;
......@@ -366,13 +448,13 @@ static int quad8_action_read(struct counter_device *counter,
const size_t signal_a_id = count->synapses[0].signal->id;
enum counter_count_direction direction;
/* Default action mode */
*action = COUNTER_SYNAPSE_ACTION_NONE;
/* Handle Index signals */
if (synapse->signal->id >= 16) {
if (!priv->preset_enable[count->id])
if (u8_get_bits(priv->ior[count->id], LOAD_PIN) == LOAD_CNTR)
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
else
*action = COUNTER_SYNAPSE_ACTION_NONE;
return 0;
}
......@@ -392,9 +474,6 @@ static int quad8_action_read(struct counter_device *counter,
spin_unlock_irqrestore(&priv->lock, irqflags);
/* Default action mode */
*action = COUNTER_SYNAPSE_ACTION_NONE;
/* Determine action mode based on current count function mode */
switch (function) {
case COUNTER_FUNCTION_PULSE_DIRECTION:
......@@ -422,67 +501,57 @@ static int quad8_action_read(struct counter_device *counter,
}
}
enum {
QUAD8_EVENT_CARRY = 0,
QUAD8_EVENT_COMPARE = 1,
QUAD8_EVENT_CARRY_BORROW = 2,
QUAD8_EVENT_INDEX = 3,
};
static int quad8_events_configure(struct counter_device *counter)
{
struct quad8 *const priv = counter_priv(counter);
unsigned long irq_enabled = 0;
unsigned long irqflags;
struct counter_event_node *event_node;
unsigned int next_irq_trigger;
unsigned long ior_cfg;
u8 flg_pins;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
list_for_each_entry(event_node, &counter->events_list, l) {
switch (event_node->event) {
case COUNTER_EVENT_OVERFLOW:
next_irq_trigger = QUAD8_EVENT_CARRY;
flg_pins = FLG1_CARRY_FLG2_BORROW;
break;
case COUNTER_EVENT_THRESHOLD:
next_irq_trigger = QUAD8_EVENT_COMPARE;
flg_pins = FLG1_COMPARE_FLG2_BORROW;
break;
case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
flg_pins = FLG1_CARRYBORROW_FLG2_UD;
break;
case COUNTER_EVENT_INDEX:
next_irq_trigger = QUAD8_EVENT_INDEX;
flg_pins = FLG1_INDX_FLG2_E;
break;
default:
/* should never reach this path */
spin_unlock_irqrestore(&priv->lock, irqflags);
return -EINVAL;
ret = -EINVAL;
goto exit_unlock;
}
/* Enable IRQ line */
irq_enabled |= BIT(event_node->channel);
/* Skip configuration if it is the same as previously set */
if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
if (flg_pins == u8_get_bits(priv->ior[event_node->channel], FLG_PINS))
continue;
/* Save new IRQ function configuration */
priv->irq_trigger[event_node->channel] = next_irq_trigger;
/* Load configuration to I/O Control Register */
ior_cfg = priv->ab_enable[event_node->channel] |
priv->preset_enable[event_node->channel] << 1 |
priv->irq_trigger[event_node->channel] << 3;
iowrite8(QUAD8_CTR_IOR | ior_cfg,
&priv->reg->channel[event_node->channel].control);
ret = quad8_control_register_update(priv->map, priv->ior, event_node->channel,
flg_pins, FLG_PINS);
if (ret)
goto exit_unlock;
}
iowrite8(irq_enabled, &priv->reg->index_interrupt);
ret = regmap_write(priv->map, QUAD8_INDEX_INTERRUPT, irq_enabled);
exit_unlock:
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_watch_validate(struct counter_device *counter,
......@@ -531,7 +600,7 @@ static int quad8_index_polarity_get(struct counter_device *counter,
const struct quad8 *const priv = counter_priv(counter);
const size_t channel_id = signal->id - 16;
*index_polarity = priv->index_polarity[channel_id];
*index_polarity = u8_get_bits(priv->idr[channel_id], INDEX_POLARITY);
return 0;
}
......@@ -542,22 +611,17 @@ static int quad8_index_polarity_set(struct counter_device *counter,
{
struct quad8 *const priv = counter_priv(counter);
const size_t channel_id = signal->id - 16;
u8 __iomem *const control = &priv->reg->channel[channel_id].control;
unsigned long irqflags;
unsigned int idr_cfg = index_polarity << 1;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
idr_cfg |= priv->synchronous_mode[channel_id];
priv->index_polarity[channel_id] = index_polarity;
/* Load Index Control configuration to Index Control Register */
iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
ret = quad8_control_register_update(priv->map, priv->idr, channel_id, index_polarity,
INDEX_POLARITY);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_polarity_read(struct counter_device *counter,
......@@ -571,7 +635,7 @@ static int quad8_polarity_read(struct counter_device *counter,
if (err)
return err;
*polarity = (index_polarity) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
*polarity = (index_polarity == POSITIVE_INDEX_POLARITY) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
COUNTER_SIGNAL_POLARITY_NEGATIVE;
return 0;
......@@ -581,7 +645,8 @@ static int quad8_polarity_write(struct counter_device *counter,
struct counter_signal *signal,
enum counter_signal_polarity polarity)
{
const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? 1 : 0;
const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? POSITIVE_INDEX_POLARITY :
NEGATIVE_INDEX_POLARITY;
return quad8_index_polarity_set(counter, signal, pol);
}
......@@ -598,7 +663,7 @@ static int quad8_synchronous_mode_get(struct counter_device *counter,
const struct quad8 *const priv = counter_priv(counter);
const size_t channel_id = signal->id - 16;
*synchronous_mode = priv->synchronous_mode[channel_id];
*synchronous_mode = u8_get_bits(priv->idr[channel_id], INDEX_MODE);
return 0;
}
......@@ -609,28 +674,26 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
{
struct quad8 *const priv = counter_priv(counter);
const size_t channel_id = signal->id - 16;
u8 __iomem *const control = &priv->reg->channel[channel_id].control;
u8 quadrature_mode;
unsigned long irqflags;
unsigned int idr_cfg = synchronous_mode;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
idr_cfg |= priv->index_polarity[channel_id] << 1;
/* Index function must be non-synchronous in non-quadrature mode */
if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
spin_unlock_irqrestore(&priv->lock, irqflags);
return -EINVAL;
quadrature_mode = u8_get_bits(priv->idr[channel_id], QUADRATURE_MODE);
if (synchronous_mode && quadrature_mode == NON_QUADRATURE) {
ret = -EINVAL;
goto exit_unlock;
}
priv->synchronous_mode[channel_id] = synchronous_mode;
/* Load Index Control configuration to Index Control Register */
iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
ret = quad8_control_register_update(priv->map, priv->idr, channel_id, synchronous_mode,
INDEX_MODE);
exit_unlock:
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_count_floor_read(struct counter_device *counter,
......@@ -648,18 +711,17 @@ static int quad8_count_mode_read(struct counter_device *counter,
{
const struct quad8 *const priv = counter_priv(counter);
/* Map 104-QUAD-8 count mode to Generic Counter count mode */
switch (priv->count_mode[count->id]) {
case 0:
switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
case NORMAL_COUNT:
*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
break;
case 1:
case RANGE_LIMIT:
*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
break;
case 2:
case NON_RECYCLE_COUNT:
*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
break;
case 3:
case MODULO_N:
*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
break;
}
......@@ -673,23 +735,21 @@ static int quad8_count_mode_write(struct counter_device *counter,
{
struct quad8 *const priv = counter_priv(counter);
unsigned int count_mode;
unsigned int mode_cfg;
u8 __iomem *const control = &priv->reg->channel[count->id].control;
unsigned long irqflags;
int ret;
/* Map Generic Counter count mode to 104-QUAD-8 count mode */
switch (cnt_mode) {
case COUNTER_COUNT_MODE_NORMAL:
count_mode = 0;
count_mode = NORMAL_COUNT;
break;
case COUNTER_COUNT_MODE_RANGE_LIMIT:
count_mode = 1;
count_mode = RANGE_LIMIT;
break;
case COUNTER_COUNT_MODE_NON_RECYCLE:
count_mode = 2;
count_mode = NON_RECYCLE_COUNT;
break;
case COUNTER_COUNT_MODE_MODULO_N:
count_mode = 3;
count_mode = MODULO_N;
break;
default:
/* should never reach this path */
......@@ -698,21 +758,12 @@ static int quad8_count_mode_write(struct counter_device *counter,
spin_lock_irqsave(&priv->lock, irqflags);
priv->count_mode[count->id] = count_mode;
/* Set count mode configuration value */
mode_cfg = count_mode << 1;
/* Add quadrature mode configuration */
if (priv->quadrature_mode[count->id])
mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
/* Load mode configuration to Counter Mode Register */
iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
ret = quad8_control_register_update(priv->map, priv->cmr, count->id, count_mode,
COUNT_MODE);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_count_enable_read(struct counter_device *counter,
......@@ -720,7 +771,7 @@ static int quad8_count_enable_read(struct counter_device *counter,
{
const struct quad8 *const priv = counter_priv(counter);
*enable = priv->ab_enable[count->id];
*enable = u8_get_bits(priv->ior[count->id], AB_GATE);
return 0;
}
......@@ -729,23 +780,16 @@ static int quad8_count_enable_write(struct counter_device *counter,
struct counter_count *count, u8 enable)
{
struct quad8 *const priv = counter_priv(counter);
u8 __iomem *const control = &priv->reg->channel[count->id].control;
unsigned long irqflags;
unsigned int ior_cfg;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
priv->ab_enable[count->id] = enable;
ior_cfg = enable | priv->preset_enable[count->id] << 1 |
priv->irq_trigger[count->id] << 3;
/* Load I/O control configuration */
iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
ret = quad8_control_register_update(priv->map, priv->ior, count->id, enable, AB_GATE);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static const char *const quad8_noise_error_states[] = {
......@@ -757,9 +801,13 @@ static int quad8_error_noise_get(struct counter_device *counter,
struct counter_count *count, u32 *noise_error)
{
const struct quad8 *const priv = counter_priv(counter);
u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
unsigned int flag;
int ret;
*noise_error = !!(ioread8(flag_addr) & QUAD8_FLAG_E);
ret = regmap_read(priv->map, QUAD8_CONTROL(count->id), &flag);
if (ret)
return ret;
*noise_error = u8_get_bits(flag, FLAG_E);
return 0;
}
......@@ -774,38 +822,24 @@ static int quad8_count_preset_read(struct counter_device *counter,
return 0;
}
static void quad8_preset_register_set(struct quad8 *const priv, const int id,
const unsigned int preset)
{
struct channel_reg __iomem *const chan = priv->reg->channel + id;
int i;
priv->preset[id] = preset;
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
/* Set Preset Register */
for (i = 0; i < 3; i++)
iowrite8(preset >> (8 * i), &chan->data);
}
static int quad8_count_preset_write(struct counter_device *counter,
struct counter_count *count, u64 preset)
{
struct quad8 *const priv = counter_priv(counter);
unsigned long irqflags;
int ret;
if (preset > LS7267_CNTR_MAX)
return -ERANGE;
spin_lock_irqsave(&priv->lock, irqflags);
quad8_preset_register_set(priv, count->id, preset);
priv->preset[count->id] = preset;
ret = quad8_preset_register_set(priv, count->id, preset);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_count_ceiling_read(struct counter_device *counter,
......@@ -817,9 +851,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
spin_lock_irqsave(&priv->lock, irqflags);
/* Range Limit and Modulo-N count modes use preset value as ceiling */
switch (priv->count_mode[count->id]) {
case 1:
case 3:
switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
case RANGE_LIMIT:
case MODULO_N:
*ceiling = priv->preset[count->id];
break;
default:
......@@ -837,6 +871,7 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
{
struct quad8 *const priv = counter_priv(counter);
unsigned long irqflags;
int ret;
if (ceiling > LS7267_CNTR_MAX)
return -ERANGE;
......@@ -844,17 +879,20 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
spin_lock_irqsave(&priv->lock, irqflags);
/* Range Limit and Modulo-N count modes use preset value as ceiling */
switch (priv->count_mode[count->id]) {
case 1:
case 3:
quad8_preset_register_set(priv, count->id, ceiling);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
case RANGE_LIMIT:
case MODULO_N:
priv->preset[count->id] = ceiling;
ret = quad8_preset_register_set(priv, count->id, ceiling);
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&priv->lock, irqflags);
return -EINVAL;
return ret;
}
static int quad8_count_preset_enable_read(struct counter_device *counter,
......@@ -863,7 +901,8 @@ static int quad8_count_preset_enable_read(struct counter_device *counter,
{
const struct quad8 *const priv = counter_priv(counter);
*preset_enable = !priv->preset_enable[count->id];
/* Preset enable is active low in Input/Output Control register */
*preset_enable = !u8_get_bits(priv->ior[count->id], LOAD_PIN);
return 0;
}
......@@ -873,26 +912,18 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
u8 preset_enable)
{
struct quad8 *const priv = counter_priv(counter);
u8 __iomem *const control = &priv->reg->channel[count->id].control;
unsigned long irqflags;
unsigned int ior_cfg;
/* Preset enable is active low in Input/Output Control register */
preset_enable = !preset_enable;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
priv->preset_enable[count->id] = preset_enable;
ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
priv->irq_trigger[count->id] << 3;
/* Load I/O control configuration to Input / Output Control Register */
iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
/* Preset enable is active low in Input/Output Control register */
ret = quad8_control_register_update(priv->map, priv->ior, count->id, !preset_enable,
LOAD_PIN);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_signal_cable_fault_read(struct counter_device *counter,
......@@ -903,7 +934,7 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
const size_t channel_id = signal->id / 2;
unsigned long irqflags;
bool disabled;
unsigned int status;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
......@@ -914,13 +945,16 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
return -EINVAL;
}
/* Logic 0 = cable fault */
status = ioread8(&priv->reg->cable_status);
ret = regmap_test_bits(priv->map, QUAD8_CABLE_STATUS, BIT(channel_id));
if (ret < 0) {
spin_unlock_irqrestore(&priv->lock, irqflags);
return ret;
}
spin_unlock_irqrestore(&priv->lock, irqflags);
/* Mask respective channel and invert logic */
*cable_fault = !(status & BIT(channel_id));
/* Logic 0 = cable fault */
*cable_fault = !ret;
return 0;
}
......@@ -945,6 +979,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
const size_t channel_id = signal->id / 2;
unsigned long irqflags;
unsigned int cable_fault_enable;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
......@@ -956,11 +991,11 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
/* Enable is active low in Differential Encoder Cable Status register */
cable_fault_enable = ~priv->cable_fault_enable;
iowrite8(cable_fault_enable, &priv->reg->cable_status);
ret = regmap_write(priv->map, QUAD8_CABLE_STATUS, cable_fault_enable);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
......@@ -974,30 +1009,37 @@ static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
return 0;
}
static int quad8_filter_clock_prescaler_set(struct quad8 *const priv, const size_t id,
const u8 prescaler)
{
int ret;
ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BP);
if (ret)
return ret;
ret = regmap_write(priv->map, QUAD8_DATA(id), prescaler);
if (ret)
return ret;
return regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | TRANSFER_PR0_TO_PSC);
}
static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
struct counter_signal *signal,
u8 prescaler)
{
struct quad8 *const priv = counter_priv(counter);
const size_t channel_id = signal->id / 2;
struct channel_reg __iomem *const chan = priv->reg->channel + channel_id;
unsigned long irqflags;
int ret;
spin_lock_irqsave(&priv->lock, irqflags);
priv->fck_prescaler[channel_id] = prescaler;
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
/* Set filter clock factor */
iowrite8(prescaler, &chan->data);
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
&chan->control);
ret = quad8_filter_clock_prescaler_set(priv, channel_id, prescaler);
spin_unlock_irqrestore(&priv->lock, irqflags);
return 0;
return ret;
}
static struct counter_comp quad8_signal_ext[] = {
......@@ -1150,77 +1192,93 @@ static irqreturn_t quad8_irq_handler(int irq, void *private)
{
struct counter_device *counter = private;
struct quad8 *const priv = counter_priv(counter);
unsigned int status;
unsigned long irq_status;
unsigned long channel;
unsigned int flg_pins;
u8 event;
int ret;
irq_status = ioread8(&priv->reg->interrupt_status);
if (!irq_status)
ret = regmap_read(priv->map, QUAD8_INTERRUPT_STATUS, &status);
if (ret)
return ret;
if (!status)
return IRQ_NONE;
irq_status = status;
for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
switch (priv->irq_trigger[channel]) {
case QUAD8_EVENT_CARRY:
flg_pins = u8_get_bits(priv->ior[channel], FLG_PINS);
switch (flg_pins) {
case FLG1_CARRY_FLG2_BORROW:
event = COUNTER_EVENT_OVERFLOW;
break;
case QUAD8_EVENT_COMPARE:
case FLG1_COMPARE_FLG2_BORROW:
event = COUNTER_EVENT_THRESHOLD;
break;
case QUAD8_EVENT_CARRY_BORROW:
case FLG1_CARRYBORROW_FLG2_UD:
event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
break;
case QUAD8_EVENT_INDEX:
case FLG1_INDX_FLG2_E:
event = COUNTER_EVENT_INDEX;
break;
default:
/* should never reach this path */
WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
priv->irq_trigger[channel], channel);
flg_pins, channel);
continue;
}
counter_push_event(counter, event, channel);
}
/* Clear pending interrupts on device */
iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION, CLEAR_PENDING_INTERRUPTS);
if (ret)
return ret;
return IRQ_HANDLED;
}
static void quad8_init_counter(struct channel_reg __iomem *const chan)
static int quad8_init_counter(struct quad8 *const priv, const size_t channel)
{
unsigned long i;
int ret;
ret = quad8_filter_clock_prescaler_set(priv, channel, 0);
if (ret)
return ret;
ret = quad8_preset_register_set(priv, channel, 0);
if (ret)
return ret;
ret = quad8_flag_register_reset(priv, channel);
if (ret)
return ret;
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
/* Reset filter clock factor */
iowrite8(0, &chan->data);
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
&chan->control);
/* Reset Byte Pointer */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
/* Reset Preset Register */
for (i = 0; i < 3; i++)
iowrite8(0x00, &chan->data);
/* Reset Borrow, Carry, Compare, and Sign flags */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
/* Reset Error flag */
iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
/* Binary encoding; Normal count; non-quadrature mode */
iowrite8(QUAD8_CTR_CMR, &chan->control);
priv->cmr[channel] = SELECT_CMR | BINARY | u8_encode_bits(NORMAL_COUNT, COUNT_MODE) |
u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
ret = regmap_write(priv->map, QUAD8_CONTROL(channel), priv->cmr[channel]);
if (ret)
return ret;
/* Disable A and B inputs; preset on index; FLG1 as Carry */
iowrite8(QUAD8_CTR_IOR, &chan->control);
priv->ior[channel] = SELECT_IOR | DISABLE_AB | u8_encode_bits(LOAD_CNTR, LOAD_PIN) |
u8_encode_bits(FLG1_CARRY_FLG2_BORROW, FLG_PINS);
ret = regmap_write(priv->map, QUAD8_CONTROL(channel), priv->ior[channel]);
if (ret)
return ret;
/* Disable index function; negative index polarity */
iowrite8(QUAD8_CTR_IDR, &chan->control);
priv->idr[channel] = SELECT_IDR | u8_encode_bits(DISABLE_INDEX_MODE, INDEX_MODE) |
u8_encode_bits(NEGATIVE_INDEX_POLARITY, INDEX_POLARITY);
return regmap_write(priv->map, QUAD8_CONTROL(channel), priv->idr[channel]);
}
static int quad8_probe(struct device *dev, unsigned int id)
{
struct counter_device *counter;
struct quad8 *priv;
void __iomem *regs;
unsigned long i;
int err;
int ret;
if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
......@@ -1233,10 +1291,15 @@ static int quad8_probe(struct device *dev, unsigned int id)
return -ENOMEM;
priv = counter_priv(counter);
priv->reg = devm_ioport_map(dev, base[id], QUAD8_EXTENT);
if (!priv->reg)
regs = devm_ioport_map(dev, base[id], QUAD8_EXTENT);
if (!regs)
return -ENOMEM;
priv->map = devm_regmap_init_mmio(dev, regs, &quad8_regmap_config);
if (IS_ERR(priv->map))
return dev_err_probe(dev, PTR_ERR(priv->map),
"Unable to initialize register map\n");
/* Initialize Counter device and driver data */
counter->name = dev_name(dev);
counter->parent = dev;
......@@ -1249,25 +1312,38 @@ static int quad8_probe(struct device *dev, unsigned int id)
spin_lock_init(&priv->lock);
/* Reset Index/Interrupt Register */
iowrite8(0x00, &priv->reg->index_interrupt);
ret = regmap_write(priv->map, QUAD8_INDEX_INTERRUPT, 0x00);
if (ret)
return ret;
/* Reset all counters and disable interrupt function */
iowrite8(QUAD8_CHAN_OP_RESET_COUNTERS, &priv->reg->channel_oper);
ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION,
RESET_COUNTERS | DISABLE_INTERRUPT_FUNCTION);
if (ret)
return ret;
/* Set initial configuration for all counters */
for (i = 0; i < QUAD8_NUM_COUNTERS; i++)
quad8_init_counter(priv->reg->channel + i);
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
ret = quad8_init_counter(priv, i);
if (ret)
return ret;
}
/* Disable Differential Encoder Cable Status for all channels */
iowrite8(0xFF, &priv->reg->cable_status);
ret = regmap_write(priv->map, QUAD8_CABLE_STATUS, GENMASK(7, 0));
if (ret)
return ret;
/* Enable all counters and enable interrupt function */
iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION,
ENABLE_COUNTERS | ENABLE_INTERRUPT_FUNCTION);
if (ret)
return ret;
err = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler,
ret = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler,
IRQF_SHARED, counter->name, counter);
if (err)
return err;
if (ret)
return ret;
err = devm_counter_add(dev, counter);
if (err < 0)
return dev_err_probe(dev, err, "Failed to add counter\n");
ret = devm_counter_add(dev, counter);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to add counter\n");
return 0;
}
......
......@@ -10,6 +10,21 @@ menuconfig COUNTER
interface. You only need to enable this, if you also want to enable
one or more of the counter device drivers below.
config I8254
tristate
select COUNTER
select REGMAP
help
Enables support for the i8254 interface library functions. The i8254
interface library provides functions to facilitate communication with
interfaces compatible with the venerable Intel 8254 Programmable
Interval Timer (PIT). The Intel 825x family of chips was first
released in the early 1980s but compatible interfaces are nowadays
typically found embedded in larger VLSI processing chips and FPGA
components.
If built as a module its name will be i8254.
if COUNTER
config 104_QUAD_8
......@@ -17,14 +32,15 @@ config 104_QUAD_8
depends on (PC104 && X86) || COMPILE_TEST
depends on HAS_IOPORT_MAP
select ISA_BUS_API
select REGMAP_MMIO
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
A counter's respective error flag may be cleared by performing a write
operation on the respective count value attribute. Although the
104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
set, either directly or via the counter's preset attribute.
operation on the respective count value attribute. The 104-QUAD-8
counters may be set either directly or via the counter's preset
attribute.
The base port addresses for the devices may be configured via the base
array module parameter. The interrupt line numbers for the devices may
......
......@@ -6,6 +6,7 @@
obj-$(CONFIG_COUNTER) += counter.o
counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
obj-$(CONFIG_I8254) += i8254.o
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o
obj-$(CONFIG_RZ_MTU3_CNT) += rz-mtu3-cnt.o
......
......@@ -88,7 +88,13 @@ static const char *const counter_count_mode_str[] = {
[COUNTER_COUNT_MODE_NORMAL] = "normal",
[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n",
[COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT] = "interrupt on terminal count",
[COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT] = "hardware retriggerable one-shot",
[COUNTER_COUNT_MODE_RATE_GENERATOR] = "rate generator",
[COUNTER_COUNT_MODE_SQUARE_WAVE_MODE] = "square wave mode",
[COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE] = "software triggered strobe",
[COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE] = "hardware triggered strobe",
};
static const char *const counter_signal_polarity_str[] = {
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel 8254 Programmable Interval Timer
* Copyright (C) William Breathitt Gray
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/counter.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/i8254.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <asm/unaligned.h>
#define I8254_COUNTER_REG(_counter) (_counter)
#define I8254_CONTROL_REG 0x3
#define I8254_SC GENMASK(7, 6)
#define I8254_RW GENMASK(5, 4)
#define I8254_M GENMASK(3, 1)
#define I8254_CONTROL(_sc, _rw, _m) \
(u8_encode_bits(_sc, I8254_SC) | u8_encode_bits(_rw, I8254_RW) | \
u8_encode_bits(_m, I8254_M))
#define I8254_RW_TWO_BYTE 0x3
#define I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT 0
#define I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT 1
#define I8254_MODE_RATE_GENERATOR 2
#define I8254_MODE_SQUARE_WAVE_MODE 3
#define I8254_MODE_SOFTWARE_TRIGGERED_STROBE 4
#define I8254_MODE_HARDWARE_TRIGGERED_STROBE 5
#define I8254_COUNTER_LATCH(_counter) I8254_CONTROL(_counter, 0x0, 0x0)
#define I8254_PROGRAM_COUNTER(_counter, _mode) I8254_CONTROL(_counter, I8254_RW_TWO_BYTE, _mode)
#define I8254_NUM_COUNTERS 3
/**
* struct i8254 - I8254 device private data structure
* @lock: synchronization lock to prevent I/O race conditions
* @preset: array of Counter Register states
* @out_mode: array of mode configuration states
* @map: Regmap for the device
*/
struct i8254 {
struct mutex lock;
u16 preset[I8254_NUM_COUNTERS];
u8 out_mode[I8254_NUM_COUNTERS];
struct regmap *map;
};
static int i8254_count_read(struct counter_device *const counter, struct counter_count *const count,
u64 *const val)
{
struct i8254 *const priv = counter_priv(counter);
int ret;
u8 value[2];
mutex_lock(&priv->lock);
ret = regmap_write(priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
if (ret) {
mutex_unlock(&priv->lock);
return ret;
}
ret = regmap_noinc_read(priv->map, I8254_COUNTER_REG(count->id), value, sizeof(value));
if (ret) {
mutex_unlock(&priv->lock);
return ret;
}
mutex_unlock(&priv->lock);
*val = get_unaligned_le16(value);
return ret;
}
static int i8254_function_read(struct counter_device *const counter,
struct counter_count *const count,
enum counter_function *const function)
{
*function = COUNTER_FUNCTION_DECREASE;
return 0;
}
#define I8254_SYNAPSES_PER_COUNT 2
#define I8254_SIGNAL_ID_CLK 0
#define I8254_SIGNAL_ID_GATE 1
static int i8254_action_read(struct counter_device *const counter,
struct counter_count *const count,
struct counter_synapse *const synapse,
enum counter_synapse_action *const action)
{
struct i8254 *const priv = counter_priv(counter);
switch (synapse->signal->id % I8254_SYNAPSES_PER_COUNT) {
case I8254_SIGNAL_ID_CLK:
*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
return 0;
case I8254_SIGNAL_ID_GATE:
switch (priv->out_mode[count->id]) {
case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
case I8254_MODE_RATE_GENERATOR:
case I8254_MODE_SQUARE_WAVE_MODE:
case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
return 0;
default:
*action = COUNTER_SYNAPSE_ACTION_NONE;
return 0;
}
default:
/* should never reach this path */
return -EINVAL;
}
}
static int i8254_count_ceiling_read(struct counter_device *const counter,
struct counter_count *const count, u64 *const ceiling)
{
struct i8254 *const priv = counter_priv(counter);
mutex_lock(&priv->lock);
switch (priv->out_mode[count->id]) {
case I8254_MODE_RATE_GENERATOR:
/* Rate Generator decrements 0 by one and the counter "wraps around" */
*ceiling = (priv->preset[count->id] == 0) ? U16_MAX : priv->preset[count->id];
break;
case I8254_MODE_SQUARE_WAVE_MODE:
if (priv->preset[count->id] % 2)
*ceiling = priv->preset[count->id] - 1;
else if (priv->preset[count->id] == 0)
/* Square Wave Mode decrements 0 by two and the counter "wraps around" */
*ceiling = U16_MAX - 1;
else
*ceiling = priv->preset[count->id];
break;
default:
*ceiling = U16_MAX;
break;
}
mutex_unlock(&priv->lock);
return 0;
}
static int i8254_count_mode_read(struct counter_device *const counter,
struct counter_count *const count,
enum counter_count_mode *const count_mode)
{
const struct i8254 *const priv = counter_priv(counter);
switch (priv->out_mode[count->id]) {
case I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT:
*count_mode = COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT;
return 0;
case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
*count_mode = COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
return 0;
case I8254_MODE_RATE_GENERATOR:
*count_mode = COUNTER_COUNT_MODE_RATE_GENERATOR;
return 0;
case I8254_MODE_SQUARE_WAVE_MODE:
*count_mode = COUNTER_COUNT_MODE_SQUARE_WAVE_MODE;
return 0;
case I8254_MODE_SOFTWARE_TRIGGERED_STROBE:
*count_mode = COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE;
return 0;
case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
*count_mode = COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE;
return 0;
default:
/* should never reach this path */
return -EINVAL;
}
}
static int i8254_count_mode_write(struct counter_device *const counter,
struct counter_count *const count,
const enum counter_count_mode count_mode)
{
struct i8254 *const priv = counter_priv(counter);
u8 out_mode;
int ret;
switch (count_mode) {
case COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT:
out_mode = I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT;
break;
case COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
out_mode = I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
break;
case COUNTER_COUNT_MODE_RATE_GENERATOR:
out_mode = I8254_MODE_RATE_GENERATOR;
break;
case COUNTER_COUNT_MODE_SQUARE_WAVE_MODE:
out_mode = I8254_MODE_SQUARE_WAVE_MODE;
break;
case COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE:
out_mode = I8254_MODE_SOFTWARE_TRIGGERED_STROBE;
break;
case COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE:
out_mode = I8254_MODE_HARDWARE_TRIGGERED_STROBE;
break;
default:
/* should never reach this path */
return -EINVAL;
}
mutex_lock(&priv->lock);
/* Counter Register is cleared when the counter is programmed */
priv->preset[count->id] = 0;
priv->out_mode[count->id] = out_mode;
ret = regmap_write(priv->map, I8254_CONTROL_REG,
I8254_PROGRAM_COUNTER(count->id, out_mode));
mutex_unlock(&priv->lock);
return ret;
}
static int i8254_count_floor_read(struct counter_device *const counter,
struct counter_count *const count, u64 *const floor)
{
struct i8254 *const priv = counter_priv(counter);
mutex_lock(&priv->lock);
switch (priv->out_mode[count->id]) {
case I8254_MODE_RATE_GENERATOR:
/* counter is always reloaded after 1, but 0 is a possible reload value */
*floor = (priv->preset[count->id] == 0) ? 0 : 1;
break;
case I8254_MODE_SQUARE_WAVE_MODE:
/* counter is always reloaded after 2 for even preset values */
*floor = (priv->preset[count->id] % 2 || priv->preset[count->id] == 0) ? 0 : 2;
break;
default:
*floor = 0;
break;
}
mutex_unlock(&priv->lock);
return 0;
}
static int i8254_count_preset_read(struct counter_device *const counter,
struct counter_count *const count, u64 *const preset)
{
const struct i8254 *const priv = counter_priv(counter);
*preset = priv->preset[count->id];
return 0;
}
static int i8254_count_preset_write(struct counter_device *const counter,
struct counter_count *const count, const u64 preset)
{
struct i8254 *const priv = counter_priv(counter);
int ret;
u8 value[2];
if (preset > U16_MAX)
return -ERANGE;
mutex_lock(&priv->lock);
if (priv->out_mode[count->id] == I8254_MODE_RATE_GENERATOR ||
priv->out_mode[count->id] == I8254_MODE_SQUARE_WAVE_MODE) {
if (preset == 1) {
mutex_unlock(&priv->lock);
return -EINVAL;
}
}
priv->preset[count->id] = preset;
put_unaligned_le16(preset, value);
ret = regmap_noinc_write(priv->map, I8254_COUNTER_REG(count->id), value, 2);
mutex_unlock(&priv->lock);
return ret;
}
static int i8254_init_hw(struct regmap *const map)
{
unsigned long i;
int ret;
for (i = 0; i < I8254_NUM_COUNTERS; i++) {
/* Initialize each counter to Mode 0 */
ret = regmap_write(map, I8254_CONTROL_REG,
I8254_PROGRAM_COUNTER(i, I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT));
if (ret)
return ret;
}
return 0;
}
static const struct counter_ops i8254_ops = {
.count_read = i8254_count_read,
.function_read = i8254_function_read,
.action_read = i8254_action_read,
};
#define I8254_SIGNAL(_id, _name) { \
.id = (_id), \
.name = (_name), \
}
static struct counter_signal i8254_signals[] = {
I8254_SIGNAL(0, "CLK 0"), I8254_SIGNAL(1, "GATE 0"),
I8254_SIGNAL(2, "CLK 1"), I8254_SIGNAL(3, "GATE 1"),
I8254_SIGNAL(4, "CLK 2"), I8254_SIGNAL(5, "GATE 2"),
};
static const enum counter_synapse_action i8254_clk_actions[] = {
COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
};
static const enum counter_synapse_action i8254_gate_actions[] = {
COUNTER_SYNAPSE_ACTION_NONE,
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
};
#define I8254_SYNAPSES_BASE(_id) ((_id) * I8254_SYNAPSES_PER_COUNT)
#define I8254_SYNAPSE_CLK(_id) { \
.actions_list = i8254_clk_actions, \
.num_actions = ARRAY_SIZE(i8254_clk_actions), \
.signal = &i8254_signals[I8254_SYNAPSES_BASE(_id) + 0], \
}
#define I8254_SYNAPSE_GATE(_id) { \
.actions_list = i8254_gate_actions, \
.num_actions = ARRAY_SIZE(i8254_gate_actions), \
.signal = &i8254_signals[I8254_SYNAPSES_BASE(_id) + 1], \
}
static struct counter_synapse i8254_synapses[] = {
I8254_SYNAPSE_CLK(0), I8254_SYNAPSE_GATE(0),
I8254_SYNAPSE_CLK(1), I8254_SYNAPSE_GATE(1),
I8254_SYNAPSE_CLK(2), I8254_SYNAPSE_GATE(2),
};
static const enum counter_function i8254_functions_list[] = {
COUNTER_FUNCTION_DECREASE,
};
static const enum counter_count_mode i8254_count_modes[] = {
COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
COUNTER_COUNT_MODE_RATE_GENERATOR,
COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
};
static DEFINE_COUNTER_AVAILABLE(i8254_count_modes_available, i8254_count_modes);
static struct counter_comp i8254_count_ext[] = {
COUNTER_COMP_CEILING(i8254_count_ceiling_read, NULL),
COUNTER_COMP_COUNT_MODE(i8254_count_mode_read, i8254_count_mode_write,
i8254_count_modes_available),
COUNTER_COMP_FLOOR(i8254_count_floor_read, NULL),
COUNTER_COMP_PRESET(i8254_count_preset_read, i8254_count_preset_write),
};
#define I8254_COUNT(_id, _name) { \
.id = (_id), \
.name = (_name), \
.functions_list = i8254_functions_list, \
.num_functions = ARRAY_SIZE(i8254_functions_list), \
.synapses = &i8254_synapses[I8254_SYNAPSES_BASE(_id)], \
.num_synapses = I8254_SYNAPSES_PER_COUNT, \
.ext = i8254_count_ext, \
.num_ext = ARRAY_SIZE(i8254_count_ext) \
}
static struct counter_count i8254_counts[I8254_NUM_COUNTERS] = {
I8254_COUNT(0, "Counter 0"), I8254_COUNT(1, "Counter 1"), I8254_COUNT(2, "Counter 2"),
};
/**
* devm_i8254_regmap_register - Register an i8254 Counter device
* @dev: device that is registering this i8254 Counter device
* @config: configuration for i8254_regmap_config
*
* Registers an Intel 8254 Programmable Interval Timer Counter device. Returns 0 on success and
* negative error number on failure.
*/
int devm_i8254_regmap_register(struct device *const dev,
const struct i8254_regmap_config *const config)
{
struct counter_device *counter;
struct i8254 *priv;
int err;
if (!config->parent)
return -EINVAL;
if (!config->map)
return -EINVAL;
counter = devm_counter_alloc(dev, sizeof(*priv));
if (!counter)
return -ENOMEM;
priv = counter_priv(counter);
priv->map = config->map;
counter->name = dev_name(config->parent);
counter->parent = config->parent;
counter->ops = &i8254_ops;
counter->counts = i8254_counts;
counter->num_counts = ARRAY_SIZE(i8254_counts);
counter->signals = i8254_signals;
counter->num_signals = ARRAY_SIZE(i8254_signals);
mutex_init(&priv->lock);
err = i8254_init_hw(priv->map);
if (err)
return err;
err = devm_counter_add(dev, counter);
if (err < 0)
return dev_err_probe(dev, err, "Failed to add counter\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, I8254);
MODULE_AUTHOR("William Breathitt Gray");
MODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(COUNTER);
......@@ -342,6 +342,9 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
/* Reset input selector to its default input */
regmap_write(priv->regmap, TIM_TISEL, 0x0);
/* Register Counter device */
ret = devm_counter_add(dev, counter);
if (ret < 0)
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) William Breathitt Gray */
#ifndef _I8254_H_
#define _I8254_H_
struct device;
struct regmap;
/**
* struct i8254_regmap_config - Configuration for the register map of an i8254
* @parent: parent device
* @map: regmap for the i8254
*/
struct i8254_regmap_config {
struct device *parent;
struct regmap *map;
};
int devm_i8254_regmap_register(struct device *dev, const struct i8254_regmap_config *config);
#endif /* _I8254_H_ */
......@@ -127,6 +127,12 @@ enum counter_count_mode {
COUNTER_COUNT_MODE_RANGE_LIMIT,
COUNTER_COUNT_MODE_NON_RECYCLE,
COUNTER_COUNT_MODE_MODULO_N,
COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
COUNTER_COUNT_MODE_RATE_GENERATOR,
COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
};
/* Count function values */
......
/counter_example
/include/linux/counter.h
......@@ -40,6 +40,7 @@ $(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
clean:
rm -f $(ALL_PROGRAMS)
rm -rf $(OUTPUT)include/linux/counter.h
rmdir -p $(OUTPUT)include/linux
find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
install: $(ALL_PROGRAMS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment