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>
15bef87adfSJagan Teki #include <spi.h>
1615a56f9cSThomas Chou #include <asm/io.h>
1715a56f9cSThomas Chou
1815a56f9cSThomas Chou DECLARE_GLOBAL_DATA_PTR;
19661ba140SThomas Chou
20bef87adfSJagan Teki #define ALTERA_SPI_STATUS_RRDY_MSK BIT(7)
21bef87adfSJagan Teki #define ALTERA_SPI_CONTROL_SSO_MSK BIT(10)
22bef87adfSJagan 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
spi_cs_activate(struct udevice * dev,uint cs)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
spi_cs_deactivate(struct udevice * dev)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
altera_spi_claim_bus(struct udevice * dev)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
altera_spi_release_bus(struct udevice * dev)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
altera_spi_xfer(struct udevice * dev,unsigned int bitlen,const void * dout,void * din,unsigned long flags)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
altera_spi_set_speed(struct udevice * bus,uint speed)15315a56f9cSThomas Chou static int altera_spi_set_speed(struct udevice *bus, uint speed)
15415a56f9cSThomas Chou {
15515a56f9cSThomas Chou return 0;
15615a56f9cSThomas Chou }
15715a56f9cSThomas Chou
altera_spi_set_mode(struct udevice * bus,uint mode)15815a56f9cSThomas Chou static int altera_spi_set_mode(struct udevice *bus, uint mode)
15915a56f9cSThomas Chou {
16015a56f9cSThomas Chou return 0;
16115a56f9cSThomas Chou }
16215a56f9cSThomas Chou
altera_spi_probe(struct udevice * bus)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
altera_spi_ofdata_to_platdata(struct udevice * bus)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
177*a821c4afSSimon Glass plat->regs = map_physmem(devfdt_get_addr(bus),
1787313e21aSThomas Chou sizeof(struct altera_spi_regs),
1797313e21aSThomas Chou MAP_NOCACHE);
18015a56f9cSThomas Chou
18115a56f9cSThomas Chou return 0;
18215a56f9cSThomas Chou }
18315a56f9cSThomas Chou
18415a56f9cSThomas Chou static const struct dm_spi_ops altera_spi_ops = {
18515a56f9cSThomas Chou .claim_bus = altera_spi_claim_bus,
18615a56f9cSThomas Chou .release_bus = altera_spi_release_bus,
18715a56f9cSThomas Chou .xfer = altera_spi_xfer,
18815a56f9cSThomas Chou .set_speed = altera_spi_set_speed,
18915a56f9cSThomas Chou .set_mode = altera_spi_set_mode,
19015a56f9cSThomas Chou /*
19115a56f9cSThomas Chou * cs_info is not needed, since we require all chip selects to be
19215a56f9cSThomas Chou * in the device tree explicitly
19315a56f9cSThomas Chou */
19415a56f9cSThomas Chou };
19515a56f9cSThomas Chou
19615a56f9cSThomas Chou static const struct udevice_id altera_spi_ids[] = {
197ddf34c26SThomas Chou { .compatible = "altr,spi-1.0" },
19815a56f9cSThomas Chou {}
19915a56f9cSThomas Chou };
20015a56f9cSThomas Chou
20115a56f9cSThomas Chou U_BOOT_DRIVER(altera_spi) = {
20215a56f9cSThomas Chou .name = "altera_spi",
20315a56f9cSThomas Chou .id = UCLASS_SPI,
20415a56f9cSThomas Chou .of_match = altera_spi_ids,
20515a56f9cSThomas Chou .ops = &altera_spi_ops,
20615a56f9cSThomas Chou .ofdata_to_platdata = altera_spi_ofdata_to_platdata,
20715a56f9cSThomas Chou .platdata_auto_alloc_size = sizeof(struct altera_spi_platdata),
20815a56f9cSThomas Chou .priv_auto_alloc_size = sizeof(struct altera_spi_priv),
20915a56f9cSThomas Chou .probe = altera_spi_probe,
21015a56f9cSThomas Chou };
211