diff options
Diffstat (limited to 'drivers/spi/spi-clps711x.c')
| -rw-r--r-- | drivers/spi/spi-clps711x.c | 260 | 
1 files changed, 112 insertions, 148 deletions
diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 5655acf55bf..4cd62f63654 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -1,7 +1,7 @@  /*   *  CLPS711X SPI bus driver   * - *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + *  Copyright (C) 2012-2014 Alexander Shiyan <shc_work@mail.ru>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -11,158 +11,125 @@  #include <linux/io.h>  #include <linux/clk.h> -#include <linux/init.h>  #include <linux/gpio.h>  #include <linux/delay.h>  #include <linux/module.h>  #include <linux/interrupt.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/platform_data/spi-clps711x.h> -#include <mach/hardware.h> -  #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; -	u32			max_speed_hz;  	u8			*tx_buf;  	u8			*rx_buf; -	int			count; +	unsigned int		bpw;  	int			len; - -	int			chipselect[0];  };  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 */ -	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;  } -static void spi_clps711x_setup_mode(struct spi_device *spi) +static void spi_clps711x_setup_xfer(struct spi_device *spi, +				    struct spi_transfer *xfer)  { -	/* 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) -{ -	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 */ -	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 -		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_transfer *xfer; -	int status = 0, cs = hw->chipselect[msg->spi->chip_select]; -	u32 data; +	struct spi_device *spi = msg->spi; -	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; -		} - -		gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH)); - -		INIT_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)  { -	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 */ -	data = clps_readb(SYNCIO); +	data = readb(hw->syncio);  	if (hw->rx_buf) -		hw->rx_buf[hw->count] = (u8)data; - -	hw->count++; +		*hw->rx_buf++ = data;  	/* 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 -		complete(&hw->done); +		spi_finalize_current_transfer(master);  	return IRQ_HANDLED;  }  static int spi_clps711x_probe(struct platform_device *pdev)  { -	int i, ret; -	struct spi_master *master;  	struct spi_clps711x_data *hw;  	struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev); +	struct spi_master *master; +	struct resource *res; +	int i, irq, ret;  	if (!pdata) {  		dev_err(&pdev->dev, "No platform data supplied\n"); @@ -174,33 +141,37 @@ static int spi_clps711x_probe(struct platform_device *pdev)  		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; + +	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->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->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);  	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 (gpio_request(hw->chipselect[i], DRIVER_NAME)) { +		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); -			ret = -EINVAL;  			goto err_out;  		}  	} @@ -211,73 +182,66 @@ static int spi_clps711x_probe(struct platform_device *pdev)  		ret = PTR_ERR(hw->spi_clk);  		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); +	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 */ -	clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3); +	regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0);  	/* Clear possible pending interrupt */ -	clps_readl(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"); -		clk_put(hw->spi_clk); -		goto clk_out; -	} +	readl(hw->syncio); -	ret = spi_register_master(master); +	ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0, +			       dev_name(&pdev->dev), master); +	if (ret) +		goto err_out; + +	ret = devm_spi_register_master(&pdev->dev, master);  	if (!ret) {  		dev_info(&pdev->dev,  			 "SPI bus driver initialized. Master clock %u Hz\n", -			 hw->max_speed_hz); +			 master->max_speed_hz);  		return 0;  	}  	dev_err(&pdev->dev, "Failed to register master\n"); -clk_out:  err_out: -	while (--i >= 0) -		if (gpio_is_valid(hw->chipselect[i])) -			gpio_free(hw->chipselect[i]); -  	spi_master_put(master); -	kfree(master);  	return ret;  } -static int spi_clps711x_remove(struct platform_device *pdev) -{ -	int i; -	struct spi_master *master = platform_get_drvdata(pdev); -	struct spi_clps711x_data *hw = spi_master_get_devdata(master); - -	for (i = 0; i < master->num_chipselect; i++) -		if (gpio_is_valid(hw->chipselect[i])) -			gpio_free(hw->chipselect[i]); - -	spi_unregister_master(master); -	kfree(master); - -	return 0; -} -  static struct platform_driver clps711x_spi_driver = {  	.driver	= {  		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE,  	},  	.probe	= spi_clps711x_probe, -	.remove	= spi_clps711x_remove,  };  module_platform_driver(clps711x_spi_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");  MODULE_DESCRIPTION("CLPS711X SPI bus driver"); +MODULE_ALIAS("platform:" DRIVER_NAME);  | 
