ARM: late Exynos multiplatform changes

These continue the multiplatform support for exynos, adding support
 for building most of the essential drivers (clocksource, clk, irqchip)
 when combined with other platforms. As a result, it should become
 really easy to add full multiplatform exynos support in 3.11, although
 we don't yet enable it for 3.10.
 
 The changes were not included in the earlier multiplatform series
 in order to avoid clashes with the other Exynos updates.
 
 This also includes work from Tomasz Figa to fix the pwm clocksource
 code on Exynos, which is not strictly required for multiplatform,
 but related to the other patches in this set and needed as a bug
 fix for at least one board.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIVAwUAUYgmgWCrR//JCVInAQIp6A//cb87A7biCHo0hd64v7RtX2dIvYTc8ZDh
 7O9yH7NuAtbSI7FF7cVQGGK6nCRqmwO2SM/KLFgbt2MF36FLgQKKZhJIDM/qB4jb
 3DCHHH814eqExf4MFfZL4Yxl4FaMqxzSwYX8fD28GmpeVxLeHjh0yQCKmPejz5MW
 WgkMcBJS3IPqbhhKMcMZmXteLrEzEm43Uj6dxkZP7RbinyuWzHvx3IWWv4gQ6ITz
 3jcCvZC5JWBo9MEPH43vlmOd8qsAn0OvkbtbYiy2Tre5VerqOgbEEXU2U0A2zUSj
 YTmRvwIGsIylL2EkVsJTkMj8KJ8TAHZjHyNUY8m2UzWuS+9EdZjf6rXeKIdUz9Wa
 0dmiWJEOEvejk0RnHEJm7anmKp7a9YHFkFSRnHbLOAXAMkUZWWcVAMZ4UbDK8RtF
 RX6R+ga9tR8R7aBLIzqYyfSHaZ7xUpF6nSBOM4GNVNKtViJv3PENWVQrm2GHcQ9w
 +4IMUqXO/5IRvuHW93l+oN8tENDTF0cR0+S7t0R6Vuuh7OebRt9TAE421Hrvt+7p
 gI5tvhEeV3o1CMmXWod8X1jxY/1OrONG7wX/x07ymiRnXSd+sZ0CPkYyWultKNw8
 bCAsnOP2aFpO1RB0XEC5y8FZ5uSfcQ7Ngu2kyAP7mEXV6qbSHgmb+lyxf2G8ftL2
 Rn0M7nbLcz4=
 =FY7+
 -----END PGP SIGNATURE-----

Merge tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull late ARM Exynos multiplatform changes from Arnd Bergmann:
 "These continue the multiplatform support for exynos, adding support
  for building most of the essential drivers (clocksource, clk, irqchip)
  when combined with other platforms.  As a result, it should become
  really easy to add full multiplatform exynos support in 3.11, although
  we don't yet enable it for 3.10.

  The changes were not included in the earlier multiplatform series in
  order to avoid clashes with the other Exynos updates.

  This also includes work from Tomasz Figa to fix the pwm clocksource
  code on Exynos, which is not strictly required for multiplatform, but
  related to the other patches in this set and needed as a bug fix for
  at least one board."

* tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (22 commits)
  ARM: dts: exynops4210: really add universal_c210 dts
  ARM: dts: exynos4210: Add basic dts file for universal_c210 board
  ARM: dts: exynos4: Add node for PWM device
  ARM: SAMSUNG: Do not register legacy timer interrupts on Exynos
  clocksource: samsung_pwm_timer: Work around rounding errors in clockevents core
  clocksource: samsung_pwm_timer: Correct programming of clock events
  clocksource: samsung_pwm_timer: Use proper clockevents max_delta
  clocksource: samsung_pwm_timer: Add support for non-DT platforms
  clocksource: samsung_pwm_timer: Drop unused samsung_pwm struct
  clocksource: samsung_pwm_timer: Keep all driver data in a structure
  clocksource: samsung_pwm_timer: Make PWM spinlock global
  clocksource: samsung_pwm_timer: Let platforms select the driver
  Documentation: Add device tree bindings for Samsung PWM timers
  clocksource: add samsung pwm timer driver
  irqchip: exynos: look up irq using irq_find_mapping
  irqchip: exynos: pass irq_base from platform
  irqchip: exynos: localize irq lookup for ATAGS
  irqchip: exynos: allocate combiner_data dynamically
  irqchip: exynos: pass max combiner number to combiner_init
  ARM: exynos: add missing properties for combiner IRQs
  ...
This commit is contained in:
Linus Torvalds 2013-05-07 11:28:42 -07:00
commit bb9055b274
20 changed files with 1098 additions and 154 deletions

View file

