1 /* 2 * Memory-mapped interface driver for DW SPI Core 3 * 4 * Copyright (c) 2010, Octasic semiconductor. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 */ 10 11 #include <linux/clk.h> 12 #include <linux/err.h> 13 #include <linux/interrupt.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 #include <linux/spi/spi.h> 17 #include <linux/scatterlist.h> 18 #include <linux/module.h> 19 20 #include "spi-dw.h" 21 22 #define DRIVER_NAME "dw_spi_mmio" 23 24 struct dw_spi_mmio { 25 struct dw_spi dws; 26 struct clk *clk; 27 }; 28 29 static int dw_spi_mmio_probe(struct platform_device *pdev) 30 { 31 struct dw_spi_mmio *dwsmmio; 32 struct dw_spi *dws; 33 struct resource *mem, *ioarea; 34 int ret; 35 36 dwsmmio = kzalloc(sizeof(struct dw_spi_mmio), GFP_KERNEL); 37 if (!dwsmmio) { 38 ret = -ENOMEM; 39 goto err_end; 40 } 41 42 dws = &dwsmmio->dws; 43 44 /* Get basic io resource and map it */ 45 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 46 if (!mem) { 47 dev_err(&pdev->dev, "no mem resource?\n"); 48 ret = -EINVAL; 49 goto err_kfree; 50 } 51 52 ioarea = request_mem_region(mem->start, resource_size(mem), 53 pdev->name); 54 if (!ioarea) { 55 dev_err(&pdev->dev, "SPI region already claimed\n"); 56 ret = -EBUSY; 57 goto err_kfree; 58 } 59 60 dws->regs = ioremap_nocache(mem->start, resource_size(mem)); 61 if (!dws->regs) { 62 dev_err(&pdev->dev, "SPI region already mapped\n"); 63 ret = -ENOMEM; 64 goto err_release_reg; 65 } 66 67 dws->irq = platform_get_irq(pdev, 0); 68 if (dws->irq < 0) { 69 dev_err(&pdev->dev, "no irq resource?\n"); 70 ret = dws->irq; /* -ENXIO */ 71 goto err_unmap; 72 } 73 74 dwsmmio->clk = clk_get(&pdev->dev, NULL); 75 if (IS_ERR(dwsmmio->clk)) { 76 ret = PTR_ERR(dwsmmio->clk); 77 goto err_irq; 78 } 79 clk_enable(dwsmmio->clk); 80 81 dws->parent_dev = &pdev->dev; 82 dws->bus_num = 0; 83 dws->num_cs = 4; 84 dws->max_freq = clk_get_rate(dwsmmio->clk); 85 86 ret = dw_spi_add_host(dws); 87 if (ret) 88 goto err_clk; 89 90 platform_set_drvdata(pdev, dwsmmio); 91 return 0; 92 93 err_clk: 94 clk_disable(dwsmmio->clk); 95 clk_put(dwsmmio->clk); 96 dwsmmio->clk = NULL; 97 err_irq: 98 free_irq(dws->irq, dws); 99 err_unmap: 100 iounmap(dws->regs); 101 err_release_reg: 102 release_mem_region(mem->start, resource_size(mem)); 103 err_kfree: 104 kfree(dwsmmio); 105 err_end: 106 return ret; 107 } 108 109 static int dw_spi_mmio_remove(struct platform_device *pdev) 110 { 111 struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); 112 struct resource *mem; 113 114 platform_set_drvdata(pdev, NULL); 115 116 clk_disable(dwsmmio->clk); 117 clk_put(dwsmmio->clk); 118 dwsmmio->clk = NULL; 119 120 free_irq(dwsmmio->dws.irq, &dwsmmio->dws); 121 dw_spi_remove_host(&dwsmmio->dws); 122 iounmap(dwsmmio->dws.regs); 123 kfree(dwsmmio); 124 125 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 126 release_mem_region(mem->start, resource_size(mem)); 127 return 0; 128 } 129 130 static struct platform_driver dw_spi_mmio_driver = { 131 .probe = dw_spi_mmio_probe, 132 .remove = dw_spi_mmio_remove, 133 .driver = { 134 .name = DRIVER_NAME, 135 .owner = THIS_MODULE, 136 }, 137 }; 138 module_platform_driver(dw_spi_mmio_driver); 139 140 MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>"); 141 MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core"); 142 MODULE_LICENSE("GPL v2"); 143