diff --git a/drivers/gpio/gpio-fsm.c b/drivers/gpio/gpio-fsm.c index b708953c59f8a1..8f09b5a2e5aa57 100644 --- a/drivers/gpio/gpio-fsm.c +++ b/drivers/gpio/gpio-fsm.c @@ -193,131 +193,11 @@ static void free_symbols(struct symtab_entry **symtab) } } -static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off) -{ - struct gpio_fsm *gf = gpiochip_get_data(gc); - struct soft_gpio *sg; - - if (off >= gf->num_soft_gpios) - return -EINVAL; - sg = &gf->soft_gpios[off]; - - return sg->dir; -} - -static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off) -{ - struct gpio_fsm *gf = gpiochip_get_data(gc); - struct soft_gpio *sg; - - if (off >= gf->num_soft_gpios) - return -EINVAL; - sg = &gf->soft_gpios[off]; - - return sg->value; -} - -static void gpio_fsm_go_to_state(struct gpio_fsm *gf, - struct fsm_state *new_state) -{ - struct input_gpio_state *inp_state; - struct gpio_event *gp_ev; - struct fsm_state *state; - int i; - - dev_dbg(gf->dev, "go_to_state(%s)\n", - new_state ? new_state->name : ""); - - spin_lock(&gf->spinlock); - - if (gf->next_state) { - /* Something else has already requested a transition */ - spin_unlock(&gf->spinlock); - return; - } - - gf->next_state = new_state; - state = gf->current_state; - gf->delay_target_state = NULL; - - if (state) { - /* Disarm any GPIO IRQs */ - for (i = 0; i < state->num_gpio_events; i++) { - gp_ev = &state->gpio_events[i]; - inp_state = &gf->input_gpio_states[gp_ev->index]; - inp_state->target = NULL; - } - } - - spin_unlock(&gf->spinlock); - - if (new_state) - schedule_work(&gf->work); -} - static void gpio_fsm_set_soft(struct gpio_fsm *gf, - unsigned int off, int val) -{ - struct soft_gpio *sg = &gf->soft_gpios[off]; - struct gpio_event *gp_ev; - struct fsm_state *state; - int i; - - dev_dbg(gf->dev, "set(%d,%d)\n", off, val); - state = gf->current_state; - sg->value = val; - for (i = 0; i < state->num_soft_events; i++) { - gp_ev = &state->soft_events[i]; - if (gp_ev->index == off && gp_ev->value == val) { - if (gf->debug) - dev_info(gf->dev, - "GF_SOFT %d->%d -> %s\n", gp_ev->index, - gp_ev->value, gp_ev->target->name); - gpio_fsm_go_to_state(gf, gp_ev->target); - break; - } - } -} - -static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off) -{ - struct gpio_fsm *gf = gpiochip_get_data(gc); - struct soft_gpio *sg; - - if (off >= gf->num_soft_gpios) - return -EINVAL; - sg = &gf->soft_gpios[off]; - sg->dir = GPIOF_DIR_IN; - - return 0; -} - -static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off, - int value) -{ - struct gpio_fsm *gf = gpiochip_get_data(gc); - struct soft_gpio *sg; - - if (off >= gf->num_soft_gpios) - return -EINVAL; - sg = &gf->soft_gpios[off]; - sg->dir = GPIOF_DIR_OUT; - gpio_fsm_set_soft(gf, off, value); - - return 0; -} - -static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val) -{ - struct gpio_fsm *gf; - - gf = gpiochip_get_data(gc); - if (off < gf->num_soft_gpios) - gpio_fsm_set_soft(gf, off, val); -} + unsigned int off, int val); static void gpio_fsm_enter_state(struct gpio_fsm *gf, - struct fsm_state *state) + struct fsm_state *state) { struct input_gpio_state *inp_state; struct output_signal *signal; @@ -330,6 +210,7 @@ static void gpio_fsm_enter_state(struct gpio_fsm *gf, dev_dbg(gf->dev, "enter_state(%s)\n", state->name); gf->current_state = state; + gf->delay_target_state = NULL; // 1. Apply any listed signals for (i = 0; i < state->num_signals; i++) { @@ -388,7 +269,7 @@ static void gpio_fsm_enter_state(struct gpio_fsm *gf, dev_info(gf->dev, "GF_SOFT %d=%d -> %s\n", event->index, event->value, event->target->name); - gpio_fsm_go_to_state(gf, event->target); + gpio_fsm_enter_state(gf, event->target); return; } } @@ -401,7 +282,7 @@ static void gpio_fsm_enter_state(struct gpio_fsm *gf, inp_state->value = event->value; inp_state->enabled = true; - value = gpiod_get_value(gf->input_gpios->desc[event->index]); + value = gpiod_get_value_cansleep(gf->input_gpios->desc[event->index]); // Clear stale event state disable_irq(inp_state->irq); @@ -416,7 +297,7 @@ static void gpio_fsm_enter_state(struct gpio_fsm *gf, dev_info(gf->dev, "GF_IN %d=%d -> %s\n", event->index, event->value, event->target->name); - gpio_fsm_go_to_state(gf, event->target); + gpio_fsm_enter_state(gf, event->target); return; } } @@ -431,40 +312,79 @@ static void gpio_fsm_enter_state(struct gpio_fsm *gf, } } -static void gpio_fsm_work(struct work_struct *work) +static void gpio_fsm_go_to_state(struct gpio_fsm *gf, + struct fsm_state *new_state) { struct input_gpio_state *inp_state; - struct fsm_state *new_state; + struct gpio_event *gp_ev; struct fsm_state *state; + int i; + + dev_dbg(gf->dev, "go_to_state(%s)\n", + new_state ? new_state->name : ""); + + state = gf->current_state; + + /* Disable any enabled GPIO IRQs */ + for (i = 0; i < state->num_gpio_events; i++) { + gp_ev = &state->gpio_events[i]; + inp_state = &gf->input_gpio_states[gp_ev->index]; + if (inp_state->enabled) { + inp_state->enabled = false; + irq_set_irq_type(inp_state->irq, + IRQF_TRIGGER_NONE); + } + } + + gpio_fsm_enter_state(gf, new_state); +} + +static void gpio_fsm_go_to_state_deferred(struct gpio_fsm *gf, + struct fsm_state *new_state) +{ + struct input_gpio_state *inp_state; struct gpio_event *gp_ev; - struct gpio_fsm *gf; + struct fsm_state *state; int i; - gf = container_of(work, struct gpio_fsm, work); + dev_dbg(gf->dev, "go_to_state_deferred(%s)\n", + new_state ? new_state->name : ""); + spin_lock(&gf->spinlock); + + if (gf->next_state) { + /* Something else has already requested a transition */ + spin_unlock(&gf->spinlock); + return; + } + + gf->next_state = new_state; state = gf->current_state; + + /* Disarm any GPIO IRQs */ + for (i = 0; i < state->num_gpio_events; i++) { + gp_ev = &state->gpio_events[i]; + inp_state = &gf->input_gpio_states[gp_ev->index]; + inp_state->target = NULL; + } + + spin_unlock(&gf->spinlock); + + schedule_work(&gf->work); +} + +static void gpio_fsm_work(struct work_struct *work) +{ + struct fsm_state *new_state; + struct gpio_fsm *gf; + + gf = container_of(work, struct gpio_fsm, work); + spin_lock(&gf->spinlock); new_state = gf->next_state; - if (!new_state) - new_state = gf->delay_target_state; gf->next_state = NULL; - gf->delay_target_state = NULL; spin_unlock(&gf->spinlock); - if (state) { - /* Disable any enabled GPIO IRQs */ - for (i = 0; i < state->num_gpio_events; i++) { - gp_ev = &state->gpio_events[i]; - inp_state = &gf->input_gpio_states[gp_ev->index]; - if (inp_state->enabled) { - inp_state->enabled = false; - irq_set_irq_type(inp_state->irq, - IRQF_TRIGGER_NONE); - } - } - } - - if (new_state) - gpio_fsm_enter_state(gf, new_state); + gpio_fsm_go_to_state(gf, new_state); } static irqreturn_t gpio_fsm_gpio_irq_handler(int irq, void *dev_id) @@ -483,7 +403,7 @@ static irqreturn_t gpio_fsm_gpio_irq_handler(int irq, void *dev_id) if (gf->debug) dev_info(gf->dev, "GF_IN %d->%d -> %s\n", inp_state->index, inp_state->value, target->name); - gpio_fsm_go_to_state(gf, target); + gpio_fsm_go_to_state_deferred(gf, target); return IRQ_HANDLED; } @@ -495,12 +415,11 @@ static void gpio_fsm_timer(struct timer_list *timer) target = gf->delay_target_state; if (!target) return; - if (gf->debug) dev_info(gf->dev, "GF_DELAY %d -> %s\n", gf->delay_ms, target->name); - gpio_fsm_go_to_state(gf, target); + gpio_fsm_go_to_state_deferred(gf, target); } int gpio_fsm_parse_signals(struct gpio_fsm *gf, struct fsm_state *state, @@ -851,6 +770,90 @@ static int resolve_sym_to_state(struct gpio_fsm *gf, struct fsm_state **pstate) return 0; } +static void gpio_fsm_set_soft(struct gpio_fsm *gf, + unsigned int off, int val) +{ + struct soft_gpio *sg = &gf->soft_gpios[off]; + struct gpio_event *gp_ev; + struct fsm_state *state; + int i; + + dev_dbg(gf->dev, "set(%d,%d)\n", off, val); + state = gf->current_state; + sg->value = val; + for (i = 0; i < state->num_soft_events; i++) { + gp_ev = &state->soft_events[i]; + if (gp_ev->index == off && gp_ev->value == val) { + if (gf->debug) + dev_info(gf->dev, + "GF_SOFT %d->%d -> %s\n", gp_ev->index, + gp_ev->value, gp_ev->target->name); + gpio_fsm_go_to_state(gf, gp_ev->target); + break; + } + } +} + +static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off) +{ + struct gpio_fsm *gf = gpiochip_get_data(gc); + struct soft_gpio *sg; + + if (off >= gf->num_soft_gpios) + return -EINVAL; + sg = &gf->soft_gpios[off]; + + return sg->value; +} + +static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val) +{ + struct gpio_fsm *gf; + + gf = gpiochip_get_data(gc); + if (off < gf->num_soft_gpios) + gpio_fsm_set_soft(gf, off, val); +} + +static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off) +{ + struct gpio_fsm *gf = gpiochip_get_data(gc); + struct soft_gpio *sg; + + if (off >= gf->num_soft_gpios) + return -EINVAL; + sg = &gf->soft_gpios[off]; + + return sg->dir; +} + +static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off) +{ + struct gpio_fsm *gf = gpiochip_get_data(gc); + struct soft_gpio *sg; + + if (off >= gf->num_soft_gpios) + return -EINVAL; + sg = &gf->soft_gpios[off]; + sg->dir = GPIOF_DIR_IN; + + return 0; +} + +static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off, + int value) +{ + struct gpio_fsm *gf = gpiochip_get_data(gc); + struct soft_gpio *sg; + + if (off >= gf->num_soft_gpios) + return -EINVAL; + sg = &gf->soft_gpios[off]; + sg->dir = GPIOF_DIR_OUT; + gpio_fsm_set_soft(gf, off, value); + + return 0; +} /* * /sys/class/gpio-fsm// @@ -1114,7 +1117,7 @@ static int gpio_fsm_probe(struct platform_device *pdev) if (gf->debug) dev_info(gf->dev, "Start -> %s\n", gf->start_state->name); - gpio_fsm_go_to_state(gf, gf->start_state); + gpio_fsm_enter_state(gf, gf->start_state); return devm_gpiochip_add_data(dev, &gf->gc, gf); }