diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/Kconfig | 4 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-spi.c | 98 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-spi.h | 13 |
3 files changed, 101 insertions, 14 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3524bec..2946ab4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -236,7 +236,7 @@ config SPI_FSL_CPM depends on FSL_SOC config SPI_FSL_SPI - bool "Freescale SPI controller" + bool "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" depends on OF select SPI_FSL_LIB select SPI_FSL_CPM if FSL_SOC @@ -244,6 +244,8 @@ config SPI_FSL_SPI This enables using the Freescale SPI controllers in master mode. MPC83xx platform uses the controller in cpu mode or CPM/QE mode. MPC8569 uses the controller in QE mode, MPC8610 in cpu mode. + This also enables using the Aeroflex Gaisler GRLIB SPI controller in + master mode. config SPI_FSL_ESPI bool "Freescale eSPI controller" diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 7eff63a..6846459 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -10,6 +10,10 @@ * Copyright (c) 2009 MontaVista Software, Inc. * Author: Anton Vorontsov <avorontsov@ru.mvista.com> * + * GRLIB support: + * Copyright (c) 2012 Aeroflex Gaisler AB. + * Author: Andreas Larsson <andreas@gaisler.com> + * * 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 the * Free Software Foundation; either version 2 of the License, or (at your @@ -40,6 +44,7 @@ #include "spi-fsl-spi.h" #define TYPE_FSL 0 +#define TYPE_GRLIB 1 struct fsl_spi_match_data { int type; @@ -49,11 +54,19 @@ static struct fsl_spi_match_data of_fsl_spi_fsl_config = { .type = TYPE_FSL, }; +static struct fsl_spi_match_data of_fsl_spi_grlib_config = { + .type = TYPE_GRLIB, +}; + static struct of_device_id of_fsl_spi_match[] = { { .compatible = "fsl,spi", .data = &of_fsl_spi_fsl_config, }, + { + .compatible = "aeroflexgaisler,spictrl", + .data = &of_fsl_spi_grlib_config, + }, {} }; MODULE_DEVICE_TABLE(of, of_fsl_spi_match); @@ -141,6 +154,21 @@ static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift, } } +static void fsl_spi_grlib_set_shifts(u32 *rx_shift, u32 *tx_shift, + int bits_per_word, int msb_first) +{ + *rx_shift = 0; + *tx_shift = 0; + if (bits_per_word <= 16) { + if (msb_first) { + *rx_shift = 16; /* LSB in bit 16 */ + *tx_shift = 32 - bits_per_word; /* MSB in bit 31 */ + } else { + *rx_shift = 16 - bits_per_word; /* MSB in bit 15 */ + } + } +} + static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, struct spi_device *spi, struct mpc8xxx_spi *mpc8xxx_spi, @@ -494,6 +522,42 @@ static void fsl_spi_remove(struct mpc8xxx_spi *mspi) fsl_spi_cpm_free(mspi); } +static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base; + u32 slvsel; + u16 cs = spi->chip_select; + + slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); + slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); + mpc8xxx_spi_write_reg(®_base->slvsel, slvsel); +} + +static void fsl_spi_grlib_probe(struct device *dev) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct spi_master *master = dev_get_drvdata(dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base; + int mbits; + u32 capabilities; + + capabilities = mpc8xxx_spi_read_reg(®_base->cap); + + mpc8xxx_spi->set_shifts = fsl_spi_grlib_set_shifts; + mbits = SPCAP_MAXWLEN(capabilities); + if (mbits) + mpc8xxx_spi->max_bits_per_word = mbits + 1; + + master->num_chipselect = 1; /* Allow for an always selected chip */ + if (SPCAP_SSEN(capabilities)) { + master->num_chipselect = SPCAP_SSSZ(capabilities); + mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff); + } + pdata->cs_control = fsl_spi_grlib_cs_control; +} + static struct spi_master * fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { @@ -528,6 +592,15 @@ static struct spi_master * fsl_spi_probe(struct device *dev, if (ret) goto err_cpm_init; + mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); + if (mpc8xxx_spi->reg_base == NULL) { + ret = -ENOMEM; + goto err_ioremap; + } + + if (mpc8xxx_spi->type == TYPE_GRLIB) + fsl_spi_grlib_probe(dev); + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts; @@ -536,12 +609,6 @@ static struct spi_master * fsl_spi_probe(struct device *dev, mpc8xxx_spi->set_shifts(&mpc8xxx_spi->rx_shift, &mpc8xxx_spi->tx_shift, 8, 1); - mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); - if (mpc8xxx_spi->reg_base == NULL) { - ret = -ENOMEM; - goto err_ioremap; - } - /* Register for SPI Interrupt */ ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq, 0, "fsl_spi", mpc8xxx_spi); @@ -706,16 +773,19 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) struct device_node *np = ofdev->dev.of_node; struct spi_master *master; struct resource mem; - int irq; + int irq, type; int ret = -ENOMEM; ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; - ret = of_fsl_spi_get_chipselects(dev); - if (ret) - goto err; + type = fsl_spi_get_type(&ofdev->dev); + if (type == TYPE_FSL) { + ret = of_fsl_spi_get_chipselects(dev); + if (ret) + goto err; + } ret = of_address_to_resource(np, 0, &mem); if (ret) @@ -736,18 +806,22 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) return 0; err: - of_fsl_spi_free_chipselects(dev); + if (type == TYPE_FSL) + of_fsl_spi_free_chipselects(dev); return ret; } static int of_fsl_spi_remove(struct platform_device *ofdev) { + struct spi_master *master = dev_get_drvdata(&ofdev->dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); int ret; ret = mpc8xxx_spi_remove(&ofdev->dev); if (ret) return ret; - of_fsl_spi_free_chipselects(&ofdev->dev); + if (mpc8xxx_spi->type == TYPE_FSL) + of_fsl_spi_free_chipselects(&ofdev->dev); return 0; } diff --git a/drivers/spi/spi-fsl-spi.h b/drivers/spi/spi-fsl-spi.h index 8bd73a4..9a6dae0 100644 --- a/drivers/spi/spi-fsl-spi.h +++ b/drivers/spi/spi-fsl-spi.h @@ -10,6 +10,10 @@ * Copyright (c) 2009 MontaVista Software, Inc. * Author: Anton Vorontsov <avorontsov@ru.mvista.com> * + * GRLIB support: + * Copyright (c) 2012 Aeroflex Gaisler AB. + * Author: Andreas Larsson <andreas@gaisler.com> + * * 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 the * Free Software Foundation; either version 2 of the License, or (at your @@ -21,13 +25,15 @@ /* SPI Controller registers */ struct fsl_spi_reg { - u8 res1[0x20]; + __be32 cap; /* TYPE_GRLIB specific */ + u8 res1[0x1C]; __be32 mode; __be32 event; __be32 mask; __be32 command; __be32 transmit; __be32 receive; + __be32 slvsel; /* TYPE_GRLIB specific */ }; /* SPI Controller mode register definitions */ @@ -43,6 +49,11 @@ struct fsl_spi_reg { #define SPMODE_OP (1 << 14) #define SPMODE_CG(x) ((x) << 7) +/* TYPE_GRLIB SPI Controller capability register definitions */ +#define SPCAP_SSEN(x) (((x) >> 16) & 0x1) +#define SPCAP_SSSZ(x) (((x) >> 24) & 0xff) +#define SPCAP_MAXWLEN(x) (((x) >> 20) & 0xf) + /* * Default for SPI Mode: * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk |