* [PATCH v2 5/9] clk: stm32f4: Add I2S clock
From: gabriel.fernandez at st.com @ 2016-11-24 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479998749-20358-1-git-send-email-gabriel.fernandez@st.com>
From: Gabriel Fernandez <gabriel.fernandez@st.com>
This patch introduces I2S clock for stm32f4 soc.
The I2S clock could be derived from an external clock or from pll-i2s
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
Documentation/devicetree/bindings/clock/st,stm32-rcc.txt | 4 +++-
drivers/clk/clk-stm32f4.c | 14 +++++++++++++-
include/dt-bindings/clock/stm32f4-clock.h | 3 ++-
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
index 4cd08da6..8c1ca68 100644
--- a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
+++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
@@ -19,6 +19,7 @@ Required properties:
use.
- clocks: External oscillator clock phandle
- high speed external clock signal (HSE)
+ - external I2S clock (I2S_CKIN)
Example:
@@ -27,7 +28,7 @@ Example:
#clock-cells = <2>
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
reg = <0x40023800 0x400>;
- clocks = <&clk_hse>;
+ clocks = <&clk_hse>, <&clk_i2s_ckin>;
};
Specifying gated clocks
@@ -77,6 +78,7 @@ The secondary index is bound with the following magic numbers:
6 PLL_VCO_I2S (vco frequency of I2S pll)
7 PLL_VCO_SAI (vco frequency of SAI pll)
8 CLK_LCD (LCD-TFT)
+ 9 CLK_I2S (I2S clocks)
Example:
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 86244fc..3063b30 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -935,6 +935,8 @@ static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
static const char *lcd_parent[1] = { "pllsai-r-div" };
+static const char *i2s_parents[2] = { "plli2s-r", NULL };
+
struct stm32_aux_clk {
int idx;
const char *name;
@@ -969,6 +971,12 @@ struct stm32f4_clk_data {
STM32F4_RCC_APB2ENR, 26,
CLK_SET_RATE_PARENT
},
+ {
+ CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
+ STM32F4_RCC_CFGR, 23, 1,
+ NO_GATE, 0,
+ CLK_SET_RATE_PARENT
+ },
};
static const struct stm32f4_clk_data stm32f429_clk_data = {
@@ -1063,7 +1071,7 @@ static struct clk_hw *stm32_register_aux_clk(const char *name,
static void __init stm32f4_rcc_init(struct device_node *np)
{
- const char *hse_clk;
+ const char *hse_clk, *i2s_in_clk;
int n;
const struct of_device_id *match;
const struct stm32f4_clk_data *data;
@@ -1098,6 +1106,10 @@ static void __init stm32f4_rcc_init(struct device_node *np)
hse_clk = of_clk_get_parent_name(np, 0);
+ i2s_in_clk = of_clk_get_parent_name(np, 1);
+
+ i2s_parents[1] = i2s_in_clk;
+
clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
16000000, 160000);
pllcfgr = readl(base + STM32F4_RCC_PLLCFGR);
diff --git a/include/dt-bindings/clock/stm32f4-clock.h b/include/dt-bindings/clock/stm32f4-clock.h
index 1be4a3a..b129ab9 100644
--- a/include/dt-bindings/clock/stm32f4-clock.h
+++ b/include/dt-bindings/clock/stm32f4-clock.h
@@ -28,7 +28,8 @@
#define PLL_VCO_I2S 6
#define PLL_VCO_SAI 7
#define CLK_LCD 8
+#define CLK_I2S 9
-#define END_PRIMARY_CLK 9
+#define END_PRIMARY_CLK 10
#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/9] clk: stm32f4: Add SAI clocks
From: gabriel.fernandez at st.com @ 2016-11-24 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479998749-20358-1-git-send-email-gabriel.fernandez@st.com>
From: Gabriel Fernandez <gabriel.fernandez@st.com>
This patch introduces SAI clocks for stm32f4 socs.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
Documentation/devicetree/bindings/clock/st,stm32-rcc.txt | 2 ++
drivers/clk/clk-stm32f4.c | 16 ++++++++++++++++
include/dt-bindings/clock/stm32f4-clock.h | 4 +++-
3 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
index 8c1ca68..8f93740 100644
--- a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
+++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
@@ -79,6 +79,8 @@ The secondary index is bound with the following magic numbers:
7 PLL_VCO_SAI (vco frequency of SAI pll)
8 CLK_LCD (LCD-TFT)
9 CLK_I2S (I2S clocks)
+ 10 CLK_SAI1 (audio clocks)
+ 11 CLK_SAI2
Example:
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 3063b30..02339d1 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -937,6 +937,9 @@ static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
static const char *i2s_parents[2] = { "plli2s-r", NULL };
+static const char *sai_parents[4] = { "pllsai-q-div", "plli2s-q-div", NULL,
+ "no-clock" };
+
struct stm32_aux_clk {
int idx;
const char *name;
@@ -977,6 +980,18 @@ struct stm32f4_clk_data {
NO_GATE, 0,
CLK_SET_RATE_PARENT
},
+ {
+ CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 20, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 22, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
};
static const struct stm32f4_clk_data stm32f429_clk_data = {
@@ -1109,6 +1124,7 @@ static void __init stm32f4_rcc_init(struct device_node *np)
i2s_in_clk = of_clk_get_parent_name(np, 1);
i2s_parents[1] = i2s_in_clk;
+ sai_parents[2] = i2s_in_clk;
clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
16000000, 160000);
diff --git a/include/dt-bindings/clock/stm32f4-clock.h b/include/dt-bindings/clock/stm32f4-clock.h
index b129ab9..5431f00 100644
--- a/include/dt-bindings/clock/stm32f4-clock.h
+++ b/include/dt-bindings/clock/stm32f4-clock.h
@@ -29,7 +29,9 @@
#define PLL_VCO_SAI 7
#define CLK_LCD 8
#define CLK_I2S 9
+#define CLK_SAI1 10
+#define CLK_SAI2 11
-#define END_PRIMARY_CLK 10
+#define END_PRIMARY_CLK 12
#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v2 7/9] clk: stm32f4: SDIO & 48Mhz clock management for STM32F469 board
From: gabriel.fernandez at st.com @ 2016-11-24 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479998749-20358-1-git-send-email-gabriel.fernandez@st.com>
From: Gabriel Fernandez <gabriel.fernandez@st.com>
In the stm32f469 soc, the 48Mhz clock could be derived from pll-q or
from pll-sai-p.
The SDIO clock could be also derived from 48Mhz or from sys clock.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
drivers/clk/clk-stm32f4.c | 49 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 02339d1..161449d 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -206,7 +206,7 @@ struct stm32f4_gate_data {
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
- { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" },
+ { STM32F4_RCC_APB2ENR, 11, "sdio", "sdmux" },
{ STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
@@ -940,6 +940,10 @@ static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
static const char *sai_parents[4] = { "pllsai-q-div", "plli2s-q-div", NULL,
"no-clock" };
+static const char *pll48_parents[2] = { "pll-q", "pllsai-p" };
+
+static const char *sdmux_parents[2] = { "pll48", "sys" };
+
struct stm32_aux_clk {
int idx;
const char *name;
@@ -994,6 +998,45 @@ struct stm32f4_clk_data {
},
};
+static const struct stm32_aux_clk stm32f469_aux_clk[] = {
+ {
+ CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent),
+ NO_MUX, 0, 0,
+ STM32F4_RCC_APB2ENR, 26,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
+ STM32F4_RCC_CFGR, 23, 1,
+ NO_GATE, 0,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 20, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 22, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents),
+ STM32F4_RCC_DCKCFGR, 27, 1,
+ NO_GATE, 0,
+ 0
+ },
+ {
+ NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents),
+ STM32F4_RCC_DCKCFGR, 28, 1,
+ NO_GATE, 0,
+ 0
+ },
+};
+
static const struct stm32f4_clk_data stm32f429_clk_data = {
.gates_data = stm32f429_gates,
.gates_map = stm32f42xx_gate_map,
@@ -1008,8 +1051,8 @@ struct stm32f4_clk_data {
.gates_map = stm32f46xx_gate_map,
.gates_num = ARRAY_SIZE(stm32f469_gates),
.pll_data = stm32f469_pll,
- .aux_clk = stm32f429_aux_clk,
- .aux_clk_num = ARRAY_SIZE(stm32f429_aux_clk),
+ .aux_clk = stm32f469_aux_clk,
+ .aux_clk_num = ARRAY_SIZE(stm32f469_aux_clk),
};
static const struct of_device_id stm32f4_of_match[] = {
--
1.9.1
^ permalink raw reply related
* [PATCH v2 8/9] arm: dts: stm32f4: Add external I2S clock
From: gabriel.fernandez at st.com @ 2016-11-24 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479998749-20358-1-git-send-email-gabriel.fernandez@st.com>
From: Gabriel Fernandez <gabriel.fernandez@st.com>
This patch adds an external I2S clock in the DT.
The I2S clock could be derived from an external I2S clock or by I2S pll.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index e4dae0e..7c7dfbd 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -68,6 +68,12 @@
compatible = "fixed-clock";
clock-frequency = <32000>;
};
+
+ clk_i2s_ckin: i2s-ckin {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <0>;
+ };
};
soc {
@@ -362,7 +368,7 @@
#clock-cells = <2>;
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
reg = <0x40023800 0x400>;
- clocks = <&clk_hse>;
+ clocks = <&clk_hse>, <&clk_i2s_ckin>;
st,syscfg = <&pwrcfg>;
};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 9/9] arm: dts: stm32f4: Include auxiliary stm32f4 clock definition
From: gabriel.fernandez at st.com @ 2016-11-24 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479998749-20358-1-git-send-email-gabriel.fernandez@st.com>
From: Gabriel Fernandez <gabriel.fernandez@st.com>
This patch include auxiliary clock definition (clocks which are not derived
from system clock.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 7c7dfbd..223dc12 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -48,6 +48,7 @@
#include "skeleton.dtsi"
#include "armv7-m.dtsi"
#include <dt-bindings/pinctrl/stm32f429-pinfunc.h>
+#include <dt-bindings/clock/stm32f4-clock.h>
/ {
clocks {
--
1.9.1
^ permalink raw reply related
* TDA998x crash on HDLCD probe failure
From: Liviu Dudau @ 2016-11-24 14:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f951de07-b4c9-f10e-bd1c-0ded455ca18f@arm.com>
On Thu, Nov 24, 2016 at 02:40:50PM +0000, Robin Murphy wrote:
> On 24/11/16 13:49, Robin Murphy wrote:
> > On 24/11/16 13:29, Russell King - ARM Linux wrote:
> >> On Thu, Nov 24, 2016 at 01:18:39PM +0000, Robin Murphy wrote:
> >>> Hi Liviu, Russell,
> >>>
> >>> I'd been meaning to try digging into this if it hadn't gone away since I
> >>> first noticed it, but I don't really have the time and it still happens
> >>> with 4.9-rc and today's -next. Representative splat below, but in
> >>> summary what happens is that if the HDLCD fails to probe, the TDA998x
> >>> connector seems to get cleaned up twice, resulting in a NULL dereference
> >>> the second time. I got as far as sketching out the following flow from a
> >>> debug session (on the same 4.8-rc2 kernel), but I don't know nearly
> >>> enough to tell which driver is at fault:
> >>>
> >>> hdlcd_drm_bind
> >>> -> drm_fbdev_cma_init (fails)
> >>> ...
> >>> -> drm_mode_config_cleanup
> >>> ...
> >>> -> drm_connector_cleanup
> >>> -> component_unbind_all
> >>> ...
> >>> -> tda998x_unbind
> >>> -> drm_connector_cleanup (NULL connector)
> >>>
> >>> It's easily reproduced on Juno by booting arm64 defconfig with
> >>> CONFIG_CMA_SIZE_MBYTES=1 and a sufficiently large monitor connected to
> >>> warrant a >1MB framebuffer.
> >>
> >> It looks to me like a hdlcd bug.
> >>
> >> The probe path operates in this order:
> >>
> >> - allocates hdlcd - 1
> >> - allocates drm device - 2
> >> - drm_mode_config_init - 3
> >> - hdlcd_load - 4
> >> - binds all components - 5
> >> - enables runtime PM - 6
> >> - drm_vblank_init - 7
> >> - drm_mode_config_reset - 8
> >> - drm_kms_helper_poll_init - 9
> >> - drm_fbdev_cma_init - 10
> >> - drm_dev_register - 11
> >>
> >> However, the cleanup operates in this order:
> >> - drm_fbdev_cma_fini - undoes 10
> >> - drm_kms_helper_poll_fini - undoes 9
> >> - drm_mode_config_cleanup - undoes 3
> >> - drm_vblank_cleanup - undoes 7
> >> - pm_runtime_disable - undoes 6
> >> - component_unbind_all - undoes 5
> >> - drm_irq_uninstall - undoes 4
> >> - of_reserved_mem_device_release - undoes other half of 4
> >> - drm_dev_unref - undoes 2
> >>
> >> Spot the step which is out of the correct order - drm_mode_config_cleanup()
> >> is misplaced - it's reversing the actions of drm_mode_config_init(), not
> >> drm_mode_config_reset().
> >
> > Thanks for the explanation - that saves at least a day's worth of me
> > trying to understand DRM code :)
> >
> >> So, drm_mode_config_cleanup() should be much later, after step 4 has
> >> been undone, otherwise there are paths that leave various DRM objects
> >> (created by drm_mode_create_standard_properties()) referenced, and
> >> will cause problems exactly like you're seeing here.
> >
> > Liviu, can I leave this with you then?
>
> That said, I just tried the quick and obvious thing over lunch and it
> does *seem* to be OK:
Hi Robin,
Thanks for tracking this down and for providing a patch.
>
> ----->8-----
> From: Robin Murphy <robin.murphy@arm.com>
> Subject: [PATCH] drm: hdlcd: Fix cleanup order
>
> If hdlcd_drm_bind() fails at drm_fbdev_cma_init(), its cleanup will call
> drm_mode_config_cleanup() as if to balance drm_mode_config_reset(). The
> net result is that drm_connector_cleanup() will clean up the active
> connectors long before component_unbind_all() gets called, so when the
> connector later tries to clean up itself after being unbound, Bad Things
> can happen:
>
> [ 4.121888] Unable to handle kernel NULL pointer dereference at
> virtual address 00000000
> [ 4.129951] pgd = ffffff80091e0000
> [ 4.133345] [00000000] *pgd=00000009ffffe003, *pud=00000009ffffe003,
> *pmd=0000000000000000
> [ 4.141613] Internal error: Oops: 96000005 [#1] PREEMPT SMP
> [ 4.147144] Modules linked in:
> [ 4.150188] CPU: 0 PID: 122 Comm: kworker/u12:2 Not tainted
> 4.8.0-rc2+ #989
> [ 4.157097] Hardware name: ARM Juno development board (r1) (DT)
> [ 4.162981] Workqueue: deferwq deferred_probe_work_func
> [ 4.168173] task: ffffffc975d93200 task.stack: ffffffc975dac000
> [ 4.174055] PC is at drm_connector_cleanup+0x58/0x1c0
> [ 4.179074] LR is at tda998x_unbind+0x24/0x40
> [ 4.183401] pc : [<ffffff80084c46f0>] lr : [<ffffff800850414c>]
> pstate: 00000045
> [ 4.190750] sp : ffffffc975dafa10
> [ 4.194041] x29: ffffffc975dafa10 x28: ffffffc9768152a8
> [ 4.199325] x27: ffffffc97ff46450 x26: ffffff8008d99000
> [ 4.204608] x25: dead000000000100 x24: dead000000000200
> [ 4.209891] x23: ffffffc976bf91e8 x22: 0000000000000000
> [ 4.215172] x21: ffffffc976bf9170 x20: ffffffc976bf9170
> [ 4.220454] x19: ffffffc976bf9018 x18: 0000000000000000
> [ 4.225737] x17: 0000000074ce71ee x16: 000000008ff5d35f
> [ 4.231019] x15: ffffffc97681e91c x14: ffffffffffffffff
> [ 4.236301] x13: ffffffc97681e185 x12: 0000000000000038
> [ 4.241583] x11: 0101010101010101 x10: 0000000000000000
> [ 4.246866] x9 : 0000000040000000 x8 : 0000000000210d00
> [ 4.252148] x7 : ffffffc97fea8c00 x6 : 000000000000001b
> [ 4.257430] x5 : ffffff80084b7b8c x4 : 0000000000000080
> [ 4.262712] x3 : ffffff8008504128 x2 : ffffffc975df3800
> [ 4.267993] x1 : 0000000000000000 x0 : 0000000000000000
> ...
> [ 4.750937] [<ffffff80084c46f0>] drm_connector_cleanup+0x58/0x1c0
> [ 4.756990] [<ffffff800850414c>] tda998x_unbind+0x24/0x40
> [ 4.762354] [<ffffff8008507918>] component_unbind.isra.4+0x28/0x50
> [ 4.768492] [<ffffff8008507a0c>] component_unbind_all+0xcc/0xd8
> [ 4.774373] [<ffffff80084d5adc>] hdlcd_drm_bind+0x234/0x418
> [ 4.779909] [<ffffff8008507b58>] try_to_bring_up_master+0x140/0x1a0
> [ 4.786133] [<ffffff8008507c50>] component_add+0x98/0x170
> [ 4.791496] [<ffffff8008504b90>] tda998x_probe+0x18/0x20
> [ 4.796774] [<ffffff80086bf914>] i2c_device_probe+0x164/0x258
> [ 4.802481] [<ffffff800850d094>] driver_probe_device+0x204/0x2b0
> [ 4.808447] [<ffffff800850d28c>] __device_attach_driver+0x9c/0xf8
> [ 4.814498] [<ffffff800850b108>] bus_for_each_drv+0x58/0x98
> [ 4.820033] [<ffffff800850cd64>] __device_attach+0xc4/0x138
> [ 4.825567] [<ffffff800850d338>] device_initial_probe+0x10/0x18
> [ 4.831446] [<ffffff800850c124>] bus_probe_device+0x94/0xa0
> [ 4.836981] [<ffffff800850c5b0>] deferred_probe_work_func+0x78/0xb0
> [ 4.843207] [<ffffff80080d2998>] process_one_work+0x118/0x378
> [ 4.848914] [<ffffff80080d2c40>] worker_thread+0x48/0x498
> [ 4.854276] [<ffffff80080d8918>] kthread+0xd0/0xe8
> [ 4.859036] [<ffffff8008082e90>] ret_from_fork+0x10/0x40
> [ 4.864314] Code: f2fbd5b9 f2fbd5b8 f8478ee0 eb17001f (f9400013)
> [ 4.870472] ---[ end trace a643cfe4ce1d838b ]---
>
> Fix this by moving the drm_mode_config_cleanup() much later such that it
> correctly balances drm_mode_config_init().
>
> Suggested-by: Russell King <linux@armlinux.org.uk>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Acked-by: Liviu Dudau <Liviu.Dudau@arm.com>
Best regards,
Liviu
> ---
> drivers/gpu/drm/arm/hdlcd_drv.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c
> b/drivers/gpu/drm/arm/hdlcd_drv.c
> index 59b76054edc9..1a4fff7c0a7c 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -420,7 +420,6 @@ static int hdlcd_drm_bind(struct device *dev)
>
> err_fbdev:
> drm_kms_helper_poll_fini(drm);
> - drm_mode_config_cleanup(drm);
> drm_vblank_cleanup(drm);
> err_vblank:
> pm_runtime_disable(drm->dev);
> @@ -432,6 +431,7 @@ static int hdlcd_drm_bind(struct device *dev)
> drm_irq_uninstall(drm);
> of_reserved_mem_device_release(drm->dev);
> err_free:
> + drm_mode_config_cleanup(drm);
> dev_set_drvdata(dev, NULL);
> drm_dev_unref(drm);
>
> --
> 2.10.2.dirty
>
--
====================
| I would like to |
| fix the world, |
| but they're not |
| giving me the |
\ source code! /
---------------
?\_(?)_/?
^ permalink raw reply
* [PATCH] ARM/ARM64: defconfig: drop GPIO_SYSFS on multiplatforms
From: Linus Walleij @ 2016-11-24 14:57 UTC (permalink / raw)
To: linux-arm-kernel
The sysfs ABI to GPIO is marked obsolete and should not be
encouraged. Users should be encouraged to switch to using the
character device.
Let's begin by removing it from the multi defconfigs. Then
as time goes by I can aggressively remove it from other
defconfigs.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ARM SoC folks: please apply this for whatever branch holds
defconfig changes.
---
arch/arm/configs/multi_v5_defconfig | 1 -
arch/arm/configs/multi_v7_defconfig | 1 -
arch/arm64/configs/defconfig | 1 -
3 files changed, 3 deletions(-)
diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig
index 2658b80fa263..361686a362f1 100644
--- a/arch/arm/configs/multi_v5_defconfig
+++ b/arch/arm/configs/multi_v5_defconfig
@@ -150,7 +150,6 @@ CONFIG_SPI=y
CONFIG_SPI_ATMEL=y
CONFIG_SPI_IMX=y
CONFIG_SPI_ORION=y
-CONFIG_GPIO_SYSFS=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_POWER_RESET_QNAP=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 11f37ed1dbff..ed0053da56c6 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -404,7 +404,6 @@ CONFIG_PINCTRL_MSM8X74=y
CONFIG_PINCTRL_MSM8916=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_PINCTRL_QCOM_SSBI_PMIC=y
-CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_GPIO_DAVINCI=y
CONFIG_GPIO_DWAPB=y
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index dab2cb0c1f1c..cea5a9d73506 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -252,7 +252,6 @@ CONFIG_PINCTRL_MSM8916=y
CONFIG_PINCTRL_MSM8996=y
CONFIG_PINCTRL_QDF2XXX=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
-CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_DWAPB=y
CONFIG_GPIO_PL061=y
CONFIG_GPIO_RCAR=y
--
2.7.4
^ permalink raw reply related
* [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Ziji Hu @ 2016-11-24 15:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPDyKFr9uEjVQmTNP0KK8Zj9mxCW3i564E=47vTK0RLvXCjw3Q@mail.gmail.com>
Hi Ulf,
On 2016/11/24 21:34, Ulf Hansson wrote:
> On 24 November 2016 at 13:41, Ziji Hu <huziji@marvell.com> wrote:
>> Hi Ulf,
>>
>> On 2016/11/24 18:43, Ulf Hansson wrote:
>>> On 31 October 2016 at 12:09, Gregory CLEMENT
>>> <gregory.clement@free-electrons.com> wrote:
>>>> From: Ziji Hu <huziji@marvell.com>
>>>>
>> <snip>
>>>> +static int xenon_emmc_signal_voltage_switch(struct mmc_host *mmc,
>>>> + struct mmc_ios *ios)
>>>> +{
>>>> + unsigned char voltage = ios->signal_voltage;
>>>> +
>>>> + if ((voltage == MMC_SIGNAL_VOLTAGE_330) ||
>>>> + (voltage == MMC_SIGNAL_VOLTAGE_180))
>>>> + return __emmc_signal_voltage_switch(mmc, voltage);
>>>> +
>>>> + dev_err(mmc_dev(mmc), "Unsupported signal voltage: %d\n",
>>>> + voltage);
>>>> + return -EINVAL;
>>>
>>> This wrapper function seems unnessarry. It only adds a dev_err(), so
>>> then might as well do that in __emmc_signal_voltage_switch().
>>>
>> Sure. Will merge it back to __emmc_signal_voltage_switch().
>>
>>>> +}
>>>> +
>>>> +static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
>>>> + struct mmc_ios *ios)
>>>> +{
>>>> + struct sdhci_host *host = mmc_priv(mmc);
>>>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>>> + struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>>>> +
>>>> + /*
>>>> + * Before SD/SDIO set signal voltage, SD bus clock should be
>>>> + * disabled. However, sdhci_set_clock will also disable the Internal
>>>> + * clock in mmc_set_signal_voltage().
>>>
>>> If that's the case then that is wrong in the generic sdhci code.
>>> What's the reason why it can't be fixed there instead of having this
>>> workaround?
>>>
>> In my very own opinion, SD Spec doesn't specify whether SDCLK should be
>> enabled or not during power setting.
>> Enabling SDCLK might be a special condition only required by our SDHC.
>> I try to avoid breaking other vendors' SDHC functionality
>> if their SDHCs require SDCLK disabled.
>> Thus I prefer to keep it inside our SDHC driver.
>
> I let Adrian comment on this.
>
> For sure we should avoid breaking other sdhci variant, but on the
> other hand *if* the generic code is wrong we should fix it!
>
Of course.
>>
>>>> + * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
>>>> + * Thus here manually enable internal clock.
>>>> + *
>>>> + * After switch completes, it is unnecessary to disable internal clock,
>>>> + * since keeping internal clock active obeys SD spec.
>>>> + */
>>>> + enable_xenon_internal_clk(host);
>>>> +
>>>> + if (priv->emmc_slot)
>>>> + return xenon_emmc_signal_voltage_switch(mmc, ios);
>>>> +
>>>> + return sdhci_start_signal_voltage_switch(mmc, ios);
>>>> +}
>>>> +
>>>> +/*
>>>> + * After determining which slot is used for SDIO,
>>>> + * some additional task is required.
>>>> + */
>>>> +static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
>>>> +{
>>>> + struct sdhci_host *host = mmc_priv(mmc);
>>>> + u32 reg;
>>>> + u8 slot_idx;
>>>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>>> + struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>>>> +
>>>> + /* Link the card for delay adjustment */
>>>> + priv->card_candidate = card;
>>>> + /* Set tuning functionality of this slot */
>>>> + xenon_slot_tuning_setup(host);
>>>
>>> This looks weird. I assume this can be done as a part of the regular
>>> tuning seqeunce!?
>>>
>> It is our SDHC specific preparation prior to tuning, rather than a
>> standard step in spec.
>> Thus I leave it inside our driver.
>
> My point is that this isn't the purpose of ->init_card(). thus you are
> abusing it.
>
> Try to make it work in another way, please. I think you can.
>
Got it.
I will move it to our host specific probe function.
>>
>>>> +
>>>> + slot_idx = priv->slot_idx;
>>>> + if (!mmc_card_sdio(card)) {
>>>> + /* Clear SDIO Card Inserted indication */
>>>
>>> Why do you need this?
>>>
>>> If you need to reset this, I think it's better to do it from
>>> ->set_ios() at MMC_POWER_OFF.
>>>
>> This field indicates SDIO card and controls async interrupt feature
>> of SDIO in our SDHC.
>> This async interrupt feature is enabled when SDIO card is inserted.
>> It should be disabled if SD card is inserted instead.
>
> What do you mean by SDIO async interupts? Are you talking about SDIO
> irqs on DAT1 line?
>
> Those is supposed to be enabled when someone explicitly requests them,
> not when the card is inserted.
> In other words when an SDIO func driver have called sdio_claim_irq().
>
> Moreover, we have ->enable_sdio_irq() ops that deals with this.
>
Yes. I mean the SDIO irqs on DAT1 line in async mode.
This field enables our host to recognize the async SDIO irq from SDIO device.
It controls our host side behavior, other than the SDIO device.
I think ->enable_sdio_irq() is a more reasonable place to put this workraound.
I will export sdhci_enable_sdio_irq() and implement out host own
enable_sdio_irq() calling sdhci_enable_sdio)irq() plus this workaround.
Does it sound reasonable to you?
> [...]
>
>>>> +
>>>> + /*
>>>> + * Xenon Specific property:
>>>> + * emmc: explicitly indicate whether this slot is for eMMC
>>>> + * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
>>>> + * tun-count: the interval between re-tuning
>>>> + * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
>>>> + */
>>>> + if (of_property_read_bool(np, "marvell,xenon-emmc"))
>>>> + priv->emmc_slot = true;
>>>
>>> So, you need this because of the eMMC voltage switch behaviour, right?
>>>
>>> Then I would rather like to describe this a generic DT bindings for
>>> the eMMC voltage level support. There have acutally been some earlier
>>> discussions for this, but we haven't yet made some changes.
>>>
>>> I think what is missing is a mmc-ddr-3_3v DT binding, which when set,
>>> allows the host driver to accept I/O voltage switches to 3.3V. If not
>>> supported the ->start_signal_voltage_switch() ops may return -EINVAL.
>>> This would inform the mmc core to move on to the next supported
>>> voltage level. There might be some minor additional changes to the mmc
>>> card initialization sequence, but those should be simple.
>>>
>>> I can help out to look into this, unless you want to do it yourself of course!?
>>>
>> Yes. One of the reasons is to provide eMMC specific voltage setting.
>> But in my very own opinion, it should be irrelevant to voltage level.
>> The eMMC voltage setting on our SDHC is different from SD/SDIO voltage switch.
>> It will become more complex with different SOC implementation details.
>
> Got it. Although I think we can cope with that fine just by using the
> different SD/eMMC speed modes settings defined in DT (or from the
> SDHCI caps register)
>
In my very opinion, I'm not sure if there is any corner case that driver cannot
determine the eMMC card type from DT and SDHC caps.
>> Unfortunately, MMC driver cannot determine the card type yet when eMMC voltage
>> setting should be executed.
>> Thus an flag is required here to tell driver to execute eMMC voltage setting.
>>
>> Besides, additional eMMC specific settings might be implemented in future, besides
>> voltage setting. Most of them should be completed before MMC driver recognizes the
>> card type. Thus I have to keep this flag to indicate current SDHC is for eMMC.
>
> I doubt you will need a generic "eMMC" flag, but let's see when we go forward.
>
> Currently it's clear you don't need such a flag, so I will submit a
> change adding a DT binding for "mmc-ddr-3_3v" then we can take it from
> there, to see if it suits your needs.
>
Actually, our eMMC is usually fixed as 1.8V.
The pair "no-sd" + "no-sdio" can provide the similar information.
But I'm not sure if it is proper to use those two property in such a way.
Thank you.
Best regards
Hu Ziji
> [...]
>
> Kind regards
> Uffe
>
^ permalink raw reply
* [PATCH net-next 1/4] net: mvneta: Convert to be 64 bits compatible
From: Gregory CLEMENT @ 2016-11-24 15:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <21520380.oWTKcrq8DS@wuerfel>
Hi Arnd,
On jeu., nov. 24 2016, Arnd Bergmann <arnd@arndb.de> wrote:
> On Thursday, November 24, 2016 4:37:36 PM CET Jisheng Zhang wrote:
>> solB (a SW shadow cookie) perhaps gives a better performance: in hot path,
>> such as mvneta_rx(), the driver accesses buf_cookie and buf_phys_addr of
>> rx_desc which is allocated by dma_alloc_coherent, it's noncacheable if the
>> device isn't cache-coherent. I didn't measure the performance difference,
>> because in fact we take solA as well internally. From your experience,
>> can the performance gain deserve the complex code?
>
> Yes, a read from uncached memory is fairly slow, so if you have a chance
> to avoid that it will probably help. When adding complexity to the code,
> it probably makes sense to take a runtime profile anyway quantify how
> much it gains.
>
> On machines that have cache-coherent DMA, accessing the descriptor
> should be fine, as you already have to load the entire cache line
> to read the status field.
>
> Looking at this snippet:
>
> rx_status = rx_desc->status;
> rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
> data = (unsigned char *)rx_desc->buf_cookie;
> phys_addr = rx_desc->buf_phys_addr;
> pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
> bm_pool = &pp->bm_priv->bm_pools[pool_id];
>
> if (!mvneta_rxq_desc_is_first_last(rx_status) ||
> (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
> err_drop_frame_ret_pool:
> /* Return the buffer to the pool */
> mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool,
> rx_desc->buf_phys_addr);
> err_drop_frame:
>
>
> I think there is more room for optimizing if you start: you read
> the status field twice (the second one in MVNETA_RX_GET_BM_POOL_ID)
> and you can cache the buf_phys_addr along with the virtual address
> once you add that.
I agree we can optimize this code but it is not related to the 64 bits
conversion. Indeed this part is running when we use the HW buffer
management, however currently this part is not ready at all for 64
bits. The virtual address is directly handled by the hardware but it has
only 32 bits to store it in the cookie. So if we want to use the HWBM in
64 bits we need to redesign the code, (maybe by storing the virtual
address in a array and pass the index in the cookie).
Gregory
>
> Generally speaking, I'd recommend using READ_ONCE()/WRITE_ONCE()
> to access the descriptor fields, to ensure the compiler doesn't
> add extra references as well as to annotate the expensive
> operations.
>
> Arnd
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH v2 0/3] ARM: davinci: use gpio descriptors for mmc pins
From: Axel Haslam @ 2016-11-24 15:04 UTC (permalink / raw)
To: linux-arm-kernel
For the boards that use gpios managed by the davinci gpio driver,
we can use gpio descriptors to control the the mmc pins.
This will let the mmc driver register an interrupt for the
card detect pin, and also allows us to remove the dependency
on platform callbacks for these boards.
For boards using a CPLD or an MSP the conversion is not yet done,
and they still rely on the platform callbacks and polling.
More work is needed to be able to manage those pins with gpio
descriptors. Once that is done, we would be able to remove
completely platform callbacks.
Changes v1->v2
*Convert da850-evm and da830-evm
*keep hack board pins as they are not compatible with lcdk (Sekhar)
Dependency:
This patch depends on a mmc driver patch currently in linux-next.
MMC: davinci: fix card detect and write protect
https://lkml.org/lkml/2016/11/15/592
Axel Haslam (3):
ARM: davinci: hawk: use gpio descriptor for card detect
ARM: davinci: da850-evm: use gpio descriptor for mmc pins
ARM: davinci: da830-evm: use gpio descriptor for mmc pins
arch/arm/mach-davinci/board-da830-evm.c | 41 ++++++++--------------------
arch/arm/mach-davinci/board-da850-evm.c | 35 +++++++-----------------
arch/arm/mach-davinci/board-omapl138-hawk.c | 42 ++++++++---------------------
3 files changed, 32 insertions(+), 86 deletions(-)
--
2.9.3
^ permalink raw reply
* [PATCH v2 1/3] ARM: davinci: hawk: use gpio descriptor for mmc pins
From: Axel Haslam @ 2016-11-24 15:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161124150454.23899-1-ahaslam@baylibre.com>
Currently the mmc driver is polling the gpio to know if the
card was removed.
By using a gpio descriptor instead of the platform callbacks,
the driver will be able to register the gpio using the mmc core
API's designed for this purpose.
This has the advantage that an irq will be registered, and
polling is no longer needed. Also, a dependency on platform
callbacks is removed for this board.
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
---
arch/arm/mach-davinci/board-omapl138-hawk.c | 42 ++++++++---------------------
1 file changed, 11 insertions(+), 31 deletions(-)
diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c
index a4e8726..a2966d3 100644
--- a/arch/arm/mach-davinci/board-omapl138-hawk.c
+++ b/arch/arm/mach-davinci/board-omapl138-hawk.c
@@ -15,6 +15,7 @@
#include <linux/gpio.h>
#include <linux/platform_data/gpio-davinci.h>
#include <linux/regulator/machine.h>
+#include <linux/gpio/machine.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -25,8 +26,6 @@
#include <mach/mux.h>
#define HAWKBOARD_PHY_ID "davinci_mdio-0:07"
-#define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12)
-#define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13)
#define DA850_USB1_VBUS_PIN GPIO_TO_PIN(2, 4)
#define DA850_USB1_OC_PIN GPIO_TO_PIN(6, 13)
@@ -123,19 +122,16 @@ static const short hawk_mmcsd0_pins[] = {
-1
};
-static int da850_hawk_mmc_get_ro(int index)
-{
- return gpio_get_value(DA850_HAWK_MMCSD_WP_PIN);
-}
-
-static int da850_hawk_mmc_get_cd(int index)
-{
- return !gpio_get_value(DA850_HAWK_MMCSD_CD_PIN);
-}
+static struct gpiod_lookup_table mmc_gpios_table = {
+ .dev_id = "da830-mmc.0",
+ .table = {
+ /* CD: gpio3_12: gpio60: chip 1 contains gpio range 32-63*/
+ GPIO_LOOKUP("davinci_gpio.1", 28, "cd", GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP("davinci_gpio.1", 29, "wp", GPIO_ACTIVE_LOW),
+ },
+};
static struct davinci_mmc_config da850_mmc_config = {
- .get_ro = da850_hawk_mmc_get_ro,
- .get_cd = da850_hawk_mmc_get_cd,
.wires = 4,
.max_freq = 50000000,
.caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
@@ -151,21 +147,7 @@ static __init void omapl138_hawk_mmc_init(void)
return;
}
- ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN,
- GPIOF_DIR_IN, "MMC CD");
- if (ret < 0) {
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA850_HAWK_MMCSD_CD_PIN);
- return;
- }
-
- ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN,
- GPIOF_DIR_IN, "MMC WP");
- if (ret < 0) {
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA850_HAWK_MMCSD_WP_PIN);
- goto mmc_setup_wp_fail;
- }
+ gpiod_add_lookup_table(&mmc_gpios_table);
ret = da8xx_register_mmcsd0(&da850_mmc_config);
if (ret) {
@@ -176,9 +158,7 @@ static __init void omapl138_hawk_mmc_init(void)
return;
mmc_setup_mmcsd_fail:
- gpio_free(DA850_HAWK_MMCSD_WP_PIN);
-mmc_setup_wp_fail:
- gpio_free(DA850_HAWK_MMCSD_CD_PIN);
+ gpiod_remove_lookup_table(&mmc_gpios_table);
}
static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id);
--
2.9.3
^ permalink raw reply related
* [PATCH v2 2/3] ARM: davinci: da850-evm: use gpio descriptor for mmc pins
From: Axel Haslam @ 2016-11-24 15:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161124150454.23899-1-ahaslam@baylibre.com>
Currently the mmc driver is polling the gpio to know if the
card was removed.
By using a gpio descriptor instead of the platform callbacks,
the driver will be able to register the gpio using the mmc core
API's designed for this purpose.
This has the advantage that an irq will be registered, and
polling is no longer needed. Also, a dependency on platform
callbacks is removed for this board.
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
---
arch/arm/mach-davinci/board-da850-evm.c | 35 ++++++++++-----------------------
1 file changed, 10 insertions(+), 25 deletions(-)
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index ec5cb10..1a31ac3 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
+#include <linux/gpio/machine.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
@@ -56,9 +57,6 @@
#define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8)
#define DA850_LCD_BL_PIN GPIO_TO_PIN(2, 15)
-#define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0)
-#define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1)
-
#define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6)
static struct mtd_partition da850evm_spiflash_part[] = {
@@ -776,19 +774,16 @@ static const short da850_evm_mcasp_pins[] __initconst = {
-1
};
-static int da850_evm_mmc_get_ro(int index)
-{
- return gpio_get_value(DA850_MMCSD_WP_PIN);
-}
-
-static int da850_evm_mmc_get_cd(int index)
-{
- return !gpio_get_value(DA850_MMCSD_CD_PIN);
-}
+static struct gpiod_lookup_table mmc_gpios_table = {
+ .dev_id = "da830-mmc.0",
+ .table = {
+ /* gpio chip 2 contains gpio range 64-95 */
+ GPIO_LOOKUP("davinci_gpio.2", 0, "cd", GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP("davinci_gpio.2", 1, "wp", GPIO_ACTIVE_LOW),
+ },
+};
static struct davinci_mmc_config da850_mmc_config = {
- .get_ro = da850_evm_mmc_get_ro,
- .get_cd = da850_evm_mmc_get_cd,
.wires = 4,
.max_freq = 50000000,
.caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
@@ -1383,17 +1378,7 @@ static __init void da850_evm_init(void)
pr_warn("%s: MMCSD0 mux setup failed: %d\n",
__func__, ret);
- ret = gpio_request(DA850_MMCSD_CD_PIN, "MMC CD\n");
- if (ret)
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA850_MMCSD_CD_PIN);
- gpio_direction_input(DA850_MMCSD_CD_PIN);
-
- ret = gpio_request(DA850_MMCSD_WP_PIN, "MMC WP\n");
- if (ret)
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA850_MMCSD_WP_PIN);
- gpio_direction_input(DA850_MMCSD_WP_PIN);
+ gpiod_add_lookup_table(&mmc_gpios_table);
ret = da8xx_register_mmcsd0(&da850_mmc_config);
if (ret)
--
2.9.3
^ permalink raw reply related
* [PATCH v2 3/3] ARM: davinci: da830-evm: use gpio descriptor for mmc pins
From: Axel Haslam @ 2016-11-24 15:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161124150454.23899-1-ahaslam@baylibre.com>
Currently the mmc driver is polling the gpio to know if the
card was removed.
By using a gpio descriptor instead of the platform callbacks,
the driver will be able to register the gpio using the mmc core
API's designed for this purpose.
This has the advantage that an irq will be registered, and
polling is no longer needed. Also, a dependency on platform
callbacks is removed for this board.
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
---
arch/arm/mach-davinci/board-da830-evm.c | 41 +++++++++------------------------
1 file changed, 11 insertions(+), 30 deletions(-)
diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index 5db0901..5807562 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -14,6 +14,7 @@
#include <linux/console.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
@@ -204,22 +205,16 @@ static const short da830_evm_mmc_sd_pins[] = {
-1
};
-#define DA830_MMCSD_WP_PIN GPIO_TO_PIN(2, 1)
-#define DA830_MMCSD_CD_PIN GPIO_TO_PIN(2, 2)
-
-static int da830_evm_mmc_get_ro(int index)
-{
- return gpio_get_value(DA830_MMCSD_WP_PIN);
-}
-
-static int da830_evm_mmc_get_cd(int index)
-{
- return !gpio_get_value(DA830_MMCSD_CD_PIN);
-}
+static struct gpiod_lookup_table mmc_gpios_table = {
+ .dev_id = "da830-mmc.0",
+ .table = {
+ /* gpio chip 1 contains gpio range 32-63 */
+ GPIO_LOOKUP("davinci_gpio.1", 2, "cd", GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP("davinci_gpio.1", 1, "wp", GPIO_ACTIVE_LOW),
+ },
+};
static struct davinci_mmc_config da830_evm_mmc_config = {
- .get_ro = da830_evm_mmc_get_ro,
- .get_cd = da830_evm_mmc_get_cd,
.wires = 8,
.max_freq = 50000000,
.caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
@@ -235,26 +230,12 @@ static inline void da830_evm_init_mmc(void)
return;
}
- ret = gpio_request(DA830_MMCSD_WP_PIN, "MMC WP");
- if (ret) {
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA830_MMCSD_WP_PIN);
- return;
- }
- gpio_direction_input(DA830_MMCSD_WP_PIN);
-
- ret = gpio_request(DA830_MMCSD_CD_PIN, "MMC CD\n");
- if (ret) {
- pr_warn("%s: can not open GPIO %d\n",
- __func__, DA830_MMCSD_CD_PIN);
- return;
- }
- gpio_direction_input(DA830_MMCSD_CD_PIN);
+ gpiod_add_lookup_table(&mmc_gpios_table);
ret = da8xx_register_mmcsd0(&da830_evm_mmc_config);
if (ret) {
pr_warn("%s: mmc/sd registration failed: %d\n", __func__, ret);
- gpio_free(DA830_MMCSD_WP_PIN);
+ gpiod_remove_lookup_table(&mmc_gpios_table);
}
}
--
2.9.3
^ permalink raw reply related
* [RFC PATCH] ARM: dts: Add support for Turris Omnia
From: Andrew Lunn @ 2016-11-24 15:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4e3f9628-dbf8-27c1-abea-d0ef58a67e51@kleine-koenig.org>
> @Tomas: I think it doesn't make sense when we alternate sending patches
> without prior arrangement. Do you already work on a v5? If not I can do
> that to fix the last few comments. Not sure when a submission is too
> late to enter v4.10, but I think the window isn't that big any more.
It is getting a bit late. But maybe Linus will add in another -rc
week.
>
> > No leds? No buttons via gpio-keys?
>
> The leds are controlled by a Cortex-M0 and without intervention blink
> according to a hardware function (network, power, pci). IMHO that's ok
> for an initial setup.
Yes. That is fine. It is just unusual. Most boards have gpio-led and
gpio-keys, which are easy to add. That is why i asked. Adding an LED
driver which talks to this M0 can be added later.
Andrew
^ permalink raw reply
* [PATCH net-next 1/4] net: mvneta: Convert to be 64 bits compatible
From: Marcin Wojtas @ 2016-11-24 15:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8760ncly5s.fsf@free-electrons.com>
Hi Gregory,
2016-11-24 16:01 GMT+01:00 Gregory CLEMENT <gregory.clement@free-electrons.com>:
> Hi Arnd,
>
> On jeu., nov. 24 2016, Arnd Bergmann <arnd@arndb.de> wrote:
>
>> On Thursday, November 24, 2016 4:37:36 PM CET Jisheng Zhang wrote:
>>> solB (a SW shadow cookie) perhaps gives a better performance: in hot path,
>>> such as mvneta_rx(), the driver accesses buf_cookie and buf_phys_addr of
>>> rx_desc which is allocated by dma_alloc_coherent, it's noncacheable if the
>>> device isn't cache-coherent. I didn't measure the performance difference,
>>> because in fact we take solA as well internally. From your experience,
>>> can the performance gain deserve the complex code?
>>
>> Yes, a read from uncached memory is fairly slow, so if you have a chance
>> to avoid that it will probably help. When adding complexity to the code,
>> it probably makes sense to take a runtime profile anyway quantify how
>> much it gains.
>>
>> On machines that have cache-coherent DMA, accessing the descriptor
>> should be fine, as you already have to load the entire cache line
>> to read the status field.
>>
>> Looking at this snippet:
>>
>> rx_status = rx_desc->status;
>> rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
>> data = (unsigned char *)rx_desc->buf_cookie;
>> phys_addr = rx_desc->buf_phys_addr;
>> pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
>> bm_pool = &pp->bm_priv->bm_pools[pool_id];
>>
>> if (!mvneta_rxq_desc_is_first_last(rx_status) ||
>> (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
>> err_drop_frame_ret_pool:
>> /* Return the buffer to the pool */
>> mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool,
>> rx_desc->buf_phys_addr);
>> err_drop_frame:
>>
>>
>> I think there is more room for optimizing if you start: you read
>> the status field twice (the second one in MVNETA_RX_GET_BM_POOL_ID)
>> and you can cache the buf_phys_addr along with the virtual address
>> once you add that.
>
> I agree we can optimize this code but it is not related to the 64 bits
> conversion. Indeed this part is running when we use the HW buffer
> management, however currently this part is not ready at all for 64
> bits. The virtual address is directly handled by the hardware but it has
> only 32 bits to store it in the cookie. So if we want to use the HWBM in
> 64 bits we need to redesign the code, (maybe by storing the virtual
> address in a array and pass the index in the cookie).
>
How about storing data (virt address and maybe other stuff) as a part
of data buffer and using rx_packet_offset? It has to be used for a3700
anyway. No need of additional rings whatsoever.
Best regards,
Marcin
^ permalink raw reply
* [PATCH] arm64: mm: Fix memmap to be initialized for the entire section
From: Robert Richter @ 2016-11-24 15:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu-SXX_Vv1Wi1Nm=5vdaXSS0MBp20QBpQVacSKPLfdH0-A@mail.gmail.com>
On 24.11.16 14:23:16, Ard Biesheuvel wrote:
> On 24 November 2016 at 14:11, Robert Richter <robert.richter@cavium.com> wrote:
> > On 24.11.16 13:58:30, Ard Biesheuvel wrote:
> >> On 24 November 2016 at 13:51, Robert Richter <robert.richter@cavium.com> wrote:
> >> > On 24.11.16 13:44:31, Ard Biesheuvel wrote:
> >> >> On 24 November 2016 at 13:42, Robert Richter <robert.richter@cavium.com> wrote:
> >> >> > On 23.11.16 21:25:06, Ard Biesheuvel wrote:
> >> >> >> Why? MEMREMAP_WB is used often, among other things for mapping
> >> >> >> firmware tables, which are marked as NOMAP, so in these cases, the
> >> >> >> linear address is not mapped.
> >> >> >
> >> >> > If fw tables are mapped wb, that is wrong and needs a separate fix.
> >> >> >
> >> >>
> >> >> Why is that wrong?
> >> >
> >> > The whole issue with mapping acpi tables is not marking them cachable,
> >> > what wb does.
> >>
> >> What 'issue'?
> >>
> >> > Otherwise we could just use linear mapping for those mem
> >> > ranges.
> >> >
> >>
> >> Regions containing firmware tables are owned by the firmware, and it
> >> is the firmware that tells us which memory attributes we are allowed
> >> to use. If those attributes include WB, it is perfectly legal to use a
> >> cacheable mapping. That does *not* mean they should be covered by the
> >> linear mapping. The linear mapping is read-write-non-exec, for
> >> instance, and we may prefer to use a read-only mapping and/or
> >> executable mapping.
> >
> > Ok, I am going to fix try_ram_remap().
> >
>
> Thanks. Could you also add an arm64 version of page_is_ram() that uses
> memblock_is_memory() while you're at it? I think using memblock
> directly in try_ram_remap() may not be the best approach
Sure. I also want to mark the patches as stable.
> > Are there other concerns with this patch?
> >
>
> I think we all agree that pfn_valid() should return whether a pfn has
> a struct page associated with it, the debate is about whether it makes
> sense to allocate struct pages for memory that the kernel does not
> own. But given that it does not really hurt to do so for small holes,
> I think your suggestion makes sense.
Thanks for your comments and the review.
> Should we be doing anything more to ensure that those pages are not
> dereferenced inadvertently? Is there a page flag we should be setting?
I don't think so. Boot mem is initialized in free_low_memory_core_
early(). The PageReserved flag is set for pages from reserved memory
ranges, and memory ranges not marked NOMAP is just freed. Since pages
are either reservered or in the free_list of pages, pages from other
memory ranges (NOMAP) is not visible to mm.
-Robert
^ permalink raw reply
* [PATCH v2 0/7] Add pwm and IIO timer drivers for stm32
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
version 2:
- keep only one compatible per driver
- use DT parameters to describe hardware block configuration:
- pwm channels, complementary output, counter size, break input
- triggers accepted and create by IIO timers
- change DT to limite use of reference to the node
- interrupt is now in IIO timer driver
- rename stm32-mfd-timer to stm32-gptimer (for general purpose timer)
The following patches enable pwm and IIO Timer features for stm32 platforms.
Those two features are mixed into the registers of the same hardware block
(named general purpose timer) which lead to introduce a multifunctions driver
on the top of them to be able to share the registers.
In stm32 14 instances of timer hardware block exist, even if they all have
the same register mapping they could have a different number of pwm channels
and/or different triggers capabilities. We use various parameters in DT to
describe the differences between hardware blocks
The MFD (stm32-gptimer.c) takes care of clock and register mapping
by using regmap. stm32_gptimer_dev structure is provided to its sub-node to
share those information.
PWM driver is implemented into pwm-stm32.c. Depending of the instance we may
have up to 4 channels, sometime with complementary outputs or 32 bits counter
instead of 16 bits. Some hardware blocks may also have a break input function
which allows to stop pwm depending of a level, defined in devicetree, on an
external pin.
IIO timer driver (stm32-iio-timer.c and stm32-iio-timers.h) define a list of
hardware triggers usable by hardware blocks like ADC, DAC or other timers.
The matrix of possible connections between blocks is quite complex so we use
trigger names and is_stm32_iio_timer_trigger() function to be sure that
triggers are valid and configure the IPs.
Possible triggers ar listed in include/dt-bindings/iio/timer/st,stm32-iio-timer.h
At run time IIO timer hardware blocks can configure (through "master_mode"
IIO device attribute) which internal signal (counter enable, reset,
comparison block, etc...) is used to generate the trigger.
By using "slave_mode" IIO device attribute timer can also configure on which
event (level, rising edge) of the block is enabled.
Since we can use trigger from one hardware to control an other block, we can
use a pwm to control an other one. The following example shows how to configure
pwm1 and pwm3 to make pwm3 generate pulse only when pwm1 pulse level is high.
/sys/bus/iio/devices # ls
iio:device0 iio:device1 trigger0 trigger1
configure timer1 to use pwm1 channel 0 as output trigger
/sys/bus/iio/devices # echo 4 > iio\:device0/master_mode
configure timer3 to enable only when input is high
/sys/bus/iio/devices # echo 5 > iio\:device1/slave_mode
/sys/bus/iio/devices # cat trigger0/name
tim1_trgo
configure timer2 to use timer1 trigger is input
/sys/bus/iio/devices # echo "tim1_trgo" > iio\:device1/trigger/current_trigger
configure pwm3 channel 0 to generate a signal with a period of 100ms and a
duty cycle of 50%
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 0 > export
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 100000000 > pwm0/period
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 50000000 > pwm0/duty_cycle
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4# echo 1 > pwm0/enable
here pwm3 channel 0, as expected, doesn't start because has to be triggered by
pwm1 channel 0
configure pwm1 channel 0 to generate a signal with a period of 1s and a
duty cycle of 50%
/sys/devices/platform/soc/40010000.gptimer1/40010000.gptimer1:pwm1 at 0/pwm/pwmchip0 # echo 0 > export
/sys/devices/platform/soc/40010000.gptimer1/40010000.gptimer1:pwm1 at 0/pwm/pwmchip0 # echo 1000000000 > pwm0/period
/sys/devices/platform/soc/40010000.gptimer1/40010000.gptimer1:pwm1 at 0/pwm/pwmchip0 # echo 500000000 > pwm0/duty_cycle
/sys/devices/platform/soc/40010000.gptimer1/40010000.gptimer1:pwm1 at 0/pwm/pwmchip0 # echo 1 > pwm0/enable
finally pwm1 starts and pwm3 only generates pulse when pwm1 signal is high
An other example to use a timer as source of clock for another device.
Here timer1 is used a source clock for pwm3:
/sys/bus/iio/devices # echo 100000 > trigger0/sampling_frequency
/sys/bus/iio/devices # echo tim1_trgo > iio\:device1/trigger/current_trigger
/sys/bus/iio/devices # echo 7 > iio\:device1/slave_mode
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 0 > export
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 1000000 > pwm0/period
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 500000 > pwm0/duty_cycle
/sys/devices/platform/soc/40000400.gptimer3/40000400.gptimer3:pwm3 at 0/pwm/pwmchip4 # echo 1 > pwm0/enable
Benjamin Gaignard (7):
MFD: add bindings for stm32 general purpose timer driver
MFD: add stm32 general purpose timer driver
PWM: add pwm-stm32 DT bindings
PWM: add pwm driver for stm32 plaftorm
IIO: add bindings for stm32 IIO timer driver
IIO: add STM32 IIO timer driver
ARM: dts: stm32: add stm32 general purpose timer driver in DT
.../bindings/iio/timer/stm32-iio-timer.txt | 41 ++
.../bindings/mfd/stm32-general-purpose-timer.txt | 43 ++
.../devicetree/bindings/pwm/pwm-stm32.txt | 37 ++
arch/arm/boot/dts/stm32f429.dtsi | 305 +++++++++++++-
arch/arm/boot/dts/stm32f469-disco.dts | 28 ++
drivers/iio/Kconfig | 2 +-
drivers/iio/Makefile | 1 +
drivers/iio/timer/Kconfig | 15 +
drivers/iio/timer/Makefile | 1 +
drivers/iio/timer/stm32-iio-timer.c | 448 +++++++++++++++++++++
drivers/iio/trigger/Kconfig | 1 -
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 2 +
drivers/mfd/stm32-gptimer.c | 73 ++++
drivers/pwm/Kconfig | 8 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-stm32.c | 285 +++++++++++++
include/dt-bindings/iio/timer/st,stm32-iio-timer.h | 23 ++
include/linux/iio/timer/stm32-iio-timers.h | 16 +
include/linux/mfd/stm32-gptimer.h | 62 +++
20 files changed, 1399 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
create mode 100644 Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
create mode 100644 drivers/iio/timer/Kconfig
create mode 100644 drivers/iio/timer/Makefile
create mode 100644 drivers/iio/timer/stm32-iio-timer.c
create mode 100644 drivers/mfd/stm32-gptimer.c
create mode 100644 drivers/pwm/pwm-stm32.c
create mode 100644 include/dt-bindings/iio/timer/st,stm32-iio-timer.h
create mode 100644 include/linux/iio/timer/stm32-iio-timers.h
create mode 100644 include/linux/mfd/stm32-gptimer.h
--
1.9.1
^ permalink raw reply
* [PATCH v2 1/7] MFD: add bindings for stm32 general purpose timer driver
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
Add bindings information for stm32 general purpose timer
version 2:
- rename stm32-mfd-timer to stm32-gptimer
- only keep one compatible string
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
.../bindings/mfd/stm32-general-purpose-timer.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
new file mode 100644
index 0000000..2f10e67
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
@@ -0,0 +1,43 @@
+STM32 general purpose timer driver
+
+Required parameters:
+- compatible: must be "st,stm32-gptimer"
+
+- reg: Physical base address and length of the controller's
+ registers.
+- clock-names: Set to "clk_int".
+- clocks: Phandle to the clock used by the timer module.
+ For Clk properties, please refer to ../clock/clock-bindings.txt
+
+Optional parameters:
+- resets: Phandle to the parent reset controller.
+ See ..reset/st,stm32-rcc.txt
+
+Optional subnodes:
+- pwm: See ../pwm/pwm-stm32.txt
+- iiotimer: See ../iio/timer/stm32-iio-timer.txt
+
+Example:
+ gptimer1: gptimer1 at 40010000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40010000 0x400>;
+ clocks = <&rcc 0 160>;
+ clock-names = "clk_int";
+
+ pwm1 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,breakinput;
+ st,complementary;
+ };
+
+ iiotimer1 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <27>;
+ st,input-triggers-names = TIM5_TRGO,
+ TIM2_TRGO,
+ TIM4_TRGO,
+ TIM3_TRGO;
+ st,output-triggers-names = TIM1_TRGO;
+ };
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/7] MFD: add stm32 general purpose timer driver
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
This hardware block could at used at same time for PWM generation
and IIO timer for other IPs like DAC, ADC or other timers.
PWM and IIO timer configuration are mixed in the same registers
so we need a multi fonction driver to be able to share those registers.
version 2:
- rename driver "stm32-gptimer" to be align with SoC documentation
- only keep one compatible
- use of_platform_populate() instead of devm_mfd_add_devices()
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
drivers/mfd/Kconfig | 10 ++++++
drivers/mfd/Makefile | 2 ++
drivers/mfd/stm32-gptimer.c | 73 +++++++++++++++++++++++++++++++++++++++
include/linux/mfd/stm32-gptimer.h | 62 +++++++++++++++++++++++++++++++++
4 files changed, 147 insertions(+)
create mode 100644 drivers/mfd/stm32-gptimer.c
create mode 100644 include/linux/mfd/stm32-gptimer.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..e75abcb 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1607,6 +1607,15 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.
+config MFD_STM32_GP_TIMER
+ tristate "Support for STM32 General Purpose Timer"
+ select MFD_CORE
+ select REGMAP
+ depends on ARCH_STM32
+ depends on OF
+ help
+ Select this option to enable stm32 general purpose timer
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
@@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG
on the ARM Ltd. Versatile Express board.
endmenu
+
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..86353b9 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+
+obj-$(CONFIG_MFD_STM32_GP_TIMER) += stm32-gptimer.o
diff --git a/drivers/mfd/stm32-gptimer.c b/drivers/mfd/stm32-gptimer.c
new file mode 100644
index 0000000..54fb95c
--- /dev/null
+++ b/drivers/mfd/stm32-gptimer.c
@@ -0,0 +1,73 @@
+/*
+ * stm32-gptimer.c
+ *
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+#include <linux/mfd/stm32-gptimer.h>
+
+static const struct regmap_config stm32_gptimer_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x400,
+ .fast_io = true,
+};
+
+static int stm32_gptimer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_gptimer_dev *mfd;
+ struct resource *res;
+ void __iomem *mmio;
+
+ mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL);
+ if (!mfd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOMEM;
+
+ mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ mfd->regmap = devm_regmap_init_mmio_clk(dev, "clk_int", mmio,
+ &stm32_gptimer_regmap_cfg);
+ if (IS_ERR(mfd->regmap))
+ return PTR_ERR(mfd->regmap);
+
+ mfd->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(mfd->clk))
+ return PTR_ERR(mfd->clk);
+
+ platform_set_drvdata(pdev, mfd);
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id stm32_gptimer_of_match[] = {
+ {
+ .compatible = "st,stm32-gptimer",
+ },
+};
+MODULE_DEVICE_TABLE(of, stm32_gptimer_of_match);
+
+static struct platform_driver stm32_gptimer_driver = {
+ .probe = stm32_gptimer_probe,
+ .driver = {
+ .name = "stm32-gptimer",
+ .of_match_table = stm32_gptimer_of_match,
+ },
+};
+module_platform_driver(stm32_gptimer_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 general purpose timer");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/stm32-gptimer.h b/include/linux/mfd/stm32-gptimer.h
new file mode 100644
index 0000000..f8c92de
--- /dev/null
+++ b/include/linux/mfd/stm32-gptimer.h
@@ -0,0 +1,62 @@
+/*
+ * stm32-gptimer.h
+ *
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LINUX_STM32_GPTIMER_H_
+#define _LINUX_STM32_GPTIMER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define TIM_CR1 0x00 /* Control Register 1 */
+#define TIM_CR2 0x04 /* Control Register 2 */
+#define TIM_SMCR 0x08 /* Slave mode control reg */
+#define TIM_DIER 0x0C /* DMA/interrupt register */
+#define TIM_SR 0x10 /* Status register */
+#define TIM_EGR 0x14 /* Event Generation Reg */
+#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
+#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
+#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
+#define TIM_PSC 0x28 /* Prescaler */
+#define TIM_ARR 0x2c /* Auto-Reload Register */
+#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
+#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */
+#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */
+#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */
+#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
+
+#define TIM_CR1_CEN BIT(0) /* Counter Enable */
+#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */
+#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */
+#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
+#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
+#define TIM_DIER_UIE BIT(0) /* Update interrupt */
+#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
+#define TIM_EGR_UG BIT(0) /* Update Generation */
+#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
+#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
+#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
+#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
+#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
+#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
+#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
+#define TIM_BDTR_BKE BIT(12) /* Break input enable */
+#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
+#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */
+#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
+
+#define MAX_TIM_PSC 0xFFFF
+
+struct stm32_gptimer_dev {
+ /* Device data */
+ struct clk *clk;
+
+ /* Registers mapping */
+ struct regmap *regmap;
+};
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/7] PWM: add pwm-stm32 DT bindings
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
Define bindings for pwm-stm32
version 2:
- use parameters instead of compatible of handle the hardware configuration
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
.../devicetree/bindings/pwm/pwm-stm32.txt | 37 ++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
new file mode 100644
index 0000000..36263f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
@@ -0,0 +1,37 @@
+STMicroelectronics PWM driver bindings for STM32
+
+Must be a sub-node of STM32 general purpose timer driver
+
+Required parameters:
+- compatible: Must be "st,stm32-pwm"
+- pinctrl-names: Set to "default".
+- pinctrl-0: List of phandles pointing to pin configuration nodes
+ for PWM module.
+ For Pinctrl properties, please refer to [1].
+
+Optional parameters:
+- st,breakinput: Set if the hardware have break input capabilities
+- st,breakinput-polarity: Set break input polarity. Default is 0
+ The value define the active polarity:
+ - 0 (active LOW)
+ - 1 (active HIGH)
+- st,pwm-num-chan: Number of available PWM channels. Default is 0.
+- st,32bits-counter: Set if the hardware have a 32 bits counter
+- st,complementary: Set if the hardware have complementary output channels
+
+[1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+
+Example:
+ gptimer1: gptimer1 at 40010000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40010000 0x400>;
+ clocks = <&rcc 0 160>;
+ clock-names = "clk_int";
+
+ pwm1 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,breakinput;
+ st,complementary;
+ };
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 4/7] PWM: add pwm driver for stm32 plaftorm
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
This driver add support for pwm driver on stm32 platform.
The SoC have multiple instances of the hardware IP and each
of them could have small differences: number of channels,
complementary output, counter register size...
Use DT parameters to handle those differentes configuration
version 2:
- only keep one comptatible
- use DT paramaters to discover hardware block configuration
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
drivers/pwm/Kconfig | 8 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-stm32.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 294 insertions(+)
create mode 100644 drivers/pwm/pwm-stm32.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index bf01288..a89fdba 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -388,6 +388,14 @@ config PWM_STI
To compile this driver as a module, choose M here: the module
will be called pwm-sti.
+config PWM_STM32
+ bool "STMicroelectronics STM32 PWM"
+ depends on ARCH_STM32
+ depends on OF
+ select MFD_STM32_GP_TIMER
+ help
+ Generic PWM framework driver for STM32 SoCs.
+
config PWM_STMPE
bool "STMPE expander PWM export"
depends on MFD_STMPE
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 1194c54..5aa9308 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
obj-$(CONFIG_PWM_STI) += pwm-sti.o
+obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
new file mode 100644
index 0000000..a362f63
--- /dev/null
+++ b/drivers/pwm/pwm-stm32.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Gerald Baeza <gerald.baeza@st.com>
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by timer-stm32.c from Maxime Coquelin
+ * pwm-atmel.c from Bo Shen
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#include <linux/mfd/stm32-gptimer.h>
+
+#define DRIVER_NAME "stm32-pwm"
+
+#define CAP_COMPLEMENTARY BIT(0)
+#define CAP_32BITS_COUNTER BIT(1)
+#define CAP_BREAKINPUT BIT(2)
+#define CAP_BREAKINPUT_POLARITY BIT(3)
+
+struct stm32_pwm_dev {
+ struct device *dev;
+ struct clk *clk;
+ struct regmap *regmap;
+ struct pwm_chip chip;
+ int caps;
+ int npwm;
+ u32 polarity;
+};
+
+#define to_stm32_pwm_dev(x) container_of(chip, struct stm32_pwm_dev, chip)
+
+static u32 __active_channels(struct stm32_pwm_dev *pwm_dev)
+{
+ u32 ccer;
+
+ regmap_read(pwm_dev->regmap, TIM_CCER, &ccer);
+
+ return ccer & TIM_CCER_CCXE;
+}
+
+static int write_ccrx(struct stm32_pwm_dev *dev, struct pwm_device *pwm,
+ u32 ccr)
+{
+ switch (pwm->hwpwm) {
+ case 0:
+ return regmap_write(dev->regmap, TIM_CCR1, ccr);
+ case 1:
+ return regmap_write(dev->regmap, TIM_CCR2, ccr);
+ case 2:
+ return regmap_write(dev->regmap, TIM_CCR3, ccr);
+ case 3:
+ return regmap_write(dev->regmap, TIM_CCR4, ccr);
+ }
+ return -EINVAL;
+}
+
+static int stm32_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
+ unsigned long long prd, div, dty;
+ int prescaler = 0;
+ u32 max_arr = 0xFFFF, ccmr, mask, shift, bdtr;
+
+ if (dev->caps & CAP_32BITS_COUNTER)
+ max_arr = 0xFFFFFFFF;
+
+ /* Period and prescaler values depends of clock rate */
+ div = (unsigned long long)clk_get_rate(dev->clk) * period_ns;
+
+ do_div(div, NSEC_PER_SEC);
+ prd = div;
+
+ while (div > max_arr) {
+ prescaler++;
+ div = prd;
+ do_div(div, (prescaler + 1));
+ }
+ prd = div;
+
+ if (prescaler > MAX_TIM_PSC) {
+ dev_err(chip->dev, "prescaler exceeds the maximum value\n");
+ return -EINVAL;
+ }
+
+ /* All channels share the same prescaler and counter so
+ * when two channels are active at the same we can't change them
+ */
+ if (__active_channels(dev) & ~(1 << pwm->hwpwm * 4)) {
+ u32 psc, arr;
+
+ regmap_read(dev->regmap, TIM_PSC, &psc);
+ regmap_read(dev->regmap, TIM_ARR, &arr);
+
+ if ((psc != prescaler) || (arr != prd - 1))
+ return -EINVAL;
+ }
+
+ regmap_write(dev->regmap, TIM_PSC, prescaler);
+ regmap_write(dev->regmap, TIM_ARR, prd - 1);
+ regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
+
+ /* Calculate the duty cycles */
+ dty = prd * duty_ns;
+ do_div(dty, period_ns);
+
+ write_ccrx(dev, pwm, dty);
+
+ /* Configure output mode */
+ shift = (pwm->hwpwm & 0x1) * 8;
+ ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
+ mask = 0xFF << shift;
+
+ if (pwm->hwpwm & 0x2)
+ regmap_update_bits(dev->regmap, TIM_CCMR2, mask, ccmr);
+ else
+ regmap_update_bits(dev->regmap, TIM_CCMR1, mask, ccmr);
+
+ if (!(dev->caps & CAP_BREAKINPUT))
+ return 0;
+
+ bdtr = TIM_BDTR_MOE | TIM_BDTR_AOE;
+
+ if (dev->caps & CAP_BREAKINPUT_POLARITY)
+ bdtr |= TIM_BDTR_BKE;
+
+ if (dev->polarity)
+ bdtr |= TIM_BDTR_BKP;
+
+ regmap_update_bits(dev->regmap, TIM_BDTR,
+ TIM_BDTR_MOE | TIM_BDTR_AOE |
+ TIM_BDTR_BKP | TIM_BDTR_BKE,
+ bdtr);
+
+ return 0;
+}
+
+static int stm32_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ u32 mask;
+ struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
+
+ mask = TIM_CCER_CC1P << (pwm->hwpwm * 4);
+ if (dev->caps & CAP_COMPLEMENTARY)
+ mask |= TIM_CCER_CC1NP << (pwm->hwpwm * 4);
+
+ regmap_update_bits(dev->regmap, TIM_CCER, mask,
+ polarity == PWM_POLARITY_NORMAL ? 0 : mask);
+
+ return 0;
+}
+
+static int stm32_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ u32 mask;
+ struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
+
+ clk_enable(dev->clk);
+
+ /* Enable channel */
+ mask = TIM_CCER_CC1E << (pwm->hwpwm * 4);
+ if (dev->caps & CAP_COMPLEMENTARY)
+ mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4);
+
+ regmap_update_bits(dev->regmap, TIM_CCER, mask, mask);
+
+ /* Make sure that registers are updated */
+ regmap_update_bits(dev->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
+
+ /* Enable controller */
+ regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
+
+ return 0;
+}
+
+static void stm32_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ u32 mask;
+ struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
+
+ /* Disable channel */
+ mask = TIM_CCER_CC1E << (pwm->hwpwm * 4);
+ if (dev->caps & CAP_COMPLEMENTARY)
+ mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4);
+
+ regmap_update_bits(dev->regmap, TIM_CCER, mask, 0);
+
+ /* When all channels are disabled, we can disable the controller */
+ if (!__active_channels(dev))
+ regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+
+ clk_disable(dev->clk);
+}
+
+static const struct pwm_ops stm32pwm_ops = {
+ .config = stm32_pwm_config,
+ .set_polarity = stm32_pwm_set_polarity,
+ .enable = stm32_pwm_enable,
+ .disable = stm32_pwm_disable,
+};
+
+static int stm32_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct stm32_gptimer_dev *mfd = dev_get_drvdata(pdev->dev.parent);
+ struct stm32_pwm_dev *pwm;
+ int ret;
+
+ pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
+ if (!pwm)
+ return -ENOMEM;
+
+ pwm->regmap = mfd->regmap;
+ pwm->clk = mfd->clk;
+
+ if (!pwm->regmap || !pwm->clk)
+ return -EINVAL;
+
+ if (of_property_read_bool(np, "st,complementary"))
+ pwm->caps |= CAP_COMPLEMENTARY;
+
+ if (of_property_read_bool(np, "st,32bits-counter"))
+ pwm->caps |= CAP_32BITS_COUNTER;
+
+ if (of_property_read_bool(np, "st,breakinput"))
+ pwm->caps |= CAP_BREAKINPUT;
+
+ if (!of_property_read_u32(np, "st,breakinput-polarity", &pwm->polarity))
+ pwm->caps |= CAP_BREAKINPUT_POLARITY;
+
+ of_property_read_u32(np, "st,pwm-num-chan", &pwm->npwm);
+
+ pwm->chip.base = -1;
+ pwm->chip.dev = dev;
+ pwm->chip.ops = &stm32pwm_ops;
+ pwm->chip.npwm = pwm->npwm;
+
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, pwm);
+
+ return 0;
+}
+
+static int stm32_pwm_remove(struct platform_device *pdev)
+{
+ struct stm32_pwm_dev *pwm = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pwm->npwm; i++)
+ pwm_disable(&pwm->chip.pwms[i]);
+
+ pwmchip_remove(&pwm->chip);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_pwm_of_match[] = {
+ {
+ .compatible = "st,stm32-pwm",
+ },
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
+
+static struct platform_driver stm32_pwm_driver = {
+ .probe = stm32_pwm_probe,
+ .remove = stm32_pwm_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = stm32_pwm_of_match,
+ },
+};
+module_platform_driver(stm32_pwm_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/7] IIO: add bindings for stm32 IIO timer driver
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
Define bindings for stm32 IIO timer
version 2:
- only keep one compatible
- add DT parameters to set lists of the triggers:
one list describe the triggers created by the device
another one give the triggers accepted by the device
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
.../bindings/iio/timer/stm32-iio-timer.txt | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
new file mode 100644
index 0000000..840b417
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
@@ -0,0 +1,41 @@
+timer IIO trigger bindings for STM32
+
+Must be a sub-node of STM32 general purpose timer driver
+
+Required parameters:
+- compatible: must be "st,stm32-iio-timer"
+- interrupts: Interrupt for this device
+ See ../interrupt-controller/st,stm32-exti.txt
+
+Optional parameters:
+- st,input-triggers-names: List of the possible input triggers for
+ the device
+- st,output-triggers-names: List of the possible output triggers for
+ the device
+
+Possible triggers are defined in include/dt-bindings/iio/timer/st,stm32-iio-timer.h
+
+Example:
+ gptimer1: gptimer1 at 40010000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40010000 0x400>;
+ clocks = <&rcc 0 160>;
+ clock-names = "clk_int";
+
+ pwm1 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,breakinput;
+ st,complementary;
+ };
+
+ iiotimer1 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <27>;
+ st,input-triggers-names = TIM5_TRGO,
+ TIM2_TRGO,
+ TIM4_TRGO,
+ TIM3_TRGO;
+ st,output-triggers-names = TIM1_TRGO;
+ };
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/7] IIO: add STM32 IIO timer driver
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
Timers IPs can be used to generate triggers for other IPs like
DAC, ADC or other timers.
Each trigger may result of timer internals signals like counter enable,
reset or edge, this configuration could be done through "master_mode"
device attribute.
A timer device could be triggered by other timers, we use the trigger
name and is_stm32_iio_timer_trigger() function to distinguish them
and configure IP input switch.
Timer may also decide on which event (edge, level) they could
be activated by a trigger, this configuration is done by writing in
"slave_mode" device attribute.
Since triggers could also be used by DAC or ADC their names are defined
in include/dt-bindings/iio/timer/st,stm32-iio-timer.h so those IPs will be able
to configure themselves in valid_trigger function
Trigger have a "sampling_frequency" attribute which allow to configure
timer sampling frequency without using pwm interface
version 2:
- keep only one compatible
- use st,input-triggers-names and st,output-triggers-names
to know which triggers are accepted and/or create by the device
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
drivers/iio/Kconfig | 2 +-
drivers/iio/Makefile | 1 +
drivers/iio/timer/Kconfig | 15 +
drivers/iio/timer/Makefile | 1 +
drivers/iio/timer/stm32-iio-timer.c | 448 +++++++++++++++++++++
drivers/iio/trigger/Kconfig | 1 -
include/dt-bindings/iio/timer/st,stm32-iio-timer.h | 23 ++
include/linux/iio/timer/stm32-iio-timers.h | 16 +
8 files changed, 505 insertions(+), 2 deletions(-)
create mode 100644 drivers/iio/timer/Kconfig
create mode 100644 drivers/iio/timer/Makefile
create mode 100644 drivers/iio/timer/stm32-iio-timer.c
create mode 100644 include/dt-bindings/iio/timer/st,stm32-iio-timer.h
create mode 100644 include/linux/iio/timer/stm32-iio-timers.h
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18..2de2a80 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -90,5 +90,5 @@ source "drivers/iio/potentiometer/Kconfig"
source "drivers/iio/pressure/Kconfig"
source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"
-
+source "drivers/iio/timer/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c43..b797c08 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -32,4 +32,5 @@ obj-y += potentiometer/
obj-y += pressure/
obj-y += proximity/
obj-y += temperature/
+obj-y += timer/
obj-y += trigger/
diff --git a/drivers/iio/timer/Kconfig b/drivers/iio/timer/Kconfig
new file mode 100644
index 0000000..7a73bc6
--- /dev/null
+++ b/drivers/iio/timer/Kconfig
@@ -0,0 +1,15 @@
+#
+# Timers drivers
+
+menu "Timers"
+
+config IIO_STM32_TIMER
+ tristate "stm32 iio timer"
+ depends on ARCH_STM32
+ depends on OF
+ select IIO_TRIGGERED_EVENT
+ select MFD_STM32_GP_TIMER
+ help
+ Select this option to enable stm32 timers hardware IPs
+
+endmenu
diff --git a/drivers/iio/timer/Makefile b/drivers/iio/timer/Makefile
new file mode 100644
index 0000000..a360c9f
--- /dev/null
+++ b/drivers/iio/timer/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_IIO_STM32_TIMER) += stm32-iio-timer.o
diff --git a/drivers/iio/timer/stm32-iio-timer.c b/drivers/iio/timer/stm32-iio-timer.c
new file mode 100644
index 0000000..35f2687
--- /dev/null
+++ b/drivers/iio/timer/stm32-iio-timer.c
@@ -0,0 +1,448 @@
+/*
+ * stm32-iio-timer.c
+ *
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-iio-timers.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stm32-gptimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "stm32-iio-timer"
+
+struct stm32_iio_timer_dev {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *clk;
+ int irq;
+ bool own_timer;
+ unsigned int sampling_frequency;
+ struct iio_trigger *active_trigger;
+};
+
+static ssize_t _store_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
+ unsigned int freq;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &freq);
+ if (ret)
+ return ret;
+
+ stm32->sampling_frequency = freq;
+
+ return len;
+}
+
+static ssize_t _read_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
+ unsigned long long freq = stm32->sampling_frequency;
+ u32 psc, arr, cr1;
+
+ regmap_read(stm32->regmap, TIM_CR1, &cr1);
+ regmap_read(stm32->regmap, TIM_PSC, &psc);
+ regmap_read(stm32->regmap, TIM_ARR, &arr);
+
+ if (psc && arr && (cr1 & TIM_CR1_CEN)) {
+ freq = (unsigned long long)clk_get_rate(stm32->clk);
+ do_div(freq, psc);
+ do_div(freq, arr);
+ }
+
+ return sprintf(buf, "%d\n", (unsigned int)freq);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ _read_frequency,
+ _store_frequency);
+
+static struct attribute *stm32_trigger_attrs[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stm32_trigger_attr_group = {
+ .attrs = stm32_trigger_attrs,
+};
+
+static const struct attribute_group *stm32_trigger_attr_groups[] = {
+ &stm32_trigger_attr_group,
+ NULL,
+};
+
+static
+ssize_t _show_master_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
+ u32 cr2;
+
+ regmap_read(stm32->regmap, TIM_CR2, &cr2);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", (cr2 >> 4) & 0x7);
+}
+
+static
+ssize_t _store_master_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
+ u8 mode;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &mode);
+ if (ret)
+ return ret;
+
+ if (mode > 0x7)
+ return -EINVAL;
+
+ regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, mode << 4);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(master_mode, S_IRUGO | S_IWUSR,
+ _show_master_mode,
+ _store_master_mode,
+ 0);
+
+static
+ssize_t _show_slave_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
+ u32 smcr;
+
+ regmap_read(stm32->regmap, TIM_SMCR, &smcr);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", smcr & 0x3);
+}
+
+static
+ssize_t _store_slave_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
+ u8 mode;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &mode);
+ if (ret)
+ return ret;
+
+ if (mode > 0x7)
+ return -EINVAL;
+
+ regmap_update_bits(stm32->regmap, TIM_SMCR, TIM_SMCR_SMS, mode);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(slave_mode, S_IRUGO | S_IWUSR,
+ _show_slave_mode,
+ _store_slave_mode,
+ 0);
+
+static struct attribute *stm32_timer_attrs[] = {
+ &iio_dev_attr_master_mode.dev_attr.attr,
+ &iio_dev_attr_slave_mode.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stm32_timer_attr_group = {
+ .attrs = stm32_timer_attrs,
+};
+
+static int stm32_timer_start(struct stm32_iio_timer_dev *stm32)
+{
+ unsigned long long prd, div;
+ int prescaler = 0;
+ u32 max_arr = 0xFFFF, cr1;
+
+ if (stm32->sampling_frequency == 0)
+ return 0;
+
+ /* Period and prescaler values depends of clock rate */
+ div = (unsigned long long)clk_get_rate(stm32->clk);
+
+ do_div(div, stm32->sampling_frequency);
+
+ prd = div;
+
+ while (div > max_arr) {
+ prescaler++;
+ div = prd;
+ do_div(div, (prescaler + 1));
+ }
+ prd = div;
+
+ if (prescaler > MAX_TIM_PSC) {
+ dev_err(stm32->dev, "prescaler exceeds the maximum value\n");
+ return -EINVAL;
+ }
+
+ /* Check that we own the timer */
+ regmap_read(stm32->regmap, TIM_CR1, &cr1);
+ if ((cr1 & TIM_CR1_CEN) && !stm32->own_timer)
+ return -EBUSY;
+
+ if (!stm32->own_timer) {
+ stm32->own_timer = true;
+ clk_enable(stm32->clk);
+ }
+
+ regmap_write(stm32->regmap, TIM_PSC, prescaler);
+ regmap_write(stm32->regmap, TIM_ARR, prd - 1);
+ regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
+
+ /* Force master mode to update mode */
+ regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, 0x20);
+
+ /* Make sure that registers are updated */
+ regmap_update_bits(stm32->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
+
+ /* Enable interrupt */
+ regmap_write(stm32->regmap, TIM_SR, 0);
+ regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, TIM_DIER_UIE);
+
+ /* Enable controller */
+ regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
+
+ return 0;
+}
+
+static int stm32_timer_stop(struct stm32_iio_timer_dev *stm32)
+{
+ if (!stm32->own_timer)
+ return 0;
+
+ /* Stop timer */
+ regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, 0);
+ regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+ regmap_write(stm32->regmap, TIM_PSC, 0);
+ regmap_write(stm32->regmap, TIM_ARR, 0);
+
+ clk_disable(stm32->clk);
+
+ stm32->own_timer = false;
+ stm32->active_trigger = NULL;
+
+ return 0;
+}
+
+static int stm32_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
+
+ stm32->active_trigger = trig;
+
+ if (state)
+ return stm32_timer_start(stm32);
+ else
+ return stm32_timer_stop(stm32);
+}
+
+static irqreturn_t stm32_timer_irq_handler(int irq, void *private)
+{
+ struct stm32_iio_timer_dev *stm32 = private;
+ u32 sr;
+
+ regmap_read(stm32->regmap, TIM_SR, &sr);
+ regmap_write(stm32->regmap, TIM_SR, 0);
+
+ if ((sr & TIM_SR_UIF) && stm32->active_trigger)
+ iio_trigger_poll(stm32->active_trigger);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops timer_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = stm32_set_trigger_state,
+};
+
+static int stm32_setup_iio_triggers(struct stm32_iio_timer_dev *stm32)
+{
+ int ret;
+ struct property *p;
+ const char *cur = NULL;
+
+ p = of_find_property(stm32->dev->of_node,
+ "st,output-triggers-names", NULL);
+
+ while ((cur = of_prop_next_string(p, cur)) != NULL) {
+ struct iio_trigger *trig;
+
+ trig = devm_iio_trigger_alloc(stm32->dev, "%s", cur);
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = stm32->dev->parent;
+ trig->ops = &timer_trigger_ops;
+ trig->dev.groups = stm32_trigger_attr_groups;
+ iio_trigger_set_drvdata(trig, stm32);
+
+ ret = devm_iio_trigger_register(stm32->dev, trig);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * is_stm32_iio_timer_trigger
+ * @trig: trigger to be checked
+ *
+ * return true if the trigger is a valid stm32 iio timer trigger
+ * either return false
+ */
+bool is_stm32_iio_timer_trigger(struct iio_trigger *trig)
+{
+ return (trig->ops == &timer_trigger_ops);
+}
+EXPORT_SYMBOL(is_stm32_iio_timer_trigger);
+
+static int stm32_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_iio_timer_dev *dev = iio_priv(indio_dev);
+ int ret;
+
+ if (!is_stm32_iio_timer_trigger(trig))
+ return -EINVAL;
+
+ ret = of_property_match_string(dev->dev->of_node,
+ "st,input-triggers-names",
+ trig->name);
+
+ if (ret < 0)
+ return ret;
+
+ regmap_update_bits(dev->regmap, TIM_SMCR, TIM_SMCR_TS, ret << 4);
+
+ return 0;
+}
+
+static const struct iio_info stm32_trigger_info = {
+ .driver_module = THIS_MODULE,
+ .validate_trigger = stm32_validate_trigger,
+ .attrs = &stm32_timer_attr_group,
+};
+
+static struct stm32_iio_timer_dev *stm32_setup_iio_device(struct device *dev)
+{
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct stm32_iio_timer_dev));
+ if (!indio_dev)
+ return NULL;
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &stm32_trigger_info;
+ indio_dev->modes = INDIO_EVENT_TRIGGERED;
+ indio_dev->num_channels = 0;
+ indio_dev->dev.of_node = dev->of_node;
+
+ ret = iio_triggered_event_setup(indio_dev,
+ NULL,
+ stm32_timer_irq_handler);
+ if (ret)
+ return NULL;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ iio_triggered_event_cleanup(indio_dev);
+ return NULL;
+ }
+
+ return iio_priv(indio_dev);
+}
+
+static int stm32_iio_timer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_iio_timer_dev *stm32;
+ struct stm32_gptimer_dev *mfd = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ stm32 = stm32_setup_iio_device(dev);
+ if (!stm32)
+ return -ENOMEM;
+
+ stm32->dev = dev;
+ stm32->regmap = mfd->regmap;
+ stm32->clk = mfd->clk;
+
+ stm32->irq = platform_get_irq(pdev, 0);
+ if (stm32->irq < 0)
+ return -EINVAL;
+
+ ret = devm_request_irq(stm32->dev, stm32->irq,
+ stm32_timer_irq_handler, IRQF_SHARED,
+ "iiotimer_event", stm32);
+ if (ret)
+ return ret;
+
+ ret = stm32_setup_iio_triggers(stm32);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, stm32);
+
+ return 0;
+}
+
+static int stm32_iio_timer_remove(struct platform_device *pdev)
+{
+ struct stm32_iio_timer_dev *stm32 = platform_get_drvdata(pdev);
+
+ iio_triggered_event_cleanup((struct iio_dev *)stm32);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_trig_of_match[] = {
+ {
+ .compatible = "st,stm32-iio-timer",
+ },
+};
+MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
+
+static struct platform_driver stm32_iio_timer_driver = {
+ .probe = stm32_iio_timer_probe,
+ .remove = stm32_iio_timer_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = stm32_trig_of_match,
+ },
+};
+module_platform_driver(stm32_iio_timer_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_DESCRIPTION("STMicroelectronics STM32 iio timer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index 809b2e7..f2af4fe 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -46,5 +46,4 @@ config IIO_SYSFS_TRIGGER
To compile this driver as a module, choose M here: the
module will be called iio-trig-sysfs.
-
endmenu
diff --git a/include/dt-bindings/iio/timer/st,stm32-iio-timer.h b/include/dt-bindings/iio/timer/st,stm32-iio-timer.h
new file mode 100644
index 0000000..d39bf16
--- /dev/null
+++ b/include/dt-bindings/iio/timer/st,stm32-iio-timer.h
@@ -0,0 +1,23 @@
+/*
+ * st,stm32-iio-timer.h
+ *
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _DT_BINDINGS_IIO_TIMER_H_
+#define _DT_BINDINGS_IIO_TIMER_H_
+
+#define TIM1_TRGO "tim1_trgo"
+#define TIM2_TRGO "tim2_trgo"
+#define TIM3_TRGO "tim3_trgo"
+#define TIM4_TRGO "tim4_trgo"
+#define TIM5_TRGO "tim5_trgo"
+#define TIM6_TRGO "tim6_trgo"
+#define TIM7_TRGO "tim7_trgo"
+#define TIM8_TRGO "tim8_trgo"
+#define TIM9_TRGO "tim9_trgo"
+#define TIM12_TRGO "tim12_trgo"
+
+#endif
diff --git a/include/linux/iio/timer/stm32-iio-timers.h b/include/linux/iio/timer/stm32-iio-timers.h
new file mode 100644
index 0000000..5d1b86c
--- /dev/null
+++ b/include/linux/iio/timer/stm32-iio-timers.h
@@ -0,0 +1,16 @@
+/*
+ * stm32-iio-timers.h
+ *
+ * Copyright (C) STMicroelectronics 2016
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STM32_IIO_TIMERS_H_
+#define _STM32_IIO_TIMERS_H_
+
+#include <dt-bindings/iio/timer/st,stm32-iio-timer.h>
+
+bool is_stm32_iio_timer_trigger(struct iio_trigger *trig);
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v2 7/7] ARM: dts: stm32: add stm32 general purpose timer driver in DT
From: Benjamin Gaignard @ 2016-11-24 15:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480000463-9625-1-git-send-email-benjamin.gaignard@st.com>
Add general purpose timers and it sub-nodes into DT for stm32f4.
Define and enable pwm1 and pwm3 for stm32f469 discovery board
version 2:
- use parameters to describe hardware capabilities
- do not use references for pwm and iio timer subnodes
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 305 +++++++++++++++++++++++++++++++++-
arch/arm/boot/dts/stm32f469-disco.dts | 28 ++++
2 files changed, 332 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index bca491d..8f217b1 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -48,7 +48,7 @@
#include "skeleton.dtsi"
#include "armv7-m.dtsi"
#include <dt-bindings/pinctrl/stm32f429-pinfunc.h>
-
+#include <dt-bindings/iio/timer/st,stm32-iio-timer.h>
/ {
clocks {
clk_hse: clk-hse {
@@ -355,6 +355,21 @@
slew-rate = <2>;
};
};
+
+ pwm1_pins: pwm at 1 {
+ pins {
+ pinmux = <STM32F429_PA8_FUNC_TIM1_CH1>,
+ <STM32F429_PB13_FUNC_TIM1_CH1N>,
+ <STM32F429_PB12_FUNC_TIM1_BKIN>;
+ };
+ };
+
+ pwm3_pins: pwm at 3 {
+ pins {
+ pinmux = <STM32F429_PB4_FUNC_TIM3_CH1>,
+ <STM32F429_PB5_FUNC_TIM3_CH2>;
+ };
+ };
};
rcc: rcc at 40023810 {
@@ -426,6 +441,294 @@
interrupts = <80>;
clocks = <&rcc 0 38>;
};
+
+ gptimer1: gptimer1 at 40010000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40010000 0x400>;
+ clocks = <&rcc 0 160>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm1 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,breakinput;
+ st,complementary;
+ status = "disabled";
+ };
+
+ iiotimer1 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <27>;
+ st,input-triggers-names = TIM5_TRGO,
+ TIM2_TRGO,
+ TIM4_TRGO,
+ TIM3_TRGO;
+ st,output-triggers-names = TIM1_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer2: gptimer2 at 40000000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40000000 0x400>;
+ clocks = <&rcc 0 128>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm2 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,32bits-counter;
+ status = "disabled";
+ };
+
+ iiotimer2 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <28>;
+ st,input-triggers-names = TIM1_TRGO,
+ TIM8_TRGO,
+ TIM3_TRGO,
+ TIM4_TRGO;
+ st,output-triggers-names = TIM2_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer3: gptimer3 at 40000400 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40000400 0x400>;
+ clocks = <&rcc 0 129>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm3 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ status = "disabled";
+ };
+
+ iiotimer3 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <29>;
+ st,input-triggers-names = TIM1_TRGO,
+ TIM8_TRGO,
+ TIM5_TRGO,
+ TIM4_TRGO;
+ st,output-triggers-names = TIM3_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer4: gptimer4 at 40000800 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40000800 0x400>;
+ clocks = <&rcc 0 130>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm4 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ status = "disabled";
+ };
+
+ iiotimer4 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <30>;
+ st,input-triggers-names = TIM1_TRGO,
+ TIM2_TRGO,
+ TIM3_TRGO,
+ TIM8_TRGO;
+ st,output-triggers-names = TIM4_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer5: gptimer5 at 40000C00 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40000C00 0x400>;
+ clocks = <&rcc 0 131>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm5 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,32bits-counter;
+ status = "disabled";
+ };
+
+ iiotimer5 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <50>;
+ st,input-triggers-names = TIM2_TRGO,
+ TIM3_TRGO,
+ TIM4_TRGO,
+ TIM8_TRGO;
+ st,output-triggers-names = TIM5_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer6: gptimer6 at 40001000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40001000 0x400>;
+ clocks = <&rcc 0 132>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ iiotimer6 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <54>;
+ st,output-triggers-names = TIM6_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer7: gptimer7 at 40001400 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40001400 0x400>;
+ clocks = <&rcc 0 133>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ iiotimer7 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <55>;
+ st,output-triggers-names = TIM7_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer8: gptimer8 at 40010400 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40010400 0x400>;
+ clocks = <&rcc 0 161>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm8 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <4>;
+ st,complementary;
+ st,breakinput;
+ status = "disabled";
+ };
+
+ iiotimer8 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <46>;
+ st,input-triggers-names = TIM1_TRGO,
+ TIM2_TRGO,
+ TIM4_TRGO,
+ TIM5_TRGO;
+ st,output-triggers-names = TIM8_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer9: gptimer9 at 40014000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40014000 0x400>;
+ clocks = <&rcc 0 176>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm9 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <2>;
+ status = "disabled";
+ };
+
+ iiotimer9 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <24>;
+ st,input-triggers-names = TIM2_TRGO,
+ TIM3_TRGO;
+ st,output-triggers-names = TIM9_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer10: gptimer10 at 40014400 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40014400 0x400>;
+ clocks = <&rcc 0 177>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm10 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <1>;
+ status = "disabled";
+ };
+ };
+
+ gptimer11: gptimer11 at 40014800 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40014800 0x400>;
+ clocks = <&rcc 0 178>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm11 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <1>;
+ status = "disabled";
+ };
+ };
+
+ gptimer12: gptimer12 at 40001800 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40001800 0x400>;
+ clocks = <&rcc 0 134>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm12 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <2>;
+ status = "disabled";
+ };
+
+ iiotimer12 at 0 {
+ compatible = "st,stm32-iio-timer";
+ interrupts = <43>;
+ st,input-triggers-names = TIM4_TRGO,
+ TIM5_TRGO;
+ st,output-triggers-names = TIM12_TRGO;
+ status = "disabled";
+ };
+ };
+
+ gptimer13: gptimer13 at 40001C00 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40001C00 0x400>;
+ clocks = <&rcc 0 135>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm13 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <1>;
+ status = "disabled";
+ };
+ };
+
+ gptimer14: gptimer14 at 40002000 {
+ compatible = "st,stm32-gptimer";
+ reg = <0x40002000 0x400>;
+ clocks = <&rcc 0 136>;
+ clock-names = "clk_int";
+ status = "disabled";
+
+ pwm14 at 0 {
+ compatible = "st,stm32-pwm";
+ st,pwm-num-chan = <1>;
+ status = "disabled";
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
index 8a163d7..0c93846 100644
--- a/arch/arm/boot/dts/stm32f469-disco.dts
+++ b/arch/arm/boot/dts/stm32f469-disco.dts
@@ -81,3 +81,31 @@
&usart3 {
status = "okay";
};
+
+&gptimer1 {
+ status = "okay";
+
+ pwm1 at 0 {
+ pinctrl-0 = <&pwm1_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
+ iiotimer1 at 0 {
+ status = "okay";
+ };
+};
+
+&gptimer3 {
+ status = "okay";
+
+ pwm3 at 0 {
+ pinctrl-0 = <&pwm3_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
+ iiotimer3 at 0 {
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH RFC] drm/sun4i: rgb: Add 5% tolerance to dot clock frequency check
From: Icenowy Zheng @ 2016-11-24 15:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161124112231.4297-1-wens@csie.org>
24.11.2016, 19:27, "Chen-Yu Tsai" <wens@csie.org>:
> The panels shipped with Allwinner devices are very "generic", i.e.
> they do not have model numbers or reliable sources of information
> for the timings (that we know of) other than the fex files shipped
> on them. The dot clock frequency provided in the fex files have all
> been rounded to the nearest MHz, as that is the unit used in them.
>
> We were using the simple panel "urt,umsh-8596md-t" as a substitute
> for the A13 Q8 tablets in the absence of a specific model for what
> may be many different but otherwise timing compatible panels. This
> was usable without any visual artifacts or side effects, until the
> dot clock rate check was added in commit bb43d40d7c83 ("drm/sun4i:
> rgb: Validate the clock rate").
>
> The reason this check fails is because the dotclock frequency for
> this model is 33.26 MHz, which is not achievable with our dot clock
> hardware, and the rate returned by clk_round_rate deviates slightly,
> causing the driver to reject the display mode.
>
> The LCD panels have some tolerance on the dot clock frequency, even
> if it's not specified in their datasheets.
>
> This patch adds a 5% tolerence to the dot clock check.
Tested by me on an A33 Q8 tablet with 800x480 LCD and
"urt,umsh-8596md-t" compatible.
The tablet is Aoson M751S.
Works properly with sun4i-drm, with my pll-mipi patch applied.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
>
> The few LCD panel datasheets I found did not list minimums or maximums
> for the dot clock rate. The 5% tolerance is just something I made up.
> The point is to be able to use our dot clock, which doesn't have the
> resolution needed to generate the exact clock rate requested. AFAIK
> the sun4i driver is one of the strictest ones with regards to the dot
> clock frequency. Some drivers don't even check it.
>
> The clock rate given in vendor fex files are already rounded down to
> MHz resolution. I doubt not using the exact rate as specified in simple
> panels would cause any issues. But my experience is limited here.
> Feedback on this is requested.
>
> ---
> ?drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++--
> ?1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> index d198ad7e5323..66ad86afa561 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> @@ -93,11 +93,12 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector,
>
> ?????????DRM_DEBUG_DRIVER("Vertical parameters OK\n");
>
> + /* Check against a 5% tolerance for the dot clock */
> ?????????rounded_rate = clk_round_rate(tcon->dclk, rate);
> - if (rounded_rate < rate)
> + if (rounded_rate < rate * 19 / 20)
> ?????????????????return MODE_CLOCK_LOW;
>
> - if (rounded_rate > rate)
> + if (rounded_rate > rate * 21 / 20)
> ?????????????????return MODE_CLOCK_HIGH;
>
> ?????????DRM_DEBUG_DRIVER("Clock rate OK\n");
> --
> 2.10.2
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox