| /* |
| * Xilinx SPI OF device driver |
| * |
| * Copyright (c) 2009 Intel Corporation |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| /* Supports: |
| * Xilinx SPI devices as OF devices |
| * |
| * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| |
| #include <linux/of_platform.h> |
| #include <linux/of_device.h> |
| #include <linux/of_spi.h> |
| |
| #include <linux/spi/xilinx_spi.h> |
| #include "xilinx_spi.h" |
| |
| |
| static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, |
| const struct of_device_id *match) |
| { |
| struct spi_master *master; |
| struct xspi_platform_data *pdata; |
| struct resource r_mem; |
| struct resource r_irq; |
| int rc = 0; |
| const u32 *prop; |
| int len; |
| |
| rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); |
| if (rc) { |
| dev_warn(&ofdev->dev, "invalid address\n"); |
| return rc; |
| } |
| |
| rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq); |
| if (rc == NO_IRQ) { |
| dev_warn(&ofdev->dev, "no IRQ found\n"); |
| return -ENODEV; |
| } |
| |
| ofdev->dev.platform_data = |
| kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL); |
| pdata = ofdev->dev.platform_data; |
| if (!pdata) |
| return -ENOMEM; |
| |
| /* number of slave select bits is required */ |
| prop = of_get_property(ofdev->dev.of_node, "xlnx,num-ss-bits", &len); |
| if (!prop || len < sizeof(*prop)) { |
| dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); |
| return -EINVAL; |
| } |
| pdata->num_chipselect = *prop; |
| pdata->bits_per_word = 8; |
| master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1); |
| if (!master) |
| return -ENODEV; |
| |
| dev_set_drvdata(&ofdev->dev, master); |
| |
| return 0; |
| } |
| |
| static int __devexit xilinx_spi_remove(struct of_device *ofdev) |
| { |
| xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); |
| dev_set_drvdata(&ofdev->dev, 0); |
| kfree(ofdev->dev.platform_data); |
| ofdev->dev.platform_data = NULL; |
| return 0; |
| } |
| |
| static int __exit xilinx_spi_of_remove(struct of_device *op) |
| { |
| return xilinx_spi_remove(op); |
| } |
| |
| static const struct of_device_id xilinx_spi_of_match[] = { |
| { .compatible = "xlnx,xps-spi-2.00.a", }, |
| { .compatible = "xlnx,xps-spi-2.00.b", }, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); |
| |
| static struct of_platform_driver xilinx_spi_of_driver = { |
| .probe = xilinx_spi_of_probe, |
| .remove = __exit_p(xilinx_spi_of_remove), |
| .driver = { |
| .name = "xilinx-xps-spi", |
| .owner = THIS_MODULE, |
| .of_match_table = xilinx_spi_of_match, |
| }, |
| }; |
| |
| static int __init xilinx_spi_of_init(void) |
| { |
| return of_register_platform_driver(&xilinx_spi_of_driver); |
| } |
| module_init(xilinx_spi_of_init); |
| |
| static void __exit xilinx_spi_of_exit(void) |
| { |
| of_unregister_platform_driver(&xilinx_spi_of_driver); |
| } |
| module_exit(xilinx_spi_of_exit); |
| |
| MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); |
| MODULE_DESCRIPTION("Xilinx SPI platform driver"); |
| MODULE_LICENSE("GPL v2"); |