diff options
author | Sven Van Asbroeck <thesven73@gmail.com> | 2018-12-17 10:48:00 -0500 |
---|---|---|
committer | Shawn Guo <shawnguo@kernel.org> | 2019-01-11 02:16:45 -0500 |
commit | c7995bcb36ef61e8b4136efab31ecf3c9b1633f9 (patch) | |
tree | 7f8be64f47e0407b4f2012df087de0d37cfcb5a3 | |
parent | 8b8cb52af34da2faa293614b2554c8eac30faeaa (diff) |
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
-rw-r--r-- | drivers/bus/imx-weim.c | 35 |
1 files changed, 32 insertions, 3 deletions
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 1a0e0277a404..db74334ca5ef 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c | |||
@@ -46,8 +46,18 @@ static const struct imx_weim_devtype imx51_weim_devtype = { | |||
46 | }; | 46 | }; |
47 | 47 | ||
48 | #define MAX_CS_REGS_COUNT 6 | 48 | #define MAX_CS_REGS_COUNT 6 |
49 | #define MAX_CS_COUNT 6 | ||
49 | #define OF_REG_SIZE 3 | 50 | #define OF_REG_SIZE 3 |
50 | 51 | ||
52 | struct cs_timing { | ||
53 | bool is_applied; | ||
54 | u32 regs[MAX_CS_REGS_COUNT]; | ||
55 | }; | ||
56 | |||
57 | struct cs_timing_state { | ||
58 | struct cs_timing cs[MAX_CS_COUNT]; | ||
59 | }; | ||
60 | |||
51 | static const struct of_device_id weim_id_table[] = { | 61 | static const struct of_device_id weim_id_table[] = { |
52 | /* i.MX1/21 */ | 62 | /* i.MX1/21 */ |
53 | { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, | 63 | { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, |
@@ -112,15 +122,20 @@ err: | |||
112 | } | 122 | } |
113 | 123 | ||
114 | /* Parse and set the timing for this device. */ | 124 | /* Parse and set the timing for this device. */ |
115 | static int __init weim_timing_setup(struct device_node *np, void __iomem *base, | 125 | static int __init weim_timing_setup(struct device *dev, |
116 | const struct imx_weim_devtype *devtype) | 126 | struct device_node *np, void __iomem *base, |
127 | const struct imx_weim_devtype *devtype, | ||
128 | struct cs_timing_state *ts) | ||
117 | { | 129 | { |
118 | u32 cs_idx, value[MAX_CS_REGS_COUNT]; | 130 | u32 cs_idx, value[MAX_CS_REGS_COUNT]; |
119 | int i, ret; | 131 | int i, ret; |
120 | int reg_idx, num_regs; | 132 | int reg_idx, num_regs; |
133 | struct cs_timing *cst; | ||
121 | 134 | ||
122 | if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) | 135 | if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) |
123 | return -EINVAL; | 136 | return -EINVAL; |
137 | if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) | ||
138 | return -EINVAL; | ||
124 | 139 | ||
125 | ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", | 140 | ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", |
126 | value, devtype->cs_regs_count); | 141 | value, devtype->cs_regs_count); |
@@ -146,10 +161,23 @@ static int __init weim_timing_setup(struct device_node *np, void __iomem *base, | |||
146 | if (cs_idx >= devtype->cs_count) | 161 | if (cs_idx >= devtype->cs_count) |
147 | return -EINVAL; | 162 | return -EINVAL; |
148 | 163 | ||
164 | /* prevent re-configuring a CS that's already been configured */ | ||
165 | cst = &ts->cs[cs_idx]; | ||
166 | if (cst->is_applied && memcmp(value, cst->regs, | ||
167 | devtype->cs_regs_count * sizeof(u32))) { | ||
168 | dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
149 | /* set the timing for WEIM */ | 172 | /* set the timing for WEIM */ |
150 | for (i = 0; i < devtype->cs_regs_count; i++) | 173 | for (i = 0; i < devtype->cs_regs_count; i++) |
151 | writel(value[i], | 174 | writel(value[i], |
152 | base + cs_idx * devtype->cs_stride + i * 4); | 175 | base + cs_idx * devtype->cs_stride + i * 4); |
176 | if (!cst->is_applied) { | ||
177 | cst->is_applied = true; | ||
178 | memcpy(cst->regs, value, | ||
179 | devtype->cs_regs_count * sizeof(u32)); | ||
180 | } | ||
153 | } | 181 | } |
154 | 182 | ||
155 | return 0; | 183 | return 0; |
@@ -163,6 +191,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, | |||
163 | const struct imx_weim_devtype *devtype = of_id->data; | 191 | const struct imx_weim_devtype *devtype = of_id->data; |
164 | struct device_node *child; | 192 | struct device_node *child; |
165 | int ret, have_child = 0; | 193 | int ret, have_child = 0; |
194 | struct cs_timing_state ts = {}; | ||
166 | 195 | ||
167 | if (devtype == &imx50_weim_devtype) { | 196 | if (devtype == &imx50_weim_devtype) { |
168 | ret = imx_weim_gpr_setup(pdev); | 197 | ret = imx_weim_gpr_setup(pdev); |
@@ -171,7 +200,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, | |||
171 | } | 200 | } |
172 | 201 | ||
173 | for_each_available_child_of_node(pdev->dev.of_node, child) { | 202 | for_each_available_child_of_node(pdev->dev.of_node, child) { |
174 | ret = weim_timing_setup(child, base, devtype); | 203 | ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); |
175 | if (ret) | 204 | if (ret) |
176 | dev_warn(&pdev->dev, "%pOF set timing failed.\n", | 205 | dev_warn(&pdev->dev, "%pOF set timing failed.\n", |
177 | child); | 206 | child); |