12cb1b3b3SRich Felker /* 22cb1b3b3SRich Felker * J-Core SPI controller driver 32cb1b3b3SRich Felker * 42cb1b3b3SRich Felker * Copyright (C) 2012-2016 Smart Energy Instruments, Inc. 52cb1b3b3SRich Felker * 62cb1b3b3SRich Felker * Current version by Rich Felker 72cb1b3b3SRich Felker * Based loosely on initial version by Oleksandr G Zhadan 82cb1b3b3SRich Felker * 92cb1b3b3SRich Felker */ 102cb1b3b3SRich Felker #include <linux/init.h> 112cb1b3b3SRich Felker #include <linux/interrupt.h> 122cb1b3b3SRich Felker #include <linux/errno.h> 132cb1b3b3SRich Felker #include <linux/module.h> 142cb1b3b3SRich Felker #include <linux/platform_device.h> 152cb1b3b3SRich Felker #include <linux/spi/spi.h> 162cb1b3b3SRich Felker #include <linux/clk.h> 172cb1b3b3SRich Felker #include <linux/err.h> 182cb1b3b3SRich Felker #include <linux/io.h> 192cb1b3b3SRich Felker #include <linux/of.h> 202cb1b3b3SRich Felker #include <linux/delay.h> 212cb1b3b3SRich Felker 222cb1b3b3SRich Felker #define DRV_NAME "jcore_spi" 232cb1b3b3SRich Felker 242cb1b3b3SRich Felker #define CTRL_REG 0x0 252cb1b3b3SRich Felker #define DATA_REG 0x4 262cb1b3b3SRich Felker 272cb1b3b3SRich Felker #define JCORE_SPI_CTRL_XMIT 0x02 282cb1b3b3SRich Felker #define JCORE_SPI_STAT_BUSY 0x02 292cb1b3b3SRich Felker #define JCORE_SPI_CTRL_LOOP 0x08 302cb1b3b3SRich Felker #define JCORE_SPI_CTRL_CS_BITS 0x15 312cb1b3b3SRich Felker 322cb1b3b3SRich Felker #define JCORE_SPI_WAIT_RDY_MAX_LOOP 2000000 332cb1b3b3SRich Felker 342cb1b3b3SRich Felker struct jcore_spi { 352cb1b3b3SRich Felker struct spi_master *master; 362cb1b3b3SRich Felker void __iomem *base; 372cb1b3b3SRich Felker unsigned int cs_reg; 382cb1b3b3SRich Felker unsigned int speed_reg; 392cb1b3b3SRich Felker unsigned int speed_hz; 402cb1b3b3SRich Felker unsigned int clock_freq; 412cb1b3b3SRich Felker }; 422cb1b3b3SRich Felker 432cb1b3b3SRich Felker static int jcore_spi_wait(void __iomem *ctrl_reg) 442cb1b3b3SRich Felker { 452cb1b3b3SRich Felker unsigned timeout = JCORE_SPI_WAIT_RDY_MAX_LOOP; 462cb1b3b3SRich Felker 472cb1b3b3SRich Felker do { 482cb1b3b3SRich Felker if (!(readl(ctrl_reg) & JCORE_SPI_STAT_BUSY)) 492cb1b3b3SRich Felker return 0; 502cb1b3b3SRich Felker cpu_relax(); 512cb1b3b3SRich Felker } while (--timeout); 522cb1b3b3SRich Felker 532cb1b3b3SRich Felker return -EBUSY; 542cb1b3b3SRich Felker } 552cb1b3b3SRich Felker 562cb1b3b3SRich Felker static void jcore_spi_program(struct jcore_spi *hw) 572cb1b3b3SRich Felker { 582cb1b3b3SRich Felker void __iomem *ctrl_reg = hw->base + CTRL_REG; 592cb1b3b3SRich Felker 602cb1b3b3SRich Felker if (jcore_spi_wait(ctrl_reg)) 612cb1b3b3SRich Felker dev_err(hw->master->dev.parent, 622cb1b3b3SRich Felker "timeout waiting to program ctrl reg.\n"); 632cb1b3b3SRich Felker 642cb1b3b3SRich Felker writel(hw->cs_reg | hw->speed_reg, ctrl_reg); 652cb1b3b3SRich Felker } 662cb1b3b3SRich Felker 672cb1b3b3SRich Felker static void jcore_spi_chipsel(struct spi_device *spi, bool value) 682cb1b3b3SRich Felker { 692cb1b3b3SRich Felker struct jcore_spi *hw = spi_master_get_devdata(spi->master); 702cb1b3b3SRich Felker u32 csbit = 1U << (2 * spi->chip_select); 712cb1b3b3SRich Felker 722cb1b3b3SRich Felker dev_dbg(hw->master->dev.parent, "chipselect %d\n", spi->chip_select); 732cb1b3b3SRich Felker 742cb1b3b3SRich Felker if (value) 752cb1b3b3SRich Felker hw->cs_reg |= csbit; 762cb1b3b3SRich Felker else 772cb1b3b3SRich Felker hw->cs_reg &= ~csbit; 782cb1b3b3SRich Felker 792cb1b3b3SRich Felker jcore_spi_program(hw); 802cb1b3b3SRich Felker } 812cb1b3b3SRich Felker 822cb1b3b3SRich Felker static void jcore_spi_baudrate(struct jcore_spi *hw, int speed) 832cb1b3b3SRich Felker { 842cb1b3b3SRich Felker if (speed == hw->speed_hz) return; 852cb1b3b3SRich Felker hw->speed_hz = speed; 862cb1b3b3SRich Felker if (speed >= hw->clock_freq / 2) 872cb1b3b3SRich Felker hw->speed_reg = 0; 882cb1b3b3SRich Felker else 892cb1b3b3SRich Felker hw->speed_reg = ((hw->clock_freq / 2 / speed) - 1) << 27; 902cb1b3b3SRich Felker jcore_spi_program(hw); 912cb1b3b3SRich Felker dev_dbg(hw->master->dev.parent, "speed=%d reg=0x%x\n", 922cb1b3b3SRich Felker speed, hw->speed_reg); 932cb1b3b3SRich Felker } 942cb1b3b3SRich Felker 952cb1b3b3SRich Felker static int jcore_spi_txrx(struct spi_master *master, struct spi_device *spi, 962cb1b3b3SRich Felker struct spi_transfer *t) 972cb1b3b3SRich Felker { 982cb1b3b3SRich Felker struct jcore_spi *hw = spi_master_get_devdata(master); 992cb1b3b3SRich Felker 1002cb1b3b3SRich Felker void __iomem *ctrl_reg = hw->base + CTRL_REG; 1012cb1b3b3SRich Felker void __iomem *data_reg = hw->base + DATA_REG; 1022cb1b3b3SRich Felker u32 xmit; 1032cb1b3b3SRich Felker 1042cb1b3b3SRich Felker /* data buffers */ 1052cb1b3b3SRich Felker const unsigned char *tx; 1062cb1b3b3SRich Felker unsigned char *rx; 1072cb1b3b3SRich Felker unsigned int len; 1082cb1b3b3SRich Felker unsigned int count; 1092cb1b3b3SRich Felker 1102cb1b3b3SRich Felker jcore_spi_baudrate(hw, t->speed_hz); 1112cb1b3b3SRich Felker 1122cb1b3b3SRich Felker xmit = hw->cs_reg | hw->speed_reg | JCORE_SPI_CTRL_XMIT; 1132cb1b3b3SRich Felker tx = t->tx_buf; 1142cb1b3b3SRich Felker rx = t->rx_buf; 1152cb1b3b3SRich Felker len = t->len; 1162cb1b3b3SRich Felker 1172cb1b3b3SRich Felker for (count = 0; count < len; count++) { 1182cb1b3b3SRich Felker if (jcore_spi_wait(ctrl_reg)) 1192cb1b3b3SRich Felker break; 1202cb1b3b3SRich Felker 1212cb1b3b3SRich Felker writel(tx ? *tx++ : 0, data_reg); 1222cb1b3b3SRich Felker writel(xmit, ctrl_reg); 1232cb1b3b3SRich Felker 1242cb1b3b3SRich Felker if (jcore_spi_wait(ctrl_reg)) 1252cb1b3b3SRich Felker break; 1262cb1b3b3SRich Felker 1272cb1b3b3SRich Felker if (rx) 1282cb1b3b3SRich Felker *rx++ = readl(data_reg); 1292cb1b3b3SRich Felker } 1302cb1b3b3SRich Felker 1312cb1b3b3SRich Felker spi_finalize_current_transfer(master); 1322cb1b3b3SRich Felker 1332cb1b3b3SRich Felker if (count < len) 1342cb1b3b3SRich Felker return -EREMOTEIO; 1352cb1b3b3SRich Felker 1362cb1b3b3SRich Felker return 0; 1372cb1b3b3SRich Felker } 1382cb1b3b3SRich Felker 1392cb1b3b3SRich Felker static int jcore_spi_probe(struct platform_device *pdev) 1402cb1b3b3SRich Felker { 1412cb1b3b3SRich Felker struct device_node *node = pdev->dev.of_node; 1422cb1b3b3SRich Felker struct jcore_spi *hw; 1432cb1b3b3SRich Felker struct spi_master *master; 1442cb1b3b3SRich Felker struct resource *res; 1452cb1b3b3SRich Felker u32 clock_freq; 1462cb1b3b3SRich Felker struct clk *clk; 1472cb1b3b3SRich Felker int err = -ENODEV; 1482cb1b3b3SRich Felker 1492cb1b3b3SRich Felker master = spi_alloc_master(&pdev->dev, sizeof(struct jcore_spi)); 1502cb1b3b3SRich Felker if (!master) 1512cb1b3b3SRich Felker return err; 1522cb1b3b3SRich Felker 1532cb1b3b3SRich Felker /* Setup the master state. */ 1542cb1b3b3SRich Felker master->num_chipselect = 3; 1552cb1b3b3SRich Felker master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 1562cb1b3b3SRich Felker master->transfer_one = jcore_spi_txrx; 1572cb1b3b3SRich Felker master->set_cs = jcore_spi_chipsel; 1582cb1b3b3SRich Felker master->dev.of_node = node; 1592cb1b3b3SRich Felker master->bus_num = pdev->id; 1602cb1b3b3SRich Felker 1612cb1b3b3SRich Felker hw = spi_master_get_devdata(master); 1622cb1b3b3SRich Felker hw->master = master; 1632cb1b3b3SRich Felker platform_set_drvdata(pdev, hw); 1642cb1b3b3SRich Felker 1652cb1b3b3SRich Felker /* Find and map our resources */ 1662cb1b3b3SRich Felker res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1672cb1b3b3SRich Felker if (!res) 1682cb1b3b3SRich Felker goto exit_busy; 1692cb1b3b3SRich Felker if (!devm_request_mem_region(&pdev->dev, res->start, 1702cb1b3b3SRich Felker resource_size(res), pdev->name)) 1712cb1b3b3SRich Felker goto exit_busy; 1722cb1b3b3SRich Felker hw->base = devm_ioremap_nocache(&pdev->dev, res->start, 1732cb1b3b3SRich Felker resource_size(res)); 1742cb1b3b3SRich Felker if (!hw->base) 1752cb1b3b3SRich Felker goto exit_busy; 1762cb1b3b3SRich Felker 1772cb1b3b3SRich Felker /* 1782cb1b3b3SRich Felker * The SPI clock rate controlled via a configurable clock divider 1792cb1b3b3SRich Felker * which is applied to the reference clock. A 50 MHz reference is 1802cb1b3b3SRich Felker * most suitable for obtaining standard SPI clock rates, but some 1812cb1b3b3SRich Felker * designs may have a different reference clock, and the DT must 1822cb1b3b3SRich Felker * make the driver aware so that it can properly program the 1832cb1b3b3SRich Felker * requested rate. If the clock is omitted, 50 MHz is assumed. 1842cb1b3b3SRich Felker */ 1852cb1b3b3SRich Felker clock_freq = 50000000; 1862cb1b3b3SRich Felker clk = devm_clk_get(&pdev->dev, "ref_clk"); 1872cb1b3b3SRich Felker if (!IS_ERR_OR_NULL(clk)) { 1882cb1b3b3SRich Felker if (clk_enable(clk) == 0) 1892cb1b3b3SRich Felker clock_freq = clk_get_rate(clk); 1902cb1b3b3SRich Felker else 1912cb1b3b3SRich Felker dev_warn(&pdev->dev, "could not enable ref_clk\n"); 1922cb1b3b3SRich Felker } 1932cb1b3b3SRich Felker hw->clock_freq = clock_freq; 1942cb1b3b3SRich Felker 1952cb1b3b3SRich Felker /* Initialize all CS bits to high. */ 1962cb1b3b3SRich Felker hw->cs_reg = JCORE_SPI_CTRL_CS_BITS; 1972cb1b3b3SRich Felker jcore_spi_baudrate(hw, 400000); 1982cb1b3b3SRich Felker 1992cb1b3b3SRich Felker /* Register our spi controller */ 2002cb1b3b3SRich Felker err = devm_spi_register_master(&pdev->dev, master); 2012cb1b3b3SRich Felker if (err) 2022cb1b3b3SRich Felker goto exit; 2032cb1b3b3SRich Felker 2042cb1b3b3SRich Felker return 0; 2052cb1b3b3SRich Felker 2062cb1b3b3SRich Felker exit_busy: 2072cb1b3b3SRich Felker err = -EBUSY; 2082cb1b3b3SRich Felker exit: 2092cb1b3b3SRich Felker platform_set_drvdata(pdev, NULL); 2102cb1b3b3SRich Felker spi_master_put(master); 2112cb1b3b3SRich Felker return err; 2122cb1b3b3SRich Felker } 2132cb1b3b3SRich Felker 2142cb1b3b3SRich Felker static const struct of_device_id jcore_spi_of_match[] = { 2152cb1b3b3SRich Felker { .compatible = "jcore,spi2" }, 2162cb1b3b3SRich Felker {}, 2172cb1b3b3SRich Felker }; 2182cb1b3b3SRich Felker 2192cb1b3b3SRich Felker static struct platform_driver jcore_spi_driver = { 2202cb1b3b3SRich Felker .probe = jcore_spi_probe, 2212cb1b3b3SRich Felker .driver = { 2222cb1b3b3SRich Felker .name = DRV_NAME, 2232cb1b3b3SRich Felker .of_match_table = jcore_spi_of_match, 2242cb1b3b3SRich Felker }, 2252cb1b3b3SRich Felker }; 2262cb1b3b3SRich Felker 2272cb1b3b3SRich Felker module_platform_driver(jcore_spi_driver); 2282cb1b3b3SRich Felker 2292cb1b3b3SRich Felker MODULE_DESCRIPTION("J-Core SPI driver"); 2302cb1b3b3SRich Felker MODULE_AUTHOR("Rich Felker <dalias@libc.org>"); 2312cb1b3b3SRich Felker MODULE_LICENSE("GPL"); 2322cb1b3b3SRich Felker MODULE_ALIAS("platform:" DRV_NAME); 233