From 1da53d23a41c5f77963984d8da5623ed56918ada Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Fri, 30 Dec 2022 12:29:20 +0300 Subject: fpga: microchip-spi: move SPI I/O buffers out of stack As spi-summary doc says: > I/O buffers use the usual Linux rules, and must be DMA-safe. > You'd normally allocate them from the heap or free page pool. > Don't use the stack, or anything that's declared "static". Replace spi_write() with spi_write_then_read(), which is dma-safe for on-stack buffers. Use cacheline aligned buffers for transfers used in spi_sync_transfer(). Although everything works OK with stack-located I/O buffers, better follow the doc to be safe. Fixes: 5f8d4a900830 ("fpga: microchip-spi: add Microchip MPF FPGA manager") Signed-off-by: Ivan Bornyakov Acked-by: Conor Dooley Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20221230092922.18822-2-i.bornyakov@metrotek.ru Signed-off-by: Xu Yilun --- drivers/fpga/microchip-spi.c | 93 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 46 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 7436976ea904..bb69f5beefe7 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -42,46 +42,55 @@ struct mpf_priv { struct spi_device *spi; bool program_mode; + u8 tx __aligned(ARCH_KMALLOC_MINALIGN); + u8 rx; }; -static int mpf_read_status(struct spi_device *spi) +static int mpf_read_status(struct mpf_priv *priv) { - u8 status = 0, status_command = MPF_SPI_READ_STATUS; - struct spi_transfer xfers[2] = { 0 }; - int ret; - /* * HW status is returned on MISO in the first byte after CS went * active. However, first reading can be inadequate, so we submit * two identical SPI transfers and use result of the later one. */ - xfers[0].tx_buf = &status_command; - xfers[1].tx_buf = &status_command; - xfers[0].rx_buf = &status; - xfers[1].rx_buf = &status; - xfers[0].len = 1; - xfers[1].len = 1; - xfers[0].cs_change = 1; + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + .cs_change = 1, + }, { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + }, + }; + u8 status; + int ret; + + priv->tx = MPF_SPI_READ_STATUS; + + ret = spi_sync_transfer(priv->spi, xfers, 2); + if (ret) + return ret; - ret = spi_sync_transfer(spi, xfers, 2); + status = priv->rx; if ((status & MPF_STATUS_SPI_VIOLATION) || (status & MPF_STATUS_SPI_ERROR)) - ret = -EIO; + return -EIO; - return ret ? : status; + return status; } static enum fpga_mgr_states mpf_ops_state(struct fpga_manager *mgr) { struct mpf_priv *priv = mgr->priv; - struct spi_device *spi; bool program_mode; int status; - spi = priv->spi; program_mode = priv->program_mode; - status = mpf_read_status(spi); + status = mpf_read_status(priv); if (!program_mode && !status) return FPGA_MGR_STATE_OPERATING; @@ -186,12 +195,12 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, } /* Poll HW status until busy bit is cleared and mask bits are set. */ -static int mpf_poll_status(struct spi_device *spi, u8 mask) +static int mpf_poll_status(struct mpf_priv *priv, u8 mask) { int status, retries = MPF_STATUS_POLL_RETRIES; while (retries--) { - status = mpf_read_status(spi); + status = mpf_read_status(priv); if (status < 0) return status; @@ -205,32 +214,32 @@ static int mpf_poll_status(struct spi_device *spi, u8 mask) return -EBUSY; } -static int mpf_spi_write(struct spi_device *spi, const void *buf, size_t buf_size) +static int mpf_spi_write(struct mpf_priv *priv, const void *buf, size_t buf_size) { - int status = mpf_poll_status(spi, 0); + int status = mpf_poll_status(priv, 0); if (status < 0) return status; - return spi_write(spi, buf, buf_size); + return spi_write_then_read(priv->spi, buf, buf_size, NULL, 0); } -static int mpf_spi_write_then_read(struct spi_device *spi, +static int mpf_spi_write_then_read(struct mpf_priv *priv, const void *txbuf, size_t txbuf_size, void *rxbuf, size_t rxbuf_size) { const u8 read_command[] = { MPF_SPI_READ_DATA }; int ret; - ret = mpf_spi_write(spi, txbuf, txbuf_size); + ret = mpf_spi_write(priv, txbuf, txbuf_size); if (ret) return ret; - ret = mpf_poll_status(spi, MPF_STATUS_READY); + ret = mpf_poll_status(priv, MPF_STATUS_READY); if (ret < 0) return ret; - return spi_write_then_read(spi, read_command, sizeof(read_command), + return spi_write_then_read(priv->spi, read_command, sizeof(read_command), rxbuf, rxbuf_size); } @@ -242,7 +251,6 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, const u8 isc_en_command[] = { MPF_SPI_ISC_ENABLE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; u32 isc_ret = 0; int ret; @@ -251,9 +259,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EOPNOTSUPP; } - spi = priv->spi; - - ret = mpf_spi_write_then_read(spi, isc_en_command, sizeof(isc_en_command), + ret = mpf_spi_write_then_read(priv, isc_en_command, sizeof(isc_en_command), &isc_ret, sizeof(isc_ret)); if (ret || isc_ret) { dev_err(dev, "Failed to enable ISC: spi_ret %d, isc_ret %u\n", @@ -261,7 +267,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EFAULT; } - ret = mpf_spi_write(spi, program_mode, sizeof(program_mode)); + ret = mpf_spi_write(priv, program_mode, sizeof(program_mode)); if (ret) { dev_err(dev, "Failed to enter program mode: %d\n", ret); return ret; @@ -274,11 +280,9 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count) { - u8 spi_frame_command[] = { MPF_SPI_FRAME }; struct spi_transfer xfers[2] = { 0 }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret, i; if (count % MPF_SPI_FRAME_SIZE) { @@ -287,18 +291,18 @@ static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count return -EINVAL; } - spi = priv->spi; - - xfers[0].tx_buf = spi_frame_command; - xfers[0].len = sizeof(spi_frame_command); + xfers[0].tx_buf = &priv->tx; + xfers[0].len = 1; for (i = 0; i < count / MPF_SPI_FRAME_SIZE; i++) { xfers[1].tx_buf = buf + i * MPF_SPI_FRAME_SIZE; xfers[1].len = MPF_SPI_FRAME_SIZE; - ret = mpf_poll_status(spi, 0); - if (ret >= 0) - ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); + ret = mpf_poll_status(priv, 0); + if (ret >= 0) { + priv->tx = MPF_SPI_FRAME; + ret = spi_sync_transfer(priv->spi, xfers, ARRAY_SIZE(xfers)); + } if (ret) { dev_err(dev, "Failed to write bitstream frame %d/%zu\n", @@ -317,12 +321,9 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, const u8 release_command[] = { MPF_SPI_RELEASE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret; - spi = priv->spi; - - ret = mpf_spi_write(spi, isc_dis_command, sizeof(isc_dis_command)); + ret = mpf_spi_write(priv, isc_dis_command, sizeof(isc_dis_command)); if (ret) { dev_err(dev, "Failed to disable ISC: %d\n", ret); return ret; @@ -330,7 +331,7 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, usleep_range(1000, 2000); - ret = mpf_spi_write(spi, release_command, sizeof(release_command)); + ret = mpf_spi_write(priv, release_command, sizeof(release_command)); if (ret) { dev_err(dev, "Failed to exit program mode: %d\n", ret); return ret; -- cgit v1.2.3 From 88e705697e801299a13ecaf2ba54599964fe711c Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Fri, 30 Dec 2022 12:29:21 +0300 Subject: fpga: microchip-spi: rewrite status polling in a time measurable way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original busy loop with retries count in mpf_poll_status() is not too reliable, as it takes different times on different systems. Replace it with read_poll_timeout() macro. While at it, fix polling stop condition to met function's original intention declared in the comment. The issue with original polling stop condition is that it stops if any of mask bits is set, while intention was to stop if all mask bits is set. This was not noticible because only MPF_STATUS_READY is passed as mask argument and it is BIT(1). Fixes: 5f8d4a900830 ("fpga: microchip-spi: add Microchip MPF FPGA manager") Signed-off-by: Ivan Bornyakov Reviewed-by: Ilpo Järvinen Acked-by: Conor Dooley Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20221230092922.18822-3-i.bornyakov@metrotek.ru Signed-off-by: Xu Yilun --- drivers/fpga/microchip-spi.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index bb69f5beefe7..137fafdf57a6 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,7 @@ #define MPF_BITS_PER_COMPONENT_SIZE 22 -#define MPF_STATUS_POLL_RETRIES 10000 +#define MPF_STATUS_POLL_TIMEOUT (2 * USEC_PER_SEC) #define MPF_STATUS_BUSY BIT(0) #define MPF_STATUS_READY BIT(1) #define MPF_STATUS_SPI_VIOLATION BIT(2) @@ -194,24 +195,25 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, return 0; } -/* Poll HW status until busy bit is cleared and mask bits are set. */ static int mpf_poll_status(struct mpf_priv *priv, u8 mask) { - int status, retries = MPF_STATUS_POLL_RETRIES; + int ret, status; - while (retries--) { - status = mpf_read_status(priv); - if (status < 0) - return status; - - if (status & MPF_STATUS_BUSY) - continue; - - if (!mask || (status & mask)) - return status; - } + /* + * Busy poll HW status. Polling stops if any of the following + * conditions are met: + * - timeout is reached + * - mpf_read_status() returns an error + * - busy bit is cleared AND mask bits are set + */ + ret = read_poll_timeout(mpf_read_status, status, + (status < 0) || + ((status & (MPF_STATUS_BUSY | mask)) == mask), + 0, MPF_STATUS_POLL_TIMEOUT, false, priv); + if (ret < 0) + return ret; - return -EBUSY; + return status; } static int mpf_spi_write(struct mpf_priv *priv, const void *buf, size_t buf_size) -- cgit v1.2.3 From 9d94ec985702a454638d49816fa66718cb8b228a Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Fri, 30 Dec 2022 12:29:22 +0300 Subject: fpga: microchip-spi: separate data frame write routine mpf_ops_write() function writes bitstream data to the FPGA by a smaller frames. Introduce mpf_spi_frame_write() function which is for writing a single data frame and use it in mpf_ops_write(). No functional changes intended. Signed-off-by: Ivan Bornyakov Acked-by: Conor Dooley Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20221230092922.18822-4-i.bornyakov@metrotek.ru Signed-off-by: Xu Yilun --- drivers/fpga/microchip-spi.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 137fafdf57a6..d6070e7f5205 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -280,9 +280,30 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return 0; } +static int mpf_spi_frame_write(struct mpf_priv *priv, const char *buf) +{ + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .len = 1, + }, { + .tx_buf = buf, + .len = MPF_SPI_FRAME_SIZE, + }, + }; + int ret; + + ret = mpf_poll_status(priv, 0); + if (ret < 0) + return ret; + + priv->tx = MPF_SPI_FRAME; + + return spi_sync_transfer(priv->spi, xfers, ARRAY_SIZE(xfers)); +} + static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count) { - struct spi_transfer xfers[2] = { 0 }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; int ret, i; @@ -293,19 +314,8 @@ static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count return -EINVAL; } - xfers[0].tx_buf = &priv->tx; - xfers[0].len = 1; - for (i = 0; i < count / MPF_SPI_FRAME_SIZE; i++) { - xfers[1].tx_buf = buf + i * MPF_SPI_FRAME_SIZE; - xfers[1].len = MPF_SPI_FRAME_SIZE; - - ret = mpf_poll_status(priv, 0); - if (ret >= 0) { - priv->tx = MPF_SPI_FRAME; - ret = spi_sync_transfer(priv->spi, xfers, ARRAY_SIZE(xfers)); - } - + ret = mpf_spi_frame_write(priv, buf + i * MPF_SPI_FRAME_SIZE); if (ret) { dev_err(dev, "Failed to write bitstream frame %d/%zu\n", i, count / MPF_SPI_FRAME_SIZE); -- cgit v1.2.3 From 782d8e61b5d6c15c5b7cfd5726da1f20f7cc8366 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 12 Jan 2023 22:37:20 -0800 Subject: fpga: dfl: kernel-doc corrections Fix W=1 kernel-doc warnings in drivers/fpga/: drivers/fpga/dfl.c:54: warning: cannot understand function prototype: 'struct dfl_dev_info ' drivers/fpga/dfl.c:74: warning: cannot understand function prototype: 'struct dfl_chardev_info ' drivers/fpga/dfl-fme-pr.c:175: warning: Function parameter or member 'feature' not described in 'dfl_fme_create_mgr' drivers/fpga/dfl-fme-pr.c:280: warning: expecting prototype for dfl_fme_destroy_bridge(). Prototype was for dfl_fme_destroy_bridges() instead Signed-off-by: Randy Dunlap Cc: Wu Hao Cc: Tom Rix Cc: Moritz Fischer Cc: Xu Yilun Cc: linux-fpga@vger.kernel.org Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20230113063720.10668-1-rdunlap@infradead.org Signed-off-by: Xu Yilun --- drivers/fpga/dfl-fme-pr.c | 4 ++-- drivers/fpga/dfl.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index d61ce9a18879..cdcf6dea4cc9 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -164,7 +164,7 @@ free_exit: /** * dfl_fme_create_mgr - create fpga mgr platform device as child device - * + * @feature: sub feature info * @pdata: fme platform_device's pdata * * Return: mgr platform device if successful, and error code otherwise. @@ -273,7 +273,7 @@ static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) } /** - * dfl_fme_destroy_bridge - destroy all fpga bridge platform device + * dfl_fme_destroy_bridges - destroy all fpga bridge platform device * @pdata: fme platform device's pdata */ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index b9aae85ba930..9e59642dd5ba 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -45,7 +45,7 @@ static const char *dfl_pdata_key_strings[DFL_ID_MAX] = { }; /** - * dfl_dev_info - dfl feature device information. + * struct dfl_dev_info - dfl feature device information. * @name: name string of the feature platform device. * @dfh_id: id value in Device Feature Header (DFH) register by DFL spec. * @id: idr id of the feature dev. @@ -67,7 +67,7 @@ static struct dfl_dev_info dfl_devs[] = { }; /** - * dfl_chardev_info - chardev information of dfl feature device + * struct dfl_chardev_info - chardev information of dfl feature device * @name: nmae string of the char device. * @devt: devt of the char device. */ @@ -708,6 +708,7 @@ struct build_feature_devs_info { * struct dfl_feature_info - sub feature info collected during feature dev build * * @fid: id of this sub feature. + * @revision: revision value of this sub feature. * @mmio_res: mmio resource of this sub feature. * @ioaddr: mapped base address of mmio resource. * @node: node in sub_features linked list. -- cgit v1.2.3 From a73c125bbbcf6185b5a77ae946aa240876b4fe71 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Sat, 14 Jan 2023 23:54:44 +0800 Subject: fpga: dfl: more kernel-doc corrections Fix more kernel-doc warnings in drivers/fpga/: $ ./scripts/kernel-doc -none drivers/fpga/* drivers/fpga/dfl-afu.h:61: warning: expecting prototype for struct fpga_afu_dma_region. Prototype was for struct dfl_afu_dma_region instead drivers/fpga/dfl-afu-region.c:52: warning: Function parameter or member 'pdata' not described in 'afu_mmio_region_add' drivers/fpga/dfl-fme-perf.c:161: warning: Function parameter or member 'node' not described in 'fme_perf_priv' drivers/fpga/dfl-fme-pr.h:70: warning: expecting prototype for struct dfl_fme_bridge_pdata. Prototype was for struct dfl_fme_br_pdata instead drivers/fpga/dfl.h:256: warning: Function parameter or member 'revision' not described in 'dfl_feature' Signed-off-by: Xu Yilun Reviewed-by: Randy Dunlap Link: https://lore.kernel.org/r/20230114155444.794712-1-yilun.xu@intel.com --- drivers/fpga/dfl-afu-region.c | 1 + drivers/fpga/dfl-afu.h | 2 +- drivers/fpga/dfl-fme-perf.c | 2 +- drivers/fpga/dfl-fme-pr.h | 2 +- drivers/fpga/dfl.h | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/dfl-afu-region.c b/drivers/fpga/dfl-afu-region.c index 0804b7a0c298..2e7b41629406 100644 --- a/drivers/fpga/dfl-afu-region.c +++ b/drivers/fpga/dfl-afu-region.c @@ -39,6 +39,7 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu, /** * afu_mmio_region_add - add a mmio region to given feature dev. * + * @pdata: afu platform device's pdata. * @region_index: region index. * @region_size: region size. * @phys: region's physical address of this region. diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h index e5020e2b1f3d..674e9772f0ea 100644 --- a/drivers/fpga/dfl-afu.h +++ b/drivers/fpga/dfl-afu.h @@ -41,7 +41,7 @@ struct dfl_afu_mmio_region { }; /** - * struct fpga_afu_dma_region - afu DMA region data structure + * struct dfl_afu_dma_region - afu DMA region data structure * * @user_addr: region userspace virtual address. * @length: region length. diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 587c82be12f7..7422d2bc6f37 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -141,7 +141,7 @@ * @fab_port_id: used to indicate current working mode of fabric counters. * @fab_lock: lock to protect fabric counters working mode. * @cpu: active CPU to which the PMU is bound for accesses. - * @cpuhp_node: node for CPU hotplug notifier link. + * @node: node for CPU hotplug notifier link. * @cpuhp_state: state for CPU hotplug notification; */ struct fme_perf_priv { diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h index 096a699089d3..761f80f63312 100644 --- a/drivers/fpga/dfl-fme-pr.h +++ b/drivers/fpga/dfl-fme-pr.h @@ -58,7 +58,7 @@ struct dfl_fme_bridge { }; /** - * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device. + * struct dfl_fme_br_pdata - platform data for FME bridge platform device. * * @cdev: container device. * @port_id: port id. diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 06cfcd5e84bb..5fc424d505c8 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -231,6 +231,7 @@ struct dfl_feature_irq_ctx { * * @dev: ptr to pdev of the feature device which has the sub feature. * @id: sub feature id. + * @revision: revision value of this sub feature. * @resource_index: each sub feature has one mmio resource for its registers. * this index is used to find its mmio resource from the * feature dev (platform device)'s resources. -- cgit v1.2.3 From 48ca6e5fa4ffc40a14fe45bd5499428a5a108a68 Mon Sep 17 00:00:00 2001 From: Marco Pagani Date: Wed, 25 Jan 2023 15:06:22 +0100 Subject: fpga: bridge: return errors in the show() method of the "state" attribute This patch changes the show() method of the "state" sysfs attribute to propagate errors returned by the enable_show() op. In this way, userspace can distinguish between when the bridge is actually "enabled" (i.e., allowing signals) or "disabled" (i.e., gating signals), or when there is an error. Currently, enable_show() returns an integer representing the bridge's state (enabled or disabled) or an error code. However, this integer value is interpreted in state_show() as a bool, resulting in the method printing "enabled" (i.e., the bridge allows signals to pass), without propagating the error, even when enable_show() returns an error code. Signed-off-by: Marco Pagani Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20230125140622.176870-1-marpagan@redhat.com Signed-off-by: Xu Yilun --- drivers/fpga/fpga-bridge.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/fpga') diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 727704431f61..5cd40acab5bf 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -293,12 +293,15 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpga_bridge *bridge = to_fpga_bridge(dev); - int enable = 1; + int state = 1; - if (bridge->br_ops && bridge->br_ops->enable_show) - enable = bridge->br_ops->enable_show(bridge); + if (bridge->br_ops && bridge->br_ops->enable_show) { + state = bridge->br_ops->enable_show(bridge); + if (state < 0) + return state; + } - return sprintf(buf, "%s\n", enable ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); } static DEVICE_ATTR_RO(name); -- cgit v1.2.3