c4fe57f762
The commitdd11444327
("spi: dw-spi: Convert 16bit accesses to 32bit accesses") changed all 16bit accesses in the DW_apb_ssi driver to 32bit. This, unfortunately, breaks data register access on picoXcell, where the DW IP needs data register accesses to be word accesses (all other accesses appear to be OK). This change introduces a new master variable to allow interface drivers to specify that 16bit data transfer I/O is required. This change also introduces the ability to set this variable via device tree bindings in the MMIO interface driver. Both the core and the MMIO interface driver default to the current 32bit behaviour. Before this change, on a picoXcell pc3x3: spi_master spi32766: interrupt_transfer: fifo overrun/underrun m25p80 spi32766.0: error -5 reading 9f m25p80: probe of spi32766.0 failed with error -5 After this change: m25p80 spi32766.0: m25p40 (512 Kbytes) Fixes:dd11444327
("spi: dw-spi: Convert 16bit accesses to 32bit accesses") Signed-off-by: Michael van der Westhuizen <michael@smart-africa.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
148 lines
3.2 KiB
C
148 lines
3.2 KiB
C
/*
|
|
* Memory-mapped interface driver for DW SPI Core
|
|
*
|
|
* 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>
|
|
#include <linux/err.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_platform.h>
|
|
|
|
#include "spi-dw.h"
|
|
|
|
#define DRIVER_NAME "dw_spi_mmio"
|
|
|
|
struct dw_spi_mmio {
|
|
struct dw_spi dws;
|
|
struct clk *clk;
|
|
};
|
|
|
|
static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|
{
|
|
struct dw_spi_mmio *dwsmmio;
|
|
struct dw_spi *dws;
|
|
struct resource *mem;
|
|
int ret;
|
|
int num_cs;
|
|
|
|
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
|
GFP_KERNEL);
|
|
if (!dwsmmio)
|
|
return -ENOMEM;
|
|
|
|
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");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dws->regs = devm_ioremap_resource(&pdev->dev, mem);
|
|
if (IS_ERR(dws->regs)) {
|
|
dev_err(&pdev->dev, "SPI region map failed\n");
|
|
return PTR_ERR(dws->regs);
|
|
}
|
|
|
|
dws->irq = platform_get_irq(pdev, 0);
|
|
if (dws->irq < 0) {
|
|
dev_err(&pdev->dev, "no irq resource?\n");
|
|
return dws->irq; /* -ENXIO */
|
|
}
|
|
|
|
dwsmmio->clk = devm_clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(dwsmmio->clk))
|
|
return PTR_ERR(dwsmmio->clk);
|
|
ret = clk_prepare_enable(dwsmmio->clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dws->bus_num = pdev->id;
|
|
|
|
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
|
|
|
of_property_read_u32(pdev->dev.of_node, "reg-io-width",
|
|
&dws->reg_io_width);
|
|
|
|
num_cs = 4;
|
|
|
|
if (pdev->dev.of_node)
|
|
of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
|
|
|
|
dws->num_cs = num_cs;
|
|
|
|
if (pdev->dev.of_node) {
|
|
int i;
|
|
|
|
for (i = 0; i < dws->num_cs; i++) {
|
|
int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
|
|
"cs-gpios", i);
|
|
|
|
if (cs_gpio == -EPROBE_DEFER) {
|
|
ret = cs_gpio;
|
|
goto out;
|
|
}
|
|
|
|
if (gpio_is_valid(cs_gpio)) {
|
|
ret = devm_gpio_request(&pdev->dev, cs_gpio,
|
|
dev_name(&pdev->dev));
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = dw_spi_add_host(&pdev->dev, dws);
|
|
if (ret)
|
|
goto out;
|
|
|
|
platform_set_drvdata(pdev, dwsmmio);
|
|
return 0;
|
|
|
|
out:
|
|
clk_disable_unprepare(dwsmmio->clk);
|
|
return ret;
|
|
}
|
|
|
|
static int dw_spi_mmio_remove(struct platform_device *pdev)
|
|
{
|
|
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
|
|
|
|
clk_disable_unprepare(dwsmmio->clk);
|
|
dw_spi_remove_host(&dwsmmio->dws);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id dw_spi_mmio_of_match[] = {
|
|
{ .compatible = "snps,dw-apb-ssi", },
|
|
{ /* end of table */}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
|
|
|
static struct platform_driver dw_spi_mmio_driver = {
|
|
.probe = dw_spi_mmio_probe,
|
|
.remove = dw_spi_mmio_remove,
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.of_match_table = dw_spi_mmio_of_match,
|
|
},
|
|
};
|
|
module_platform_driver(dw_spi_mmio_driver);
|
|
|
|
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");
|