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 <jt...@openedev.com> > Signed-off-by: Purna Chandra Mandal <purna.man...@microchip.com> >
Reviewed-by: Daniel Schwierzeck <daniel.schwierz...@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.man...@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, > +}; > -- - Daniel
signature.asc
Description: OpenPGP digital signature
_______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot