/* * Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "efm32-spi" #define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask) #define REG_CTRL 0x00 #define REG_CTRL_SYNC 0x0001 #define REG_CTRL_CLKPOL 0x0100 #define REG_CTRL_CLKPHA 0x0200 #define REG_CTRL_MSBF 0x0400 #define REG_CTRL_TXBIL 0x1000 #define REG_FRAME 0x04 #define REG_FRAME_DATABITS__MASK 0x000f #define REG_FRAME_DATABITS(n) ((n) - 3) #define REG_CMD 0x0c #define REG_CMD_RXEN 0x0001 #define REG_CMD_RXDIS 0x0002 #define REG_CMD_TXEN 0x0004 #define REG_CMD_TXDIS 0x0008 #define REG_CMD_MASTEREN 0x0010 #define REG_STATUS 0x10 #define REG_STATUS_TXENS 0x0002 #define REG_STATUS_TXC 0x0020 #define REG_STATUS_TXBL 0x0040 #define REG_STATUS_RXDATAV 0x0080 #define REG_CLKDIV 0x14 #define REG_RXDATAX 0x18 #define REG_RXDATAX_RXDATA__MASK 0x01ff #define REG_RXDATAX_PERR 0x4000 #define REG_RXDATAX_FERR 0x8000 #define REG_TXDATA 0x34 #define REG_IF 0x40 #define REG_IF_TXBL 0x0002 #define REG_IF_RXDATAV 0x0004 #define REG_IFS 0x44 #define REG_IFC 0x48 #define REG_IEN 0x4c #define REG_ROUTE 0x54 #define REG_ROUTE_RXPEN 0x0001 #define REG_ROUTE_TXPEN 0x0002 #define REG_ROUTE_CLKPEN 0x0008 #define REG_ROUTE_LOCATION__MASK 0x0700 #define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n)) struct efm32_spi_ddata { struct spi_bitbang bitbang; spinlock_t lock; struct clk *clk; void __iomem *base; unsigned int rxirq, txirq; struct efm32_spi_pdata pdata; /* irq data */ struct completion done; const u8 *tx_buf; u8 *rx_buf; unsigned tx_len, rx_len; /* chip selects */ unsigned csgpio[]; }; #define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev)) #define efm32_spi_vdbg(ddata, format, arg...) \ dev_vdbg(ddata_to_dev(ddata), format, ##arg) static void efm32_spi_write32(struct efm32_spi_ddata *ddata, u32 value, unsigned offset) { writel_relaxed(value, ddata->base + offset); } static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset) { return readl_relaxed(ddata->base + offset); } static void efm32_spi_chipselect(struct spi_device *spi, int is_on) { struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE); gpio_set_value(ddata->csgpio[spi->chip_select], value); } static int efm32_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); unsigned bpw = t->bits_per_word ?: spi->bits_per_word; unsigned speed = t->speed_hz ?: spi->max_speed_hz; unsigned long clkfreq = clk_get_rate(ddata->clk); u32 clkdiv; efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF | (spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) | (spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL); efm32_spi_write32(ddata, REG_FRAME_DATABITS(bpw), REG_FRAME); if (2 * speed >= clkfreq) clkdiv = 0; else clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4); if (clkdiv > (1U << 21)) return -EINVAL; efm32_spi_write32(ddata, clkdiv, REG_CLKDIV); efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD); efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD); return 0; } static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata) { u8 val = 0; if (ddata->tx_buf) { val = *ddata->tx_buf; ddata->tx_buf++; } ddata->tx_len--; efm32_