Release 4.12 drivers/spi/spi-mpc512x-psc.c
  
  
  
/*
 * MPC512x PSC in SPI mode driver.
 *
 * Copyright (C) 2007,2008 Freescale Semiconductor Inc.
 * Original port from 52xx driver:
 *      Hongjun Chen <hong-jun.chen@freescale.com>
 *
 * Fork of mpc52xx_psc_spi.c:
 *      Copyright (C) 2006 TOPTICA Photonics AG., Dragos Carp
 *
 * 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
 * option) any later version.
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/fsl_devices.h>
#include <linux/gpio.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 {
	
void (*cs_control)(struct spi_device *spi, bool on);
	/* driver internal data */
	
int type;
	
void __iomem *psc;
	
struct mpc512x_psc_fifo __iomem *fifo;
	
unsigned int irq;
	
u8 bits_per_word;
	
struct clk *clk_mclk;
	
struct clk *clk_ipg;
	
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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 86 | 100.00% | 1 | 100.00% | 
| Total | 86 | 100.00% | 1 | 100.00% | 
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_master_get_devdata(spi->master);
	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 |= 0x00001000;
	else
		sicr &= ~0x00001000;
	if (spi->mode & SPI_CPOL)
		sicr |= 0x00002000;
	else
		sicr &= ~0x00002000;
	if (spi->mode & SPI_LSB_FIRST)
		sicr |= 0x10000000;
	else
		sicr &= ~0x10000000;
	out_be32(psc_addr(mps, sicr), sicr);
	ccr = in_be32(psc_addr(mps, ccr));
	ccr &= 0xFF000000;
	speed = cs->speed_hz;
	if (!speed)
		speed = 1000000;	/* default 1MHz */
	bclkdiv = (mps->mclk_rate / speed) - 1;
	ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
	out_be32(psc_addr(mps, ccr), ccr);
	mps->bits_per_word = cs->bits_per_word;
	if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
		mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 212 | 85.83% | 2 | 50.00% | 
| Uwe Kleine-König | 20 | 8.10% | 1 | 25.00% | 
| Gerhard Sittig | 15 | 6.07% | 1 | 25.00% | 
| Total | 247 | 100.00% | 4 | 100.00% | 
static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi)
{
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
	if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
		mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 55 | 100.00% | 2 | 100.00% | 
| Total | 55 | 100.00% | 2 | 100.00% | 
/* extract and scale size field in txsz or rxsz */
#define MPC512x_PSC_FIFO_SZ(sz) ((sz & 0x7ff) << 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_master_get_devdata(spi->master);
	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 10us and thus work at speeds as low as
                         * some 20kbps, while the maximum timeout at the
                         * transfer's end could be 5ms _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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 261 | 61.41% | 2 | 40.00% | 
| Gerhard Sittig | 162 | 38.12% | 2 | 40.00% | 
| Wolfram Sang | 2 | 0.47% | 1 | 20.00% | 
| Total | 425 | 100.00% | 5 | 100.00% | 
static int mpc512x_psc_spi_msg_xfer(struct spi_master *master,
				    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;
		if (t->delay_usecs)
			udelay(t->delay_usecs);
		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(master);
	return status;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 156 | 87.15% | 1 | 33.33% | 
| Gerhard Sittig | 17 | 9.50% | 1 | 33.33% | 
| Axel Lin | 6 | 3.35% | 1 | 33.33% | 
| Total | 179 | 100.00% | 3 | 100.00% | 
static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master)
{
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
	dev_dbg(&master->dev, "%s()\n", __func__);
	/* Zero MR2 */
	in_8(psc_addr(mps, mr2));
	out_8(psc_addr(mps, mr2), 0x0);
	/* enable transmitter/receiver */
	out_8(psc_addr(mps, command), MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Gerhard Sittig | 52 | 70.27% | 1 | 33.33% | 
| Uwe Kleine-König | 17 | 22.97% | 1 | 33.33% | 
| Anatolij Gustschin | 5 | 6.76% | 1 | 33.33% | 
| Total | 74 | 100.00% | 3 | 100.00% | 
static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master)
{
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
	struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
	dev_dbg(&master->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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Gerhard Sittig | 48 | 67.61% | 1 | 33.33% | 
| Anatolij Gustschin | 18 | 25.35% | 1 | 33.33% | 
| Uwe Kleine-König | 5 | 7.04% | 1 | 33.33% | 
| Total | 71 | 100.00% | 3 | 100.00% | 
static int mpc512x_psc_spi_setup(struct spi_device *spi)
{
	struct mpc512x_psc_spi_cs *cs = spi->controller_state;
	int ret;
	if (spi->bits_per_word % 8)
		return -EINVAL;
	if (!cs) {
		cs = kzalloc(sizeof *cs, GFP_KERNEL);
		if (!cs)
			return -ENOMEM;
		if (gpio_is_valid(spi->cs_gpio)) {
			ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
			if (ret) {
				dev_err(&spi->dev, "can't get CS gpio: %d\n",
					ret);
				kfree(cs);
				return ret;
			}
			gpio_direction_output(spi->cs_gpio,
					spi->mode & SPI_CS_HIGH ? 0 : 1);
		}
		spi->controller_state = cs;
	}
	cs->bits_per_word = spi->bits_per_word;
	cs->speed_hz = spi->max_speed_hz;
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 147 | 93.04% | 2 | 66.67% | 
| Gerhard Sittig | 11 | 6.96% | 1 | 33.33% | 
| Total | 158 | 100.00% | 3 | 100.00% | 
static void mpc512x_psc_spi_cleanup(struct spi_device *spi)
{
	if (gpio_is_valid(spi->cs_gpio))
		gpio_free(spi->cs_gpio);
	kfree(spi->controller_state);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 34 | 100.00% | 2 | 100.00% | 
| Total | 34 | 100.00% | 2 | 100.00% | 
static int mpc512x_psc_spi_port_config(struct spi_master *master,
				       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 =	0x01000000 |	/* SIM = 0001 -- 8 bit */
		0x00800000 |	/* GenClk = 1 -- internal clk */
		0x00008000 |	/* SPI = 1 */
		0x00004000 |	/* MSTR = 1   -- SPI master */
		0x00000800;	/* UseEOF = 1 -- SS low until EOF */
	out_be32(psc_addr(mps, sicr), sicr);
	ccr = in_be32(psc_addr(mps, ccr));
	ccr &= 0xFF000000;
	speed = 1000000;	/* default 1MHz */
	bclkdiv = (mps->mclk_rate / speed) - 1;
	ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
	out_be32(psc_addr(mps, ccr), ccr);
	/* Set 2ms DTL delay */
	out_8(psc_addr(mps, ctur), 0x00);
	out_8(psc_addr(mps, ctlr), 0x82);
	/* we don't use the alarms */
	out_be32(&fifo->rxalarm, 0xfff);
	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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 242 | 81.21% | 1 | 33.33% | 
| Uwe Kleine-König | 45 | 15.10% | 1 | 33.33% | 
| Gerhard Sittig | 11 | 3.69% | 1 | 33.33% | 
| Total | 298 | 100.00% | 3 | 100.00% | 
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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 90 | 97.83% | 1 | 33.33% | 
| Gerhard Sittig | 2 | 2.17% | 2 | 66.67% | 
| Total | 92 | 100.00% | 3 | 100.00% | 
static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff)
{
	gpio_set_value(spi->cs_gpio, onoff);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 23 | 100.00% | 1 | 100.00% | 
| Total | 23 | 100.00% | 1 | 100.00% | 
static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
					      u32 size, unsigned int irq)
{
	struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
	struct mpc512x_psc_spi *mps;
	struct spi_master *master;
	int ret;
	void *tempp;
	struct clk *clk;
	master = spi_alloc_master(dev, sizeof *mps);
	if (master == NULL)
		return -ENOMEM;
	dev_set_drvdata(dev, master);
	mps = spi_master_get_devdata(master);
	mps->type = (int)of_device_get_match_data(dev);
	mps->irq = irq;
	if (pdata == NULL) {
		mps->cs_control = mpc512x_spi_cs_control;
	} else {
		mps->cs_control = pdata->cs_control;
		master->bus_num = pdata->bus_num;
		master->num_chipselect = pdata->max_chipselect;
	}
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
	master->setup = mpc512x_psc_spi_setup;
	master->prepare_transfer_hardware = mpc512x_psc_spi_prep_xfer_hw;
	master->transfer_one_message = mpc512x_psc_spi_msg_xfer;
	master->unprepare_transfer_hardware = mpc512x_psc_spi_unprep_xfer_hw;
	master->cleanup = mpc512x_psc_spi_cleanup;
	master->dev.of_node = dev->of_node;
	tempp = devm_ioremap(dev, regaddr, size);
	if (!tempp) {
		dev_err(dev, "could not ioremap I/O port range\n");
		ret = -EFAULT;
		goto free_master;
	}
	mps->psc = tempp;
	mps->fifo =
		(struct mpc512x_psc_fifo *)(tempp + sizeof(struct mpc52xx_psc));
	ret = devm_request_irq(dev, mps->irq, mpc512x_psc_spi_isr, IRQF_SHARED,
				"mpc512x-psc-spi", mps);
	if (ret)
		goto free_master;
	init_completion(&mps->txisrdone);
	clk = devm_clk_get(dev, "mclk");
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto free_master;
	}
	ret = clk_prepare_enable(clk);
	if (ret)
		goto free_master;
	mps->clk_mclk = clk;
	mps->mclk_rate = clk_get_rate(clk);
	clk = devm_clk_get(dev, "ipg");
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto free_mclk_clock;
	}
	ret = clk_prepare_enable(clk);
	if (ret)
		goto free_mclk_clock;
	mps->clk_ipg = clk;
	ret = mpc512x_psc_spi_port_config(master, mps);
	if (ret < 0)
		goto free_ipg_clock;
	ret = devm_spi_register_master(dev, master);
	if (ret < 0)
		goto free_ipg_clock;
	return ret;
free_ipg_clock:
	clk_disable_unprepare(mps->clk_ipg);
free_mclk_clock:
	clk_disable_unprepare(mps->clk_mclk);
free_master:
	spi_master_put(master);
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 287 | 61.99% | 4 | 30.77% | 
| Gerhard Sittig | 141 | 30.45% | 4 | 30.77% | 
| Jingoo Han | 14 | 3.02% | 3 | 23.08% | 
| Uwe Kleine-König | 12 | 2.59% | 1 | 7.69% | 
| Wei Yongjun | 9 | 1.94% | 1 | 7.69% | 
| Total | 463 | 100.00% | 13 | 100.00% | 
static int mpc512x_psc_spi_do_remove(struct device *dev)
{
	struct spi_master *master = dev_get_drvdata(dev);
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
	clk_disable_unprepare(mps->clk_mclk);
	clk_disable_unprepare(mps->clk_ipg);
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 34 | 70.83% | 1 | 33.33% | 
| Gerhard Sittig | 14 | 29.17% | 2 | 66.67% | 
| Total | 48 | 100.00% | 3 | 100.00% | 
static int mpc512x_psc_spi_of_probe(struct platform_device *op)
{
	const u32 *regaddr_p;
	u64 regaddr64, size64;
	regaddr_p = of_get_address(op->dev.of_node, 0, &size64, NULL);
	if (!regaddr_p) {
		dev_err(&op->dev, "Invalid PSC address\n");
		return -EINVAL;
	}
	regaddr64 = of_translate_address(op->dev.of_node, regaddr_p);
	return mpc512x_psc_spi_do_probe(&op->dev, (u32) regaddr64, (u32) size64,
				irq_of_parse_and_map(op->dev.of_node, 0));
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 102 | 99.03% | 2 | 66.67% | 
| Grant C. Likely | 1 | 0.97% | 1 | 33.33% | 
| Total | 103 | 100.00% | 3 | 100.00% | 
static int mpc512x_psc_spi_of_remove(struct platform_device *op)
{
	return mpc512x_psc_spi_do_remove(&op->dev);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 19 | 95.00% | 1 | 50.00% | 
| Grant C. Likely | 1 | 5.00% | 1 | 50.00% | 
| Total | 20 | 100.00% | 2 | 100.00% | 
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,
	.remove = mpc512x_psc_spi_of_remove,
	.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");
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Anatolij Gustschin | 1952 | 74.36% | 6 | 24.00% | 
| Gerhard Sittig | 485 | 18.48% | 5 | 20.00% | 
| Uwe Kleine-König | 146 | 5.56% | 2 | 8.00% | 
| Jingoo Han | 14 | 0.53% | 3 | 12.00% | 
| Wei Yongjun | 9 | 0.34% | 1 | 4.00% | 
| Grant C. Likely | 7 | 0.27% | 4 | 16.00% | 
| Axel Lin | 6 | 0.23% | 1 | 4.00% | 
| Rob Herring | 3 | 0.11% | 1 | 4.00% | 
| Wolfram Sang | 2 | 0.08% | 1 | 4.00% | 
| Fabian Frederick | 1 | 0.04% | 1 | 4.00% | 
| Total | 2625 | 100.00% | 25 | 100.00% | 
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.