Initial commit

This commit is contained in:
domenico
2025-06-24 16:03:39 +02:00
commit f3256cdaf2
6949 changed files with 1441681 additions and 0 deletions

View File

@@ -0,0 +1,374 @@
/*
* Cavium CNS3xxx I2C Host Controller
*
* Copyright 2010 Cavium Network
* Copyright 2012 Gateworks Corporation
* Chris Lang <clang@gateworks.com>
* Tim Harvey <tharvey@gateworks.com>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/clk.h>
/*
* We need the memory map
*/
#define I2C_MEM_MAP_ADDR(x) (i2c->base + x)
#define I2C_MEM_MAP_VALUE(x) (*((unsigned int volatile*)I2C_MEM_MAP_ADDR(x)))
#define I2C_CONTROLLER_REG I2C_MEM_MAP_VALUE(0x00)
#define I2C_TIME_OUT_REG I2C_MEM_MAP_VALUE(0x04)
#define I2C_SLAVE_ADDRESS_REG I2C_MEM_MAP_VALUE(0x08)
#define I2C_WRITE_DATA_REG I2C_MEM_MAP_VALUE(0x0C)
#define I2C_READ_DATA_REG I2C_MEM_MAP_VALUE(0x10)
#define I2C_INTERRUPT_STATUS_REG I2C_MEM_MAP_VALUE(0x14)
#define I2C_INTERRUPT_ENABLE_REG I2C_MEM_MAP_VALUE(0x18)
#define I2C_TWI_OUT_DLY_REG I2C_MEM_MAP_VALUE(0x1C)
#define I2C_BUS_ERROR_FLAG (0x1)
#define I2C_ACTION_DONE_FLAG (0x2)
#define CNS3xxx_I2C_ENABLE() (I2C_CONTROLLER_REG) |= ((unsigned int)0x1 << 31)
#define CNS3xxx_I2C_DISABLE() (I2C_CONTROLLER_REG) &= ~((unsigned int)0x1 << 31)
#define CNS3xxx_I2C_ENABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) |= 0x03
#define CNS3xxx_I2C_DISABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) &= 0xfc
#define TWI_TIMEOUT (10*HZ)
#define I2C_100KHZ 100000
#define I2C_200KHZ 200000
#define I2C_300KHZ 300000
#define I2C_400KHZ 400000
#define CNS3xxx_I2C_CLK I2C_100KHZ
#define STATE_DONE 1
#define STATE_ERROR 2
struct cns3xxx_i2c {
struct device *dev;
void __iomem *base; /* virtual */
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *msg;
u8 state; /* see STATE_ */
u8 error; /* see TWI_STATUS register */
int rd_wr_len;
u8 *buf;
};
static u32 cns3xxx_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static int
cns3xxx_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg)
{
struct cns3xxx_i2c *i2c = i2c_get_adapdata(adap);
int i, j;
u8 buf[1] = { 0 };
if (msg->len == 0) {
/*
* We are probably doing a probe for a device here,
* so set the length to one, and data to 0
*/
msg->len = 1;
i2c->buf = buf;
} else {
i2c->buf = msg->buf;
}
if (msg->flags & I2C_M_TEN) {
printk
("%s:%d: Presently the driver does not handle extended addressing\n",
__FUNCTION__, __LINE__);
return -EINVAL;
}
i2c->msg = msg;
for (i = 0; i < msg->len; i++) {
if (msg->len - i >= 4)
i2c->rd_wr_len = 3;
else
i2c->rd_wr_len = msg->len - i - 1;
// Set Data Width and TWI_EN
I2C_CONTROLLER_REG = 0x80000000 | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
// Clear Write Reg
I2C_WRITE_DATA_REG = 0;
// Set the slave address
I2C_SLAVE_ADDRESS_REG = (msg->addr << 1);
// Are we Writing
if (!(msg->flags & I2C_M_RD)) {
I2C_CONTROLLER_REG |= (1 << 4);
if (i != 0) {
/*
* We need to set the address in the first byte.
* The base address is going to be in buf[0] and then
* it needs to be incremented by i - 1.
*/
i2c->buf--;
*i2c->buf = buf[0] + i - 1;
if (i2c->rd_wr_len < 3) {
i += i2c->rd_wr_len;
i2c->rd_wr_len++;
I2C_CONTROLLER_REG = 0x80000000 | (1 << 4) | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
} else {
i += i2c->rd_wr_len - 1;
}
} else {
i += i2c->rd_wr_len;
buf[0] = *i2c->buf;
}
for (j = 0; j <= i2c->rd_wr_len; j++) {
I2C_WRITE_DATA_REG |= ((*i2c->buf++) << (8 * j));
}
} else {
i += i2c->rd_wr_len;
}
// Start the Transfer
i2c->state = 0; // Clear out the State
i2c->error = 0;
I2C_CONTROLLER_REG |= (1 << 6);
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
(i2c->state == STATE_DONE), TWI_TIMEOUT)) {
if (i2c->state == STATE_ERROR) {
dev_dbg(i2c->dev, "controller error: 0x%2x", i2c->error);
return -EAGAIN; // try again
}
} else {
dev_err(i2c->dev, "controller timed out "
"waiting for start condition to finish\n");
return -ETIMEDOUT;
}
}
return 0;
}
static int
cns3xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int i;
int ret;
for (i = 0; i < num; i++)
{
ret = cns3xxx_i2c_xfer_msg(adap, &msgs[i]);
if (ret < 0) {
return ret;
}
}
return num;
}
static struct i2c_algorithm cns3xxx_i2c_algo = {
.master_xfer = cns3xxx_i2c_xfer,
.functionality = cns3xxx_i2c_func,
};
static struct i2c_adapter cns3xxx_i2c_adapter = {
.owner = THIS_MODULE,
.algo = &cns3xxx_i2c_algo,
.algo_data = NULL,
.nr = 0,
.name = "CNS3xxx I2C 0",
.retries = 5,
};
static void cns3xxx_i2c_adapter_init(struct cns3xxx_i2c *i2c)
{
struct clk *clk;
clk = devm_clk_get(i2c->dev, "cpu");
if (WARN_ON(!clk))
return;
/* Disable the I2C */
I2C_CONTROLLER_REG = 0; /* Disabled the I2C */
/* Check the Reg Dump when testing */
I2C_TIME_OUT_REG =
(((((clk_get_rate(clk) / (2 * CNS3xxx_I2C_CLK)) -
1) & 0x3FF) << 8) | (1 << 7) | 0x7F);
I2C_TWI_OUT_DLY_REG |= 0x3;
/* Enable The Interrupt */
CNS3xxx_I2C_ENABLE_INTR();
/* Clear Interrupt Status (0x2 | 0x1) */
I2C_INTERRUPT_STATUS_REG |= (I2C_ACTION_DONE_FLAG | I2C_BUS_ERROR_FLAG);
/* Enable the I2C Controller */
CNS3xxx_I2C_ENABLE();
}
static irqreturn_t cns3xxx_i2c_isr(int irq, void *dev_id)
{
struct cns3xxx_i2c *i2c = dev_id;
int i;
uint32_t stat = I2C_INTERRUPT_STATUS_REG;
/* Clear Interrupt */
I2C_INTERRUPT_STATUS_REG |= 0x1;
if (stat & I2C_BUS_ERROR_FLAG) {
i2c->state = STATE_ERROR;
i2c->error = (I2C_INTERRUPT_STATUS_REG & 0xff00)>>8;
} else {
if (i2c->msg->flags & I2C_M_RD) {
for (i = 0; i <= i2c->rd_wr_len; i++)
{
*i2c->buf++ = ((I2C_READ_DATA_REG >> (8 * i)) & 0xff);
}
}
i2c->state = STATE_DONE;
}
wake_up(&i2c->wait);
return IRQ_HANDLED;
}
static int cns3xxx_i2c_probe(struct platform_device *pdev)
{
struct cns3xxx_i2c *i2c;
struct resource *res, *res2;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk("%s: IORESOURCE_MEM not defined \n", __FUNCTION__);
return -ENODEV;
}
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res2) {
printk("%s: IORESOURCE_IRQ not defined \n", __FUNCTION__);
return -ENODEV;
}
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
if (!request_mem_region(res->start, res->end - res->start + 1,
pdev->name)) {
dev_err(&pdev->dev, "Memory region busy\n");
ret = -EBUSY;
goto request_mem_failed;
}
i2c->dev = &pdev->dev;
i2c->base = ioremap(res->start, res->end - res->start + 1);
if (!i2c->base) {
dev_err(&pdev->dev, "Unable to map registers\n");
ret = -EIO;
goto map_failed;
}
cns3xxx_i2c_adapter_init(i2c);
init_waitqueue_head(&i2c->wait);
ret = request_irq(res2->start, cns3xxx_i2c_isr, 0, pdev->name, i2c);
if (ret) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto request_irq_failed;
}
platform_set_drvdata(pdev, i2c);
i2c->adap = cns3xxx_i2c_adapter;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
/* add i2c adapter to i2c tree */
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret) {
dev_err(&pdev->dev, "Failed to add adapter\n");
goto add_adapter_failed;
}
return 0;
add_adapter_failed:
free_irq(res2->start, i2c);
request_irq_failed:
iounmap(i2c->base);
map_failed:
release_mem_region(res->start, res->end - res->start + 1);
request_mem_failed:
kfree(i2c);
return ret;
}
static int cns3xxx_i2c_remove(struct platform_device *pdev)
{
struct cns3xxx_i2c *i2c = platform_get_drvdata(pdev);
struct resource *res;
/* disable i2c logic */
CNS3xxx_I2C_DISABLE_INTR();
CNS3xxx_I2C_DISABLE();
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
platform_set_drvdata(pdev, NULL);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
free_irq(res->start, i2c);
iounmap(i2c->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, res->end - res->start + 1);
kfree(i2c);
return 0;
}
static struct platform_driver cns3xxx_i2c_driver = {
.probe = cns3xxx_i2c_probe,
.remove = cns3xxx_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "cns3xxx-i2c",
},
};
static int __init cns3xxx_i2c_init(void)
{
return platform_driver_register(&cns3xxx_i2c_driver);
}
static void __exit cns3xxx_i2c_exit(void)
{
platform_driver_unregister(&cns3xxx_i2c_driver);
}
module_init(cns3xxx_i2c_init);
module_exit(cns3xxx_i2c_exit);
MODULE_AUTHOR("Cavium Networks");
MODULE_DESCRIPTION("Cavium CNS3XXX I2C Controller");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,24 @@
config NET_VENDOR_CAVIUM
bool "Cavium devices"
default y
depends on ARCH_CNS3XXX
---help---
If you have a network (Ethernet) chipset belonging to this class,
say Y.
Note that the answer to this question does not directly affect
the kernel: saying N will just case the configurator to skip all
the questions regarding AMD chipsets. If you say Y, you will be asked
for your specific chipset/driver in the following questions.
if NET_VENDOR_CAVIUM
config CNS3XXX_ETH
tristate "Cavium CNS3xxx Ethernet support"
depends on ARCH_CNS3XXX
select PHYLIB
help
Say Y here if you want to use built-in Ethernet ports
on CNS3XXX processor.
endif

