mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
counter: 104-quad-8: Migrate to the regmap API
The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. With regmap we get boundary checks, read-write permissions, operation synchronization locks, and more for free. Most important of all, rather than rolling our own we utilize implementations that are known to work and gain from any future improvements and fixes that come. Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/1f1f7920d2be94aedb6fdf49f429fe6137c8cb24.1681753140.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray <william.gray@linaro.org>
This commit is contained in:
parent
142c8622b5
commit
98ffe02529
@ -9,7 +9,7 @@
|
||||
#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>
|
||||
@ -18,8 +18,9 @@
|
||||
#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>
|
||||
|
||||
@ -37,35 +38,13 @@ 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
|
||||
@ -76,7 +55,7 @@ struct quad8_reg {
|
||||
* @fck_prescaler: array of filter clock prescaler configurations
|
||||
* @preset: array of preset values
|
||||
* @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;
|
||||
@ -86,7 +65,30 @@ struct quad8 {
|
||||
unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
|
||||
unsigned int preset[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 */
|
||||
@ -202,12 +204,12 @@ struct quad8 {
|
||||
/* Each Counter is 24 bits wide */
|
||||
#define LS7267_CNTR_MAX GENMASK(23, 0)
|
||||
|
||||
static __always_inline void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
|
||||
const size_t channel, const u8 val,
|
||||
const u8 field)
|
||||
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);
|
||||
iowrite8(buf[channel], &priv->reg->channel[channel].control);
|
||||
return regmap_write(map, QUAD8_CONTROL(channel), buf[channel]);
|
||||
}
|
||||
|
||||
static int quad8_signal_read(struct counter_device *counter,
|
||||
@ -215,15 +217,17 @@ static int quad8_signal_read(struct counter_device *counter,
|
||||
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;
|
||||
}
|
||||
@ -232,48 +236,56 @@ 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;
|
||||
u8 value[3];
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
iowrite8(SELECT_RLD | RESET_BP | TRANSFER_CNTR_TO_OL, &chan->control);
|
||||
ioread8_rep(&chan->data, value, sizeof(value));
|
||||
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);
|
||||
|
||||
*val = get_unaligned_le24(value);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void quad8_preset_register_set(struct quad8 *const priv, const size_t id,
|
||||
const unsigned long preset)
|
||||
static int quad8_preset_register_set(struct quad8 *const priv, const size_t id,
|
||||
const unsigned long preset)
|
||||
{
|
||||
struct channel_reg __iomem *const chan = priv->reg->channel + id;
|
||||
u8 value[3];
|
||||
int ret;
|
||||
|
||||
put_unaligned_le24(preset, value);
|
||||
|
||||
iowrite8(SELECT_RLD | RESET_BP, &chan->control);
|
||||
iowrite8_rep(&chan->data, value, sizeof(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 void quad8_flag_register_reset(struct quad8 *const priv, const size_t id)
|
||||
static int quad8_flag_register_reset(struct quad8 *const priv, const size_t id)
|
||||
{
|
||||
struct channel_reg __iomem *const chan = priv->reg->channel + id;
|
||||
int ret;
|
||||
|
||||
iowrite8(SELECT_RLD | RESET_BT_CT_CPT_S_IDX, &chan->control);
|
||||
iowrite8(SELECT_RLD | RESET_E, &chan->control);
|
||||
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 ret;
|
||||
|
||||
if (val > LS7267_CNTR_MAX)
|
||||
return -ERANGE;
|
||||
@ -281,17 +293,24 @@ static int quad8_count_write(struct counter_device *counter,
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
/* Counter can only be set via Preset Register */
|
||||
quad8_preset_register_set(priv, count->id, val);
|
||||
iowrite8(SELECT_RLD | TRANSFER_PR_TO_CNTR, &chan->control);
|
||||
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;
|
||||
|
||||
quad8_flag_register_reset(priv, count->id);
|
||||
ret = quad8_flag_register_reset(priv, count->id);
|
||||
if (ret)
|
||||
goto exit_unlock;
|
||||
|
||||
/* Set Preset Register back to original value */
|
||||
quad8_preset_register_set(priv, count->id, priv->preset[count->id]);
|
||||
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[] = {
|
||||
@ -349,6 +368,7 @@ static int quad8_function_write(struct counter_device *counter,
|
||||
unsigned long irqflags;
|
||||
unsigned int mode_cfg;
|
||||
bool synchronous_mode;
|
||||
int ret;
|
||||
|
||||
switch (function) {
|
||||
case COUNTER_FUNCTION_PULSE_DIRECTION:
|
||||
@ -372,14 +392,19 @@ static int quad8_function_write(struct counter_device *counter,
|
||||
|
||||
/* 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)
|
||||
quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, 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;
|
||||
}
|
||||
|
||||
quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
|
||||
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,
|
||||
@ -387,10 +412,12 @@ static int quad8_direction_read(struct counter_device *counter,
|
||||
enum counter_count_direction *direction)
|
||||
{
|
||||
const struct quad8 *const priv = counter_priv(counter);
|
||||
u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
|
||||
u8 flag;
|
||||
unsigned int flag;
|
||||
int ret;
|
||||
|
||||
flag = ioread8(flag_addr);
|
||||
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;
|
||||
|
||||
@ -481,6 +508,7 @@ static int quad8_events_configure(struct counter_device *counter)
|
||||
unsigned long irqflags;
|
||||
struct counter_event_node *event_node;
|
||||
u8 flg_pins;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
@ -500,8 +528,8 @@ static int quad8_events_configure(struct counter_device *counter)
|
||||
break;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
spin_unlock_irqrestore(&priv->lock, irqflags);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
/* Enable IRQ line */
|
||||
@ -512,15 +540,18 @@ static int quad8_events_configure(struct counter_device *counter)
|
||||
continue;
|
||||
|
||||
/* Save new IRQ function configuration */
|
||||
quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins,
|
||||
FLG_PINS);
|
||||
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,
|
||||
@ -581,14 +612,16 @@ 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;
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
|
||||
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,
|
||||
@ -643,21 +676,24 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
|
||||
const size_t channel_id = signal->id - 16;
|
||||
u8 quadrature_mode;
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
/* Index function must be non-synchronous in non-quadrature mode */
|
||||
quadrature_mode = u8_get_bits(priv->idr[channel_id], QUADRATURE_MODE);
|
||||
if (synchronous_mode && quadrature_mode == NON_QUADRATURE) {
|
||||
spin_unlock_irqrestore(&priv->lock, irqflags);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
|
||||
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,
|
||||
@ -700,6 +736,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
|
||||
struct quad8 *const priv = counter_priv(counter);
|
||||
unsigned int count_mode;
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
switch (cnt_mode) {
|
||||
case COUNTER_COUNT_MODE_NORMAL:
|
||||
@ -721,11 +758,12 @@ static int quad8_count_mode_write(struct counter_device *counter,
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
|
||||
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,
|
||||
@ -743,14 +781,15 @@ static int quad8_count_enable_write(struct counter_device *counter,
|
||||
{
|
||||
struct quad8 *const priv = counter_priv(counter);
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
|
||||
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[] = {
|
||||
@ -762,10 +801,12 @@ 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;
|
||||
u8 flag;
|
||||
unsigned int flag;
|
||||
int ret;
|
||||
|
||||
flag = ioread8(flag_addr);
|
||||
ret = regmap_read(priv->map, QUAD8_CONTROL(count->id), &flag);
|
||||
if (ret)
|
||||
return ret;
|
||||
*noise_error = u8_get_bits(flag, FLAG_E);
|
||||
|
||||
return 0;
|
||||
@ -786,6 +827,7 @@ static int quad8_count_preset_write(struct counter_device *counter,
|
||||
{
|
||||
struct quad8 *const priv = counter_priv(counter);
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
if (preset > LS7267_CNTR_MAX)
|
||||
return -ERANGE;
|
||||
@ -793,11 +835,11 @@ static int quad8_count_preset_write(struct counter_device *counter,
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
priv->preset[count->id] = preset;
|
||||
quad8_preset_register_set(priv, 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,
|
||||
@ -829,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;
|
||||
@ -840,14 +883,16 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
|
||||
case RANGE_LIMIT:
|
||||
case MODULO_N:
|
||||
priv->preset[count->id] = ceiling;
|
||||
quad8_preset_register_set(priv, count->id, ceiling);
|
||||
spin_unlock_irqrestore(&priv->lock, irqflags);
|
||||
return 0;
|
||||
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,
|
||||
@ -868,15 +913,17 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
|
||||
{
|
||||
struct quad8 *const priv = counter_priv(counter);
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
/* Preset enable is active low in Input/Output Control register */
|
||||
quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);
|
||||
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,
|
||||
@ -887,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);
|
||||
|
||||
@ -898,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;
|
||||
}
|
||||
@ -929,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);
|
||||
|
||||
@ -940,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,
|
||||
@ -958,14 +1009,18 @@ static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void quad8_filter_clock_prescaler_set(struct quad8 *const priv, const size_t id,
|
||||
const u8 prescaler)
|
||||
static int quad8_filter_clock_prescaler_set(struct quad8 *const priv, const size_t id,
|
||||
const u8 prescaler)
|
||||
{
|
||||
struct channel_reg __iomem *const chan = priv->reg->channel + id;
|
||||
int ret;
|
||||
|
||||
iowrite8(SELECT_RLD | RESET_BP, &chan->control);
|
||||
iowrite8(prescaler, &chan->data);
|
||||
iowrite8(SELECT_RLD | TRANSFER_PR0_TO_PSC, &chan->control);
|
||||
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,
|
||||
@ -975,15 +1030,16 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
|
||||
struct quad8 *const priv = counter_priv(counter);
|
||||
const size_t channel_id = signal->id / 2;
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
|
||||
priv->fck_prescaler[channel_id] = prescaler;
|
||||
quad8_filter_clock_prescaler_set(priv, channel_id, prescaler);
|
||||
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[] = {
|
||||
@ -1136,15 +1192,20 @@ 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) {
|
||||
flg_pins = u8_get_bits(priv->ior[channel], FLG_PINS);
|
||||
switch (flg_pins) {
|
||||
@ -1170,42 +1231,54 @@ static irqreturn_t quad8_irq_handler(int irq, void *private)
|
||||
counter_push_event(counter, event, channel);
|
||||
}
|
||||
|
||||
/* Clear pending interrupts on device */
|
||||
iowrite8(CLEAR_PENDING_INTERRUPTS, &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 quad8 *const priv, const size_t channel)
|
||||
static int quad8_init_counter(struct quad8 *const priv, const size_t channel)
|
||||
{
|
||||
struct channel_reg __iomem *const chan = priv->reg->channel + channel;
|
||||
int ret;
|
||||
|
||||
quad8_filter_clock_prescaler_set(priv, channel, 0);
|
||||
quad8_preset_register_set(priv, channel, 0);
|
||||
quad8_flag_register_reset(priv, channel);
|
||||
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;
|
||||
|
||||
/* Binary encoding; Normal count; non-quadrature mode */
|
||||
priv->cmr[channel] = SELECT_CMR | BINARY | u8_encode_bits(NORMAL_COUNT, COUNT_MODE) |
|
||||
u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
|
||||
iowrite8(priv->cmr[channel], &chan->control);
|
||||
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 */
|
||||
priv->ior[channel] = SELECT_IOR | DISABLE_AB | u8_encode_bits(LOAD_CNTR, LOAD_PIN) |
|
||||
u8_encode_bits(FLG1_CARRY_FLG2_BORROW, FLG_PINS);
|
||||
iowrite8(priv->ior[channel], &chan->control);
|
||||
ret = regmap_write(priv->map, QUAD8_CONTROL(channel), priv->ior[channel]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable index function; negative index polarity */
|
||||
priv->idr[channel] = SELECT_IDR | u8_encode_bits(DISABLE_INDEX_MODE, INDEX_MODE) |
|
||||
u8_encode_bits(NEGATIVE_INDEX_POLARITY, INDEX_POLARITY);
|
||||
iowrite8(priv->idr[channel], &chan->control);
|
||||
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",
|
||||
@ -1218,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;
|
||||
@ -1234,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(RESET_COUNTERS | DISABLE_INTERRUPT_FUNCTION, &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, 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(ENABLE_COUNTERS | ENABLE_INTERRUPT_FUNCTION, &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;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ config 104_QUAD_8
|
||||
tristate "ACCES 104-QUAD-8 driver"
|
||||
depends on (PC104 && X86) || COMPILE_TEST
|
||||
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).
|
||||
|
Loading…
Reference in New Issue
Block a user