From 7c367d3da697846b80058859937f606c0081beda Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:07 +0200 Subject: pinctrl: samsung: Detect and handle unsupported configuration types This patch modifies the pinctrl-samsung driver to detect when width of a bit field is set to zero (which means that such configuraton type is not supported) and return an error instead of trying to modify an inexistent register. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index dd108a94acf9..c660fa5071d7 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -391,6 +391,9 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, return -EINVAL; } + if (!width) + return -EINVAL; + mask = (1 << width) - 1; shift = pin_offset * width; data = readl(reg_base + cfg_reg); -- cgit v1.2.3 From 62f14c0ef5d1bbd640b42a59f8f084f764a067c4 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:08 +0200 Subject: pinctrl: samsung: Do not pass gpio_chip to pin_to_reg_bank The pointer to gpio_chip passed to pin_to_reg_bank utility function is used only to retrieve a pointer to samsung_pinctrl_drv_data structure. This patch modifies the function and its users to pass a pointer to samsung_pinctrl_drv_data directly. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index c660fa5071d7..aa42d54e89c6 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -250,14 +250,12 @@ static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev, * given a pin number that is local to a pin controller, find out the pin bank * and the register base of the pin bank. */ -static void pin_to_reg_bank(struct gpio_chip *gc, unsigned pin, - void __iomem **reg, u32 *offset, +static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata, + unsigned pin, void __iomem **reg, u32 *offset, struct samsung_pin_bank **bank) { - struct samsung_pinctrl_drv_data *drvdata; struct samsung_pin_bank *b; - drvdata = dev_get_drvdata(gc->dev); b = drvdata->ctrl->pin_banks; while ((pin >= b->pin_base) && @@ -292,7 +290,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, * pin function number in the config register. */ for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) { - pin_to_reg_bank(drvdata->gc, pins[cnt] - drvdata->ctrl->base, + pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base, ®, &pin_offset, &bank); mask = (1 << bank->func_width) - 1; shift = pin_offset * bank->func_width; @@ -329,10 +327,13 @@ static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input) { struct samsung_pin_bank *bank; + struct samsung_pinctrl_drv_data *drvdata; void __iomem *reg; u32 data, pin_offset, mask, shift; - pin_to_reg_bank(range->gc, offset, ®, &pin_offset, &bank); + drvdata = pinctrl_dev_get_drvdata(pctldev); + + pin_to_reg_bank(drvdata, offset, ®, &pin_offset, &bank); mask = (1 << bank->func_width) - 1; shift = pin_offset * bank->func_width; @@ -366,7 +367,7 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, u32 cfg_value, cfg_reg; drvdata = pinctrl_dev_get_drvdata(pctldev); - pin_to_reg_bank(drvdata->gc, pin - drvdata->ctrl->base, ®_base, + pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, ®_base, &pin_offset, &bank); switch (cfg_type) { @@ -468,8 +469,11 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { void __iomem *reg; u32 pin_offset, data; + struct samsung_pinctrl_drv_data *drvdata; - pin_to_reg_bank(gc, offset, ®, &pin_offset, NULL); + drvdata = dev_get_drvdata(gc->dev); + + pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL); data = readl(reg + DAT_REG); data &= ~(1 << pin_offset); if (value) @@ -482,8 +486,11 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) { void __iomem *reg; u32 pin_offset, data; + struct samsung_pinctrl_drv_data *drvdata; + + drvdata = dev_get_drvdata(gc->dev); - pin_to_reg_bank(gc, offset, ®, &pin_offset, NULL); + pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL); data = readl(reg + DAT_REG); data >>= pin_offset; data &= 1; -- cgit v1.2.3 From 40ba6227aeb3712b0cea0c4f9c3e355cf801f4c4 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:09 +0200 Subject: pinctrl: samsung: Assing pin numbers dynamically This patch modifies the pinctrl-samsung driver to assign numbers to pins dynamically instead of static enumerations. Thanks to this change the amount of code requried to support a SoC can be greatly reduced and the code made more readable. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-exynos.c | 83 +++++++++++++++++---------------------- drivers/pinctrl/pinctrl-exynos.h | 11 ++---- drivers/pinctrl/pinctrl-samsung.c | 22 ++++++++++- 3 files changed, 62 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 21362f48d370..0ea2164bf6d9 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -484,51 +484,51 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) /* pin banks of exynos4210 pin-controller 0 */ static struct samsung_pin_bank exynos4210_pin_banks0[] = { - EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_A0, "gpa0"), - EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_A1, "gpa1"), - EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_B, "gpb"), - EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_C0, "gpc0"), - EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_C1, "gpc1"), - EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_D0, "gpd0"), - EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_D1, "gpd1"), - EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_E0, "gpe0"), - EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_E1, "gpe1"), - EXYNOS_PIN_BANK_EINTG(0x120, EXYNOS4210_GPIO_E2, "gpe2"), - EXYNOS_PIN_BANK_EINTG(0x140, EXYNOS4210_GPIO_E3, "gpe3"), - EXYNOS_PIN_BANK_EINTG(0x160, EXYNOS4210_GPIO_E4, "gpe4"), - EXYNOS_PIN_BANK_EINTG(0x180, EXYNOS4210_GPIO_F0, "gpf0"), - EXYNOS_PIN_BANK_EINTG(0x1A0, EXYNOS4210_GPIO_F1, "gpf1"), - EXYNOS_PIN_BANK_EINTG(0x1C0, EXYNOS4210_GPIO_F2, "gpf2"), - EXYNOS_PIN_BANK_EINTG(0x1E0, EXYNOS4210_GPIO_F3, "gpf3"), + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0"), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1"), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb"), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0"), + EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1"), + EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpd0"), + EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpd1"), + EXYNOS_PIN_BANK_EINTG(5, 0x0E0, "gpe0"), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpe1"), + EXYNOS_PIN_BANK_EINTG(6, 0x120, "gpe2"), + EXYNOS_PIN_BANK_EINTG(8, 0x140, "gpe3"), + EXYNOS_PIN_BANK_EINTG(8, 0x160, "gpe4"), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf0"), + EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpf1"), + EXYNOS_PIN_BANK_EINTG(8, 0x1C0, "gpf2"), + EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf3"), }; /* pin banks of exynos4210 pin-controller 1 */ static struct samsung_pin_bank exynos4210_pin_banks1[] = { - EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_J0, "gpj0"), - EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_J1, "gpj1"), - EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_K0, "gpk0"), - EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_K1, "gpk1"), - EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_K2, "gpk2"), - EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_K3, "gpk3"), - EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_L0, "gpl0"), - EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_L1, "gpl1"), - EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_L2, "gpl2"), - EXYNOS_PIN_BANK_EINTN(0x120, EXYNOS4210_GPIO_Y0, "gpy0"), - EXYNOS_PIN_BANK_EINTN(0x140, EXYNOS4210_GPIO_Y1, "gpy1"), - EXYNOS_PIN_BANK_EINTN(0x160, EXYNOS4210_GPIO_Y2, "gpy2"), - EXYNOS_PIN_BANK_EINTN(0x180, EXYNOS4210_GPIO_Y3, "gpy3"), - EXYNOS_PIN_BANK_EINTN(0x1A0, EXYNOS4210_GPIO_Y4, "gpy4"), - EXYNOS_PIN_BANK_EINTN(0x1C0, EXYNOS4210_GPIO_Y5, "gpy5"), - EXYNOS_PIN_BANK_EINTN(0x1E0, EXYNOS4210_GPIO_Y6, "gpy6"), - EXYNOS_PIN_BANK_EINTN(0xC00, EXYNOS4210_GPIO_X0, "gpx0"), - EXYNOS_PIN_BANK_EINTN(0xC20, EXYNOS4210_GPIO_X1, "gpx1"), - EXYNOS_PIN_BANK_EINTN(0xC40, EXYNOS4210_GPIO_X2, "gpx2"), - EXYNOS_PIN_BANK_EINTN(0xC60, EXYNOS4210_GPIO_X3, "gpx3"), + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpj0"), + EXYNOS_PIN_BANK_EINTG(5, 0x020, "gpj1"), + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpk0"), + EXYNOS_PIN_BANK_EINTG(7, 0x060, "gpk1"), + EXYNOS_PIN_BANK_EINTG(7, 0x080, "gpk2"), + EXYNOS_PIN_BANK_EINTG(7, 0x0A0, "gpk3"), + EXYNOS_PIN_BANK_EINTG(8, 0x0C0, "gpl0"), + EXYNOS_PIN_BANK_EINTG(3, 0x0E0, "gpl1"), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpl2"), + EXYNOS_PIN_BANK_EINTN(6, 0x120, "gpy0"), + EXYNOS_PIN_BANK_EINTN(4, 0x140, "gpy1"), + EXYNOS_PIN_BANK_EINTN(6, 0x160, "gpy2"), + EXYNOS_PIN_BANK_EINTN(8, 0x180, "gpy3"), + EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"), + EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"), + EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"), + EXYNOS_PIN_BANK_EINTN(8, 0xC00, "gpx0"), + EXYNOS_PIN_BANK_EINTN(8, 0xC20, "gpx1"), + EXYNOS_PIN_BANK_EINTN(8, 0xC40, "gpx2"), + EXYNOS_PIN_BANK_EINTN(8, 0xC60, "gpx3"), }; /* pin banks of exynos4210 pin-controller 2 */ static struct samsung_pin_bank exynos4210_pin_banks2[] = { - EXYNOS_PIN_BANK_EINTN(0x000, EXYNOS4210_GPIO_Z, "gpz"), + EXYNOS_PIN_BANK_EINTN(7, 0x000, "gpz"), }; /* @@ -540,9 +540,6 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { /* pin-controller instance 0 data */ .pin_banks = exynos4210_pin_banks0, .nr_banks = ARRAY_SIZE(exynos4210_pin_banks0), - .base = EXYNOS4210_GPIO_A0_START, - .nr_pins = EXYNOS4210_GPIOA_NR_PINS, - .nr_gint = EXYNOS4210_GPIOA_NR_GINT, .geint_con = EXYNOS_GPIO_ECON_OFFSET, .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, @@ -553,9 +550,6 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { /* pin-controller instance 1 data */ .pin_banks = exynos4210_pin_banks1, .nr_banks = ARRAY_SIZE(exynos4210_pin_banks1), - .base = EXYNOS4210_GPIOA_NR_PINS, - .nr_pins = EXYNOS4210_GPIOB_NR_PINS, - .nr_gint = EXYNOS4210_GPIOB_NR_GINT, .nr_wint = 32, .geint_con = EXYNOS_GPIO_ECON_OFFSET, .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, @@ -571,9 +565,6 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { /* pin-controller instance 2 data */ .pin_banks = exynos4210_pin_banks2, .nr_banks = ARRAY_SIZE(exynos4210_pin_banks2), - .base = EXYNOS4210_GPIOA_NR_PINS + - EXYNOS4210_GPIOB_NR_PINS, - .nr_pins = EXYNOS4210_GPIOC_NR_PINS, .label = "exynos4210-gpio-ctrl2", }, }; diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 31d0a06174e4..178846739e80 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -165,11 +165,10 @@ enum exynos4210_gpio_xc_start { #define EXYNOS_EINT_MAX_PER_BANK 8 #define EXYNOS_EINT_NR_WKUP_EINT -#define EXYNOS_PIN_BANK_EINTN(reg, __gpio, id) \ +#define EXYNOS_PIN_BANK_EINTN(pins, reg, id) \ { \ .pctl_offset = reg, \ - .pin_base = (__gpio##_START), \ - .nr_pins = (__gpio##_NR), \ + .nr_pins = pins, \ .func_width = 4, \ .pud_width = 2, \ .drv_width = 2, \ @@ -179,18 +178,16 @@ enum exynos4210_gpio_xc_start { .name = id \ } -#define EXYNOS_PIN_BANK_EINTG(reg, __gpio, id) \ +#define EXYNOS_PIN_BANK_EINTG(pins, reg, id) \ { \ .pctl_offset = reg, \ - .pin_base = (__gpio##_START), \ - .nr_pins = (__gpio##_NR), \ + .nr_pins = pins, \ .func_width = 4, \ .pud_width = 2, \ .drv_width = 2, \ .conpdn_width = 2, \ .pudpdn_width = 2, \ .eint_type = EINT_TYPE_GPIO, \ - .irq_base = (__gpio##_IRQ), \ .name = id \ } diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index aa42d54e89c6..f219bb6779ef 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -46,6 +46,8 @@ struct pin_config { { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN }, }; +static unsigned int pin_base = 0; + /* check if the selector is a valid pin group selector */ static int samsung_get_group_count(struct pinctrl_dev *pctldev) { @@ -792,6 +794,9 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( int id; const struct of_device_id *match; const struct device_node *node = pdev->dev.of_node; + struct samsung_pin_ctrl *ctrl; + struct samsung_pin_bank *bank; + int i; id = of_alias_get_id(pdev->dev.of_node, "pinctrl"); if (id < 0) { @@ -799,7 +804,22 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( return NULL; } match = of_match_node(samsung_pinctrl_dt_match, node); - return (struct samsung_pin_ctrl *)match->data + id; + ctrl = (struct samsung_pin_ctrl *)match->data + id; + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + bank->pin_base = ctrl->nr_pins; + ctrl->nr_pins += bank->nr_pins; + if (bank->eint_type == EINT_TYPE_GPIO) { + bank->irq_base = ctrl->nr_gint; + ctrl->nr_gint += bank->nr_pins; + } + } + + ctrl->base = pin_base; + pin_base += ctrl->nr_pins; + + return ctrl; } static int __devinit samsung_pinctrl_probe(struct platform_device *pdev) -- cgit v1.2.3 From 3a232ba86f3a93217ac717306645c5c555429858 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:10 +0200 Subject: pinctrl: samsung: Remove static pin enumerations Since pin numbers are now assigned dynamically, those are not needed anymore. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-exynos.h | 119 --------------------------------------- 1 file changed, 119 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 178846739e80..2de4a2936e9c 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -17,125 +17,6 @@ * (at your option) any later version. */ -#define EXYNOS_GPIO_START(__gpio) ((__gpio##_START) + (__gpio##_NR)) - -#define EXYNOS4210_GPIO_A0_NR (8) -#define EXYNOS4210_GPIO_A1_NR (6) -#define EXYNOS4210_GPIO_B_NR (8) -#define EXYNOS4210_GPIO_C0_NR (5) -#define EXYNOS4210_GPIO_C1_NR (5) -#define EXYNOS4210_GPIO_D0_NR (4) -#define EXYNOS4210_GPIO_D1_NR (4) -#define EXYNOS4210_GPIO_E0_NR (5) -#define EXYNOS4210_GPIO_E1_NR (8) -#define EXYNOS4210_GPIO_E2_NR (6) -#define EXYNOS4210_GPIO_E3_NR (8) -#define EXYNOS4210_GPIO_E4_NR (8) -#define EXYNOS4210_GPIO_F0_NR (8) -#define EXYNOS4210_GPIO_F1_NR (8) -#define EXYNOS4210_GPIO_F2_NR (8) -#define EXYNOS4210_GPIO_F3_NR (6) -#define EXYNOS4210_GPIO_J0_NR (8) -#define EXYNOS4210_GPIO_J1_NR (5) -#define EXYNOS4210_GPIO_K0_NR (7) -#define EXYNOS4210_GPIO_K1_NR (7) -#define EXYNOS4210_GPIO_K2_NR (7) -#define EXYNOS4210_GPIO_K3_NR (7) -#define EXYNOS4210_GPIO_L0_NR (8) -#define EXYNOS4210_GPIO_L1_NR (3) -#define EXYNOS4210_GPIO_L2_NR (8) -#define EXYNOS4210_GPIO_Y0_NR (6) -#define EXYNOS4210_GPIO_Y1_NR (4) -#define EXYNOS4210_GPIO_Y2_NR (6) -#define EXYNOS4210_GPIO_Y3_NR (8) -#define EXYNOS4210_GPIO_Y4_NR (8) -#define EXYNOS4210_GPIO_Y5_NR (8) -#define EXYNOS4210_GPIO_Y6_NR (8) -#define EXYNOS4210_GPIO_X0_NR (8) -#define EXYNOS4210_GPIO_X1_NR (8) -#define EXYNOS4210_GPIO_X2_NR (8) -#define EXYNOS4210_GPIO_X3_NR (8) -#define EXYNOS4210_GPIO_Z_NR (7) - -enum exynos4210_gpio_xa_start { - EXYNOS4210_GPIO_A0_START = 0, - EXYNOS4210_GPIO_A1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_A0), - EXYNOS4210_GPIO_B_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_A1), - EXYNOS4210_GPIO_C0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_B), - EXYNOS4210_GPIO_C1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_C0), - EXYNOS4210_GPIO_D0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_C1), - EXYNOS4210_GPIO_D1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_D0), - EXYNOS4210_GPIO_E0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_D1), - EXYNOS4210_GPIO_E1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E0), - EXYNOS4210_GPIO_E2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E1), - EXYNOS4210_GPIO_E3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E2), - EXYNOS4210_GPIO_E4_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E3), - EXYNOS4210_GPIO_F0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E4), - EXYNOS4210_GPIO_F1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F0), - EXYNOS4210_GPIO_F2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F1), - EXYNOS4210_GPIO_F3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F2), -}; - -enum exynos4210_gpio_xb_start { - EXYNOS4210_GPIO_J0_START = 0, - EXYNOS4210_GPIO_J1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_J0), - EXYNOS4210_GPIO_K0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_J1), - EXYNOS4210_GPIO_K1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K0), - EXYNOS4210_GPIO_K2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K1), - EXYNOS4210_GPIO_K3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K2), - EXYNOS4210_GPIO_L0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K3), - EXYNOS4210_GPIO_L1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L0), - EXYNOS4210_GPIO_L2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L1), - EXYNOS4210_GPIO_Y0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2), - EXYNOS4210_GPIO_Y1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y0), - EXYNOS4210_GPIO_Y2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y1), - EXYNOS4210_GPIO_Y3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y2), - EXYNOS4210_GPIO_Y4_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y3), - EXYNOS4210_GPIO_Y5_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y4), - EXYNOS4210_GPIO_Y6_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y5), - EXYNOS4210_GPIO_X0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y6), - EXYNOS4210_GPIO_X1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X0), - EXYNOS4210_GPIO_X2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X1), - EXYNOS4210_GPIO_X3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X2), -}; - -enum exynos4210_gpio_xc_start { - EXYNOS4210_GPIO_Z_START = 0, -}; - -#define EXYNOS4210_GPIO_A0_IRQ EXYNOS4210_GPIO_A0_START -#define EXYNOS4210_GPIO_A1_IRQ EXYNOS4210_GPIO_A1_START -#define EXYNOS4210_GPIO_B_IRQ EXYNOS4210_GPIO_B_START -#define EXYNOS4210_GPIO_C0_IRQ EXYNOS4210_GPIO_C0_START -#define EXYNOS4210_GPIO_C1_IRQ EXYNOS4210_GPIO_C1_START -#define EXYNOS4210_GPIO_D0_IRQ EXYNOS4210_GPIO_D0_START -#define EXYNOS4210_GPIO_D1_IRQ EXYNOS4210_GPIO_D1_START -#define EXYNOS4210_GPIO_E0_IRQ EXYNOS4210_GPIO_E0_START -#define EXYNOS4210_GPIO_E1_IRQ EXYNOS4210_GPIO_E1_START -#define EXYNOS4210_GPIO_E2_IRQ EXYNOS4210_GPIO_E2_START -#define EXYNOS4210_GPIO_E3_IRQ EXYNOS4210_GPIO_E3_START -#define EXYNOS4210_GPIO_E4_IRQ EXYNOS4210_GPIO_E4_START -#define EXYNOS4210_GPIO_F0_IRQ EXYNOS4210_GPIO_F0_START -#define EXYNOS4210_GPIO_F1_IRQ EXYNOS4210_GPIO_F1_START -#define EXYNOS4210_GPIO_F2_IRQ EXYNOS4210_GPIO_F2_START -#define EXYNOS4210_GPIO_F3_IRQ EXYNOS4210_GPIO_F3_START -#define EXYNOS4210_GPIO_J0_IRQ EXYNOS4210_GPIO_J0_START -#define EXYNOS4210_GPIO_J1_IRQ EXYNOS4210_GPIO_J1_START -#define EXYNOS4210_GPIO_K0_IRQ EXYNOS4210_GPIO_K0_START -#define EXYNOS4210_GPIO_K1_IRQ EXYNOS4210_GPIO_K1_START -#define EXYNOS4210_GPIO_K2_IRQ EXYNOS4210_GPIO_K2_START -#define EXYNOS4210_GPIO_K3_IRQ EXYNOS4210_GPIO_K3_START -#define EXYNOS4210_GPIO_L0_IRQ EXYNOS4210_GPIO_L0_START -#define EXYNOS4210_GPIO_L1_IRQ EXYNOS4210_GPIO_L1_START -#define EXYNOS4210_GPIO_L2_IRQ EXYNOS4210_GPIO_L2_START -#define EXYNOS4210_GPIO_Z_IRQ EXYNOS4210_GPIO_Z_START - -#define EXYNOS4210_GPIOA_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3) -#define EXYNOS4210_GPIOA_NR_GINT EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3) -#define EXYNOS4210_GPIOB_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_X3) -#define EXYNOS4210_GPIOB_NR_GINT EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2) -#define EXYNOS4210_GPIOC_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_Z) - /* External GPIO and wakeup interrupt related definitions */ #define EXYNOS_GPIO_ECON_OFFSET 0x700 #define EXYNOS_GPIO_EMASK_OFFSET 0x900 -- cgit v1.2.3 From 724e56a48c05dacc63ceb535571eec2ad6949368 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:11 +0200 Subject: pinctrl: samsung: Distinguish between pin group and bank nodes This patch modifies the loop iterating over all child nodes and parsing pin groups to check whether the node is really a pin group node by checking for existence of samsung,pins property. This is a prerequisite for further patches adding additional subnodes to the pinctrl node, required for per bank GPIO and interrupt specifiers. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f219bb6779ef..94e13780a6a6 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -609,7 +609,7 @@ static int __init samsung_pinctrl_parse_dt(struct platform_device *pdev, */ for_each_child_of_node(dev_np, cfg_np) { u32 function; - if (of_find_property(cfg_np, "interrupt-controller", NULL)) + if (!of_find_property(cfg_np, "samsung,pins", NULL)) continue; ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np, -- cgit v1.2.3 From ab663789d69760d2735402f66501f20b60312a3d Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:13 +0200 Subject: pinctrl: samsung: Match pin banks with their device nodes This patch is a preparation for converting the pinctrl-samsung driver to one GPIO chip and IRQ domain per bank. It binds banks defined by internal driver data with bank nodes in device tree. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 13 +++++++++++++ drivers/pinctrl/pinctrl-samsung.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 94e13780a6a6..f266710a1b04 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -794,6 +794,7 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( int id; const struct of_device_id *match; const struct device_node *node = pdev->dev.of_node; + struct device_node *np; struct samsung_pin_ctrl *ctrl; struct samsung_pin_bank *bank; int i; @@ -816,6 +817,18 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( } } + for_each_child_of_node(node, np) { + if (!of_find_property(np, "gpio-controller", NULL)) + continue; + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!strcmp(bank->name, np->name)) { + bank->of_node = np; + break; + } + } + } + ctrl->base = pin_base; pin_base += ctrl->nr_pins; diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index b8956934cda6..5c53f32cca06 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -111,6 +111,7 @@ struct samsung_pinctrl_drv_data; * @eint_type: type of the external interrupt supported by the bank. * @irq_base: starting controller local irq number of the bank. * @name: name to be prefixed for each pin in this pin bank. + * @of_node: OF node of the bank. */ struct samsung_pin_bank { u32 pctl_offset; @@ -124,6 +125,7 @@ struct samsung_pin_bank { enum eint_type eint_type; u32 irq_base; char *name; + struct device_node *of_node; }; /** -- cgit v1.2.3 From 6defe9a0ddc59aa2302473aa3c8b3fdb543fdc1b Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:14 +0200 Subject: pinctrl: samsung: Hold pointer to driver data in bank struct This patch is a preparation for converting the pinctrl-samsung driver to one GPIO chip and IRQ domain per bank. It allows one having only a pointer to particular bank struct to access driver data struct. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 18 ++++++++++-------- drivers/pinctrl/pinctrl-samsung.h | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f266710a1b04..53493c3c35ff 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -789,17 +789,18 @@ static const struct of_device_id samsung_pinctrl_dt_match[]; /* retrieve the soc specific data */ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( + struct samsung_pinctrl_drv_data *d, struct platform_device *pdev) { int id; const struct of_device_id *match; - const struct device_node *node = pdev->dev.of_node; + struct device_node *node = pdev->dev.of_node; struct device_node *np; struct samsung_pin_ctrl *ctrl; struct samsung_pin_bank *bank; int i; - id = of_alias_get_id(pdev->dev.of_node, "pinctrl"); + id = of_alias_get_id(node, "pinctrl"); if (id < 0) { dev_err(&pdev->dev, "failed to get alias id\n"); return NULL; @@ -809,6 +810,7 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( bank = ctrl->pin_banks; for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + bank->drvdata = d; bank->pin_base = ctrl->nr_pins; ctrl->nr_pins += bank->nr_pins; if (bank->eint_type == EINT_TYPE_GPIO) { @@ -848,18 +850,18 @@ static int __devinit samsung_pinctrl_probe(struct platform_device *pdev) return -ENODEV; } - ctrl = samsung_pinctrl_get_soc_data(pdev); - if (!ctrl) { - dev_err(&pdev->dev, "driver data not available\n"); - return -EINVAL; - } - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) { dev_err(dev, "failed to allocate memory for driver's " "private data\n"); return -ENOMEM; } + + ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev); + if (!ctrl) { + dev_err(&pdev->dev, "driver data not available\n"); + return -EINVAL; + } drvdata->ctrl = ctrl; drvdata->dev = dev; diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 5c53f32cca06..ea5dadd35913 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -112,6 +112,7 @@ struct samsung_pinctrl_drv_data; * @irq_base: starting controller local irq number of the bank. * @name: name to be prefixed for each pin in this pin bank. * @of_node: OF node of the bank. + * @drvdata: link to controller driver data */ struct samsung_pin_bank { u32 pctl_offset; @@ -126,6 +127,7 @@ struct samsung_pin_bank { u32 irq_base; char *name; struct device_node *of_node; + struct samsung_pinctrl_drv_data *drvdata; }; /** -- cgit v1.2.3 From 1b6056d6db2426cd612f03dabacf655ecb6a27ae Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:15 +0200 Subject: pinctrl: samsung: Include bank-specific eint offset in bank struct Some SoCs, like Exynos4x12, have non-sequential layout of EINT control registers and so current way of calculating register addresses does not work correctly for them. This patch adds eint_offset field to samsung_pin_bank struct and modifies the driver to use it instead of calculating the offsets from bank index. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-exynos.c | 55 +++++++++++++++++++-------------------- drivers/pinctrl/pinctrl-exynos.h | 3 ++- drivers/pinctrl/pinctrl-samsung.h | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 0ea2164bf6d9..bd9f1307a793 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -146,7 +146,7 @@ static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw, struct samsung_pin_bank *bank = d->ctrl->pin_banks; struct exynos_geint_data *eint_data; unsigned int nr_banks = d->ctrl->nr_banks, idx; - unsigned int irq_base = 0, eint_offset = 0; + unsigned int irq_base = 0; if (hw >= d->ctrl->nr_gint) { dev_err(d->dev, "unsupported ext-gpio interrupt\n"); @@ -159,7 +159,6 @@ static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw, if ((hw >= irq_base) && (hw < (irq_base + bank->nr_pins))) break; irq_base += bank->nr_pins; - eint_offset += 4; } if (idx == nr_banks) { @@ -175,7 +174,7 @@ static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw, eint_data->bank = bank; eint_data->pin = hw - irq_base; - eint_data->eint_offset = eint_offset; + eint_data->eint_offset = bank->eint_offset; return eint_data; } @@ -484,35 +483,35 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) /* pin banks of exynos4210 pin-controller 0 */ static struct samsung_pin_bank exynos4210_pin_banks0[] = { - EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0"), - EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1"), - EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb"), - EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0"), - EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1"), - EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpd0"), - EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpd1"), - EXYNOS_PIN_BANK_EINTG(5, 0x0E0, "gpe0"), - EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpe1"), - EXYNOS_PIN_BANK_EINTG(6, 0x120, "gpe2"), - EXYNOS_PIN_BANK_EINTG(8, 0x140, "gpe3"), - EXYNOS_PIN_BANK_EINTG(8, 0x160, "gpe4"), - EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf0"), - EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpf1"), - EXYNOS_PIN_BANK_EINTG(8, 0x1C0, "gpf2"), - EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf3"), + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0", 0x0c), + EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1", 0x10), + EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpd0", 0x14), + EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpd1", 0x18), + EXYNOS_PIN_BANK_EINTG(5, 0x0E0, "gpe0", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpe1", 0x20), + EXYNOS_PIN_BANK_EINTG(6, 0x120, "gpe2", 0x24), + EXYNOS_PIN_BANK_EINTG(8, 0x140, "gpe3", 0x28), + EXYNOS_PIN_BANK_EINTG(8, 0x160, "gpe4", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf0", 0x30), + EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpf1", 0x34), + EXYNOS_PIN_BANK_EINTG(8, 0x1C0, "gpf2", 0x38), + EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf3", 0x3c), }; /* pin banks of exynos4210 pin-controller 1 */ static struct samsung_pin_bank exynos4210_pin_banks1[] = { - EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpj0"), - EXYNOS_PIN_BANK_EINTG(5, 0x020, "gpj1"), - EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpk0"), - EXYNOS_PIN_BANK_EINTG(7, 0x060, "gpk1"), - EXYNOS_PIN_BANK_EINTG(7, 0x080, "gpk2"), - EXYNOS_PIN_BANK_EINTG(7, 0x0A0, "gpk3"), - EXYNOS_PIN_BANK_EINTG(8, 0x0C0, "gpl0"), - EXYNOS_PIN_BANK_EINTG(3, 0x0E0, "gpl1"), - EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpl2"), + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpj0", 0x00), + EXYNOS_PIN_BANK_EINTG(5, 0x020, "gpj1", 0x04), + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpk0", 0x08), + EXYNOS_PIN_BANK_EINTG(7, 0x060, "gpk1", 0x0c), + EXYNOS_PIN_BANK_EINTG(7, 0x080, "gpk2", 0x10), + EXYNOS_PIN_BANK_EINTG(7, 0x0A0, "gpk3", 0x14), + EXYNOS_PIN_BANK_EINTG(8, 0x0C0, "gpl0", 0x18), + EXYNOS_PIN_BANK_EINTG(3, 0x0E0, "gpl1", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpl2", 0x20), EXYNOS_PIN_BANK_EINTN(6, 0x120, "gpy0"), EXYNOS_PIN_BANK_EINTN(4, 0x140, "gpy1"), EXYNOS_PIN_BANK_EINTN(6, 0x160, "gpy2"), diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 2de4a2936e9c..5d8e380fbadf 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -59,7 +59,7 @@ .name = id \ } -#define EXYNOS_PIN_BANK_EINTG(pins, reg, id) \ +#define EXYNOS_PIN_BANK_EINTG(pins, reg, id, offs) \ { \ .pctl_offset = reg, \ .nr_pins = pins, \ @@ -69,6 +69,7 @@ .conpdn_width = 2, \ .pudpdn_width = 2, \ .eint_type = EINT_TYPE_GPIO, \ + .eint_offset = offs, \ .name = id \ } diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index ea5dadd35913..d77d9bcc5d7d 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -124,6 +124,7 @@ struct samsung_pin_bank { u8 conpdn_width; u8 pudpdn_width; enum eint_type eint_type; + u32 eint_offset; u32 irq_base; char *name; struct device_node *of_node; -- cgit v1.2.3 From 595be7268a85735d229451821b56f122d09d7bdc Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:16 +0200 Subject: pinctrl: exynos: Use one IRQ domain per pin bank Instead of registering one IRQ domain for all pin banks of a pin controller, this patch implements registration of per-bank domains. At a cost of a little memory overhead (~2.5KiB for all GPIO interrupts of Exynos4x12) it simplifies driver code and device tree sources, because GPIO interrupts can be now specified per banks. Example: device { /* ... */ interrupt-parent = <&gpa1>; interrupts = <3 0>; /* ... */ }; Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- arch/arm/boot/dts/exynos4210.dtsi | 4 -- drivers/pinctrl/pinctrl-exynos.c | 117 +++++++++++--------------------------- drivers/pinctrl/pinctrl-exynos.h | 12 ---- drivers/pinctrl/pinctrl-samsung.c | 4 -- drivers/pinctrl/pinctrl-samsung.h | 7 +-- 5 files changed, 35 insertions(+), 109 deletions(-) (limited to 'drivers') diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index b768e9f4c582..c27aea73abf7 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -46,16 +46,12 @@ compatible = "samsung,pinctrl-exynos4210"; reg = <0x11400000 0x1000>; interrupts = <0 47 0>; - interrupt-controller; - #interrupt-cells = <2>; }; pinctrl_1: pinctrl@11000000 { compatible = "samsung,pinctrl-exynos4210"; reg = <0x11000000 0x1000>; interrupts = <0 46 0>; - interrupt-controller; - #interrupt-cells = <2>; wakup_eint: wakeup-interrupt-controller { compatible = "samsung,exynos4210-wakeup-eint"; diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index bd9f1307a793..be757b1d4fcd 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -40,46 +40,46 @@ static const struct of_device_id exynos_wkup_irq_ids[] = { static void exynos_gpio_irq_unmask(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; - struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); - unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset; + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; unsigned long mask; mask = readl(d->virt_base + reg_mask); - mask &= ~(1 << edata->pin); + mask &= ~(1 << irqd->hwirq); writel(mask, d->virt_base + reg_mask); } static void exynos_gpio_irq_mask(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; - struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); - unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset; + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; unsigned long mask; mask = readl(d->virt_base + reg_mask); - mask |= 1 << edata->pin; + mask |= 1 << irqd->hwirq; writel(mask, d->virt_base + reg_mask); } static void exynos_gpio_irq_ack(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; - struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); - unsigned long reg_pend = d->ctrl->geint_pend + edata->eint_offset; + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset; - writel(1 << edata->pin, d->virt_base + reg_pend); + writel(1 << irqd->hwirq, d->virt_base + reg_pend); } static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) { - struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; struct samsung_pin_ctrl *ctrl = d->ctrl; - struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); - struct samsung_pin_bank *bank = edata->bank; - unsigned int shift = EXYNOS_EINT_CON_LEN * edata->pin; + unsigned int pin = irqd->hwirq; + unsigned int shift = EXYNOS_EINT_CON_LEN * pin; unsigned int con, trig_type; - unsigned long reg_con = ctrl->geint_con + edata->eint_offset; + unsigned long reg_con = ctrl->geint_con + bank->eint_offset; unsigned int mask; switch (type) { @@ -114,7 +114,7 @@ static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) writel(con, d->virt_base + reg_con); reg_con = bank->pctl_offset; - shift = edata->pin * bank->func_width; + shift = pin * bank->func_width; mask = (1 << bank->func_width) - 1; con = readl(d->virt_base + reg_con); @@ -136,81 +136,23 @@ static struct irq_chip exynos_gpio_irq_chip = { .irq_set_type = exynos_gpio_irq_set_type, }; -/* - * given a controller-local external gpio interrupt number, prepare the handler - * data for it. - */ -static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw, - struct samsung_pinctrl_drv_data *d) -{ - struct samsung_pin_bank *bank = d->ctrl->pin_banks; - struct exynos_geint_data *eint_data; - unsigned int nr_banks = d->ctrl->nr_banks, idx; - unsigned int irq_base = 0; - - if (hw >= d->ctrl->nr_gint) { - dev_err(d->dev, "unsupported ext-gpio interrupt\n"); - return NULL; - } - - for (idx = 0; idx < nr_banks; idx++, bank++) { - if (bank->eint_type != EINT_TYPE_GPIO) - continue; - if ((hw >= irq_base) && (hw < (irq_base + bank->nr_pins))) - break; - irq_base += bank->nr_pins; - } - - if (idx == nr_banks) { - dev_err(d->dev, "pin bank not found for ext-gpio interrupt\n"); - return NULL; - } - - eint_data = devm_kzalloc(d->dev, sizeof(*eint_data), GFP_KERNEL); - if (!eint_data) { - dev_err(d->dev, "no memory for eint-gpio data\n"); - return NULL; - } - - eint_data->bank = bank; - eint_data->pin = hw - irq_base; - eint_data->eint_offset = bank->eint_offset; - return eint_data; -} - static int exynos_gpio_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { - struct samsung_pinctrl_drv_data *d = h->host_data; - struct exynos_geint_data *eint_data; - - eint_data = exynos_get_eint_data(hw, d); - if (!eint_data) - return -EINVAL; + struct samsung_pin_bank *b = h->host_data; - irq_set_handler_data(virq, eint_data); - irq_set_chip_data(virq, h->host_data); + irq_set_chip_data(virq, b); irq_set_chip_and_handler(virq, &exynos_gpio_irq_chip, handle_level_irq); set_irq_flags(virq, IRQF_VALID); return 0; } -static void exynos_gpio_irq_unmap(struct irq_domain *h, unsigned int virq) -{ - struct samsung_pinctrl_drv_data *d = h->host_data; - struct exynos_geint_data *eint_data; - - eint_data = irq_get_handler_data(virq); - devm_kfree(d->dev, eint_data); -} - /* * irq domain callbacks for external gpio interrupt controller. */ static const struct irq_domain_ops exynos_gpio_irqd_ops = { .map = exynos_gpio_irq_map, - .unmap = exynos_gpio_irq_unmap, .xlate = irq_domain_xlate_twocell, }; @@ -229,7 +171,7 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) return IRQ_HANDLED; bank += (group - 1); - virq = irq_linear_revmap(d->gpio_irqd, bank->irq_base + pin); + virq = irq_linear_revmap(bank->irq_domain, pin); if (!virq) return IRQ_NONE; generic_handle_irq(virq); @@ -242,8 +184,10 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) */ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) { + struct samsung_pin_bank *bank; struct device *dev = d->dev; unsigned int ret; + unsigned int i; if (!d->irq) { dev_err(dev, "irq number not available\n"); @@ -257,11 +201,16 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) return -ENXIO; } - d->gpio_irqd = irq_domain_add_linear(dev->of_node, d->ctrl->nr_gint, - &exynos_gpio_irqd_ops, d); - if (!d->gpio_irqd) { - dev_err(dev, "gpio irq domain allocation failed\n"); - return -ENXIO; + bank = d->ctrl->pin_banks; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + if (bank->eint_type != EINT_TYPE_GPIO) + continue; + bank->irq_domain = irq_domain_add_linear(bank->of_node, + bank->nr_pins, &exynos_gpio_irqd_ops, bank); + if (!bank->irq_domain) { + dev_err(dev, "gpio irq domain add failed\n"); + return -ENXIO; + } } return 0; diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 5d8e380fbadf..f05efa074658 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -73,18 +73,6 @@ .name = id \ } -/** - * struct exynos_geint_data: gpio eint specific data for irq_chip callbacks. - * @bank: pin bank from which this gpio interrupt originates. - * @pin: pin number within the bank. - * @eint_offset: offset to be added to the con/pend/mask register bank base. - */ -struct exynos_geint_data { - struct samsung_pin_bank *bank; - u32 pin; - u32 eint_offset; -}; - /** * struct exynos_weint_data: irq specific data for all the wakeup interrupts * generated by the external wakeup interrupt controller. diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 53493c3c35ff..e1ef5d28094d 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -813,10 +813,6 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( bank->drvdata = d; bank->pin_base = ctrl->nr_pins; ctrl->nr_pins += bank->nr_pins; - if (bank->eint_type == EINT_TYPE_GPIO) { - bank->irq_base = ctrl->nr_gint; - ctrl->nr_gint += bank->nr_pins; - } } for_each_child_of_node(node, np) { diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index d77d9bcc5d7d..e56be22302cd 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -109,10 +109,10 @@ struct samsung_pinctrl_drv_data; * @conpdn_width: width of the sleep mode function selector bin field. * @pudpdn_width: width of the sleep mode pull up/down selector bit field. * @eint_type: type of the external interrupt supported by the bank. - * @irq_base: starting controller local irq number of the bank. * @name: name to be prefixed for each pin in this pin bank. * @of_node: OF node of the bank. * @drvdata: link to controller driver data + * @irq_domain: IRQ domain of the bank. */ struct samsung_pin_bank { u32 pctl_offset; @@ -125,10 +125,10 @@ struct samsung_pin_bank { u8 pudpdn_width; enum eint_type eint_type; u32 eint_offset; - u32 irq_base; char *name; struct device_node *of_node; struct samsung_pinctrl_drv_data *drvdata; + struct irq_domain *irq_domain; }; /** @@ -137,7 +137,6 @@ struct samsung_pin_bank { * @nr_banks: number of pin banks. * @base: starting system wide pin number. * @nr_pins: number of pins supported by the controller. - * @nr_gint: number of external gpio interrupts supported. * @nr_wint: number of external wakeup interrupts supported. * @geint_con: offset of the ext-gpio controller registers. * @geint_mask: offset of the ext-gpio interrupt mask registers. @@ -158,7 +157,6 @@ struct samsung_pin_ctrl { u32 base; u32 nr_pins; - u32 nr_gint; u32 nr_wint; u32 geint_con; @@ -205,7 +203,6 @@ struct samsung_pinctrl_drv_data { const struct samsung_pmx_func *pmx_functions; unsigned int nr_functions; - struct irq_domain *gpio_irqd; struct irq_domain *wkup_irqd; struct gpio_chip *gc; -- cgit v1.2.3 From d3a7b9e3a168df881a0ae3bd0d582f44a5d5aca3 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:17 +0200 Subject: pinctrl: samsung: Use one GPIO chip per pin bank This patch modifies the pinctrl-samsung driver to register one GPIO chip per pin bank, instead of a single chip for all pin banks of the controller. It simplifies GPIO accesses a lot (constant time instead of looping through the list of banks to find the right one) and should have a good effect on performance of any bit-banging driver. In addition it allows to reference GPIO pins by a phandle to the bank node and a local pin offset inside of the bank (similar to previous gpiolib driver), which is more clear and readable than using indices relative to the whole pin controller. Example: device { /* ... */ gpios = <&gpk0 4 0>; /* ... */ }; Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 117 ++++++++++++++++++++++++-------------- drivers/pinctrl/pinctrl-samsung.h | 11 ++-- 2 files changed, 79 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index e1ef5d28094d..0a38368edcd3 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -48,6 +48,11 @@ struct pin_config { static unsigned int pin_base = 0; +static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) +{ + return container_of(gc, struct samsung_pin_bank, gpio_chip); +} + /* check if the selector is a valid pin group selector */ static int samsung_get_group_count(struct pinctrl_dev *pctldev) { @@ -333,9 +338,12 @@ static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, void __iomem *reg; u32 data, pin_offset, mask, shift; + bank = gc_to_pin_bank(range->gc); drvdata = pinctrl_dev_get_drvdata(pctldev); - pin_to_reg_bank(drvdata, offset, ®, &pin_offset, &bank); + pin_offset = offset - bank->pin_base; + reg = drvdata->virt_base + bank->pctl_offset; + mask = (1 << bank->func_width) - 1; shift = pin_offset * bank->func_width; @@ -469,17 +477,16 @@ static struct pinconf_ops samsung_pinconf_ops = { /* gpiolib gpio_set callback function */ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { + struct samsung_pin_bank *bank = gc_to_pin_bank(gc); void __iomem *reg; - u32 pin_offset, data; - struct samsung_pinctrl_drv_data *drvdata; + u32 data; - drvdata = dev_get_drvdata(gc->dev); + reg = bank->drvdata->virt_base + bank->pctl_offset; - pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL); data = readl(reg + DAT_REG); - data &= ~(1 << pin_offset); + data &= ~(1 << offset); if (value) - data |= 1 << pin_offset; + data |= 1 << offset; writel(data, reg + DAT_REG); } @@ -487,14 +494,13 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) { void __iomem *reg; - u32 pin_offset, data; - struct samsung_pinctrl_drv_data *drvdata; + u32 data; + struct samsung_pin_bank *bank = gc_to_pin_bank(gc); - drvdata = dev_get_drvdata(gc->dev); + reg = bank->drvdata->virt_base + bank->pctl_offset; - pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL); data = readl(reg + DAT_REG); - data >>= pin_offset; + data >>= offset; data &= 1; return data; } @@ -724,12 +730,16 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev, return -EINVAL; } - drvdata->grange.name = "samsung-pctrl-gpio-range"; - drvdata->grange.id = 0; - drvdata->grange.base = drvdata->ctrl->base; - drvdata->grange.npins = drvdata->ctrl->nr_pins; - drvdata->grange.gc = drvdata->gc; - pinctrl_add_gpio_range(drvdata->pctl_dev, &drvdata->grange); + for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) { + pin_bank = &drvdata->ctrl->pin_banks[bank]; + pin_bank->grange.name = pin_bank->name; + pin_bank->grange.id = bank; + pin_bank->grange.pin_base = pin_bank->pin_base; + pin_bank->grange.base = pin_bank->gpio_chip.base; + pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; + pin_bank->grange.gc = &pin_bank->gpio_chip; + pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange); + } ret = samsung_pinctrl_parse_dt(pdev, drvdata); if (ret) { @@ -740,49 +750,68 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev, return 0; } +static const struct gpio_chip samsung_gpiolib_chip = { + .set = samsung_gpio_set, + .get = samsung_gpio_get, + .direction_input = samsung_gpio_direction_input, + .direction_output = samsung_gpio_direction_output, + .owner = THIS_MODULE, +}; + /* register the gpiolib interface with the gpiolib subsystem */ static int __init samsung_gpiolib_register(struct platform_device *pdev, struct samsung_pinctrl_drv_data *drvdata) { + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + struct samsung_pin_bank *bank = ctrl->pin_banks; struct gpio_chip *gc; int ret; + int i; - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); - if (!gc) { - dev_err(&pdev->dev, "mem alloc for gpio_chip failed\n"); - return -ENOMEM; - } - - drvdata->gc = gc; - gc->base = drvdata->ctrl->base; - gc->ngpio = drvdata->ctrl->nr_pins; - gc->dev = &pdev->dev; - gc->set = samsung_gpio_set; - gc->get = samsung_gpio_get; - gc->direction_input = samsung_gpio_direction_input; - gc->direction_output = samsung_gpio_direction_output; - gc->label = drvdata->ctrl->label; - gc->owner = THIS_MODULE; - ret = gpiochip_add(gc); - if (ret) { - dev_err(&pdev->dev, "failed to register gpio_chip %s, error " - "code: %d\n", gc->label, ret); - return ret; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + bank->gpio_chip = samsung_gpiolib_chip; + + gc = &bank->gpio_chip; + gc->base = ctrl->base + bank->pin_base; + gc->ngpio = bank->nr_pins; + gc->dev = &pdev->dev; + gc->of_node = bank->of_node; + gc->label = bank->name; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", + gc->label, ret); + goto fail; + } } return 0; + +fail: + for (--i, --bank; i >= 0; --i, --bank) + if (gpiochip_remove(&bank->gpio_chip)) + dev_err(&pdev->dev, "gpio chip %s remove failed\n", + bank->gpio_chip.label); + return ret; } /* unregister the gpiolib interface with the gpiolib subsystem */ static int __init samsung_gpiolib_unregister(struct platform_device *pdev, struct samsung_pinctrl_drv_data *drvdata) { - int ret = gpiochip_remove(drvdata->gc); - if (ret) { + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + struct samsung_pin_bank *bank = ctrl->pin_banks; + int ret = 0; + int i; + + for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) + ret = gpiochip_remove(&bank->gpio_chip); + + if (ret) dev_err(&pdev->dev, "gpio chip remove failed\n"); - return ret; - } - return 0; + + return ret; } static const struct of_device_id samsung_pinctrl_dt_match[]; diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index e56be22302cd..dac40ffd5e67 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -23,6 +23,8 @@ #include #include +#include + /* register offsets within a pin bank */ #define DAT_REG 0x4 #define PUD_REG 0x8 @@ -113,6 +115,8 @@ struct samsung_pinctrl_drv_data; * @of_node: OF node of the bank. * @drvdata: link to controller driver data * @irq_domain: IRQ domain of the bank. + * @gpio_chip: GPIO chip of the bank. + * @grange: linux gpio pin range supported by this bank. */ struct samsung_pin_bank { u32 pctl_offset; @@ -129,6 +133,8 @@ struct samsung_pin_bank { struct device_node *of_node; struct samsung_pinctrl_drv_data *drvdata; struct irq_domain *irq_domain; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range grange; }; /** @@ -186,8 +192,6 @@ struct samsung_pin_ctrl { * @nr_groups: number of such pin groups. * @pmx_functions: list of pin functions available to the driver. * @nr_function: number of such pin functions. - * @gc: gpio_chip instance registered with gpiolib. - * @grange: linux gpio pin range supported by this controller. */ struct samsung_pinctrl_drv_data { void __iomem *virt_base; @@ -204,9 +208,6 @@ struct samsung_pinctrl_drv_data { unsigned int nr_functions; struct irq_domain *wkup_irqd; - - struct gpio_chip *gc; - struct pinctrl_gpio_range grange; }; /** -- cgit v1.2.3 From a04b07c0fc4d63e3fb9fea84d48a177ac5bd9164 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:18 +0200 Subject: pinctrl: samsung: Use per-bank IRQ domain for wake-up interrupts This patch reworks wake-up interrupt handling in pinctrl-exynos driver, so each pin bank, which provides wake-up interrupts, has its own IRQ domain. Information about whether given pin bank provides wake-up interrupts, how many and whether they are separate or muxed are parsed from device tree. It gives following advantages: - interrupts can be specified in device tree in a more readable way, e.g. : device { /* ... */ interrupt-parent = <&gpx2>; interrupts = <4 0>; /* ... */ }; - the amount and layout of interrupts is not hardcoded in the code anymore, but defined in SoC-specific structure - bank and pin of each wake-up interrupt can be easily identified, to allow operations, such as setting the pin to EINT function, from irq_set_type() callback Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- arch/arm/boot/dts/exynos4210-pinctrl.dtsi | 6 ++ arch/arm/boot/dts/exynos4210.dtsi | 8 +- drivers/pinctrl/pinctrl-exynos.c | 163 ++++++++++++++++++------------ drivers/pinctrl/pinctrl-exynos.h | 29 +++++- drivers/pinctrl/pinctrl-samsung.h | 6 +- 5 files changed, 136 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi index f207d8d69170..6a4a1a04221c 100644 --- a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi @@ -445,6 +445,9 @@ #gpio-cells = <2>; interrupt-controller; + interrupt-parent = <&gic>; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>; #interrupt-cells = <2>; }; @@ -453,6 +456,9 @@ #gpio-cells = <2>; interrupt-controller; + interrupt-parent = <&gic>; + interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, + <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>; #interrupt-cells = <2>; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index c27aea73abf7..d877dbe7ac0e 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -56,13 +56,7 @@ wakup_eint: wakeup-interrupt-controller { compatible = "samsung,exynos4210-wakeup-eint"; interrupt-parent = <&gic>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, - <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, - <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, - <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, - <0 32 0>; + interrupts = <0 32 0>; }; }; diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index be757b1d4fcd..4bf2fc40aca8 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -218,46 +218,43 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) static void exynos_wkup_irq_unmask(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); - unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; - unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); - unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2); + struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = b->drvdata; + unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset; unsigned long mask; mask = readl(d->virt_base + reg_mask); - mask &= ~(1 << pin); + mask &= ~(1 << irqd->hwirq); writel(mask, d->virt_base + reg_mask); } static void exynos_wkup_irq_mask(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); - unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; - unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); - unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2); + struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = b->drvdata; + unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset; unsigned long mask; mask = readl(d->virt_base + reg_mask); - mask |= 1 << pin; + mask |= 1 << irqd->hwirq; writel(mask, d->virt_base + reg_mask); } static void exynos_wkup_irq_ack(struct irq_data *irqd) { - struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); - unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; - unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); - unsigned long pend = d->ctrl->weint_pend + (bank << 2); + struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = b->drvdata; + unsigned long pend = d->ctrl->weint_pend + b->eint_offset; - writel(1 << pin, d->virt_base + pend); + writel(1 << irqd->hwirq, d->virt_base + pend); } static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) { - struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); - unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; - unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); - unsigned long reg_con = d->ctrl->weint_con + (bank << 2); + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned int pin = irqd->hwirq; + unsigned long reg_con = d->ctrl->weint_con + bank->eint_offset; unsigned long shift = EXYNOS_EINT_CON_LEN * pin; unsigned long con, trig_type; @@ -309,6 +306,7 @@ static struct irq_chip exynos_wkup_irq_chip = { static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) { struct exynos_weint_data *eintd = irq_get_handler_data(irq); + struct samsung_pin_bank *bank = eintd->bank; struct irq_chip *chip = irq_get_chip(irq); int eint_irq; @@ -318,20 +316,20 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) if (chip->irq_ack) chip->irq_ack(&desc->irq_data); - eint_irq = irq_linear_revmap(eintd->domain, eintd->irq); + eint_irq = irq_linear_revmap(bank->irq_domain, eintd->irq); generic_handle_irq(eint_irq); chip->irq_unmask(&desc->irq_data); chained_irq_exit(chip, desc); } -static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend, - struct irq_domain *domain) +static inline void exynos_irq_demux_eint(unsigned long pend, + struct irq_domain *domain) { unsigned int irq; while (pend) { irq = fls(pend) - 1; - generic_handle_irq(irq_find_mapping(domain, irq_base + irq)); + generic_handle_irq(irq_find_mapping(domain, irq)); pend &= ~(1 << irq); } } @@ -340,18 +338,22 @@ static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend, static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); - struct exynos_weint_data *eintd = irq_get_handler_data(irq); - struct samsung_pinctrl_drv_data *d = eintd->domain->host_data; + struct exynos_muxed_weint_data *eintd = irq_get_handler_data(irq); + struct samsung_pinctrl_drv_data *d = eintd->banks[0]->drvdata; + struct samsung_pin_ctrl *ctrl = d->ctrl; unsigned long pend; unsigned long mask; + int i; chained_irq_enter(chip, desc); - pend = readl(d->virt_base + d->ctrl->weint_pend + 0x8); - mask = readl(d->virt_base + d->ctrl->weint_mask + 0x8); - exynos_irq_demux_eint(16, pend & ~mask, eintd->domain); - pend = readl(d->virt_base + d->ctrl->weint_pend + 0xC); - mask = readl(d->virt_base + d->ctrl->weint_mask + 0xC); - exynos_irq_demux_eint(24, pend & ~mask, eintd->domain); + + for (i = 0; i < eintd->nr_banks; ++i) { + struct samsung_pin_bank *b = eintd->banks[i]; + pend = readl(d->virt_base + ctrl->weint_pend + b->eint_offset); + mask = readl(d->virt_base + ctrl->weint_mask + b->eint_offset); + exynos_irq_demux_eint(pend & ~mask, b->irq_domain); + } + chained_irq_exit(chip, desc); } @@ -381,7 +383,11 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) struct device *dev = d->dev; struct device_node *wkup_np = NULL; struct device_node *np; + struct samsung_pin_bank *bank; struct exynos_weint_data *weint_data; + struct exynos_muxed_weint_data *muxed_data; + unsigned int muxed_banks = 0; + unsigned int i; int idx, irq; for_each_child_of_node(dev->of_node, np) { @@ -393,40 +399,74 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) if (!wkup_np) return -ENODEV; - d->wkup_irqd = irq_domain_add_linear(wkup_np, d->ctrl->nr_wint, - &exynos_wkup_irqd_ops, d); - if (!d->wkup_irqd) { - dev_err(dev, "wakeup irq domain allocation failed\n"); - return -ENXIO; - } + bank = d->ctrl->pin_banks; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + if (bank->eint_type != EINT_TYPE_WKUP) + continue; - weint_data = devm_kzalloc(dev, sizeof(*weint_data) * 17, GFP_KERNEL); - if (!weint_data) { - dev_err(dev, "could not allocate memory for weint_data\n"); - return -ENOMEM; - } + bank->irq_domain = irq_domain_add_linear(bank->of_node, + bank->nr_pins, &exynos_wkup_irqd_ops, bank); + if (!bank->irq_domain) { + dev_err(dev, "wkup irq domain add failed\n"); + return -ENXIO; + } - irq = irq_of_parse_and_map(wkup_np, 16); - if (irq) { - weint_data[16].domain = d->wkup_irqd; - irq_set_chained_handler(irq, exynos_irq_demux_eint16_31); - irq_set_handler_data(irq, &weint_data[16]); - } else { - dev_err(dev, "irq number for EINT16-32 not found\n"); - } + if (!of_find_property(bank->of_node, "interrupts", NULL)) { + bank->eint_type = EINT_TYPE_WKUP_MUX; + ++muxed_banks; + continue; + } - for (idx = 0; idx < 16; idx++) { - weint_data[idx].domain = d->wkup_irqd; - weint_data[idx].irq = idx; + weint_data = devm_kzalloc(dev, bank->nr_pins + * sizeof(*weint_data), GFP_KERNEL); + if (!weint_data) { + dev_err(dev, "could not allocate memory for weint_data\n"); + return -ENOMEM; + } - irq = irq_of_parse_and_map(wkup_np, idx); - if (irq) { + for (idx = 0; idx < bank->nr_pins; ++idx) { + irq = irq_of_parse_and_map(bank->of_node, idx); + if (!irq) { + dev_err(dev, "irq number for eint-%s-%d not found\n", + bank->name, idx); + continue; + } + weint_data[idx].irq = idx; + weint_data[idx].bank = bank; irq_set_handler_data(irq, &weint_data[idx]); irq_set_chained_handler(irq, exynos_irq_eint0_15); - } else { - dev_err(dev, "irq number for eint-%x not found\n", idx); } } + + if (!muxed_banks) + return 0; + + irq = irq_of_parse_and_map(wkup_np, 0); + if (!irq) { + dev_err(dev, "irq number for muxed EINTs not found\n"); + return 0; + } + + muxed_data = devm_kzalloc(dev, sizeof(*muxed_data) + + muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL); + if (!muxed_data) { + dev_err(dev, "could not allocate memory for muxed_data\n"); + return -ENOMEM; + } + + irq_set_chained_handler(irq, exynos_irq_demux_eint16_31); + irq_set_handler_data(irq, muxed_data); + + bank = d->ctrl->pin_banks; + idx = 0; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + if (bank->eint_type != EINT_TYPE_WKUP_MUX) + continue; + + muxed_data->banks[idx++] = bank; + } + muxed_data->nr_banks = muxed_banks; + return 0; } @@ -468,10 +508,10 @@ static struct samsung_pin_bank exynos4210_pin_banks1[] = { EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"), EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"), EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"), - EXYNOS_PIN_BANK_EINTN(8, 0xC00, "gpx0"), - EXYNOS_PIN_BANK_EINTN(8, 0xC20, "gpx1"), - EXYNOS_PIN_BANK_EINTN(8, 0xC40, "gpx2"), - EXYNOS_PIN_BANK_EINTN(8, 0xC60, "gpx3"), + EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c), }; /* pin banks of exynos4210 pin-controller 2 */ @@ -498,7 +538,6 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { /* pin-controller instance 1 data */ .pin_banks = exynos4210_pin_banks1, .nr_banks = ARRAY_SIZE(exynos4210_pin_banks1), - .nr_wint = 32, .geint_con = EXYNOS_GPIO_ECON_OFFSET, .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index f05efa074658..0a708890d8b4 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -73,13 +73,36 @@ .name = id \ } +#define EXYNOS_PIN_BANK_EINTW(pins, reg, id, offs) \ + { \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .func_width = 4, \ + .pud_width = 2, \ + .drv_width = 2, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_offset = offs, \ + .name = id \ + } + /** * struct exynos_weint_data: irq specific data for all the wakeup interrupts * generated by the external wakeup interrupt controller. - * @domain: irq domain representing the external wakeup interrupts * @irq: interrupt number within the domain. + * @bank: bank responsible for this interrupt */ struct exynos_weint_data { - struct irq_domain *domain; - u32 irq; + unsigned int irq; + struct samsung_pin_bank *bank; +}; + +/** + * struct exynos_muxed_weint_data: irq specific data for muxed wakeup interrupts + * generated by the external wakeup interrupt controller. + * @nr_banks: count of banks being part of the mux + * @banks: array of banks being part of the mux + */ +struct exynos_muxed_weint_data { + unsigned int nr_banks; + struct samsung_pin_bank *banks[]; }; diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index dac40ffd5e67..0670d9ea43fa 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -66,6 +66,7 @@ enum pincfg_type { * @EINT_TYPE_NONE: bank does not support external interrupts * @EINT_TYPE_GPIO: bank supportes external gpio interrupts * @EINT_TYPE_WKUP: bank supportes external wakeup interrupts + * @EINT_TYPE_WKUP_MUX: bank supports multiplexed external wakeup interrupts * * Samsung GPIO controller groups all the available pins into banks. The pins * in a pin bank can support external gpio interrupts or external wakeup @@ -78,6 +79,7 @@ enum eint_type { EINT_TYPE_NONE, EINT_TYPE_GPIO, EINT_TYPE_WKUP, + EINT_TYPE_WKUP_MUX, }; /* maximum length of a pin in pin descriptor (example: "gpa0-0") */ @@ -143,7 +145,6 @@ struct samsung_pin_bank { * @nr_banks: number of pin banks. * @base: starting system wide pin number. * @nr_pins: number of pins supported by the controller. - * @nr_wint: number of external wakeup interrupts supported. * @geint_con: offset of the ext-gpio controller registers. * @geint_mask: offset of the ext-gpio interrupt mask registers. * @geint_pend: offset of the ext-gpio interrupt pending registers. @@ -163,7 +164,6 @@ struct samsung_pin_ctrl { u32 base; u32 nr_pins; - u32 nr_wint; u32 geint_con; u32 geint_mask; @@ -206,8 +206,6 @@ struct samsung_pinctrl_drv_data { unsigned int nr_groups; const struct samsung_pmx_func *pmx_functions; unsigned int nr_functions; - - struct irq_domain *wkup_irqd; }; /** -- cgit v1.2.3 From 22b9ba033bb4401e4cceb69c9e1af74a4631dd74 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:19 +0200 Subject: pinctrl: exynos: Set pin function to EINT in irq_set_type of wake-up EINT Pins used as wake-up interrupts need to be configured as EINTs. This patch adds the required configuration code to exynos_wkup_irq_set_type, to set the pin as EINT when its interrupt trigger type is configured. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-exynos.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 4bf2fc40aca8..73a0aa27cd56 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -257,6 +257,7 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) unsigned long reg_con = d->ctrl->weint_con + bank->eint_offset; unsigned long shift = EXYNOS_EINT_CON_LEN * pin; unsigned long con, trig_type; + unsigned int mask; switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -288,6 +289,16 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) con &= ~(EXYNOS_EINT_CON_MASK << shift); con |= trig_type << shift; writel(con, d->virt_base + reg_con); + + reg_con = bank->pctl_offset; + shift = pin * bank->func_width; + mask = (1 << bank->func_width) - 1; + + con = readl(d->virt_base + reg_con); + con &= ~(mask << shift); + con |= EXYNOS_EINT_FUNC << shift; + writel(con, d->virt_base + reg_con); + return 0; } -- cgit v1.2.3 From a19fe2d45cc550ca42a3b0be3e716a8452e4b0c6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:20 +0200 Subject: pinctrl: samsung: Add GPIO to IRQ translation Some drivers require a way to translate GPIO pins to their IRQ numbers. This patch adds the .to_irq() gpiolib callback to pinctrl-samsung driver, which creates (if not present yet) and returns an IRQ mapping for given GPIO pin. Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-samsung.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 0a38368edcd3..0db88bbbb2bb 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "core.h" #include "pinctrl-samsung.h" @@ -527,6 +528,23 @@ static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset, return pinctrl_gpio_direction_output(gc->base + offset); } +/* + * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin + * and a virtual IRQ, if not already present. + */ +static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct samsung_pin_bank *bank = gc_to_pin_bank(gc); + unsigned int virq; + + if (!bank->irq_domain) + return -ENXIO; + + virq = irq_create_mapping(bank->irq_domain, offset); + + return (virq) ? : -ENXIO; +} + /* * Parse the pin names listed in the 'samsung,pins' property and convert it * into a list of gpio numbers are create a pin group from it. @@ -755,6 +773,7 @@ static const struct gpio_chip samsung_gpiolib_chip = { .get = samsung_gpio_get, .direction_input = samsung_gpio_direction_input, .direction_output = samsung_gpio_direction_output, + .to_irq = samsung_gpio_to_irq, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 2eb2478d471e45e1d0c8bb3defbf82bf7204e13d Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 5 Oct 2012 13:04:40 -0400 Subject: uio: uio_pruss: replace private SRAM API with genalloc Remove the use of the private DaVinci SRAM API in favor of genalloc. The pool to be used is provided by platform data. Signed-off-by: Matt Porter Signed-off-by: "Hans J. Koch" Signed-off-by: Sekhar Nori --- drivers/uio/Kconfig | 1 + drivers/uio/uio_pruss.c | 24 +++++++++++++++++------- include/linux/platform_data/uio_pruss.h | 3 ++- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 6f3ea9bbc818..c48b93813fc1 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -97,6 +97,7 @@ config UIO_NETX config UIO_PRUSS tristate "Texas Instruments PRUSS driver" depends on ARCH_DAVINCI_DA850 + select GENERIC_ALLOCATOR help PRUSS driver for OMAPL138/DA850/AM18XX devices PRUSS driver requires user space components, examples and user space diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 33a7a273b453..f8738de342be 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #define DRV_NAME "pruss_uio" #define DRV_VERSION "1.0" @@ -65,10 +65,11 @@ struct uio_pruss_dev { dma_addr_t sram_paddr; dma_addr_t ddr_paddr; void __iomem *prussio_vaddr; - void *sram_vaddr; + unsigned long sram_vaddr; void *ddr_vaddr; unsigned int hostirq_start; unsigned int pintc_base; + struct gen_pool *sram_pool; }; static irqreturn_t pruss_handler(int irq, struct uio_info *info) @@ -106,7 +107,9 @@ static void pruss_cleanup(struct platform_device *dev, gdev->ddr_paddr); } if (gdev->sram_vaddr) - sram_free(gdev->sram_vaddr, sram_pool_sz); + gen_pool_free(gdev->sram_pool, + gdev->sram_vaddr, + sram_pool_sz); kfree(gdev->info); clk_put(gdev->pruss_clk); kfree(gdev); @@ -152,10 +155,17 @@ static int __devinit pruss_probe(struct platform_device *dev) goto out_free; } - gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr)); - if (!gdev->sram_vaddr) { - dev_err(&dev->dev, "Could not allocate SRAM pool\n"); - goto out_free; + if (pdata->sram_pool) { + gdev->sram_pool = pdata->sram_pool; + gdev->sram_vaddr = + gen_pool_alloc(gdev->sram_pool, sram_pool_sz); + if (!gdev->sram_vaddr) { + dev_err(&dev->dev, "Could not allocate SRAM pool\n"); + goto out_free; + } + gdev->sram_paddr = + gen_pool_virt_to_phys(gdev->sram_pool, + gdev->sram_vaddr); } gdev->ddr_vaddr = dma_alloc_coherent(&dev->dev, extram_pool_sz, diff --git a/include/linux/platform_data/uio_pruss.h b/include/linux/platform_data/uio_pruss.h index f39140aabc6f..3d47d219827f 100644 --- a/include/linux/platform_data/uio_pruss.h +++ b/include/linux/platform_data/uio_pruss.h @@ -20,6 +20,7 @@ /* To configure the PRUSS INTC base offset for UIO driver */ struct uio_pruss_pdata { - u32 pintc_base; + u32 pintc_base; + struct gen_pool *sram_pool; }; #endif /* _UIO_PRUSS_H_ */ -- cgit v1.2.3 From 2389d5014342e9535aad212d0c68d439aaf534ba Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Oct 2012 22:04:31 +0100 Subject: ARM: plat-versatile: move FPGA irq driver to drivers/irqchip This moves the Versatile FPGA interrupt controller driver, used in the Integrator/AP, Integrator/CP and some Versatile boards, out of arch/arm/plat-versatile and down to drivers/irqchip where we have consensus that such drivers belong. The header file is consequently moved to . Signed-off-by: Linus Walleij --- arch/arm/Kconfig | 4 +- arch/arm/mach-integrator/integrator_ap.c | 3 +- arch/arm/mach-integrator/integrator_cp.c | 2 +- arch/arm/mach-versatile/core.c | 2 +- arch/arm/plat-versatile/Kconfig | 9 -- arch/arm/plat-versatile/Makefile | 1 - arch/arm/plat-versatile/fpga-irq.c | 204 ------------------------ arch/arm/plat-versatile/include/plat/fpga-irq.h | 13 -- drivers/irqchip/Kconfig | 9 +- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-versatile-fpga.c | 204 ++++++++++++++++++++++++ include/linux/irqchip/versatile-fpga.h | 13 ++ 12 files changed, 231 insertions(+), 234 deletions(-) delete mode 100644 arch/arm/plat-versatile/fpga-irq.c delete mode 100644 arch/arm/plat-versatile/include/plat/fpga-irq.h create mode 100644 drivers/irqchip/irq-versatile-fpga.c create mode 100644 include/linux/irqchip/versatile-fpga.h (limited to 'drivers') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 73067efd4845..2205e3eb55bf 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -284,8 +284,8 @@ config ARCH_INTEGRATOR select MULTI_IRQ_HANDLER select NEED_MACH_MEMORY_H select PLAT_VERSATILE - select PLAT_VERSATILE_FPGA_IRQ select SPARSE_IRQ + select VERSATILE_FPGA_IRQ help Support for ARM's Integrator platform. @@ -318,7 +318,7 @@ config ARCH_VERSATILE select PLAT_VERSATILE select PLAT_VERSATILE_CLCD select PLAT_VERSATILE_CLOCK - select PLAT_VERSATILE_FPGA_IRQ + select VERSATILE_FPGA_IRQ help This enables support for ARM Ltd Versatile board. diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index 4f13bc57e5a4..e67a9fe18d1b 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -56,8 +57,6 @@ #include #include -#include - #include "common.h" /* diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 4423bc8e84c5..acecf04f50f7 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,6 @@ #include #include -#include #include #include "common.h" diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 5b5c1eeb5b5c..5d5929450366 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,6 @@ #include #include -#include #include #include "core.h" diff --git a/arch/arm/plat-versatile/Kconfig b/arch/arm/plat-versatile/Kconfig index 2a4ae8a6a081..619f0fa0f06c 100644 --- a/arch/arm/plat-versatile/Kconfig +++ b/arch/arm/plat-versatile/Kconfig @@ -6,15 +6,6 @@ config PLAT_VERSATILE_CLOCK config PLAT_VERSATILE_CLCD bool -config PLAT_VERSATILE_FPGA_IRQ - bool - select IRQ_DOMAIN - -config PLAT_VERSATILE_FPGA_IRQ_NR - int - default 4 - depends on PLAT_VERSATILE_FPGA_IRQ - config PLAT_VERSATILE_LEDS def_bool y if NEW_LEDS depends on ARCH_REALVIEW || ARCH_VERSATILE diff --git a/arch/arm/plat-versatile/Makefile b/arch/arm/plat-versatile/Makefile index 74cfd94cbf80..f88d448b629c 100644 --- a/arch/arm/plat-versatile/Makefile +++ b/arch/arm/plat-versatile/Makefile @@ -2,7 +2,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include obj-$(CONFIG_PLAT_VERSATILE_CLOCK) += clock.o obj-$(CONFIG_PLAT_VERSATILE_CLCD) += clcd.o -obj-$(CONFIG_PLAT_VERSATILE_FPGA_IRQ) += fpga-irq.o obj-$(CONFIG_PLAT_VERSATILE_LEDS) += leds.o obj-$(CONFIG_PLAT_VERSATILE_SCHED_CLOCK) += sched-clock.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/plat-versatile/fpga-irq.c b/arch/arm/plat-versatile/fpga-irq.c deleted file mode 100644 index dfe317c20354..000000000000 --- a/arch/arm/plat-versatile/fpga-irq.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Support for Versatile FPGA-based IRQ controllers - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define IRQ_STATUS 0x00 -#define IRQ_RAW_STATUS 0x04 -#define IRQ_ENABLE_SET 0x08 -#define IRQ_ENABLE_CLEAR 0x0c -#define INT_SOFT_SET 0x10 -#define INT_SOFT_CLEAR 0x14 -#define FIQ_STATUS 0x20 -#define FIQ_RAW_STATUS 0x24 -#define FIQ_ENABLE 0x28 -#define FIQ_ENABLE_SET 0x28 -#define FIQ_ENABLE_CLEAR 0x2C - -/** - * struct fpga_irq_data - irq data container for the FPGA IRQ controller - * @base: memory offset in virtual memory - * @chip: chip container for this instance - * @domain: IRQ domain for this instance - * @valid: mask for valid IRQs on this controller - * @used_irqs: number of active IRQs on this controller - */ -struct fpga_irq_data { - void __iomem *base; - struct irq_chip chip; - u32 valid; - struct irq_domain *domain; - u8 used_irqs; -}; - -/* we cannot allocate memory when the controllers are initially registered */ -static struct fpga_irq_data fpga_irq_devices[CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR]; -static int fpga_irq_id; - -static void fpga_irq_mask(struct irq_data *d) -{ - struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); - u32 mask = 1 << d->hwirq; - - writel(mask, f->base + IRQ_ENABLE_CLEAR); -} - -static void fpga_irq_unmask(struct irq_data *d) -{ - struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); - u32 mask = 1 << d->hwirq; - - writel(mask, f->base + IRQ_ENABLE_SET); -} - -static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc) -{ - struct fpga_irq_data *f = irq_desc_get_handler_data(desc); - u32 status = readl(f->base + IRQ_STATUS); - - if (status == 0) { - do_bad_IRQ(irq, desc); - return; - } - - do { - irq = ffs(status) - 1; - status &= ~(1 << irq); - generic_handle_irq(irq_find_mapping(f->domain, irq)); - } while (status); -} - -/* - * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero - * if we've handled at least one interrupt. This does a single read of the - * status register and handles all interrupts in order from LSB first. - */ -static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) -{ - int handled = 0; - int irq; - u32 status; - - while ((status = readl(f->base + IRQ_STATUS))) { - irq = ffs(status) - 1; - handle_IRQ(irq_find_mapping(f->domain, irq), regs); - handled = 1; - } - - return handled; -} - -/* - * Keep iterating over all registered FPGA IRQ controllers until there are - * no pending interrupts. - */ -asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) -{ - int i, handled; - - do { - for (i = 0, handled = 0; i < fpga_irq_id; ++i) - handled |= handle_one_fpga(&fpga_irq_devices[i], regs); - } while (handled); -} - -static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct fpga_irq_data *f = d->host_data; - - /* Skip invalid IRQs, only register handlers for the real ones */ - if (!(f->valid & BIT(hwirq))) - return -ENOTSUPP; - irq_set_chip_data(irq, f); - irq_set_chip_and_handler(irq, &f->chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - return 0; -} - -static struct irq_domain_ops fpga_irqdomain_ops = { - .map = fpga_irqdomain_map, - .xlate = irq_domain_xlate_onetwocell, -}; - -void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, - int parent_irq, u32 valid, struct device_node *node) -{ - struct fpga_irq_data *f; - int i; - - if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { - pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); - return; - } - f = &fpga_irq_devices[fpga_irq_id]; - f->base = base; - f->chip.name = name; - f->chip.irq_ack = fpga_irq_mask; - f->chip.irq_mask = fpga_irq_mask; - f->chip.irq_unmask = fpga_irq_unmask; - f->valid = valid; - - if (parent_irq != -1) { - irq_set_handler_data(parent_irq, f); - irq_set_chained_handler(parent_irq, fpga_irq_handle); - } - - /* This will also allocate irq descriptors */ - f->domain = irq_domain_add_simple(node, fls(valid), irq_start, - &fpga_irqdomain_ops, f); - - /* This will allocate all valid descriptors in the linear case */ - for (i = 0; i < fls(valid); i++) - if (valid & BIT(i)) { - if (!irq_start) - irq_create_mapping(f->domain, i); - f->used_irqs++; - } - - pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", - fpga_irq_id, name, base, f->used_irqs); - - fpga_irq_id++; -} - -#ifdef CONFIG_OF -int __init fpga_irq_of_init(struct device_node *node, - struct device_node *parent) -{ - struct fpga_irq_data *f; - void __iomem *base; - u32 clear_mask; - u32 valid_mask; - - if (WARN_ON(!node)) - return -ENODEV; - - base = of_iomap(node, 0); - WARN(!base, "unable to map fpga irq registers\n"); - - if (of_property_read_u32(node, "clear-mask", &clear_mask)) - clear_mask = 0; - - if (of_property_read_u32(node, "valid-mask", &valid_mask)) - valid_mask = 0; - - fpga_irq_init(base, node->name, 0, -1, valid_mask, node); - - writel(clear_mask, base + IRQ_ENABLE_CLEAR); - writel(clear_mask, base + FIQ_ENABLE_CLEAR); - - return 0; -} -#endif diff --git a/arch/arm/plat-versatile/include/plat/fpga-irq.h b/arch/arm/plat-versatile/include/plat/fpga-irq.h deleted file mode 100644 index 1fac9651d3ca..000000000000 --- a/arch/arm/plat-versatile/include/plat/fpga-irq.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef PLAT_FPGA_IRQ_H -#define PLAT_FPGA_IRQ_H - -struct device_node; -struct pt_regs; - -void fpga_handle_irq(struct pt_regs *regs); -void fpga_irq_init(void __iomem *, const char *, int, int, u32, - struct device_node *node); -int fpga_irq_of_init(struct device_node *node, - struct device_node *parent); - -#endif diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 1bb8bf6d7fd4..62ca575701d3 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -1 +1,8 @@ -# empty +config VERSATILE_FPGA_IRQ + bool + select IRQ_DOMAIN + +config VERSATILE_FPGA_IRQ_NR + int + default 4 + depends on VERSATILE_FPGA_IRQ diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 054321db4350..e2e6eb5d32f4 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c new file mode 100644 index 000000000000..789b3e526930 --- /dev/null +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -0,0 +1,204 @@ +/* + * Support for Versatile FPGA-based IRQ controllers + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define IRQ_STATUS 0x00 +#define IRQ_RAW_STATUS 0x04 +#define IRQ_ENABLE_SET 0x08 +#define IRQ_ENABLE_CLEAR 0x0c +#define INT_SOFT_SET 0x10 +#define INT_SOFT_CLEAR 0x14 +#define FIQ_STATUS 0x20 +#define FIQ_RAW_STATUS 0x24 +#define FIQ_ENABLE 0x28 +#define FIQ_ENABLE_SET 0x28 +#define FIQ_ENABLE_CLEAR 0x2C + +/** + * struct fpga_irq_data - irq data container for the FPGA IRQ controller + * @base: memory offset in virtual memory + * @chip: chip container for this instance + * @domain: IRQ domain for this instance + * @valid: mask for valid IRQs on this controller + * @used_irqs: number of active IRQs on this controller + */ +struct fpga_irq_data { + void __iomem *base; + struct irq_chip chip; + u32 valid; + struct irq_domain *domain; + u8 used_irqs; +}; + +/* we cannot allocate memory when the controllers are initially registered */ +static struct fpga_irq_data fpga_irq_devices[CONFIG_VERSATILE_FPGA_IRQ_NR]; +static int fpga_irq_id; + +static void fpga_irq_mask(struct irq_data *d) +{ + struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); + u32 mask = 1 << d->hwirq; + + writel(mask, f->base + IRQ_ENABLE_CLEAR); +} + +static void fpga_irq_unmask(struct irq_data *d) +{ + struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); + u32 mask = 1 << d->hwirq; + + writel(mask, f->base + IRQ_ENABLE_SET); +} + +static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc) +{ + struct fpga_irq_data *f = irq_desc_get_handler_data(desc); + u32 status = readl(f->base + IRQ_STATUS); + + if (status == 0) { + do_bad_IRQ(irq, desc); + return; + } + + do { + irq = ffs(status) - 1; + status &= ~(1 << irq); + generic_handle_irq(irq_find_mapping(f->domain, irq)); + } while (status); +} + +/* + * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero + * if we've handled at least one interrupt. This does a single read of the + * status register and handles all interrupts in order from LSB first. + */ +static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) +{ + int handled = 0; + int irq; + u32 status; + + while ((status = readl(f->base + IRQ_STATUS))) { + irq = ffs(status) - 1; + handle_IRQ(irq_find_mapping(f->domain, irq), regs); + handled = 1; + } + + return handled; +} + +/* + * Keep iterating over all registered FPGA IRQ controllers until there are + * no pending interrupts. + */ +asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) +{ + int i, handled; + + do { + for (i = 0, handled = 0; i < fpga_irq_id; ++i) + handled |= handle_one_fpga(&fpga_irq_devices[i], regs); + } while (handled); +} + +static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct fpga_irq_data *f = d->host_data; + + /* Skip invalid IRQs, only register handlers for the real ones */ + if (!(f->valid & BIT(hwirq))) + return -ENOTSUPP; + irq_set_chip_data(irq, f); + irq_set_chip_and_handler(irq, &f->chip, + handle_level_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + return 0; +} + +static struct irq_domain_ops fpga_irqdomain_ops = { + .map = fpga_irqdomain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, + int parent_irq, u32 valid, struct device_node *node) +{ + struct fpga_irq_data *f; + int i; + + if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { + pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); + return; + } + f = &fpga_irq_devices[fpga_irq_id]; + f->base = base; + f->chip.name = name; + f->chip.irq_ack = fpga_irq_mask; + f->chip.irq_mask = fpga_irq_mask; + f->chip.irq_unmask = fpga_irq_unmask; + f->valid = valid; + + if (parent_irq != -1) { + irq_set_handler_data(parent_irq, f); + irq_set_chained_handler(parent_irq, fpga_irq_handle); + } + + /* This will also allocate irq descriptors */ + f->domain = irq_domain_add_simple(node, fls(valid), irq_start, + &fpga_irqdomain_ops, f); + + /* This will allocate all valid descriptors in the linear case */ + for (i = 0; i < fls(valid); i++) + if (valid & BIT(i)) { + if (!irq_start) + irq_create_mapping(f->domain, i); + f->used_irqs++; + } + + pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", + fpga_irq_id, name, base, f->used_irqs); + + fpga_irq_id++; +} + +#ifdef CONFIG_OF +int __init fpga_irq_of_init(struct device_node *node, + struct device_node *parent) +{ + struct fpga_irq_data *f; + void __iomem *base; + u32 clear_mask; + u32 valid_mask; + + if (WARN_ON(!node)) + return -ENODEV; + + base = of_iomap(node, 0); + WARN(!base, "unable to map fpga irq registers\n"); + + if (of_property_read_u32(node, "clear-mask", &clear_mask)) + clear_mask = 0; + + if (of_property_read_u32(node, "valid-mask", &valid_mask)) + valid_mask = 0; + + fpga_irq_init(base, node->name, 0, -1, valid_mask, node); + + writel(clear_mask, base + IRQ_ENABLE_CLEAR); + writel(clear_mask, base + FIQ_ENABLE_CLEAR); + + return 0; +} +#endif diff --git a/include/linux/irqchip/versatile-fpga.h b/include/linux/irqchip/versatile-fpga.h new file mode 100644 index 000000000000..1fac9651d3ca --- /dev/null +++ b/include/linux/irqchip/versatile-fpga.h @@ -0,0 +1,13 @@ +#ifndef PLAT_FPGA_IRQ_H +#define PLAT_FPGA_IRQ_H + +struct device_node; +struct pt_regs; + +void fpga_handle_irq(struct pt_regs *regs); +void fpga_irq_init(void __iomem *, const char *, int, int, u32, + struct device_node *node); +int fpga_irq_of_init(struct device_node *node, + struct device_node *parent); + +#endif -- cgit v1.2.3 From 3ecbf05be159a95e1d23ba9b3b21c5bc2941ba6b Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 14:55:40 +0100 Subject: mfd: Versatile Express config infrastructure Versatile Express platform has an elaborated configuration system, consisting of microcontrollers residing on the mother- and daughterboards known as Motherboard/Daughterboard Configuration Controller (MCC and DCC). The controllers are responsible for the platform initialization (reset generation, flash programming, FPGA bitfiles loading etc.) but also control clock generators, voltage regulators, gather environmental data like temperature, power consumption etc. Even the video output switch (FPGA) is controlled that way. Those devices are _not_ visible in the main address space and the usual communication channel uses some kind of a bridge in the peripheral block sending commands (requests) to the controllers and receiving responses. It can take up to 500 microseconds for a transaction to be completed, therefore it is important to provide a non-blocking interface to it. This patch adds an abstraction of this infrastructure. Bridge drivers can register themselves with the framework. Then, a driver of a device can request an abstract "function" - the request will be redirected to a bridge referred by thedd "arm,vexpress,config-bridge" property of the device tree node. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 68 ++++- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 1 + drivers/mfd/vexpress-config.c | 277 +++++++++++++++++++++ include/linux/vexpress.h | 85 +++++++ 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/vexpress-config.c create mode 100644 include/linux/vexpress.h (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index ec8b50cbb2e8..5d9996b9eabf 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -11,6 +11,10 @@ the motherboard file using a /include/ directive. As the motherboard can be initialized in one of two different configurations ("memory maps"), care must be taken to include the correct one. + +Root node +--------- + Required properties in the root node: - compatible value: compatible = "arm,vexpress,", "arm,vexpress"; @@ -45,6 +49,10 @@ Optional properties in the root node: - Coretile Express A9x4 (V2P-CA9) HBI-0225: arm,hbi = <0x225>; + +CPU nodes +--------- + Top-level standard "cpus" node is required. It must contain a node with device_type = "cpu" property for every available core, eg.: @@ -59,6 +67,52 @@ with device_type = "cpu" property for every available core, eg.: }; }; + +Configuration infrastructure +---------------------------- + +The platform has an elaborated configuration system, consisting of +microcontrollers residing on the mother- and daughterboards known +as Motherboard/Daughterboard Configuration Controller (MCC and DCC). +The controllers are responsible for the platform initialization +(reset generation, flash programming, FPGA bitfiles loading etc.) +but also control clock generators, voltage regulators, gather +environmental data like temperature, power consumption etc. Even +the video output switch (FPGA) is controlled that way. + +Nodes describing devices controlled by this infrastructure should +point at the bridge device node: +- bridge phandle: + arm,vexpress,config-bridge = ; +This property can be also defined in a parent node (eg. for a DCC) +and is effective for all children. + + +Platform topology +----------------- + +As Versatile Express can be configured in number of physically +different setups, the device tree should describe platform topology. +Root node and main motherboard node must define the following +property, describing physical location of the children nodes: +- site number: + arm,vexpress,site = ; + where 0 means motherboard, 1 or 2 are daugtherboard sites, + 0xf means "master" site (site containing main CPU tile) +- when daughterboards are stacked on one site, their position + in the stack be be described with: + arm,vexpress,position = ; +- when describing tiles consisting more than one DCC, its number + can be described with: + arm,vexpress,dcc = ; + +Any of the numbers above defaults to zero if not defined in +the node or any of its parent. + + +Motherboard +----------- + The motherboard description file provides a single "motherboard" node using 2 address cells corresponding to the Static Memory Bus used between the motherboard and the tile. The first cell defines the Chip @@ -96,13 +150,16 @@ The tile description must define "ranges", "interrupt-map-mask" and "interrupt-map" properties to translate the motherboard's address and interrupt space into one used by the tile's processor. -Abbreviated example: + +Example of a VE tile description (simplified) +--------------------------------------------- /dts-v1/; / { model = "V2P-CA5s"; arm,hbi = <0x225>; + arm,vexpress,site = <0xf>; compatible = "arm,vexpress-v2p-ca5s", "arm,vexpress"; interrupt-parent = <&gic>; #address-cells = <1>; @@ -134,6 +191,15 @@ Abbreviated example: <0x2c000100 0x100>; }; + dcc { + compatible = "simple-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + }; + }; + motherboard { /* CS0 is visible at 0x08000000 */ ranges = <0 0 0x08000000 0x04000000>; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a310..637bcdf8ce77 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1070,3 +1070,9 @@ config MCP_UCB1200_TS depends on MCP_UCB1200 && INPUT endmenu + +config VEXPRESS_CONFIG + bool + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d8ccb630ddb0..e807164f68da 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,3 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c new file mode 100644 index 000000000000..fae15d880758 --- /dev/null +++ b/drivers/mfd/vexpress-config.c @@ -0,0 +1,277 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2012 ARM Limited + */ + +#define pr_fmt(fmt) "vexpress-config: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VEXPRESS_CONFIG_MAX_BRIDGES 2 + +struct vexpress_config_bridge { + struct device_node *node; + struct vexpress_config_bridge_info *info; + struct list_head transactions; + spinlock_t transactions_lock; +} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; + +static DECLARE_BITMAP(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); +static DEFINE_MUTEX(vexpress_config_bridges_mutex); + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info) +{ + struct vexpress_config_bridge *bridge; + int i; + + pr_debug("Registering bridge '%s'\n", info->name); + + mutex_lock(&vexpress_config_bridges_mutex); + i = find_first_zero_bit(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); + if (i >= ARRAY_SIZE(vexpress_config_bridges)) { + pr_err("Can't register more bridges!\n"); + mutex_unlock(&vexpress_config_bridges_mutex); + return NULL; + } + __set_bit(i, vexpress_config_bridges_map); + bridge = &vexpress_config_bridges[i]; + + bridge->node = node; + bridge->info = info; + INIT_LIST_HEAD(&bridge->transactions); + spin_lock_init(&bridge->transactions_lock); + + mutex_unlock(&vexpress_config_bridges_mutex); + + return bridge; +} + +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) +{ + struct vexpress_config_bridge __bridge = *bridge; + int i; + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) + if (&vexpress_config_bridges[i] == bridge) + __clear_bit(i, vexpress_config_bridges_map); + mutex_unlock(&vexpress_config_bridges_mutex); + + WARN_ON(!list_empty(&__bridge.transactions)); + while (!list_empty(&__bridge.transactions)) + cpu_relax(); +} + + +struct vexpress_config_func { + struct vexpress_config_bridge *bridge; + void *func; +}; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node) +{ + struct device_node *bridge_node; + struct vexpress_config_func *func; + int i; + + if (WARN_ON(dev && node && dev->of_node != node)) + return NULL; + if (dev && !node) + node = dev->of_node; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return NULL; + + bridge_node = of_node_get(node); + while (bridge_node) { + const __be32 *prop = of_get_property(bridge_node, + "arm,vexpress,config-bridge", NULL); + + if (prop) { + bridge_node = of_find_node_by_phandle( + be32_to_cpup(prop)); + break; + } + + bridge_node = of_get_next_parent(bridge_node); + } + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { + struct vexpress_config_bridge *bridge = + &vexpress_config_bridges[i]; + + if (test_bit(i, vexpress_config_bridges_map) && + bridge->node == bridge_node) { + func->bridge = bridge; + func->func = bridge->info->func_get(dev, node); + break; + } + } + mutex_unlock(&vexpress_config_bridges_mutex); + + if (!func->func) { + of_node_put(node); + kfree(func); + return NULL; + } + + return func; +} + +void vexpress_config_func_put(struct vexpress_config_func *func) +{ + func->bridge->info->func_put(func->func); + of_node_put(func->bridge->node); + kfree(func); +} + + +struct vexpress_config_trans { + struct vexpress_config_func *func; + int offset; + bool write; + u32 *data; + int status; + struct completion completion; + struct list_head list; +}; + +static void vexpress_config_dump_trans(const char *what, + struct vexpress_config_trans *trans) +{ + pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", + what, trans->write ? "write" : "read", trans, + trans->func->func, trans->offset, + trans->data ? *trans->data : 0, trans->status); +} + +static int vexpress_config_schedule(struct vexpress_config_trans *trans) +{ + int status; + struct vexpress_config_bridge *bridge = trans->func->bridge; + unsigned long flags; + + init_completion(&trans->completion); + trans->status = -EFAULT; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + vexpress_config_dump_trans("Executing", trans); + + if (list_empty(&bridge->transactions)) + status = bridge->info->func_exec(trans->func->func, + trans->offset, trans->write, trans->data); + else + status = VEXPRESS_CONFIG_STATUS_WAIT; + + switch (status) { + case VEXPRESS_CONFIG_STATUS_DONE: + vexpress_config_dump_trans("Finished", trans); + trans->status = status; + break; + case VEXPRESS_CONFIG_STATUS_WAIT: + list_add_tail(&trans->list, &bridge->transactions); + break; + } + + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + return status; +} + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status) +{ + struct vexpress_config_trans *trans; + unsigned long flags; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + trans = list_first_entry(&bridge->transactions, + struct vexpress_config_trans, list); + vexpress_config_dump_trans("Completed", trans); + + trans->status = status; + list_del(&trans->list); + + if (!list_empty(&bridge->transactions)) { + vexpress_config_dump_trans("Pending", trans); + + bridge->info->func_exec(trans->func->func, trans->offset, + trans->write, trans->data); + } + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + complete(&trans->completion); +} + +int vexpress_config_wait(struct vexpress_config_trans *trans) +{ + wait_for_completion(&trans->completion); + + return trans->status; +} + + +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = false, + .data = data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_read); + +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = true, + .data = &data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_write); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h new file mode 100644 index 000000000000..c2d877a7b691 --- /dev/null +++ b/include/linux/vexpress.h @@ -0,0 +1,85 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2012 ARM Limited + */ + +#ifndef _LINUX_VEXPRESS_H +#define _LINUX_VEXPRESS_H + +#include + +#define VEXPRESS_SITE_MB 0 +#define VEXPRESS_SITE_DB1 1 +#define VEXPRESS_SITE_DB2 2 +#define VEXPRESS_SITE_MASTER 0xf + +#define VEXPRESS_CONFIG_STATUS_DONE 0 +#define VEXPRESS_CONFIG_STATUS_WAIT 1 + +/* Config bridge API */ + +/** + * struct vexpress_config_bridge_info - description of the platform + * configuration infrastructure bridge. + * + * @name: Bridge name + * + * @func_get: Obtains pointer to a configuration function for a given + * device or a Device Tree node, to be used with @func_put + * and @func_exec. The node pointer should take precedence + * over device pointer when both are passed. + * + * @func_put: Tells the bridge that the function will not be used any + * more, so all allocated resources can be released. + * + * @func_exec: Executes a configuration function read or write operation. + * The offset selects a 32 bit word of the value accessed. + * Must return VEXPRESS_CONFIG_STATUS_DONE when operation + * is finished immediately, VEXPRESS_CONFIG_STATUS_WAIT when + * will be completed in some time or negative value in case + * of error. + */ +struct vexpress_config_bridge_info { + const char *name; + void *(*func_get)(struct device *dev, struct device_node *node); + void (*func_put)(void *func); + int (*func_exec)(void *func, int offset, bool write, u32 *data); +}; + +struct vexpress_config_bridge; + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info); +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge); + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status); + +/* Config function API */ + +struct vexpress_config_func; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node); +#define vexpress_config_func_get_by_dev(dev) \ + __vexpress_config_func_get(dev, NULL) +#define vexpress_config_func_get_by_node(node) \ + __vexpress_config_func_get(NULL, node) +void vexpress_config_func_put(struct vexpress_config_func *func); + +/* Both may sleep! */ +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data); +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data); + +#endif -- cgit v1.2.3 From 88e0abcd7a8171ca7af3402373e7bd81fe9b6754 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 18 Sep 2012 12:24:57 +0100 Subject: mfd: Versatile Express system registers driver This is a platform driver for Versatile Express' "system register" block. It's a random collection of registers providing the following functionality: - low level platform functions like board ID access; in order to use those, the driver must be initialized early, either statically or based on the DT - config bus bridge via "system control" interface; as the response from the controller does not generate interrupt (yet), the status register is periodically polled using a timer - pseudo GPIO lines providing MMC card status and Flash WP# signal control - LED interface for a set of 8 LEDs on the motherboard, with "heartbeat", "mmc0" and "cpu0" to "cpu5" as default triggers Signed-off-by: Pawel Moll --- .../devicetree/bindings/arm/vexpress-sysreg.txt | 50 ++ drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-sysreg.c | 552 +++++++++++++++++++++ include/linux/vexpress.h | 25 + 4 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/vexpress-sysreg.txt create mode 100644 drivers/mfd/vexpress-sysreg.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt new file mode 100644 index 000000000000..9cf3f25544c7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt @@ -0,0 +1,50 @@ +ARM Versatile Express system registers +-------------------------------------- + +This is a system control registers block, providing multiple low level +platform functions like board detection and identification, software +interrupt generation, MMC and NOR Flash control etc. + +Required node properties: +- compatible value : = "arm,vexpress,sysreg"; +- reg : physical base address and the size of the registers window +- gpio-controller : specifies that the node is a GPIO controller +- #gpio-cells : size of the GPIO specifier, should be 2: + - first cell is the pseudo-GPIO line number: + 0 - MMC CARDIN + 1 - MMC WPROT + 2 - NOR FLASH WPn + - second cell can take standard GPIO flags (currently ignored). + +Example: + v2m_sysreg: sysreg@10000000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x10000000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + +This block also can also act a bridge to the platform's configuration +bus via "system control" interface, addressing devices with site number, +position in the board stack, config controller, function and device +numbers - see motherboard's TRM for more details. + +The node describing a config device must refer to the sysreg node via +"arm,vexpress,config-bridge" phandle (can be also defined in the node's +parent) and relies on the board topology properties - see main vexpress +node documentation for more details. It must must also define the +following property: +- arm,vexpress-sysreg,func : must contain two cells: + - first cell defines function number (eg. 1 for clock generator, + 2 for voltage regulators etc.) + - device number (eg. osc 0, osc 1 etc.) + +Example: + mcc { + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + }; + }; diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e807164f68da..296817c6c06f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,4 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c new file mode 100644 index 000000000000..059d6b17b14a --- /dev/null +++ b/drivers/mfd/vexpress-sysreg.c @@ -0,0 +1,552 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2012 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYS_ID 0x000 +#define SYS_SW 0x004 +#define SYS_LED 0x008 +#define SYS_100HZ 0x024 +#define SYS_FLAGS 0x030 +#define SYS_FLAGSSET 0x030 +#define SYS_FLAGSCLR 0x034 +#define SYS_NVFLAGS 0x038 +#define SYS_NVFLAGSSET 0x038 +#define SYS_NVFLAGSCLR 0x03c +#define SYS_MCI 0x048 +#define SYS_FLASH 0x04c +#define SYS_CFGSW 0x058 +#define SYS_24MHZ 0x05c +#define SYS_MISC 0x060 +#define SYS_DMA 0x064 +#define SYS_PROCID0 0x084 +#define SYS_PROCID1 0x088 +#define SYS_CFGDATA 0x0a0 +#define SYS_CFGCTRL 0x0a4 +#define SYS_CFGSTAT 0x0a8 + +#define SYS_HBI_MASK 0xfff +#define SYS_ID_HBI_SHIFT 16 +#define SYS_PROCIDx_HBI_SHIFT 0 + +#define SYS_MCI_CARDIN (1 << 0) +#define SYS_MCI_WPROT (1 << 1) + +#define SYS_FLASH_WPn (1 << 0) + +#define SYS_MISC_MASTERSITE (1 << 14) + +#define SYS_CFGCTRL_START (1 << 31) +#define SYS_CFGCTRL_WRITE (1 << 30) +#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) +#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) +#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) +#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) +#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) + +#define SYS_CFGSTAT_ERR (1 << 1) +#define SYS_CFGSTAT_COMPLETE (1 << 0) + + +static void __iomem *vexpress_sysreg_base; +static struct device *vexpress_sysreg_dev; +static int vexpress_master_site; + + +void vexpress_flags_set(u32 data) +{ + writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); + writel(data, vexpress_sysreg_base + SYS_FLAGSSET); +} + +u32 vexpress_get_procid(int site) +{ + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? + SYS_PROCID0 : SYS_PROCID1)); +} + +u32 vexpress_get_hbi(int site) +{ + u32 id; + + switch (site) { + case VEXPRESS_SITE_MB: + id = readl(vexpress_sysreg_base + SYS_ID); + return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; + case VEXPRESS_SITE_MASTER: + case VEXPRESS_SITE_DB1: + case VEXPRESS_SITE_DB2: + id = vexpress_get_procid(site); + return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; + } + + return ~0; +} + +void __iomem *vexpress_get_24mhz_clock_base(void) +{ + return vexpress_sysreg_base + SYS_24MHZ; +} + + +static void vexpress_sysreg_find_prop(struct device_node *node, + const char *name, u32 *val) +{ + of_node_get(node); + while (node) { + if (of_property_read_u32(node, name, val) == 0) { + of_node_put(node); + return; + } + node = of_get_next_parent(node); + } +} + +unsigned __vexpress_get_site(struct device *dev, struct device_node *node) +{ + u32 site = 0; + + WARN_ON(dev && node && dev->of_node != node); + if (dev && !node) + node = dev->of_node; + + if (node) { + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) + site = pdev->resource[0].start; + } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { + site = VEXPRESS_SITE_MASTER; + } + + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return site; +} + + +struct vexpress_sysreg_config_func { + u32 template; + u32 device; +}; + +static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; +static struct timer_list vexpress_sysreg_config_timer; +static u32 *vexpress_sysreg_config_data; +static int vexpress_sysreg_config_tries; + +static void *vexpress_sysreg_config_func_get(struct device *dev, + struct device_node *node) +{ + struct vexpress_sysreg_config_func *config_func; + u32 site; + u32 position = 0; + u32 dcc = 0; + u32 func_device[2]; + int err = -EFAULT; + + if (node) { + of_node_get(node); + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + vexpress_sysreg_find_prop(node, "arm,vexpress,position", + &position); + vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); + err = of_property_read_u32_array(node, + "arm,vexpress-sysreg,func", func_device, + ARRAY_SIZE(func_device)); + of_node_put(node); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) { + site = pdev->resource[0].start; + func_device[0] = pdev->resource[0].end; + func_device[1] = pdev->id; + err = 0; + } + } + if (err) + return NULL; + + config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); + if (!config_func) + return NULL; + + config_func->template = SYS_CFGCTRL_DCC(dcc); + config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); + config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? + vexpress_master_site : site); + config_func->template |= SYS_CFGCTRL_POSITION(position); + config_func->device |= func_device[1]; + + dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, + config_func->template, config_func->device); + + return config_func; +} + +static void vexpress_sysreg_config_func_put(void *func) +{ + kfree(func); +} + +static int vexpress_sysreg_config_func_exec(void *func, int offset, + bool write, u32 *data) +{ + int status; + struct vexpress_sysreg_config_func *config_func = func; + u32 command; + + if (WARN_ON(!vexpress_sysreg_base)) + return -ENOENT; + + command = readl(vexpress_sysreg_base + SYS_CFGCTRL); + if (WARN_ON(command & SYS_CFGCTRL_START)) + return -EBUSY; + + command = SYS_CFGCTRL_START; + command |= write ? SYS_CFGCTRL_WRITE : 0; + command |= config_func->template; + command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); + + /* Use a canary for reads */ + if (!write) + *data = 0xdeadbeef; + + dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", + command, *data); + writel(*data, vexpress_sysreg_base + SYS_CFGDATA); + writel(0, vexpress_sysreg_base + SYS_CFGSTAT); + writel(command, vexpress_sysreg_base + SYS_CFGCTRL); + mb(); + + if (vexpress_sysreg_dev) { + /* Schedule completion check */ + if (!write) + vexpress_sysreg_config_data = data; + vexpress_sysreg_config_tries = 100; + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(100)); + status = VEXPRESS_CONFIG_STATUS_WAIT; + } else { + /* Early execution, no timer available, have to spin */ + u32 cfgstat; + + do { + cpu_relax(); + cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + } while (!cfgstat); + + if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) + *data = readl(vexpress_sysreg_base + SYS_CFGDATA); + status = VEXPRESS_CONFIG_STATUS_DONE; + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + } + + return status; +} + +struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { + .name = "vexpress-sysreg", + .func_get = vexpress_sysreg_config_func_get, + .func_put = vexpress_sysreg_config_func_put, + .func_exec = vexpress_sysreg_config_func_exec, +}; + +static void vexpress_sysreg_config_complete(unsigned long data) +{ + int status = VEXPRESS_CONFIG_STATUS_DONE; + u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + if (!vexpress_sysreg_config_tries--) + status = -ETIMEDOUT; + + if (status < 0) { + dev_err(vexpress_sysreg_dev, "error %d\n", status); + } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(50)); + return; + } + + if (vexpress_sysreg_config_data) { + *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + + SYS_CFGDATA); + dev_dbg(vexpress_sysreg_dev, "read data %x\n", + *vexpress_sysreg_config_data); + vexpress_sysreg_config_data = NULL; + } + + vexpress_config_complete(vexpress_sysreg_config_bridge, status); +} + + +void __init vexpress_sysreg_early_init(void __iomem *base) +{ + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); + + if (node) + base = of_iomap(node, 0); + + if (WARN_ON(!base)) + return; + + vexpress_sysreg_base = base; + + if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + vexpress_master_site = VEXPRESS_SITE_DB2; + else + vexpress_master_site = VEXPRESS_SITE_DB1; + + vexpress_sysreg_config_bridge = vexpress_config_bridge_register( + node, &vexpress_sysreg_config_bridge_info); + WARN_ON(!vexpress_sysreg_config_bridge); +} + +void __init vexpress_sysreg_of_early_init(void) +{ + vexpress_sysreg_early_init(NULL); +} + + +static struct vexpress_sysreg_gpio { + unsigned long reg; + u32 value; +} vexpress_sysreg_gpios[] = { + [VEXPRESS_GPIO_MMC_CARDIN] = { + .reg = SYS_MCI, + .value = SYS_MCI_CARDIN, + }, + [VEXPRESS_GPIO_MMC_WPROT] = { + .reg = SYS_MCI, + .value = SYS_MCI_WPROT, + }, + [VEXPRESS_GPIO_FLASH_WPn] = { + .reg = SYS_FLASH, + .value = SYS_FLASH_WPn, + }, +}; + +static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return 0; +} + +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return 0; +} + +static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + return !!(reg_value & gpio->value); +} + +static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + if (value) + reg_value |= gpio->value; + else + reg_value &= ~gpio->value; + + writel(reg_value, vexpress_sysreg_base + gpio->reg); +} + +static struct gpio_chip vexpress_sysreg_gpio_chip = { + .label = "vexpress-sysreg", + .direction_input = vexpress_sysreg_gpio_direction_input, + .direction_output = vexpress_sysreg_gpio_direction_output, + .get = vexpress_sysreg_gpio_get, + .set = vexpress_sysreg_gpio_set, + .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), + .base = 0, +}; + + +static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); +} + +DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); + +static int __devinit vexpress_sysreg_probe(struct platform_device *pdev) +{ + int err; + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 0); + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to request memory region!\n"); + return -EBUSY; + } + + if (!vexpress_sysreg_base) + vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!vexpress_sysreg_base) { + dev_err(&pdev->dev, "Failed to obtain base address!\n"); + return -EFAULT; + } + + setup_timer(&vexpress_sysreg_config_timer, + vexpress_sysreg_config_complete, 0); + + vexpress_sysreg_gpio_chip.dev = &pdev->dev; + err = gpiochip_add(&vexpress_sysreg_gpio_chip); + if (err) { + vexpress_config_bridge_unregister( + vexpress_sysreg_config_bridge); + dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", + err); + return err; + } + + vexpress_sysreg_dev = &pdev->dev; + + device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); + + return 0; +} + +static const struct of_device_id vexpress_sysreg_match[] = { + { .compatible = "arm,vexpress-sysreg", }, + {}, +}; + +static struct platform_driver vexpress_sysreg_driver = { + .driver = { + .name = "vexpress-sysreg", + .of_match_table = vexpress_sysreg_match, + }, + .probe = vexpress_sysreg_probe, +}; + +static int __init vexpress_sysreg_init(void) +{ + return platform_driver_register(&vexpress_sysreg_driver); +} +core_initcall(vexpress_sysreg_init); + + +#if defined(CONFIG_LEDS_CLASS) + +struct vexpress_sysreg_led { + u32 mask; + struct led_classdev cdev; +} vexpress_sysreg_leds[] = { + { .mask = 1 << 0, .cdev.name = "v2m:green:user1", + .cdev.default_trigger = "heartbeat", }, + { .mask = 1 << 1, .cdev.name = "v2m:green:user2", + .cdev.default_trigger = "mmc0", }, + { .mask = 1 << 2, .cdev.name = "v2m:green:user3", + .cdev.default_trigger = "cpu0", }, + { .mask = 1 << 3, .cdev.name = "v2m:green:user4", + .cdev.default_trigger = "cpu1", }, + { .mask = 1 << 4, .cdev.name = "v2m:green:user5", + .cdev.default_trigger = "cpu2", }, + { .mask = 1 << 5, .cdev.name = "v2m:green:user6", + .cdev.default_trigger = "cpu3", }, + { .mask = 1 << 6, .cdev.name = "v2m:green:user7", + .cdev.default_trigger = "cpu4", }, + { .mask = 1 << 7, .cdev.name = "v2m:green:user8", + .cdev.default_trigger = "cpu5", }, +}; + +static DEFINE_SPINLOCK(vexpress_sysreg_leds_lock); + +static void vexpress_sysreg_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct vexpress_sysreg_led *led = container_of(cdev, + struct vexpress_sysreg_led, cdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&vexpress_sysreg_leds_lock, flags); + + val = readl(vexpress_sysreg_base + SYS_LED); + if (brightness == LED_OFF) + val &= ~led->mask; + else + val |= led->mask; + writel(val, vexpress_sysreg_base + SYS_LED); + + spin_unlock_irqrestore(&vexpress_sysreg_leds_lock, flags); +} + +static int __init vexpress_sysreg_init_leds(void) +{ + struct vexpress_sysreg_led *led; + int i; + + /* Clear all user LEDs */ + writel(0, vexpress_sysreg_base + SYS_LED); + + for (i = 0, led = vexpress_sysreg_leds; + i < ARRAY_SIZE(vexpress_sysreg_leds); i++, led++) { + int err; + + led->cdev.brightness_set = vexpress_sysreg_led_brightness_set; + err = led_classdev_register(vexpress_sysreg_dev, &led->cdev); + if (err) { + dev_err(vexpress_sysreg_dev, + "Failed to register LED %d! (%d)\n", + i, err); + while (led--, i--) + led_classdev_unregister(&led->cdev); + return err; + } + } + + return 0; +} +device_initcall(vexpress_sysreg_init_leds); + +#endif diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c2d877a7b691..09c81d7a22de 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -24,6 +24,17 @@ #define VEXPRESS_CONFIG_STATUS_DONE 0 #define VEXPRESS_CONFIG_STATUS_WAIT 1 +#define VEXPRESS_GPIO_MMC_CARDIN 0 +#define VEXPRESS_GPIO_MMC_WPROT 1 +#define VEXPRESS_GPIO_FLASH_WPn 2 + +#define VEXPRESS_RES_FUNC(_site, _func) \ +{ \ + .start = (_site), \ + .end = (_func), \ + .flags = IORESOURCE_BUS, \ +} + /* Config bridge API */ /** @@ -82,4 +93,18 @@ int vexpress_config_read(struct vexpress_config_func *func, int offset, int vexpress_config_write(struct vexpress_config_func *func, int offset, u32 data); +/* Platform control */ + +u32 vexpress_get_procid(int site); +u32 vexpress_get_hbi(int site); +void *vexpress_get_24mhz_clock_base(void); +void vexpress_flags_set(u32 data); + +#define vexpress_get_site_by_node(node) __vexpress_get_site(NULL, node) +#define vexpress_get_site_by_dev(dev) __vexpress_get_site(dev, NULL) +unsigned __vexpress_get_site(struct device *dev, struct device_node *node); + +void vexpress_sysreg_early_init(void __iomem *base); +void vexpress_sysreg_of_early_init(void); + #endif -- cgit v1.2.3 From be6a98d3f00c292d347465d96acbec9d8c2783cf Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 12 Oct 2012 12:45:34 -0500 Subject: cpuidle: add Calxeda SOC idle support Add support for core powergating on Calxeda platforms. Initially, this supports ECX-1000 (highbank), but support will be added for ECX-2000 later. Signed-off-by: Rob Herring Cc: Len Brown Cc: "Rafael J. Wysocki" --- drivers/cpuidle/Kconfig | 10 +++ drivers/cpuidle/Makefile | 2 + drivers/cpuidle/cpuidle-calxeda.c | 161 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/cpuidle/cpuidle-calxeda.c (limited to 'drivers') diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index a76b689e553b..d2d007b0651a 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -21,3 +21,13 @@ config CPU_IDLE_GOV_MENU config ARCH_NEEDS_CPU_IDLE_COUPLED def_bool n + +if CPU_IDLE + +config CPU_IDLE_CALXEDA + bool "CPU Idle Driver for Calxeda processors" + depends on ARCH_HIGHBANK + help + Select this to enable cpuidle on Calxeda processors. + +endif diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 38c8f69f30cf..03ee87482c71 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -4,3 +4,5 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o + +obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o diff --git a/drivers/cpuidle/cpuidle-calxeda.c b/drivers/cpuidle/cpuidle-calxeda.c new file mode 100644 index 000000000000..e1aab38c5a8d --- /dev/null +++ b/drivers/cpuidle/cpuidle-calxeda.c @@ -0,0 +1,161 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * Based on arch/arm/plat-mxc/cpuidle.c: + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void highbank_set_cpu_jump(int cpu, void *jump_addr); +extern void *scu_base_addr; + +static struct cpuidle_device __percpu *calxeda_idle_cpuidle_devices; + +static inline unsigned int get_auxcr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val) : : "cc"); + return val; +} + +static inline void set_auxcr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR" + : : "r" (val) : "cc"); + isb(); +} + +static noinline void calxeda_idle_restore(void) +{ + set_cr(get_cr() | CR_C); + set_auxcr(get_auxcr() | 0x40); + scu_power_mode(scu_base_addr, SCU_PM_NORMAL); +} + +static int calxeda_idle_finish(unsigned long val) +{ + /* Already flushed cache, but do it again as the outer cache functions + * dirty the cache with spinlocks */ + flush_cache_all(); + + set_auxcr(get_auxcr() & ~0x40); + set_cr(get_cr() & ~CR_C); + + scu_power_mode(scu_base_addr, SCU_PM_DORMANT); + + cpu_do_idle(); + + /* Restore things if we didn't enter power-gating */ + calxeda_idle_restore(); + return 1; +} + +static int calxeda_pwrdown_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + highbank_set_cpu_jump(smp_processor_id(), cpu_resume); + cpu_suspend(0, calxeda_idle_finish); + return index; +} + +static void calxeda_idle_cpuidle_devices_uninit(void) +{ + int i; + struct cpuidle_device *dev; + + for_each_possible_cpu(i) { + dev = per_cpu_ptr(calxeda_idle_cpuidle_devices, i); + cpuidle_unregister_device(dev); + } + + free_percpu(calxeda_idle_cpuidle_devices); +} + +static struct cpuidle_driver calxeda_idle_driver = { + .name = "calxeda_idle", + .en_core_tk_irqen = 1, + .states = { + ARM_CPUIDLE_WFI_STATE, + { + .name = "PG", + .desc = "Power Gate", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 30, + .power_usage = 50, + .target_residency = 200, + .enter = calxeda_pwrdown_idle, + }, + }, + .state_count = 2, +}; + +static int __init calxeda_cpuidle_init(void) +{ + int cpu_id; + int ret; + struct cpuidle_device *dev; + struct cpuidle_driver *drv = &calxeda_idle_driver; + + if (!of_machine_is_compatible("calxeda,highbank")) + return -ENODEV; + + ret = cpuidle_register_driver(drv); + if (ret) + return ret; + + calxeda_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device); + if (calxeda_idle_cpuidle_devices == NULL) { + ret = -ENOMEM; + goto unregister_drv; + } + + /* initialize state data for each cpuidle_device */ + for_each_possible_cpu(cpu_id) { + dev = per_cpu_ptr(calxeda_idle_cpuidle_devices, cpu_id); + dev->cpu = cpu_id; + dev->state_count = drv->state_count; + + ret = cpuidle_register_device(dev); + if (ret) { + pr_err("Failed to register cpu %u, error: %d\n", + cpu_id, ret); + goto uninit; + } + } + + return 0; + +uninit: + calxeda_idle_cpuidle_devices_uninit(); +unregister_drv: + cpuidle_unregister_driver(drv); + return ret; +} +module_init(calxeda_cpuidle_init); -- cgit v1.2.3 From 9d626eccb1de90a310f3fb9bc5e8803706be1a95 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Oct 2012 20:06:55 -0700 Subject: sh: clkfwk: add sh_clk_fsidiv_register() This patch adds sh_clk_fsidiv_register() to share FSI-DIV clock code Signed-off-by: Kuninori Morimoto Acked-by: Paul Mundt Signed-off-by: Simon Horman --- drivers/sh/clk/cpg.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sh_clk.h | 9 ++++++ 2 files changed, 95 insertions(+) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 07e9fb4f8041..b3dc44146ca0 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -361,3 +361,89 @@ int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div4_reparent_clk_ops); } + +/* FSI-DIV */ +static unsigned long fsidiv_recalc(struct clk *clk) +{ + u32 value; + + value = __raw_readl(clk->mapping->base); + + value >>= 16; + if (value < 2) + return clk->parent->rate; + + return clk->parent->rate / value; +} + +static long fsidiv_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_div_range_round(clk, 1, 0xffff, rate); +} + +static void fsidiv_disable(struct clk *clk) +{ + __raw_writel(0, clk->mapping->base); +} + +static int fsidiv_enable(struct clk *clk) +{ + u32 value; + + value = __raw_readl(clk->mapping->base) >> 16; + if (value < 2) + return 0; + + __raw_writel((value << 16) | 0x3, clk->mapping->base); + + return 0; +} + +static int fsidiv_set_rate(struct clk *clk, unsigned long rate) +{ + u32 val; + int idx; + + idx = (clk->parent->rate / rate) & 0xffff; + if (idx < 2) + __raw_writel(0, clk->mapping->base); + else + __raw_writel(idx << 16, clk->mapping->base); + + return 0; +} + +static struct sh_clk_ops fsidiv_clk_ops = { + .recalc = fsidiv_recalc, + .round_rate = fsidiv_round_rate, + .set_rate = fsidiv_set_rate, + .enable = fsidiv_enable, + .disable = fsidiv_disable, +}; + +int __init sh_clk_fsidiv_register(struct clk *clks, int nr) +{ + struct clk_mapping *map; + int i; + + for (i = 0; i < nr; i++) { + + map = kzalloc(sizeof(struct clk_mapping), GFP_KERNEL); + if (!map) { + pr_err("%s: unable to alloc memory\n", __func__); + return -ENOMEM; + } + + /* clks[i].enable_reg came from SH_CLK_FSIDIV() */ + map->phys = (phys_addr_t)clks[i].enable_reg; + map->len = 8; + + clks[i].enable_reg = 0; /* remove .enable_reg */ + clks[i].ops = &fsidiv_clk_ops; + clks[i].mapping = map; + + clk_register(&clks[i]); + } + + return 0; +} diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 50910913b268..60c72395ec6b 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -199,4 +199,13 @@ int sh_clk_div6_reparent_register(struct clk *clks, int nr); #define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk } #define CLKDEV_ICK_ID(_cid, _did, _clk) { .con_id = _cid, .dev_id = _did, .clk = _clk } +/* .enable_reg will be updated to .mapping on sh_clk_fsidiv_register() */ +#define SH_CLK_FSIDIV(_reg, _parent) \ +{ \ + .enable_reg = (void __iomem *)_reg, \ + .parent = _parent, \ +} + +int sh_clk_fsidiv_register(struct clk *clks, int nr); + #endif /* __SH_CLOCK_H */ -- cgit v1.2.3 From 376aaac1837af8ed6c1014958396322c44306cbf Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 30 Oct 2012 10:03:26 -0200 Subject: mx2_camera: Fix regression caused by clock conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since mx27 transitioned to the commmon clock framework in 3.5, the correct way to acquire the csi clock is to get csi_ahb and csi_per clocks separately. By not doing so the camera sensor does not probe correctly: soc-camera-pdrv soc-camera-pdrv.0: Probing soc-camera-pdrv.0 mx2-camera mx2-camera.0: Camera driver attached to camera 0 ov2640 0-0030: Product ID error fb:fb mx2-camera mx2-camera.0: Camera driver detached from camera 0 mx2-camera mx2-camera.0: MX2 Camera (CSI) driver probed, clock frequency: 66500000 Adapt the mx2_camera driver to the new clock framework and make it functional again. Tested-by: Gaëtan Carlier Tested-by: Javier Martin Signed-off-by: Fabio Estevam Signed-off-by: Sascha Hauer Acked-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 39 +++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index e575ae82771d..558f6a3e510d 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -278,7 +278,8 @@ struct mx2_camera_dev { struct device *dev; struct soc_camera_host soc_host; struct soc_camera_device *icd; - struct clk *clk_csi, *clk_emma_ahb, *clk_emma_ipg; + struct clk *clk_emma_ahb, *clk_emma_ipg; + struct clk *clk_csi_ahb, *clk_csi_per; void __iomem *base_csi, *base_emma; @@ -464,7 +465,8 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { unsigned long flags; - clk_disable_unprepare(pcdev->clk_csi); + clk_disable_unprepare(pcdev->clk_csi_ahb); + clk_disable_unprepare(pcdev->clk_csi_per); writel(0, pcdev->base_csi + CSICR1); if (is_imx27_camera(pcdev)) { writel(0, pcdev->base_emma + PRP_CNTL); @@ -492,10 +494,14 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) if (pcdev->icd) return -EBUSY; - ret = clk_prepare_enable(pcdev->clk_csi); + ret = clk_prepare_enable(pcdev->clk_csi_ahb); if (ret < 0) return ret; + ret = clk_prepare_enable(pcdev->clk_csi_per); + if (ret < 0) + goto exit_csi_ahb; + csicr1 = CSICR1_MCLKEN; if (is_imx27_camera(pcdev)) @@ -512,6 +518,11 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) icd->devnum); return 0; + +exit_csi_ahb: + clk_disable_unprepare(pcdev->clk_csi_ahb); + + return ret; } static void mx2_camera_remove_device(struct soc_camera_device *icd) @@ -1772,10 +1783,17 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) break; } - pcdev->clk_csi = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(pcdev->clk_csi)) { - dev_err(&pdev->dev, "Could not get csi clock\n"); - err = PTR_ERR(pcdev->clk_csi); + pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(pcdev->clk_csi_ahb)) { + dev_err(&pdev->dev, "Could not get csi ahb clock\n"); + err = PTR_ERR(pcdev->clk_csi_ahb); + goto exit; + } + + pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(pcdev->clk_csi_per)) { + dev_err(&pdev->dev, "Could not get csi per clock\n"); + err = PTR_ERR(pcdev->clk_csi_per); goto exit; } @@ -1785,12 +1803,13 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->platform_flags = pcdev->pdata->flags; - rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2); + rate = clk_round_rate(pcdev->clk_csi_per, + pcdev->pdata->clk * 2); if (rate <= 0) { err = -ENODEV; goto exit; } - err = clk_set_rate(pcdev->clk_csi, rate); + err = clk_set_rate(pcdev->clk_csi_per, rate); if (err < 0) goto exit; } @@ -1848,7 +1867,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) goto exit_free_emma; dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi)); + clk_get_rate(pcdev->clk_csi_per)); return 0; -- cgit v1.2.3 From 404525d5a7ecc847b5ac178dad96402f1e102ccc Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 16 Nov 2012 21:21:43 +0100 Subject: clk: sunxi: Add dummy fixed rate clock for Allwinner A1X SoCs Signed-off-by: Maxime Ripard Cc: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-sunxi.c | 30 ++++++++++++++++++++++++++++++ include/linux/clk/sunxi.h | 22 ++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 drivers/clk/clk-sunxi.c create mode 100644 include/linux/clk/sunxi.h (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 71a25b91de00..9c300a828ede 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -19,6 +19,7 @@ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o +obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c new file mode 100644 index 000000000000..0e831b584ba7 --- /dev/null +++ b/drivers/clk/clk-sunxi.c @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +static const __initconst struct of_device_id clk_match[] = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + {} +}; + +void __init sunxi_init_clocks(void) +{ + of_clk_init(clk_match); +} diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..e074fdd5a236 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +void __init sunxi_init_clocks(void); + +#endif -- cgit v1.2.3 From b2ac5d7549710173ea0217bf8c7b3f71da5220d4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 12 Nov 2012 15:07:50 +0100 Subject: clocksource: sunxi: Add Allwinner A1X Timer Driver Signed-off-by: Maxime Ripard CC: Thomas Gleixner CC: John Stultz --- .../bindings/timer/allwinner,sunxi-timer.txt | 17 +++ drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/sunxi_timer.c | 170 +++++++++++++++++++++ include/linux/sunxi_timer.h | 24 +++ 5 files changed, 215 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt create mode 100644 drivers/clocksource/sunxi_timer.c create mode 100644 include/linux/sunxi_timer.h (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt new file mode 100644 index 000000000000..0c7b64e95a61 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt @@ -0,0 +1,17 @@ +Allwinner A1X SoCs Timer Controller + +Required properties: + +- compatible : should be "allwinner,sunxi-timer" +- reg : Specifies base physical address and size of the registers. +- interrupts : The interrupt of the first timer +- clocks: phandle to the source clock (usually a 24 MHz fixed clock) + +Example: + +timer { + compatible = "allwinner,sunxi-timer"; + reg = <0x01c20c00 0x400>; + interrupts = <22>; + clocks = <&osc>; +}; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 6a78073c3808..a0985732f1e2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,6 +22,9 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool +config SUNXI_TIMER + bool + config CLKSRC_DBX500_PRCMU bool "Clocksource PRCMU Timer" depends on UX500_SOC_DB8500 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 603be366f762..36f06de4c5ab 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o +obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c new file mode 100644 index 000000000000..3c46434b64cb --- /dev/null +++ b/drivers/clocksource/sunxi_timer.c @@ -0,0 +1,170 @@ +/* + * Allwinner A1X SoCs timer handling. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * Based on code from + * Allwinner Technology Co., Ltd. + * Benn Huang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_CTL_REG 0x00 +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_IRQ_ST_REG 0x04 +#define TIMER0_CTL_REG 0x10 +#define TIMER0_CTL_ENABLE (1 << 0) +#define TIMER0_CTL_AUTORELOAD (1 << 1) +#define TIMER0_CTL_ONESHOT (1 << 7) +#define TIMER0_INTVAL_REG 0x14 +#define TIMER0_CNTVAL_REG 0x18 + +#define TIMER_SCAL 16 + +static void __iomem *timer_base; + +static void sunxi_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + u32 u = readl(timer_base + TIMER0_CTL_REG); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + u &= ~(TIMER0_CTL_ONESHOT); + writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + break; + + case CLOCK_EVT_MODE_ONESHOT: + writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + break; + } +} + +static int sunxi_clkevt_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + u32 u = readl(timer_base + TIMER0_CTL_REG); + writel(evt, timer_base + TIMER0_CNTVAL_REG); + writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, + timer_base + TIMER0_CTL_REG); + + return 0; +} + +static struct clock_event_device sunxi_clockevent = { + .name = "sunxi_tick", + .shift = 32, + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sunxi_clkevt_mode, + .set_next_event = sunxi_clkevt_next_event, +}; + + +static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + writel(0x1, timer_base + TIMER_IRQ_ST_REG); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction sunxi_timer_irq = { + .name = "sunxi_timer0", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = sunxi_timer_interrupt, + .dev_id = &sunxi_clockevent, +}; + +static struct of_device_id sunxi_timer_dt_ids[] = { + { .compatible = "allwinner,sunxi-timer" }, +}; + +static void __init sunxi_timer_init(void) +{ + struct device_node *node; + unsigned long rate = 0; + struct clk *clk; + int ret, irq; + u32 val; + + node = of_find_matching_node(NULL, sunxi_timer_dt_ids); + if (!node) + panic("No sunxi timer node"); + + timer_base = of_iomap(node, 0); + if (!timer_base) + panic("Can't map registers"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + sunxi_init_clocks(); + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) + panic("Can't get timer clock"); + + rate = clk_get_rate(clk); + + writel(rate / (TIMER_SCAL * HZ), + timer_base + TIMER0_INTVAL_REG); + + /* set clock source to HOSC, 16 pre-division */ + val = readl(timer_base + TIMER0_CTL_REG); + val &= ~(0x07 << 4); + val &= ~(0x03 << 2); + val |= (4 << 4) | (1 << 2); + writel(val, timer_base + TIMER0_CTL_REG); + + /* set mode to auto reload */ + val = readl(timer_base + TIMER0_CTL_REG); + writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + + ret = setup_irq(irq, &sunxi_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); + + /* Enable timer0 interrupt */ + val = readl(timer_base + TIMER_CTL_REG); + writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); + + sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL, + NSEC_PER_SEC, + sunxi_clockevent.shift); + sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff, + &sunxi_clockevent); + sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1, + &sunxi_clockevent); + sunxi_clockevent.cpumask = cpumask_of(0); + + clockevents_register_device(&sunxi_clockevent); +} + +struct sys_timer sunxi_timer = { + .init = sunxi_timer_init, +}; diff --git a/include/linux/sunxi_timer.h b/include/linux/sunxi_timer.h new file mode 100644 index 000000000000..b9165bba6e61 --- /dev/null +++ b/include/linux/sunxi_timer.h @@ -0,0 +1,24 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SUNXI_TIMER_H +#define __SUNXI_TIMER_H + +#include + +extern struct sys_timer sunxi_timer; + +#endif -- cgit v1.2.3 From afd24e146826cec0f46929263a0c874406a19cd8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 14 Nov 2012 09:59:14 +0100 Subject: irqchip: sunxi: Add irq controller driver Signed-off-by: Maxime Ripard CC: Thomas Gleixner --- .../interrupt-controller/allwinner,sunxi-ic.txt | 104 ++++++++++++++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-sunxi.c | 150 +++++++++++++++++++++ include/linux/irqchip/sunxi.h | 27 ++++ 4 files changed, 282 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt create mode 100644 drivers/irqchip/irq-sunxi.c create mode 100644 include/linux/irqchip/sunxi.h (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt new file mode 100644 index 000000000000..7f9fb85f5456 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt @@ -0,0 +1,104 @@ +Allwinner Sunxi Interrupt Controller + +Required properties: + +- compatible : should be "allwinner,sunxi-ic" +- reg : Specifies base physical address and size of the registers. +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +The interrupt sources are as follows: + +0: ENMI +1: UART0 +2: UART1 +3: UART2 +4: UART3 +5: IR0 +6: IR1 +7: I2C0 +8: I2C1 +9: I2C2 +10: SPI0 +11: SPI1 +12: SPI2 +13: SPDIF +14: AC97 +15: TS +16: I2S +17: UART4 +18: UART5 +19: UART6 +20: UART7 +21: KEYPAD +22: TIMER0 +23: TIMER1 +24: TIMER2 +25: TIMER3 +26: CAN +27: DMA +28: PIO +29: TOUCH_PANEL +30: AUDIO_CODEC +31: LRADC +32: SDMC0 +33: SDMC1 +34: SDMC2 +35: SDMC3 +36: MEMSTICK +37: NAND +38: USB0 +39: USB1 +40: USB2 +41: SCR +42: CSI0 +43: CSI1 +44: LCDCTRL0 +45: LCDCTRL1 +46: MP +47: DEFEBE0 +48: DEFEBE1 +49: PMU +50: SPI3 +51: TZASC +52: PATA +53: VE +54: SS +55: EMAC +56: SATA +57: GPS +58: HDMI +59: TVE +60: ACE +61: TVD +62: PS2_0 +63: PS2_1 +64: USB3 +65: USB4 +66: PLE_PFM +67: TIMER4 +68: TIMER5 +69: GPU_GP +70: GPU_GPMMU +71: GPU_PP0 +72: GPU_PPMMU0 +73: GPU_PMU +74: GPU_RSV0 +75: GPU_RSV1 +76: GPU_RSV2 +77: GPU_RSV3 +78: GPU_RSV4 +79: GPU_RSV5 +80: GPU_RSV6 +82: SYNC_TIMER0 +83: SYNC_TIMER1 + +Example: + +intc: interrupt-controller { + compatible = "allwinner,sunxi-ic"; + reg = <0x01c20400 0x400>; + interrupt-controller; + #interrupt-cells = <2>; +}; diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 054321db4350..2444d07544cd 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o diff --git a/drivers/irqchip/irq-sunxi.c b/drivers/irqchip/irq-sunxi.c new file mode 100644 index 000000000000..eef41a49acae --- /dev/null +++ b/drivers/irqchip/irq-sunxi.c @@ -0,0 +1,150 @@ +/* + * Allwinner A1X SoCs IRQ chip driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * Based on code from + * Allwinner Technology Co., Ltd. + * Benn Huang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include + +#define SUNXI_IRQ_VECTOR_REG 0x00 +#define SUNXI_IRQ_PROTECTION_REG 0x08 +#define SUNXI_IRQ_NMI_CTRL_REG 0x0c +#define SUNXI_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) +#define SUNXI_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) +#define SUNXI_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) +#define SUNXI_IRQ_MASK_REG(x) (0x50 + 0x4 * x) + +static void __iomem *sunxi_irq_base; +static struct irq_domain *sunxi_irq_domain; + +void sunxi_irq_ack(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); + writel(val | (1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); +} + +static void sunxi_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); + writel(val & ~(1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); +} + +static void sunxi_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); + writel(val | (1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); +} + +static struct irq_chip sunxi_irq_chip = { + .name = "sunxi_irq", + .irq_ack = sunxi_irq_ack, + .irq_mask = sunxi_irq_mask, + .irq_unmask = sunxi_irq_unmask, +}; + +static int sunxi_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &sunxi_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops sunxi_irq_ops = { + .map = sunxi_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init sunxi_of_init(struct device_node *node, + struct device_node *parent) +{ + sunxi_irq_base = of_iomap(node, 0); + if (!sunxi_irq_base) + panic("%s: unable to map IC registers\n", + node->full_name); + + /* Disable all interrupts */ + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0)); + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1)); + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2)); + + /* Mask all the interrupts */ + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0)); + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1)); + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2)); + + /* Clear all the pending interrupts */ + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0)); + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1)); + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2)); + + /* Enable protection mode */ + writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG); + + /* Configure the external interrupt source type */ + writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG); + + sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32, + &sunxi_irq_ops, NULL); + if (!sunxi_irq_domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + return 0; +} + +static struct of_device_id sunxi_irq_dt_ids[] __initconst = { + { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init } +}; + +void __init sunxi_init_irq(void) +{ + of_irq_init(sunxi_irq_dt_ids); +} + +asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs) +{ + u32 irq, hwirq; + + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; + while (hwirq != 0) { + irq = irq_find_mapping(sunxi_irq_domain, hwirq); + handle_IRQ(irq, regs); + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; + } +} diff --git a/include/linux/irqchip/sunxi.h b/include/linux/irqchip/sunxi.h new file mode 100644 index 000000000000..1fe2c2260e2b --- /dev/null +++ b/include/linux/irqchip/sunxi.h @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_IRQCHIP_SUNXI_H +#define __LINUX_IRQCHIP_SUNXI_H + +#include + +extern void sunxi_init_irq(void); + +extern asmlinkage void __exception_irq_entry sunxi_handle_irq( + struct pt_regs *regs); + +#endif -- cgit v1.2.3 From 6edc794a5ff245faf60488d32e9fdbeb0aad2223 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Nov 2012 08:44:59 +0900 Subject: pinctrl: samsung: Add support for EXYNOS4X12 This patch extends the driver with any necessary SoC-specific definitions to support EXYNOS4X12 SoCs. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Acked-by: Thomas Abraham Acked-by: Linus Walleij Signed-off-by: Kukjin Kim --- .../bindings/pinctrl/samsung-pinctrl.txt | 1 + drivers/pinctrl/pinctrl-exynos.c | 110 +++++++++++++++++++++ drivers/pinctrl/pinctrl-samsung.c | 2 + drivers/pinctrl/pinctrl-samsung.h | 1 + 4 files changed, 114 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt index 63806e2d49c2..e97a27856b21 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt @@ -8,6 +8,7 @@ on-chip controllers onto these pads. Required Properties: - compatible: should be one of the following. - "samsung,pinctrl-exynos4210": for Exynos4210 compatible pin-controller. + - "samsung,pinctrl-exynos4x12": for Exynos4x12 compatible pin-controller. - "samsung,pinctrl-exynos5250": for Exynos5250 compatible pin-controller. - reg: Base address of the pin controller hardware module and length of diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 73a0aa27cd56..19fab68a9fbf 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -566,3 +566,113 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { .label = "exynos4210-gpio-ctrl2", }, }; + +/* pin banks of exynos4x12 pin-controller 0 */ +static struct samsung_pin_bank exynos4x12_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0", 0x0c), + EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1", 0x10), + EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpd0", 0x14), + EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpd1", 0x18), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf0", 0x30), + EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpf1", 0x34), + EXYNOS_PIN_BANK_EINTG(8, 0x1C0, "gpf2", 0x38), + EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf3", 0x3c), + EXYNOS_PIN_BANK_EINTG(8, 0x240, "gpj0", 0x40), + EXYNOS_PIN_BANK_EINTG(5, 0x260, "gpj1", 0x44), +}; + +/* pin banks of exynos4x12 pin-controller 1 */ +static struct samsung_pin_bank exynos4x12_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpk0", 0x08), + EXYNOS_PIN_BANK_EINTG(7, 0x060, "gpk1", 0x0c), + EXYNOS_PIN_BANK_EINTG(7, 0x080, "gpk2", 0x10), + EXYNOS_PIN_BANK_EINTG(7, 0x0A0, "gpk3", 0x14), + EXYNOS_PIN_BANK_EINTG(7, 0x0C0, "gpl0", 0x18), + EXYNOS_PIN_BANK_EINTG(2, 0x0E0, "gpl1", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpl2", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x260, "gpm0", 0x24), + EXYNOS_PIN_BANK_EINTG(7, 0x280, "gpm1", 0x28), + EXYNOS_PIN_BANK_EINTG(5, 0x2A0, "gpm2", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x2C0, "gpm3", 0x30), + EXYNOS_PIN_BANK_EINTG(8, 0x2E0, "gpm4", 0x34), + EXYNOS_PIN_BANK_EINTN(6, 0x120, "gpy0"), + EXYNOS_PIN_BANK_EINTN(4, 0x140, "gpy1"), + EXYNOS_PIN_BANK_EINTN(6, 0x160, "gpy2"), + EXYNOS_PIN_BANK_EINTN(8, 0x180, "gpy3"), + EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"), + EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"), + EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"), + EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c), +}; + +/* pin banks of exynos4x12 pin-controller 2 */ +static struct samsung_pin_bank exynos4x12_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00), +}; + +/* pin banks of exynos4x12 pin-controller 3 */ +static struct samsung_pin_bank exynos4x12_pin_banks3[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpv0", 0x00), + EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpv1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpv2", 0x08), + EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpv3", 0x0c), + EXYNOS_PIN_BANK_EINTG(2, 0x080, "gpv4", 0x10), +}; + +/* + * Samsung pinctrl driver data for Exynos4x12 SoC. Exynos4x12 SoC includes + * four gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos4x12_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks0), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl0", + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos4x12_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks1), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .weint_con = EXYNOS_WKUP_ECON_OFFSET, + .weint_mask = EXYNOS_WKUP_EMASK_OFFSET, + .weint_pend = EXYNOS_WKUP_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .label = "exynos4x12-gpio-ctrl1", + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos4x12_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks2), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl2", + }, { + /* pin-controller instance 3 data */ + .pin_banks = exynos4x12_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks3), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl3", + }, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index fc34cac8a1b0..81c9896d4f64 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -947,6 +947,8 @@ static int __devinit samsung_pinctrl_probe(struct platform_device *pdev) static const struct of_device_id samsung_pinctrl_dt_match[] = { { .compatible = "samsung,pinctrl-exynos4210", .data = (void *)exynos4210_pin_ctrl }, + { .compatible = "samsung,pinctrl-exynos4x12", + .data = (void *)exynos4x12_pin_ctrl }, {}, }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 0670d9ea43fa..5addfd16e3cc 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -236,5 +236,6 @@ struct samsung_pmx_func { /* list of all exported SoC specific data */ extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; #endif /* __PINCTRL_SAMSUNG_H */ -- cgit v1.2.3 From 4a991b410cd7940c48f261e854e8698923e23fd2 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 19 Nov 2012 09:45:19 +0900 Subject: pinctrl: samsung: Update error check for unsigned variables Checking '< 0' for unsigned variables always returns false. For error codes, use IS_ERR_VALUE() instead. Acked-by: Linus Walleij Signed-off-by: Tushar Behera Signed-off-by: Kukjin Kim --- drivers/pinctrl/pinctrl-samsung.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 81c9896d4f64..3b52c17c4fd2 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -560,7 +560,7 @@ static int __devinit samsung_pinctrl_parse_dt_pins(struct platform_device *pdev, const char *pin_name; *npins = of_property_count_strings(cfg_np, "samsung,pins"); - if (*npins < 0) { + if (IS_ERR_VALUE(*npins)) { dev_err(dev, "invalid pin list in %s node", cfg_np->name); return -EINVAL; } -- cgit v1.2.3 From dd850f1223fe039ed649b34b1d2872b1f4221de9 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 17 Nov 2012 17:57:09 +0400 Subject: ARM: clps711x: Transform clps711x-framebuffer to platform driver and use it clps711x-framebuffer driver needs to be updated and this is a first step to make driver better. With this patch we are convert clps711x-framebuffer to platform device and load this driver from board code. Signed-off-by: Alexander Shiyan Signed-off-by: Olof Johansson --- arch/arm/mach-clps711x/autcpu12.c | 1 + arch/arm/mach-clps711x/edb7211.c | 1 + arch/arm/mach-clps711x/p720t.c | 6 ++++++ drivers/video/clps711xfb.c | 21 +++++++++++++++------ 4 files changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-clps711x/autcpu12.c b/arch/arm/mach-clps711x/autcpu12.c index d9b7f94a7ec9..c20043ba2076 100644 --- a/arch/arm/mach-clps711x/autcpu12.c +++ b/arch/arm/mach-clps711x/autcpu12.c @@ -61,6 +61,7 @@ static struct platform_device autcpu12_nvram_pdev __initdata = { static void __init autcpu12_init(void) { + platform_device_register_simple("video-clps711x", 0, NULL, 0); platform_device_register_simple("cs89x0", 0, autcpu12_cs8900_resource, ARRAY_SIZE(autcpu12_cs8900_resource)); platform_device_register(&autcpu12_nvram_pdev); diff --git a/arch/arm/mach-clps711x/edb7211.c b/arch/arm/mach-clps711x/edb7211.c index 7add9add5bf3..59dec3bd0705 100644 --- a/arch/arm/mach-clps711x/edb7211.c +++ b/arch/arm/mach-clps711x/edb7211.c @@ -83,6 +83,7 @@ fixup_edb7211(struct tag *tags, char **cmdline, struct meminfo *mi) static void __init edb7211_init(void) { + platform_device_register_simple("video-clps711x", 0, NULL, 0); platform_device_register_simple("cs89x0", 0, edb7211_cs8900_resource, ARRAY_SIZE(edb7211_cs8900_resource)); } diff --git a/arch/arm/mach-clps711x/p720t.c b/arch/arm/mach-clps711x/p720t.c index 8fe33b3a204c..d95cb8ace251 100644 --- a/arch/arm/mach-clps711x/p720t.c +++ b/arch/arm/mach-clps711x/p720t.c @@ -119,6 +119,11 @@ static struct gpio_led_platform_data p720t_gpio_led_pdata __initdata = { .num_leds = ARRAY_SIZE(p720t_gpio_leds), }; +static void __init p720t_init(void) +{ + platform_device_register_simple("video-clps711x", 0, NULL, 0); +} + static void __init p720t_init_late(void) { platform_device_register_data(&platform_bus, "leds-gpio", 0, @@ -134,6 +139,7 @@ MACHINE_START(P720T, "ARM-Prospector720T") .init_early = p720t_init_early, .init_irq = clps711x_init_irq, .timer = &clps711x_timer, + .init_machine = p720t_init, .init_late = p720t_init_late, .restart = clps711x_restart, MACHINE_END diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c index f994c8b8f10a..2ccbb9b2a255 100644 --- a/drivers/video/clps711xfb.c +++ b/drivers/video/clps711xfb.c @@ -270,7 +270,7 @@ static const struct file_operations backlight_proc_fops = { .write = backlight_proc_write, }; -static void __init clps711x_guess_lcd_params(struct fb_info *info) +static void __devinit clps711x_guess_lcd_params(struct fb_info *info) { unsigned int lcdcon, syscon, size; unsigned long phys_base = PAGE_OFFSET; @@ -358,7 +358,7 @@ static void __init clps711x_guess_lcd_params(struct fb_info *info) info->fix.type = FB_TYPE_PACKED_PIXELS; } -int __init clps711xfb_init(void) +static int __devinit clps711x_fb_probe(struct platform_device *pdev) { int err = -ENOMEM; @@ -410,7 +410,7 @@ int __init clps711xfb_init(void) out: return err; } -static void __exit clps711xfb_exit(void) +static int __devexit clps711x_fb_remove(struct platform_device *pdev) { unregister_framebuffer(cfb); kfree(cfb); @@ -422,11 +422,20 @@ static void __exit clps711xfb_exit(void) PLD_LCDEN = 0; PLD_PWR &= ~(PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON); } + + return 0; } -module_init(clps711xfb_init); -module_exit(clps711xfb_exit); +static struct platform_driver clps711x_fb_driver = { + .driver = { + .name = "video-clps711x", + .owner = THIS_MODULE, + }, + .probe = clps711x_fb_probe, + .remove = __devexit_p(clps711x_fb_remove), +}; +module_platform_driver(clps711x_fb_driver); MODULE_AUTHOR("Russell King "); -MODULE_DESCRIPTION("CLPS711x framebuffer driver"); +MODULE_DESCRIPTION("CLPS711X framebuffer driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 86449336dd3c6e0edf97b971db2daa88917c1d7e Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 17 Nov 2012 17:57:12 +0400 Subject: ARM: clps711x: cdb89712: Special driver for handling memory is removed This patch provide migration to using "physmap-flash" and "mtd-ram" drivers instead of using special driver for handling memory. Signed-off-by: Alexander Shiyan Signed-off-by: Olof Johansson --- arch/arm/mach-clps711x/cdb89712.c | 84 ++++++++ arch/arm/mach-clps711x/include/mach/hardware.h | 3 + drivers/mtd/maps/Kconfig | 7 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/cdb89712.c | 278 ------------------------- 5 files changed, 87 insertions(+), 286 deletions(-) delete mode 100644 drivers/mtd/maps/cdb89712.c (limited to 'drivers') diff --git a/arch/arm/mach-clps711x/cdb89712.c b/arch/arm/mach-clps711x/cdb89712.c index 235e6256775f..24f573b5715d 100644 --- a/arch/arm/mach-clps711x/cdb89712.c +++ b/arch/arm/mach-clps711x/cdb89712.c @@ -26,6 +26,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -44,8 +48,88 @@ static struct resource cdb89712_cs8900_resource[] __initdata = { DEFINE_RES_IRQ(CDB89712_CS8900_IRQ), }; +static struct mtd_partition cdb89712_flash_partitions[] __initdata = { + { + .name = "Flash", + .offset = 0, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct physmap_flash_data cdb89712_flash_pdata __initdata = { + .width = 4, + .probe_type = "map_rom", + .parts = cdb89712_flash_partitions, + .nr_parts = ARRAY_SIZE(cdb89712_flash_partitions), +}; + +static struct resource cdb89712_flash_resources[] __initdata = { + DEFINE_RES_MEM(CS0_PHYS_BASE, SZ_8M), +}; + +static struct platform_device cdb89712_flash_pdev __initdata = { + .name = "physmap-flash", + .id = 0, + .resource = cdb89712_flash_resources, + .num_resources = ARRAY_SIZE(cdb89712_flash_resources), + .dev = { + .platform_data = &cdb89712_flash_pdata, + }, +}; + +static struct mtd_partition cdb89712_bootrom_partitions[] __initdata = { + { + .name = "BootROM", + .offset = 0, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct physmap_flash_data cdb89712_bootrom_pdata __initdata = { + .width = 4, + .probe_type = "map_rom", + .parts = cdb89712_bootrom_partitions, + .nr_parts = ARRAY_SIZE(cdb89712_bootrom_partitions), +}; + +static struct resource cdb89712_bootrom_resources[] __initdata = { + DEFINE_RES_NAMED(CS7_PHYS_BASE, SZ_128, "BOOTROM", IORESOURCE_MEM | + IORESOURCE_CACHEABLE | IORESOURCE_READONLY), +}; + +static struct platform_device cdb89712_bootrom_pdev __initdata = { + .name = "physmap-flash", + .id = 1, + .resource = cdb89712_bootrom_resources, + .num_resources = ARRAY_SIZE(cdb89712_bootrom_resources), + .dev = { + .platform_data = &cdb89712_bootrom_pdata, + }, +}; + +static struct platdata_mtd_ram cdb89712_sram_pdata __initdata = { + .bankwidth = 4, +}; + +static struct resource cdb89712_sram_resources[] __initdata = { + DEFINE_RES_MEM(CLPS711X_SRAM_BASE, CLPS711X_SRAM_SIZE), +}; + +static struct platform_device cdb89712_sram_pdev __initdata = { + .name = "mtd-ram", + .id = 0, + .resource = cdb89712_sram_resources, + .num_resources = ARRAY_SIZE(cdb89712_sram_resources), + .dev = { + .platform_data = &cdb89712_sram_pdata, + }, +}; + static void __init cdb89712_init(void) { + platform_device_register(&cdb89712_flash_pdev); + platform_device_register(&cdb89712_bootrom_pdev); + platform_device_register(&cdb89712_sram_pdev); platform_device_register_simple("cs89x0", 0, cdb89712_cs8900_resource, ARRAY_SIZE(cdb89712_cs8900_resource)); } diff --git a/arch/arm/mach-clps711x/include/mach/hardware.h b/arch/arm/mach-clps711x/include/mach/hardware.h index bd919e7bb86f..5a278cb02b03 100644 --- a/arch/arm/mach-clps711x/include/mach/hardware.h +++ b/arch/arm/mach-clps711x/include/mach/hardware.h @@ -64,6 +64,9 @@ #define CS7_PHYS_BASE (0x00000000) #endif +#define CLPS711X_SRAM_BASE CS6_PHYS_BASE +#define CLPS711X_SRAM_SIZE (48 * 1024) + #if defined (CONFIG_ARCH_EDB7211) /* The extra 8 lines of the keyboard matrix are wired to chip select 3 */ diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 2e47c2ed0a2d..df304868bebb 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -324,13 +324,6 @@ config MTD_SOLUTIONENGINE This enables access to the flash chips on the Hitachi SolutionEngine and similar boards. Say 'Y' if you are building a kernel for such a board. -config MTD_CDB89712 - tristate "Cirrus CDB89712 evaluation board mappings" - depends on MTD_CFI && ARCH_CDB89712 - help - This enables access to the flash or ROM chips on the CDB89712 board. - If you have such a board, say 'Y'. - config MTD_SA1100 tristate "CFI Flash device mapped on StrongARM SA11x0" depends on MTD_CFI && ARCH_SA1100 diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index deb43e9a1e7f..a0240edd1961 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_MTD) += map_funcs.o endif # Chip mappings -obj-$(CONFIG_MTD_CDB89712) += cdb89712.o obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c deleted file mode 100644 index c29cbf87ea0c..000000000000 --- a/drivers/mtd/maps/cdb89712.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Flash on Cirrus CDB89712 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* dynamic ioremap() areas */ -#define FLASH_START 0x00000000 -#define FLASH_SIZE 0x800000 -#define FLASH_WIDTH 4 - -#define SRAM_START 0x60000000 -#define SRAM_SIZE 0xc000 -#define SRAM_WIDTH 4 - -#define BOOTROM_START 0x70000000 -#define BOOTROM_SIZE 0x80 -#define BOOTROM_WIDTH 4 - - -static struct mtd_info *flash_mtd; - -struct map_info cdb89712_flash_map = { - .name = "flash", - .size = FLASH_SIZE, - .bankwidth = FLASH_WIDTH, - .phys = FLASH_START, -}; - -struct resource cdb89712_flash_resource = { - .name = "Flash", - .start = FLASH_START, - .end = FLASH_START + FLASH_SIZE - 1, - .flags = IORESOURCE_IO | IORESOURCE_BUSY, -}; - -static int __init init_cdb89712_flash (void) -{ - int err; - - if (request_resource (&ioport_resource, &cdb89712_flash_resource)) { - printk(KERN_NOTICE "Failed to reserve Cdb89712 FLASH space\n"); - err = -EBUSY; - goto out; - } - - cdb89712_flash_map.virt = ioremap(FLASH_START, FLASH_SIZE); - if (!cdb89712_flash_map.virt) { - printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); - err = -EIO; - goto out_resource; - } - simple_map_init(&cdb89712_flash_map); - flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); - if (!flash_mtd) { - flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); - if (flash_mtd) - flash_mtd->erasesize = 0x10000; - } - if (!flash_mtd) { - printk("FLASH probe failed\n"); - err = -ENXIO; - goto out_ioremap; - } - - flash_mtd->owner = THIS_MODULE; - - if (mtd_device_register(flash_mtd, NULL, 0)) { - printk("FLASH device addition failed\n"); - err = -ENOMEM; - goto out_probe; - } - - return 0; - -out_probe: - map_destroy(flash_mtd); - flash_mtd = 0; -out_ioremap: - iounmap((void *)cdb89712_flash_map.virt); -out_resource: - release_resource (&cdb89712_flash_resource); -out: - return err; -} - - - - - -static struct mtd_info *sram_mtd; - -struct map_info cdb89712_sram_map = { - .name = "SRAM", - .size = SRAM_SIZE, - .bankwidth = SRAM_WIDTH, - .phys = SRAM_START, -}; - -struct resource cdb89712_sram_resource = { - .name = "SRAM", - .start = SRAM_START, - .end = SRAM_START + SRAM_SIZE - 1, - .flags = IORESOURCE_IO | IORESOURCE_BUSY, -}; - -static int __init init_cdb89712_sram (void) -{ - int err; - - if (request_resource (&ioport_resource, &cdb89712_sram_resource)) { - printk(KERN_NOTICE "Failed to reserve Cdb89712 SRAM space\n"); - err = -EBUSY; - goto out; - } - - cdb89712_sram_map.virt = ioremap(SRAM_START, SRAM_SIZE); - if (!cdb89712_sram_map.virt) { - printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); - err = -EIO; - goto out_resource; - } - simple_map_init(&cdb89712_sram_map); - sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); - if (!sram_mtd) { - printk("SRAM probe failed\n"); - err = -ENXIO; - goto out_ioremap; - } - - sram_mtd->owner = THIS_MODULE; - sram_mtd->erasesize = 16; - - if (mtd_device_register(sram_mtd, NULL, 0)) { - printk("SRAM device addition failed\n"); - err = -ENOMEM; - goto out_probe; - } - - return 0; - -out_probe: - map_destroy(sram_mtd); - sram_mtd = 0; -out_ioremap: - iounmap((void *)cdb89712_sram_map.virt); -out_resource: - release_resource (&cdb89712_sram_resource); -out: - return err; -} - - - - - - - -static struct mtd_info *bootrom_mtd; - -struct map_info cdb89712_bootrom_map = { - .name = "BootROM", - .size = BOOTROM_SIZE, - .bankwidth = BOOTROM_WIDTH, - .phys = BOOTROM_START, -}; - -struct resource cdb89712_bootrom_resource = { - .name = "BootROM", - .start = BOOTROM_START, - .end = BOOTROM_START + BOOTROM_SIZE - 1, - .flags = IORESOURCE_IO | IORESOURCE_BUSY, -}; - -static int __init init_cdb89712_bootrom (void) -{ - int err; - - if (request_resource (&ioport_resource, &cdb89712_bootrom_resource)) { - printk(KERN_NOTICE "Failed to reserve Cdb89712 BOOTROM space\n"); - err = -EBUSY; - goto out; - } - - cdb89712_bootrom_map.virt = ioremap(BOOTROM_START, BOOTROM_SIZE); - if (!cdb89712_bootrom_map.virt) { - printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); - err = -EIO; - goto out_resource; - } - simple_map_init(&cdb89712_bootrom_map); - bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); - if (!bootrom_mtd) { - printk("BootROM probe failed\n"); - err = -ENXIO; - goto out_ioremap; - } - - bootrom_mtd->owner = THIS_MODULE; - bootrom_mtd->erasesize = 0x10000; - - if (mtd_device_register(bootrom_mtd, NULL, 0)) { - printk("BootROM device addition failed\n"); - err = -ENOMEM; - goto out_probe; - } - - return 0; - -out_probe: - map_destroy(bootrom_mtd); - bootrom_mtd = 0; -out_ioremap: - iounmap((void *)cdb89712_bootrom_map.virt); -out_resource: - release_resource (&cdb89712_bootrom_resource); -out: - return err; -} - - - - - -static int __init init_cdb89712_maps(void) -{ - - printk(KERN_INFO "Cirrus CDB89712 MTD mappings:\n Flash 0x%x at 0x%x\n SRAM 0x%x at 0x%x\n BootROM 0x%x at 0x%x\n", - FLASH_SIZE, FLASH_START, SRAM_SIZE, SRAM_START, BOOTROM_SIZE, BOOTROM_START); - - init_cdb89712_flash(); - init_cdb89712_sram(); - init_cdb89712_bootrom(); - - return 0; -} - - -static void __exit cleanup_cdb89712_maps(void) -{ - if (sram_mtd) { - mtd_device_unregister(sram_mtd); - map_destroy(sram_mtd); - iounmap((void *)cdb89712_sram_map.virt); - release_resource (&cdb89712_sram_resource); - } - - if (flash_mtd) { - mtd_device_unregister(flash_mtd); - map_destroy(flash_mtd); - iounmap((void *)cdb89712_flash_map.virt); - release_resource (&cdb89712_flash_resource); - } - - if (bootrom_mtd) { - mtd_device_unregister(bootrom_mtd); - map_destroy(bootrom_mtd); - iounmap((void *)cdb89712_bootrom_map.virt); - release_resource (&cdb89712_bootrom_resource); - } -} - -module_init(init_cdb89712_maps); -module_exit(cleanup_cdb89712_maps); - -MODULE_AUTHOR("Ray L"); -MODULE_DESCRIPTION("ARM CDB89712 map driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 90383e0ac2ae3df283f2b56997040f71f6d1df08 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 17 Nov 2012 17:57:17 +0400 Subject: ARM: clps711x: autcpu12: Special driver for handling NAND memory is removed This patch provide migration to using "gpio-nand" and "basic-mmio-gpio" drivers instead of using special driver for handling NAND memory. Signed-off-by: Alexander Shiyan Signed-off-by: Olof Johansson --- arch/arm/mach-clps711x/autcpu12.c | 98 ++++++++++ arch/arm/mach-clps711x/include/mach/autcpu12.h | 10 -- drivers/mtd/nand/Kconfig | 7 - drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/autcpu12.c | 237 ------------------------- 5 files changed, 98 insertions(+), 255 deletions(-) delete mode 100644 drivers/mtd/nand/autcpu12.c (limited to 'drivers') diff --git a/arch/arm/mach-clps711x/autcpu12.c b/arch/arm/mach-clps711x/autcpu12.c index 3de19554d800..3fbf43f72589 100644 --- a/arch/arm/mach-clps711x/autcpu12.c +++ b/arch/arm/mach-clps711x/autcpu12.c @@ -23,9 +23,13 @@ #include #include #include +#include #include #include +#include +#include #include +#include #include #include @@ -43,6 +47,15 @@ #define AUTCPU12_CS8900_BASE (CS2_PHYS_BASE + 0x300) #define AUTCPU12_CS8900_IRQ (IRQ_EINT3) +#define AUTCPU12_SMC_BASE (CS1_PHYS_BASE + 0x06000000) +#define AUTCPU12_SMC_SEL_BASE (AUTCPU12_SMC_BASE + 0x10) + +#define AUTCPU12_MMGPIO_BASE (CLPS711X_NR_GPIO) +#define AUTCPU12_SMC_NCE (AUTCPU12_MMGPIO_BASE + 0) /* Bit 0 */ +#define AUTCPU12_SMC_RDY CLPS711X_GPIO(1, 2) +#define AUTCPU12_SMC_ALE CLPS711X_GPIO(1, 3) +#define AUTCPU12_SMC_CLE CLPS711X_GPIO(1, 3) + static struct resource autcpu12_cs8900_resource[] __initdata = { DEFINE_RES_MEM(AUTCPU12_CS8900_BASE, SZ_1K), DEFINE_RES_IRQ(AUTCPU12_CS8900_IRQ), @@ -59,14 +72,98 @@ static struct platform_device autcpu12_nvram_pdev __initdata = { .num_resources = ARRAY_SIZE(autcpu12_nvram_resource), }; +static struct resource autcpu12_nand_resource[] __initdata = { + DEFINE_RES_MEM(AUTCPU12_SMC_BASE, SZ_16), +}; + +static struct mtd_partition autcpu12_nand_parts[] __initdata = { + { + .name = "Flash partition 1", + .offset = 0, + .size = SZ_8M, + }, + { + .name = "Flash partition 2", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static void __init autcpu12_adjust_parts(struct gpio_nand_platdata *pdata, + size_t sz) +{ + switch (sz) { + case SZ_16M: + case SZ_32M: + break; + case SZ_64M: + case SZ_128M: + pdata->parts[0].size = SZ_16M; + break; + default: + pr_warn("Unsupported SmartMedia device size %u\n", sz); + break; + } +} + +static struct gpio_nand_platdata autcpu12_nand_pdata __initdata = { + .gpio_rdy = AUTCPU12_SMC_RDY, + .gpio_nce = AUTCPU12_SMC_NCE, + .gpio_ale = AUTCPU12_SMC_ALE, + .gpio_cle = AUTCPU12_SMC_CLE, + .gpio_nwp = -1, + .chip_delay = 20, + .parts = autcpu12_nand_parts, + .num_parts = ARRAY_SIZE(autcpu12_nand_parts), + .adjust_parts = autcpu12_adjust_parts, +}; + +static struct platform_device autcpu12_nand_pdev __initdata = { + .name = "gpio-nand", + .id = -1, + .resource = autcpu12_nand_resource, + .num_resources = ARRAY_SIZE(autcpu12_nand_resource), + .dev = { + .platform_data = &autcpu12_nand_pdata, + }, +}; + +static struct resource autcpu12_mmgpio_resource[] __initdata = { + DEFINE_RES_MEM_NAMED(AUTCPU12_SMC_SEL_BASE, SZ_1, "dat"), +}; + +static struct bgpio_pdata autcpu12_mmgpio_pdata __initdata = { + .base = AUTCPU12_MMGPIO_BASE, + .ngpio = 8, +}; + +static struct platform_device autcpu12_mmgpio_pdev __initdata = { + .name = "basic-mmio-gpio", + .id = -1, + .resource = autcpu12_mmgpio_resource, + .num_resources = ARRAY_SIZE(autcpu12_mmgpio_resource), + .dev = { + .platform_data = &autcpu12_mmgpio_pdata, + }, +}; + static void __init autcpu12_init(void) { platform_device_register_simple("video-clps711x", 0, NULL, 0); platform_device_register_simple("cs89x0", 0, autcpu12_cs8900_resource, ARRAY_SIZE(autcpu12_cs8900_resource)); + platform_device_register(&autcpu12_mmgpio_pdev); platform_device_register(&autcpu12_nvram_pdev); } +static void __init autcpu12_init_late(void) +{ + if (IS_ENABLED(MTD_NAND_GPIO) && IS_ENABLED(GPIO_GENERIC_PLATFORM)) { + /* We are need both drivers to handle NAND */ + platform_device_register(&autcpu12_nand_pdev); + } +} + MACHINE_START(AUTCPU12, "autronix autcpu12") /* Maintainer: Thomas Gleixner */ .atag_offset = 0x20000, @@ -75,6 +172,7 @@ MACHINE_START(AUTCPU12, "autronix autcpu12") .init_irq = clps711x_init_irq, .timer = &clps711x_timer, .init_machine = autcpu12_init, + .init_late = autcpu12_init_late, .handle_irq = clps711x_handle_irq, .restart = clps711x_restart, MACHINE_END diff --git a/arch/arm/mach-clps711x/include/mach/autcpu12.h b/arch/arm/mach-clps711x/include/mach/autcpu12.h index b077abd8a475..0452f5f3f034 100644 --- a/arch/arm/mach-clps711x/include/mach/autcpu12.h +++ b/arch/arm/mach-clps711x/include/mach/autcpu12.h @@ -40,8 +40,6 @@ #define AUTCPU12_PHYS_CSAUX1 CS1_PHYS_BASE +0x04000000 /* physical */ -#define AUTCPU12_PHYS_SMC CS1_PHYS_BASE +0x06000000 /* physical */ - #define AUTCPU12_PHYS_CAN CS1_PHYS_BASE +0x08000000 /* physical */ #define AUTCPU12_PHYS_TOUCH CS1_PHYS_BASE +0x0A000000 /* physical */ @@ -50,14 +48,6 @@ #define AUTCPU12_PHYS_LPT CS1_PHYS_BASE +0x0E000000 /* physical */ -/* -* defines for smartmedia card access -*/ -#define AUTCPU12_SMC_RDY (1<<2) -#define AUTCPU12_SMC_ALE (1<<3) -#define AUTCPU12_SMC_CLE (1<<4) -#define AUTCPU12_SMC_PORT_OFFSET PBDR -#define AUTCPU12_SMC_SELECT_OFFSET 0x10 /* * defines for lcd contrast */ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4883139460be..e6e0a82cbaaf 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -49,13 +49,6 @@ config MTD_NAND_MUSEUM_IDS NAND chips (page size 256 byte, erase size 4-8KiB). The IDs of these chips were reused by later, larger chips. -config MTD_NAND_AUTCPU12 - tristate "SmartMediaCard on autronix autcpu12 board" - depends on ARCH_AUTCPU12 - help - This enables the driver for the autronix autcpu12 board to - access the SmartMediaCard. - config MTD_NAND_DENALI depends on PCI tristate "Support Denali NAND controller on Intel Moorestown" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 2cbd0916b733..0b1fca24866c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o -obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c deleted file mode 100644 index 04769a49a7cb..000000000000 --- a/drivers/mtd/nand/autcpu12.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * drivers/mtd/autcpu12.c - * - * Copyright (c) 2002 Thomas Gleixner - * - * Derived from drivers/mtd/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * autronix autcpu12 board, which is a SmartMediaCard. It supports - * 16MiB, 32MiB and 64MiB cards. - * - * - * 02-12-2002 TG Cleanup of module params - * - * 02-20-2002 TG adjusted for different rd/wr address support - * added support for read device ready/busy line - * added page_cache - * - * 10-06-2002 TG 128K card support added - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * MTD structure for AUTCPU12 board - */ -static struct mtd_info *autcpu12_mtd = NULL; -static void __iomem *autcpu12_fio_base; - -/* - * Define partitions for flash devices - */ -static struct mtd_partition partition_info16k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 8 * SZ_1M }, -}; - -static struct mtd_partition partition_info32k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 24 * SZ_1M }, -}; - -static struct mtd_partition partition_info64k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 48 * SZ_1M }, -}; - -static struct mtd_partition partition_info128k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 112 * SZ_1M }, -}; - -#define NUM_PARTITIONS16K 2 -#define NUM_PARTITIONS32K 2 -#define NUM_PARTITIONS64K 2 -#define NUM_PARTITIONS128K 2 -/* - * hardware specific access to control-lines - * - * ALE bit 4 autcpu12_pedr - * CLE bit 5 autcpu12_pedr - * NCE bit 0 fio_ctrl - * - */ -static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - void __iomem *addr; - unsigned char bits; - - bits = clps_readb(AUTCPU12_SMC_PORT_OFFSET) & ~0x30; - bits |= (ctrl & NAND_CLE) << 4; - bits |= (ctrl & NAND_ALE) << 2; - clps_writeb(bits, AUTCPU12_SMC_PORT_OFFSET); - - addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET; - writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -int autcpu12_device_ready(struct mtd_info *mtd) -{ - return clps_readb(AUTCPU12_SMC_PORT_OFFSET) & AUTCPU12_SMC_RDY; -} - -/* - * Main initialization routine - */ -static int __init autcpu12_init(void) -{ - struct nand_chip *this; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), - GFP_KERNEL); - if (!autcpu12_mtd) { - printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n"); - err = -ENOMEM; - goto out; - } - - /* map physical address */ - autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K); - if (!autcpu12_fio_base) { - printk("Ioremap autcpu12 SmartMedia Card failed\n"); - err = -EIO; - goto out_mtd; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&autcpu12_mtd[1]); - - /* Initialize structures */ - memset(autcpu12_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - autcpu12_mtd->priv = this; - autcpu12_mtd->owner = THIS_MODULE; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = autcpu12_fio_base; - this->IO_ADDR_W = autcpu12_fio_base; - this->cmd_ctrl = autcpu12_hwcontrol; - this->dev_ready = autcpu12_device_ready; - /* 20 us command delay time */ - this->chip_delay = 20; - this->ecc.mode = NAND_ECC_SOFT; - - /* Enable the following for a flash based bad block table */ - /* - this->bbt_options = NAND_BBT_USE_FLASH; - */ - this->bbt_options = NAND_BBT_USE_FLASH; - - /* Scan to find existence of the device */ - if (nand_scan(autcpu12_mtd, 1)) { - err = -ENXIO; - goto out_ior; - } - - /* Register the partitions */ - switch (autcpu12_mtd->size) { - case SZ_16M: - mtd_device_register(autcpu12_mtd, partition_info16k, - NUM_PARTITIONS16K); - break; - case SZ_32M: - mtd_device_register(autcpu12_mtd, partition_info32k, - NUM_PARTITIONS32K); - break; - case SZ_64M: - mtd_device_register(autcpu12_mtd, partition_info64k, - NUM_PARTITIONS64K); - break; - case SZ_128M: - mtd_device_register(autcpu12_mtd, partition_info128k, - NUM_PARTITIONS128K); - break; - default: - printk("Unsupported SmartMedia device\n"); - err = -ENXIO; - goto out_ior; - } - goto out; - - out_ior: - iounmap(autcpu12_fio_base); - out_mtd: - kfree(autcpu12_mtd); - out: - return err; -} - -module_init(autcpu12_init); - -/* - * Clean up routine - */ -static void __exit autcpu12_cleanup(void) -{ - /* Release resources, unregister device */ - nand_release(autcpu12_mtd); - - /* unmap physical address */ - iounmap(autcpu12_fio_base); - - /* Free the MTD device structure */ - kfree(autcpu12_mtd); -} - -module_exit(autcpu12_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Thomas Gleixner "); -MODULE_DESCRIPTION("Glue layer for SmartMediaCard on autronix autcpu12"); -- cgit v1.2.3 From 87c37b51ade7d1296ab99ed8f237637bed7779c7 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 17 Nov 2012 17:57:18 +0400 Subject: ARM: clps711x: Moving power management of framebuffer driver to the board This patch moves the power management for clps711x-framebuffer driver to the board code. To control we use "platform-lcd" driver. Signed-off-by: Alexander Shiyan Signed-off-by: Olof Johansson --- arch/arm/mach-clps711x/edb7211.c | 34 ++++++++++++++++++++ arch/arm/mach-clps711x/include/mach/hardware.h | 2 -- arch/arm/mach-clps711x/p720t.c | 20 ++++++++++++ drivers/video/clps711xfb.c | 44 ++++---------------------- 4 files changed, 60 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-clps711x/edb7211.c b/arch/arm/mach-clps711x/edb7211.c index cc32a65d6982..81cc6835d7a5 100644 --- a/arch/arm/mach-clps711x/edb7211.c +++ b/arch/arm/mach-clps711x/edb7211.c @@ -8,6 +8,8 @@ */ #include +#include +#include #include #include #include @@ -18,12 +20,17 @@ #include #include +#include