Skip to content

Instantly share code, notes, and snippets.

@snegovick
Last active August 26, 2017 20:34
Show Gist options
  • Save snegovick/a81575130bd5f789027e963a7050798a to your computer and use it in GitHub Desktop.
Save snegovick/a81575130bd5f789027e963a7050798a to your computer and use it in GitHub Desktop.
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#define LPC2468_SPI_MAX_CLOCK_HZ (9000000)
#define SPCR_OFFT 0x00
#define SPSR_OFFT 0x04
#define SPDR_OFFT 0x08
#define CCR_OFFT 0x0c
#define SPCR_CPHA (1<<3)
#define SPCR_CPOL (1<<4)
#define SPCR_MSTR (1<<5)
#define SPCR_LSBF (1<<6)
#define SPSR_ABRT (1<<3)
#define SPSR_MODF (1<<4)
#define SPSR_POVR (1<<5)
#define SPSR_WCOL (1<<6)
#define SPSR_SPIF (1<<7)
#define SPSR_ERRORS (SPSR_ABRT | SPSR_MODF | SPSR_POVR | SPSR_WCOL)
struct lpc2468_spi {
void __iomem *register_base;
struct clk *clk;
};
static int lpc2468_spi_wait_ready(struct lpc2468_spi *p)
{
unsigned int loops = 0;
u16 spsr = 0;
do {
if (loops++)
__delay(500);
spsr = readw(p->register_base + SPSR_OFFT);
if (spsr & (~SPSR_ERRORS)) {
return -EIO;
}
} while (!(spsr & SPSR_SPIF));
return 0;
}
static int lpc2468_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct lpc2468_spi *p = spi_master_get_devdata(master);
int mode = spi->mode;
unsigned int clkdiv = DIV_ROUND_UP(clk_get_rate(p->clk), xfer->speed_hz);
u16 cpha = ((mode & SPI_CPHA) ? SPCR_CPHA : 0);
u16 cpol = ((mode & SPI_CPOL) ? SPCR_CPOL : 0);
u16 lsbf = ((mode & SPI_LSB_FIRST) ? SPCR_LSBF : 0);
u16 spcr = cpha | cpol | lsbf | SPCR_MSTR;
u16 ccr = clkdiv;
const u8 *tx_buf;
u8 *rx_buf;
int len;
u16 tx;
u16 rx;
int ret;
int status = 0;
writew(ccr, p->register_base + CCR_OFFT);
writew(spcr, p->register_base + SPCR_OFFT);
tx_buf = xfer->tx_buf;
rx_buf = xfer->rx_buf;
len = xfer->len;
for (; len>0; len--)
{
tx = (*tx_buf++)&0xff;
writew(tx, p->register_base + SPDR_OFFT);
ret = lpc2468_spi_wait_ready(p);
if (ret < 0) {
status = -EIO;
goto err;
}
rx = readw(p->register_base + SPDR_OFFT);
if (rx_buf) {
*rx_buf++ = rx;
}
}
ret = lpc2468_spi_wait_ready(p);
return 0;
err:
return status;
}
static int lpc2468_spi_probe(struct platform_device *pdev)
{
struct resource *mem;
void __iomem *reg_base;
struct spi_master *master;
struct lpc2468_spi *p;
int ret = 0;
int err = -ENOENT;
int num_cs = 4;
master = spi_alloc_master(&pdev->dev, sizeof(struct lpc2468_spi));
if (!master)
return -ENOMEM;
p = spi_master_get_devdata(master);
platform_set_drvdata(pdev, master);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(reg_base)) {
err = PTR_ERR(reg_base);
goto fail;
}
p->register_base = reg_base;
p->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(p->clk))
goto fail;
ret = clk_prepare_enable(p->clk);
if (ret)
goto fail;
device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
master->num_chipselect = num_cs;
if (pdev->dev.of_node) {
int i;
for (i = 0; i < master->num_chipselect; i++) {
int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
"cs-gpios", i);
if (cs_gpio == -EPROBE_DEFER) {
ret = cs_gpio;
goto fail_w_clk;
}
if (gpio_is_valid(cs_gpio)) {
ret = devm_gpio_request(&pdev->dev, cs_gpio,
dev_name(&pdev->dev));
if (ret)
goto fail_w_clk;
}
}
}
master->mode_bits = SPI_CPHA |
SPI_CPOL |
SPI_CS_HIGH |
SPI_LSB_FIRST |
SPI_3WIRE;
master->transfer_one = lpc2468_spi_transfer_one;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->max_speed_hz = LPC2468_SPI_MAX_CLOCK_HZ;
master->dev.of_node = pdev->dev.of_node;
err = devm_spi_register_master(&pdev->dev, master);
if (err) {
dev_err(&pdev->dev, "register master failed: %d\n", err);
goto fail_w_clk;
}
dev_info(&pdev->dev, "LPC2468 SPI bus driver\n");
return 0;
fail_w_clk:
clk_disable_unprepare(p->clk);
fail:
spi_master_put(master);
return err;
}
static int lpc2468_spi_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct lpc2468_spi *p;
master = platform_get_drvdata(pdev);
p = spi_master_get_devdata(master);
clk_disable_unprepare(p->clk);
spi_master_put(master);
return 0;
}
static const struct of_device_id lpc2468_spi_match[] = {
{ .compatible = "unknown,lpc2468-spi", },
{},
};
MODULE_DEVICE_TABLE(of, lpc2468_spi_match);
static struct platform_driver lpc2468_spi_driver = {
.driver = {
.name = "spi-lpc2468",
.of_match_table = lpc2468_spi_match,
},
.probe = lpc2468_spi_probe,
.remove = lpc2468_spi_remove,
};
module_platform_driver(lpc2468_spi_driver);
MODULE_DESCRIPTION("LPC2468 SPI bus driver");
MODULE_AUTHOR("Konstantin Kirik");
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment