Loading drivers/mtd/hyperbus/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,11 @@ config HBMC_AM654 This is the driver for HyperBus controller on TI's AM65x and other SoCs config RPCIF_HYPERBUS tristate "Renesas RPC-IF HyperBus driver" depends on RENESAS_RPCIF || COMPILE_TEST depends on MTD_CFI_BE_BYTE_SWAP help This option includes Renesas RPC-IF HyperBus support. endif # MTD_HYPERBUS drivers/mtd/hyperbus/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -2,3 +2,4 @@ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o obj-$(CONFIG_RPCIF_HYPERBUS) += rpc-if.o drivers/mtd/hyperbus/hbmc-am654.c +128 −16 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ // Author: Vignesh Raghavendra <vigneshr@ti.com> #include <linux/completion.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> Loading @@ -13,11 +17,18 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sched/task_stack.h> #include <linux/types.h> #define AM654_HBMC_CALIB_COUNT 25 struct am654_hbmc_device_priv { struct completion rx_dma_complete; phys_addr_t device_base; struct hyperbus_ctlr *ctlr; struct dma_chan *rx_chan; }; struct am654_hbmc_priv { struct hyperbus_ctlr ctlr; struct hyperbus_device hbdev; Loading Loading @@ -52,13 +63,103 @@ static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) return ret; } static void am654_hbmc_dma_callback(void *param) { struct am654_hbmc_device_priv *priv = param; complete(&priv->rx_dma_complete); } static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to, unsigned long from, ssize_t len) { enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; struct dma_chan *rx_chan = priv->rx_chan; struct dma_async_tx_descriptor *tx; dma_addr_t dma_dst, dma_src; dma_cookie_t cookie; int ret; if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to)) return -EINVAL; dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE); if (dma_mapping_error(rx_chan->device->dev, dma_dst)) { dev_dbg(priv->ctlr->dev, "DMA mapping failed\n"); return -EIO; } dma_src = priv->device_base + from; tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, len, flags); if (!tx) { dev_err(priv->ctlr->dev, "device_prep_dma_memcpy error\n"); ret = -EIO; goto unmap_dma; } reinit_completion(&priv->rx_dma_complete); tx->callback = am654_hbmc_dma_callback; tx->callback_param = priv; cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { dev_err(priv->ctlr->dev, "dma_submit_error %d\n", cookie); goto unmap_dma; } dma_async_issue_pending(rx_chan); if (!wait_for_completion_timeout(&priv->rx_dma_complete, msecs_to_jiffies(len + 1000))) { dmaengine_terminate_sync(rx_chan); dev_err(priv->ctlr->dev, "DMA wait_for_completion_timeout\n"); ret = -ETIMEDOUT; } unmap_dma: dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE); return ret; } static void am654_hbmc_read(struct hyperbus_device *hbdev, void *to, unsigned long from, ssize_t len) { struct am654_hbmc_device_priv *priv = hbdev->priv; if (len < SZ_1K || am654_hbmc_dma_read(priv, to, from, len)) memcpy_fromio(to, hbdev->map.virt + from, len); } static const struct hyperbus_ops am654_hbmc_ops = { .calibrate = am654_hbmc_calibrate, .copy_from = am654_hbmc_read, }; static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv *priv) { struct dma_chan *rx_chan; dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); rx_chan = dma_request_chan_by_mask(&mask); if (IS_ERR(rx_chan)) { if (PTR_ERR(rx_chan) == -EPROBE_DEFER) return -EPROBE_DEFER; dev_dbg(priv->ctlr->dev, "No DMA channel available\n"); return 0; } priv->rx_chan = rx_chan; init_completion(&priv->rx_dma_complete); return 0; } static int am654_hbmc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct am654_hbmc_device_priv *dev_priv; struct device *dev = &pdev->dev; struct am654_hbmc_priv *priv; struct resource res; Loading @@ -70,7 +171,8 @@ static int am654_hbmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); ret = of_address_to_resource(np, 0, &res); priv->hbdev.np = of_get_next_child(np, NULL); ret = of_address_to_resource(priv->hbdev.np, 0, &res); if (ret) return ret; Loading @@ -88,13 +190,6 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->mux_ctrl = control; } pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { pm_runtime_put_noidle(dev); goto disable_pm; } priv->hbdev.map.size = resource_size(&res); priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); if (IS_ERR(priv->hbdev.map.virt)) Loading @@ -103,17 +198,32 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->ctlr.dev = dev; priv->ctlr.ops = &am654_hbmc_ops; priv->hbdev.ctlr = &priv->ctlr; priv->hbdev.np = of_get_next_child(dev->of_node, NULL); dev_priv = devm_kzalloc(dev, sizeof(*dev_priv), GFP_KERNEL); if (!dev_priv) { ret = -ENOMEM; goto disable_mux; } priv->hbdev.priv = dev_priv; dev_priv->device_base = res.start; dev_priv->ctlr = &priv->ctlr; ret = am654_hbmc_request_mmap_dma(dev_priv); if (ret) goto disable_mux; ret = hyperbus_register_device(&priv->hbdev); if (ret) { dev_err(dev, "failed to register controller\n"); pm_runtime_put_sync(&pdev->dev); goto disable_pm; goto release_dma; } return 0; disable_pm: pm_runtime_disable(dev); release_dma: if (dev_priv->rx_chan) dma_release_channel(dev_priv->rx_chan); disable_mux: if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); return ret; Loading @@ -122,13 +232,15 @@ static int am654_hbmc_probe(struct platform_device *pdev) static int am654_hbmc_remove(struct platform_device *pdev) { struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv; int ret; ret = hyperbus_unregister_device(&priv->hbdev); if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); if (dev_priv->rx_chan) dma_release_channel(dev_priv->rx_chan); return ret; } Loading drivers/mtd/hyperbus/rpc-if.c 0 → 100644 +170 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Linux driver for RPC-IF HyperFlash * * Copyright (C) 2019-2020 Cogent Embedded, Inc. */ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/hyperbus.h> #include <linux/mtd/mtd.h> #include <linux/mux/consumer.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/types.h> #include <memory/renesas-rpc-if.h> struct rpcif_hyperbus { struct rpcif rpc; struct hyperbus_ctlr ctlr; struct hyperbus_device hbdev; }; static const struct rpcif_op rpcif_op_tmpl = { .cmd = { .buswidth = 8, .ddr = true, }, .ocmd = { .buswidth = 8, .ddr = true, }, .addr = { .nbytes = 1, .buswidth = 8, .ddr = true, }, .data = { .buswidth = 8, .ddr = true, }, }; static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to, unsigned long from, ssize_t len) { struct rpcif_op op = rpcif_op_tmpl; op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM; op.addr.val = from >> 1; op.dummy.buswidth = 1; op.dummy.ncycles = 15; op.data.dir = RPCIF_DATA_IN; op.data.nbytes = len; op.data.buf.in = to; rpcif_prepare(rpc, &op, NULL, NULL); } static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to, void *from, ssize_t len) { struct rpcif_op op = rpcif_op_tmpl; op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM; op.addr.val = to >> 1; op.data.dir = RPCIF_DATA_OUT; op.data.nbytes = len; op.data.buf.out = from; rpcif_prepare(rpc, &op, NULL, NULL); } static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); map_word data; rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2); rpcif_manual_xfer(&hyperbus->rpc); return data.x[0]; } static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr, u16 data) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2); rpcif_manual_xfer(&hyperbus->rpc); } static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to, unsigned long from, ssize_t len) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len); rpcif_dirmap_read(&hyperbus->rpc, from, len, to); } static const struct hyperbus_ops rpcif_hb_ops = { .read16 = rpcif_hb_read16, .write16 = rpcif_hb_write16, .copy_from = rpcif_hb_copy_from, }; static int rpcif_hb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpcif_hyperbus *hyperbus; int error; hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL); if (!hyperbus) return -ENOMEM; rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent); platform_set_drvdata(pdev, hyperbus); rpcif_enable_rpm(&hyperbus->rpc); rpcif_hw_init(&hyperbus->rpc, true); hyperbus->hbdev.map.size = hyperbus->rpc.size; hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; hyperbus->ctlr.dev = dev; hyperbus->ctlr.ops = &rpcif_hb_ops; hyperbus->hbdev.ctlr = &hyperbus->ctlr; hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); error = hyperbus_register_device(&hyperbus->hbdev); if (error) rpcif_disable_rpm(&hyperbus->rpc); return error; } static int rpcif_hb_remove(struct platform_device *pdev) { struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); int error = hyperbus_unregister_device(&hyperbus->hbdev); struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent); rpcif_disable_rpm(rpc); return error; } static struct platform_driver rpcif_platform_driver = { .probe = rpcif_hb_probe, .remove = rpcif_hb_remove, .driver = { .name = "rpc-if-hyperflash", }, }; module_platform_driver(rpcif_platform_driver); MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver"); MODULE_LICENSE("GPL v2"); include/linux/mtd/hyperbus.h +13 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,17 @@ #include <linux/mtd/map.h> /* HyperBus command bits */ #define HYPERBUS_RW 0x80 /* R/W# */ #define HYPERBUS_RW_WRITE 0 #define HYPERBUS_RW_READ 0x80 #define HYPERBUS_AS 0x40 /* Address Space */ #define HYPERBUS_AS_MEM 0 #define HYPERBUS_AS_REG 0x40 #define HYPERBUS_BT 0x20 /* Burst Type */ #define HYPERBUS_BT_WRAPPED 0 #define HYPERBUS_BT_LINEAR 0x20 enum hyperbus_memtype { HYPERFLASH, HYPERRAM, Loading @@ -20,6 +31,7 @@ enum hyperbus_memtype { * @mtd: pointer to MTD struct * @ctlr: pointer to HyperBus controller struct * @memtype: type of memory device: HyperFlash or HyperRAM * @priv: pointer to controller specific per device private data */ struct hyperbus_device { Loading @@ -28,6 +40,7 @@ struct hyperbus_device { struct mtd_info *mtd; struct hyperbus_ctlr *ctlr; enum hyperbus_memtype memtype; void *priv; }; /** Loading Loading
drivers/mtd/hyperbus/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,11 @@ config HBMC_AM654 This is the driver for HyperBus controller on TI's AM65x and other SoCs config RPCIF_HYPERBUS tristate "Renesas RPC-IF HyperBus driver" depends on RENESAS_RPCIF || COMPILE_TEST depends on MTD_CFI_BE_BYTE_SWAP help This option includes Renesas RPC-IF HyperBus support. endif # MTD_HYPERBUS
drivers/mtd/hyperbus/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -2,3 +2,4 @@ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o obj-$(CONFIG_RPCIF_HYPERBUS) += rpc-if.o
drivers/mtd/hyperbus/hbmc-am654.c +128 −16 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ // Author: Vignesh Raghavendra <vigneshr@ti.com> #include <linux/completion.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> Loading @@ -13,11 +17,18 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sched/task_stack.h> #include <linux/types.h> #define AM654_HBMC_CALIB_COUNT 25 struct am654_hbmc_device_priv { struct completion rx_dma_complete; phys_addr_t device_base; struct hyperbus_ctlr *ctlr; struct dma_chan *rx_chan; }; struct am654_hbmc_priv { struct hyperbus_ctlr ctlr; struct hyperbus_device hbdev; Loading Loading @@ -52,13 +63,103 @@ static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) return ret; } static void am654_hbmc_dma_callback(void *param) { struct am654_hbmc_device_priv *priv = param; complete(&priv->rx_dma_complete); } static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to, unsigned long from, ssize_t len) { enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; struct dma_chan *rx_chan = priv->rx_chan; struct dma_async_tx_descriptor *tx; dma_addr_t dma_dst, dma_src; dma_cookie_t cookie; int ret; if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to)) return -EINVAL; dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE); if (dma_mapping_error(rx_chan->device->dev, dma_dst)) { dev_dbg(priv->ctlr->dev, "DMA mapping failed\n"); return -EIO; } dma_src = priv->device_base + from; tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, len, flags); if (!tx) { dev_err(priv->ctlr->dev, "device_prep_dma_memcpy error\n"); ret = -EIO; goto unmap_dma; } reinit_completion(&priv->rx_dma_complete); tx->callback = am654_hbmc_dma_callback; tx->callback_param = priv; cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { dev_err(priv->ctlr->dev, "dma_submit_error %d\n", cookie); goto unmap_dma; } dma_async_issue_pending(rx_chan); if (!wait_for_completion_timeout(&priv->rx_dma_complete, msecs_to_jiffies(len + 1000))) { dmaengine_terminate_sync(rx_chan); dev_err(priv->ctlr->dev, "DMA wait_for_completion_timeout\n"); ret = -ETIMEDOUT; } unmap_dma: dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE); return ret; } static void am654_hbmc_read(struct hyperbus_device *hbdev, void *to, unsigned long from, ssize_t len) { struct am654_hbmc_device_priv *priv = hbdev->priv; if (len < SZ_1K || am654_hbmc_dma_read(priv, to, from, len)) memcpy_fromio(to, hbdev->map.virt + from, len); } static const struct hyperbus_ops am654_hbmc_ops = { .calibrate = am654_hbmc_calibrate, .copy_from = am654_hbmc_read, }; static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv *priv) { struct dma_chan *rx_chan; dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); rx_chan = dma_request_chan_by_mask(&mask); if (IS_ERR(rx_chan)) { if (PTR_ERR(rx_chan) == -EPROBE_DEFER) return -EPROBE_DEFER; dev_dbg(priv->ctlr->dev, "No DMA channel available\n"); return 0; } priv->rx_chan = rx_chan; init_completion(&priv->rx_dma_complete); return 0; } static int am654_hbmc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct am654_hbmc_device_priv *dev_priv; struct device *dev = &pdev->dev; struct am654_hbmc_priv *priv; struct resource res; Loading @@ -70,7 +171,8 @@ static int am654_hbmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); ret = of_address_to_resource(np, 0, &res); priv->hbdev.np = of_get_next_child(np, NULL); ret = of_address_to_resource(priv->hbdev.np, 0, &res); if (ret) return ret; Loading @@ -88,13 +190,6 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->mux_ctrl = control; } pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { pm_runtime_put_noidle(dev); goto disable_pm; } priv->hbdev.map.size = resource_size(&res); priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); if (IS_ERR(priv->hbdev.map.virt)) Loading @@ -103,17 +198,32 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->ctlr.dev = dev; priv->ctlr.ops = &am654_hbmc_ops; priv->hbdev.ctlr = &priv->ctlr; priv->hbdev.np = of_get_next_child(dev->of_node, NULL); dev_priv = devm_kzalloc(dev, sizeof(*dev_priv), GFP_KERNEL); if (!dev_priv) { ret = -ENOMEM; goto disable_mux; } priv->hbdev.priv = dev_priv; dev_priv->device_base = res.start; dev_priv->ctlr = &priv->ctlr; ret = am654_hbmc_request_mmap_dma(dev_priv); if (ret) goto disable_mux; ret = hyperbus_register_device(&priv->hbdev); if (ret) { dev_err(dev, "failed to register controller\n"); pm_runtime_put_sync(&pdev->dev); goto disable_pm; goto release_dma; } return 0; disable_pm: pm_runtime_disable(dev); release_dma: if (dev_priv->rx_chan) dma_release_channel(dev_priv->rx_chan); disable_mux: if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); return ret; Loading @@ -122,13 +232,15 @@ static int am654_hbmc_probe(struct platform_device *pdev) static int am654_hbmc_remove(struct platform_device *pdev) { struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv; int ret; ret = hyperbus_unregister_device(&priv->hbdev); if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); if (dev_priv->rx_chan) dma_release_channel(dev_priv->rx_chan); return ret; } Loading
drivers/mtd/hyperbus/rpc-if.c 0 → 100644 +170 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Linux driver for RPC-IF HyperFlash * * Copyright (C) 2019-2020 Cogent Embedded, Inc. */ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/hyperbus.h> #include <linux/mtd/mtd.h> #include <linux/mux/consumer.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/types.h> #include <memory/renesas-rpc-if.h> struct rpcif_hyperbus { struct rpcif rpc; struct hyperbus_ctlr ctlr; struct hyperbus_device hbdev; }; static const struct rpcif_op rpcif_op_tmpl = { .cmd = { .buswidth = 8, .ddr = true, }, .ocmd = { .buswidth = 8, .ddr = true, }, .addr = { .nbytes = 1, .buswidth = 8, .ddr = true, }, .data = { .buswidth = 8, .ddr = true, }, }; static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to, unsigned long from, ssize_t len) { struct rpcif_op op = rpcif_op_tmpl; op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM; op.addr.val = from >> 1; op.dummy.buswidth = 1; op.dummy.ncycles = 15; op.data.dir = RPCIF_DATA_IN; op.data.nbytes = len; op.data.buf.in = to; rpcif_prepare(rpc, &op, NULL, NULL); } static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to, void *from, ssize_t len) { struct rpcif_op op = rpcif_op_tmpl; op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM; op.addr.val = to >> 1; op.data.dir = RPCIF_DATA_OUT; op.data.nbytes = len; op.data.buf.out = from; rpcif_prepare(rpc, &op, NULL, NULL); } static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); map_word data; rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2); rpcif_manual_xfer(&hyperbus->rpc); return data.x[0]; } static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr, u16 data) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2); rpcif_manual_xfer(&hyperbus->rpc); } static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to, unsigned long from, ssize_t len) { struct rpcif_hyperbus *hyperbus = container_of(hbdev, struct rpcif_hyperbus, hbdev); rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len); rpcif_dirmap_read(&hyperbus->rpc, from, len, to); } static const struct hyperbus_ops rpcif_hb_ops = { .read16 = rpcif_hb_read16, .write16 = rpcif_hb_write16, .copy_from = rpcif_hb_copy_from, }; static int rpcif_hb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpcif_hyperbus *hyperbus; int error; hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL); if (!hyperbus) return -ENOMEM; rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent); platform_set_drvdata(pdev, hyperbus); rpcif_enable_rpm(&hyperbus->rpc); rpcif_hw_init(&hyperbus->rpc, true); hyperbus->hbdev.map.size = hyperbus->rpc.size; hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; hyperbus->ctlr.dev = dev; hyperbus->ctlr.ops = &rpcif_hb_ops; hyperbus->hbdev.ctlr = &hyperbus->ctlr; hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); error = hyperbus_register_device(&hyperbus->hbdev); if (error) rpcif_disable_rpm(&hyperbus->rpc); return error; } static int rpcif_hb_remove(struct platform_device *pdev) { struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); int error = hyperbus_unregister_device(&hyperbus->hbdev); struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent); rpcif_disable_rpm(rpc); return error; } static struct platform_driver rpcif_platform_driver = { .probe = rpcif_hb_probe, .remove = rpcif_hb_remove, .driver = { .name = "rpc-if-hyperflash", }, }; module_platform_driver(rpcif_platform_driver); MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver"); MODULE_LICENSE("GPL v2");
include/linux/mtd/hyperbus.h +13 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,17 @@ #include <linux/mtd/map.h> /* HyperBus command bits */ #define HYPERBUS_RW 0x80 /* R/W# */ #define HYPERBUS_RW_WRITE 0 #define HYPERBUS_RW_READ 0x80 #define HYPERBUS_AS 0x40 /* Address Space */ #define HYPERBUS_AS_MEM 0 #define HYPERBUS_AS_REG 0x40 #define HYPERBUS_BT 0x20 /* Burst Type */ #define HYPERBUS_BT_WRAPPED 0 #define HYPERBUS_BT_LINEAR 0x20 enum hyperbus_memtype { HYPERFLASH, HYPERRAM, Loading @@ -20,6 +31,7 @@ enum hyperbus_memtype { * @mtd: pointer to MTD struct * @ctlr: pointer to HyperBus controller struct * @memtype: type of memory device: HyperFlash or HyperRAM * @priv: pointer to controller specific per device private data */ struct hyperbus_device { Loading @@ -28,6 +40,7 @@ struct hyperbus_device { struct mtd_info *mtd; struct hyperbus_ctlr *ctlr; enum hyperbus_memtype memtype; void *priv; }; /** Loading