diff options
Diffstat (limited to 'drivers/pinctrl/pinctrl-nomadik.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-nomadik.c | 540 |
1 files changed, 449 insertions, 91 deletions
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index cf82d9ce4dee..36d20293de5c 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -25,32 +25,17 @@ #include <linux/irqdomain.h> #include <linux/slab.h> #include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> /* Since we request GPIOs from ourself */ #include <linux/pinctrl/consumer.h> -/* - * For the U8500 archs, use the PRCMU register interface, for the older - * Nomadik, provide some stubs. The functions using these will only be - * called on the U8500 series. - */ -#ifdef CONFIG_ARCH_U8500 -#include <linux/mfd/dbx500-prcmu.h> -#else -static inline u32 prcmu_read(unsigned int reg) { - return 0; -} -static inline void prcmu_write(unsigned int reg, u32 value) {} -static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value) {} -#endif - +#include <linux/platform_data/pinctrl-nomadik.h> #include <asm/mach/irq.h> - -#include <plat/pincfg.h> -#include <plat/gpio-nomadik.h> - #include "pinctrl-nomadik.h" +#include "core.h" /* * The GPIO module in the Nomadik family of Systems-on-Chip is an @@ -60,8 +45,6 @@ static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value) {} * Symbols in this file are called "nmk_gpio" for "nomadik gpio" */ -#define NMK_GPIO_PER_CHIP 32 - struct nmk_gpio_chip { struct gpio_chip chip; struct irq_domain *domain; @@ -86,10 +69,18 @@ struct nmk_gpio_chip { u32 lowemi; }; +/** + * struct nmk_pinctrl - state container for the Nomadik pin controller + * @dev: containing device pointer + * @pctl: corresponding pin controller device + * @soc: SoC data for this specific chip + * @prcm_base: PRCM register range virtual base + */ struct nmk_pinctrl { struct device *dev; struct pinctrl_dev *pctl; const struct nmk_pinctrl_soc_data *soc; + void __iomem *prcm_base; }; static struct nmk_gpio_chip * @@ -227,7 +218,7 @@ nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset) u32 falling = nmk_chip->fimsc & BIT(offset); u32 rising = nmk_chip->rimsc & BIT(offset); int gpio = nmk_chip->chip.base + offset; - int irq = NOMADIK_GPIO_TO_IRQ(gpio); + int irq = irq_find_mapping(nmk_chip->domain, offset); struct irq_data *d = irq_get_irq_data(irq); if (!rising && !falling) @@ -251,6 +242,15 @@ nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset) dev_dbg(nmk_chip->chip.dev, "%d: clearing interrupt mask\n", gpio); } +static void nmk_write_masked(void __iomem *reg, u32 mask, u32 value) +{ + u32 val; + + val = readl(reg); + val = ((val & ~mask) | (value & mask)); + writel(val, reg); +} + static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, unsigned offset, unsigned alt_num) { @@ -261,6 +261,9 @@ static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, const struct prcm_gpiocr_altcx_pin_desc *pin_desc; const u16 *gpiocr_regs; + if (!npct->prcm_base) + return; + if (alt_num > PRCM_IDX_GPIOCR_ALTC_MAX) { dev_err(npct->dev, "PRCM GPIOCR: alternate-C%i is invalid\n", alt_num); @@ -289,8 +292,8 @@ static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, if (pin_desc->altcx[i].used == true) { reg = gpiocr_regs[pin_desc->altcx[i].reg_index]; bit = pin_desc->altcx[i].control_bit; - if (prcmu_read(reg) & BIT(bit)) { - prcmu_write_masked(reg, BIT(bit), 0); + if (readl(npct->prcm_base + reg) & BIT(bit)) { + nmk_write_masked(npct->prcm_base + reg, BIT(bit), 0); dev_dbg(npct->dev, "PRCM GPIOCR: pin %i: alternate-C%i has been disabled\n", offset, i+1); @@ -318,8 +321,8 @@ static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, if (pin_desc->altcx[i].used == true) { reg = gpiocr_regs[pin_desc->altcx[i].reg_index]; bit = pin_desc->altcx[i].control_bit; - if (prcmu_read(reg) & BIT(bit)) { - prcmu_write_masked(reg, BIT(bit), 0); + if (readl(npct->prcm_base + reg) & BIT(bit)) { + nmk_write_masked(npct->prcm_base + reg, BIT(bit), 0); dev_dbg(npct->dev, "PRCM GPIOCR: pin %i: alternate-C%i has been disabled\n", offset, i+1); @@ -331,7 +334,7 @@ static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, bit = pin_desc->altcx[alt_index].control_bit; dev_dbg(npct->dev, "PRCM GPIOCR: pin %i: alternate-C%i has been selected\n", offset, alt_index+1); - prcmu_write_masked(reg, BIT(bit), BIT(bit)); + nmk_write_masked(npct->prcm_base + reg, BIT(bit), BIT(bit)); } static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, @@ -536,7 +539,7 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) * and its sleep mode based on the specified configuration. The @cfg is * usually one of the SoC specific macros defined in mach/<soc>-pins.h. These * are constructed using, and can be further enhanced with, the macros in - * plat/pincfg.h. + * <linux/platform_data/pinctrl-nomadik.h> * * If a pin's mode is set to GPIO, it is configured as an input to avoid * side-effects. The gpio can be manipulated later using standard GPIO API @@ -675,6 +678,38 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode) } EXPORT_SYMBOL(nmk_gpio_set_mode); +static int __maybe_unused nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio) +{ + int i; + u16 reg; + u8 bit; + struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + const struct prcm_gpiocr_altcx_pin_desc *pin_desc; + const u16 *gpiocr_regs; + + if (!npct->prcm_base) + return NMK_GPIO_ALT_C; + + for (i = 0; i < npct->soc->npins_altcx; i++) { + if (npct->soc->altcx_pins[i].pin == gpio) + break; + } + if (i == npct->soc->npins_altcx) + return NMK_GPIO_ALT_C; + + pin_desc = npct->soc->altcx_pins + i; + gpiocr_regs = npct->soc->prcm_gpiocr_registers; + for (i = 0; i < PRCM_IDX_GPIOCR_ALTC_MAX; i++) { + if (pin_desc->altcx[i].used == true) { + reg = gpiocr_regs[pin_desc->altcx[i].reg_index]; + bit = pin_desc->altcx[i].control_bit; + if (readl(npct->prcm_base + reg) & BIT(bit)) + return NMK_GPIO_ALT_C+i+1; + } + } + return NMK_GPIO_ALT_C; +} + int nmk_gpio_get_mode(int gpio) { struct nmk_gpio_chip *nmk_chip; @@ -1063,8 +1098,9 @@ static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset) #include <linux/seq_file.h> -static void nmk_gpio_dbg_show_one(struct seq_file *s, struct gpio_chip *chip, - unsigned offset, unsigned gpio) +static void nmk_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, struct gpio_chip *chip, + unsigned offset, unsigned gpio) { const char *label = gpiochip_is_requested(chip, offset); struct nmk_gpio_chip *nmk_chip = @@ -1078,12 +1114,18 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s, struct gpio_chip *chip, [NMK_GPIO_ALT_A] = "altA", [NMK_GPIO_ALT_B] = "altB", [NMK_GPIO_ALT_C] = "altC", + [NMK_GPIO_ALT_C+1] = "altC1", + [NMK_GPIO_ALT_C+2] = "altC2", + [NMK_GPIO_ALT_C+3] = "altC3", + [NMK_GPIO_ALT_C+4] = "altC4", }; clk_enable(nmk_chip->clk); is_out = !!(readl(nmk_chip->addr + NMK_GPIO_DIR) & bit); pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit); mode = nmk_gpio_get_mode(gpio); + if ((mode == NMK_GPIO_ALT_C) && pctldev) + mode = nmk_prcm_gpiocr_get_mode(pctldev, gpio); seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s", gpio, label ?: "(none)", @@ -1127,13 +1169,14 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned gpio = chip->base; for (i = 0; i < chip->ngpio; i++, gpio++) { - nmk_gpio_dbg_show_one(s, chip, i, gpio); + nmk_gpio_dbg_show_one(s, NULL, chip, i, gpio); seq_printf(s, "\n"); } } #else static inline void nmk_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned offset, unsigned gpio) { @@ -1250,8 +1293,8 @@ void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up) } } -int nmk_gpio_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static int nmk_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct nmk_gpio_chip *nmk_chip = d->host_data; @@ -1271,7 +1314,7 @@ const struct irq_domain_ops nmk_gpio_irq_simple_ops = { .xlate = irq_domain_xlate_twocell, }; -static int __devinit nmk_gpio_probe(struct platform_device *dev) +static int nmk_gpio_probe(struct platform_device *dev) { struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; struct device_node *np = dev->dev.of_node; @@ -1300,8 +1343,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) if (of_property_read_u32(np, "gpio-bank", &dev->id)) { dev_err(&dev->dev, "gpio-bank property not found\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } pdata->first_gpio = dev->id * NMK_GPIO_PER_CHIP; @@ -1309,41 +1351,29 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) } res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto out; - } + if (!res) + return -ENOENT; irq = platform_get_irq(dev, 0); - if (irq < 0) { - ret = irq; - goto out; - } + if (irq < 0) + return irq; secondary_irq = platform_get_irq(dev, 1); - if (secondary_irq >= 0 && !pdata->get_secondary_status) { - ret = -EINVAL; - goto out; - } + if (secondary_irq >= 0 && !pdata->get_secondary_status) + return -EINVAL; - base = devm_request_and_ioremap(&dev->dev, res); - if (!base) { - ret = -ENOMEM; - goto out; - } + base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto out; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); clk_prepare(clk); nmk_chip = devm_kzalloc(&dev->dev, sizeof(*nmk_chip), GFP_KERNEL); - if (!nmk_chip) { - ret = -ENOMEM; - goto out; - } + if (!nmk_chip) + return -ENOMEM; /* * The virt address in nmk_chip->addr is in the nomadik register space, @@ -1377,7 +1407,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) ret = gpiochip_add(&nmk_chip->chip); if (ret) - goto out; + return ret; BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips)); @@ -1386,14 +1416,15 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) platform_set_drvdata(dev, nmk_chip); if (!np) - irq_start = NOMADIK_GPIO_TO_IRQ(pdata->first_gpio); + irq_start = pdata->first_irq; nmk_chip->domain = irq_domain_add_simple(np, NMK_GPIO_PER_CHIP, irq_start, &nmk_gpio_irq_simple_ops, nmk_chip); if (!nmk_chip->domain) { dev_err(&dev->dev, "failed to create irqdomain\n"); - ret = -ENOSYS; - goto out; + /* Just do this, no matter if it fails */ + ret = gpiochip_remove(&nmk_chip->chip); + return -ENOSYS; } nmk_gpio_init_irq(nmk_chip); @@ -1401,12 +1432,6 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) dev_info(&dev->dev, "at address %p\n", nmk_chip->addr); return 0; - -out: - dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, - pdata->first_gpio, pdata->first_gpio+31); - - return ret; } static int nmk_get_groups_cnt(struct pinctrl_dev *pctldev) @@ -1464,7 +1489,279 @@ static void nmk_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, return; } chip = range->gc; - nmk_gpio_dbg_show_one(s, chip, offset - chip->base, offset); + nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset); +} + +static void nmk_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + kfree(map); +} + +static int nmk_dt_reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, unsigned reserve) +{ + unsigned old_num = *reserved_maps; + unsigned new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int nmk_dt_add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, const char *group, + const char *function) +{ + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int nmk_dt_add_map_configs(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, const char *group, + unsigned long *configs, unsigned num_configs) +{ + unsigned long *dup_configs; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) + return -ENOMEM; + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; + + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +#define NMK_CONFIG_PIN(x,y) { .property = x, .config = y, } +#define NMK_CONFIG_PIN_ARRAY(x,y) { .property = x, .choice = y, \ + .size = ARRAY_SIZE(y), } + +static const unsigned long nmk_pin_input_modes[] = { + PIN_INPUT_NOPULL, + PIN_INPUT_PULLUP, + PIN_INPUT_PULLDOWN, +}; + +static const unsigned long nmk_pin_output_modes[] = { + PIN_OUTPUT_LOW, + PIN_OUTPUT_HIGH, + PIN_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_modes[] = { + PIN_SLEEPMODE_DISABLED, + PIN_SLEEPMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_input_modes[] = { + PIN_SLPM_INPUT_NOPULL, + PIN_SLPM_INPUT_PULLUP, + PIN_SLPM_INPUT_PULLDOWN, + PIN_SLPM_DIR_INPUT, +}; + +static const unsigned long nmk_pin_sleep_output_modes[] = { + PIN_SLPM_OUTPUT_LOW, + PIN_SLPM_OUTPUT_HIGH, + PIN_SLPM_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_wakeup_modes[] = { + PIN_SLPM_WAKEUP_DISABLE, + PIN_SLPM_WAKEUP_ENABLE, +}; + +static const unsigned long nmk_pin_gpio_modes[] = { + PIN_GPIOMODE_DISABLED, + PIN_GPIOMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_pdis_modes[] = { + PIN_SLPM_PDIS_DISABLED, + PIN_SLPM_PDIS_ENABLED, +}; + +struct nmk_cfg_param { + const char *property; + unsigned long config; + const unsigned long *choice; + int size; +}; + +static const struct nmk_cfg_param nmk_cfg_params[] = { + NMK_CONFIG_PIN_ARRAY("ste,input", nmk_pin_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,output", nmk_pin_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep", nmk_pin_sleep_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-input", nmk_pin_sleep_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-output", nmk_pin_sleep_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-wakeup", nmk_pin_sleep_wakeup_modes), + NMK_CONFIG_PIN_ARRAY("ste,gpio", nmk_pin_gpio_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-pull-disable", nmk_pin_sleep_pdis_modes), +}; + +static int nmk_dt_pin_config(int index, int val, unsigned long *config) +{ + int ret = 0; + + if (nmk_cfg_params[index].choice == NULL) + *config = nmk_cfg_params[index].config; + else { + /* test if out of range */ + if (val < nmk_cfg_params[index].size) { + *config = nmk_cfg_params[index].config | + nmk_cfg_params[index].choice[val]; + } + } + return ret; +} + +static const char *nmk_find_pin_name(struct pinctrl_dev *pctldev, const char *pin_name) +{ + int i, pin_number; + struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) + for (i = 0; i < npct->soc->npins; i++) + if (npct->soc->pins[i].number == pin_number) + return npct->soc->pins[i].name; + return NULL; +} + +static bool nmk_pinctrl_dt_get_config(struct device_node *np, + unsigned long *configs) +{ + bool has_config = 0; + unsigned long cfg = 0; + int i, val, ret; + + for (i = 0; i < ARRAY_SIZE(nmk_cfg_params); i++) { + ret = of_property_read_u32(np, + nmk_cfg_params[i].property, &val); + if (ret != -EINVAL) { + if (nmk_dt_pin_config(i, val, &cfg) == 0) { + *configs |= cfg; + has_config = 1; + } + } + } + + return has_config; +} + +int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + int ret; + const char *function = NULL; + unsigned long configs = 0; + bool has_config = 0; + unsigned reserve = 0; + struct property *prop; + const char *group, *gpio_name; + struct device_node *np_config; + + ret = of_property_read_string(np, "ste,function", &function); + if (ret >= 0) + reserve = 1; + + has_config = nmk_pinctrl_dt_get_config(np, &configs); + + np_config = of_parse_phandle(np, "ste,config", 0); + if (np_config) + has_config |= nmk_pinctrl_dt_get_config(np_config, &configs); + + ret = of_property_count_strings(np, "ste,pins"); + if (ret < 0) + goto exit; + + if (has_config) + reserve++; + + reserve *= ret; + + ret = nmk_dt_reserve_map(map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "ste,pins", prop, group) { + if (function) { + ret = nmk_dt_add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + if (has_config) { + gpio_name = nmk_find_pin_name(pctldev, group); + + ret = nmk_dt_add_map_configs(map, reserved_maps, num_maps, + gpio_name, &configs, 1); + if (ret < 0) + goto exit; + } + + } +exit: + return ret; +} + +int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + unsigned reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = nmk_pinctrl_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + nmk_pinctrl_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; } static struct pinctrl_ops nmk_pinctrl_ops = { @@ -1472,6 +1769,8 @@ static struct pinctrl_ops nmk_pinctrl_ops = { .get_group_name = nmk_get_group_name, .get_group_pins = nmk_get_group_pins, .pin_dbg_show = nmk_pin_dbg_show, + .dt_node_to_map = nmk_pinctrl_dt_node_to_map, + .dt_free_map = nmk_pinctrl_dt_free_map, }; static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) @@ -1635,9 +1934,9 @@ static void nmk_pmx_disable(struct pinctrl_dev *pctldev, dev_dbg(npct->dev, "disable group %s, %u pins\n", g->name, g->npins); } -int nmk_gpio_request_enable(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, - unsigned offset) +static int nmk_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) { struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); struct nmk_gpio_chip *nmk_chip; @@ -1666,9 +1965,9 @@ int nmk_gpio_request_enable(struct pinctrl_dev *pctldev, return 0; } -void nmk_gpio_disable_free(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, - unsigned offset) +static void nmk_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) { struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); @@ -1686,17 +1985,15 @@ static struct pinmux_ops nmk_pinmux_ops = { .gpio_disable_free = nmk_gpio_disable_free, }; -int nmk_pin_config_get(struct pinctrl_dev *pctldev, - unsigned pin, - unsigned long *config) +static int nmk_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) { /* Not implemented */ return -EINVAL; } -int nmk_pin_config_set(struct pinctrl_dev *pctldev, - unsigned pin, - unsigned long config) +static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config) { static const char *pullnames[] = { [NMK_GPIO_PULL_NONE] = "none", @@ -1807,17 +2104,45 @@ static struct pinctrl_desc nmk_pinctrl_desc = { static const struct of_device_id nmk_pinctrl_match[] = { { - .compatible = "stericsson,nmk_pinctrl", + .compatible = "stericsson,nmk-pinctrl-stn8815", + .data = (void *)PINCTRL_NMK_STN8815, + }, + { + .compatible = "stericsson,nmk-pinctrl", .data = (void *)PINCTRL_NMK_DB8500, }, {}, }; -static int __devinit nmk_pinctrl_probe(struct platform_device *pdev) +static int nmk_pinctrl_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct nmk_pinctrl *npct; + + npct = platform_get_drvdata(pdev); + if (!npct) + return -EINVAL; + + return pinctrl_force_sleep(npct->pctl); +} + +static int nmk_pinctrl_resume(struct platform_device *pdev) +{ + struct nmk_pinctrl *npct; + + npct = platform_get_drvdata(pdev); + if (!npct) + return -EINVAL; + + return pinctrl_force_default(npct->pctl); +} + +static int nmk_pinctrl_probe(struct platform_device *pdev) { const struct platform_device_id *platid = platform_get_device_id(pdev); struct device_node *np = pdev->dev.of_node; + struct device_node *prcm_np; struct nmk_pinctrl *npct; + struct resource *res; unsigned int version = 0; int i; @@ -1827,9 +2152,14 @@ static int __devinit nmk_pinctrl_probe(struct platform_device *pdev) if (platid) version = platid->driver_data; - else if (np) - version = (unsigned int) - of_match_device(nmk_pinctrl_match, &pdev->dev)->data; + else if (np) { + const struct of_device_id *match; + + match = of_match_device(nmk_pinctrl_match, &pdev->dev); + if (!match) + return -ENODEV; + version = (unsigned int) match->data; + } /* Poke in other ASIC variants here */ if (version == PINCTRL_NMK_STN8815) @@ -1839,22 +2169,45 @@ static int __devinit nmk_pinctrl_probe(struct platform_device *pdev) if (version == PINCTRL_NMK_DB8540) nmk_pinctrl_db8540_init(&npct->soc); + if (np) { + prcm_np = of_parse_phandle(np, "prcm", 0); + if (prcm_np) + npct->prcm_base = of_iomap(prcm_np, 0); + } + + /* Allow platform passed information to over-write DT. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + npct->prcm_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!npct->prcm_base) { + if (version == PINCTRL_NMK_STN8815) { + dev_info(&pdev->dev, + "No PRCM base, " + "assuming no ALT-Cx control is available\n"); + } else { + dev_err(&pdev->dev, "missing PRCM base address\n"); + return -EINVAL; + } + } + /* * We need all the GPIO drivers to probe FIRST, or we will not be able * to obtain references to the struct gpio_chip * for them, and we * need this to proceed. */ for (i = 0; i < npct->soc->gpio_num_ranges; i++) { - if (!nmk_gpio_chips[i]) { + if (!nmk_gpio_chips[npct->soc->gpio_ranges[i].id]) { dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i); return -EPROBE_DEFER; } - npct->soc->gpio_ranges[i].gc = &nmk_gpio_chips[i]->chip; + npct->soc->gpio_ranges[i].gc = &nmk_gpio_chips[npct->soc->gpio_ranges[i].id]->chip; } nmk_pinctrl_desc.pins = npct->soc->pins; nmk_pinctrl_desc.npins = npct->soc->npins; npct->dev = &pdev->dev; + npct->pctl = pinctrl_register(&nmk_pinctrl_desc, &pdev->dev, npct); if (!npct->pctl) { dev_err(&pdev->dev, "could not register Nomadik pinctrl driver\n"); @@ -1889,6 +2242,7 @@ static const struct platform_device_id nmk_pinctrl_id[] = { { "pinctrl-stn8815", PINCTRL_NMK_STN8815 }, { "pinctrl-db8500", PINCTRL_NMK_DB8500 }, { "pinctrl-db8540", PINCTRL_NMK_DB8540 }, + { } }; static struct platform_driver nmk_pinctrl_driver = { @@ -1899,6 +2253,10 @@ static struct platform_driver nmk_pinctrl_driver = { }, .probe = nmk_pinctrl_probe, .id_table = nmk_pinctrl_id, +#ifdef CONFIG_PM + .suspend = nmk_pinctrl_suspend, + .resume = nmk_pinctrl_resume, +#endif }; static int __init nmk_gpio_init(void) |