spi-dw-mmio.c 3.2 KB
Newer Older
1
/*
G
Grant Likely 已提交
2
 * Memory-mapped interface driver for DW SPI Core
3 4 5 6 7 8 9 10 11
 *
 * Copyright (c) 2010, Octasic semiconductor.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 */

#include <linux/clk.h>
12
#include <linux/err.h>
13 14
#include <linux/interrupt.h>
#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

G
Grant Likely 已提交
20
#include "spi-dw.h"
21 22 23 24

#define DRIVER_NAME "dw_spi_mmio"

struct dw_spi_mmio {
25 26
	struct dw_spi  dws;
	struct clk     *clk;
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
};

static int __devinit dw_spi_mmio_probe(struct platform_device *pdev)
{
	struct dw_spi_mmio *dwsmmio;
	struct dw_spi *dws;
	struct resource *mem, *ioarea;
	int ret;

	dwsmmio = kzalloc(sizeof(struct dw_spi_mmio), GFP_KERNEL);
	if (!dwsmmio) {
		ret = -ENOMEM;
		goto err_end;
	}

	dws = &dwsmmio->dws;

	/* Get basic io resource and map it */
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem) {
		dev_err(&pdev->dev, "no mem resource?\n");
		ret = -EINVAL;
		goto err_kfree;
	}

	ioarea = request_mem_region(mem->start, resource_size(mem),
			pdev->name);
	if (!ioarea) {
		dev_err(&pdev->dev, "SPI region already claimed\n");
		ret = -EBUSY;
		goto err_kfree;
	}

	dws->regs = ioremap_nocache(mem->start, resource_size(mem));
	if (!dws->regs) {
		dev_err(&pdev->dev, "SPI region already mapped\n");
		ret = -ENOMEM;
		goto err_release_reg;
	}

	dws->irq = platform_get_irq(pdev, 0);
	if (dws->irq < 0) {
		dev_err(&pdev->dev, "no irq resource?\n");
		ret = dws->irq; /* -ENXIO */
		goto err_unmap;
	}

	dwsmmio->clk = clk_get(&pdev->dev, NULL);
75 76
	if (IS_ERR(dwsmmio->clk)) {
		ret = PTR_ERR(dwsmmio->clk);
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		goto err_irq;
	}
	clk_enable(dwsmmio->clk);

	dws->parent_dev = &pdev->dev;
	dws->bus_num = 0;
	dws->num_cs = 4;
	dws->max_freq = clk_get_rate(dwsmmio->clk);

	ret = dw_spi_add_host(dws);
	if (ret)
		goto err_clk;

	platform_set_drvdata(pdev, dwsmmio);
	return 0;

err_clk:
	clk_disable(dwsmmio->clk);
	clk_put(dwsmmio->clk);
	dwsmmio->clk = NULL;
err_irq:
	free_irq(dws->irq, dws);
err_unmap:
	iounmap(dws->regs);
err_release_reg:
	release_mem_region(mem->start, resource_size(mem));
err_kfree:
	kfree(dwsmmio);
err_end:
	return ret;
}

static int __devexit dw_spi_mmio_remove(struct platform_device *pdev)
{
	struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
	struct resource *mem;

	platform_set_drvdata(pdev, NULL);

	clk_disable(dwsmmio->clk);
	clk_put(dwsmmio->clk);
	dwsmmio->clk = NULL;

	free_irq(dwsmmio->dws.irq, &dwsmmio->dws);
	dw_spi_remove_host(&dwsmmio->dws);
	iounmap(dwsmmio->dws.regs);
	kfree(dwsmmio);

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	release_mem_region(mem->start, resource_size(mem));
	return 0;
}

static struct platform_driver dw_spi_mmio_driver = {
131
	.probe		= dw_spi_mmio_probe,
132 133 134 135 136 137
	.remove		= __devexit_p(dw_spi_mmio_remove),
	.driver		= {
		.name	= DRIVER_NAME,
		.owner	= THIS_MODULE,
	},
};
138
module_platform_driver(dw_spi_mmio_driver);
139 140 141 142

MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>");
MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
MODULE_LICENSE("GPL v2");