1661ba140SThomas Chou /* 2661ba140SThomas Chou * Altera SPI driver 3661ba140SThomas Chou * 4661ba140SThomas Chou * based on bfin_spi.c 5661ba140SThomas Chou * Copyright (c) 2005-2008 Analog Devices Inc. 6661ba140SThomas Chou * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> 7661ba140SThomas Chou * 8e7b1e452SJagannadha Sutradharudu Teki * SPDX-License-Identifier: GPL-2.0+ 9661ba140SThomas Chou */ 10661ba140SThomas Chou #include <common.h> 1115a56f9cSThomas Chou #include <dm.h> 1215a56f9cSThomas Chou #include <errno.h> 13661ba140SThomas Chou #include <malloc.h> 1415a56f9cSThomas Chou #include <fdtdec.h> 15*bef87adfSJagan Teki #include <spi.h> 1615a56f9cSThomas Chou #include <asm/io.h> 1715a56f9cSThomas Chou 1815a56f9cSThomas Chou DECLARE_GLOBAL_DATA_PTR; 19661ba140SThomas Chou 20*bef87adfSJagan Teki #define ALTERA_SPI_STATUS_RRDY_MSK BIT(7) 21*bef87adfSJagan Teki #define ALTERA_SPI_CONTROL_SSO_MSK BIT(10) 22*bef87adfSJagan Teki 23cdcdad85SMarek Vasut #ifndef CONFIG_ALTERA_SPI_IDLE_VAL 24cdcdad85SMarek Vasut #define CONFIG_ALTERA_SPI_IDLE_VAL 0xff 25cdcdad85SMarek Vasut #endif 26cdcdad85SMarek Vasut 27eef67029SMarek Vasut struct altera_spi_regs { 28eef67029SMarek Vasut u32 rxdata; 29eef67029SMarek Vasut u32 txdata; 30eef67029SMarek Vasut u32 status; 31eef67029SMarek Vasut u32 control; 32eef67029SMarek Vasut u32 _reserved; 33eef67029SMarek Vasut u32 slave_sel; 34eef67029SMarek Vasut }; 35661ba140SThomas Chou 3615a56f9cSThomas Chou struct altera_spi_platdata { 37eef67029SMarek Vasut struct altera_spi_regs *regs; 38661ba140SThomas Chou }; 39661ba140SThomas Chou 4015a56f9cSThomas Chou struct altera_spi_priv { 4115a56f9cSThomas Chou struct altera_spi_regs *regs; 4215a56f9cSThomas Chou }; 4315a56f9cSThomas Chou 4415a56f9cSThomas Chou static void spi_cs_activate(struct udevice *dev, uint cs) 45661ba140SThomas Chou { 4615a56f9cSThomas Chou struct udevice *bus = dev->parent; 4715a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 4815a56f9cSThomas Chou struct altera_spi_regs *const regs = priv->regs; 4915a56f9cSThomas Chou 5015a56f9cSThomas Chou writel(1 << cs, ®s->slave_sel); 5115a56f9cSThomas Chou writel(ALTERA_SPI_CONTROL_SSO_MSK, ®s->control); 52661ba140SThomas Chou } 53661ba140SThomas Chou 5415a56f9cSThomas Chou static void spi_cs_deactivate(struct udevice *dev) 55661ba140SThomas Chou { 5615a56f9cSThomas Chou struct udevice *bus = dev->parent; 5715a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 5815a56f9cSThomas Chou struct altera_spi_regs *const regs = priv->regs; 5915a56f9cSThomas Chou 6015a56f9cSThomas Chou writel(0, ®s->control); 6115a56f9cSThomas Chou writel(0, ®s->slave_sel); 62661ba140SThomas Chou } 63661ba140SThomas Chou 6415a56f9cSThomas Chou static int altera_spi_claim_bus(struct udevice *dev) 65661ba140SThomas Chou { 6615a56f9cSThomas Chou struct udevice *bus = dev->parent; 6715a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 6815a56f9cSThomas Chou struct altera_spi_regs *const regs = priv->regs; 69661ba140SThomas Chou 7015a56f9cSThomas Chou writel(0, ®s->control); 7115a56f9cSThomas Chou writel(0, ®s->slave_sel); 72661ba140SThomas Chou 73661ba140SThomas Chou return 0; 74661ba140SThomas Chou } 75661ba140SThomas Chou 7615a56f9cSThomas Chou static int altera_spi_release_bus(struct udevice *dev) 77661ba140SThomas Chou { 7815a56f9cSThomas Chou struct udevice *bus = dev->parent; 7915a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 8015a56f9cSThomas Chou struct altera_spi_regs *const regs = priv->regs; 81661ba140SThomas Chou 8215a56f9cSThomas Chou writel(0, ®s->slave_sel); 8315a56f9cSThomas Chou 8415a56f9cSThomas Chou return 0; 85661ba140SThomas Chou } 86661ba140SThomas Chou 8715a56f9cSThomas Chou static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen, 8815a56f9cSThomas Chou const void *dout, void *din, unsigned long flags) 89661ba140SThomas Chou { 9015a56f9cSThomas Chou struct udevice *bus = dev->parent; 9115a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 9215a56f9cSThomas Chou struct altera_spi_regs *const regs = priv->regs; 9315a56f9cSThomas Chou struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); 9415a56f9cSThomas Chou 95661ba140SThomas Chou /* assume spi core configured to do 8 bit transfers */ 96bc76b821SMarek Vasut unsigned int bytes = bitlen / 8; 97bc76b821SMarek Vasut const unsigned char *txp = dout; 98bc76b821SMarek Vasut unsigned char *rxp = din; 99bc76b821SMarek Vasut uint32_t reg, data, start; 100661ba140SThomas Chou 101661ba140SThomas Chou debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, 10215a56f9cSThomas Chou bus->seq, slave_plat->cs, bitlen, bytes, flags); 10337dcc130SMarek Vasut 104661ba140SThomas Chou if (bitlen == 0) 105661ba140SThomas Chou goto done; 106661ba140SThomas Chou 107661ba140SThomas Chou if (bitlen % 8) { 108661ba140SThomas Chou flags |= SPI_XFER_END; 109661ba140SThomas Chou goto done; 110661ba140SThomas Chou } 111661ba140SThomas Chou 112661ba140SThomas Chou /* empty read buffer */ 11315a56f9cSThomas Chou if (readl(®s->status) & ALTERA_SPI_STATUS_RRDY_MSK) 11415a56f9cSThomas Chou readl(®s->rxdata); 11537dcc130SMarek Vasut 116661ba140SThomas Chou if (flags & SPI_XFER_BEGIN) 11715a56f9cSThomas Chou spi_cs_activate(dev, slave_plat->cs); 118661ba140SThomas Chou 119661ba140SThomas Chou while (bytes--) { 120bc76b821SMarek Vasut if (txp) 121bc76b821SMarek Vasut data = *txp++; 122bc76b821SMarek Vasut else 123bc76b821SMarek Vasut data = CONFIG_ALTERA_SPI_IDLE_VAL; 12437dcc130SMarek Vasut 125bc76b821SMarek Vasut debug("%s: tx:%x ", __func__, data); 12615a56f9cSThomas Chou writel(data, ®s->txdata); 12737dcc130SMarek Vasut 12880d73338SMarek Vasut start = get_timer(0); 12980d73338SMarek Vasut while (1) { 13015a56f9cSThomas Chou reg = readl(®s->status); 13180d73338SMarek Vasut if (reg & ALTERA_SPI_STATUS_RRDY_MSK) 13280d73338SMarek Vasut break; 13380d73338SMarek Vasut if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { 13415a56f9cSThomas Chou debug("%s: Transmission timed out!\n", __func__); 13515a56f9cSThomas Chou return -1; 13680d73338SMarek Vasut } 13780d73338SMarek Vasut } 13837dcc130SMarek Vasut 13915a56f9cSThomas Chou data = readl(®s->rxdata); 140661ba140SThomas Chou if (rxp) 141bc76b821SMarek Vasut *rxp++ = data & 0xff; 14237dcc130SMarek Vasut 143bc76b821SMarek Vasut debug("rx:%x\n", data); 144661ba140SThomas Chou } 14537dcc130SMarek Vasut 146661ba140SThomas Chou done: 147661ba140SThomas Chou if (flags & SPI_XFER_END) 14815a56f9cSThomas Chou spi_cs_deactivate(dev); 149661ba140SThomas Chou 150661ba140SThomas Chou return 0; 151661ba140SThomas Chou } 15215a56f9cSThomas Chou 15315a56f9cSThomas Chou static int altera_spi_set_speed(struct udevice *bus, uint speed) 15415a56f9cSThomas Chou { 15515a56f9cSThomas Chou return 0; 15615a56f9cSThomas Chou } 15715a56f9cSThomas Chou 15815a56f9cSThomas Chou static int altera_spi_set_mode(struct udevice *bus, uint mode) 15915a56f9cSThomas Chou { 16015a56f9cSThomas Chou return 0; 16115a56f9cSThomas Chou } 16215a56f9cSThomas Chou 16315a56f9cSThomas Chou static int altera_spi_probe(struct udevice *bus) 16415a56f9cSThomas Chou { 16515a56f9cSThomas Chou struct altera_spi_platdata *plat = dev_get_platdata(bus); 16615a56f9cSThomas Chou struct altera_spi_priv *priv = dev_get_priv(bus); 16715a56f9cSThomas Chou 16815a56f9cSThomas Chou priv->regs = plat->regs; 16915a56f9cSThomas Chou 17015a56f9cSThomas Chou return 0; 17115a56f9cSThomas Chou } 17215a56f9cSThomas Chou 17315a56f9cSThomas Chou static int altera_spi_ofdata_to_platdata(struct udevice *bus) 17415a56f9cSThomas Chou { 17515a56f9cSThomas Chou struct altera_spi_platdata *plat = dev_get_platdata(bus); 17615a56f9cSThomas Chou 17715a56f9cSThomas Chou plat->regs = ioremap(dev_get_addr(bus), 17815a56f9cSThomas Chou sizeof(struct altera_spi_regs)); 17915a56f9cSThomas Chou 18015a56f9cSThomas Chou return 0; 18115a56f9cSThomas Chou } 18215a56f9cSThomas Chou 18315a56f9cSThomas Chou static const struct dm_spi_ops altera_spi_ops = { 18415a56f9cSThomas Chou .claim_bus = altera_spi_claim_bus, 18515a56f9cSThomas Chou .release_bus = altera_spi_release_bus, 18615a56f9cSThomas Chou .xfer = altera_spi_xfer, 18715a56f9cSThomas Chou .set_speed = altera_spi_set_speed, 18815a56f9cSThomas Chou .set_mode = altera_spi_set_mode, 18915a56f9cSThomas Chou /* 19015a56f9cSThomas Chou * cs_info is not needed, since we require all chip selects to be 19115a56f9cSThomas Chou * in the device tree explicitly 19215a56f9cSThomas Chou */ 19315a56f9cSThomas Chou }; 19415a56f9cSThomas Chou 19515a56f9cSThomas Chou static const struct udevice_id altera_spi_ids[] = { 19615a56f9cSThomas Chou { .compatible = "altr,spi-1.0", }, 19715a56f9cSThomas Chou { } 19815a56f9cSThomas Chou }; 19915a56f9cSThomas Chou 20015a56f9cSThomas Chou U_BOOT_DRIVER(altera_spi) = { 20115a56f9cSThomas Chou .name = "altera_spi", 20215a56f9cSThomas Chou .id = UCLASS_SPI, 20315a56f9cSThomas Chou .of_match = altera_spi_ids, 20415a56f9cSThomas Chou .ops = &altera_spi_ops, 20515a56f9cSThomas Chou .ofdata_to_platdata = altera_spi_ofdata_to_platdata, 20615a56f9cSThomas Chou .platdata_auto_alloc_size = sizeof(struct altera_spi_platdata), 20715a56f9cSThomas Chou .priv_auto_alloc_size = sizeof(struct altera_spi_priv), 20815a56f9cSThomas Chou .probe = altera_spi_probe, 20915a56f9cSThomas Chou }; 210