Message ID | 1464857769-16550-1-git-send-email-purna.mandal@microchip.com |
---|---|
State | Accepted |
Commit | e19b9004575cacf1f64fff894621adafe0e7ea7f |
Delegated to: | Daniel Schwierzeck |
Headers | show |
Am 02.06.2016 um 10:56 schrieb Purna Chandra Mandal: > This driver implements SPI protocol in master mode to communicate > with the SPI device connected on SPI bus. It handles /CS explicitly > by controlling respective pin as gpio ('cs-gpios' property in dt node) > and uses PIO mode for SPI transaction. It is configurable based > on driver-model only. > > Cc: Jagan Teki <jteki@openedev.com> > Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com> > Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> > --- > > drivers/spi/Kconfig | 8 + > drivers/spi/Makefile | 1 + > drivers/spi/pic32_spi.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 457 insertions(+) > create mode 100644 drivers/spi/pic32_spi.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index b7fd8e5..aca385d 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -75,6 +75,14 @@ config ICH_SPI > access the SPI NOR flash on platforms embedding this Intel > ICH IP core. > > +config PIC32_SPI > + bool "Microchip PIC32 SPI driver" > + depends on MACH_PIC32 > + help > + Enable the Microchip PIC32 SPI driver. This driver can be used > + to access the SPI NOR flash, MMC-over-SPI on platforms based on > + Microchip PIC32 family devices. > + > config ROCKCHIP_SPI > bool "Rockchip SPI driver" > help > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 7fb2926..b1d9e20 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -40,6 +40,7 @@ obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o > obj-$(CONFIG_MXC_SPI) += mxc_spi.o > obj-$(CONFIG_MXS_SPI) += mxs_spi.o > obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o > +obj-$(CONFIG_PIC32_SPI) += pic32_spi.o > obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o > obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o > obj-$(CONFIG_SH_SPI) += sh_spi.o > diff --git a/drivers/spi/pic32_spi.c b/drivers/spi/pic32_spi.c > new file mode 100644 > index 0000000..25ca1f3 > --- /dev/null > +++ b/drivers/spi/pic32_spi.c > @@ -0,0 +1,448 @@ > +/* > + * Microchip PIC32 SPI controller driver. > + * > + * Copyright (c) 2015, Microchip Technology Inc. > + * Purna Chandra Mandal <purna.mandal@microchip.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <clk.h> > +#include <dm.h> > +#include <linux/compat.h> > +#include <malloc.h> > +#include <spi.h> > + > +#include <asm/types.h> > +#include <asm/io.h> > +#include <asm/gpio.h> > +#include <dt-bindings/clock/microchip,clock.h> > +#include <mach/pic32.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +/* PIC32 SPI controller registers */ > +struct pic32_reg_spi { > + struct pic32_reg_atomic ctrl; > + struct pic32_reg_atomic status; > + struct pic32_reg_atomic buf; > + struct pic32_reg_atomic baud; > + struct pic32_reg_atomic ctrl2; > +}; > + > +/* Bit fields in SPI Control Register */ > +#define PIC32_SPI_CTRL_MSTEN BIT(5) /* Enable SPI Master */ > +#define PIC32_SPI_CTRL_CKP BIT(6) /* active low */ > +#define PIC32_SPI_CTRL_CKE BIT(8) /* Tx on falling edge */ > +#define PIC32_SPI_CTRL_SMP BIT(9) /* Rx at middle or end of tx */ > +#define PIC32_SPI_CTRL_BPW_MASK 0x03 /* Bits per word */ > +#define PIC32_SPI_CTRL_BPW_8 0x0 > +#define PIC32_SPI_CTRL_BPW_16 0x1 > +#define PIC32_SPI_CTRL_BPW_32 0x2 > +#define PIC32_SPI_CTRL_BPW_SHIFT 10 > +#define PIC32_SPI_CTRL_ON BIT(15) /* Macro enable */ > +#define PIC32_SPI_CTRL_ENHBUF BIT(16) /* Enable enhanced buffering */ > +#define PIC32_SPI_CTRL_MCLKSEL BIT(23) /* Select SPI Clock src */ > +#define PIC32_SPI_CTRL_MSSEN BIT(28) /* SPI macro will drive SS */ > +#define PIC32_SPI_CTRL_FRMEN BIT(31) /* Enable framing mode */ > + > +/* Bit fields in SPI Status Register */ > +#define PIC32_SPI_STAT_RX_OV BIT(6) /* err, s/w needs to clear */ > +#define PIC32_SPI_STAT_TF_LVL_MASK 0x1f > +#define PIC32_SPI_STAT_TF_LVL_SHIFT 16 > +#define PIC32_SPI_STAT_RF_LVL_MASK 0x1f > +#define PIC32_SPI_STAT_RF_LVL_SHIFT 24 > + > +/* Bit fields in SPI Baud Register */ > +#define PIC32_SPI_BAUD_MASK 0x1ff > + > +struct pic32_spi_priv { > + struct pic32_reg_spi *regs; > + u32 fifo_depth; /* FIFO depth in bytes */ > + u32 fifo_n_word; /* FIFO depth in words */ > + struct gpio_desc cs_gpio; > + > + /* Current SPI slave specific */ > + ulong clk_rate; > + u32 speed_hz; /* spi-clk rate */ > + int mode; > + > + /* Current message/transfer state */ > + const void *tx; > + const void *tx_end; > + const void *rx; > + const void *rx_end; > + u32 len; > + > + /* SPI FiFo accessor */ > + void (*rx_fifo)(struct pic32_spi_priv *); > + void (*tx_fifo)(struct pic32_spi_priv *); > +}; > + > +static inline void pic32_spi_enable(struct pic32_spi_priv *priv) > +{ > + writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.set); > +} > + > +static inline void pic32_spi_disable(struct pic32_spi_priv *priv) > +{ > + writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.clr); > +} > + > +static inline u32 pic32_spi_rx_fifo_level(struct pic32_spi_priv *priv) > +{ > + u32 sr = readl(&priv->regs->status.raw); > + > + return (sr >> PIC32_SPI_STAT_RF_LVL_SHIFT) & PIC32_SPI_STAT_RF_LVL_MASK; > +} > + > +static inline u32 pic32_spi_tx_fifo_level(struct pic32_spi_priv *priv) > +{ > + u32 sr = readl(&priv->regs->status.raw); > + > + return (sr >> PIC32_SPI_STAT_TF_LVL_SHIFT) & PIC32_SPI_STAT_TF_LVL_MASK; > +} > + > +/* Return the max entries we can fill into tx fifo */ > +static u32 pic32_tx_max(struct pic32_spi_priv *priv, int n_bytes) > +{ > + u32 tx_left, tx_room, rxtx_gap; > + > + tx_left = (priv->tx_end - priv->tx) / n_bytes; > + tx_room = priv->fifo_n_word - pic32_spi_tx_fifo_level(priv); > + > + rxtx_gap = (priv->rx_end - priv->rx) - (priv->tx_end - priv->tx); > + rxtx_gap /= n_bytes; > + return min3(tx_left, tx_room, (u32)(priv->fifo_n_word - rxtx_gap)); > +} > + > +/* Return the max entries we should read out of rx fifo */ > +static u32 pic32_rx_max(struct pic32_spi_priv *priv, int n_bytes) > +{ > + u32 rx_left = (priv->rx_end - priv->rx) / n_bytes; > + > + return min_t(u32, rx_left, pic32_spi_rx_fifo_level(priv)); > +} > + > +#define BUILD_SPI_FIFO_RW(__name, __type, __bwl) \ > +static void pic32_spi_rx_##__name(struct pic32_spi_priv *priv) \ > +{ \ > + __type val; \ > + u32 mx = pic32_rx_max(priv, sizeof(__type)); \ > + \ > + for (; mx; mx--) { \ > + val = read##__bwl(&priv->regs->buf.raw); \ > + if (priv->rx_end - priv->len) \ > + *(__type *)(priv->rx) = val; \ > + priv->rx += sizeof(__type); \ > + } \ > +} \ > + \ > +static void pic32_spi_tx_##__name(struct pic32_spi_priv *priv) \ > +{ \ > + __type val; \ > + u32 mx = pic32_tx_max(priv, sizeof(__type)); \ > + \ > + for (; mx ; mx--) { \ > + val = (__type) ~0U; \ > + if (priv->tx_end - priv->len) \ > + val = *(__type *)(priv->tx); \ > + write##__bwl(val, &priv->regs->buf.raw); \ > + priv->tx += sizeof(__type); \ > + } \ > +} > +BUILD_SPI_FIFO_RW(byte, u8, b); > +BUILD_SPI_FIFO_RW(word, u16, w); > +BUILD_SPI_FIFO_RW(dword, u32, l); > + > +static int pic32_spi_set_word_size(struct pic32_spi_priv *priv, > + unsigned int wordlen) > +{ > + u32 bits_per_word; > + u32 val; > + > + switch (wordlen) { > + case 8: > + priv->rx_fifo = pic32_spi_rx_byte; > + priv->tx_fifo = pic32_spi_tx_byte; > + bits_per_word = PIC32_SPI_CTRL_BPW_8; > + break; > + case 16: > + priv->rx_fifo = pic32_spi_rx_word; > + priv->tx_fifo = pic32_spi_tx_word; > + bits_per_word = PIC32_SPI_CTRL_BPW_16; > + break; > + case 32: > + priv->rx_fifo = pic32_spi_rx_dword; > + priv->tx_fifo = pic32_spi_tx_dword; > + bits_per_word = PIC32_SPI_CTRL_BPW_32; > + break; > + default: > + printf("pic32-spi: unsupported wordlen\n"); > + return -EINVAL; > + } > + > + /* set bits-per-word */ > + val = readl(&priv->regs->ctrl.raw); > + val &= ~(PIC32_SPI_CTRL_BPW_MASK << PIC32_SPI_CTRL_BPW_SHIFT); > + val |= bits_per_word << PIC32_SPI_CTRL_BPW_SHIFT; > + writel(val, &priv->regs->ctrl.raw); > + > + /* calculate maximum number of words fifo can hold */ > + priv->fifo_n_word = DIV_ROUND_UP(priv->fifo_depth, wordlen / 8); > + > + return 0; > +} > + > +static int pic32_spi_claim_bus(struct udevice *slave) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); > + > + /* enable chip */ > + pic32_spi_enable(priv); > + > + return 0; > +} > + > +static int pic32_spi_release_bus(struct udevice *slave) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); > + > + /* disable chip */ > + pic32_spi_disable(priv); > + > + return 0; > +} > + > +static void spi_cs_activate(struct pic32_spi_priv *priv) > +{ > + if (!dm_gpio_is_valid(&priv->cs_gpio)) > + return; > + > + dm_gpio_set_value(&priv->cs_gpio, 1); > +} > + > +static void spi_cs_deactivate(struct pic32_spi_priv *priv) > +{ > + if (!dm_gpio_is_valid(&priv->cs_gpio)) > + return; > + > + dm_gpio_set_value(&priv->cs_gpio, 0); > +} > + > +static int pic32_spi_xfer(struct udevice *slave, unsigned int bitlen, > + const void *tx_buf, void *rx_buf, > + unsigned long flags) > +{ > + struct dm_spi_slave_platdata *slave_plat; > + struct udevice *bus = slave->parent; > + struct pic32_spi_priv *priv; > + int len = bitlen / 8; > + int ret = 0; > + ulong tbase; > + > + priv = dev_get_priv(bus); > + slave_plat = dev_get_parent_platdata(slave); > + > + debug("spi_xfer: bus:%i cs:%i flags:%lx\n", > + bus->seq, slave_plat->cs, flags); > + debug("msg tx %p, rx %p submitted of %d byte(s)\n", > + tx_buf, rx_buf, len); > + > + /* assert cs */ > + if (flags & SPI_XFER_BEGIN) > + spi_cs_activate(priv); > + > + /* set current transfer information */ > + priv->tx = tx_buf; > + priv->rx = rx_buf; > + priv->tx_end = priv->tx + len; > + priv->rx_end = priv->rx + len; > + priv->len = len; > + > + /* transact by polling */ > + tbase = get_timer(0); > + for (;;) { > + priv->tx_fifo(priv); > + priv->rx_fifo(priv); > + > + /* received sufficient data */ > + if (priv->rx >= priv->rx_end) { > + ret = 0; > + break; > + } > + > + if (get_timer(tbase) > 5 * CONFIG_SYS_HZ) { > + printf("pic32_spi: error, xfer timedout.\n"); > + flags |= SPI_XFER_END; > + ret = -ETIMEDOUT; > + break; > + } > + } > + > + /* deassert cs */ > + if (flags & SPI_XFER_END) > + spi_cs_deactivate(priv); > + > + return ret; > +} > + > +static int pic32_spi_set_speed(struct udevice *bus, uint speed) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(bus); > + u32 div; > + > + debug("%s: %s, speed %u\n", __func__, bus->name, speed); > + > + /* div = [clk_in / (2 * spi_clk)] - 1 */ > + div = (priv->clk_rate / 2 / speed) - 1; > + div &= PIC32_SPI_BAUD_MASK; > + writel(div, &priv->regs->baud.raw); > + > + priv->speed_hz = speed; > + > + return 0; > +} > + > +static int pic32_spi_set_mode(struct udevice *bus, uint mode) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(bus); > + u32 val; > + > + debug("%s: %s, mode %d\n", __func__, bus->name, mode); > + > + /* set spi-clk mode */ > + val = readl(&priv->regs->ctrl.raw); > + /* HIGH when idle */ > + if (mode & SPI_CPOL) > + val |= PIC32_SPI_CTRL_CKP; > + else > + val &= ~PIC32_SPI_CTRL_CKP; > + > + /* TX at idle-to-active clk transition */ > + if (mode & SPI_CPHA) > + val &= ~PIC32_SPI_CTRL_CKE; > + else > + val |= PIC32_SPI_CTRL_CKE; > + > + /* RX at end of tx */ > + val |= PIC32_SPI_CTRL_SMP; > + writel(val, &priv->regs->ctrl.raw); > + > + priv->mode = mode; > + > + return 0; > +} > + > +static int pic32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); > + > + return pic32_spi_set_word_size(priv, wordlen); > +} > + > +static void pic32_spi_hw_init(struct pic32_spi_priv *priv) > +{ > + u32 val; > + > + /* disable module */ > + pic32_spi_disable(priv); > + > + val = readl(&priv->regs->ctrl); > + > + /* enable enhanced fifo of 128bit deep */ > + val |= PIC32_SPI_CTRL_ENHBUF; > + priv->fifo_depth = 16; > + > + /* disable framing mode */ > + val &= ~PIC32_SPI_CTRL_FRMEN; > + > + /* enable master mode */ > + val |= PIC32_SPI_CTRL_MSTEN; > + > + /* select clk source */ > + val &= ~PIC32_SPI_CTRL_MCLKSEL; > + > + /* set manual /CS mode */ > + val &= ~PIC32_SPI_CTRL_MSSEN; > + > + writel(val, &priv->regs->ctrl); > + > + /* clear rx overflow indicator */ > + writel(PIC32_SPI_STAT_RX_OV, &priv->regs->status.clr); > +} > + > +static int pic32_spi_probe(struct udevice *bus) > +{ > + struct pic32_spi_priv *priv = dev_get_priv(bus); > + struct dm_spi_bus *dm_spi = dev_get_uclass_priv(bus); > + struct udevice *clkdev; > + fdt_addr_t addr; > + fdt_size_t size; > + int ret; > + > + debug("%s: %d, bus: %i\n", __func__, __LINE__, bus->seq); > + addr = fdtdec_get_addr_size(gd->fdt_blob, bus->of_offset, "reg", &size); > + if (addr == FDT_ADDR_T_NONE) > + return -EINVAL; > + > + priv->regs = ioremap(addr, size); > + if (!priv->regs) > + return -EINVAL; > + > + dm_spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset, > + "spi-max-frequency", 250000000); > + /* get clock rate */ > + ret = clk_get_by_index(bus, 0, &clkdev); > + if (ret < 0) { > + printf("pic32-spi: error, clk not found\n"); > + return ret; > + } > + priv->clk_rate = clk_get_periph_rate(clkdev, ret); > + > + /* initialize HW */ > + pic32_spi_hw_init(priv); > + > + /* set word len */ > + pic32_spi_set_word_size(priv, SPI_DEFAULT_WORDLEN); > + > + /* PIC32 SPI controller can automatically drive /CS during transfer > + * depending on fifo fill-level. /CS will stay asserted as long as > + * TX fifo is non-empty, else will be deasserted confirming completion > + * of the ongoing transfer. To avoid this sort of error we will drive > + * /CS manually by toggling cs-gpio pins. > + */ > + ret = gpio_request_by_name_nodev(gd->fdt_blob, bus->of_offset, > + "cs-gpios", 0, > + &priv->cs_gpio, GPIOD_IS_OUT); > + if (ret) { > + printf("pic32-spi: error, cs-gpios not found\n"); > + return ret; > + } > + > + return 0; > +} > + > +static const struct dm_spi_ops pic32_spi_ops = { > + .claim_bus = pic32_spi_claim_bus, > + .release_bus = pic32_spi_release_bus, > + .xfer = pic32_spi_xfer, > + .set_speed = pic32_spi_set_speed, > + .set_mode = pic32_spi_set_mode, > + .set_wordlen = pic32_spi_set_wordlen, > +}; > + > +static const struct udevice_id pic32_spi_ids[] = { > + { .compatible = "microchip,pic32mzda-spi" }, > + { } > +}; > + > +U_BOOT_DRIVER(pic32_spi) = { > + .name = "pic32_spi", > + .id = UCLASS_SPI, > + .of_match = pic32_spi_ids, > + .ops = &pic32_spi_ops, > + .priv_auto_alloc_size = sizeof(struct pic32_spi_priv), > + .probe = pic32_spi_probe, > +}; >
Am 02.06.2016 um 10:56 schrieb Purna Chandra Mandal: > This driver implements SPI protocol in master mode to communicate > with the SPI device connected on SPI bus. It handles /CS explicitly > by controlling respective pin as gpio ('cs-gpios' property in dt node) > and uses PIO mode for SPI transaction. It is configurable based > on driver-model only. > > Cc: Jagan Teki <jteki@openedev.com> > Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com> > > --- > > drivers/spi/Kconfig | 8 + > drivers/spi/Makefile | 1 + > drivers/spi/pic32_spi.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 457 insertions(+) > create mode 100644 drivers/spi/pic32_spi.c > applied to u-boot-mips, thanks.
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7fd8e5..aca385d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,6 +75,14 @@ config ICH_SPI access the SPI NOR flash on platforms embedding this Intel ICH IP core. +config PIC32_SPI + bool "Microchip PIC32 SPI driver" + depends on MACH_PIC32 + help + Enable the Microchip PIC32 SPI driver. This driver can be used + to access the SPI NOR flash, MMC-over-SPI on platforms based on + Microchip PIC32 family devices. + config ROCKCHIP_SPI bool "Rockchip SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 7fb2926..b1d9e20 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o obj-$(CONFIG_MXC_SPI) += mxc_spi.o obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o +obj-$(CONFIG_PIC32_SPI) += pic32_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o diff --git a/drivers/spi/pic32_spi.c b/drivers/spi/pic32_spi.c new file mode 100644 index 0000000..25ca1f3 --- /dev/null +++ b/drivers/spi/pic32_spi.c @@ -0,0 +1,448 @@ +/* + * Microchip PIC32 SPI controller driver. + * + * Copyright (c) 2015, Microchip Technology Inc. + * Purna Chandra Mandal <purna.mandal@microchip.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <linux/compat.h> +#include <malloc.h> +#include <spi.h> + +#include <asm/types.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <dt-bindings/clock/microchip,clock.h> +#include <mach/pic32.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* PIC32 SPI controller registers */ +struct pic32_reg_spi { + struct pic32_reg_atomic ctrl; + struct pic32_reg_atomic status; + struct pic32_reg_atomic buf; + struct pic32_reg_atomic baud; + struct pic32_reg_atomic ctrl2; +}; + +/* Bit fields in SPI Control Register */ +#define PIC32_SPI_CTRL_MSTEN BIT(5) /* Enable SPI Master */ +#define PIC32_SPI_CTRL_CKP BIT(6) /* active low */ +#define PIC32_SPI_CTRL_CKE BIT(8) /* Tx on falling edge */ +#define PIC32_SPI_CTRL_SMP BIT(9) /* Rx at middle or end of tx */ +#define PIC32_SPI_CTRL_BPW_MASK 0x03 /* Bits per word */ +#define PIC32_SPI_CTRL_BPW_8 0x0 +#define PIC32_SPI_CTRL_BPW_16 0x1 +#define PIC32_SPI_CTRL_BPW_32 0x2 +#define PIC32_SPI_CTRL_BPW_SHIFT 10 +#define PIC32_SPI_CTRL_ON BIT(15) /* Macro enable */ +#define PIC32_SPI_CTRL_ENHBUF BIT(16) /* Enable enhanced buffering */ +#define PIC32_SPI_CTRL_MCLKSEL BIT(23) /* Select SPI Clock src */ +#define PIC32_SPI_CTRL_MSSEN BIT(28) /* SPI macro will drive SS */ +#define PIC32_SPI_CTRL_FRMEN BIT(31) /* Enable framing mode */ + +/* Bit fields in SPI Status Register */ +#define PIC32_SPI_STAT_RX_OV BIT(6) /* err, s/w needs to clear */ +#define PIC32_SPI_STAT_TF_LVL_MASK 0x1f +#define PIC32_SPI_STAT_TF_LVL_SHIFT 16 +#define PIC32_SPI_STAT_RF_LVL_MASK 0x1f +#define PIC32_SPI_STAT_RF_LVL_SHIFT 24 + +/* Bit fields in SPI Baud Register */ +#define PIC32_SPI_BAUD_MASK 0x1ff + +struct pic32_spi_priv { + struct pic32_reg_spi *regs; + u32 fifo_depth; /* FIFO depth in bytes */ + u32 fifo_n_word; /* FIFO depth in words */ + struct gpio_desc cs_gpio; + + /* Current SPI slave specific */ + ulong clk_rate; + u32 speed_hz; /* spi-clk rate */ + int mode; + + /* Current message/transfer state */ + const void *tx; + const void *tx_end; + const void *rx; + const void *rx_end; + u32 len; + + /* SPI FiFo accessor */ + void (*rx_fifo)(struct pic32_spi_priv *); + void (*tx_fifo)(struct pic32_spi_priv *); +}; + +static inline void pic32_spi_enable(struct pic32_spi_priv *priv) +{ + writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.set); +} + +static inline void pic32_spi_disable(struct pic32_spi_priv *priv) +{ + writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.clr); +} + +static inline u32 pic32_spi_rx_fifo_level(struct pic32_spi_priv *priv) +{ + u32 sr = readl(&priv->regs->status.raw); + + return (sr >> PIC32_SPI_STAT_RF_LVL_SHIFT) & PIC32_SPI_STAT_RF_LVL_MASK; +} + +static inline u32 pic32_spi_tx_fifo_level(struct pic32_spi_priv *priv) +{ + u32 sr = readl(&priv->regs->status.raw); + + return (sr >> PIC32_SPI_STAT_TF_LVL_SHIFT) & PIC32_SPI_STAT_TF_LVL_MASK; +} + +/* Return the max entries we can fill into tx fifo */ +static u32 pic32_tx_max(struct pic32_spi_priv *priv, int n_bytes) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (priv->tx_end - priv->tx) / n_bytes; + tx_room = priv->fifo_n_word - pic32_spi_tx_fifo_level(priv); + + rxtx_gap = (priv->rx_end - priv->rx) - (priv->tx_end - priv->tx); + rxtx_gap /= n_bytes; + return min3(tx_left, tx_room, (u32)(priv->fifo_n_word - rxtx_gap)); +} + +/* Return the max entries we should read out of rx fifo */ +static u32 pic32_rx_max(struct pic32_spi_priv *priv, int n_bytes) +{ + u32 rx_left = (priv->rx_end - priv->rx) / n_bytes; + + return min_t(u32, rx_left, pic32_spi_rx_fifo_level(priv)); +} + +#define BUILD_SPI_FIFO_RW(__name, __type, __bwl) \ +static void pic32_spi_rx_##__name(struct pic32_spi_priv *priv) \ +{ \ + __type val; \ + u32 mx = pic32_rx_max(priv, sizeof(__type)); \ + \ + for (; mx; mx--) { \ + val = read##__bwl(&priv->regs->buf.raw); \ + if (priv->rx_end - priv->len) \ + *(__type *)(priv->rx) = val; \ + priv->rx += sizeof(__type); \ + } \ +} \ + \ +static void pic32_spi_tx_##__name(struct pic32_spi_priv *priv) \ +{ \ + __type val; \ + u32 mx = pic32_tx_max(priv, sizeof(__type)); \ + \ + for (; mx ; mx--) { \ + val = (__type) ~0U; \ + if (priv->tx_end - priv->len) \ + val = *(__type *)(priv->tx); \ + write##__bwl(val, &priv->regs->buf.raw); \ + priv->tx += sizeof(__type); \ + } \ +} +BUILD_SPI_FIFO_RW(byte, u8, b); +BUILD_SPI_FIFO_RW(word, u16, w); +BUILD_SPI_FIFO_RW(dword, u32, l); + +static int pic32_spi_set_word_size(struct pic32_spi_priv *priv, + unsigned int wordlen) +{ + u32 bits_per_word; + u32 val; + + switch (wordlen) { + case 8: + priv->rx_fifo = pic32_spi_rx_byte; + priv->tx_fifo = pic32_spi_tx_byte; + bits_per_word = PIC32_SPI_CTRL_BPW_8; + break; + case 16: + priv->rx_fifo = pic32_spi_rx_word; + priv->tx_fifo = pic32_spi_tx_word; + bits_per_word = PIC32_SPI_CTRL_BPW_16; + break; + case 32: + priv->rx_fifo = pic32_spi_rx_dword; + priv->tx_fifo = pic32_spi_tx_dword; + bits_per_word = PIC32_SPI_CTRL_BPW_32; + break; + default: + printf("pic32-spi: unsupported wordlen\n"); + return -EINVAL; + } + + /* set bits-per-word */ + val = readl(&priv->regs->ctrl.raw); + val &= ~(PIC32_SPI_CTRL_BPW_MASK << PIC32_SPI_CTRL_BPW_SHIFT); + val |= bits_per_word << PIC32_SPI_CTRL_BPW_SHIFT; + writel(val, &priv->regs->ctrl.raw); + + /* calculate maximum number of words fifo can hold */ + priv->fifo_n_word = DIV_ROUND_UP(priv->fifo_depth, wordlen / 8); + + return 0; +} + +static int pic32_spi_claim_bus(struct udevice *slave) +{ + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); + + /* enable chip */ + pic32_spi_enable(priv); + + return 0; +} + +static int pic32_spi_release_bus(struct udevice *slave) +{ + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); + + /* disable chip */ + pic32_spi_disable(priv); + + return 0; +} + +static void spi_cs_activate(struct pic32_spi_priv *priv) +{ + if (!dm_gpio_is_valid(&priv->cs_gpio)) + return; + + dm_gpio_set_value(&priv->cs_gpio, 1); +} + +static void spi_cs_deactivate(struct pic32_spi_priv *priv) +{ + if (!dm_gpio_is_valid(&priv->cs_gpio)) + return; + + dm_gpio_set_value(&priv->cs_gpio, 0); +} + +static int pic32_spi_xfer(struct udevice *slave, unsigned int bitlen, + const void *tx_buf, void *rx_buf, + unsigned long flags) +{ + struct dm_spi_slave_platdata *slave_plat; + struct udevice *bus = slave->parent; + struct pic32_spi_priv *priv; + int len = bitlen / 8; + int ret = 0; + ulong tbase; + + priv = dev_get_priv(bus); + slave_plat = dev_get_parent_platdata(slave); + + debug("spi_xfer: bus:%i cs:%i flags:%lx\n", + bus->seq, slave_plat->cs, flags); + debug("msg tx %p, rx %p submitted of %d byte(s)\n", + tx_buf, rx_buf, len); + + /* assert cs */ + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(priv); + + /* set current transfer information */ + priv->tx = tx_buf; + priv->rx = rx_buf; + priv->tx_end = priv->tx + len; + priv->rx_end = priv->rx + len; + priv->len = len; + + /* transact by polling */ + tbase = get_timer(0); + for (;;) { + priv->tx_fifo(priv); + priv->rx_fifo(priv); + + /* received sufficient data */ + if (priv->rx >= priv->rx_end) { + ret = 0; + break; + } + + if (get_timer(tbase) > 5 * CONFIG_SYS_HZ) { + printf("pic32_spi: error, xfer timedout.\n"); + flags |= SPI_XFER_END; + ret = -ETIMEDOUT; + break; + } + } + + /* deassert cs */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(priv); + + return ret; +} + +static int pic32_spi_set_speed(struct udevice *bus, uint speed) +{ + struct pic32_spi_priv *priv = dev_get_priv(bus); + u32 div; + + debug("%s: %s, speed %u\n", __func__, bus->name, speed); + + /* div = [clk_in / (2 * spi_clk)] - 1 */ + div = (priv->clk_rate / 2 / speed) - 1; + div &= PIC32_SPI_BAUD_MASK; + writel(div, &priv->regs->baud.raw); + + priv->speed_hz = speed; + + return 0; +} + +static int pic32_spi_set_mode(struct udevice *bus, uint mode) +{ + struct pic32_spi_priv *priv = dev_get_priv(bus); + u32 val; + + debug("%s: %s, mode %d\n", __func__, bus->name, mode); + + /* set spi-clk mode */ + val = readl(&priv->regs->ctrl.raw); + /* HIGH when idle */ + if (mode & SPI_CPOL) + val |= PIC32_SPI_CTRL_CKP; + else + val &= ~PIC32_SPI_CTRL_CKP; + + /* TX at idle-to-active clk transition */ + if (mode & SPI_CPHA) + val &= ~PIC32_SPI_CTRL_CKE; + else + val |= PIC32_SPI_CTRL_CKE; + + /* RX at end of tx */ + val |= PIC32_SPI_CTRL_SMP; + writel(val, &priv->regs->ctrl.raw); + + priv->mode = mode; + + return 0; +} + +static int pic32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen) +{ + struct pic32_spi_priv *priv = dev_get_priv(slave->parent); + + return pic32_spi_set_word_size(priv, wordlen); +} + +static void pic32_spi_hw_init(struct pic32_spi_priv *priv) +{ + u32 val; + + /* disable module */ + pic32_spi_disable(priv); + + val = readl(&priv->regs->ctrl); + + /* enable enhanced fifo of 128bit deep */ + val |= PIC32_SPI_CTRL_ENHBUF; + priv->fifo_depth = 16; + + /* disable framing mode */ + val &= ~PIC32_SPI_CTRL_FRMEN; + + /* enable master mode */ + val |= PIC32_SPI_CTRL_MSTEN; + + /* select clk source */ + val &= ~PIC32_SPI_CTRL_MCLKSEL; + + /* set manual /CS mode */ + val &= ~PIC32_SPI_CTRL_MSSEN; + + writel(val, &priv->regs->ctrl); + + /* clear rx overflow indicator */ + writel(PIC32_SPI_STAT_RX_OV, &priv->regs->status.clr); +} + +static int pic32_spi_probe(struct udevice *bus) +{ + struct pic32_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_bus *dm_spi = dev_get_uclass_priv(bus); + struct udevice *clkdev; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + debug("%s: %d, bus: %i\n", __func__, __LINE__, bus->seq); + addr = fdtdec_get_addr_size(gd->fdt_blob, bus->of_offset, "reg", &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + if (!priv->regs) + return -EINVAL; + + dm_spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset, + "spi-max-frequency", 250000000); + /* get clock rate */ + ret = clk_get_by_index(bus, 0, &clkdev); + if (ret < 0) { + printf("pic32-spi: error, clk not found\n"); + return ret; + } + priv->clk_rate = clk_get_periph_rate(clkdev, ret); + + /* initialize HW */ + pic32_spi_hw_init(priv); + + /* set word len */ + pic32_spi_set_word_size(priv, SPI_DEFAULT_WORDLEN); + + /* PIC32 SPI controller can automatically drive /CS during transfer + * depending on fifo fill-level. /CS will stay asserted as long as + * TX fifo is non-empty, else will be deasserted confirming completion + * of the ongoing transfer. To avoid this sort of error we will drive + * /CS manually by toggling cs-gpio pins. + */ + ret = gpio_request_by_name_nodev(gd->fdt_blob, bus->of_offset, + "cs-gpios", 0, + &priv->cs_gpio, GPIOD_IS_OUT); + if (ret) { + printf("pic32-spi: error, cs-gpios not found\n"); + return ret; + } + + return 0; +} + +static const struct dm_spi_ops pic32_spi_ops = { + .claim_bus = pic32_spi_claim_bus, + .release_bus = pic32_spi_release_bus, + .xfer = pic32_spi_xfer, + .set_speed = pic32_spi_set_speed, + .set_mode = pic32_spi_set_mode, + .set_wordlen = pic32_spi_set_wordlen, +}; + +static const struct udevice_id pic32_spi_ids[] = { + { .compatible = "microchip,pic32mzda-spi" }, + { } +}; + +U_BOOT_DRIVER(pic32_spi) = { + .name = "pic32_spi", + .id = UCLASS_SPI, + .of_match = pic32_spi_ids, + .ops = &pic32_spi_ops, + .priv_auto_alloc_size = sizeof(struct pic32_spi_priv), + .probe = pic32_spi_probe, +};
This driver implements SPI protocol in master mode to communicate with the SPI device connected on SPI bus. It handles /CS explicitly by controlling respective pin as gpio ('cs-gpios' property in dt node) and uses PIO mode for SPI transaction. It is configurable based on driver-model only. Cc: Jagan Teki <jteki@openedev.com> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com> --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/pic32_spi.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 drivers/spi/pic32_spi.c