Loading drivers/pinctrl/qcom/pinctrl-msm.c +153 −41 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -70,6 +70,7 @@ struct msm_pinctrl { const struct msm_pinctrl_soc_data *soc; void __iomem *regs; void __iomem *pdc_regs; }; static int msm_get_groups_count(struct pinctrl_dev *pctldev) Loading Loading @@ -777,6 +778,102 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; static struct irq_chip msm_dirconn_irq_chip; static void msm_gpio_dirconn_handler(struct irq_desc *desc) { struct irq_data *irqd = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); generic_handle_irq(irqd->irq); chained_irq_exit(chip, desc); } static void setup_pdc_gpio(struct irq_domain *domain, unsigned int parent_irq, unsigned int gpio) { int irq; if (gpio != 0) { irq = irq_find_mapping(domain, gpio); irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); } static void request_dc_interrupt(struct irq_domain *domain, struct irq_domain *parent, irq_hw_number_t hwirq, unsigned int gpio) { struct irq_fwspec fwspec; unsigned int parent_irq; fwspec.fwnode = parent->fwnode; fwspec.param[0] = 0; /* SPI */ fwspec.param[1] = hwirq; fwspec.param[2] = IRQ_TYPE_NONE; fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); setup_pdc_gpio(domain, parent_irq, gpio); } /** * gpio_muxed_to_pdc: Mux the GPIO to a PDC IRQ * * @pdc_domain: the PDC's domain * @d: the GPIO's IRQ data * * Find a free PDC port for the GPIO and map the GPIO's mux information to the * PDC registers; so the GPIO can be used a wakeup source. */ static void gpio_muxed_to_pdc(struct irq_domain *pdc_domain, struct irq_data *d) { int i, j; unsigned int mux; struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); unsigned int gpio = d->hwirq; struct msm_pinctrl *pctrl; unsigned int irq; if (!gc || !parent_data) return; pctrl = gpiochip_get_data(gc); for (i = 0; i < pctrl->soc->n_gpio_mux_in; i++) { if (gpio != pctrl->soc->gpio_mux_in[i].gpio) continue; mux = pctrl->soc->gpio_mux_in[i].mux; for (j = 0; j < pctrl->soc->n_pdc_mux_out; j++) { struct msm_pdc_mux_output *pdc_out = &pctrl->soc->pdc_mux_out[j]; if (pdc_out->mux == mux) break; if (pdc_out->mux) continue; pdc_out->mux = gpio; irq = irq_find_mapping(pdc_domain, pdc_out->hwirq + 32); /* setup the IRQ parent for the GPIO */ setup_pdc_gpio(pctrl->chip.irqdomain, irq, gpio); /* program pdc select grp register */ writel_relaxed((mux & 0x3F), pctrl->pdc_regs + (0x14 * j)); break; } /* We have no more PDC port available */ WARN_ON(j == pctrl->soc->n_pdc_mux_out); } } static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) { struct irq_desc *desc = irq_data_to_desc(d); Loading @@ -797,6 +894,17 @@ static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) return true; } } for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { struct msm_pdc_mux_output *dir_conn = &pctrl->soc->pdc_mux_out[i]; if (dir_conn->mux == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *dir_conn_irq = dir_conn->hwirq + 32; return true; } } return false; } Loading @@ -814,9 +922,12 @@ static void msm_dirconn_irq_mask(struct irq_data *d) irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_mask) if (!dir_conn_data) return; if (dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } if (parent_data->chip->irq_mask) parent_data->chip->irq_mask(parent_data); } Loading @@ -835,7 +946,9 @@ static void msm_dirconn_irq_unmask(struct irq_data *d) irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_unmask) if (!dir_conn_data) return; if (dir_conn_data->chip->irq_unmask) dir_conn_data->chip->irq_unmask(dir_conn_data); } if (parent_data->chip->irq_unmask) Loading Loading @@ -1103,57 +1216,53 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } static void msm_gpio_dirconn_handler(struct irq_desc *desc) { struct irq_data *irqd = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); generic_handle_irq(irqd->irq); chained_irq_exit(chip, desc); } static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) { struct device_node *parent_node; struct irq_domain *parent_domain; struct irq_fwspec fwspec; struct irq_domain *pdc_domain; unsigned int i; parent_node = of_irq_find_parent(pctrl->dev->of_node); if (!parent_node) return; parent_domain = irq_find_host(parent_node); if (!parent_domain) pdc_domain = irq_find_host(parent_node); if (!pdc_domain) return; fwspec.fwnode = parent_domain->fwnode; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { const struct msm_dir_conn *dirconn = &pctrl->soc->dir_conn[i]; unsigned int parent_irq; int irq; fwspec.param[0] = 0; /* SPI */ fwspec.param[1] = dirconn->hwirq; fwspec.param[2] = IRQ_TYPE_NONE; fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain, dirconn->hwirq, dirconn->gpio); } if (dirconn->gpio != 0) { irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { struct msm_pdc_mux_output *pdc_out = &pctrl->soc->pdc_mux_out[i]; irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } else { __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain, pdc_out->hwirq, 0); } /* * Statically choose the GPIOs for mapping to PDC. Dynamic mux mapping * is very difficult. */ for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { unsigned int irq; struct irq_data *d; struct msm_gpio_mux_input *gpio_in = &pctrl->soc->gpio_mux_in[i]; if (!gpio_in->init) continue; irq = irq_find_mapping(pctrl->chip.irqdomain, gpio_in->gpio); d = irq_get_irq_data(irq); if (!d) continue; gpio_muxed_to_pdc(pdc_domain, d); } } Loading Loading @@ -1263,6 +1372,9 @@ int msm_pinctrl_probe(struct platform_device *pdev, if (IS_ERR(pctrl->regs)) return PTR_ERR(pctrl->regs); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); pctrl->pdc_regs = devm_ioremap_resource(&pdev->dev, res); msm_pinctrl_setup_pm_reset(pctrl); pctrl->irq = platform_get_irq(pdev, 0); Loading drivers/pinctrl/qcom/pinctrl-msm.h +35 −3 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -99,13 +100,35 @@ struct msm_pingroup { unsigned intr_detection_bit:5; unsigned intr_detection_width:5; unsigned dir_conn_en_bit:8; } }; /** * struct msm_gpio_mux_input - Map GPIO to Mux pin * @mux:: The mux pin to which the GPIO is connected to * @gpio: GPIO pin number * @init: Setup PDC connection at probe */ struct msm_gpio_mux_input { unsigned int mux; unsigned int gpio; bool init; }; /** * struct msm_pdc_mux_output - GPIO mux pin to PDC port * @mux: GPIO mux pin number * @hwirq: The PDC port (hwirq) that GPIO is connected to */ struct msm_pdc_mux_output { unsigned int mux; irq_hw_number_t hwirq; }; /** * struct msm_dir_conn - Direct GPIO connect configuration * @gpio: GPIO pin number * @hwirq: The GIC interrupt that the pin is connected to */; */ struct msm_dir_conn { unsigned int gpio; irq_hw_number_t hwirq; Loading @@ -122,8 +145,12 @@ struct msm_dir_conn { * @ngpio: The number of pingroups the driver should expose as GPIOs. * @dir_conn: An array describing all the pins directly connected to GIC. * @ndirconns: The number of pins directly connected to GIC * @dir_conn_offsets: Direct connect register offsets for each tile. * @dir_conn_irq_base: Direct connect interrupt base register for kpss. * @gpio_mux_in: Map of GPIO pin to the hwirq. * @n_gpioc_mux_in: The number of entries in @pdc_mux_in. * @pdc_mux_out: Map of GPIO mux to PDC port. * @n_pdc_mux_out: The number of entries in @pdc_mux_out. * @n_pdc_offset: The offset for the PDC mux pins */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; Loading @@ -136,6 +163,11 @@ struct msm_pinctrl_soc_data { const struct msm_dir_conn *dir_conn; unsigned int n_dir_conns; unsigned int dir_conn_irq_base; struct msm_pdc_mux_output *pdc_mux_out; unsigned int n_pdc_mux_out; struct msm_gpio_mux_input *gpio_mux_in; unsigned int n_gpio_mux_in; unsigned int n_pdc_mux_offset; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading Loading
drivers/pinctrl/qcom/pinctrl-msm.c +153 −41 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -70,6 +70,7 @@ struct msm_pinctrl { const struct msm_pinctrl_soc_data *soc; void __iomem *regs; void __iomem *pdc_regs; }; static int msm_get_groups_count(struct pinctrl_dev *pctldev) Loading Loading @@ -777,6 +778,102 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; static struct irq_chip msm_dirconn_irq_chip; static void msm_gpio_dirconn_handler(struct irq_desc *desc) { struct irq_data *irqd = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); generic_handle_irq(irqd->irq); chained_irq_exit(chip, desc); } static void setup_pdc_gpio(struct irq_domain *domain, unsigned int parent_irq, unsigned int gpio) { int irq; if (gpio != 0) { irq = irq_find_mapping(domain, gpio); irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); } static void request_dc_interrupt(struct irq_domain *domain, struct irq_domain *parent, irq_hw_number_t hwirq, unsigned int gpio) { struct irq_fwspec fwspec; unsigned int parent_irq; fwspec.fwnode = parent->fwnode; fwspec.param[0] = 0; /* SPI */ fwspec.param[1] = hwirq; fwspec.param[2] = IRQ_TYPE_NONE; fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); setup_pdc_gpio(domain, parent_irq, gpio); } /** * gpio_muxed_to_pdc: Mux the GPIO to a PDC IRQ * * @pdc_domain: the PDC's domain * @d: the GPIO's IRQ data * * Find a free PDC port for the GPIO and map the GPIO's mux information to the * PDC registers; so the GPIO can be used a wakeup source. */ static void gpio_muxed_to_pdc(struct irq_domain *pdc_domain, struct irq_data *d) { int i, j; unsigned int mux; struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); unsigned int gpio = d->hwirq; struct msm_pinctrl *pctrl; unsigned int irq; if (!gc || !parent_data) return; pctrl = gpiochip_get_data(gc); for (i = 0; i < pctrl->soc->n_gpio_mux_in; i++) { if (gpio != pctrl->soc->gpio_mux_in[i].gpio) continue; mux = pctrl->soc->gpio_mux_in[i].mux; for (j = 0; j < pctrl->soc->n_pdc_mux_out; j++) { struct msm_pdc_mux_output *pdc_out = &pctrl->soc->pdc_mux_out[j]; if (pdc_out->mux == mux) break; if (pdc_out->mux) continue; pdc_out->mux = gpio; irq = irq_find_mapping(pdc_domain, pdc_out->hwirq + 32); /* setup the IRQ parent for the GPIO */ setup_pdc_gpio(pctrl->chip.irqdomain, irq, gpio); /* program pdc select grp register */ writel_relaxed((mux & 0x3F), pctrl->pdc_regs + (0x14 * j)); break; } /* We have no more PDC port available */ WARN_ON(j == pctrl->soc->n_pdc_mux_out); } } static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) { struct irq_desc *desc = irq_data_to_desc(d); Loading @@ -797,6 +894,17 @@ static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) return true; } } for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { struct msm_pdc_mux_output *dir_conn = &pctrl->soc->pdc_mux_out[i]; if (dir_conn->mux == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *dir_conn_irq = dir_conn->hwirq + 32; return true; } } return false; } Loading @@ -814,9 +922,12 @@ static void msm_dirconn_irq_mask(struct irq_data *d) irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_mask) if (!dir_conn_data) return; if (dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } if (parent_data->chip->irq_mask) parent_data->chip->irq_mask(parent_data); } Loading @@ -835,7 +946,9 @@ static void msm_dirconn_irq_unmask(struct irq_data *d) irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_unmask) if (!dir_conn_data) return; if (dir_conn_data->chip->irq_unmask) dir_conn_data->chip->irq_unmask(dir_conn_data); } if (parent_data->chip->irq_unmask) Loading Loading @@ -1103,57 +1216,53 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } static void msm_gpio_dirconn_handler(struct irq_desc *desc) { struct irq_data *irqd = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); generic_handle_irq(irqd->irq); chained_irq_exit(chip, desc); } static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) { struct device_node *parent_node; struct irq_domain *parent_domain; struct irq_fwspec fwspec; struct irq_domain *pdc_domain; unsigned int i; parent_node = of_irq_find_parent(pctrl->dev->of_node); if (!parent_node) return; parent_domain = irq_find_host(parent_node); if (!parent_domain) pdc_domain = irq_find_host(parent_node); if (!pdc_domain) return; fwspec.fwnode = parent_domain->fwnode; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { const struct msm_dir_conn *dirconn = &pctrl->soc->dir_conn[i]; unsigned int parent_irq; int irq; fwspec.param[0] = 0; /* SPI */ fwspec.param[1] = dirconn->hwirq; fwspec.param[2] = IRQ_TYPE_NONE; fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain, dirconn->hwirq, dirconn->gpio); } if (dirconn->gpio != 0) { irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { struct msm_pdc_mux_output *pdc_out = &pctrl->soc->pdc_mux_out[i]; irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } else { __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain, pdc_out->hwirq, 0); } /* * Statically choose the GPIOs for mapping to PDC. Dynamic mux mapping * is very difficult. */ for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) { unsigned int irq; struct irq_data *d; struct msm_gpio_mux_input *gpio_in = &pctrl->soc->gpio_mux_in[i]; if (!gpio_in->init) continue; irq = irq_find_mapping(pctrl->chip.irqdomain, gpio_in->gpio); d = irq_get_irq_data(irq); if (!d) continue; gpio_muxed_to_pdc(pdc_domain, d); } } Loading Loading @@ -1263,6 +1372,9 @@ int msm_pinctrl_probe(struct platform_device *pdev, if (IS_ERR(pctrl->regs)) return PTR_ERR(pctrl->regs); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); pctrl->pdc_regs = devm_ioremap_resource(&pdev->dev, res); msm_pinctrl_setup_pm_reset(pctrl); pctrl->irq = platform_get_irq(pdev, 0); Loading
drivers/pinctrl/qcom/pinctrl-msm.h +35 −3 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -99,13 +100,35 @@ struct msm_pingroup { unsigned intr_detection_bit:5; unsigned intr_detection_width:5; unsigned dir_conn_en_bit:8; } }; /** * struct msm_gpio_mux_input - Map GPIO to Mux pin * @mux:: The mux pin to which the GPIO is connected to * @gpio: GPIO pin number * @init: Setup PDC connection at probe */ struct msm_gpio_mux_input { unsigned int mux; unsigned int gpio; bool init; }; /** * struct msm_pdc_mux_output - GPIO mux pin to PDC port * @mux: GPIO mux pin number * @hwirq: The PDC port (hwirq) that GPIO is connected to */ struct msm_pdc_mux_output { unsigned int mux; irq_hw_number_t hwirq; }; /** * struct msm_dir_conn - Direct GPIO connect configuration * @gpio: GPIO pin number * @hwirq: The GIC interrupt that the pin is connected to */; */ struct msm_dir_conn { unsigned int gpio; irq_hw_number_t hwirq; Loading @@ -122,8 +145,12 @@ struct msm_dir_conn { * @ngpio: The number of pingroups the driver should expose as GPIOs. * @dir_conn: An array describing all the pins directly connected to GIC. * @ndirconns: The number of pins directly connected to GIC * @dir_conn_offsets: Direct connect register offsets for each tile. * @dir_conn_irq_base: Direct connect interrupt base register for kpss. * @gpio_mux_in: Map of GPIO pin to the hwirq. * @n_gpioc_mux_in: The number of entries in @pdc_mux_in. * @pdc_mux_out: Map of GPIO mux to PDC port. * @n_pdc_mux_out: The number of entries in @pdc_mux_out. * @n_pdc_offset: The offset for the PDC mux pins */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; Loading @@ -136,6 +163,11 @@ struct msm_pinctrl_soc_data { const struct msm_dir_conn *dir_conn; unsigned int n_dir_conns; unsigned int dir_conn_irq_base; struct msm_pdc_mux_output *pdc_mux_out; unsigned int n_pdc_mux_out; struct msm_gpio_mux_input *gpio_mux_in; unsigned int n_gpio_mux_in; unsigned int n_pdc_mux_offset; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading