Quelle spi-mpc512x-psc.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MPC512x PSC in SPI mode driver .
*
* Copyright ( C ) 2007 , 2008 Freescale Semiconductor Inc .
* Original port from 52 xx driver :
* Hongjun Chen < hong - jun . chen @ freescale . com >
*
* Fork of mpc52xx_psc_spi . c :
* Copyright ( C ) 2006 TOPTICA Photonics AG . , Dragos Carp
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <asm /mpc52xx_psc.h>
enum {
TYPE_MPC5121,
TYPE_MPC5125,
};
/*
* This macro abstracts the differences in the PSC register layout between
* MPC5121 ( which uses a struct mpc52xx_psc ) and MPC5125 ( using mpc5125_psc ) .
*/
#define psc_addr(mps, regname) ({ \
void *__ret = NULL; \
switch (mps->type) { \
case TYPE_MPC5121: { \
struct mpc52xx_psc __iomem *psc = mps->psc; \
__ret = &psc->regname; \
}; \
break ; \
case TYPE_MPC5125: { \
struct mpc5125_psc __iomem *psc = mps->psc; \
__ret = &psc->regname; \
}; \
break ; \
} \
__ret; })
struct mpc512x_psc_spi {
/* driver internal data */
int type;
void __iomem *psc;
struct mpc512x_psc_fifo __iomem *fifo;
int irq;
u8 bits_per_word;
u32 mclk_rate;
struct completion txisrdone;
};
/* controller state */
struct mpc512x_psc_spi_cs {
int bits_per_word;
int speed_hz;
};
/* set clock freq, clock ramp, bits per work
* if t is NULL then reset the values to the default values
*/
static int mpc512x_psc_spi_transfer_setup(struct spi_device *spi,
struct spi_transfer *t)
{
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
cs->speed_hz = (t && t->speed_hz)
? t->speed_hz : spi->max_speed_hz;
cs->bits_per_word = (t && t->bits_per_word)
? t->bits_per_word : spi->bits_per_word;
cs->bits_per_word = ((cs->bits_per_word + 7 ) / 8 ) * 8 ;
return 0 ;
}
static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
{
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
struct mpc512x_psc_spi *mps = spi_controller_get_devdata(spi->controller);
u32 sicr;
u32 ccr;
int speed;
u16 bclkdiv;
sicr = in_be32(psc_addr(mps, sicr));
/* Set clock phase and polarity */
if (spi->mode & SPI_CPHA)
sicr |= 0 x00001000;
else
sicr &= ~0 x00001000;
if (spi->mode & SPI_CPOL)
sicr |= 0 x00002000;
else
sicr &= ~0 x00002000;
if (spi->mode & SPI_LSB_FIRST)
sicr |= 0 x10000000;
else
sicr &= ~0 x10000000;
out_be32(psc_addr(mps, sicr), sicr);
ccr = in_be32(psc_addr(mps, ccr));
ccr &= 0 xFF000000;
speed = cs->speed_hz;
if (!speed)
speed = 1000000 ; /* default 1MHz */
bclkdiv = (mps->mclk_rate / speed) - 1 ;
ccr |= (((bclkdiv & 0 xff) << 16 ) | (((bclkdiv >> 8 ) & 0 xff) << 8 ));
out_be32(psc_addr(mps, ccr), ccr);
mps->bits_per_word = cs->bits_per_word;
if (spi_get_csgpiod(spi, 0 )) {
/* gpiolib will deal with the inversion */
gpiod_set_value(spi_get_csgpiod(spi, 0 ), 1 );
}
}
static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi)
{
if (spi_get_csgpiod(spi, 0 )) {
/* gpiolib will deal with the inversion */
gpiod_set_value(spi_get_csgpiod(spi, 0 ), 0 );
}
}
/* extract and scale size field in txsz or rxsz */
#define MPC512x_PSC_FIFO_SZ(sz) ((sz & 0 x7ff) << 2 );
#define EOFBYTE 1
static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
struct spi_transfer *t)
{
struct mpc512x_psc_spi *mps = spi_controller_get_devdata(spi->controller);
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
size_t tx_len = t->len;
size_t rx_len = t->len;
u8 *tx_buf = (u8 *)t->tx_buf;
u8 *rx_buf = (u8 *)t->rx_buf;
if (!tx_buf && !rx_buf && t->len)
return -EINVAL;
while (rx_len || tx_len) {
size_t txcount;
u8 data;
size_t fifosz;
size_t rxcount;
int rxtries;
/*
* send the TX bytes in as large a chunk as possible
* but neither exceed the TX nor the RX FIFOs
*/
fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->txsz));
txcount = min(fifosz, tx_len);
fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->rxsz));
fifosz -= in_be32(&fifo->rxcnt) + 1 ;
txcount = min(fifosz, txcount);
if (txcount) {
/* fill the TX FIFO */
while (txcount-- > 0 ) {
data = tx_buf ? *tx_buf++ : 0 ;
if (tx_len == EOFBYTE && t->cs_change)
setbits32(&fifo->txcmd,
MPC512x_PSC_FIFO_EOF);
out_8(&fifo->txdata_8, data);
tx_len--;
}
/* have the ISR trigger when the TX FIFO is empty */
reinit_completion(&mps->txisrdone);
out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY);
wait_for_completion(&mps->txisrdone);
}
/*
* consume as much RX data as the FIFO holds , while we
* iterate over the transfer ' s TX data length
*
* only insist in draining all the remaining RX bytes
* when the TX bytes were exhausted ( that ' s at the very
* end of this transfer , not when still iterating over
* the transfer ' s chunks )
*/
rxtries = 50 ;
do {
/*
* grab whatever was in the FIFO when we started
* looking , don ' t bother fetching what was added to
* the FIFO while we read from it - - we ' ll return
* here eventually and prefer sending out remaining
* TX data
*/
fifosz = in_be32(&fifo->rxcnt);
rxcount = min(fifosz, rx_len);
while (rxcount-- > 0 ) {
data = in_8(&fifo->rxdata_8);
if (rx_buf)
*rx_buf++ = data;
rx_len--;
}
/*
* come back later if there still is TX data to send ,
* bail out of the RX drain loop if all of the TX data
* was sent and all of the RX data was received ( i . e .
* when the transmission has completed )
*/
if (tx_len)
break ;
if (!rx_len)
break ;
/*
* TX data transmission has completed while RX data
* is still pending - - that ' s a transient situation
* which depends on wire speed and specific
* hardware implementation details ( buffering ) yet
* should resolve very quickly
*
* just yield for a moment to not hog the CPU for
* too long when running SPI at low speed
*
* the timeout range is rather arbitrary and tries
* to balance throughput against system load ; the
* chosen values result in a minimal timeout of 50
* times 10 us and thus work at speeds as low as
* some 20 kbps , while the maximum timeout at the
* transfer ' s end could be 5 ms _ if_ nothing else
* ticks in the system _ and_ RX data still wasn ' t
* received , which only occurs in situations that
* are exceptional ; removing the unpredictability
* of the timeout either decreases throughput
* ( longer timeouts ) , or puts more load on the
* system ( fixed short timeouts ) or requires the
* use of a timeout API instead of a counter and an
* unknown inner delay
*/
usleep_range(10 , 100 );
} while (--rxtries > 0 );
if (!tx_len && rx_len && !rxtries) {
/*
* not enough RX bytes even after several retries
* and the resulting rather long timeout ?
*/
rxcount = in_be32(&fifo->rxcnt);
dev_warn(&spi->dev,
"short xfer, missing %zd RX bytes, FIFO level %zd\n" ,
rx_len, rxcount);
}
/*
* drain and drop RX data which " should not be there " in
* the first place , for undisturbed transmission this turns
* into a NOP ( except for the FIFO level fetch )
*/
if (!tx_len && !rx_len) {
while (in_be32(&fifo->rxcnt))
in_8(&fifo->rxdata_8);
}
}
return 0 ;
}
static int mpc512x_psc_spi_msg_xfer(struct spi_controller *host,
struct spi_message *m)
{
struct spi_device *spi;
unsigned cs_change;
int status;
struct spi_transfer *t;
spi = m->spi;
cs_change = 1 ;
status = 0 ;
list_for_each_entry(t, &m->transfers, transfer_list) {
status = mpc512x_psc_spi_transfer_setup(spi, t);
if (status < 0 )
break ;
if (cs_change)
mpc512x_psc_spi_activate_cs(spi);
cs_change = t->cs_change;
status = mpc512x_psc_spi_transfer_rxtx(spi, t);
if (status)
break ;
m->actual_length += t->len;
spi_transfer_delay_exec(t);
if (cs_change)
mpc512x_psc_spi_deactivate_cs(spi);
}
m->status = status;
if (m->complete)
m->complete(m->context);
if (status || !cs_change)
mpc512x_psc_spi_deactivate_cs(spi);
mpc512x_psc_spi_transfer_setup(spi, NULL);
spi_finalize_current_message(host);
return status;
}
static int mpc512x_psc_spi_prep_xfer_hw(struct spi_controller *host)
{
struct mpc512x_psc_spi *mps = spi_controller_get_devdata(host);
dev_dbg(&host->dev, "%s()\n" , __func__);
/* Zero MR2 */
in_8(psc_addr(mps, mr2));
out_8(psc_addr(mps, mr2), 0 x0);
/* enable transmitter/receiver */
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
return 0 ;
}
static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_controller *host)
{
struct mpc512x_psc_spi *mps = spi_controller_get_devdata(host);
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
dev_dbg(&host->dev, "%s()\n" , __func__);
/* disable transmitter/receiver and fifo interrupt */
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
out_be32(&fifo->tximr, 0 );
return 0 ;
}
static int mpc512x_psc_spi_setup(struct spi_device *spi)
{
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
if (spi->bits_per_word % 8 )
return -EINVAL;
if (!cs) {
cs = kzalloc(sizeof (*cs), GFP_KERNEL);
if (!cs)
return -ENOMEM;
spi->controller_state = cs;
}
cs->bits_per_word = spi->bits_per_word;
cs->speed_hz = spi->max_speed_hz;
return 0 ;
}
static void mpc512x_psc_spi_cleanup(struct spi_device *spi)
{
kfree(spi->controller_state);
}
static int mpc512x_psc_spi_port_config(struct spi_controller *host,
struct mpc512x_psc_spi *mps)
{
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
u32 sicr;
u32 ccr;
int speed;
u16 bclkdiv;
/* Reset the PSC into a known state */
out_8(psc_addr(mps, command), MPC52xx_PSC_RST_RX);
out_8(psc_addr(mps, command), MPC52xx_PSC_RST_TX);
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
/* Disable psc interrupts all useful interrupts are in fifo */
out_be16(psc_addr(mps, isr_imr.imr), 0 );
/* Disable fifo interrupts, will be enabled later */
out_be32(&fifo->tximr, 0 );
out_be32(&fifo->rximr, 0 );
/* Setup fifo slice address and size */
/*out_be32(&fifo->txsz, 0x0fe00004);*/
/*out_be32(&fifo->rxsz, 0x0ff00004);*/
sicr = 0 x01000000 | /* SIM = 0001 -- 8 bit */
0 x00800000 | /* GenClk = 1 -- internal clk */
0 x00008000 | /* SPI = 1 */
0 x00004000 | /* MSTR = 1 -- SPI host */
0 x00000800; /* UseEOF = 1 -- SS low until EOF */
out_be32(psc_addr(mps, sicr), sicr);
ccr = in_be32(psc_addr(mps, ccr));
ccr &= 0 xFF000000;
speed = 1000000 ; /* default 1MHz */
bclkdiv = (mps->mclk_rate / speed) - 1 ;
ccr |= (((bclkdiv & 0 xff) << 16 ) | (((bclkdiv >> 8 ) & 0 xff) << 8 ));
out_be32(psc_addr(mps, ccr), ccr);
/* Set 2ms DTL delay */
out_8(psc_addr(mps, ctur), 0 x00);
out_8(psc_addr(mps, ctlr), 0 x82);
/* we don't use the alarms */
out_be32(&fifo->rxalarm, 0 xfff);
out_be32(&fifo->txalarm, 0 );
/* Enable FIFO slices for Rx/Tx */
out_be32(&fifo->rxcmd,
MPC512x_PSC_FIFO_ENABLE_SLICE | MPC512x_PSC_FIFO_ENABLE_DMA);
out_be32(&fifo->txcmd,
MPC512x_PSC_FIFO_ENABLE_SLICE | MPC512x_PSC_FIFO_ENABLE_DMA);
mps->bits_per_word = 8 ;
return 0 ;
}
static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
{
struct mpc512x_psc_spi *mps = (struct mpc512x_psc_spi *)dev_id;
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
/* clear interrupt and wake up the rx/tx routine */
if (in_be32(&fifo->txisr) &
in_be32(&fifo->tximr) & MPC512x_PSC_FIFO_EMPTY) {
out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
out_be32(&fifo->tximr, 0 );
complete(&mps->txisrdone);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int mpc512x_psc_spi_of_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mpc512x_psc_spi *mps;
struct spi_controller *host;
int ret;
void *tempp;
struct clk *clk;
host = devm_spi_alloc_host(dev, sizeof (*mps));
if (host == NULL)
return -ENOMEM;
dev_set_drvdata(dev, host);
mps = spi_controller_get_devdata(host);
mps->type = (int )device_get_match_data(dev);
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
host->setup = mpc512x_psc_spi_setup;
host->prepare_transfer_hardware = mpc512x_psc_spi_prep_xfer_hw;
host->transfer_one_message = mpc512x_psc_spi_msg_xfer;
host->unprepare_transfer_hardware = mpc512x_psc_spi_unprep_xfer_hw;
host->use_gpio_descriptors = true ;
host->cleanup = mpc512x_psc_spi_cleanup;
device_set_node(&host->dev, dev_fwnode(dev));
tempp = devm_platform_get_and_ioremap_resource(pdev, 0 , NULL);
if (IS_ERR(tempp))
return dev_err_probe(dev, PTR_ERR(tempp), "could not ioremap I/O port range\n" );
mps->psc = tempp;
mps->fifo =
(struct mpc512x_psc_fifo *)(tempp + sizeof (struct mpc52xx_psc));
mps->irq = platform_get_irq(pdev, 0 );
if (mps->irq < 0 )
return mps->irq;
ret = devm_request_irq(dev, mps->irq, mpc512x_psc_spi_isr, IRQF_SHARED,
"mpc512x-psc-spi" , mps);
if (ret)
return ret;
init_completion(&mps->txisrdone);
clk = devm_clk_get_enabled(dev, "mclk" );
if (IS_ERR(clk))
return PTR_ERR(clk);
mps->mclk_rate = clk_get_rate(clk);
clk = devm_clk_get_enabled(dev, "ipg" );
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = mpc512x_psc_spi_port_config(host, mps);
if (ret < 0 )
return ret;
return devm_spi_register_controller(dev, host);
}
static const struct of_device_id mpc512x_psc_spi_of_match[] = {
{ .compatible = "fsl,mpc5121-psc-spi" , .data = (void *)TYPE_MPC5121 },
{ .compatible = "fsl,mpc5125-psc-spi" , .data = (void *)TYPE_MPC5125 },
{},
};
MODULE_DEVICE_TABLE(of, mpc512x_psc_spi_of_match);
static struct platform_driver mpc512x_psc_spi_of_driver = {
.probe = mpc512x_psc_spi_of_probe,
.driver = {
.name = "mpc512x-psc-spi" ,
.of_match_table = mpc512x_psc_spi_of_match,
},
};
module_platform_driver(mpc512x_psc_spi_of_driver);
MODULE_AUTHOR("John Rigby" );
MODULE_DESCRIPTION("MPC512x PSC SPI Driver" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=93 H=88 G=90
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691