1/* 2 * dw_spi_mmio.c - 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/interrupt.h> 13#include <linux/platform_device.h> 14#include <linux/slab.h> 15#include <linux/spi/dw_spi.h> 16#include <linux/spi/spi.h> 17 18#define DRIVER_NAME "dw_spi_mmio" 19 20struct dw_spi_mmio { 21 struct dw_spi dws; 22 struct clk *clk; 23}; 24 25static int __devinit dw_spi_mmio_probe(struct platform_device *pdev) 26{ 27 struct dw_spi_mmio *dwsmmio; 28 struct dw_spi *dws; 29 struct resource *mem, *ioarea; 30 int ret; 31 32 dwsmmio = kzalloc(sizeof(struct dw_spi_mmio), GFP_KERNEL); 33 if (!dwsmmio) { 34 ret = -ENOMEM; 35 goto err_end; 36 } 37 38 dws = &dwsmmio->dws; 39 40 /* Get basic io resource and map it */ 41 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 42 if (!mem) { 43 dev_err(&pdev->dev, "no mem resource?\n"); 44 ret = -EINVAL; 45 goto err_kfree; 46 } 47 48 ioarea = request_mem_region(mem->start, resource_size(mem), 49 pdev->name); 50 if (!ioarea) { 51 dev_err(&pdev->dev, "SPI region already claimed\n"); 52 ret = -EBUSY; 53 goto err_kfree; 54 } 55 56 dws->regs = ioremap_nocache(mem->start, resource_size(mem)); 57 if (!dws->regs) { 58 dev_err(&pdev->dev, "SPI region already mapped\n"); 59 ret = -ENOMEM; 60 goto err_release_reg; 61 } 62 63 dws->irq = platform_get_irq(pdev, 0); 64 if (dws->irq < 0) { 65 dev_err(&pdev->dev, "no irq resource?\n"); 66 ret = dws->irq; /* -ENXIO */ 67 goto err_unmap; 68 } 69 70 dwsmmio->clk = clk_get(&pdev->dev, NULL); 71 if (!dwsmmio->clk) { 72 ret = -ENODEV; 73 goto err_irq; 74 } 75 clk_enable(dwsmmio->clk); 76 77 dws->parent_dev = &pdev->dev; 78 dws->bus_num = 0; 79 dws->num_cs = 4; 80 dws->max_freq = clk_get_rate(dwsmmio->clk); 81 82 ret = dw_spi_add_host(dws); 83 if (ret) 84 goto err_clk; 85 86 platform_set_drvdata(pdev, dwsmmio); 87 return 0; 88 89err_clk: 90 clk_disable(dwsmmio->clk); 91 clk_put(dwsmmio->clk); 92 dwsmmio->clk = NULL; 93err_irq: 94 free_irq(dws->irq, dws); 95err_unmap: 96 iounmap(dws->regs); 97err_release_reg: 98 release_mem_region(mem->start, resource_size(mem)); 99err_kfree: 100 kfree(dwsmmio); 101err_end: 102 return ret; 103} 104 105static int __devexit dw_spi_mmio_remove(struct platform_device *pdev) 106{ 107 struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); 108 struct resource *mem; 109 110 platform_set_drvdata(pdev, NULL); 111 112 clk_disable(dwsmmio->clk); 113 clk_put(dwsmmio->clk); 114 dwsmmio->clk = NULL; 115 116 free_irq(dwsmmio->dws.irq, &dwsmmio->dws); 117 dw_spi_remove_host(&dwsmmio->dws); 118 iounmap(dwsmmio->dws.regs); 119 kfree(dwsmmio); 120 121 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 122 release_mem_region(mem->start, resource_size(mem)); 123 return 0; 124} 125 126static struct platform_driver dw_spi_mmio_driver = { 127 .remove = __devexit_p(dw_spi_mmio_remove), 128 .driver = { 129 .name = DRIVER_NAME, 130 .owner = THIS_MODULE, 131 }, 132}; 133 134static int __init dw_spi_mmio_init(void) 135{ 136 return platform_driver_probe(&dw_spi_mmio_driver, dw_spi_mmio_probe); 137} 138module_init(dw_spi_mmio_init); 139 140static void __exit dw_spi_mmio_exit(void) 141{ 142 platform_driver_unregister(&dw_spi_mmio_driver); 143} 144module_exit(dw_spi_mmio_exit); 145 146MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>"); 147MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core"); 148MODULE_LICENSE("GPL v2"); 149