View File

@@ -0,0 +1,5 @@
#
# Makefile for the Cavium ethernet device drivers.
#
obj-$(CONFIG_CNS3XXX_ETH) += cns3xxx_eth.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,448 @@
/*******************************************************************************
*
* CNS3XXX SPI controller driver (master mode only)
*
* Copyright (c) 2008 Cavium Networks
* Copyright 2011 Gateworks Corporation
* Chris Lang <clang@gateworks.com>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful,
* but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or
* visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
*
******************************************************************************/
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/memory.h>
#include <asm/dma.h>
#include <asm/delay.h>
#include <linux/module.h>
/*
* define access macros
*/
#define SPI_MEM_MAP_VALUE(reg_offset) (*((u32 volatile *)(hw->base + reg_offset)))
#define SPI_CONFIGURATION_REG SPI_MEM_MAP_VALUE(0x00)
#define SPI_SERVICE_STATUS_REG SPI_MEM_MAP_VALUE(0x04)
#define SPI_BIT_RATE_CONTROL_REG SPI_MEM_MAP_VALUE(0x08)
#define SPI_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x0C)
#define SPI_TRANSMIT_BUFFER_REG SPI_MEM_MAP_VALUE(0x10)
#define SPI_RECEIVE_CONTROL_REG SPI_MEM_MAP_VALUE(0x14)
#define SPI_RECEIVE_BUFFER_REG SPI_MEM_MAP_VALUE(0x18)
#define SPI_FIFO_TRANSMIT_CONFIG_REG SPI_MEM_MAP_VALUE(0x1C)
#define SPI_FIFO_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x20)
#define SPI_FIFO_RECEIVE_CONFIG_REG SPI_MEM_MAP_VALUE(0x24)
#define SPI_INTERRUPT_STATUS_REG SPI_MEM_MAP_VALUE(0x28)
#define SPI_INTERRUPT_ENABLE_REG SPI_MEM_MAP_VALUE(0x2C)
#define SPI_TRANSMIT_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x10)
#define SPI_RECEIVE_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x18)
/* Structure for SPI controller of CNS3XXX SOCs */
struct cns3xxx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
wait_queue_head_t wait;
int len;
int count;
int last_in_message_list;
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
void __iomem *base;
struct spi_master *master;
struct platform_device *pdev;
struct device *dev;
};
static inline u8 cns3xxx_spi_bus_idle(struct cns3xxx_spi *hw)
{
return ((SPI_SERVICE_STATUS_REG & 0x1) ? 0 : 1);
}
static inline u8 cns3xxx_spi_tx_buffer_empty(struct cns3xxx_spi *hw)
{
return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 3)) ? 1 : 0);
}
static inline u8 cns3xxx_spi_rx_buffer_full(struct cns3xxx_spi *hw)
{
return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 2)) ? 1 : 0);
}
u8 cns3xxx_spi_tx_rx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof,
u32 tx_data, u32 * rx_data)
{
u8 rx_channel;
u8 rx_eof;
while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing
while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2);
SPI_TRANSMIT_BUFFER_REG = tx_data;
while (!cns3xxx_spi_rx_buffer_full(hw)) ; // do nothing
rx_channel = SPI_RECEIVE_CONTROL_REG & 0x3;
rx_eof = (SPI_RECEIVE_CONTROL_REG & (0x1 << 2)) ? 1 : 0;
*rx_data = SPI_RECEIVE_BUFFER_REG;
if ((tx_channel != rx_channel) || (tx_eof != rx_eof)) {
return 0;
} else {
return 1;
}
}
u8 cns3xxx_spi_tx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, u32 tx_data)
{
while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing
while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2);
SPI_TRANSMIT_BUFFER_REG = tx_data;
return 1;
}
static inline struct cns3xxx_spi *to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static int cns3xxx_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
return 0;
}
static void cns3xxx_spi_chipselect(struct spi_device *spi, int value)
{
struct cns3xxx_spi *hw = to_hw(spi);
unsigned int spi_config;
switch (value) {
case BITBANG_CS_INACTIVE:
break;
case BITBANG_CS_ACTIVE:
spi_config = SPI_CONFIGURATION_REG;
if (spi->mode & SPI_CPHA)
spi_config |= (0x1 << 13);
else
spi_config &= ~(0x1 << 13);
if (spi->mode & SPI_CPOL)
spi_config |= (0x1 << 14);
else
spi_config &= ~(0x1 << 14);
/* write new configration */
SPI_CONFIGURATION_REG = spi_config;
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (spi->chip_select & 0x3);
break;
}
}
static int cns3xxx_spi_setup(struct spi_device *spi)
{
if (!spi->bits_per_word)
spi->bits_per_word = 8;
return 0;
}
static int cns3xxx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct cns3xxx_spi *hw = to_hw(spi);
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf,
t->len);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
hw->last_in_message_list = t->last_in_message_list;
init_completion(&hw->done);
if (hw->tx) {
int i;
u32 rx_data;
for (i = 0; i < (hw->len - 1); i++) {
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->tx[%02d]: 0x%02x\n", i,
hw->tx[i]);
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i],
&rx_data);
if (hw->rx) {
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
}
if (t->last_in_message_list) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, hw->tx[i],
&rx_data);
if (hw->rx) {
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
} else {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i],
&rx_data);
}
goto done;
}
if (hw->rx) {
int i;
u32 rx_data;
for (i = 0; i < (hw->len - 1); i++) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data);
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", i,
hw->rx[i]);
}
if (t->last_in_message_list) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, 0xff, &rx_data);
} else {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data);
}
hw->rx[i] = rx_data;
dev_dbg(&spi->dev, "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
done:
return hw->len;
}
static void __init cns3xxx_spi_initial(struct cns3xxx_spi *hw)
{
SPI_CONFIGURATION_REG = (((0x0 & 0x3) << 0) | /* 8bits shift length */
(0x0 << 9) | /* SPI mode */
(0x0 << 10) | /* disable FIFO */
(0x1 << 11) | /* SPI master mode */
(0x0 << 12) | /* disable SPI loopback mode */
(0x1 << 13) | /* clock phase */
(0x1 << 14) | /* clock polarity */
(0x0 << 24) | /* disable - SPI data swap */
(0x1 << 29) | /* enable - 2IO Read mode */
(0x0 << 30) | /* disable - SPI high speed read for system boot up */
(0x0 << 31)); /* disable - SPI */
/* Set SPI bit rate PCLK/2 */
SPI_BIT_RATE_CONTROL_REG = 0x1;
/* Set SPI Tx channel 0 */
SPI_TRANSMIT_CONTROL_REG = 0x0;
/* Set Tx FIFO Threshold, Tx FIFO has 2 words */
SPI_FIFO_TRANSMIT_CONFIG_REG &= ~(0x03 << 4);
SPI_FIFO_TRANSMIT_CONFIG_REG |= ((0x0 & 0x03) << 4);
/* Set Rx FIFO Threshold, Rx FIFO has 2 words */
SPI_FIFO_RECEIVE_CONFIG_REG &= ~(0x03 << 4);
SPI_FIFO_RECEIVE_CONFIG_REG |= ((0x0 & 0x03) << 4);
/* Disable all interrupt */
SPI_INTERRUPT_ENABLE_REG = 0x0;
/* Clear spurious interrupt sources */
SPI_INTERRUPT_STATUS_REG = (0x0F << 4);
/* Enable SPI */
SPI_CONFIGURATION_REG |= (0x1 << 31);
return;
}
static int cns3xxx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct cns3xxx_spi *hw;
struct resource *res;
int err = 0;
printk("%s: setup CNS3XXX SPI Controller\n", __FUNCTION__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
/* Allocate master with space for cns3xxx_spi */
master = spi_alloc_master(&pdev->dev, sizeof(struct cns3xxx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct cns3xxx_spi));
hw->master = spi_master_get(master);
hw->dev = &pdev->dev;
hw->base = devm_ioremap_resource(hw->dev, res);
if (IS_ERR(hw->base)) {
dev_err(hw->dev, "Unable to map registers\n");
err = PTR_ERR(hw->base);
goto err_register;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
/* setup the master state. */
master->num_chipselect = 4;
master->bus_num = 1;
/* setup the state for the bitbang driver */
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = cns3xxx_spi_setup_transfer;
hw->bitbang.chipselect = cns3xxx_spi_chipselect;
hw->bitbang.txrx_bufs = cns3xxx_spi_txrx;
hw->bitbang.master->setup = cns3xxx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* SPI controller initializations */
cns3xxx_spi_initial(hw);
/* register SPI controller */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
spi_master_put(hw->master);;
err_nomem:
return err;
}
static int cns3xxx_spi_remove(struct platform_device *dev)
{
struct cns3xxx_spi *hw = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
spi_unregister_master(hw->master);
spi_master_put(hw->master);
return 0;
}
#ifdef CONFIG_PM
static int cns3xxx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct cns3xxx_spi *hw = platform_get_drvdata(pdev);
return 0;
}
static int cns3xxx_spi_resume(struct platform_device *pdev)
{
struct cns3xxx_spi *hw = platform_get_drvdata(pdev);
return 0;
}
#else
#define cns3xxx_spi_suspend NULL
#define cns3xxx_spi_resume NULL
#endif
static struct platform_driver cns3xxx_spi_driver = {
.probe = cns3xxx_spi_probe,
.remove = cns3xxx_spi_remove,
.suspend = cns3xxx_spi_suspend,
.resume = cns3xxx_spi_resume,
.driver = {
.name = "cns3xxx_spi",
.owner = THIS_MODULE,
},
};
static int __init cns3xxx_spi_init(void)
{
return platform_driver_register(&cns3xxx_spi_driver);
}
static void __exit cns3xxx_spi_exit(void)
{
platform_driver_unregister(&cns3xxx_spi_driver);
}
module_init(cns3xxx_spi_init);
module_exit(cns3xxx_spi_exit);
MODULE_AUTHOR("Cavium Networks");
MODULE_DESCRIPTION("CNS3XXX SPI Controller Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:cns3xxx_spi");
EXPORT_SYMBOL_GPL(cns3xxx_spi_tx_rx);