Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Sven Van Asbroeck and committed by
Shawn Guo
c7995bcb 8b8cb52a

+32 -3
+32 -3
drivers/bus/imx-weim.c
··· 46 46 }; 47 47 48 48 #define MAX_CS_REGS_COUNT 6 49 + #define MAX_CS_COUNT 6 49 50 #define OF_REG_SIZE 3 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 + }; 50 60 51 61 static const struct of_device_id weim_id_table[] = { 52 62 /* i.MX1/21 */ ··· 122 112 } 123 113 124 114 /* Parse and set the timing for this device. */ 125 - static int __init weim_timing_setup(struct device_node *np, void __iomem *base, 126 - const struct imx_weim_devtype *devtype) 115 + static int __init weim_timing_setup(struct device *dev, 116 + struct device_node *np, void __iomem *base, 117 + const struct imx_weim_devtype *devtype, 118 + struct cs_timing_state *ts) 127 119 { 128 120 u32 cs_idx, value[MAX_CS_REGS_COUNT]; 129 121 int i, ret; 130 122 int reg_idx, num_regs; 123 + struct cs_timing *cst; 131 124 132 125 if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 126 + return -EINVAL; 127 + if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) 133 128 return -EINVAL; 134 129 135 130 ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", ··· 161 146 if (cs_idx >= devtype->cs_count) 162 147 return -EINVAL; 163 148 149 + /* prevent re-configuring a CS that's already been configured */ 150 + cst = &ts->cs[cs_idx]; 151 + if (cst->is_applied && memcmp(value, cst->regs, 152 + devtype->cs_regs_count * sizeof(u32))) { 153 + dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); 154 + return -EINVAL; 155 + } 156 + 164 157 /* set the timing for WEIM */ 165 158 for (i = 0; i < devtype->cs_regs_count; i++) 166 159 writel(value[i], 167 160 base + cs_idx * devtype->cs_stride + i * 4); 161 + if (!cst->is_applied) { 162 + cst->is_applied = true; 163 + memcpy(cst->regs, value, 164 + devtype->cs_regs_count * sizeof(u32)); 165 + } 168 166 } 169 167 170 168 return 0; ··· 191 163 const struct imx_weim_devtype *devtype = of_id->data; 192 164 struct device_node *child; 193 165 int ret, have_child = 0; 166 + struct cs_timing_state ts = {}; 194 167 195 168 if (devtype == &imx50_weim_devtype) { 196 169 ret = imx_weim_gpr_setup(pdev); ··· 200 171 } 201 172 202 173 for_each_available_child_of_node(pdev->dev.of_node, child) { 203 - ret = weim_timing_setup(child, base, devtype); 174 + ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); 204 175 if (ret) 205 176 dev_warn(&pdev->dev, "%pOF set timing failed.\n", 206 177 child);