18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/misc/xillybus_of.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 Xillybus Ltd, http://xillybus.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Driver for the Xillybus FPGA/host framework using Open Firmware. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include "xillybus.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xillybus driver for Open Firmware"); 198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); 208c2ecf20Sopenharmony_ciMODULE_VERSION("1.06"); 218c2ecf20Sopenharmony_ciMODULE_ALIAS("xillybus_of"); 228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char xillyname[] = "xillybus_of"; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Match table for of_platform binding */ 278c2ecf20Sopenharmony_cistatic const struct of_device_id xillybus_of_match[] = { 288c2ecf20Sopenharmony_ci { .compatible = "xillybus,xillybus-1.00.a", }, 298c2ecf20Sopenharmony_ci { .compatible = "xlnx,xillybus-1.00.a", }, /* Deprecated */ 308c2ecf20Sopenharmony_ci {} 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xillybus_of_match); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void xilly_dma_sync_single_for_cpu_of(struct xilly_endpoint *ep, 368c2ecf20Sopenharmony_ci dma_addr_t dma_handle, 378c2ecf20Sopenharmony_ci size_t size, 388c2ecf20Sopenharmony_ci int direction) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(ep->dev, dma_handle, size, direction); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void xilly_dma_sync_single_for_device_of(struct xilly_endpoint *ep, 448c2ecf20Sopenharmony_ci dma_addr_t dma_handle, 458c2ecf20Sopenharmony_ci size_t size, 468c2ecf20Sopenharmony_ci int direction) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci dma_sync_single_for_device(ep->dev, dma_handle, size, direction); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void xilly_dma_sync_single_nop(struct xilly_endpoint *ep, 528c2ecf20Sopenharmony_ci dma_addr_t dma_handle, 538c2ecf20Sopenharmony_ci size_t size, 548c2ecf20Sopenharmony_ci int direction) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void xilly_of_unmap(void *ptr) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct xilly_mapping *data = ptr; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci dma_unmap_single(data->device, data->dma_addr, 638c2ecf20Sopenharmony_ci data->size, data->direction); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci kfree(ptr); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int xilly_map_single_of(struct xilly_endpoint *ep, 698c2ecf20Sopenharmony_ci void *ptr, 708c2ecf20Sopenharmony_ci size_t size, 718c2ecf20Sopenharmony_ci int direction, 728c2ecf20Sopenharmony_ci dma_addr_t *ret_dma_handle 738c2ecf20Sopenharmony_ci ) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci dma_addr_t addr; 768c2ecf20Sopenharmony_ci struct xilly_mapping *this; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci this = kzalloc(sizeof(*this), GFP_KERNEL); 798c2ecf20Sopenharmony_ci if (!this) 808c2ecf20Sopenharmony_ci return -ENOMEM; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci addr = dma_map_single(ep->dev, ptr, size, direction); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (dma_mapping_error(ep->dev, addr)) { 858c2ecf20Sopenharmony_ci kfree(this); 868c2ecf20Sopenharmony_ci return -ENODEV; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci this->device = ep->dev; 908c2ecf20Sopenharmony_ci this->dma_addr = addr; 918c2ecf20Sopenharmony_ci this->size = size; 928c2ecf20Sopenharmony_ci this->direction = direction; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci *ret_dma_handle = addr; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return devm_add_action_or_reset(ep->dev, xilly_of_unmap, this); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct xilly_endpoint_hardware of_hw = { 1008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1018c2ecf20Sopenharmony_ci .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of, 1028c2ecf20Sopenharmony_ci .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of, 1038c2ecf20Sopenharmony_ci .map_single = xilly_map_single_of, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct xilly_endpoint_hardware of_hw_coherent = { 1078c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1088c2ecf20Sopenharmony_ci .hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop, 1098c2ecf20Sopenharmony_ci .hw_sync_sgl_for_device = xilly_dma_sync_single_nop, 1108c2ecf20Sopenharmony_ci .map_single = xilly_map_single_of, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int xilly_drv_probe(struct platform_device *op) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct device *dev = &op->dev; 1168c2ecf20Sopenharmony_ci struct xilly_endpoint *endpoint; 1178c2ecf20Sopenharmony_ci int rc; 1188c2ecf20Sopenharmony_ci int irq; 1198c2ecf20Sopenharmony_ci struct xilly_endpoint_hardware *ephw = &of_hw; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (of_property_read_bool(dev->of_node, "dma-coherent")) 1228c2ecf20Sopenharmony_ci ephw = &of_hw_coherent; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci endpoint = xillybus_init_endpoint(NULL, dev, ephw); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!endpoint) 1278c2ecf20Sopenharmony_ci return -ENOMEM; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dev_set_drvdata(dev, endpoint); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci endpoint->registers = devm_platform_ioremap_resource(op, 0); 1328c2ecf20Sopenharmony_ci if (IS_ERR(endpoint->registers)) 1338c2ecf20Sopenharmony_ci return PTR_ERR(endpoint->registers); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci irq = platform_get_irq(op, 0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (rc) { 1408c2ecf20Sopenharmony_ci dev_err(endpoint->dev, 1418c2ecf20Sopenharmony_ci "Failed to register IRQ handler. Aborting.\n"); 1428c2ecf20Sopenharmony_ci return -ENODEV; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return xillybus_endpoint_discovery(endpoint); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int xilly_drv_remove(struct platform_device *op) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct device *dev = &op->dev; 1518c2ecf20Sopenharmony_ci struct xilly_endpoint *endpoint = dev_get_drvdata(dev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci xillybus_endpoint_remove(endpoint); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct platform_driver xillybus_platform_driver = { 1598c2ecf20Sopenharmony_ci .probe = xilly_drv_probe, 1608c2ecf20Sopenharmony_ci .remove = xilly_drv_remove, 1618c2ecf20Sopenharmony_ci .driver = { 1628c2ecf20Sopenharmony_ci .name = xillyname, 1638c2ecf20Sopenharmony_ci .of_match_table = xillybus_of_match, 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cimodule_platform_driver(xillybus_platform_driver); 168