fpga: xilinx-selectmap: control CSI_B and RDWR_B during configuration

The driver requests the optional CSI_B and RDWR_B GPIOs with
GPIOD_OUT_HIGH and never touches them again. That flag carries a
logical value, so both signals end up asserted from probe on, whatever
polarity the device tree states, and they stay asserted for the
lifetime of the device.

Neither signal belongs to the driver's lifetime. CSI_B (Chip Select)
selects the device on the SelectMAP port, RDWR_B (Read/Write) selects
the transfer direction, so both belong to the data transfer. A device
that is never deselected never lets go of the port, and a port pinned
to write mode cannot be read back.

Keep the two descriptors in the driver private data, request them
deasserted, and assert them only around the configuration data
transfer. RDWR_B is asserted first, UG470 describes in chapter
"SelectMAP ABORT" that changing it while the device is selected aborts
the configuration on the next CCLK.

Signed-off-by: Heiko Schocher <hs@nabladev.com>
Reviewed-by: Xu Yilun <yilun.xu@intel.com>
Link: https://lore.kernel.org/r/20260909112200.13256-1-hs@nabladev.com
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
This commit is contained in:
Heiko Schocher 2026-09-09 13:21:54 +02:00 committed by Xu Yilun
parent a090601269
commit 4e3ca71894

View File

@ -19,6 +19,8 @@
struct xilinx_selectmap_conf {
struct xilinx_fpga_core core;
void __iomem *base;
struct gpio_desc *csi_b;
struct gpio_desc *rdwr_b;
};
#define to_xilinx_selectmap_conf(obj) \
@ -30,16 +32,28 @@ static int xilinx_selectmap_write(struct xilinx_fpga_core *core,
struct xilinx_selectmap_conf *conf = to_xilinx_selectmap_conf(core);
size_t i;
/*
* Select this device and put its SelectMAP port into write mode.
*
* RDWR_B is set before CSI_B. UG470 describes in chapter
* "SelectMAP ABORT" that changing RDWR_B while the device is
* selected aborts the configuration on the next CCLK.
*/
gpiod_set_value(conf->rdwr_b, 1);
gpiod_set_value(conf->csi_b, 1);
for (i = 0; i < count; ++i)
writeb(buf[i], conf->base);
gpiod_set_value(conf->csi_b, 0);
gpiod_set_value(conf->rdwr_b, 0);
return 0;
}
static int xilinx_selectmap_probe(struct platform_device *pdev)
{
struct xilinx_selectmap_conf *conf;
struct gpio_desc *gpio;
void __iomem *base;
conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
@ -55,16 +69,23 @@ static int xilinx_selectmap_probe(struct platform_device *pdev)
"ioremap error\n");
conf->base = base;
/* CSI_B is active low */
gpio = devm_gpiod_get_optional(&pdev->dev, "csi", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
/*
* Request both signals deasserted, so a device sharing the SelectMAP
* bus with others stays off that bus until its bitstream is written.
*
* The value in the gpiod flags is logical, gpiolib drives the line
* high for GPIOD_OUT_LOW when the firmware describes it active low.
*/
conf->csi_b = devm_gpiod_get_optional(&pdev->dev, "csi",
GPIOD_OUT_LOW);
if (IS_ERR(conf->csi_b))
return dev_err_probe(&pdev->dev, PTR_ERR(conf->csi_b),
"Failed to get CSI_B gpio\n");
/* RDWR_B is active low */
gpio = devm_gpiod_get_optional(&pdev->dev, "rdwr", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
conf->rdwr_b = devm_gpiod_get_optional(&pdev->dev, "rdwr",
GPIOD_OUT_LOW);
if (IS_ERR(conf->rdwr_b))
return dev_err_probe(&pdev->dev, PTR_ERR(conf->rdwr_b),
"Failed to get RDWR_B gpio\n");
return xilinx_core_probe(&conf->core);