[PATCH 1/4] clk: add pxa27x clock drivers
Robert Jarzmik
robert.jarzmik at free.fr
Sun Jun 29 11:32:21 PDT 2014
Move pxa27x clock drivers from arch/arm/mach-pxa to driver/clk.
In the move :
- convert to new clock framework legacy clocks
- provide clocks as before for platform data based boards
- provide clocks through devicetree with clk-pxa-dt
This is the preliminary step in the conversion. The remaining steps are
:
- migrate pxa25x and pxa3xx
- once PXA is fully converted to device tree, if that happens,
clk-pxa2* and clk-pxa3* should only hold the core clocks which cannot
be described in devicetree.
Signed-off-by: Robert Jarzmik <robert.jarzmik at free.fr>
---
drivers/clk/Makefile | 1 +
drivers/clk/pxa/Makefile | 4 +
drivers/clk/pxa/clk-pxa-dt.c | 76 ++++++++++
drivers/clk/pxa/clk-pxa27x.c | 324 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/pxa/clk-pxa2xx.c | 74 ++++++++++
drivers/clk/pxa/clk-pxa2xx.h | 47 +++++++
6 files changed, 526 insertions(+)
create mode 100644 drivers/clk/pxa/Makefile
create mode 100644 drivers/clk/pxa/clk-pxa-dt.c
create mode 100644 drivers/clk/pxa/clk-pxa27x.c
create mode 100644 drivers/clk/pxa/clk-pxa2xx.c
create mode 100644 drivers/clk/pxa/clk-pxa2xx.h
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..40390ea 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_ARCH_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile
new file mode 100644
index 0000000..65ff461
--- /dev/null
+++ b/drivers/clk/pxa/Makefile
@@ -0,0 +1,4 @@
+obj-y += clk-pxa-dt.o
+obj-$(CONFIG_PXA25x) += clk-pxa2xx.o clk-pxa25x.o
+obj-$(CONFIG_PXA27x) += clk-pxa2xx.o clk-pxa27x.o
+obj-$(CONFIG_PXA3xx) += clk-pxa3xx.o
diff --git a/drivers/clk/pxa/clk-pxa-dt.c b/drivers/clk/pxa/clk-pxa-dt.c
new file mode 100644
index 0000000..530e75b
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa-dt.c
@@ -0,0 +1,76 @@
+/*
+ * Marvell PXA2xx family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * 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; version 2 of the License.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mach/pxa2xx-regs.h>
+
+#define PXA2XX_MAX_CLOCKS 32
+
+static DEFINE_SPINLOCK(cken_lock);
+
+static void __init pxa2xx_clocks_init(struct device_node *np)
+{
+ struct clk *clk;
+ struct clk_onecell_data *onecell_data = NULL;
+ const char *name;
+ const char *parent_name;
+ int i, ret;
+ u32 clkidx;
+ void __iomem *reg;
+
+ ret = -ENOMEM;
+ onecell_data = kmalloc(sizeof(*onecell_data), GFP_KERNEL);
+ if (!onecell_data)
+ goto err_mem;
+ onecell_data->clks = kcalloc(PXA2XX_MAX_CLOCKS, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!onecell_data->clks)
+ goto err_mem;
+ onecell_data->clk_num = PXA2XX_MAX_CLOCKS;
+
+ reg = of_iomap(np, 0);
+ if (!reg)
+ return;
+
+ for (i = 0; i < PXA2XX_MAX_CLOCKS; i++) {
+ ret = of_property_read_string_index(np, "clock-output-names",
+ i, &name);
+ if (ret < 0 || strlen(name) == 0)
+ continue;
+
+ parent_name = of_clk_get_parent_name(np, i);
+ ret = of_property_read_u32_index(np, "clock-indices", i,
+ &clkidx);
+ if (parent_name == NULL || ret < 0)
+ break;
+ if (clkidx >= PXA2XX_MAX_CLOCKS) {
+ pr_err("%s: invalid clock %s %s index %u)\n",
+ __func__, np->name, name, clkidx);
+ continue;
+ }
+
+ clk = clk_register_gate(NULL, name, parent_name, 0,
+ reg, clkidx, 0, &cken_lock);
+ if (!IS_ERR(clk))
+ onecell_data->clks[i] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data);
+ return;
+err_mem:
+ if (onecell_data)
+ kfree(onecell_data->clks);
+ kfree(onecell_data);
+}
+CLK_OF_DECLARE(pxa2xx_clks, "marvell,pxa-clocks", pxa2xx_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
new file mode 100644
index 0000000..5e1e172
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa27x.c
@@ -0,0 +1,324 @@
+/*
+ * Marvell PXA27x family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Heavily inspired from former arch/arm/mach-pxa/clock.c.
+ *
+ * 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; version 2 of the License.
+ *
+ * For non-devicetree platforms. Once pxa is fully converted to devicetree, this
+ * should go away.
+ */
+#include <linux/clk-provider.h>
+#include <mach/pxa2xx-regs.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include "clk-pxa2xx.h"
+
+static DEFINE_SPINLOCK(cken_lock);
+
+/* Crystal clock: 13MHz */
+#define BASE_CLK 13000000
+
+static unsigned long get_run_clock(unsigned long parent_rate)
+{
+ unsigned long ccsr = CCSR;
+ unsigned int l = ccsr & 0x1f;
+
+ return parent_rate * l;
+}
+
+static unsigned long get_turbo_clock(unsigned long parent_rate)
+{
+ unsigned long ccsr = CCSR;
+ unsigned int n2 = (ccsr >> 7) & 0xf;
+
+ return (get_run_clock(parent_rate) * n2) / 2;
+}
+
+static unsigned long get_halfturbo_clock(unsigned long parent_rate)
+{
+ return get_turbo_clock(parent_rate) / 2;
+}
+
+static unsigned long get_cpu_core_clock(unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht, b;
+
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+ b = clkcfg & (1 << 3);
+
+ if (ht)
+ return get_halfturbo_clock(parent_rate);
+ if (t)
+ return get_turbo_clock(parent_rate);
+ return get_run_clock(parent_rate);
+}
+
+static unsigned long get_sysbus_clock(unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht, b;
+
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+ b = clkcfg & (1 << 3);
+
+ return b ? get_run_clock(parent_rate) : get_run_clock(parent_rate) / 2;
+}
+
+static unsigned long get_memory_clock(unsigned long parent_rate)
+{
+ int cccr_a = CCCR & (1 << 25);
+ unsigned long ccsr = CCSR;
+ unsigned int l = ccsr & 0x1f;
+
+ cccr_a = CCCR & (1 << 25);
+ if (cccr_a)
+ return get_sysbus_clock(parent_rate);
+ if (l <= 10)
+ return get_run_clock(parent_rate);
+ if (l <= 20)
+ return get_run_clock(parent_rate) / 2;
+ return get_run_clock(parent_rate) / 4;
+};
+
+static unsigned long get_lcd_clock(unsigned long parent_rate)
+{
+ unsigned long ccsr = CCSR;
+ unsigned int l = ccsr & 0x1f;
+ int k = (l <= 7) ? 1 : (l <= 16) ? 2 : 4;
+
+ return get_run_clock(parent_rate) / k;
+}
+
+
+/*
+ * Get the clock frequency as reflected by CCSR and the turbo flag.
+ * We assume these values have been applied via a fcs.
+ * If info is not 0 we also display the current settings.
+ */
+unsigned int pxa27x_get_clk_frequency_khz(int info)
+{
+ unsigned long core_clk, run_mode, turbo_mode, mem_clk, sys_clk;
+
+ core_clk = get_cpu_core_clock(BASE_CLK);
+ run_mode = get_run_clock(BASE_CLK);
+ turbo_mode = get_turbo_clock(BASE_CLK);
+ mem_clk = get_memory_clock(BASE_CLK);
+ sys_clk = get_sysbus_clock(BASE_CLK);
+
+ if (info) {
+ pr_info("Run Mode clock: %ld.%02ldMHz\n",
+ run_mode / 1000000, (run_mode % 1000000) / 10000);
+ pr_info("Turbo Mode clock: %ld.%02ldMHz\n",
+ turbo_mode / 1000000, (turbo_mode % 1000000) / 10000);
+ pr_info("Memory clock: %ld.%02ldMHz\n",
+ mem_clk / 1000000, (mem_clk % 1000000) / 10000);
+ pr_info("System bus clock: %ld.%02ldMHz\n",
+ sys_clk / 1000000, (sys_clk % 1000000) / 10000);
+ }
+
+ return core_clk;
+}
+
+/*
+ * Return the current mem clock frequency as reflected by CCCR[A], B, and L
+ */
+static unsigned long clk_pxa27x_mem_getrate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return get_memory_clock(BASE_CLK);
+}
+
+static const struct clk_ops clk_pxa27x_mem_ops = {
+ .recalc_rate = clk_pxa27x_mem_getrate,
+};
+
+static unsigned long clk_pxa27x_lcd_getrate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return get_lcd_clock(BASE_CLK);
+}
+
+static const struct clk_ops clk_pxa27x_lcd_ops = {
+ .recalc_rate = clk_pxa27x_lcd_getrate,
+};
+
+static struct pxa27x_clocks_fixed_cken pxa27x_without_dt_clocks[] __initdata = {
+ PXA2_FIXED_RATE("pxa2xx-uart.0", NULL, FFUART, 14857000, 1),
+ PXA2_FIXED_RATE("pxa2xx-uart.1", NULL, BTUART, 14857000, 1),
+ PXA2_FIXED_RATE("pxa2xx-uart.2", NULL, STUART, 14857000, 1),
+ PXA2_FIXED_RATE(NULL, "UARTCLK", STUART, 14857000, 1),
+ PXA2_FIXED_RATE("pxa2xx-i2s", NULL, I2S, 14682000, 0),
+ PXA2_FIXED_RATE("pxa2xx-i2c.0", NULL, I2C, 32842000, 0),
+ PXA2_FIXED_RATE("pxa27x-udc", NULL, USB, 48000000, 5),
+ PXA2_FIXED_RATE("pxa2xx-mci.0", NULL, MMC, 19500000, 0),
+ PXA2_FIXED_RATE("pxa2xx-ir", "FICPCLK", FICP, 48000000, 0),
+ PXA2_FIXED_RATE("pxa27x-ohci", NULL, USBHOST, 48000000, 0),
+ PXA2_FIXED_RATE("pxa2xx-i2c.1", NULL, PWRI2C, 13000000, 0),
+ PXA2_FIXED_RATE("pxa27x-keypad", NULL, KEYPAD, 32768, 0),
+ PXA2_FIXED_RATE("pxa27x-ssp.0", NULL, SSP1, 13000000, 0),
+ PXA2_FIXED_RATE("pxa27x-ssp.1", NULL, SSP2, 13000000, 0),
+ PXA2_FIXED_RATE("pxa27x-ssp.2", NULL, SSP3, 13000000, 0),
+ PXA2_FIXED_RATE("pxa27x-pwm.0", NULL, PWM0, 13000000, 0),
+ PXA2_FIXED_RATE("pxa27x-pwm.1", NULL, PWM1, 13000000, 0),
+ PXA2_FIXED_RATE(NULL, "AC97CLK", AC97, 24576000, 0),
+ PXA2_FIXED_RATE(NULL, "AC97CONFCLK", AC97CONF, 24576000, 0),
+ PXA2_FIXED_RATE(NULL, "MSLCLK", MSL, 48000000, 0),
+ PXA2_FIXED_RATE(NULL, "USIMCLK", USIM, 48000000, 0),
+ PXA2_FIXED_RATE(NULL, "MSTKCLK", MEMSTK, 19500000, 0),
+ PXA2_FIXED_RATE(NULL, "IMCLK", IM, 0, 0),
+ PXA2_FIXED_RATE_AO("pxa27x-memc", "MEMCLK", MEMC, 0, 0),
+};
+
+static struct pxa27x_clocks_var_rate pxa27x_var_rate_clocks[] __initdata = {
+ PXA2_VAR_RATE("pxa2xx-fb", true, LCD, &clk_pxa27x_lcd_ops),
+ PXA2_VAR_RATE("pxa27x-camera.0", true, CAMERA, &clk_pxa27x_lcd_ops),
+ PXA2_VAR_RATE("pxa2xx-pcmcia", false, MEMC, &clk_pxa27x_mem_ops),
+};
+
+static int __init pxa27x_clocks_init(void)
+{
+ int i;
+ unsigned long rate, flags;
+ struct clk *clk_parent, *clk;
+ struct pxa27x_clocks_fixed_cken *fclock;
+ struct pxa27x_clocks_var_rate *vclock;
+ char parent_name[80], *name;
+
+ /*
+ * Once device-tree conversion is complete :
+ * if (of_have_populated_dt())
+ * return 0;
+ */
+ for (i = 0; i < ARRAY_SIZE(pxa27x_without_dt_clocks); i++) {
+ fclock = &pxa27x_without_dt_clocks[i];
+ clk_parent = NULL;
+ rate = fclock->rate;
+ snprintf(parent_name, sizeof(parent_name), "clk-%lu", rate);
+ flags = CLK_GET_RATE_NOCACHE | CLK_IS_ROOT;
+ clk_parent = clk_register_fixed_rate(NULL, parent_name, NULL,
+ flags, rate);
+ name = fclock->name ? fclock->name : fclock->con_id;
+ flags = fclock->always_on ? CLK_IGNORE_UNUSED : 0;
+ clk = clk_register_gate(NULL, name, parent_name, flags,
+ (void *)&CKEN, fclock->cken, 0,
+ &cken_lock);
+ if (!IS_ERR(clk))
+ clk_register_clkdev(clk, fclock->con_id, fclock->name);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pxa27x_var_rate_clocks); i++) {
+ vclock = &pxa27x_var_rate_clocks[i];
+ flags = CLK_GET_RATE_NOCACHE;
+ if (vclock->has_cken) {
+ snprintf(parent_name, sizeof(parent_name), "clk-%s",
+ vclock->name);
+ clk_parent = clk_pxa2xx_register_dummy(parent_name,
+ vclock->clk_ops);
+ clk = clk_register_gate(NULL, vclock->name, parent_name,
+ flags, (void *)&CKEN,
+ vclock->cken, 0, &cken_lock);
+ } else {
+ clk = clk_pxa2xx_register_dummy(vclock->name,
+ vclock->clk_ops);
+ }
+ if (!IS_ERR(clk))
+ clk_register_clkdev(clk, NULL, vclock->name);
+ }
+ return 0;
+}
+
+postcore_initcall(pxa27x_clocks_init);
+
+struct clk_pxa27x_core {
+ struct clk_hw hw;
+ unsigned long (*get_rate)(unsigned long parent_rate);
+};
+
+static unsigned long clk_pxa27x_ops_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pxa27x_core *clk_core =
+ container_of(hw, struct clk_pxa27x_core, hw);
+
+ return clk_core->get_rate(parent_rate);
+}
+
+const struct clk_ops clk_pxa27x_ops = {
+ .recalc_rate = clk_pxa27x_ops_get_rate,
+};
+
+static struct clk __init *clk_register_pxa27x_core(const char *name,
+ const char *parent_name,
+ unsigned long (*get_rate)(unsigned long))
+{
+ struct clk_pxa27x_core *clk_core;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ clk_core = kzalloc(sizeof(*clk_core), GFP_KERNEL);
+ if (!clk_core) {
+ pr_err("%s: could not allocate pxa27x_core clock\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_pxa27x_ops;
+ init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ clk_core->get_rate = get_rate;
+ clk_core->hw.init = &init;
+ clk = clk_register(NULL, &clk_core->hw);
+ return clk;
+}
+
+#define NB_CORE_CLOCKS 7
+static struct clk *core_clks[NB_CORE_CLOCKS];
+static struct clk_onecell_data onecell_data = {
+ .clks = core_clks,
+ .clk_num = NB_CORE_CLOCKS,
+};
+
+static void __init pxa27x_core_clocks_init(struct device_node *np)
+{
+ const char *pname;
+
+ pname = of_clk_get_parent_name(np, 0);
+ onecell_data.clks[0] =
+ clk_register_pxa27x_core("run mode", pname, get_run_clock);
+ onecell_data.clks[1] =
+ clk_register_pxa27x_core("turbo mode", pname, get_turbo_clock);
+ onecell_data.clks[2] =
+ clk_register_pxa27x_core("halt-turbo mode", pname,
+ get_halfturbo_clock);
+ onecell_data.clks[3] =
+ clk_register_pxa27x_core("cpu core", pname,
+ get_cpu_core_clock);
+ onecell_data.clks[4] =
+ clk_register_pxa27x_core("system bus", pname,
+ get_sysbus_clock);
+ onecell_data.clks[5] =
+ clk_register_pxa27x_core("memory", pname,
+ get_memory_clock);
+ onecell_data.clks[5] =
+ clk_register_pxa27x_core("lcd", pname,
+ get_lcd_clock);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
+}
+CLK_OF_DECLARE(pxa2xx_clks, "marvell,pxa270-core-clocks",
+ pxa27x_core_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa2xx.c b/drivers/clk/pxa/clk-pxa2xx.c
new file mode 100644
index 0000000..6e6d657
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa2xx.c
@@ -0,0 +1,74 @@
+/*
+ * Marvell PXA2xx family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/syscore_ops.h>
+#include <mach/pxa2xx-regs.h>
+
+struct dummy_clk {
+ struct clk_hw hw;
+};
+
+struct clk * __init clk_pxa2xx_register_dummy(const char *name,
+ const struct clk_ops *ops)
+{
+ struct clk_init_data init;
+ struct dummy_clk *clock;
+ struct clk *clk;
+
+ clock = kzalloc(sizeof(*clock), GFP_KERNEL);
+ if (!clock) {
+ pr_err("%s: failed to allocate PXA2xx clock.\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
+ clock->hw.init = &init;
+ clk = clk_register(NULL, &clock->hw);
+ if (IS_ERR(clk))
+ kfree(clock);
+ return clk;
+}
+
+
+#ifdef CONFIG_PM
+static uint32_t saved_cken;
+
+static int pxa2xx_clock_suspend(void)
+{
+ saved_cken = CKEN;
+ return 0;
+}
+
+static void pxa2xx_clock_resume(void)
+{
+ CKEN = saved_cken;
+}
+#else
+#define pxa2xx_clock_suspend NULL
+#define pxa2xx_clock_resume NULL
+#endif
+
+static struct syscore_ops pxa2xx_clock_syscore_ops = {
+ .suspend = pxa2xx_clock_suspend,
+ .resume = pxa2xx_clock_resume,
+};
+
+static int __init pxa2xx_clocks_init(void)
+{
+ register_syscore_ops(&pxa2xx_clock_syscore_ops);
+ return 0;
+}
+
+postcore_initcall(pxa2xx_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa2xx.h b/drivers/clk/pxa/clk-pxa2xx.h
new file mode 100644
index 0000000..79f62af
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa2xx.h
@@ -0,0 +1,47 @@
+/*
+ * Marvell PXA27x family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * 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; version 2 of the License.
+ *
+ * For non-devicetree platforms. Once pxa is fully converted to devicetree, this
+ * should go away.
+ */
+
+#ifndef _CLK_PXA2XX_H_
+#define _CLK_PXA2XX_H_
+
+#define PXA2_VAR_RATE(_name, _has_cken, _cken, _clk_ops) \
+ { .name = _name, .has_cken = _has_cken, .cken = CKEN_ ## _cken, \
+ .clk_ops = _clk_ops }
+
+struct pxa27x_clocks_var_rate {
+ char *name;
+ bool has_cken;
+ unsigned int cken;
+ const struct clk_ops *clk_ops;
+};
+
+#define PXA2_FIXED_RATE(_name, _con_id, _cken, _rate, _delay) \
+ { .name = _name, .con_id = _con_id, .cken = CKEN_ ## _cken, \
+ .rate = _rate, .delay = _delay, .always_on = false, }
+#define PXA2_FIXED_RATE_AO(_name, _con_id, _cken, _rate, _delay) \
+ { .name = _name, .con_id = _con_id, .cken = CKEN_ ## _cken, \
+ .rate = _rate, .delay = _delay, .always_on = true, }
+
+struct pxa27x_clocks_fixed_cken {
+ char *name;
+ char *con_id;
+ unsigned int cken;
+ unsigned long rate;
+ int delay;
+ bool always_on;
+};
+
+extern struct clk * __init clk_pxa2xx_register_dummy(const char *name,
+ const struct clk_ops *ops);
+
+#endif
--
2.0.0.rc2
More information about the linux-arm-kernel
mailing list