diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-11 09:34:56 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-11 09:34:56 +0100 |
commit | f9efbce6334844c7f8b9b9459f6d7a6fbc2928e0 (patch) | |
tree | 4490a75f7058885561ab498b68fa68e32cf5b969 /drivers | |
parent | Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff) | |
parent | Merge tag 'dt-3.13-5' of git://git.infradead.org/linux-mvebu into next/dt (diff) | |
download | linux-f9efbce6334844c7f8b9b9459f6d7a6fbc2928e0.tar.xz linux-f9efbce6334844c7f8b9b9459f6d7a6fbc2928e0.zip |
Merge tag 'dt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC DT updates from Olof Johansson:
"Most of this branch consists of updates, additions and general churn
of the device tree source files in the kernel (arch/arm/boot/dts).
Besides that, there are a few things to point out:
- Lots of platform conversion on OMAP2+, with removal of old board
files for various platforms.
- Final conversion of a bunch of ux500 (ST-Ericsson) platforms as
well
- Some updates to pinctrl and other subsystems. Most of these are
for DT-enablement of the various platforms and acks have been
collected"
* tag 'dt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (385 commits)
ARM: dts: bcm11351: Use GIC/IRQ defines for sdio interrupts
ARM: dts: bcm: Add missing UARTs for bcm11351 (bcm281xx)
ARM: dts: bcm281xx: Add card detect GPIO
ARM: dts: rename ARCH_BCM to ARCH_BCM_MOBILE (dt)
ARM: bcm281xx: Add device node for the GPIO controller
ARM: mvebu: Add Netgear ReadyNAS 104 board
ARM: tegra: fix Tegra114 IOMMU register address
ARM: kirkwood: add support for OpenBlocks A7 platform
ARM: dts: omap4-panda: add DPI pinmuxing
ARM: dts: AM33xx: Add RNG node
ARM: dts: AM33XX: Add hwspinlock node
ARM: dts: OMAP5: Add hwspinlock node
ARM: dts: OMAP4: Add hwspinlock node
ARM: dts: use 'status' property for PCIe nodes
ARM: dts: sirf: add missed address-cells and size-cells for prima2 I2C
ARM: dts: sirf: add missed cell, cs and dma channel for SPI nodes
ARM: dts: sirf: add missed graphics2d iobg in atlas6 dts
ARM: dts: sirf: add missed chhifbg node in prima2 and atlas6 dts
ARM: dts: sirf: add missed memcontrol-monitor node in prima2 and atlas6 dts
ARM: mvebu: Add the core-divider clock to Armada 370/XP
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/ux500/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/ux500/u8500_of_clk.c | 559 | ||||
-rw-r--r-- | drivers/clk/ux500/u8540_clk.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 34 | ||||
-rw-r--r-- | drivers/irqchip/irq-vic.c | 7 | ||||
-rw-r--r-- | drivers/mfd/db8500-prcmu.c | 1 | ||||
-rw-r--r-- | drivers/mfd/dbx500-prcmu-regs.h | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-single.c | 388 | ||||
-rw-r--r-- | drivers/usb/musb/ux500.c | 2 |
9 files changed, 954 insertions, 41 deletions
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index c6a806ed0e8c..521483f0ba33 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile @@ -8,6 +8,7 @@ obj-y += clk-prcmu.o obj-y += clk-sysctrl.o # Clock definitions +obj-y += u8500_of_clk.o obj-y += u8500_clk.o obj-y += u9540_clk.o obj-y += u8540_clk.o diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c new file mode 100644 index 000000000000..cdeff299de26 --- /dev/null +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -0,0 +1,559 @@ +/* + * Clock definitions for u8500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/clk-ux500.h> +#include "clk.h" + +#define PRCC_NUM_PERIPH_CLUSTERS 6 +#define PRCC_PERIPHS_PER_CLUSTER 32 + +static struct clk *prcmu_clk[PRCMU_NUM_CLKS]; +static struct clk *prcc_pclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER]; +static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER]; + +#define PRCC_SHOW(clk, base, bit) \ + clk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] +#define PRCC_PCLK_STORE(clk, base, bit) \ + prcc_pclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk +#define PRCC_KCLK_STORE(clk, base, bit) \ + prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk + +struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk **clk_data = data; + unsigned int base, bit; + + if (clkspec->args_count != 2) + return ERR_PTR(-EINVAL); + + base = clkspec->args[0]; + bit = clkspec->args[1]; + + if (base != 1 && base != 2 && base != 3 && base != 5 && base != 6) { + pr_err("%s: invalid PRCC base %d\n", __func__, base); + return ERR_PTR(-EINVAL); + } + + return PRCC_SHOW(clk_data, base, bit); +} + +static const struct of_device_id u8500_clk_of_match[] = { + { .compatible = "stericsson,u8500-clks", }, + { }, +}; + +void u8500_of_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) +{ + struct prcmu_fw_version *fw_version; + struct device_node *np = NULL; + struct device_node *child = NULL; + const char *sgaclk_parent = NULL; + struct clk *clk, *rtc_clk, *twd_clk; + + if (of_have_populated_dt()) + np = of_find_matching_node(NULL, u8500_clk_of_match); + if (!np) { + pr_err("Either DT or U8500 Clock node not found\n"); + return; + } + + /* Clock sources */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLSOC0] = clk; + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLSOC1] = clk; + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLDDR] = clk; + + /* FIXME: Add sys, ulp and int clocks here. */ + + rtc_clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL", + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + + /* PRCMU clocks */ + fw_version = prcmu_get_fw_version(); + if (fw_version != NULL) { + switch (fw_version->project) { + case PRCMU_FW_PROJECT_U8500_C2: + case PRCMU_FW_PROJECT_U8520: + case PRCMU_FW_PROJECT_U8420: + sgaclk_parent = "soc0_pll"; + break; + default: + break; + } + } + + if (sgaclk_parent) + clk = clk_reg_prcmu_gate("sgclk", sgaclk_parent, + PRCMU_SGACLK, 0); + else + clk = clk_reg_prcmu_gate("sgclk", NULL, + PRCMU_SGACLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SGACLK] = clk; + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_UARTCLK] = clk; + + clk = clk_reg_prcmu_gate("msp02clk", NULL, PRCMU_MSP02CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MSP02CLK] = clk; + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MSP1CLK] = clk; + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_I2CCLK] = clk; + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SLIMCLK] = clk; + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER1CLK] = clk; + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER2CLK] = clk; + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER3CLK] = clk; + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER5CLK] = clk; + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER6CLK] = clk; + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER7CLK] = clk; + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_LCDCLK] = clk; + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_BMLCLK] = clk; + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HSITXCLK] = clk; + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HSIRXCLK] = clk; + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HDMICLK] = clk; + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_APEATCLK] = clk; + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_APETRACECLK] = clk; + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MCDECLK] = clk; + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_IPI2CCLK] = clk; + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_DSIALTCLK] = clk; + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_DMACLK] = clk; + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_B2R2CLK] = clk; + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_TVCLK] = clk; + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SSPCLK] = clk; + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_RNGCLK] = clk; + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_UICCCLK] = clk; + + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_TIMCLK] = clk; + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK, + 100000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_SDMMCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_PLLDSI] = clk; + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI0CLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI1CLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI0ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI1ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI2ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable_rate("armss", NULL, + PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_ARMSS] = clk; + + twd_clk = clk_register_fixed_factor(NULL, "smp_twd", "armss", + CLK_IGNORE_UNUSED, 1, 2); + + /* + * FIXME: Add special handled PRCMU clocks here: + * 1. clkout0yuv, use PRCMU as parent + need regulator + pinctrl. + * 2. ab9540_clkout1yuv, see clkout0yuv + */ + + /* PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 1, 0); + + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 1, 1); + + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 1, 2); + + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 1, 3); + + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 1, 4); + + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 1, 5); + + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 1, 6); + + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 1, 7); + + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 1, 8); + + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base, + BIT(9), 0); + PRCC_PCLK_STORE(clk, 1, 9); + + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base, + BIT(10), 0); + PRCC_PCLK_STORE(clk, 1, 10); + + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base, + BIT(11), 0); + PRCC_PCLK_STORE(clk, 1, 11); + + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 2, 0); + + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 2, 1); + + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 2, 2); + + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 2, 3); + + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 2, 4); + + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 2, 5); + + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 2, 6); + + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 2, 7); + + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 2, 8); + + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base, + BIT(9), 0); + PRCC_PCLK_STORE(clk, 2, 9); + + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base, + BIT(10), 0); + PRCC_PCLK_STORE(clk, 2, 10); + + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base, + BIT(11), 0); + PRCC_PCLK_STORE(clk, 2, 11); + + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base, + BIT(12), 0); + PRCC_PCLK_STORE(clk, 2, 12); + + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 3, 0); + + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 3, 1); + + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 3, 2); + + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 3, 3); + + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 3, 4); + + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 3, 5); + + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 3, 6); + + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 3, 7); + + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 3, 8); + + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 5, 0); + + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 5, 1); + + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 6, 0); + + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 6, 1); + + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 6, 2); + + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 6, 3); + + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 6, 4); + + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 6, 5); + + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 6, 6); + + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 6, 7); + + /* PRCC K-clocks + * + * FIXME: Some drivers requires PERPIH[n| to be automatically enabled + * by enabling just the K-clock, even if it is not a valid parent to + * the K-clock. Until drivers get fixed we might need some kind of + * "parent muxed join". + */ + + /* Periph1 */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 0); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 1); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + clkrst1_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 2); + + clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 3); + + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 4); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk", + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 5); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + clkrst1_base, BIT(6), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 6); + + clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 8); + + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + clkrst1_base, BIT(9), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 9); + + clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 10); + + /* Periph2 */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 0); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk", + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 2); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 3); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk", + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 4); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 5); + + /* Note that rate is received from parent. */ + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + clkrst2_base, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + PRCC_KCLK_STORE(clk, 2, 6); + + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + clkrst2_base, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + PRCC_KCLK_STORE(clk, 2, 7); + + /* Periph3 */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 1); + + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 2); + + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 3); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk", + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 4); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + clkrst3_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 5); + + clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 6); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 7); + + /* Periph6 */ + clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk", + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 6, 0); + + for_each_child_of_node(np, child) { + static struct clk_onecell_data clk_data; + + if (!of_node_cmp(child->name, "prcmu-clock")) { + clk_data.clks = prcmu_clk; + clk_data.clk_num = ARRAY_SIZE(prcmu_clk); + of_clk_add_provider(child, of_clk_src_onecell_get, &clk_data); + } + if (!of_node_cmp(child->name, "prcc-periph-clock")) + of_clk_add_provider(child, ux500_twocell_get, prcc_pclk); + + if (!of_node_cmp(child->name, "prcc-kernel-clock")) + of_clk_add_provider(child, ux500_twocell_get, prcc_kclk); + + if (!of_node_cmp(child->name, "rtc32k-clock")) + of_clk_add_provider(child, of_clk_src_simple_get, rtc_clk); + + if (!of_node_cmp(child->name, "smp-twd-clock")) + of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); + } +} diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index f26258869deb..20c8add90d11 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -83,7 +83,7 @@ void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk_register_clkdev(clk, NULL, "lcd"); clk_register_clkdev(clk, "lcd", "mcde"); - clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BML8580CLK, + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT); clk_register_clkdev(clk, NULL, "bml"); diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index f6f1c7d244c4..76e02b9460e6 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2082,34 +2082,14 @@ static __init int samsung_gpiolib_init(void) int i, nr_chips; int group = 0; -#if defined(CONFIG_PINCTRL_EXYNOS) || defined(CONFIG_PINCTRL_EXYNOS5440) /* - * This gpio driver includes support for device tree support and there - * are platforms using it. In order to maintain compatibility with those - * platforms, and to allow non-dt Exynos4210 platforms to use this - * gpiolib support, a check is added to find out if there is a active - * pin-controller driver support available. If it is available, this - * gpiolib support is ignored and the gpiolib support available in - * pin-controller driver is used. This is a temporary check and will go - * away when all of the Exynos4210 platforms have switched to using - * device tree and the pin-ctrl driver. - */ - struct device_node *pctrl_np; - static const struct of_device_id exynos_pinctrl_ids[] = { - { .compatible = "samsung,s3c2412-pinctrl", }, - { .compatible = "samsung,s3c2416-pinctrl", }, - { .compatible = "samsung,s3c2440-pinctrl", }, - { .compatible = "samsung,s3c2450-pinctrl", }, - { .compatible = "samsung,exynos4210-pinctrl", }, - { .compatible = "samsung,exynos4x12-pinctrl", }, - { .compatible = "samsung,exynos5250-pinctrl", }, - { .compatible = "samsung,exynos5440-pinctrl", }, - { } - }; - for_each_matching_node(pctrl_np, exynos_pinctrl_ids) - if (pctrl_np && of_device_is_available(pctrl_np)) - return -ENODEV; -#endif + * Currently there are two drivers that can provide GPIO support for + * Samsung SoCs. For device tree enabled platforms, the new + * pinctrl-samsung driver is used, providing both GPIO and pin control + * interfaces. For legacy (non-DT) platforms this driver is used. + */ + if (of_have_populated_dt()) + return -ENODEV; samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 2bbb00404cf5..8e21ae0bab46 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -469,6 +469,8 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, int __init vic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *regs; + u32 interrupt_mask = ~0; + u32 wakeup_mask = ~0; if (WARN(parent, "non-root VICs are not supported")) return -EINVAL; @@ -477,10 +479,13 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent) if (WARN_ON(!regs)) return -EIO; + of_property_read_u32(node, "valid-mask", &interrupt_mask); + of_property_read_u32(node, "valid-wakeup-mask", &wakeup_mask); + /* * Passing 0 as first IRQ makes the simple domain allocate descriptors */ - __vic_init(regs, 0, ~0, ~0, node); + __vic_init(regs, 0, interrupt_mask, wakeup_mask, node); return 0; } diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 53f371dcbb6e..b9ce60c301de 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -480,7 +480,6 @@ static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), - CLK_MGT_ENTRY(BML8580CLK, PLL_DIV, true), CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 4f6f0fa5d3b7..7cc32a8ff01c 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -32,7 +32,6 @@ #define PRCM_PER7CLK_MGT (0x040) #define PRCM_LCDCLK_MGT (0x044) #define PRCM_BMLCLK_MGT (0x04C) -#define PRCM_BML8580CLK_MGT (0x108) #define PRCM_HSITXCLK_MGT (0x050) #define PRCM_HSIRXCLK_MGT (0x054) #define PRCM_HDMICLK_MGT (0x058) diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index a82ace4d9a20..0846922b2316 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -15,15 +15,21 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/list.h> +#include <linux/interrupt.h> + +#include <linux/irqchip/chained_irq.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf-generic.h> +#include <linux/platform_data/pinctrl-single.h> + #include "core.h" #include "pinconf.h" @@ -150,19 +156,36 @@ struct pcs_name { }; /** + * struct pcs_soc_data - SoC specific settings + * @flags: initial SoC specific PCS_FEAT_xxx values + * @irq: optional interrupt for the controller + * @irq_enable_mask: optional SoC specific interrupt enable mask + * @irq_status_mask: optional SoC specific interrupt status mask + * @rearm: optional SoC specific wake-up rearm function + */ +struct pcs_soc_data { + unsigned flags; + int irq; + unsigned irq_enable_mask; + unsigned irq_status_mask; + void (*rearm)(void); +}; + +/** * struct pcs_device - pinctrl device instance * @res: resources * @base: virtual address of the controller * @size: size of the ioremapped area * @dev: device entry * @pctl: pin controller device + * @flags: mask of PCS_FEAT_xxx values + * @lock: spinlock for register access * @mutex: mutex protecting the lists * @width: bits per mux register * @fmask: function register mask * @fshift: function register shift * @foff: value to turn mux off * @fmax: max number of functions in fmask - * @is_pinconf: whether supports pinconf * @bits_per_pin:number of bits per pin * @names: array of register names for pins * @pins: physical pins on the SoC @@ -171,6 +194,9 @@ struct pcs_name { * @pingroups: list of pingroups * @functions: list of functions * @gpiofuncs: list of gpio functions + * @irqs: list of interrupt registers + * @chip: chip container for this instance + * @domain: IRQ domain for this instance * @ngroups: number of pingroups * @nfuncs: number of functions * @desc: pin controller descriptor @@ -183,6 +209,12 @@ struct pcs_device { unsigned size; struct device *dev; struct pinctrl_dev *pctl; + unsigned flags; +#define PCS_QUIRK_SHARED_IRQ (1 << 2) +#define PCS_FEAT_IRQ (1 << 1) +#define PCS_FEAT_PINCONF (1 << 0) + struct pcs_soc_data socdata; + raw_spinlock_t lock; struct mutex mutex; unsigned width; unsigned fmask; @@ -190,7 +222,6 @@ struct pcs_device { unsigned foff; unsigned fmax; bool bits_per_mux; - bool is_pinconf; unsigned bits_per_pin; struct pcs_name *names; struct pcs_data pins; @@ -199,6 +230,9 @@ struct pcs_device { struct list_head pingroups; struct list_head functions; struct list_head gpiofuncs; + struct list_head irqs; + struct irq_chip chip; + struct irq_domain *domain; unsigned ngroups; unsigned nfuncs; struct pinctrl_desc desc; @@ -206,6 +240,10 @@ struct pcs_device { void (*write)(unsigned val, void __iomem *reg); }; +#define PCS_QUIRK_HAS_SHARED_IRQ (pcs->flags & PCS_QUIRK_SHARED_IRQ) +#define PCS_HAS_IRQ (pcs->flags & PCS_FEAT_IRQ) +#define PCS_HAS_PINCONF (pcs->flags & PCS_FEAT_PINCONF) + static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, @@ -429,9 +467,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, for (i = 0; i < func->nvals; i++) { struct pcs_func_vals *vals; + unsigned long flags; unsigned val, mask; vals = &func->vals[i]; + raw_spin_lock_irqsave(&pcs->lock, flags); val = pcs->read(vals->reg); if (pcs->bits_per_mux) @@ -442,6 +482,7 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, val &= ~mask; val |= (vals->val & mask); pcs->write(val, vals->reg); + raw_spin_unlock_irqrestore(&pcs->lock, flags); } return 0; @@ -483,13 +524,16 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, for (i = 0; i < func->nvals; i++) { struct pcs_func_vals *vals; + unsigned long flags; unsigned val; vals = &func->vals[i]; + raw_spin_lock_irqsave(&pcs->lock, flags); val = pcs->read(vals->reg); val &= ~pcs->fmask; val |= pcs->foff << pcs->fshift; pcs->write(val, vals->reg); + raw_spin_unlock_irqrestore(&pcs->lock, flags); } } @@ -1060,7 +1104,7 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, }; /* If pinconf isn't supported, don't parse properties in below. */ - if (!pcs->is_pinconf) + if (!PCS_HAS_PINCONF) return 0; /* cacluate how much properties are supported in current node */ @@ -1184,7 +1228,7 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, (*map)->data.mux.group = np->name; (*map)->data.mux.function = np->name; - if (pcs->is_pinconf) { + if (PCS_HAS_PINCONF) { res = pcs_parse_pinconf(pcs, np, function, map); if (res) goto free_pingroups; @@ -1305,7 +1349,7 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, (*map)->data.mux.group = np->name; (*map)->data.mux.function = np->name; - if (pcs->is_pinconf) { + if (PCS_HAS_PINCONF) { dev_err(pcs->dev, "pinconf not supported\n"); goto free_pingroups; } @@ -1440,11 +1484,33 @@ static void pcs_free_pingroups(struct pcs_device *pcs) } /** + * pcs_irq_free() - free interrupt + * @pcs: pcs driver instance + */ +static void pcs_irq_free(struct pcs_device *pcs) +{ + struct pcs_soc_data *pcs_soc = &pcs->socdata; + + if (pcs_soc->irq < 0) + return; + + if (pcs->domain) + irq_domain_remove(pcs->domain); + + if (PCS_QUIRK_HAS_SHARED_IRQ) + free_irq(pcs_soc->irq, pcs_soc); + else + irq_set_chained_handler(pcs_soc->irq, NULL); +} + +/** * pcs_free_resources() - free memory used by this driver * @pcs: pcs driver instance */ static void pcs_free_resources(struct pcs_device *pcs) { + pcs_irq_free(pcs); + if (pcs->pctl) pinctrl_unregister(pcs->pctl); @@ -1493,6 +1559,268 @@ static int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs) } return ret; } +/** + * @reg: virtual address of interrupt register + * @hwirq: hardware irq number + * @irq: virtual irq number + * @node: list node + */ +struct pcs_interrupt { + void __iomem *reg; + irq_hw_number_t hwirq; + unsigned int irq; + struct list_head node; +}; + +/** + * pcs_irq_set() - enables or disables an interrupt + * + * Note that this currently assumes one interrupt per pinctrl + * register that is typically used for wake-up events. + */ +static inline void pcs_irq_set(struct pcs_soc_data *pcs_soc, + int irq, const bool enable) +{ + struct pcs_device *pcs; + struct list_head *pos; + unsigned mask; + + pcs = container_of(pcs_soc, struct pcs_device, socdata); + list_for_each(pos, &pcs->irqs) { + struct pcs_interrupt *pcswi; + unsigned soc_mask; + + pcswi = list_entry(pos, struct pcs_interrupt, node); + if (irq != pcswi->irq) + continue; + + soc_mask = pcs_soc->irq_enable_mask; + raw_spin_lock(&pcs->lock); + mask = pcs->read(pcswi->reg); + if (enable) + mask |= soc_mask; + else + mask &= ~soc_mask; + pcs->write(mask, pcswi->reg); + raw_spin_unlock(&pcs->lock); + } +} + +/** + * pcs_irq_mask() - mask pinctrl interrupt + * @d: interrupt data + */ +static void pcs_irq_mask(struct irq_data *d) +{ + struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d); + + pcs_irq_set(pcs_soc, d->irq, false); +} + +/** + * pcs_irq_unmask() - unmask pinctrl interrupt + * @d: interrupt data + */ +static void pcs_irq_unmask(struct irq_data *d) +{ + struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d); + + pcs_irq_set(pcs_soc, d->irq, true); + if (pcs_soc->rearm) + pcs_soc->rearm(); +} + +/** + * pcs_irq_set_wake() - toggle the suspend and resume wake up + * @d: interrupt data + * @state: wake-up state + * + * Note that this should be called only for suspend and resume. + * For runtime PM, the wake-up events should be enabled by default. + */ +static int pcs_irq_set_wake(struct irq_data *d, unsigned int state) +{ + if (state) + pcs_irq_unmask(d); + else + pcs_irq_mask(d); + + return 0; +} + +/** + * pcs_irq_handle() - common interrupt handler + * @pcs_irq: interrupt data + * + * Note that this currently assumes we have one interrupt bit per + * mux register. This interrupt is typically used for wake-up events. + * For more complex interrupts different handlers can be specified. + */ +static int pcs_irq_handle(struct pcs_soc_data *pcs_soc) +{ + struct pcs_device *pcs; + struct list_head *pos; + int count = 0; + + pcs = container_of(pcs_soc, struct pcs_device, socdata); + list_for_each(pos, &pcs->irqs) { + struct pcs_interrupt *pcswi; + unsigned mask; + + pcswi = list_entry(pos, struct pcs_interrupt, node); + raw_spin_lock(&pcs->lock); + mask = pcs->read(pcswi->reg); + raw_spin_unlock(&pcs->lock); + if (mask & pcs_soc->irq_status_mask) { + generic_handle_irq(irq_find_mapping(pcs->domain, + pcswi->hwirq)); + count++; + } + } + + /* + * For debugging on omaps, you may want to call pcs_soc->rearm() + * here to see wake-up interrupts during runtime also. + */ + + return count; +} + +/** + * pcs_irq_handler() - handler for the shared interrupt case + * @irq: interrupt + * @d: data + * + * Use this for cases where multiple instances of + * pinctrl-single share a single interrupt like on omaps. + */ +static irqreturn_t pcs_irq_handler(int irq, void *d) +{ + struct pcs_soc_data *pcs_soc = d; + + return pcs_irq_handle(pcs_soc) ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * pcs_irq_handle() - handler for the dedicated chained interrupt case + * @irq: interrupt + * @desc: interrupt descriptor + * + * Use this if you have a separate interrupt for each + * pinctrl-single instance. + */ +static void pcs_irq_chain_handler(unsigned int irq, struct irq_desc *desc) +{ + struct pcs_soc_data *pcs_soc = irq_desc_get_handler_data(desc); + struct irq_chip *chip; + int res; + + chip = irq_get_chip(irq); + chained_irq_enter(chip, desc); + res = pcs_irq_handle(pcs_soc); + /* REVISIT: export and add handle_bad_irq(irq, desc)? */ + chained_irq_exit(chip, desc); + + return; +} + +static int pcs_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct pcs_soc_data *pcs_soc = d->host_data; + struct pcs_device *pcs; + struct pcs_interrupt *pcswi; + + pcs = container_of(pcs_soc, struct pcs_device, socdata); + pcswi = devm_kzalloc(pcs->dev, sizeof(*pcswi), GFP_KERNEL); + if (!pcswi) + return -ENOMEM; + + pcswi->reg = pcs->base + hwirq; + pcswi->hwirq = hwirq; + pcswi->irq = irq; + + mutex_lock(&pcs->mutex); + list_add_tail(&pcswi->node, &pcs->irqs); + mutex_unlock(&pcs->mutex); + + irq_set_chip_data(irq, pcs_soc); + irq_set_chip_and_handler(irq, &pcs->chip, + handle_level_irq); + +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + + return 0; +} + +static struct irq_domain_ops pcs_irqdomain_ops = { + .map = pcs_irqdomain_map, + .xlate = irq_domain_xlate_onecell, +}; + +/** + * pcs_irq_init_chained_handler() - set up a chained interrupt handler + * @pcs: pcs driver instance + * @np: device node pointer + */ +static int pcs_irq_init_chained_handler(struct pcs_device *pcs, + struct device_node *np) +{ + struct pcs_soc_data *pcs_soc = &pcs->socdata; + const char *name = "pinctrl"; + int num_irqs; + + if (!pcs_soc->irq_enable_mask || + !pcs_soc->irq_status_mask) { + pcs_soc->irq = -1; + return -EINVAL; + } + + INIT_LIST_HEAD(&pcs->irqs); + pcs->chip.name = name; + pcs->chip.irq_ack = pcs_irq_mask; + pcs->chip.irq_mask = pcs_irq_mask; + pcs->chip.irq_unmask = pcs_irq_unmask; + pcs->chip.irq_set_wake = pcs_irq_set_wake; + + if (PCS_QUIRK_HAS_SHARED_IRQ) { + int res; + + res = request_irq(pcs_soc->irq, pcs_irq_handler, + IRQF_SHARED | IRQF_NO_SUSPEND, + name, pcs_soc); + if (res) { + pcs_soc->irq = -1; + return res; + } + } else { + irq_set_handler_data(pcs_soc->irq, pcs_soc); + irq_set_chained_handler(pcs_soc->irq, + pcs_irq_chain_handler); + } + + /* + * We can use the register offset as the hardirq + * number as irq_domain_add_simple maps them lazily. + * This way we can easily support more than one + * interrupt per function if needed. + */ + num_irqs = pcs->size; + + pcs->domain = irq_domain_add_simple(np, num_irqs, 0, + &pcs_irqdomain_ops, + pcs_soc); + if (!pcs->domain) { + irq_set_chained_handler(pcs_soc->irq, NULL); + return -EINVAL; + } + + return 0; +} #ifdef CONFIG_PM static int pinctrl_single_suspend(struct platform_device *pdev, @@ -1523,8 +1851,10 @@ static int pcs_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; + struct pcs_pdata *pdata; struct resource *res; struct pcs_device *pcs; + const struct pcs_soc_data *soc; int ret; match = of_match_device(pcs_of_match, &pdev->dev); @@ -1537,11 +1867,14 @@ static int pcs_probe(struct platform_device *pdev) return -ENOMEM; } pcs->dev = &pdev->dev; + raw_spin_lock_init(&pcs->lock); mutex_init(&pcs->mutex); INIT_LIST_HEAD(&pcs->pingroups); INIT_LIST_HEAD(&pcs->functions); INIT_LIST_HEAD(&pcs->gpiofuncs); - pcs->is_pinconf = match->data; + soc = match->data; + pcs->flags = soc->flags; + memcpy(&pcs->socdata, soc, sizeof(*soc)); PCS_GET_PROP_U32("pinctrl-single,register-width", &pcs->width, "register width not specified\n"); @@ -1610,7 +1943,7 @@ static int pcs_probe(struct platform_device *pdev) pcs->desc.name = DRIVER_NAME; pcs->desc.pctlops = &pcs_pinctrl_ops; pcs->desc.pmxops = &pcs_pinmux_ops; - if (pcs->is_pinconf) + if (PCS_HAS_PINCONF) pcs->desc.confops = &pcs_pinconf_ops; pcs->desc.owner = THIS_MODULE; @@ -1629,6 +1962,27 @@ static int pcs_probe(struct platform_device *pdev) if (ret < 0) goto free; + pcs->socdata.irq = irq_of_parse_and_map(np, 0); + if (pcs->socdata.irq) + pcs->flags |= PCS_FEAT_IRQ; + + /* We still need auxdata for some omaps for PRM interrupts */ + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + if (pdata->rearm) + pcs->socdata.rearm = pdata->rearm; + if (pdata->irq) { + pcs->socdata.irq = pdata->irq; + pcs->flags |= PCS_FEAT_IRQ; + } + } + + if (PCS_HAS_IRQ) { + ret = pcs_irq_init_chained_handler(pcs, np); + if (ret < 0) + dev_warn(pcs->dev, "initialized with no interrupts\n"); + } + dev_info(pcs->dev, "%i pins at pa %p size %u\n", pcs->desc.npins, pcs->base, pcs->size); @@ -1652,9 +2006,25 @@ static int pcs_remove(struct platform_device *pdev) return 0; } +static const struct pcs_soc_data pinctrl_single_omap_wkup = { + .flags = PCS_QUIRK_SHARED_IRQ, + .irq_enable_mask = (1 << 14), /* OMAP_WAKEUP_EN */ + .irq_status_mask = (1 << 15), /* OMAP_WAKEUP_EVENT */ +}; + +static const struct pcs_soc_data pinctrl_single = { +}; + +static const struct pcs_soc_data pinconf_single = { + .flags = PCS_FEAT_PINCONF, +}; + static struct of_device_id pcs_of_match[] = { - { .compatible = "pinctrl-single", .data = (void *)false }, - { .compatible = "pinconf-single", .data = (void *)true }, + { .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup }, + { .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup }, + { .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup }, + { .compatible = "pinctrl-single", .data = &pinctrl_single }, + { .compatible = "pinconf-single", .data = &pinconf_single }, { }, }; MODULE_DEVICE_TABLE(of, pcs_of_match); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index f483d1924c28..122446bf1664 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -259,7 +259,7 @@ static int ux500_probe(struct platform_device *pdev) goto err1; } - clk = clk_get(&pdev->dev, "usb"); + clk = clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); |