diff options
Diffstat (limited to 'drivers/pinctrl/sh-pfc/gpio.c')
-rw-r--r-- | drivers/pinctrl/sh-pfc/gpio.c | 372 |
1 files changed, 304 insertions, 68 deletions
diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c index a535075c8b6..d37efa7dcf9 100644 --- a/drivers/pinctrl/sh-pfc/gpio.c +++ b/drivers/pinctrl/sh-pfc/gpio.c @@ -9,8 +9,6 @@ * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME " gpio: " fmt - #include <linux/device.h> #include <linux/gpio.h> #include <linux/init.h> @@ -21,9 +19,23 @@ #include "core.h" +struct sh_pfc_gpio_data_reg { + const struct pinmux_data_reg *info; + unsigned long shadow; +}; + +struct sh_pfc_gpio_pin { + u8 dbit; + u8 dreg; +}; + struct sh_pfc_chip { - struct sh_pfc *pfc; - struct gpio_chip gpio_chip; + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; + + struct sh_pfc_window *mem; + struct sh_pfc_gpio_data_reg *regs; + struct sh_pfc_gpio_pin *pins; }; static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) @@ -36,143 +48,367 @@ static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) return gpio_to_pfc_chip(gc)->pfc; } -static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +static void gpio_get_data_reg(struct sh_pfc_chip *chip, unsigned int gpio, + struct sh_pfc_gpio_data_reg **reg, + unsigned int *bit) { - return pinctrl_request_gpio(offset); + int idx = sh_pfc_get_pin_index(chip->pfc, gpio); + struct sh_pfc_gpio_pin *gpio_pin = &chip->pins[idx]; + + *reg = &chip->regs[gpio_pin->dreg]; + *bit = gpio_pin->dbit; } -static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +static unsigned long gpio_read_data_reg(struct sh_pfc_chip *chip, + const struct pinmux_data_reg *dreg) { - pinctrl_free_gpio(offset); + void __iomem *mem = dreg->reg - chip->mem->phys + chip->mem->virt; + + return sh_pfc_read_raw_reg(mem, dreg->reg_width); } -static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +static void gpio_write_data_reg(struct sh_pfc_chip *chip, + const struct pinmux_data_reg *dreg, + unsigned long value) { - struct pinmux_data_reg *dr = NULL; - int bit = 0; + void __iomem *mem = dreg->reg - chip->mem->phys + chip->mem->virt; - if (sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) - BUG(); - else - sh_pfc_write_bit(dr, bit, value); + sh_pfc_write_raw_reg(mem, dreg->reg_width, value); } -static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +static void gpio_setup_data_reg(struct sh_pfc_chip *chip, unsigned gpio) { - struct pinmux_data_reg *dr = NULL; - int bit = 0; + struct sh_pfc *pfc = chip->pfc; + struct sh_pfc_gpio_pin *gpio_pin = &chip->pins[gpio]; + const struct sh_pfc_pin *pin = &pfc->info->pins[gpio]; + const struct pinmux_data_reg *dreg; + unsigned int bit; + unsigned int i; + + for (i = 0, dreg = pfc->info->data_regs; dreg->reg; ++i, ++dreg) { + for (bit = 0; bit < dreg->reg_width; bit++) { + if (dreg->enum_ids[bit] == pin->enum_id) { + gpio_pin->dreg = i; + gpio_pin->dbit = bit; + return; + } + } + } - if (sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); +} + +static int gpio_setup_data_regs(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + const struct pinmux_data_reg *dreg; + unsigned int i; + + /* Count the number of data registers, allocate memory and initialize + * them. + */ + for (i = 0; pfc->info->data_regs[i].reg_width; ++i) + ; + + chip->regs = devm_kzalloc(pfc->dev, i * sizeof(*chip->regs), + GFP_KERNEL); + if (chip->regs == NULL) + return -ENOMEM; + + for (i = 0, dreg = pfc->info->data_regs; dreg->reg_width; ++i, ++dreg) { + chip->regs[i].info = dreg; + chip->regs[i].shadow = gpio_read_data_reg(chip, dreg); + } + + for (i = 0; i < pfc->info->nr_pins; i++) { + if (pfc->info->pins[i].enum_id == 0) + continue; + + gpio_setup_data_reg(chip, i); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pin GPIOs + */ + +static int gpio_pin_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + int idx = sh_pfc_get_pin_index(pfc, offset); + + if (idx < 0 || pfc->info->pins[idx].enum_id == 0) return -EINVAL; - return sh_pfc_read_bit(dr, bit); + return pinctrl_request_gpio(offset); +} + +static void gpio_pin_free(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_free_gpio(offset); +} + +static void gpio_pin_set_value(struct sh_pfc_chip *chip, unsigned offset, + int value) +{ + struct sh_pfc_gpio_data_reg *reg; + unsigned long pos; + unsigned int bit; + + gpio_get_data_reg(chip, offset, ®, &bit); + + pos = reg->info->reg_width - (bit + 1); + + if (value) + set_bit(pos, ®->shadow); + else + clear_bit(pos, ®->shadow); + + gpio_write_data_reg(chip, reg->info, reg->shadow); } -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +static int gpio_pin_direction_input(struct gpio_chip *gc, unsigned offset) { return pinctrl_gpio_direction_input(offset); } -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, +static int gpio_pin_direction_output(struct gpio_chip *gc, unsigned offset, int value) { - sh_gpio_set_value(gpio_to_pfc(gc), offset, value); + gpio_pin_set_value(gpio_to_pfc_chip(gc), offset, value); return pinctrl_gpio_direction_output(offset); } -static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +static int gpio_pin_get(struct gpio_chip *gc, unsigned offset) { - return sh_gpio_get_value(gpio_to_pfc(gc), offset); + struct sh_pfc_chip *chip = gpio_to_pfc_chip(gc); + struct sh_pfc_gpio_data_reg *reg; + unsigned long pos; + unsigned int bit; + + gpio_get_data_reg(chip, offset, ®, &bit); + + pos = reg->info->reg_width - (bit + 1); + + return (gpio_read_data_reg(chip, reg->info) >> pos) & 1; } -static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +static void gpio_pin_set(struct gpio_chip *gc, unsigned offset, int value) { - sh_gpio_set_value(gpio_to_pfc(gc), offset, value); + gpio_pin_set_value(gpio_to_pfc_chip(gc), offset, value); } -static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +static int gpio_pin_to_irq(struct gpio_chip *gc, unsigned offset) { struct sh_pfc *pfc = gpio_to_pfc(gc); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; + int i, k; - for (i = 0; i < pfc->info->gpio_irq_size; i++) { - enum_ids = pfc->info->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return pfc->info->gpio_irq[i].irq; - } + for (i = 0; i < pfc->info->gpio_irq_size; i++) { + unsigned short *gpios = pfc->info->gpio_irq[i].gpios; + + for (k = 0; gpios[k]; k++) { + if (gpios[k] == offset) + return pfc->info->gpio_irq[i].irq; } } return -ENOSYS; } -static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +static int gpio_pin_setup(struct sh_pfc_chip *chip) { struct sh_pfc *pfc = chip->pfc; struct gpio_chip *gc = &chip->gpio_chip; + int ret; + + chip->pins = devm_kzalloc(pfc->dev, pfc->nr_pins * sizeof(*chip->pins), + GFP_KERNEL); + if (chip->pins == NULL) + return -ENOMEM; + + ret = gpio_setup_data_regs(chip); + if (ret < 0) + return ret; + + gc->request = gpio_pin_request; + gc->free = gpio_pin_free; + gc->direction_input = gpio_pin_direction_input; + gc->get = gpio_pin_get; + gc->direction_output = gpio_pin_direction_output; + gc->set = gpio_pin_set; + gc->to_irq = gpio_pin_to_irq; - gc->request = sh_gpio_request; - gc->free = sh_gpio_free; - gc->direction_input = sh_gpio_direction_input; - gc->get = sh_gpio_get; - gc->direction_output = sh_gpio_direction_output; - gc->set = sh_gpio_set; - gc->to_irq = sh_gpio_to_irq; + gc->label = pfc->info->name; + gc->dev = pfc->dev; + gc->owner = THIS_MODULE; + gc->base = 0; + gc->ngpio = pfc->nr_pins; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Function GPIOs + */ + +static int gpio_function_request(struct gpio_chip *gc, unsigned offset) +{ + static bool __print_once; + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned int mark = pfc->info->func_gpios[offset].enum_id; + unsigned long flags; + int ret; + + if (!__print_once) { + dev_notice(pfc->dev, + "Use of GPIO API for function requests is deprecated." + " Convert to pinctrl\n"); + __print_once = true; + } + + if (mark == 0) + return -EINVAL; - WARN_ON(pfc->info->first_gpio != 0); /* needs testing */ + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_config_mux(pfc, mark, PINMUX_TYPE_FUNCTION); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void gpio_function_free(struct gpio_chip *gc, unsigned offset) +{ +} + +static int gpio_function_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = gpio_function_request; + gc->free = gpio_function_free; gc->label = pfc->info->name; gc->owner = THIS_MODULE; - gc->base = pfc->info->first_gpio; - gc->ngpio = (pfc->info->last_gpio - pfc->info->first_gpio) + 1; + gc->base = pfc->nr_pins; + gc->ngpio = pfc->info->nr_func_gpios; + + return 0; } -int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +/* ----------------------------------------------------------------------------- + * Register/unregister + */ + +static struct sh_pfc_chip * +sh_pfc_add_gpiochip(struct sh_pfc *pfc, int(*setup)(struct sh_pfc_chip *), + struct sh_pfc_window *mem) { struct sh_pfc_chip *chip; int ret; chip = devm_kzalloc(pfc->dev, sizeof(*chip), GFP_KERNEL); if (unlikely(!chip)) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + chip->mem = mem; chip->pfc = pfc; - sh_pfc_gpio_setup(chip); + ret = setup(chip); + if (ret < 0) + return ERR_PTR(ret); ret = gpiochip_add(&chip->gpio_chip); if (unlikely(ret < 0)) - return ret; + return ERR_PTR(ret); + + dev_info(pfc->dev, "%s handling gpio %u -> %u\n", + chip->gpio_chip.label, chip->gpio_chip.base, + chip->gpio_chip.base + chip->gpio_chip.ngpio - 1); + + return chip; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + const struct pinmux_range *ranges; + struct pinmux_range def_range; + struct sh_pfc_chip *chip; + unsigned int nr_ranges; + unsigned int i; + int ret; + + if (pfc->info->data_regs == NULL) + return 0; + + /* Find the memory window that contain the GPIO registers. Boards that + * register a separate GPIO device will not supply a memory resource + * that covers the data registers. In that case don't try to handle + * GPIOs. + */ + for (i = 0; i < pfc->num_windows; ++i) { + struct sh_pfc_window *window = &pfc->window[i]; + + if (pfc->info->data_regs[0].reg >= window->phys && + pfc->info->data_regs[0].reg < window->phys + window->size) + break; + } + + if (i == pfc->num_windows) + return 0; + + /* Register the real GPIOs chip. */ + chip = sh_pfc_add_gpiochip(pfc, gpio_pin_setup, &pfc->window[i]); + if (IS_ERR(chip)) + return PTR_ERR(chip); pfc->gpio = chip; - pr_info("%s handling gpio %d -> %d\n", - pfc->info->name, pfc->info->first_gpio, - pfc->info->last_gpio); + /* Register the GPIO to pin mappings. */ + if (pfc->info->ranges == NULL) { + def_range.begin = 0; + def_range.end = pfc->info->nr_pins - 1; + ranges = &def_range; + nr_ranges = 1; + } else { + ranges = pfc->info->ranges; + nr_ranges = pfc->info->nr_ranges; + } + + for (i = 0; i < nr_ranges; ++i) { + const struct pinmux_range *range = &ranges[i]; + + ret = gpiochip_add_pin_range(&chip->gpio_chip, + dev_name(pfc->dev), + range->begin, range->begin, + range->end - range->begin + 1); + if (ret < 0) + return ret; + } + + /* Register the function GPIOs chip. */ + if (pfc->info->nr_func_gpios == 0) + return 0; + + chip = sh_pfc_add_gpiochip(pfc, gpio_function_setup, NULL); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + pfc->func = chip; return 0; } int sh_pfc_unregister_gpiochip(struct sh_pfc *pfc) { - struct sh_pfc_chip *chip = pfc->gpio; + int err; int ret; - ret = gpiochip_remove(&chip->gpio_chip); - if (unlikely(ret < 0)) - return ret; + ret = gpiochip_remove(&pfc->gpio->gpio_chip); + err = gpiochip_remove(&pfc->func->gpio_chip); - pfc->gpio = NULL; - return 0; + return ret < 0 ? ret : err; } |