@ -12,13 +12,16 @@
#include <linux/export.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/irq.h>
#ifdef CONFIG_EXYNOS_ATAGS
#include <plat/cpu.h>
#endif
#include "irqchip.h"
@ -26,17 +29,18 @@
#define COMBINER_ENABLE_CLEAR 0x4
#define COMBINER_INT_STATUS 0xC
#define IRQ_IN_COMBINER 8
static DEFINE_SPINLOCK(irq_controller_lock);
struct combiner_chip_data {
unsigned int irq_offset;
unsigned int hwirq_offset;
unsigned int irq_mask;
void __iomem *base;
unsigned int parent_irq;
};
static struct irq_domain *combiner_irq_domain;
static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
static inline void __iomem *combiner_base(struct irq_data *data)
{
@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
if (status == 0)
goto out;
combiner_irq = __ffs(status);
combiner_irq = chip_data->hwirq_offset + __ffs(status);
cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
if (unlikely(cascade_irq >= NR_IRQS))
do_bad_IRQ(cascade_irq, desc);
if (unlikely(!cascade_irq))
do_bad_IRQ(irq, desc);
else
generic_handle_irq(cascade_irq);
@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
#endif
};
static unsigned int max_combiner_nr(void)
{
if (soc_is_exynos5250())
return EXYNOS5_MAX_COMBINER_NR;
else if (soc_is_exynos4412())
return EXYNOS4412_MAX_COMBINER_NR;
else if (soc_is_exynos4212())
return EXYNOS4212_MAX_COMBINER_NR;
else
return EXYNOS4210_MAX_COMBINER_NR;
}
static void __init combiner_cascade_irq(unsigned int combiner_nr,
static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
unsigned int irq)
{
if (combiner_nr >= max_combiner_nr())
BUG();
if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
if (irq_set_handler_data(irq, combiner_data) != 0)
BUG();
irq_set_chained_handler(irq, combiner_handle_cascade_irq);
}
static void __init combiner_init_one(unsigned int combiner_nr,
static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
unsigned int combiner_nr,
void __iomem *base, unsigned int irq)
{
combiner_data[combiner_nr].base = base;
combiner_data[combiner_nr].irq_offset = irq_find_mapping(
combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER);
combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);
combiner_data[combiner_nr].parent_irq = irq;
combiner_data->base = base;
combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
combiner_data->parent_irq = irq;
/* Disable all interrupts */
__raw_writel(combiner_data[combiner_nr].irq_mask,
base + COMBINER_ENABLE_CLEAR);
__raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
}
#ifdef CONFIG_OF
@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
if (intsize < 2)
return -EINVAL;
*out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1];
*out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
*out_type = 0;
return 0;
@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
struct combiner_chip_data *combiner_data = d->host_data;
irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
irq_set_chip_data(irq, &combiner_data[hw >> 3]);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
.map = combiner_irq_domain_map,
};
static unsigned int exynos4x12_combiner_extra_irq(int group)
static unsigned int combiner_lookup_irq(int group)
{
#ifdef CONFIG_EXYNOS_ATAGS
if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
return IRQ_SPI(group);
switch (group) {
case 16:
return IRQ_SPI(107);
@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
return IRQ_SPI(48);
case 19:
return IRQ_SPI(42);
default:
return 0;
}
#endif
return 0;
}
void __init combiner_init(void __iomem *combiner_base,
struct device_node *np)
struct device_node *np,
unsigned int max_nr,
int irq_base)
{
int i, irq, irq_base;
unsigned int max_nr, nr_irq;
int i, irq;
unsigned int nr_irq;
struct combiner_chip_data *combiner_data;
max_nr = max_combiner_nr();
nr_irq = max_nr * IRQ_IN_COMBINER;
if (np) {
if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
pr_info("%s: number of combiners not specified, "
"setting default as %d.\n",
__func__, max_nr);
}
combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
if (!combiner_data) {
pr_warning("%s: could not allocate combiner data\n", __func__);
return;
}
nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
if (IS_ERR_VALUE(irq_base)) {
irq_base = COMBINER_IRQ(0, 0);
pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
}
combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0,
&combiner_irq_domain_ops, &combiner_data);
combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
&combiner_irq_domain_ops, combiner_data);
if (WARN_ON(!combiner_irq_domain)) {
pr_warning("%s: irq domain init failed\n", __func__);
return;
}
for (i = 0; i < max_nr; i++) {
if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
irq = IRQ_SPI(i);
else
irq = exynos4x12_combiner_extra_irq(i);
#ifdef CONFIG_OF
if (np)
irq = irq_of_parse_and_map(np, i);
else
#endif
combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq);
combiner_cascade_irq(i, irq);
irq = combiner_lookup_irq(i);
combiner_init_one(&combiner_data[i], i,
combiner_base + (i >> 2) * 0x10, irq);
combiner_cascade_irq(&combiner_data[i], irq);
}
}
@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
struct device_node *parent)
{
void __iomem *combiner_base;
unsigned int max_nr = 20;
int irq_base = -1;
combiner_base = of_iomap(np, 0);
if (!combiner_base) {
@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
return -ENXIO;
}
combiner_init(combiner_base, np);
if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
pr_info("%s: number of combiners not specified, "
"setting default as %d.\n",
__func__, max_nr);
}
/*
* FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
* get their IRQ from DT, remove this in order to get dynamic
* allocation.
*/
irq_base = 160;
combiner_init(combiner_base, np, max_nr, irq_base);
return 0;
}