|
@@ -17,152 +17,120 @@
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
+#include <linux/regmap.h>
|
|
|
|
+#include <linux/mfd/syscon.h>
|
|
|
|
+#include <linux/mfd/syscon/clps711x.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/platform_data/spi-clps711x.h>
|
|
#include <linux/platform_data/spi-clps711x.h>
|
|
|
|
|
|
-#include <mach/hardware.h>
|
|
|
|
-
|
|
|
|
#define DRIVER_NAME "spi-clps711x"
|
|
#define DRIVER_NAME "spi-clps711x"
|
|
|
|
|
|
-struct spi_clps711x_data {
|
|
|
|
- struct completion done;
|
|
|
|
|
|
+#define SYNCIO_FRMLEN(x) ((x) << 8)
|
|
|
|
+#define SYNCIO_TXFRMEN (1 << 14)
|
|
|
|
|
|
|
|
+struct spi_clps711x_data {
|
|
|
|
+ void __iomem *syncio;
|
|
|
|
+ struct regmap *syscon;
|
|
|
|
+ struct regmap *syscon1;
|
|
struct clk *spi_clk;
|
|
struct clk *spi_clk;
|
|
- u32 max_speed_hz;
|
|
|
|
|
|
|
|
u8 *tx_buf;
|
|
u8 *tx_buf;
|
|
u8 *rx_buf;
|
|
u8 *rx_buf;
|
|
- int count;
|
|
|
|
|
|
+ unsigned int bpw;
|
|
int len;
|
|
int len;
|
|
-
|
|
|
|
- int chipselect[0];
|
|
|
|
};
|
|
};
|
|
|
|
|
|
static int spi_clps711x_setup(struct spi_device *spi)
|
|
static int spi_clps711x_setup(struct spi_device *spi)
|
|
{
|
|
{
|
|
- struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
|
|
|
|
-
|
|
|
|
/* We are expect that SPI-device is not selected */
|
|
/* We are expect that SPI-device is not selected */
|
|
- gpio_direction_output(hw->chipselect[spi->chip_select],
|
|
|
|
- !(spi->mode & SPI_CS_HIGH));
|
|
|
|
|
|
+ gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void spi_clps711x_setup_mode(struct spi_device *spi)
|
|
|
|
-{
|
|
|
|
- /* Setup edge for transfer */
|
|
|
|
- if (spi->mode & SPI_CPHA)
|
|
|
|
- clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3);
|
|
|
|
- else
|
|
|
|
- clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int spi_clps711x_setup_xfer(struct spi_device *spi,
|
|
|
|
- struct spi_transfer *xfer)
|
|
|
|
|
|
+static void spi_clps711x_setup_xfer(struct spi_device *spi,
|
|
|
|
+ struct spi_transfer *xfer)
|
|
{
|
|
{
|
|
- u32 speed = xfer->speed_hz ? : spi->max_speed_hz;
|
|
|
|
- u8 bpw = xfer->bits_per_word;
|
|
|
|
- struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
|
|
|
|
-
|
|
|
|
- if (bpw != 8) {
|
|
|
|
- dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
|
|
+ struct spi_master *master = spi->master;
|
|
|
|
+ struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
|
|
|
|
|
/* Setup SPI frequency divider */
|
|
/* Setup SPI frequency divider */
|
|
- if (!speed || (speed >= hw->max_speed_hz))
|
|
|
|
- clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
|
|
|
- SYSCON1_ADCKSEL(3), SYSCON1);
|
|
|
|
- else if (speed >= (hw->max_speed_hz / 2))
|
|
|
|
- clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
|
|
|
- SYSCON1_ADCKSEL(2), SYSCON1);
|
|
|
|
- else if (speed >= (hw->max_speed_hz / 8))
|
|
|
|
- clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
|
|
|
- SYSCON1_ADCKSEL(1), SYSCON1);
|
|
|
|
|
|
+ if (xfer->speed_hz >= master->max_speed_hz)
|
|
|
|
+ regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
|
|
|
+ SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(3));
|
|
|
|
+ else if (xfer->speed_hz >= (master->max_speed_hz / 2))
|
|
|
|
+ regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
|
|
|
+ SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(2));
|
|
|
|
+ else if (xfer->speed_hz >= (master->max_speed_hz / 8))
|
|
|
|
+ regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
|
|
|
+ SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(1));
|
|
else
|
|
else
|
|
- clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
|
|
|
- SYSCON1_ADCKSEL(0), SYSCON1);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
|
|
|
+ SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(0));
|
|
}
|
|
}
|
|
|
|
|
|
-static int spi_clps711x_transfer_one_message(struct spi_master *master,
|
|
|
|
- struct spi_message *msg)
|
|
|
|
|
|
+static int spi_clps711x_prepare_message(struct spi_master *master,
|
|
|
|
+ struct spi_message *msg)
|
|
{
|
|
{
|
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
|
- struct spi_transfer *xfer;
|
|
|
|
- int status = 0, cs = hw->chipselect[msg->spi->chip_select];
|
|
|
|
- u32 data;
|
|
|
|
-
|
|
|
|
- spi_clps711x_setup_mode(msg->spi);
|
|
|
|
-
|
|
|
|
- list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
|
|
- if (spi_clps711x_setup_xfer(msg->spi, xfer)) {
|
|
|
|
- status = -EINVAL;
|
|
|
|
- goto out_xfr;
|
|
|
|
- }
|
|
|
|
|
|
+ struct spi_device *spi = msg->spi;
|
|
|
|
|
|
- gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH));
|
|
|
|
-
|
|
|
|
- reinit_completion(&hw->done);
|
|
|
|
-
|
|
|
|
- hw->count = 0;
|
|
|
|
- hw->len = xfer->len;
|
|
|
|
- hw->tx_buf = (u8 *)xfer->tx_buf;
|
|
|
|
- hw->rx_buf = (u8 *)xfer->rx_buf;
|
|
|
|
-
|
|
|
|
- /* Initiate transfer */
|
|
|
|
- data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
|
|
|
|
- clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
|
|
|
|
-
|
|
|
|
- wait_for_completion(&hw->done);
|
|
|
|
|
|
+ /* Setup mode for transfer */
|
|
|
|
+ return regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCKNSEN,
|
|
|
|
+ (spi->mode & SPI_CPHA) ?
|
|
|
|
+ SYSCON3_ADCCKNSEN : 0);
|
|
|
|
+}
|
|
|
|
|
|
- if (xfer->delay_usecs)
|
|
|
|
- udelay(xfer->delay_usecs);
|
|
|
|
|
|
+static int spi_clps711x_transfer_one(struct spi_master *master,
|
|
|
|
+ struct spi_device *spi,
|
|
|
|
+ struct spi_transfer *xfer)
|
|
|
|
+{
|
|
|
|
+ struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
|
|
|
+ u8 data;
|
|
|
|
|
|
- if (xfer->cs_change ||
|
|
|
|
- list_is_last(&xfer->transfer_list, &msg->transfers))
|
|
|
|
- gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH));
|
|
|
|
|
|
+ spi_clps711x_setup_xfer(spi, xfer);
|
|
|
|
|
|
- msg->actual_length += xfer->len;
|
|
|
|
- }
|
|
|
|
|
|
+ hw->len = xfer->len;
|
|
|
|
+ hw->bpw = xfer->bits_per_word;
|
|
|
|
+ hw->tx_buf = (u8 *)xfer->tx_buf;
|
|
|
|
+ hw->rx_buf = (u8 *)xfer->rx_buf;
|
|
|
|
|
|
-out_xfr:
|
|
|
|
- msg->status = status;
|
|
|
|
- spi_finalize_current_message(master);
|
|
|
|
|
|
+ /* Initiate transfer */
|
|
|
|
+ data = hw->tx_buf ? *hw->tx_buf++ : 0;
|
|
|
|
+ writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, hw->syncio);
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
|
|
static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
|
|
{
|
|
{
|
|
- struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id;
|
|
|
|
- u32 data;
|
|
|
|
|
|
+ struct spi_master *master = dev_id;
|
|
|
|
+ struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
|
|
|
+ u8 data;
|
|
|
|
|
|
/* Handle RX */
|
|
/* Handle RX */
|
|
- data = clps_readb(SYNCIO);
|
|
|
|
|
|
+ data = readb(hw->syncio);
|
|
if (hw->rx_buf)
|
|
if (hw->rx_buf)
|
|
- hw->rx_buf[hw->count] = (u8)data;
|
|
|
|
-
|
|
|
|
- hw->count++;
|
|
|
|
|
|
+ *hw->rx_buf++ = data;
|
|
|
|
|
|
/* Handle TX */
|
|
/* Handle TX */
|
|
- if (hw->count < hw->len) {
|
|
|
|
- data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
|
|
|
|
- clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
|
|
|
|
|
|
+ if (--hw->len > 0) {
|
|
|
|
+ data = hw->tx_buf ? *hw->tx_buf++ : 0;
|
|
|
|
+ writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN,
|
|
|
|
+ hw->syncio);
|
|
} else
|
|
} else
|
|
- complete(&hw->done);
|
|
|
|
|
|
+ spi_finalize_current_transfer(master);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
|
|
|
|
static int spi_clps711x_probe(struct platform_device *pdev)
|
|
static int spi_clps711x_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
- int i, ret;
|
|
|
|
- struct spi_master *master;
|
|
|
|
struct spi_clps711x_data *hw;
|
|
struct spi_clps711x_data *hw;
|
|
struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
|
|
struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
|
|
|
|
+ struct spi_master *master;
|
|
|
|
+ struct resource *res;
|
|
|
|
+ int i, irq, ret;
|
|
|
|
|
|
if (!pdata) {
|
|
if (!pdata) {
|
|
dev_err(&pdev->dev, "No platform data supplied\n");
|
|
dev_err(&pdev->dev, "No platform data supplied\n");
|
|
@@ -174,33 +142,37 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- master = spi_alloc_master(&pdev->dev,
|
|
|
|
- sizeof(struct spi_clps711x_data) +
|
|
|
|
- sizeof(int) * pdata->num_chipselect);
|
|
|
|
- if (!master) {
|
|
|
|
- dev_err(&pdev->dev, "SPI allocating memory error\n");
|
|
|
|
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
|
|
+ if (irq < 0)
|
|
|
|
+ return irq;
|
|
|
|
+
|
|
|
|
+ master = spi_alloc_master(&pdev->dev, sizeof(*hw));
|
|
|
|
+ if (!master)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ master->cs_gpios = devm_kzalloc(&pdev->dev, sizeof(int) *
|
|
|
|
+ pdata->num_chipselect, GFP_KERNEL);
|
|
|
|
+ if (!master->cs_gpios) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto err_out;
|
|
}
|
|
}
|
|
|
|
|
|
master->bus_num = pdev->id;
|
|
master->bus_num = pdev->id;
|
|
master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
|
|
master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
|
|
- master->bits_per_word_mask = SPI_BPW_MASK(8);
|
|
|
|
|
|
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8);
|
|
master->num_chipselect = pdata->num_chipselect;
|
|
master->num_chipselect = pdata->num_chipselect;
|
|
master->setup = spi_clps711x_setup;
|
|
master->setup = spi_clps711x_setup;
|
|
- master->transfer_one_message = spi_clps711x_transfer_one_message;
|
|
|
|
|
|
+ master->prepare_message = spi_clps711x_prepare_message;
|
|
|
|
+ master->transfer_one = spi_clps711x_transfer_one;
|
|
|
|
|
|
hw = spi_master_get_devdata(master);
|
|
hw = spi_master_get_devdata(master);
|
|
|
|
|
|
for (i = 0; i < master->num_chipselect; i++) {
|
|
for (i = 0; i < master->num_chipselect; i++) {
|
|
- hw->chipselect[i] = pdata->chipselect[i];
|
|
|
|
- if (!gpio_is_valid(hw->chipselect[i])) {
|
|
|
|
- dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i);
|
|
|
|
- ret = -EINVAL;
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
- if (devm_gpio_request(&pdev->dev, hw->chipselect[i], NULL)) {
|
|
|
|
|
|
+ master->cs_gpios[i] = pdata->chipselect[i];
|
|
|
|
+ ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i],
|
|
|
|
+ DRIVER_NAME);
|
|
|
|
+ if (ret) {
|
|
dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
|
|
dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
|
|
- ret = -EINVAL;
|
|
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -211,29 +183,45 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|
ret = PTR_ERR(hw->spi_clk);
|
|
ret = PTR_ERR(hw->spi_clk);
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
- hw->max_speed_hz = clk_get_rate(hw->spi_clk);
|
|
|
|
|
|
+ master->max_speed_hz = clk_get_rate(hw->spi_clk);
|
|
|
|
|
|
- init_completion(&hw->done);
|
|
|
|
platform_set_drvdata(pdev, master);
|
|
platform_set_drvdata(pdev, master);
|
|
|
|
|
|
|
|
+ hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
|
|
|
+ if (IS_ERR(hw->syscon)) {
|
|
|
|
+ ret = PTR_ERR(hw->syscon);
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ hw->syscon1 = syscon_regmap_lookup_by_pdevname("syscon.1");
|
|
|
|
+ if (IS_ERR(hw->syscon1)) {
|
|
|
|
+ ret = PTR_ERR(hw->syscon1);
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
+ hw->syncio = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
+ if (IS_ERR(hw->syncio)) {
|
|
|
|
+ ret = PTR_ERR(hw->syncio);
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Disable extended mode due hardware problems */
|
|
/* Disable extended mode due hardware problems */
|
|
- clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3);
|
|
|
|
|
|
+ regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0);
|
|
|
|
|
|
/* Clear possible pending interrupt */
|
|
/* Clear possible pending interrupt */
|
|
- clps_readl(SYNCIO);
|
|
|
|
|
|
+ readl(hw->syncio);
|
|
|
|
|
|
- ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0,
|
|
|
|
- dev_name(&pdev->dev), hw);
|
|
|
|
- if (ret) {
|
|
|
|
- dev_err(&pdev->dev, "Can't request IRQ\n");
|
|
|
|
|
|
+ ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0,
|
|
|
|
+ dev_name(&pdev->dev), master);
|
|
|
|
+ if (ret)
|
|
goto err_out;
|
|
goto err_out;
|
|
- }
|
|
|
|
|
|
|
|
ret = devm_spi_register_master(&pdev->dev, master);
|
|
ret = devm_spi_register_master(&pdev->dev, master);
|
|
if (!ret) {
|
|
if (!ret) {
|
|
dev_info(&pdev->dev,
|
|
dev_info(&pdev->dev,
|
|
"SPI bus driver initialized. Master clock %u Hz\n",
|
|
"SPI bus driver initialized. Master clock %u Hz\n",
|
|
- hw->max_speed_hz);
|
|
|
|
|
|
+ master->max_speed_hz);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|