From 181683a6bdfbd4b536a8b37860c7dd779a0468a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Jan 2014 13:43:22 -0800 Subject: [PATCH] more zynq patches --- ...merge-i2c-driver-from-Xilinx-repository-i.patch | 1004 +++++ ...0-merge-support-for-si570-clock-generator.patch | 662 +++ ...add-a-driver-for-Arasan-s-SDHCI-controlle.patch | 327 ++ ...t-xilinx-Merge-driver-from-Xilinx-reposit.patch | 4349 ++++++++++++++++++++ ...-merge-Xilinx-gpio-support-into-LTSI-3.10.patch | 1207 ++++++ ...merge-qspi-support-from-xilinx-repository.patch | 2303 +++++++++++ ...merge-nand-flash-support-from-xilinx-repo.patch | 1876 +++++++++ ...-xilinx-merge-support-for-xilinx-watchdog.patch | 619 +++ ...ynq-merge-usb-support-for-xilinx-zynq-soc.patch | 3821 +++++++++++++++++ ...010-memory-zynq-merge-driver-for-Zynq-SMC.patch | 706 ++++ ...q-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch | 418 ++ ...ynq-merge-xilinx-zynq-defconfig-from-xili.patch | 2477 +++++++++++ series | 12 + 13 files changed, 19781 insertions(+) create mode 100644 patches.zynq/0001-i2c-xilinx-merge-i2c-driver-from-Xilinx-repository-i.patch create mode 100644 patches.zynq/0002-i2c-si570-merge-support-for-si570-clock-generator.patch create mode 100644 patches.zynq/0003-mmc-arasan-add-a-driver-for-Arasan-s-SDHCI-controlle.patch create mode 100644 patches.zynq/0004-net-ethernet-xilinx-Merge-driver-from-Xilinx-reposit.patch create mode 100644 patches.zynq/0005-gpio-xilinx-merge-Xilinx-gpio-support-into-LTSI-3.10.patch create mode 100644 patches.zynq/0006-spi-xilinx-merge-qspi-support-from-xilinx-repository.patch create mode 100644 patches.zynq/0007-mtd-xilinx-merge-nand-flash-support-from-xilinx-repo.patch create mode 100644 patches.zynq/0008-watchdog-xilinx-merge-support-for-xilinx-watchdog.patch create mode 100644 patches.zynq/0009-usb-zynq-merge-usb-support-for-xilinx-zynq-soc.patch create mode 100644 patches.zynq/0010-memory-zynq-merge-driver-for-Zynq-SMC.patch create mode 100644 patches.zynq/0011-arm-dts-zynq-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch create mode 100644 patches.zynq/0012-defconfig-zynq-merge-xilinx-zynq-defconfig-from-xili.patch diff --git a/patches.zynq/0001-i2c-xilinx-merge-i2c-driver-from-Xilinx-repository-i.patch b/patches.zynq/0001-i2c-xilinx-merge-i2c-driver-from-Xilinx-repository-i.patch new file mode 100644 index 0000000..ab31681 --- /dev/null +++ b/patches.zynq/0001-i2c-xilinx-merge-i2c-driver-from-Xilinx-repository-i.patch @@ -0,0 +1,1004 @@ +From b23b4c001f284dba44b70ba7d2d88b0f9b7d5c71 Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Fri, 20 Dec 2013 15:57:55 +0900 +Subject: i2c: xilinx: merge i2c driver from Xilinx repository into LTSI + +This merges i2c functionality from Xilinx repository (commit +efc27505715e64526653f35274717c0fc56491e3 in master branch). It has +been tested to read the eeprom on a zc702 board. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/i2c-xilinx_ps.c | 966 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 975 insertions(+) + create mode 100644 drivers/i2c/busses/i2c-xilinx_ps.c + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -736,6 +736,15 @@ config I2C_WMT + This driver can also be built as a module. If so, the module will be + called i2c-wmt. + ++config I2C_XILINX_PS ++ tristate "XILINX PS I2C Controller" ++ depends on ARCH_ZYNQ ++ help ++ Say yes here to select Xilnx PS I2C Host Controller ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-xilinx_ps. ++ + config I2C_OCTEON + tristate "Cavium OCTEON I2C bus support" + depends on CPU_CAVIUM_OCTEON +--- /dev/null ++++ b/drivers/i2c/busses/i2c-xilinx_ps.c +@@ -0,0 +1,966 @@ ++/* ++ * Xilinx I2C bus driver for the PS I2C Interfaces. ++ * ++ * 2009-2011 (c) Xilinx, Inc. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public ++ * License along with this program; if not, write to the Free ++ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA ++ * 02139, USA. ++ * ++ * ++ * Workaround in Receive Mode ++ * If there is only one message to be processed, then based on length of ++ * the message we set the HOLD bit. ++ * If the length is less than the FIFO depth, then we will directly ++ * receive a COMP interrupt and the transaction is done. ++ * If the length is more than the FIFO depth, then we enable the HOLD bit. ++ * if the requested data is greater than the max transfer size(252 bytes) ++ * update the transfer size register with max transfer size else update ++ * with the requested size. ++ * We will receive the DATA interrupt, if the transfer size register value ++ * is zero then repeat the above step for the remaining bytes (if any) and ++ * process the data in the fifo. ++ * ++ * The bus hold flag logic provides support for repeated start. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Register Map ++ * Register offsets for the I2C device. ++ */ ++#define XI2CPS_CR_OFFSET 0x00 /* Control Register, RW */ ++#define XI2CPS_SR_OFFSET 0x04 /* Status Register, RO */ ++#define XI2CPS_ADDR_OFFSET 0x08 /* I2C Address Register, RW */ ++#define XI2CPS_DATA_OFFSET 0x0C /* I2C Data Register, RW */ ++#define XI2CPS_ISR_OFFSET 0x10 /* Interrupt Status Register, RW */ ++#define XI2CPS_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */ ++#define XI2CPS_SLV_PAUSE_OFFSET 0x18 /* Slave monitor pause Register, RW */ ++#define XI2CPS_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */ ++#define XI2CPS_IMR_OFFSET 0x20 /* Interrupt Mask Register, RO */ ++#define XI2CPS_IER_OFFSET 0x24 /* Interrupt Enable Register, WO */ ++#define XI2CPS_IDR_OFFSET 0x28 /* Interrupt Disable Register, WO */ ++ ++/* ++ * Control Register Bit mask definitions ++ * This register contains various control bits that affect the operation of the ++ * I2C controller. ++ */ ++#define XI2CPS_CR_HOLD_BUS_MASK 0x00000010 /* Hold Bus bit */ ++#define XI2CPS_CR_RW_MASK 0x00000001 /* Read or Write Master transfer ++ * 0= Transmitter, 1= Receiver */ ++#define XI2CPS_CR_CLR_FIFO_MASK 0x00000040 /* 1 = Auto init FIFO to zeroes */ ++ ++/* ++ * I2C Address Register Bit mask definitions ++ * Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0] ++ * bits. A write access to this register always initiates a transfer if the I2C ++ * is in master mode. ++ */ ++#define XI2CPS_ADDR_MASK 0x000003FF /* I2C Address Mask */ ++ ++/* ++ * I2C Interrupt Registers Bit mask definitions ++ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same ++ * bit definitions. ++ */ ++#define XI2CPS_IXR_ALL_INTR_MASK 0x000002FF /* All ISR Mask */ ++ ++#define XI2CPS_FIFO_DEPTH 16 /* FIFO Depth */ ++#define XI2CPS_TIMEOUT (50 * HZ) /* Timeout for bus busy check */ ++#define XI2CPS_ENABLED_INTR 0x2EF /* Enabled Interrupts */ ++ ++#define XI2CPS_DATA_INTR_DEPTH (XI2CPS_FIFO_DEPTH - 2)/* FIFO depth at which ++ * the DATA interrupt ++ * occurs ++ */ ++#define XI2CPS_MAX_TRANSFER_SIZE 255 /* Max transfer size */ ++#define XI2CPS_TRANSFER_SIZE (XI2CPS_MAX_TRANSFER_SIZE - 3) /* Transfer size ++ in multiples of data interrupt depth */ ++ ++#define DRIVER_NAME "xi2cps" ++ ++#define xi2cps_readreg(offset) __raw_readl(id->membase + offset) ++#define xi2cps_writereg(val, offset) __raw_writel(val, id->membase + offset) ++ ++/** ++ * struct xi2cps - I2C device private data structure ++ * @membase: Base address of the I2C device ++ * @adap: I2C adapter instance ++ * @p_msg: Message pointer ++ * @err_status: Error status in Interrupt Status Register ++ * @xfer_done: Transfer complete status ++ * @p_send_buf: Pointer to transmit buffer ++ * @p_recv_buf: Pointer to receive buffer ++ * @suspended: Flag holding the device's PM status ++ * @send_count: Number of bytes still expected to send ++ * @recv_count: Number of bytes still expected to receive ++ * @irq: IRQ number ++ * @cur_timeout: The current timeout value used by the device ++ * @input_clk: Input clock to I2C controller ++ * @i2c_clk: Current I2C frequency ++ * @bus_hold_flag: Flag used in repeated start for clearing HOLD bit ++ * @clk: Pointer to struct clk ++ * @clk_rate_change_nb: Notifier block for clock rate changes ++ */ ++struct xi2cps { ++ void __iomem *membase; ++ struct i2c_adapter adap; ++ struct i2c_msg *p_msg; ++ int err_status; ++ struct completion xfer_done; ++ unsigned char *p_send_buf; ++ unsigned char *p_recv_buf; ++ u8 suspended; ++ int send_count; ++ int recv_count; ++ int irq; ++ int cur_timeout; ++ unsigned int input_clk; ++ unsigned int i2c_clk; ++ unsigned int bus_hold_flag; ++ struct clk *clk; ++ struct notifier_block clk_rate_change_nb; ++}; ++ ++#define to_xi2cps(_nb) container_of(_nb, struct xi2cps,\ ++ clk_rate_change_nb) ++#define MAX_F_ERR 10000 ++ ++/** ++ * xi2cps_isr - Interrupt handler for the I2C device ++ * @irq: irq number for the I2C device ++ * @ptr: void pointer to xi2cps structure ++ * ++ * Returns IRQ_HANDLED always ++ * ++ * This function handles the data interrupt, transfer complete interrupt and ++ * the error interrupts of the I2C device. ++ */ ++static irqreturn_t xi2cps_isr(int irq, void *ptr) ++{ ++ unsigned int isr_status, avail_bytes; ++ unsigned int bytes_to_recv, bytes_to_send; ++ unsigned int ctrl_reg = 0; ++ struct xi2cps *id = ptr; ++ ++ isr_status = xi2cps_readreg(XI2CPS_ISR_OFFSET); ++ ++ /* Handling Nack interrupt */ ++ if (isr_status & 0x00000004) ++ complete(&id->xfer_done); ++ ++ /* Handling Arbitration lost interrupt */ ++ if (isr_status & 0x00000200) ++ complete(&id->xfer_done); ++ ++ /* Handling Data interrupt */ ++ if (isr_status & 0x00000002) { ++ if (id->recv_count >= XI2CPS_DATA_INTR_DEPTH) { ++ /* Always read data interrupt threshold bytes */ ++ bytes_to_recv = XI2CPS_DATA_INTR_DEPTH; ++ id->recv_count = id->recv_count - ++ XI2CPS_DATA_INTR_DEPTH; ++ avail_bytes = xi2cps_readreg(XI2CPS_XFER_SIZE_OFFSET); ++ /* ++ * if the tranfer size register value is zero, then ++ * check for the remaining bytes and update the ++ * transfer size register. ++ */ ++ if (avail_bytes == 0) { ++ if (id->recv_count > XI2CPS_TRANSFER_SIZE) ++ xi2cps_writereg(XI2CPS_TRANSFER_SIZE, ++ XI2CPS_XFER_SIZE_OFFSET); ++ else ++ xi2cps_writereg(id->recv_count, ++ XI2CPS_XFER_SIZE_OFFSET); ++ } ++ /* Process the data received */ ++ while (bytes_to_recv) { ++ *(id->p_recv_buf)++ = ++ xi2cps_readreg(XI2CPS_DATA_OFFSET); ++ bytes_to_recv = bytes_to_recv - 1; ++ } ++ ++ if ((id->bus_hold_flag == 0) && ++ (id->recv_count <= XI2CPS_FIFO_DEPTH)) { ++ /* Clear the hold bus bit */ ++ xi2cps_writereg( ++ (xi2cps_readreg(XI2CPS_CR_OFFSET) & ++ (~XI2CPS_CR_HOLD_BUS_MASK)), ++ XI2CPS_CR_OFFSET); ++ } ++ } ++ } ++ ++ /* Handling Transfer Complete interrupt */ ++ if (isr_status & 0x00000001) { ++ if ((id->p_recv_buf) == NULL) { ++ /* ++ * If the device is sending data If there is further ++ * data to be sent. Calculate the available space ++ * in FIFO and fill the FIFO with that many bytes. ++ */ ++ if (id->send_count > 0) { ++ avail_bytes = XI2CPS_FIFO_DEPTH - ++ xi2cps_readreg(XI2CPS_XFER_SIZE_OFFSET); ++ if (id->send_count > avail_bytes) ++ bytes_to_send = avail_bytes; ++ else ++ bytes_to_send = id->send_count; ++ ++ while (bytes_to_send--) { ++ xi2cps_writereg( ++ (*(id->p_send_buf)++), ++ XI2CPS_DATA_OFFSET); ++ id->send_count--; ++ } ++ } else { ++ /* ++ * Signal the completion of transaction and clear the hold bus ++ * bit if there are no further messages to be processed. ++ */ ++ complete(&id->xfer_done); ++ } ++ if (id->send_count == 0) { ++ if (id->bus_hold_flag == 0) { ++ /* Clear the hold bus bit */ ++ ctrl_reg = ++ xi2cps_readreg(XI2CPS_CR_OFFSET); ++ if ((ctrl_reg & XI2CPS_CR_HOLD_BUS_MASK) ++ == XI2CPS_CR_HOLD_BUS_MASK) ++ xi2cps_writereg( ++ (ctrl_reg & ++ (~XI2CPS_CR_HOLD_BUS_MASK)), ++ XI2CPS_CR_OFFSET); ++ } ++ } ++ } else { ++ if (id->bus_hold_flag == 0) { ++ /* Clear the hold bus bit */ ++ ctrl_reg = ++ xi2cps_readreg(XI2CPS_CR_OFFSET); ++ if ((ctrl_reg & XI2CPS_CR_HOLD_BUS_MASK) ++ == XI2CPS_CR_HOLD_BUS_MASK) ++ xi2cps_writereg( ++ (ctrl_reg & ++ (~XI2CPS_CR_HOLD_BUS_MASK)), ++ XI2CPS_CR_OFFSET); ++ } ++ /* ++ * If the device is receiving data, then signal the completion ++ * of transaction and read the data present in the FIFO. ++ * Signal the completion of transaction. ++ */ ++ while (xi2cps_readreg(XI2CPS_SR_OFFSET) ++ & 0x00000020) { ++ *(id->p_recv_buf)++ = ++ xi2cps_readreg(XI2CPS_DATA_OFFSET); ++ id->recv_count--; ++ } ++ complete(&id->xfer_done); ++ } ++ } ++ ++ /* Update the status for errors */ ++ id->err_status = isr_status & 0x000002EC; ++ xi2cps_writereg(isr_status, XI2CPS_ISR_OFFSET); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * xi2cps_mrecv - Prepare and start a master receive operation ++ * @id: pointer to the i2c device structure ++ * ++ */ ++static void xi2cps_mrecv(struct xi2cps *id) ++{ ++ unsigned int ctrl_reg; ++ unsigned int isr_status; ++ ++ id->p_recv_buf = id->p_msg->buf; ++ id->recv_count = id->p_msg->len; ++ ++ /* ++ * Set the controller in master receive mode and clear the FIFO. ++ * Set the slave address in address register. ++ * Check for the message size against FIFO depth and set the ++ * HOLD bus bit if it is more than FIFO depth. ++ * Clear the interrupts in interrupt status register. ++ */ ++ ctrl_reg = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ ctrl_reg |= (XI2CPS_CR_RW_MASK | XI2CPS_CR_CLR_FIFO_MASK); ++ ++ if ((id->p_msg->flags & I2C_M_RECV_LEN) == I2C_M_RECV_LEN) ++ id->recv_count = I2C_SMBUS_BLOCK_MAX + 1; ++ ++ if (id->recv_count > XI2CPS_FIFO_DEPTH) ++ ctrl_reg |= XI2CPS_CR_HOLD_BUS_MASK; ++ ++ xi2cps_writereg(ctrl_reg, XI2CPS_CR_OFFSET); ++ ++ isr_status = xi2cps_readreg(XI2CPS_ISR_OFFSET); ++ xi2cps_writereg(isr_status, XI2CPS_ISR_OFFSET); ++ ++ xi2cps_writereg((id->p_msg->addr & XI2CPS_ADDR_MASK), ++ XI2CPS_ADDR_OFFSET); ++ /* ++ * The no. of bytes to receive is checked against the limit of ++ * max transfer size. Set transfer size register with no of bytes ++ * receive if it is less than transfer size and transfer size if ++ * it is more. Enable the interrupts. ++ */ ++ if (id->recv_count > XI2CPS_TRANSFER_SIZE) ++ xi2cps_writereg(XI2CPS_TRANSFER_SIZE, XI2CPS_XFER_SIZE_OFFSET); ++ else ++ xi2cps_writereg(id->recv_count, XI2CPS_XFER_SIZE_OFFSET); ++ /* ++ * Clear the bus hold flag if bytes to receive is less than FIFO size. ++ */ ++ if (id->bus_hold_flag == 0 && ++ ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && ++ (id->recv_count <= XI2CPS_FIFO_DEPTH)) { ++ /* Clear the hold bus bit */ ++ ctrl_reg = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ if ((ctrl_reg & XI2CPS_CR_HOLD_BUS_MASK) == ++ XI2CPS_CR_HOLD_BUS_MASK) ++ xi2cps_writereg( ++ (ctrl_reg & (~XI2CPS_CR_HOLD_BUS_MASK)), ++ XI2CPS_CR_OFFSET); ++ } ++ xi2cps_writereg(XI2CPS_ENABLED_INTR, XI2CPS_IER_OFFSET); ++} ++ ++/** ++ * xi2cps_msend - Prepare and start a master send operation ++ * @id: pointer to the i2c device ++ * ++ */ ++static void xi2cps_msend(struct xi2cps *id) ++{ ++ unsigned int avail_bytes; ++ unsigned int bytes_to_send; ++ unsigned int ctrl_reg; ++ unsigned int isr_status; ++ ++ id->p_recv_buf = NULL; ++ id->p_send_buf = id->p_msg->buf; ++ id->send_count = id->p_msg->len; ++ ++ /* ++ * Set the controller in Master transmit mode and clear the FIFO. ++ * Set the slave address in address register. ++ * Check for the message size against FIFO depth and set the ++ * HOLD bus bit if it is more than FIFO depth. ++ * Clear the interrupts in interrupt status register. ++ */ ++ ctrl_reg = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ ctrl_reg &= ~XI2CPS_CR_RW_MASK; ++ ctrl_reg |= XI2CPS_CR_CLR_FIFO_MASK; ++ ++ if ((id->send_count) > XI2CPS_FIFO_DEPTH) ++ ctrl_reg |= XI2CPS_CR_HOLD_BUS_MASK; ++ xi2cps_writereg(ctrl_reg, XI2CPS_CR_OFFSET); ++ ++ isr_status = xi2cps_readreg(XI2CPS_ISR_OFFSET); ++ xi2cps_writereg(isr_status, XI2CPS_ISR_OFFSET); ++ ++ /* ++ * Calculate the space available in FIFO. Check the message length ++ * against the space available, and fill the FIFO accordingly. ++ * Enable the interrupts. ++ */ ++ avail_bytes = XI2CPS_FIFO_DEPTH - ++ xi2cps_readreg(XI2CPS_XFER_SIZE_OFFSET); ++ ++ if (id->send_count > avail_bytes) ++ bytes_to_send = avail_bytes; ++ else ++ bytes_to_send = id->send_count; ++ ++ while (bytes_to_send--) { ++ xi2cps_writereg((*(id->p_send_buf)++), XI2CPS_DATA_OFFSET); ++ id->send_count--; ++ } ++ ++ xi2cps_writereg((id->p_msg->addr & XI2CPS_ADDR_MASK), ++ XI2CPS_ADDR_OFFSET); ++ ++ /* ++ * Clear the bus hold flag if there is no more data ++ * and if it is the last message. ++ */ ++ if (id->bus_hold_flag == 0 && id->send_count == 0) { ++ /* Clear the hold bus bit */ ++ ctrl_reg = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ if ((ctrl_reg & XI2CPS_CR_HOLD_BUS_MASK) == ++ XI2CPS_CR_HOLD_BUS_MASK) ++ xi2cps_writereg( ++ (ctrl_reg & (~XI2CPS_CR_HOLD_BUS_MASK)), ++ XI2CPS_CR_OFFSET); ++ } ++ xi2cps_writereg(XI2CPS_ENABLED_INTR, XI2CPS_IER_OFFSET); ++} ++ ++/** ++ * xi2cps_master_reset - Reset the interface ++ * @adap: pointer to the i2c adapter driver instance ++ * ++ * Returns none ++ * ++ * This function cleanup the fifos, clear the hold bit and status ++ * and disable the interrupts. ++ */ ++static void xi2cps_master_reset(struct i2c_adapter *adap) ++{ ++ struct xi2cps *id = adap->algo_data; ++ u32 regval; ++ ++ /* Disable the interrupts */ ++ xi2cps_writereg(XI2CPS_IXR_ALL_INTR_MASK, XI2CPS_IDR_OFFSET); ++ /* Clear the hold bit and fifos */ ++ regval = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ regval &= ~XI2CPS_CR_HOLD_BUS_MASK; ++ regval |= XI2CPS_CR_CLR_FIFO_MASK; ++ xi2cps_writereg(regval, XI2CPS_CR_OFFSET); ++ /* Update the transfercount register to zero */ ++ xi2cps_writereg(0x0, XI2CPS_XFER_SIZE_OFFSET); ++ /* Clear the interupt status register */ ++ regval = xi2cps_readreg(XI2CPS_ISR_OFFSET); ++ xi2cps_writereg(regval, XI2CPS_ISR_OFFSET); ++ /* Clear the status register */ ++ regval = xi2cps_readreg(XI2CPS_SR_OFFSET); ++ xi2cps_writereg(regval, XI2CPS_SR_OFFSET); ++} ++ ++/** ++ * xi2cps_master_xfer - The main i2c transfer function ++ * @adap: pointer to the i2c adapter driver instance ++ * @msgs: pointer to the i2c message structure ++ * @num: the number of messages to transfer ++ * ++ * Returns number of msgs processed on success, negative error otherwise ++ * ++ * This function waits for the bus idle condition and updates the timeout if ++ * modified by user. Then initiates the send/recv activity based on the ++ * transfer message received. ++ */ ++static int xi2cps_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ struct xi2cps *id = adap->algo_data; ++ unsigned int count, retries; ++ unsigned long timeout; ++ int ret; ++ ++ /* Waiting for bus-ready. If bus not ready, it returns after timeout */ ++ timeout = jiffies + XI2CPS_TIMEOUT; ++ while ((xi2cps_readreg(XI2CPS_SR_OFFSET)) & 0x00000100) { ++ if (time_after(jiffies, timeout)) { ++ dev_warn(id->adap.dev.parent, ++ "timedout waiting for bus ready\n"); ++ xi2cps_master_reset(adap); ++ return -ETIMEDOUT; ++ } ++ schedule_timeout(1); ++ } ++ ++ ++ /* The bus is free. Set the new timeout value if updated */ ++ if (id->adap.timeout != id->cur_timeout) { ++ xi2cps_writereg((id->adap.timeout & 0xFF), ++ XI2CPS_TIME_OUT_OFFSET); ++ id->cur_timeout = id->adap.timeout; ++ } ++ ++ /* ++ * Set the flag to one when multiple messages are to be ++ * processed with a repeated start. ++ */ ++ if (num > 1) { ++ id->bus_hold_flag = 1; ++ xi2cps_writereg((xi2cps_readreg(XI2CPS_CR_OFFSET) | ++ XI2CPS_CR_HOLD_BUS_MASK), XI2CPS_CR_OFFSET); ++ } else { ++ id->bus_hold_flag = 0; ++ } ++ ++ /* Process the msg one by one */ ++ for (count = 0; count < num; count++, msgs++) { ++ ++ if (count == (num - 1)) ++ id->bus_hold_flag = 0; ++ retries = adap->retries; ++retry: ++ id->err_status = 0; ++ id->p_msg = msgs; ++ init_completion(&id->xfer_done); ++ ++ /* Check for the TEN Bit mode on each msg */ ++ if (msgs->flags & I2C_M_TEN) { ++ xi2cps_writereg((xi2cps_readreg(XI2CPS_CR_OFFSET) & ++ (~0x00000004)), XI2CPS_CR_OFFSET); ++ } else { ++ if ((xi2cps_readreg(XI2CPS_CR_OFFSET) & 0x00000004) ++ == 0) ++ xi2cps_writereg( ++ (xi2cps_readreg(XI2CPS_CR_OFFSET) | ++ (0x00000004)), XI2CPS_CR_OFFSET); ++ } ++ ++ /* Check for the R/W flag on each msg */ ++ if (msgs->flags & I2C_M_RD) ++ xi2cps_mrecv(id); ++ else ++ xi2cps_msend(id); ++ ++ /* Wait for the signal of completion */ ++ ret = wait_for_completion_interruptible_timeout( ++ &id->xfer_done, HZ); ++ if (ret == 0) { ++ dev_err(id->adap.dev.parent, ++ "timeout waiting on completion\n"); ++ xi2cps_master_reset(adap); ++ return -ETIMEDOUT; ++ } ++ xi2cps_writereg(XI2CPS_IXR_ALL_INTR_MASK, XI2CPS_IDR_OFFSET); ++ ++ /* If it is bus arbitration error, try again */ ++ if (id->err_status & 0x00000200) { ++ dev_dbg(id->adap.dev.parent, ++ "Lost ownership on bus, trying again\n"); ++ if (retries--) { ++ mdelay(2); ++ goto retry; ++ } ++ dev_err(id->adap.dev.parent, ++ "Retries completed, exit\n"); ++ num = -EREMOTEIO; ++ break; ++ } ++ /* Report the other error interrupts to application as EIO */ ++ if (id->err_status & 0x000000E4) { ++ xi2cps_master_reset(adap); ++ num = -EIO; ++ break; ++ } ++ } ++ ++ id->p_msg = NULL; ++ id->err_status = 0; ++ ++ return num; ++} ++ ++/** ++ * xi2cps_func - Returns the supported features of the I2C driver ++ * @adap: pointer to the i2c adapter structure ++ * ++ * Returns 32 bit value, each bit corresponding to a feature ++ */ ++static u32 xi2cps_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | ++ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static const struct i2c_algorithm xi2cps_algo = { ++ .master_xfer = xi2cps_master_xfer, ++ .functionality = xi2cps_func, ++}; ++ ++/** ++ * xi2cps_calc_divs() - Calculate clock dividers ++ * @f: I2C clock frequency ++ * @input_clk: Input clock frequency ++ * @a: First divider (return value) ++ * @b: Second divider (return value) ++ * @err: Frequency error ++ * Return 0 on success, negative errno otherwise. ++ * ++ * f is used as input and output variable. As input it is used as target I2C ++ * frequency. On function exit f holds the actually resulting I2C frequency. ++ */ ++static int xi2cps_calc_divs(unsigned int *f, unsigned int input_clk, ++ unsigned int *a, unsigned int *b, unsigned int *err) ++{ ++ unsigned int fscl = *f; ++ unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0; ++ unsigned int last_error, current_error; ++ unsigned int best_fscl = *f, actual_fscl, temp; ++ ++ /* calculate (divisor_a+1) x (divisor_b+1) */ ++ temp = input_clk / (22 * fscl); ++ ++ /* ++ * If the calculated value is negative or 0, the fscl input is out of ++ * range. Return error. ++ */ ++ if (!temp) ++ return -EINVAL; ++ ++ last_error = -1; ++ for (div_b = 0; div_b < 64; div_b++) { ++ div_a = input_clk / (22 * fscl * (div_b + 1)); ++ ++ if (div_a != 0) ++ div_a = div_a - 1; ++ ++ if (div_a > 3) ++ continue; ++ ++ actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1)); ++ ++ current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) : ++ (fscl - actual_fscl)); ++ ++ if (last_error > current_error) { ++ calc_div_a = div_a; ++ calc_div_b = div_b; ++ best_fscl = actual_fscl; ++ last_error = current_error; ++ } ++ } ++ ++ *err = last_error; ++ *a = calc_div_a; ++ *b = calc_div_b; ++ *f = best_fscl; ++ ++ return 0; ++} ++ ++/** ++ * xi2cps_setclk - This function sets the serial clock rate for the I2C device ++ * @fscl: The clock frequency in Hz ++ * @id: Pointer to the I2C device structure ++ * ++ * Returns zero on success, negative error otherwise ++ * ++ * The device must be idle rather than busy transferring data before setting ++ * these device options. ++ * The data rate is set by values in the control register. ++ * The formula for determining the correct register values is ++ * Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1)) ++ * See the hardware data sheet for a full explanation of setting the serial ++ * clock rate. The clock can not be faster than the input clock divide by 22. ++ * The two most common clock rates are 100KHz and 400KHz. ++ */ ++static int xi2cps_setclk(unsigned int fscl, struct xi2cps *id) ++{ ++ unsigned int div_a, div_b; ++ unsigned int ctrl_reg; ++ unsigned int err; ++ int ret = 0; ++ ++ ret = xi2cps_calc_divs(&fscl, id->input_clk, &div_a, &div_b, &err); ++ if (ret) ++ return ret; ++ ++ ctrl_reg = xi2cps_readreg(XI2CPS_CR_OFFSET); ++ ctrl_reg &= ~(0x0000C000 | 0x00003F00); ++ ctrl_reg |= ((div_a << 14) | (div_b << 8)); ++ xi2cps_writereg(ctrl_reg, XI2CPS_CR_OFFSET); ++ ++ return 0; ++} ++ ++/** ++ * xi2cps_clk_notifier_cb - Clock rate change callback ++ * @nb: Pointer to notifier block ++ * @event: Notification reason ++ * @data: Pointer to notification data object ++ * Returns NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK ++ * otherwise. ++ * ++ * This function is called when the xi2cps input clock frequency changes. In the ++ * pre-rate change notification here it is determined if the rate change may be ++ * allowed or not. ++ * In th post-change case necessary adjustments are conducted. ++ */ ++static int xi2cps_clk_notifier_cb(struct notifier_block *nb, unsigned long ++ event, void *data) ++{ ++ struct clk_notifier_data *ndata = data; ++ struct xi2cps *id = to_xi2cps(nb); ++ ++ if (id->suspended) ++ return NOTIFY_OK; ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ { ++ /* ++ * if a rate change is announced we need to check whether we can ++ * maintain the current frequency by changing the clock ++ * dividers. Probably we could also define an acceptable ++ * frequency range. ++ */ ++ unsigned int input_clk = (unsigned int)ndata->new_rate; ++ unsigned int fscl = id->i2c_clk; ++ unsigned int div_a, div_b; ++ unsigned int err = 0; ++ int ret; ++ ++ ret = xi2cps_calc_divs(&fscl, input_clk, &div_a, &div_b, &err); ++ if (ret) ++ return NOTIFY_STOP; ++ if (err > MAX_F_ERR) ++ return NOTIFY_STOP; ++ ++ return NOTIFY_OK; ++ } ++ case POST_RATE_CHANGE: ++ id->input_clk = ndata->new_rate; ++ /* We probably need to stop the HW before this and restart ++ * afterwards */ ++ xi2cps_setclk(id->i2c_clk, id); ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * xi2cps_suspend - Suspend method for the driver ++ * @_dev: Address of the platform_device structure ++ * Returns 0 on success and error value on error ++ * ++ * Put the driver into low power mode. ++ */ ++static int xi2cps_suspend(struct device *_dev) ++{ ++ struct platform_device *pdev = container_of(_dev, ++ struct platform_device, dev); ++ struct xi2cps *xi2c = platform_get_drvdata(pdev); ++ ++ clk_disable(xi2c->clk); ++ xi2c->suspended = 1; ++ ++ return 0; ++} ++ ++/** ++ * xi2cps_resume - Resume from suspend ++ * @_dev: Address of the platform_device structure ++ * Returns 0 on success and error value on error ++ * ++ * Resume operation after suspend. ++ */ ++static int xi2cps_resume(struct device *_dev) ++{ ++ struct platform_device *pdev = container_of(_dev, ++ struct platform_device, dev); ++ struct xi2cps *xi2c = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = clk_enable(xi2c->clk); ++ if (ret) { ++ dev_err(_dev, "Cannot enable clock.\n"); ++ return ret; ++ } ++ ++ xi2c->suspended = 0; ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops xi2cps_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(xi2cps_suspend, xi2cps_resume) ++}; ++#define XI2CPS_PM (&xi2cps_dev_pm_ops) ++ ++#else /* ! CONFIG_PM_SLEEP */ ++#define XI2CPS_PM NULL ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++/************************/ ++/* Platform bus binding */ ++/************************/ ++ ++/** ++ * xi2cps_probe - Platform registration call ++ * @pdev: Handle to the platform device structure ++ * ++ * Returns zero on success, negative error otherwise ++ * ++ * This function does all the memory allocation and registration for the i2c ++ * device. User can modify the address mode to 10 bit address mode using the ++ * ioctl call with option I2C_TENBIT. ++ */ ++static int xi2cps_probe(struct platform_device *pdev) ++{ ++ struct resource *r_mem = NULL; ++ struct xi2cps *id; ++ int ret = 0; ++ const unsigned int *prop; ++ /* ++ * Allocate memory for xi2cps structure. ++ * Initialize the structure to zero and set the platform data. ++ * Obtain the resource base address from platform data and remap it. ++ * Get the irq resource from platform data.Initialize the adapter ++ * structure members and also xi2cps structure. ++ */ ++ id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL); ++ if (!id) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, id); ++ ++ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ id->membase = devm_ioremap_resource(&pdev->dev, r_mem); ++ if (IS_ERR(id->membase)) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ return PTR_ERR(id->membase); ++ } ++ ++ id->irq = platform_get_irq(pdev, 0); ++ ++ prop = of_get_property(pdev->dev.of_node, "bus-id", NULL); ++ if (prop) { ++ id->adap.nr = be32_to_cpup(prop); ++ } else { ++ dev_err(&pdev->dev, "couldn't determine bus-id\n"); ++ return -ENXIO; ++ } ++ id->adap.dev.of_node = pdev->dev.of_node; ++ id->adap.algo = (struct i2c_algorithm *) &xi2cps_algo; ++ id->adap.timeout = 0x1F; /* Default timeout value */ ++ id->adap.retries = 3; /* Default retry value. */ ++ id->adap.algo_data = id; ++ id->adap.dev.parent = &pdev->dev; ++ snprintf(id->adap.name, sizeof(id->adap.name), ++ "XILINX I2C at %08lx", (unsigned long)r_mem->start); ++ ++ id->cur_timeout = id->adap.timeout; ++ id->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(id->clk)) { ++ dev_err(&pdev->dev, "input clock not found.\n"); ++ return PTR_ERR(id->clk); ++ } ++ ret = clk_prepare_enable(id->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable clock.\n"); ++ return ret; ++ } ++ id->clk_rate_change_nb.notifier_call = xi2cps_clk_notifier_cb; ++ id->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(id->clk, &id->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ id->input_clk = (unsigned int)clk_get_rate(id->clk); ++ ++ prop = of_get_property(pdev->dev.of_node, "i2c-clk", NULL); ++ if (prop) { ++ id->i2c_clk = be32_to_cpup(prop); ++ } else { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "couldn't determine i2c-clk\n"); ++ goto err_clk_dis; ++ } ++ ++ /* ++ * Set Master Mode,Normal addressing mode (7 bit address), ++ * Enable Transmission of Ack in Control Register. ++ * Set the timeout and I2C clock and request the IRQ(ISR mapped). ++ * Call to the i2c_add_numbered_adapter registers the adapter. ++ */ ++ xi2cps_writereg(0x0000000E, XI2CPS_CR_OFFSET); ++ xi2cps_writereg(id->adap.timeout, XI2CPS_TIME_OUT_OFFSET); ++ ++ ret = xi2cps_setclk(id->i2c_clk, id); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", id->i2c_clk); ++ ret = -EINVAL; ++ goto err_clk_dis; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, id->irq, xi2cps_isr, 0, ++ DRIVER_NAME, id); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); ++ goto err_clk_dis; ++ } ++ ++ ret = i2c_add_numbered_adapter(&id->adap); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "reg adap failed: %d\n", ret); ++ goto err_clk_dis; ++ } ++ ++ of_i2c_register_devices(&id->adap); ++ ++ dev_info(&pdev->dev, "%d kHz mmio %08lx irq %d\n", ++ id->i2c_clk/1000, (unsigned long)r_mem->start, id->irq); ++ ++ return 0; ++ ++err_clk_dis: ++ clk_disable_unprepare(id->clk); ++ return ret; ++} ++ ++/** ++ * xi2cps_remove - Unregister the device after releasing the resources ++ * @pdev: Handle to the platform device structure ++ * ++ * Returns zero always ++ * ++ * This function frees all the resources allocated to the device. ++ */ ++static int xi2cps_remove(struct platform_device *pdev) ++{ ++ struct xi2cps *id = platform_get_drvdata(pdev); ++ ++ i2c_del_adapter(&id->adap); ++ clk_notifier_unregister(id->clk, &id->clk_rate_change_nb); ++ clk_disable_unprepare(id->clk); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id xi2cps_of_match[] = { ++ { .compatible = "xlnx,ps7-i2c-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xi2cps_of_match); ++ ++static struct platform_driver xi2cps_drv = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = xi2cps_of_match, ++ .pm = XI2CPS_PM, ++ }, ++ .probe = xi2cps_probe, ++ .remove = xi2cps_remove, ++}; ++ ++module_platform_driver(xi2cps_drv); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx PS I2C bus driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/patches.zynq/0002-i2c-si570-merge-support-for-si570-clock-generator.patch b/patches.zynq/0002-i2c-si570-merge-support-for-si570-clock-generator.patch new file mode 100644 index 0000000..19e78b8 --- /dev/null +++ b/patches.zynq/0002-i2c-si570-merge-support-for-si570-clock-generator.patch @@ -0,0 +1,662 @@ +From 2fd63c1e6eab20cbaa637da81fcf988b23f91d66 Mon Sep 17 00:00:00 2001 +From: Rob Armstrong +Date: Tue, 24 Dec 2013 09:31:54 +0900 +Subject: i2c: si570: merge support for si570 clock generator + +This merges support for the si5790 clock generator from the Xilinx +repository (commit efc27505715e64526653f35274717c0fc56491e3 from +master branch). + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/si570.c | 576 ++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/i2c/si570.h | 31 ++ + 4 files changed, 618 insertions(+) + create mode 100644 drivers/misc/si570.c + create mode 100644 include/linux/i2c/si570.h + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -507,6 +507,16 @@ config USB_SWITCH_FSA9480 + stereo and mono audio, video, microphone and UART data to use + a common connector port. + ++config SI570 ++ tristate "Silicon Labs Si570 Clock Generator" ++ depends on I2C && SYSFS ++ help ++ If you say yes here you get support for the Silicon Labs Si570 ++ digital clock generator. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called si570 ++ + config LATTICE_ECP3_CONFIG + tristate "Lattice ECP3 FPGA bitstream configuration via SPI" + depends on SPI && SYSFS +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -50,6 +50,7 @@ obj-y += carma/ + obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o + obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ + obj-$(CONFIG_INTEL_MEI) += mei/ ++obj-$(CONFIG_SI570) += si570.o + obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ + obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o + obj-$(CONFIG_SRAM) += sram.o +--- /dev/null ++++ b/drivers/misc/si570.c +@@ -0,0 +1,576 @@ ++/* ++ * Driver for Silicon Labs Si570/Si571 Programmable XO/VCXO ++ * ++ * Copyright (C) 2010, 2011 Ericsson AB. ++ * Copyright (C) 2011 Guenter Roeck. ++ * ++ * Author: Guenter Roeck ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Si570 registers */ ++#define SI570_REG_HS_N1 7 ++#define SI570_REG_N1_RFREQ0 8 ++#define SI570_REG_RFREQ1 9 ++#define SI570_REG_RFREQ2 10 ++#define SI570_REG_RFREQ3 11 ++#define SI570_REG_RFREQ4 12 ++#define SI570_REG_CONTROL 135 ++#define SI570_REG_FREEZE_DCO 137 ++ ++#define HS_DIV_SHIFT 5 ++#define HS_DIV_MASK 0xe0 ++#define HS_DIV_OFFSET 4 ++#define N1_6_2_MASK 0x1f ++#define N1_1_0_MASK 0xc0 ++#define RFREQ_37_32_MASK 0x3f ++ ++#define SI570_FOUT_FACTORY_DFLT 156250000LL ++#define SI598_FOUT_FACTORY_DFLT 10000000LL ++ ++#define SI570_MIN_FREQ 10000000L ++#define SI570_MAX_FREQ 1417500000L ++#define SI598_MAX_FREQ 525000000L ++ ++#define FDCO_MIN 4850000000LL ++#define FDCO_MAX 5670000000LL ++#define FDCO_CENTER ((FDCO_MIN + FDCO_MAX) / 2) ++ ++#define SI570_CNTRL_RECALL (1 << 0) ++#define SI570_CNTRL_FREEZE_ADC (1 << 4) ++#define SI570_CNTRL_FREEZE_M (1 << 5) ++#define SI570_CNTRL_NEWFREQ (1 << 6) ++#define SI570_CNTRL_RESET (1 << 7) ++ ++#define SI570_FREEZE_DCO (1 << 4) ++#define SI570_UNFREEZE_DCO 0xEF ++ ++struct si570_data { ++ struct attribute_group attrs; ++ struct mutex lock; ++ u64 max_freq; ++ u64 fout; /* Factory default frequency */ ++ u64 fxtal; /* Factory xtal frequency */ ++ unsigned int n1; ++ unsigned int hs_div; ++ u64 rfreq; ++ u64 frequency; ++}; ++ ++ ++static struct i2c_client *si570_client; ++ ++ ++static int si570_get_defaults(struct i2c_client *client) ++{ ++ struct si570_data *data = i2c_get_clientdata(client); ++ int reg1, reg2, reg3, reg4, reg5, reg6; ++ u64 fdco; ++ ++ i2c_smbus_write_byte_data(client, SI570_REG_CONTROL, ++ SI570_CNTRL_RECALL); ++ ++ reg1 = i2c_smbus_read_byte_data(client, SI570_REG_HS_N1); ++ if (reg1 < 0) ++ return reg1; ++ reg2 = i2c_smbus_read_byte_data(client, SI570_REG_N1_RFREQ0); ++ if (reg2 < 0) ++ return reg2; ++ reg3 = i2c_smbus_read_byte_data(client, SI570_REG_RFREQ1); ++ if (reg3 < 0) ++ return reg3; ++ reg4 = i2c_smbus_read_byte_data(client, SI570_REG_RFREQ2); ++ if (reg4 < 0) ++ return reg4; ++ reg5 = i2c_smbus_read_byte_data(client, SI570_REG_RFREQ3); ++ if (reg5 < 0) ++ return reg5; ++ reg6 = i2c_smbus_read_byte_data(client, SI570_REG_RFREQ4); ++ if (reg6 < 0) ++ return reg6; ++ ++ data->hs_div = ((reg1 & HS_DIV_MASK) >> HS_DIV_SHIFT) + HS_DIV_OFFSET; ++ data->n1 = ((reg1 & N1_6_2_MASK) << 2) + ((reg2 & N1_1_0_MASK) >> 6) ++ + 1; ++ /* Handle invalid cases */ ++ if (data->n1 > 1) ++ data->n1 &= ~1; ++ ++ data->rfreq = reg2 & RFREQ_37_32_MASK; ++ data->rfreq = (data->rfreq << 8) + reg3; ++ data->rfreq = (data->rfreq << 8) + reg4; ++ data->rfreq = (data->rfreq << 8) + reg5; ++ data->rfreq = (data->rfreq << 8) + reg6; ++ ++ /* ++ * Accept optional precision loss to avoid arithmetic overflows. ++ * Acceptable per Silicon Labs Application Note AN334. ++ */ ++ fdco = data->fout * data->n1 * data->hs_div; ++ if (fdco >= (1LL << 36)) ++ data->fxtal = div64_u64((fdco << 24), (data->rfreq >> 4)); ++ else ++ data->fxtal = div64_u64((fdco << 28), data->rfreq); ++ ++ data->frequency = data->fout; ++ ++ return 0; ++} ++ ++/* ++ * Update rfreq registers ++ * This function must be called with update mutex lock held. ++ */ ++static void si570_update_rfreq(struct i2c_client *client, ++ struct si570_data *data) ++{ ++ int status; ++ status = i2c_smbus_write_byte_data(client, SI570_REG_N1_RFREQ0, ++ ((data->n1 - 1) << 6) ++ | ((data->rfreq >> 32) & RFREQ_37_32_MASK)); ++ if (status < 0) ++ dev_err(&client->dev, ++ "unable to write 0x%llX to REG_N1_RFREQ0: %d\n", ++ (((data->n1 - 1) << 6) | ((data->rfreq >> 32) & ++ RFREQ_37_32_MASK)) & 0xff, status); ++ status = i2c_smbus_write_byte_data(client, SI570_REG_RFREQ1, ++ (data->rfreq >> 24) & 0xff); ++ if (status < 0) ++ dev_err(&client->dev, ++ "unable to write 0x%llX to REG_RFREQ1: %d\n", ++ (data->rfreq >> 24) & 0xff, status); ++ status = i2c_smbus_write_byte_data(client, SI570_REG_RFREQ2, ++ (data->rfreq >> 16) & 0xff); ++ if (status < 0) ++ dev_err(&client->dev, ++ "unable to write 0x%llX to REG_RFREQ2: %d\n", ++ (data->rfreq >> 16) & 0xff, status); ++ status = i2c_smbus_write_byte_data(client, SI570_REG_RFREQ3, ++ (data->rfreq >> 8) & 0xff); ++ if (status < 0) ++ dev_err(&client->dev, ++ "unable to write 0x%llX to REG_RFREQ3: %d\n", ++ (data->rfreq >> 8) & 0xff, status); ++ status = i2c_smbus_write_byte_data(client, SI570_REG_RFREQ4, ++ data->rfreq & 0xff); ++ if (status < 0) ++ dev_err(&client->dev, ++ "unable to write 0x%llX to REG_RFREQ4: %d\n", ++ data->rfreq & 0xff, status); ++} ++ ++/* ++ * Update si570 frequency for small frequency changes (< 3,500 ppm) ++ * This function must be called with update mutex lock held. ++ */ ++static int si570_set_frequency_small(struct i2c_client *client, ++ struct si570_data *data, ++ unsigned long frequency) ++{ ++ data->frequency = frequency; ++ /* This is a re-implementation of DIV_ROUND_CLOSEST ++ * using the div64_u64 function lieu of letting the compiler ++ * insert EABI calls ++ */ ++ data->rfreq = div64_u64((data->rfreq * frequency) + ++ div64_u64(data->frequency, 2), data->frequency); ++ i2c_smbus_write_byte_data(client, SI570_REG_CONTROL, ++ SI570_CNTRL_FREEZE_M); ++ si570_update_rfreq(client, data); ++ i2c_smbus_write_byte_data(client, SI570_REG_CONTROL, 0); ++ ++ return 0; ++} ++ ++static const uint8_t si570_hs_div_values[] = { 11, 9, 7, 6, 5, 4 }; ++ ++/* ++ * Set si570 frequency. ++ * This function must be called with update mutex lock held. ++ */ ++static int si570_set_frequency(struct i2c_client *client, ++ struct si570_data *data, ++ unsigned long frequency) ++{ ++ int i, n1, hs_div; ++ u64 fdco, best_fdco = ULLONG_MAX; ++ ++ for (i = 0; i < ARRAY_SIZE(si570_hs_div_values); i++) { ++ hs_div = si570_hs_div_values[i]; ++ /* Calculate lowest possible value for n1 */ ++ n1 = div64_u64(div64_u64(FDCO_MIN, (u64)hs_div), ++ (u64)frequency); ++ if (!n1 || (n1 & 1)) ++ n1++; ++ while (n1 <= 128) { ++ fdco = (u64)frequency * (u64)hs_div * (u64)n1; ++ if (fdco > FDCO_MAX) ++ break; ++ if (fdco >= FDCO_MIN && fdco < best_fdco) { ++ data->n1 = n1; ++ data->hs_div = hs_div; ++ data->frequency = frequency; ++ data->rfreq = div64_u64((fdco << 28), ++ data->fxtal); ++ best_fdco = fdco; ++ } ++ n1 += (n1 == 1 ? 1 : 2); ++ } ++ } ++ if (best_fdco == ULLONG_MAX) { ++ dev_err(&client->dev, "error - best FDCO is out of range\n"); ++ return -EINVAL; ++ } ++ ++ /* The DCO reg should be accessed with a read-modify-write operation ++ * per AN334 ++ */ ++ i2c_smbus_write_byte_data(client, SI570_REG_FREEZE_DCO, ++ SI570_FREEZE_DCO); ++ i2c_smbus_write_byte_data(client, SI570_REG_HS_N1, ++ ((data->hs_div - HS_DIV_OFFSET) << ++ HS_DIV_SHIFT) ++ | (((data->n1 - 1) >> 2) & N1_6_2_MASK)); ++ si570_update_rfreq(client, data); ++ i2c_smbus_write_byte_data(client, SI570_REG_FREEZE_DCO, ++ 0); ++ i2c_smbus_write_byte_data(client, SI570_REG_CONTROL, ++ SI570_CNTRL_NEWFREQ); ++ return 0; ++} ++ ++/* ++ * Reset chip. ++ * This function must be called with update mutex lock held. ++ */ ++static int si570_reset(struct i2c_client *client, struct si570_data *data) ++{ ++ i2c_smbus_write_byte_data(client, SI570_REG_CONTROL, ++ SI570_CNTRL_RESET); ++ usleep_range(1000, 5000); ++ return si570_set_frequency(client, data, data->frequency); ++} ++ ++static ssize_t show_frequency_attr(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct si570_data *data = i2c_get_clientdata(client); ++ ++ return sprintf(buf, "%llu\n", data->frequency); ++} ++ ++int get_frequency_si570(struct device *dev, unsigned long *freq) ++{ ++ int err; ++ char buf[10+1]; ++ ++ if ((!dev) || (to_i2c_client(dev) != si570_client)) ++ return -EINVAL; ++ ++ show_frequency_attr(dev, NULL, buf); ++ ++ err = kstrtoul(buf, 10, freq); ++ if (err) ++ return err; ++ ++ return 0; ++} ++EXPORT_SYMBOL(get_frequency_si570); ++ ++static ssize_t set_frequency_attr(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct si570_data *data = i2c_get_clientdata(client); ++ unsigned long val; ++ int err; ++ ++ err = kstrtoul(buf, 10, &val); ++ if (err) ++ return err; ++ ++ if (val < SI570_MIN_FREQ || val > data->max_freq) { ++ dev_err(&client->dev, ++ "requested frequency %lu Hz is out of range\n", val); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&data->lock); ++ ++ if (div64_u64(abs(val - data->frequency) * 10000LL, ++ data->frequency) < 35) ++ err = si570_set_frequency_small(client, data, val); ++ else ++ err = si570_set_frequency(client, data, val); ++ mutex_unlock(&data->lock); ++ if (err) { ++ dev_warn(&client->dev, ++ "unable to set output frequency %lu Hz: %d\n", ++ val, err); ++ return err; ++ } ++ ++ dev_info(&client->dev, ++ "set new output frequency %lu Hz\n", val); ++ ++ return count; ++} ++ ++int set_frequency_si570(struct device *dev, unsigned long freq) ++{ ++ char buf[10+1]; ++ ++ if ((!dev) || (to_i2c_client(dev) != si570_client)) ++ return -EINVAL; ++ ++ sprintf(buf, "%lu", freq); ++ ++ return set_frequency_attr(dev, NULL, buf, 0); ++} ++EXPORT_SYMBOL(set_frequency_si570); ++ ++static ssize_t show_reset_attr(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", 0); ++} ++ ++static ssize_t set_reset_attr(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct si570_data *data = i2c_get_clientdata(client); ++ unsigned long val; ++ int err; ++ ++ err = kstrtoul(buf, 10, &val); ++ if (err) ++ return err; ++ if (val == 0) ++ goto done; ++ ++ mutex_lock(&data->lock); ++ err = si570_reset(client, data); ++ mutex_unlock(&data->lock); ++ if (err) ++ return err; ++done: ++ return count; ++} ++ ++int reset_si570(struct device *dev, int id) ++{ ++ char buf[4]; ++ ++ if ((!dev) || (to_i2c_client(dev) != si570_client)) ++ return -EINVAL; ++ ++ sprintf(buf, "%lu", (unsigned long)id); ++ return set_reset_attr(dev, NULL, buf, 0); ++} ++EXPORT_SYMBOL(reset_si570); ++ ++struct i2c_client *get_i2c_client_si570(void) ++{ ++ return si570_client; ++} ++EXPORT_SYMBOL(get_i2c_client_si570); ++ ++static DEVICE_ATTR(frequency, S_IWUSR | S_IRUGO, show_frequency_attr, ++ set_frequency_attr); ++static DEVICE_ATTR(reset, S_IWUSR | S_IRUGO, show_reset_attr, set_reset_attr); ++ ++static struct attribute *si570_attr[] = { ++ &dev_attr_frequency.attr, ++ &dev_attr_reset.attr, ++ NULL ++}; ++ ++static const struct i2c_device_id si570_id[] = { ++ { "si570", 0 }, ++ { "si571", 0 }, ++ { "si598", 1 }, ++ { "si599", 1 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, si570_id); ++ ++static int si570_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct si570_platform_data *pdata = client->dev.platform_data; ++ struct si570_data *data; ++ int err; ++ unsigned long initial_fout; ++ u32 tmp = SI570_FOUT_FACTORY_DFLT; ++ ++ data = kzalloc(sizeof(struct si570_data), GFP_KERNEL); ++ if (!data) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ if (id->driver_data) { ++ data->fout = SI598_FOUT_FACTORY_DFLT; ++ data->max_freq = SI598_MAX_FREQ; ++ } else { ++ data->fout = SI570_FOUT_FACTORY_DFLT; ++ data->max_freq = SI570_MAX_FREQ; ++ } ++ ++ if (pdata && pdata->factory_fout) ++ data->fout = pdata->factory_fout; ++ ++ if (client->dev.of_node && ++ (of_property_read_u32(client->dev.of_node, "factory-fout", ++ &tmp) < 0)) ++ dev_warn(&client->dev, ++ "DTS does not contain factory-fout, using default\n"); ++ else ++ data->fout = tmp; ++ ++ i2c_set_clientdata(client, data); ++ err = si570_get_defaults(client); ++ if (err < 0) ++ goto exit_free; ++ ++ mutex_init(&data->lock); ++ ++ /* Register sysfs hooks */ ++ data->attrs.attrs = si570_attr; ++ err = sysfs_create_group(&client->dev.kobj, &data->attrs); ++ if (err) ++ goto exit_free; ++ ++ /* Display a message indicating that we've successfully registered */ ++ dev_info(&client->dev, ++ "registered %s with default frequency %llu Hz\n", ++ id->name, data->fout); ++ ++ /* Read the requested initial fout from either platform data or the ++ * device tree ++ */ ++ initial_fout = 0; ++ if (pdata && pdata->initial_fout) ++ initial_fout = pdata->initial_fout; ++ if (client->dev.of_node) { ++ of_property_read_u32(client->dev.of_node, "initial-fout", ++ (u32 *)&initial_fout); ++ if (pdata && pdata->initial_fout && ++ (pdata->initial_fout != initial_fout)) { ++ dev_warn(&client->dev, ++ "OF initial fout %lu overrides platform data fout %lu\n", ++ initial_fout, ++ pdata->initial_fout); ++ } ++ } ++ ++ if (initial_fout != 0) { ++ if (initial_fout < SI570_MIN_FREQ || ++ initial_fout > data->max_freq) { ++ dev_err(&client->dev, ++ "requested initial frequency %lu is out of range, using default\n", ++ initial_fout); ++ return 0; ++ } ++ ++ mutex_lock(&data->lock); ++ ++ if (div64_u64(abs(initial_fout - data->frequency) * ++ 10000LL, data->frequency) < 35) ++ err = si570_set_frequency_small(client, data, ++ initial_fout); ++ else ++ err = si570_set_frequency(client, data, ++ initial_fout); ++ mutex_unlock(&data->lock); ++ if (err) { ++ dev_warn(&client->dev, ++ "unable to set initial output frequency %lu: %d\n", ++ initial_fout, err); ++ return err; ++ } ++ ++ dev_info(&client->dev, ++ "set initial output frequency %lu Hz\n", ++ initial_fout); ++ } ++ ++ si570_client = client; ++ ++ return 0; ++ ++exit_free: ++ kfree(data); ++exit: ++ return err; ++} ++ ++static int si570_remove(struct i2c_client *client) ++{ ++ struct si570_data *data = i2c_get_clientdata(client); ++ ++ sysfs_remove_group(&client->dev.kobj, &data->attrs); ++ kfree(data); ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id i2c_si570_of_match[] = { ++ { .compatible = "si570" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, i2c_si570_of_match); ++#endif ++ ++static struct i2c_driver si570_driver = { ++ .driver = { ++ .name = "si570", ++ .of_match_table = of_match_ptr(i2c_si570_of_match), ++ }, ++ .probe = si570_probe, ++ .remove = si570_remove, ++ .id_table = si570_id, ++}; ++ ++static int __init si570_init(void) ++{ ++ return i2c_add_driver(&si570_driver); ++} ++ ++static void __exit si570_exit(void) ++{ ++ i2c_del_driver(&si570_driver); ++} ++ ++MODULE_AUTHOR("Guenter Roeck "); ++MODULE_DESCRIPTION("Si570 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(si570_init); ++module_exit(si570_exit); +--- /dev/null ++++ b/include/linux/i2c/si570.h +@@ -0,0 +1,31 @@ ++/* ++ * si570.h - Configuration for si570 misc driver. ++ * ++ * 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 (version 2 of the License only). ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_SI570_H ++#define __LINUX_SI570_H ++ ++#include ++#include ++#include ++ ++struct si570_platform_data { ++ u64 factory_fout; /* Factory default output frequency */ ++ unsigned long initial_fout; /* Requested initial frequency */ ++}; ++ ++int get_frequency_si570(struct device *dev, unsigned long *freq); ++int set_frequency_si570(struct device *dev, unsigned long freq); ++int reset_si570(struct device *dev, int id); ++struct i2c_client *get_i2c_client_si570(void); ++ ++#endif /* __LINUX_SI570_H */ diff --git a/patches.zynq/0003-mmc-arasan-add-a-driver-for-Arasan-s-SDHCI-controlle.patch b/patches.zynq/0003-mmc-arasan-add-a-driver-for-Arasan-s-SDHCI-controlle.patch new file mode 100644 index 0000000..e312ac4 --- /dev/null +++ b/patches.zynq/0003-mmc-arasan-add-a-driver-for-Arasan-s-SDHCI-controlle.patch @@ -0,0 +1,327 @@ +From 85d930d0b091235da43623db984ef49b982f9881 Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Fri, 20 Dec 2013 09:23:18 +0900 +Subject: mmc: arasan: add a driver for Arasan's SDHCI controller core. + +As discussed, I left binding and license header as is and fixed the typos. +v3: + - fix typo in binding documentation + - add missing '>' in MODULE_AUTHOR string +v2: + - document 'interrupts' and 'interrupt-parent' properties in the driver + bindings + - append '-8.9a' IP version specifier to comaptibility string + +Signed-off-by: Soren Brinkmann +Acked-by: Rob Herring [binding] +(patch from https://lkml.org/lkml/2013/12/2/413) +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 27 ++ + MAINTAINERS | 1 + drivers/mmc/host/Kconfig | 12 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-arasan.c | 224 +++++++++++++++++ + 5 files changed, 265 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mmc/arasan,sdhci.txt + create mode 100644 drivers/mmc/host/sdhci-of-arasan.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +@@ -0,0 +1,27 @@ ++Device Tree Bindings for the Arasan SDHCI Controller ++ ++ The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only ++ deviations are documented here. ++ ++ [1] Documentation/devicetree/bindings/mmc/mmc.txt ++ [2] Documentation/devicetree/bindings/clock/clock-bindings.txt ++ [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt ++ ++Required Properties: ++ - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' ++ - reg: From mmc bindings: Register location and length. ++ - clocks: From clock bindings: Handles to clock inputs. ++ - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" ++ - interrupts: Interrupt specifier ++ - interrupt-parent: Phandle for the interrupt controller that services ++ interrupts for this device. ++ ++Example: ++ sdhci@e0100000 { ++ compatible = "arasan,sdhci-8.9a"; ++ reg = <0xe0100000 0x1000>; ++ clock-names = "clk_xin", "clk_ahb"; ++ clocks = <&clkc 21>, <&clkc 32>; ++ interrupt-parent = <&gic>; ++ interrupts = <0 24 4>; ++ } ; +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1311,6 +1311,7 @@ T: git git://git.xilinx.com/linux-xlnx.g + S: Supported + F: arch/arm/mach-zynq/ + F: drivers/cpuidle/cpuidle-zynq.c ++F: drivers/mmc/host/sdhci-of-arasan.c + + ARM64 PORT (AARCH64 ARCHITECTURE) + M: Catalin Marinas +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM + + If unsure, say N. + ++config MMC_SDHCI_OF_ARASAN ++ tristate "SDHCI OF support for the Arasan SDHCI controllers" ++ depends on MMC_SDHCI_PLTFM ++ depends on OF ++ help ++ This selects the Arasan Secure Digital Host Controller Interface ++ (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_SDHCI_OF_ESDHC + tristate "SDHCI OF support for the Freescale eSDHC controller" + depends on MMC_SDHCI_PLTFM +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -58,6 +58,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhc + obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o + obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o + obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o ++obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o + obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o + obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o +--- /dev/null ++++ b/drivers/mmc/host/sdhci-of-arasan.c +@@ -0,0 +1,224 @@ ++/* ++ * Arasan Secure Digital Host Controller Interface. ++ * Copyright (C) 2011 - 2012 Michal Simek ++ * Copyright (c) 2012 Wind River Systems, Inc. ++ * Copyright (C) 2013 Pengutronix e.K. ++ * Copyright (C) 2013 Xilinx Inc. ++ * ++ * Based on sdhci-of-esdhc.c ++ * ++ * Copyright (c) 2007 Freescale Semiconductor, Inc. ++ * Copyright (c) 2009 MontaVista Software, Inc. ++ * ++ * Authors: Xiaobo Xie ++ * Anton Vorontsov ++ * ++ * 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 ++#include "sdhci-pltfm.h" ++ ++#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c ++ ++#define CLK_CTRL_TIMEOUT_SHIFT 16 ++#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) ++#define CLK_CTRL_TIMEOUT_MIN_EXP 13 ++ ++/** ++ * struct sdhci_arasan_data ++ * @clk_ahb: Pointer to the AHB clock ++ */ ++struct sdhci_arasan_data { ++ struct clk *clk_ahb; ++}; ++ ++static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) ++{ ++ u32 div; ++ unsigned long freq; ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ ++ div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); ++ div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; ++ ++ freq = clk_get_rate(pltfm_host->clk); ++ freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); ++ ++ return freq; ++} ++ ++static struct sdhci_ops sdhci_arasan_ops = { ++ .get_max_clock = sdhci_pltfm_clk_get_max_clock, ++ .get_timeout_clock = sdhci_arasan_get_timeout_clock, ++}; ++ ++static struct sdhci_pltfm_data sdhci_arasan_pdata = { ++ .ops = &sdhci_arasan_ops, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * sdhci_arasan_suspend - Suspend method for the driver ++ * @dev: Address of the device structure ++ * Returns 0 on success and error value on error ++ * ++ * Put the device in a low power state. ++ */ ++static int sdhci_arasan_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; ++ int ret; ++ ++ ret = sdhci_suspend_host(host); ++ if (ret) ++ return ret; ++ ++ clk_disable(pltfm_host->clk); ++ clk_disable(sdhci_arasan->clk_ahb); ++ ++ return 0; ++} ++ ++/** ++ * sdhci_arasan_resume - Resume method for the driver ++ * @dev: Address of the device structure ++ * Returns 0 on success and error value on error ++ * ++ * Resume operation after suspend ++ */ ++static int sdhci_arasan_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; ++ int ret; ++ ++ ret = clk_enable(sdhci_arasan->clk_ahb); ++ if (ret) { ++ dev_err(dev, "Cannot enable AHB clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_enable(pltfm_host->clk); ++ if (ret) { ++ dev_err(dev, "Cannot enable SD clock.\n"); ++ clk_disable(sdhci_arasan->clk_ahb); ++ return ret; ++ } ++ ++ return sdhci_resume_host(host); ++} ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, ++ sdhci_arasan_resume); ++ ++static int sdhci_arasan_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct clk *clk_xin; ++ struct sdhci_host *host; ++ struct sdhci_pltfm_host *pltfm_host; ++ struct sdhci_arasan_data *sdhci_arasan; ++ ++ sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan), ++ GFP_KERNEL); ++ if (!sdhci_arasan) ++ return -ENOMEM; ++ ++ sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); ++ if (IS_ERR(sdhci_arasan->clk_ahb)) { ++ dev_err(&pdev->dev, "clk_ahb clock not found.\n"); ++ return PTR_ERR(sdhci_arasan->clk_ahb); ++ } ++ ++ clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); ++ if (IS_ERR(clk_xin)) { ++ dev_err(&pdev->dev, "clk_xin clock not found.\n"); ++ return PTR_ERR(clk_xin); ++ } ++ ++ ret = clk_prepare_enable(sdhci_arasan->clk_ahb); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clk_xin); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable SD clock.\n"); ++ goto clk_dis_ahb; ++ } ++ ++ host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata); ++ if (IS_ERR(host)) { ++ ret = PTR_ERR(host); ++ dev_err(&pdev->dev, "platform init failed (%u)\n", ret); ++ goto clk_disable_all; ++ } ++ ++ sdhci_get_of_property(pdev); ++ pltfm_host = sdhci_priv(host); ++ pltfm_host->priv = sdhci_arasan; ++ pltfm_host->clk = clk_xin; ++ ++ ret = sdhci_add_host(host); ++ if (ret) { ++ dev_err(&pdev->dev, "platform register failed (%u)\n", ret); ++ goto err_pltfm_free; ++ } ++ ++ return 0; ++ ++err_pltfm_free: ++ sdhci_pltfm_free(pdev); ++clk_disable_all: ++ clk_disable_unprepare(clk_xin); ++clk_dis_ahb: ++ clk_disable_unprepare(sdhci_arasan->clk_ahb); ++ ++ return ret; ++} ++ ++static int sdhci_arasan_remove(struct platform_device *pdev) ++{ ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; ++ ++ clk_disable_unprepare(pltfm_host->clk); ++ clk_disable_unprepare(sdhci_arasan->clk_ahb); ++ ++ return sdhci_pltfm_unregister(pdev); ++} ++ ++static const struct of_device_id sdhci_arasan_of_match[] = { ++ { .compatible = "arasan,sdhci-8.9a" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); ++ ++static struct platform_driver sdhci_arasan_driver = { ++ .driver = { ++ .name = "sdhci-arasan", ++ .owner = THIS_MODULE, ++ .of_match_table = sdhci_arasan_of_match, ++ .pm = &sdhci_arasan_dev_pm_ops, ++ }, ++ .probe = sdhci_arasan_probe, ++ .remove = sdhci_arasan_remove, ++}; ++ ++module_platform_driver(sdhci_arasan_driver); ++ ++MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); ++MODULE_AUTHOR("Soeren Brinkmann "); ++MODULE_LICENSE("GPL"); diff --git a/patches.zynq/0004-net-ethernet-xilinx-Merge-driver-from-Xilinx-reposit.patch b/patches.zynq/0004-net-ethernet-xilinx-Merge-driver-from-Xilinx-reposit.patch new file mode 100644 index 0000000..8021561 --- /dev/null +++ b/patches.zynq/0004-net-ethernet-xilinx-Merge-driver-from-Xilinx-reposit.patch @@ -0,0 +1,4349 @@ +From 4158b8d5c7b3e5a2af192c9790a7c759834f8852 Mon Sep 17 00:00:00 2001 +From: Michal Simek +Date: Fri, 20 Dec 2013 10:18:12 +0900 +Subject: net: ethernet: xilinx: Merge driver from Xilinx repository + +Update the Xilinx ethernet driver used in ZYNQ to the one in the +Xilinx repository. +(commit efc27505715e64526653f35274717c0fc56491e3 in master branch). + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/net/ethernet/xilinx/Kconfig | 19 + drivers/net/ethernet/xilinx/Makefile | 1 + drivers/net/ethernet/xilinx/ll_temac_main.c | 48 + drivers/net/ethernet/xilinx/ll_temac_mdio.c | 5 + drivers/net/ethernet/xilinx/xilinx_axienet.h | 78 + drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 336 +- + drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 86 + drivers/net/ethernet/xilinx/xilinx_emaclite.c | 51 + drivers/net/ethernet/xilinx/xilinx_emacps.c | 2912 ++++++++++++++++++++++ + 9 files changed, 3261 insertions(+), 275 deletions(-) + create mode 100644 drivers/net/ethernet/xilinx/xilinx_emacps.c + +--- a/drivers/net/ethernet/xilinx/Kconfig ++++ b/drivers/net/ethernet/xilinx/Kconfig +@@ -27,18 +27,25 @@ config XILINX_EMACLITE + + config XILINX_AXI_EMAC + tristate "Xilinx 10/100/1000 AXI Ethernet support" +- depends on MICROBLAZE ++ depends on (MICROBLAZE || ARCH_ZYNQ) + select PHYLIB + ---help--- + This driver supports the 10/100/1000 Ethernet from Xilinx for the + AXI bus interface used in Xilinx Virtex FPGAs. + +-config XILINX_LL_TEMAC +- tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" +- depends on (PPC || MICROBLAZE) ++config XILINX_PS_EMAC ++ tristate "Xilinx PS tri-speed EMAC support" ++ depends on ARCH_ZYNQ + select PHYLIB + ---help--- +- This driver supports the Xilinx 10/100/1000 LocalLink TEMAC +- core used in Xilinx Spartan and Virtex FPGAs ++ This driver supports tri-speed EMAC. ++ ++config XILINX_PS_EMAC_HWTSTAMP ++ bool "Generate hardware packet timestamps" ++ depends on XILINX_PS_EMAC ++ default n ++ ---help--- ++ Generate hardare packet timestamps. This is to facilitate IEE 1588. ++ + + endif # NET_VENDOR_XILINX +--- a/drivers/net/ethernet/xilinx/Makefile ++++ b/drivers/net/ethernet/xilinx/Makefile +@@ -5,5 +5,6 @@ + ll_temac-objs := ll_temac_main.o ll_temac_mdio.o + obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o + obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o ++obj-$(CONFIG_XILINX_PS_EMAC) += xilinx_emacps.o + xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o + obj-$(CONFIG_XILINX_AXI_EMAC) += xilinx_emac.o +--- a/drivers/net/ethernet/xilinx/ll_temac_main.c ++++ b/drivers/net/ethernet/xilinx/ll_temac_main.c +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -244,15 +243,15 @@ static int temac_dma_bd_init(struct net_ + + /* allocate the tx and rx ring buffer descriptors. */ + /* returns a virtual address and a physical address. */ +- lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, +- sizeof(*lp->tx_bd_v) * TX_BD_NUM, +- &lp->tx_bd_p, GFP_KERNEL); ++ lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, ++ sizeof(*lp->tx_bd_v) * TX_BD_NUM, ++ &lp->tx_bd_p, GFP_KERNEL | __GFP_ZERO); + if (!lp->tx_bd_v) + goto out; + +- lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, +- sizeof(*lp->rx_bd_v) * RX_BD_NUM, +- &lp->rx_bd_p, GFP_KERNEL); ++ lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, ++ sizeof(*lp->rx_bd_v) * RX_BD_NUM, ++ &lp->rx_bd_p, GFP_KERNEL | __GFP_ZERO); + if (!lp->rx_bd_v) + goto out; + +@@ -298,12 +297,6 @@ static int temac_dma_bd_init(struct net_ + lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); + lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); + +- /* Init descriptor indexes */ +- lp->tx_bd_ci = 0; +- lp->tx_bd_next = 0; +- lp->tx_bd_tail = 0; +- lp->rx_bd_ci = 0; +- + return 0; + + out: +@@ -685,15 +678,12 @@ static int temac_start_xmit(struct sk_bu + skb_frag_t *frag; + + num_frag = skb_shinfo(skb)->nr_frags; +- frag = &skb_shinfo(skb)->frags[0]; + start_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; + cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; + + if (temac_check_tx_bd_space(lp, num_frag)) { +- if (!netif_queue_stopped(ndev)) { ++ if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); +- return NETDEV_TX_BUSY; +- } + return NETDEV_TX_BUSY; + } + +@@ -709,11 +699,12 @@ static int temac_start_xmit(struct sk_bu + + cur_p->app0 |= STS_CTRL_APP0_SOP; + cur_p->len = skb_headlen(skb); +- cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len, +- DMA_TO_DEVICE); ++ cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, ++ skb_headlen(skb), DMA_TO_DEVICE); + cur_p->app4 = (unsigned long)skb; + + for (ii = 0; ii < num_frag; ii++) { ++ frag = &skb_shinfo(skb)->frags[ii]; + lp->tx_bd_tail++; + if (lp->tx_bd_tail >= TX_BD_NUM) + lp->tx_bd_tail = 0; +@@ -724,7 +715,6 @@ static int temac_start_xmit(struct sk_bu + skb_frag_size(frag), DMA_TO_DEVICE); + cur_p->len = skb_frag_size(frag); + cur_p->app0 = 0; +- frag++; + } + cur_p->app0 |= STS_CTRL_APP0_EOP; + +@@ -1014,7 +1004,7 @@ static int temac_of_probe(struct platfor + return -ENOMEM; + + ether_setup(ndev); +- platform_set_drvdata(op, ndev); ++ dev_set_drvdata(&op->dev, ndev); + SET_NETDEV_DEV(ndev, &op->dev); + ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ + ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; +@@ -1052,12 +1042,14 @@ static int temac_of_probe(struct platfor + /* Setup checksum offload, but default to off if not specified */ + lp->temac_features = 0; + p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); ++ dev_info(&op->dev, "TX_CSUM %d\n", be32_to_cpup(p)); + if (p && be32_to_cpu(*p)) { + lp->temac_features |= TEMAC_FEATURE_TX_CSUM; + /* Can checksum TCP/UDP over IPv4. */ + ndev->features |= NETIF_F_IP_CSUM; + } + p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); ++ dev_info(&op->dev, "RX_CSUM %d\n", be32_to_cpup(p)); + if (p && be32_to_cpu(*p)) + lp->temac_features |= TEMAC_FEATURE_RX_CSUM; + +@@ -1105,14 +1097,15 @@ static int temac_of_probe(struct platfor + } + temac_init_mac_address(ndev, (void *)addr); + +- rc = temac_mdio_setup(lp, op->dev.of_node); +- if (rc) +- dev_warn(&op->dev, "error registering MDIO bus\n"); +- + lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); +- if (lp->phy_node) ++ if (lp->phy_node) { + dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); + ++ rc = temac_mdio_setup(lp, op->dev.of_node); ++ if (rc) ++ dev_warn(&op->dev, "error registering MDIO bus\n"); ++ } ++ + /* Add the device attributes */ + rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); + if (rc) { +@@ -1143,7 +1136,7 @@ static int temac_of_probe(struct platfor + + static int temac_of_remove(struct platform_device *op) + { +- struct net_device *ndev = platform_get_drvdata(op); ++ struct net_device *ndev = dev_get_drvdata(&op->dev); + struct temac_local *lp = netdev_priv(ndev); + + temac_mdio_teardown(lp); +@@ -1152,6 +1145,7 @@ static int temac_of_remove(struct platfo + if (lp->phy_node) + of_node_put(lp->phy_node); + lp->phy_node = NULL; ++ dev_set_drvdata(&op->dev, NULL); + iounmap(lp->regs); + if (lp->sdma_regs) + iounmap(lp->sdma_regs); +--- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c ++++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c +@@ -63,6 +63,7 @@ int temac_mdio_setup(struct temac_local + int clk_div; + int rc, size; + struct resource res; ++ struct device_node *np1 = of_get_parent(lp->phy_node); + + /* Calculate a reasonable divisor for the clock rate */ + clk_div = 0x3f; /* worst-case default setting */ +@@ -85,7 +86,7 @@ int temac_mdio_setup(struct temac_local + if (!bus) + return -ENOMEM; + +- of_address_to_resource(np, 0, &res); ++ of_address_to_resource(np1, 0, &res); + snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long)res.start); + bus->priv = lp; +@@ -97,7 +98,7 @@ int temac_mdio_setup(struct temac_local + + lp->mii_bus = bus; + +- rc = of_mdiobus_register(bus, np); ++ rc = of_mdiobus_register(bus, np1); + if (rc) + goto err_register; + +--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h +@@ -9,18 +9,19 @@ + #define XILINX_AXIENET_H + + #include ++#include + #include + #include ++#include + + /* Packet size info */ + #define XAE_HDR_SIZE 14 /* Size of Ethernet header */ +-#define XAE_HDR_VLAN_SIZE 18 /* Size of an Ethernet hdr + VLAN */ + #define XAE_TRL_SIZE 4 /* Size of Ethernet trailer (FCS) */ + #define XAE_MTU 1500 /* Max MTU of an Ethernet frame */ + #define XAE_JUMBO_MTU 9000 /* Max MTU of a jumbo Eth. frame */ + + #define XAE_MAX_FRAME_SIZE (XAE_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE) +-#define XAE_MAX_VLAN_FRAME_SIZE (XAE_MTU + XAE_HDR_VLAN_SIZE + XAE_TRL_SIZE) ++#define XAE_MAX_VLAN_FRAME_SIZE (XAE_MTU + VLAN_ETH_HLEN + XAE_TRL_SIZE) + #define XAE_MAX_JUMBO_FRAME_SIZE (XAE_JUMBO_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE) + + /* Configuration options */ +@@ -66,20 +67,20 @@ + + /* Axi DMA Register definitions */ + +-#define XAXIDMA_TX_CR_OFFSET 0x00000000 /* Channel control */ +-#define XAXIDMA_TX_SR_OFFSET 0x00000004 /* Status */ +-#define XAXIDMA_TX_CDESC_OFFSET 0x00000008 /* Current descriptor pointer */ +-#define XAXIDMA_TX_TDESC_OFFSET 0x00000010 /* Tail descriptor pointer */ +- +-#define XAXIDMA_RX_CR_OFFSET 0x00000030 /* Channel control */ +-#define XAXIDMA_RX_SR_OFFSET 0x00000034 /* Status */ +-#define XAXIDMA_RX_CDESC_OFFSET 0x00000038 /* Current descriptor pointer */ +-#define XAXIDMA_RX_TDESC_OFFSET 0x00000040 /* Tail descriptor pointer */ ++#define XAXIDMA_TX_CR_OFFSET 0x00000000 /* Channel control */ ++#define XAXIDMA_TX_SR_OFFSET 0x00000004 /* Status */ ++#define XAXIDMA_TX_CDESC_OFFSET 0x00000008 /* Current descriptor pointer */ ++#define XAXIDMA_TX_TDESC_OFFSET 0x00000010 /* Tail descriptor pointer */ ++ ++#define XAXIDMA_RX_CR_OFFSET 0x00000030 /* Channel control */ ++#define XAXIDMA_RX_SR_OFFSET 0x00000034 /* Status */ ++#define XAXIDMA_RX_CDESC_OFFSET 0x00000038 /* Current descriptor pointer */ ++#define XAXIDMA_RX_TDESC_OFFSET 0x00000040 /* Tail descriptor pointer */ + +-#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 /* Start/stop DMA channel */ +-#define XAXIDMA_CR_RESET_MASK 0x00000004 /* Reset DMA engine */ ++#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 /* Start/stop DMA channel */ ++#define XAXIDMA_CR_RESET_MASK 0x00000004 /* Reset DMA engine */ + +-#define XAXIDMA_BD_NDESC_OFFSET 0x00 /* Next descriptor pointer */ ++#define XAXIDMA_BD_NDESC_OFFSET 0x00 /* Next descriptor pointer */ + #define XAXIDMA_BD_BUFA_OFFSET 0x08 /* Buffer address */ + #define XAXIDMA_BD_CTRL_LEN_OFFSET 0x18 /* Control/buffer length */ + #define XAXIDMA_BD_STS_OFFSET 0x1C /* Status */ +@@ -93,8 +94,8 @@ + #define XAXIDMA_BD_HAS_DRE_OFFSET 0x3C /* Whether has DRE */ + + #define XAXIDMA_BD_HAS_DRE_SHIFT 8 /* Whether has DRE shift */ +-#define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */ +-#define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */ ++#define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */ ++#define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */ + + #define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */ + #define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */ +@@ -130,7 +131,7 @@ + #define XAXIDMA_BD_STS_ALL_ERR_MASK 0x70000000 /* All errors */ + #define XAXIDMA_BD_STS_RXSOF_MASK 0x08000000 /* First rx pkt */ + #define XAXIDMA_BD_STS_RXEOF_MASK 0x04000000 /* Last rx pkt */ +-#define XAXIDMA_BD_STS_ALL_MASK 0xFC000000 /* All status bits */ ++#define XAXIDMA_BD_STS_ALL_MASK 0xFC000000 /* All status bits */ + + #define XAXIDMA_BD_MINIMUM_ALIGNMENT 0x40 + +@@ -158,7 +159,7 @@ + #define XAE_MDIO_MCR_OFFSET 0x00000504 /* MII Management Control */ + #define XAE_MDIO_MWD_OFFSET 0x00000508 /* MII Management Write Data */ + #define XAE_MDIO_MRD_OFFSET 0x0000050C /* MII Management Read Data */ +-#define XAE_MDIO_MIS_OFFSET 0x00000600 /* MII Management Interrupt Status */ ++#define XAE_MDIO_MIS_OFFSET 0x00000600 /* MII Management Int. Status */ + #define XAE_MDIO_MIP_OFFSET 0x00000620 /* MII Mgmt Interrupt Pending + * register offset */ + #define XAE_MDIO_MIE_OFFSET 0x00000640 /* MII Management Interrupt Enable +@@ -180,16 +181,16 @@ + * destination address */ + #define XAE_RAF_BCSTREJ_MASK 0x00000004 /* Reject receive broadcast + * destination address */ +-#define XAE_RAF_TXVTAGMODE_MASK 0x00000018 /* Tx VLAN TAG mode */ +-#define XAE_RAF_RXVTAGMODE_MASK 0x00000060 /* Rx VLAN TAG mode */ ++#define XAE_RAF_TXVTAGMODE_MASK 0x00000018 /* Tx VLAN TAG mode */ ++#define XAE_RAF_RXVTAGMODE_MASK 0x00000060 /* Rx VLAN TAG mode */ + #define XAE_RAF_TXVSTRPMODE_MASK 0x00000180 /* Tx VLAN STRIP mode */ + #define XAE_RAF_RXVSTRPMODE_MASK 0x00000600 /* Rx VLAN STRIP mode */ +-#define XAE_RAF_NEWFNCENBL_MASK 0x00000800 /* New function mode */ ++#define XAE_RAF_NEWFNCENBL_MASK 0x00000800 /* New function mode */ + #define XAE_RAF_EMULTIFLTRENBL_MASK 0x00001000 /* Exteneded Multicast + * Filtering mode + */ + #define XAE_RAF_STATSRST_MASK 0x00002000 /* Stats. Counter Reset */ +-#define XAE_RAF_RXBADFRMEN_MASK 0x00004000 /* Recv Bad Frame Enable */ ++#define XAE_RAF_RXBADFRMEN_MASK 0x00004000 /* Recv Bad Frame Enable */ + #define XAE_RAF_TXVTAGMODE_SHIFT 3 /* Tx Tag mode shift bits */ + #define XAE_RAF_RXVTAGMODE_SHIFT 5 /* Rx Tag mode shift bits */ + #define XAE_RAF_TXVSTRPMODE_SHIFT 7 /* Tx strip mode shift bits*/ +@@ -273,22 +274,22 @@ + #define XAE_PHYC_SGMIILINKSPEED_MASK 0xC0000000 /* SGMII link speed mask*/ + #define XAE_PHYC_RGMIILINKSPEED_MASK 0x0000000C /* RGMII link speed */ + #define XAE_PHYC_RGMIIHD_MASK 0x00000002 /* RGMII Half-duplex */ +-#define XAE_PHYC_RGMIILINK_MASK 0x00000001 /* RGMII link status */ ++#define XAE_PHYC_RGMIILINK_MASK 0x00000001 /* RGMII link status */ + #define XAE_PHYC_RGLINKSPD_10 0x00000000 /* RGMII link 10 Mbit */ + #define XAE_PHYC_RGLINKSPD_100 0x00000004 /* RGMII link 100 Mbit */ +-#define XAE_PHYC_RGLINKSPD_1000 0x00000008 /* RGMII link 1000 Mbit */ ++#define XAE_PHYC_RGLINKSPD_1000 0x00000008 /* RGMII link 1000 Mbit */ + #define XAE_PHYC_SGLINKSPD_10 0x00000000 /* SGMII link 10 Mbit */ + #define XAE_PHYC_SGLINKSPD_100 0x40000000 /* SGMII link 100 Mbit */ +-#define XAE_PHYC_SGLINKSPD_1000 0x80000000 /* SGMII link 1000 Mbit */ ++#define XAE_PHYC_SGLINKSPD_1000 0x80000000 /* SGMII link 1000 Mbit */ + + /* Bit masks for Axi Ethernet MDIO interface MC register */ +-#define XAE_MDIO_MC_MDIOEN_MASK 0x00000040 /* MII management enable */ ++#define XAE_MDIO_MC_MDIOEN_MASK 0x00000040 /* MII management enable */ + #define XAE_MDIO_MC_CLOCK_DIVIDE_MAX 0x3F /* Maximum MDIO divisor */ + + /* Bit masks for Axi Ethernet MDIO interface MCR register */ +-#define XAE_MDIO_MCR_PHYAD_MASK 0x1F000000 /* Phy Address Mask */ ++#define XAE_MDIO_MCR_PHYAD_MASK 0x1F000000 /* Phy Address Mask */ + #define XAE_MDIO_MCR_PHYAD_SHIFT 24 /* Phy Address Shift */ +-#define XAE_MDIO_MCR_REGAD_MASK 0x001F0000 /* Reg Address Mask */ ++#define XAE_MDIO_MCR_REGAD_MASK 0x001F0000 /* Reg Address Mask */ + #define XAE_MDIO_MCR_REGAD_SHIFT 16 /* Reg Address Shift */ + #define XAE_MDIO_MCR_OP_MASK 0x0000C000 /* Operation Code Mask */ + #define XAE_MDIO_MCR_OP_SHIFT 13 /* Operation Code Shift */ +@@ -312,13 +313,13 @@ + + #define XAE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */ + +-/* Defines for different options for C_PHY_TYPE parameter in Axi Ethernet IP */ ++/* Defines different options for C_PHY_TYPE parameter in Axi Ethernet IP */ + #define XAE_PHY_TYPE_MII 0 + #define XAE_PHY_TYPE_GMII 1 + #define XAE_PHY_TYPE_RGMII_1_3 2 + #define XAE_PHY_TYPE_RGMII_2_0 3 + #define XAE_PHY_TYPE_SGMII 4 +-#define XAE_PHY_TYPE_1000BASE_X 5 ++#define XAE_PHY_TYPE_1000BASE_X 5 + + #define XAE_MULTICAST_CAM_TABLE_NUM 4 /* Total number of entries in the + * hardware multicast table. */ +@@ -337,6 +338,14 @@ + + #define DELAY_OF_ONE_MILLISEC 1000 + ++/* Read/Write access to the registers */ ++#ifndef out_be32 ++#ifdef CONFIG_ARCH_ZYNQ ++#define in_be32(offset) __raw_readl(offset) ++#define out_be32(offset, val) __raw_writel(val, offset) ++#endif ++#endif ++ + /** + * struct axidma_bd - Axi Dma buffer descriptor layout + * @next: MM2S/S2MM Next Descriptor Pointer +@@ -408,8 +417,9 @@ struct axidma_bd { + * Txed/Rxed in the existing hardware. If jumbo option is + * supported, the maximum frame size would be 9k. Else it is + * 1522 bytes (assuming support for basic VLAN) +- * @jumbo_support: Stores hardware configuration for jumbo support. If hardware +- * can handle jumbo packets, this entry will be 1, else 0. ++ * @jumbo_support: Stores hardware configuration for jumbo support. If ++ * hardware can handle jumbo packets, this entry will be 1, ++ * else 0. + */ + struct axienet_local { + struct net_device *ndev; +@@ -434,7 +444,7 @@ struct axienet_local { + u32 temac_type; + u32 phy_type; + +- u32 options; /* Current options word */ ++ u32 options; /* Current options word */ + u32 last_link; + u32 features; + +@@ -448,7 +458,7 @@ struct axienet_local { + u32 rx_bd_ci; + + u32 max_frm_size; +- u32 jumbo_support; ++ u32 rxmem; + + int csum_offload_on_tx_path; + int csum_offload_on_rx_path; +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -201,15 +201,17 @@ static int axienet_dma_bd_init(struct ne + /* + * Allocate the Tx and Rx buffer descriptors. + */ +- lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, +- sizeof(*lp->tx_bd_v) * TX_BD_NUM, +- &lp->tx_bd_p, GFP_KERNEL); ++ lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, ++ sizeof(*lp->tx_bd_v) * TX_BD_NUM, ++ &lp->tx_bd_p, ++ GFP_KERNEL | __GFP_ZERO); + if (!lp->tx_bd_v) + goto out; + +- lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, +- sizeof(*lp->rx_bd_v) * RX_BD_NUM, +- &lp->rx_bd_p, GFP_KERNEL); ++ lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, ++ sizeof(*lp->rx_bd_v) * RX_BD_NUM, ++ &lp->rx_bd_p, ++ GFP_KERNEL | __GFP_ZERO); + if (!lp->rx_bd_v) + goto out; + +@@ -263,7 +265,8 @@ static int axienet_dma_bd_init(struct ne + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); + + /* Populate the tail pointer and bring the Rx Axi DMA engine out of +- * halted state. This will make the Rx side ready for reception.*/ ++ * halted state. This will make the Rx side ready for reception. ++ */ + axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); + cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, +@@ -273,7 +276,8 @@ static int axienet_dma_bd_init(struct ne + + /* Write to the RS (Run-stop) bit in the Tx channel control register. + * Tx channel is now ready to run. But only after we write to the +- * tail pointer register that the Tx channel will start transmitting */ ++ * tail pointer register that the Tx channel will start transmitting. ++ */ + axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); + cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, +@@ -354,7 +358,8 @@ static void axienet_set_multicast_list(s + netdev_mc_count(ndev) > XAE_MULTICAST_CAM_TABLE_NUM) { + /* We must make the kernel realize we had to move into + * promiscuous mode. If it was a promiscuous mode request +- * the flag is already set. If not we set it. */ ++ * the flag is already set. If not we set it. ++ */ + ndev->flags |= IFF_PROMISC; + reg = axienet_ior(lp, XAE_FMI_OFFSET); + reg |= XAE_FMI_PM_MASK; +@@ -438,14 +443,15 @@ static void __axienet_device_reset(struc + /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset + * process of Axi DMA takes a while to complete as all pending + * commands/transfers will be flushed or completed during this +- * reset process. */ ++ * reset process. ++ */ + axienet_dma_out32(lp, offset, XAXIDMA_CR_RESET_MASK); + timeout = DELAY_OF_ONE_MILLISEC; + while (axienet_dma_in32(lp, offset) & XAXIDMA_CR_RESET_MASK) { + udelay(1); + if (--timeout == 0) { +- dev_err(dev, "axienet_device_reset DMA " +- "reset timeout!\n"); ++ dev_err(dev, ++ "axienet_device_reset DMA reset timeout!\n"); + break; + } + } +@@ -471,18 +477,21 @@ static void axienet_device_reset(struct + __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET); + + lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE; ++ lp->options |= XAE_OPTION_VLAN; + lp->options &= (~XAE_OPTION_JUMBO); + + if ((ndev->mtu > XAE_MTU) && +- (ndev->mtu <= XAE_JUMBO_MTU) && +- (lp->jumbo_support)) { +- lp->max_frm_size = ndev->mtu + XAE_HDR_VLAN_SIZE + +- XAE_TRL_SIZE; +- lp->options |= XAE_OPTION_JUMBO; ++ (ndev->mtu <= XAE_JUMBO_MTU)) { ++ lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN + ++ XAE_TRL_SIZE; ++ ++ if (lp->max_frm_size <= lp->rxmem) ++ lp->options |= XAE_OPTION_JUMBO; + } + + if (axienet_dma_bd_init(ndev)) { +- dev_err(&ndev->dev, "axienet_device_reset descriptor " ++ dev_err(&ndev->dev, ++ "axienet_device_reset descriptor " + "allocation failed\n"); + } + +@@ -497,7 +506,8 @@ static void axienet_device_reset(struct + axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); + + /* Sync default options with HW but leave receiver and +- * transmitter disabled.*/ ++ * transmitter disabled. ++ */ + axienet_setoptions(ndev, lp->options & + ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); + axienet_set_mac_address(ndev, NULL); +@@ -549,7 +559,8 @@ static void axienet_adjust_link(struct n + emmc_reg |= XAE_EMMC_LINKSPD_10; + break; + default: +- dev_err(&ndev->dev, "Speed other than 10, 100 " ++ dev_err(&ndev->dev, ++ "Speed other than 10, 100 " + "or 1Gbps is not supported\n"); + break; + } +@@ -558,8 +569,8 @@ static void axienet_adjust_link(struct n + lp->last_link = link_state; + phy_print_status(phy); + } else { +- dev_err(&ndev->dev, "Error setting Axi Ethernet " +- "mac speed\n"); ++ dev_err(&ndev->dev, ++ "Error setting Axi Ethernet mac speed\n"); + } + } + } +@@ -601,7 +612,8 @@ static void axienet_start_xmit_done(stru + size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK; + packets++; + +- lp->tx_bd_ci = ++lp->tx_bd_ci % TX_BD_NUM; ++ ++lp->tx_bd_ci; ++ lp->tx_bd_ci %= TX_BD_NUM; + cur_p = &lp->tx_bd_v[lp->tx_bd_ci]; + status = cur_p->status; + } +@@ -687,7 +699,8 @@ static int axienet_start_xmit(struct sk_ + skb_headlen(skb), DMA_TO_DEVICE); + + for (ii = 0; ii < num_frag; ii++) { +- lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM; ++ ++lp->tx_bd_tail; ++ lp->tx_bd_tail %= TX_BD_NUM; + cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; + frag = &skb_shinfo(skb)->frags[ii]; + cur_p->phys = dma_map_single(ndev->dev.parent, +@@ -701,9 +714,12 @@ static int axienet_start_xmit(struct sk_ + cur_p->app4 = (unsigned long)skb; + + tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; ++ wmb(); ++ + /* Start the transfer */ + axienet_dma_out32(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p); +- lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM; ++ ++lp->tx_bd_tail; ++ lp->tx_bd_tail %= TX_BD_NUM; + + return NETDEV_TX_OK; + } +@@ -723,15 +739,16 @@ static void axienet_recv(struct net_devi + u32 csumstatus; + u32 size = 0; + u32 packets = 0; +- dma_addr_t tail_p; ++ dma_addr_t tail_p = 0; + struct axienet_local *lp = netdev_priv(ndev); + struct sk_buff *skb, *new_skb; + struct axidma_bd *cur_p; + +- tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; ++ rmb(); + cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; + + while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) { ++ tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; + skb = (struct sk_buff *) (cur_p->sw_id_offset); + length = cur_p->app4 & 0x0000FFFF; + +@@ -775,14 +792,16 @@ static void axienet_recv(struct net_devi + cur_p->status = 0; + cur_p->sw_id_offset = (u32) new_skb; + +- lp->rx_bd_ci = ++lp->rx_bd_ci % RX_BD_NUM; ++ ++lp->rx_bd_ci; ++ lp->rx_bd_ci %= RX_BD_NUM; + cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; + } + + ndev->stats.rx_packets += packets; + ndev->stats.rx_bytes += size; + +- axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); ++ if (tail_p) ++ axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); + } + + /** +@@ -804,6 +823,7 @@ static irqreturn_t axienet_tx_irq(int ir + + status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); + if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { ++ axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); + axienet_start_xmit_done(lp->ndev); + goto out; + } +@@ -827,9 +847,9 @@ static irqreturn_t axienet_tx_irq(int ir + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); + + tasklet_schedule(&lp->dma_err_tasklet); ++ axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); + } + out: +- axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); + return IRQ_HANDLED; + } + +@@ -852,6 +872,7 @@ static irqreturn_t axienet_rx_irq(int ir + + status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); + if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { ++ axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); + axienet_recv(lp->ndev); + goto out; + } +@@ -875,9 +896,9 @@ static irqreturn_t axienet_rx_irq(int ir + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); + + tasklet_schedule(&lp->dma_err_tasklet); ++ axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); + } + out: +- axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); + return IRQ_HANDLED; + } + +@@ -891,10 +912,10 @@ static void axienet_dma_err_handler(unsi + * -ENODEV, if PHY cannot be connected to + * non-zero error value on failure + * +- * This is the driver open routine. It calls phy_start to start the PHY device. +- * It also allocates interrupt service routines, enables the interrupt lines +- * and ISR handling. Axi Ethernet core is reset through Axi DMA core. Buffer +- * descriptors are initialized. ++ * This is the driver open routine. It calls phy_start to start the PHY ++ * device. It also allocates interrupt service routines, enables the ++ * interrupt lines and ISR handling. Axi Ethernet core is reset through Axi ++ * DMA core. Buffer descriptors are initialized. + */ + static int axienet_open(struct net_device *ndev) + { +@@ -910,7 +931,8 @@ static int axienet_open(struct net_devic + /* Disable the MDIO interface till Axi Ethernet Reset is completed. + * When we do an Axi Ethernet reset, it resets the complete core + * including the MDIO. If MDIO is not disabled when the reset +- * process is started, MDIO will be broken afterwards. */ ++ * process is started, MDIO will be broken afterwards. ++ */ + axienet_iow(lp, XAE_MDIO_MC_OFFSET, + (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK))); + axienet_device_reset(ndev); +@@ -921,14 +943,20 @@ static int axienet_open(struct net_devic + return ret; + + if (lp->phy_node) { +- lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, ++ if (lp->phy_type == XAE_PHY_TYPE_GMII) { ++ lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + axienet_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); +- if (!lp->phy_dev) { +- dev_err(lp->dev, "of_phy_connect() failed\n"); +- return -ENODEV; ++ } else if (lp->phy_type == XAE_PHY_TYPE_RGMII_2_0) { ++ lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, ++ axienet_adjust_link, 0, ++ PHY_INTERFACE_MODE_RGMII_ID); + } +- phy_start(lp->phy_dev); ++ ++ if (!lp->phy_dev) ++ dev_err(lp->dev, "of_phy_connect() failed\n"); ++ else ++ phy_start(lp->phy_dev); + } + + /* Enable tasklets for Axi DMA error handling */ +@@ -1013,15 +1041,15 @@ static int axienet_change_mtu(struct net + + if (netif_running(ndev)) + return -EBUSY; +- if (lp->jumbo_support) { +- if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64)) +- return -EINVAL; +- ndev->mtu = new_mtu; +- } else { +- if ((new_mtu > XAE_MTU) || (new_mtu < 64)) +- return -EINVAL; +- ndev->mtu = new_mtu; +- } ++ ++ if ((new_mtu + VLAN_ETH_HLEN + ++ XAE_TRL_SIZE) > lp->rxmem) ++ return -EINVAL; ++ ++ if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64)) ++ return -EINVAL; ++ ++ ndev->mtu = new_mtu; + + return 0; + } +@@ -1031,8 +1059,8 @@ static int axienet_change_mtu(struct net + * axienet_poll_controller - Axi Ethernet poll mechanism. + * @ndev: Pointer to net_device structure + * +- * This implements Rx/Tx ISR poll mechanisms. The interrupts are disabled prior +- * to polling the ISRs and are enabled back after the polling is done. ++ * This implements Rx/Tx ISR poll mechanisms. The interrupts are disabled ++ * prior to polling the ISRs and are enabled back after the polling is done. + */ + static void axienet_poll_controller(struct net_device *ndev) + { +@@ -1046,6 +1074,20 @@ static void axienet_poll_controller(stru + } + #endif + ++/* Ioctl MII Interface */ ++static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct axienet_local *priv = netdev_priv(dev); ++ ++ if (!netif_running(dev)) ++ return -EINVAL; ++ ++ if (!priv->phy_dev) ++ return -EOPNOTSUPP; ++ ++ return phy_mii_ioctl(priv->phy_dev, rq, cmd); ++} ++ + static const struct net_device_ops axienet_netdev_ops = { + .ndo_open = axienet_open, + .ndo_stop = axienet_stop, +@@ -1054,6 +1096,7 @@ static const struct net_device_ops axien + .ndo_set_mac_address = netdev_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = axienet_set_multicast_list, ++ .ndo_do_ioctl = axienet_ioctl, + #ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = axienet_poll_controller, + #endif +@@ -1209,7 +1252,7 @@ axienet_ethtools_get_pauseparam(struct n + * axienet_ethtools_set_pauseparam - Set device pause parameter(flow control) + * settings. + * @ndev: Pointer to net_device structure +- * @epauseparam:Pointer to ethtool_pauseparam structure ++ * @epauseparm:Pointer to ethtool_pauseparam structure + * + * This implements ethtool command for enabling flow control on Rx and Tx + * paths. Issue "ethtool -A ethX tx on|off" under linux prompt to execute this +@@ -1223,8 +1266,9 @@ axienet_ethtools_set_pauseparam(struct n + struct axienet_local *lp = netdev_priv(ndev); + + if (netif_running(ndev)) { +- printk(KERN_ERR "%s: Please stop netif before applying " +- "configruation\n", ndev->name); ++ dev_err(&ndev->dev, ++ "%s: Please stop netif before configuration\n", ++ ndev->name); + return -EFAULT; + } + +@@ -1280,8 +1324,9 @@ static int axienet_ethtools_set_coalesce + struct axienet_local *lp = netdev_priv(ndev); + + if (netif_running(ndev)) { +- printk(KERN_ERR "%s: Please stop netif before applying " +- "configruation\n", ndev->name); ++ dev_err(&ndev->dev, ++ "%s: Please stop netif before configuration\n", ++ ndev->name); + return -EFAULT; + } + +@@ -1350,7 +1395,8 @@ static void axienet_dma_err_handler(unsi + /* Disable the MDIO interface till Axi Ethernet Reset is completed. + * When we do an Axi Ethernet reset, it resets the complete core + * including the MDIO. So if MDIO is not disabled when the reset +- * process is started, MDIO will be broken afterwards. */ ++ * process is started, MDIO will be broken afterwards. ++ */ + axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg & + ~XAE_MDIO_MC_MDIOEN_MASK)); + +@@ -1421,7 +1467,8 @@ static void axienet_dma_err_handler(unsi + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); + + /* Populate the tail pointer and bring the Rx Axi DMA engine out of +- * halted state. This will make the Rx side ready for reception.*/ ++ * halted state. This will make the Rx side ready for reception. ++ */ + axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); + cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, +@@ -1431,7 +1478,8 @@ static void axienet_dma_err_handler(unsi + + /* Write to the RS (Run-stop) bit in the Tx channel control register. + * Tx channel is now ready to run. But only after we write to the +- * tail pointer register that the Tx channel will start transmitting */ ++ * tail pointer register that the Tx channel will start transmitting ++ */ + axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); + cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, +@@ -1447,7 +1495,8 @@ static void axienet_dma_err_handler(unsi + axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); + + /* Sync default options with HW but leave receiver and +- * transmitter disabled.*/ ++ * transmitter disabled. ++ */ + axienet_setoptions(ndev, lp->options & + ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); + axienet_set_mac_address(ndev, NULL); +@@ -1456,77 +1505,84 @@ static void axienet_dma_err_handler(unsi + } + + /** +- * axienet_of_probe - Axi Ethernet probe function. +- * @op: Pointer to platform device structure. +- * @match: Pointer to device id structure ++ * axienet_probe - Axi Ethernet probe function. ++ * @pdev: Pointer to platform device structure. + * + * returns: 0, on success + * Non-zero error value on failure. + * + * This is the probe routine for Axi Ethernet driver. This is called before +- * any other driver routines are invoked. It allocates and sets up the Ethernet +- * device. Parses through device tree and populates fields of ++ * any other driver routines are invoked. It allocates and sets up the ++ * Ethernet device. Parses through device tree and populates fields of + * axienet_local. It registers the Ethernet device. + */ +-static int axienet_of_probe(struct platform_device *op) ++static int axienet_probe(struct platform_device *pdev) + { +- __be32 *p; +- int size, ret = 0; ++ int ret; + struct device_node *np; + struct axienet_local *lp; + struct net_device *ndev; +- const void *addr; ++ u8 mac_addr[6]; ++ struct resource *ethres, dmares; ++ u32 value; + + ndev = alloc_etherdev(sizeof(*lp)); + if (!ndev) + return -ENOMEM; + + ether_setup(ndev); +- platform_set_drvdata(op, ndev); ++ platform_set_drvdata(pdev, ndev); + +- SET_NETDEV_DEV(ndev, &op->dev); ++ SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ +- ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; ++ ndev->features = NETIF_F_FRAGLIST; + ndev->netdev_ops = &axienet_netdev_ops; + ndev->ethtool_ops = &axienet_ethtool_ops; + + lp = netdev_priv(ndev); + lp->ndev = ndev; +- lp->dev = &op->dev; ++ lp->dev = &pdev->dev; + lp->options = XAE_OPTION_DEFAULTS; + /* Map device registers */ +- lp->regs = of_iomap(op->dev.of_node, 0); ++ ethres = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ lp->regs = devm_ioremap_resource(&pdev->dev, ethres); + if (!lp->regs) { +- dev_err(&op->dev, "could not map Axi Ethernet regs.\n"); +- goto nodev; ++ dev_err(&pdev->dev, "could not map Axi Ethernet regs.\n"); ++ ret = -ENOMEM; ++ goto free_netdev; + } ++ + /* Setup checksum offload, but default to off if not specified */ + lp->features = 0; + +- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); +- if (p) { +- switch (be32_to_cpup(p)) { ++ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value); ++ if (!ret) { ++ dev_info(&pdev->dev, "TX_CSUM %d\n", value); ++ ++ switch (value) { + case 1: + lp->csum_offload_on_tx_path = + XAE_FEATURE_PARTIAL_TX_CSUM; + lp->features |= XAE_FEATURE_PARTIAL_TX_CSUM; + /* Can checksum TCP/UDP over IPv4. */ +- ndev->features |= NETIF_F_IP_CSUM; ++ ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; + break; + case 2: + lp->csum_offload_on_tx_path = + XAE_FEATURE_FULL_TX_CSUM; + lp->features |= XAE_FEATURE_FULL_TX_CSUM; + /* Can checksum TCP/UDP over IPv4. */ +- ndev->features |= NETIF_F_IP_CSUM; ++ ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; + break; + default: + lp->csum_offload_on_tx_path = XAE_NO_CSUM_OFFLOAD; + } + } +- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); +- if (p) { +- switch (be32_to_cpup(p)) { ++ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,rxcsum", &value); ++ if (!ret) { ++ dev_info(&pdev->dev, "RX_CSUM %d\n", value); ++ ++ switch (value) { + case 1: + lp->csum_offload_on_rx_path = + XAE_FEATURE_PARTIAL_RX_CSUM; +@@ -1542,85 +1598,80 @@ static int axienet_of_probe(struct platf + } + } + /* For supporting jumbo frames, the Axi Ethernet hardware must have +- * a larger Rx/Tx Memory. Typically, the size must be more than or +- * equal to 16384 bytes, so that we can enable jumbo option and start +- * supporting jumbo frames. Here we check for memory allocated for +- * Rx/Tx in the hardware from the device-tree and accordingly set +- * flags. */ +- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxmem", NULL); +- if (p) { +- if ((be32_to_cpup(p)) >= 0x4000) +- lp->jumbo_support = 1; +- } +- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,temac-type", +- NULL); +- if (p) +- lp->temac_type = be32_to_cpup(p); +- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,phy-type", NULL); +- if (p) +- lp->phy_type = be32_to_cpup(p); +- +- /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ +- np = of_parse_phandle(op->dev.of_node, "axistream-connected", 0); +- if (!np) { +- dev_err(&op->dev, "could not find DMA node\n"); +- goto err_iounmap; +- } +- lp->dma_regs = of_iomap(np, 0); +- if (lp->dma_regs) { +- dev_dbg(&op->dev, "MEM base: %p\n", lp->dma_regs); +- } else { +- dev_err(&op->dev, "unable to map DMA registers\n"); +- of_node_put(np); ++ * a larger Rx/Tx Memory. Typically, the size must be large so that ++ * we can enable jumbo option and start supporting jumbo frames. ++ * Here we check for memory allocated for Rx/Tx in the hardware from ++ * the device-tree and accordingly set flags. ++ */ ++ of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem); ++ of_property_read_u32(pdev->dev.of_node, "xlnx,temac-type", ++ &lp->temac_type); ++ of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", ++ &lp->phy_type); ++ ++ /* Find the DMA node, map the DMA registers, and decode DMA IRQs */ ++ np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0); ++ if (IS_ERR(np)) { ++ dev_err(&pdev->dev, "could not find DMA node\n"); ++ ret = PTR_ERR(np); ++ goto free_netdev; ++ } ++ ret = of_address_to_resource(np, 0, &dmares); ++ if (ret) { ++ dev_err(&pdev->dev, "unable to get DMA resource\n"); ++ goto free_netdev; ++ } ++ lp->dma_regs = devm_ioremap_resource(&pdev->dev, &dmares); ++ if (!lp->dma_regs) { ++ dev_err(&pdev->dev, "could not map DMA regs\n"); ++ ret = -ENOMEM; ++ goto free_netdev; + } + lp->rx_irq = irq_of_parse_and_map(np, 1); + lp->tx_irq = irq_of_parse_and_map(np, 0); + of_node_put(np); + if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { +- dev_err(&op->dev, "could not determine irqs\n"); ++ dev_err(&pdev->dev, "could not determine irqs\n"); + ret = -ENOMEM; +- goto err_iounmap_2; ++ goto free_netdev; + } + + /* Retrieve the MAC address */ +- addr = of_get_property(op->dev.of_node, "local-mac-address", &size); +- if ((!addr) || (size != 6)) { +- dev_err(&op->dev, "could not find MAC address\n"); +- ret = -ENODEV; +- goto err_iounmap_2; ++ ret = of_property_read_u8_array(pdev->dev.of_node, ++ "local-mac-address", mac_addr, 6); ++ if (ret) { ++ dev_err(&pdev->dev, "could not find MAC address\n"); ++ goto free_netdev; + } +- axienet_set_mac_address(ndev, (void *) addr); ++ axienet_set_mac_address(ndev, (void *) mac_addr); + + lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD; + lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; + +- lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); +- ret = axienet_mdio_setup(lp, op->dev.of_node); +- if (ret) +- dev_warn(&op->dev, "error registering MDIO bus\n"); ++ lp->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); ++ if (lp->phy_node) { ++ ret = axienet_mdio_setup(lp, pdev->dev.of_node); ++ if (ret) ++ dev_warn(&pdev->dev, "error registering MDIO bus\n"); ++ } + + ret = register_netdev(lp->ndev); + if (ret) { + dev_err(lp->dev, "register_netdev() error (%i)\n", ret); +- goto err_iounmap_2; ++ goto free_netdev; + } + + return 0; + +-err_iounmap_2: +- if (lp->dma_regs) +- iounmap(lp->dma_regs); +-err_iounmap: +- iounmap(lp->regs); +-nodev: ++free_netdev: + free_netdev(ndev); +- ndev = NULL; ++ + return ret; + } + +-static int axienet_of_remove(struct platform_device *op) ++static int axienet_remove(struct platform_device *pdev) + { +- struct net_device *ndev = platform_get_drvdata(op); ++ struct net_device *ndev = platform_get_drvdata(pdev); + struct axienet_local *lp = netdev_priv(ndev); + + axienet_mdio_teardown(lp); +@@ -1630,17 +1681,14 @@ static int axienet_of_remove(struct plat + of_node_put(lp->phy_node); + lp->phy_node = NULL; + +- iounmap(lp->regs); +- if (lp->dma_regs) +- iounmap(lp->dma_regs); + free_netdev(ndev); + + return 0; + } + +-static struct platform_driver axienet_of_driver = { +- .probe = axienet_of_probe, +- .remove = axienet_of_remove, ++static struct platform_driver axienet_driver = { ++ .probe = axienet_probe, ++ .remove = axienet_remove, + .driver = { + .owner = THIS_MODULE, + .name = "xilinx_axienet", +@@ -1648,7 +1696,7 @@ static struct platform_driver axienet_of + }, + }; + +-module_platform_driver(axienet_of_driver); ++module_platform_driver(axienet_driver); + + MODULE_DESCRIPTION("Xilinx Axi Ethernet driver"); + MODULE_AUTHOR("Xilinx"); +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +@@ -120,7 +120,8 @@ static int axienet_mdio_write(struct mii + * @np: Pointer to device node + * + * returns: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when +- * mdiobus_alloc (to allocate memory for mii bus structure) fails. ++ * mdiobus_alloc (to allocate memory for mii bus structure) ++ * fails. + * + * Sets up the MDIO interface by initializing the MDIO clock and enabling the + * MDIO interface in hardware. Register the MDIO interface. +@@ -128,11 +129,12 @@ static int axienet_mdio_write(struct mii + int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) + { + int ret; +- u32 clk_div, host_clock; +- u32 *property_p; ++ u32 clk_div; + struct mii_bus *bus; + struct resource res; + struct device_node *np1; ++ /* the ethernet controller device node */ ++ struct device_node *npp = NULL; + + /* clk_div can be calculated by deriving it from the equation: + * fMDIO = fHOST / ((1 + clk_div) * 2) +@@ -158,42 +160,50 @@ int axienet_mdio_setup(struct axienet_lo + * fHOST can be read from the flattened device tree as property + * "clock-frequency" from the CPU + */ +- +- np1 = of_find_node_by_name(NULL, "cpu"); +- if (!np1) { +- printk(KERN_WARNING "%s(): Could not find CPU device node.", +- __func__); +- printk(KERN_WARNING "Setting MDIO clock divisor to " +- "default %d\n", DEFAULT_CLOCK_DIVISOR); +- clk_div = DEFAULT_CLOCK_DIVISOR; +- goto issue; +- } +- property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL); +- if (!property_p) { +- printk(KERN_WARNING "%s(): Could not find CPU property: " +- "clock-frequency.", __func__); +- printk(KERN_WARNING "Setting MDIO clock divisor to " +- "default %d\n", DEFAULT_CLOCK_DIVISOR); ++ np1 = of_get_parent(lp->phy_node); ++ if (np1) ++ npp = of_get_parent(np1); ++ if (!npp) { ++ dev_warn(lp->dev, ++ "Could not find ethernet controller device node."); ++ dev_warn(lp->dev, ++ "Setting MDIO clock divisor to default %d\n", ++ DEFAULT_CLOCK_DIVISOR); + clk_div = DEFAULT_CLOCK_DIVISOR; +- of_node_put(np1); +- goto issue; ++ } else { ++ u32 *property_p; ++ ++ property_p = (uint32_t *)of_get_property(npp, ++ "clock-frequency", NULL); ++ if (!property_p) { ++ dev_warn(lp->dev, ++ "Could not find clock ethernet " ++ "controller property."); ++ dev_warn(lp->dev, ++ "Setting MDIO clock divisor to default %d\n", ++ DEFAULT_CLOCK_DIVISOR); ++ clk_div = DEFAULT_CLOCK_DIVISOR; ++ } else { ++ u32 host_clock = be32_to_cpup(property_p); ++ ++ clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; ++ ++ /* If there is any remainder from the division of ++ * fHOST / (MAX_MDIO_FREQ * 2), then we need to add 1 ++ * to the clock divisor or we will surely be ++ * above 2.5 MHz ++ */ ++ if (host_clock % (MAX_MDIO_FREQ * 2)) ++ clk_div++; ++ dev_dbg(lp->dev, ++ "Setting MDIO clock divisor to %u " ++ "based on %u Hz host clock.\n", ++ clk_div, host_clock); ++ } + } + +- host_clock = be32_to_cpup(property_p); +- clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; +- /* If there is any remainder from the division of +- * fHOST / (MAX_MDIO_FREQ * 2), then we need to add +- * 1 to the clock divisor or we will surely be above 2.5 MHz */ +- if (host_clock % (MAX_MDIO_FREQ * 2)) +- clk_div++; +- +- printk(KERN_DEBUG "%s(): Setting MDIO clock divisor to %u based " +- "on %u Hz host clock.\n", __func__, clk_div, host_clock); +- +- of_node_put(np1); +-issue: +- axienet_iow(lp, XAE_MDIO_MC_OFFSET, +- (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK)); ++ axienet_iow(lp, XAE_MDIO_MC_OFFSET, (((u32)clk_div) | ++ XAE_MDIO_MC_MDIOEN_MASK)); + + ret = axienet_mdio_wait_until_ready(lp); + if (ret < 0) +@@ -203,8 +213,7 @@ issue: + if (!bus) + return -ENOMEM; + +- np1 = of_get_parent(lp->phy_node); +- of_address_to_resource(np1, 0, &res); ++ of_address_to_resource(npp, 0, &res); + snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long) res.start); + +@@ -233,7 +242,6 @@ issue: + void axienet_mdio_teardown(struct axienet_local *lp) + { + mdiobus_unregister(lp->mii_bus); +- kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + lp->mii_bus = NULL; + } +--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c ++++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -44,7 +45,7 @@ + #define XEL_RPLR_OFFSET 0x100C /* Rx packet length */ + #define XEL_RSR_OFFSET 0x17FC /* Rx status */ + +-#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ ++#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer offset */ + + /* MDIO Address Register Bit Masks */ + #define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ +@@ -110,8 +111,8 @@ + * @next_rx_buf_to_use: next Rx buffer to read from + * @base_addr: base address of the Emaclite device + * @reset_lock: lock used for synchronization +- * @deferred_skb: holds an skb (for transmission at a later time) when the +- * Tx buffer is not free ++ * @deferred_skb: holds an skb (for transmission at a later time) when ++ * the Tx buffer is not free + * @phy_dev: pointer to the PHY device + * @phy_node: pointer to the PHY device node + * @mii_bus: pointer to the MII bus +@@ -151,8 +152,8 @@ struct net_local { + * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device + * @drvdata: Pointer to the Emaclite device private data + * +- * This function enables the Tx and Rx interrupts for the Emaclite device along +- * with the Global Interrupt Enable. ++ * This function enables the Tx and Rx interrupts for the Emaclite device ++ * along with the Global Interrupt Enable. + */ + static void xemaclite_enable_interrupts(struct net_local *drvdata) + { +@@ -174,7 +175,8 @@ static void xemaclite_enable_interrupts( + } + + /* Enable the Rx interrupts for the first buffer */ +- __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); ++ __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + ++ XEL_RSR_OFFSET); + + /* Enable the Rx interrupts for the second Buffer if + * configured in HW */ +@@ -188,7 +190,8 @@ static void xemaclite_enable_interrupts( + } + + /** +- * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device ++ * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite ++ * device. + * @drvdata: Pointer to the Emaclite device private data + * + * This function disables the Tx and Rx interrupts for the Emaclite device, +@@ -209,8 +212,8 @@ static void xemaclite_disable_interrupts + /* Disable the Tx interrupts for the second Buffer + * if configured in HW */ + if (drvdata->tx_ping_pong != 0) { +- reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + +- XEL_TSR_OFFSET); ++ reg_data = __raw_readl(drvdata->base_addr + ++ XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); +@@ -225,8 +228,8 @@ static void xemaclite_disable_interrupts + * if configured in HW */ + if (drvdata->rx_ping_pong != 0) { + +- reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + +- XEL_RSR_OFFSET); ++ reg_data = __raw_readl(drvdata->base_addr + ++ XEL_BUFFER_OFFSET + XEL_RSR_OFFSET); + __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET); +@@ -234,7 +237,8 @@ static void xemaclite_disable_interrupts + } + + /** +- * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address ++ * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned ++ * address. + * @src_ptr: Void pointer to the 16-bit aligned source address + * @dest_ptr: Pointer to the 32-bit aligned destination address + * @length: Number bytes to write from source to destination +@@ -283,8 +287,8 @@ static void xemaclite_aligned_write(void + * @dest_ptr: Pointer to the 16-bit aligned destination address + * @length: Number bytes to read from source to destination + * +- * This function reads data from a 32-bit aligned address in the EmacLite device +- * to a 16-bit aligned buffer. ++ * This function reads data from a 32-bit aligned address in the EmacLite ++ * device to a 16-bit aligned buffer. + */ + static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, + unsigned length) +@@ -326,14 +330,14 @@ static void xemaclite_aligned_read(u32 * + * @data: Pointer to the data to be sent + * @byte_count: Total frame size, including header + * +- * This function checks if the Tx buffer of the Emaclite device is free to send +- * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it +- * returns an error. ++ * This function checks if the Tx buffer of the Emaclite device is free to ++ * send data. If so, it fills the Tx buffer with data for transmission. ++ * Otherwise, it returns an error. + * + * Return: 0 upon success or -1 if the buffer(s) are full. + * +- * Note: The maximum Tx packet size can not be more than Ethernet header +- * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. ++ * Note: The maximum Tx packet size can not be more than Ethernet header ++ * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. + */ + static int xemaclite_send_data(struct net_local *drvdata, u8 *data, + unsigned int byte_count) +@@ -687,7 +691,8 @@ static irqreturn_t xemaclite_interrupt(i + } + + /* Check if the Transmission for the second buffer is completed */ +- tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); ++ tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + ++ XEL_TSR_OFFSET); + if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && + (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { + +@@ -1053,7 +1058,7 @@ static int xemaclite_send(struct sk_buff + * current transmission is complete */ + netif_stop_queue(dev); + lp->deferred_skb = new_skb; +- /* Take the time stamp now, since we can't do this in an ISR. */ ++ /* Take the time stamp now, since we can't do this in an ISR */ + skb_tx_timestamp(new_skb); + spin_unlock_irqrestore(&lp->reset_lock, flags); + return 0; +@@ -1090,7 +1095,7 @@ static void xemaclite_remove_ndev(struct + * This function looks for a property in the device node and returns the value + * of the property if its found or 0 if the property is not found. + * +- * Return: Value of the parameter if the parameter is found, or 0 otherwise ++ * Return: Value of the parameter if the parameter is found, or 0 otherwise + */ + static bool get_bool(struct platform_device *ofdev, const char *s) + { +@@ -1172,7 +1177,7 @@ static int xemaclite_of_probe(struct pla + + if (mac_address) + /* Set the MAC address. */ +- memcpy(ndev->dev_addr, mac_address, ETH_ALEN); ++ memcpy(ndev->dev_addr, mac_address, 6); + else + dev_warn(dev, "No MAC address found\n"); + +--- /dev/null ++++ b/drivers/net/ethernet/xilinx/xilinx_emacps.c +@@ -0,0 +1,2912 @@ ++/* ++ * Xilinx Ethernet: Linux driver for Ethernet. ++ * ++ * Author: Xilinx, Inc. ++ * ++ * 2010 (c) Xilinx, Inc. This file is licensed uner the terms of the GNU ++ * General Public License version 2. This program is licensed "as is" ++ * without any warranty of any kind, whether express or implied. ++ * ++ * This is a driver for xilinx processor sub-system (ps) ethernet device. ++ * This driver is mainly used in Linux 2.6.30 and above and it does _not_ ++ * support Linux 2.4 kernel due to certain new features (e.g. NAPI) is ++ * introduced in this driver. ++ * ++ * TODO: ++ * 1. JUMBO frame is not enabled per EPs spec. Please update it if this ++ * support is added in and set MAX_MTU to 9000. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/************************** Constant Definitions *****************************/ ++ ++/* Must be shorter than length of ethtool_drvinfo.driver field to fit */ ++#define DRIVER_NAME "xemacps" ++#define DRIVER_DESCRIPTION "Xilinx Tri-Mode Ethernet MAC driver" ++#define DRIVER_VERSION "1.00a" ++ ++/* Transmission timeout is 3 seconds. */ ++#define TX_TIMEOUT (3*HZ) ++ ++/* for RX skb IP header word-aligned */ ++#define RX_IP_ALIGN_OFFSET 2 ++ ++/* DMA buffer descriptors must be aligned on a 4-byte boundary. */ ++#define ALIGNMENT_BD 8 ++ ++/* Maximum value for hash bits. 2**6 */ ++#define XEMACPS_MAX_HASH_BITS 64 ++ ++/* MDC clock division ++ * currently supporting 8, 16, 32, 48, 64, 96, 128, 224. ++ */ ++enum { MDC_DIV_8 = 0, MDC_DIV_16, MDC_DIV_32, MDC_DIV_48, ++MDC_DIV_64, MDC_DIV_96, MDC_DIV_128, MDC_DIV_224 }; ++ ++/* Specify the receive buffer size in bytes, 64, 128, 192, 10240 */ ++#define XEMACPS_RX_BUF_SIZE 1536 ++ ++/* Number of receive buffer bytes as a unit, this is HW setup */ ++#define XEMACPS_RX_BUF_UNIT 64 ++ ++/* Default SEND and RECV buffer descriptors (BD) numbers. ++ * BD Space needed is (XEMACPS_SEND_BD_CNT+XEMACPS_RECV_BD_CNT)*8 ++ */ ++#undef DEBUG ++#define DEBUG ++ ++#define XEMACPS_SEND_BD_CNT 256 ++#define XEMACPS_RECV_BD_CNT 256 ++ ++#define XEMACPS_NAPI_WEIGHT 64 ++ ++/* Register offset definitions. Unless otherwise noted, register access is ++ * 32 bit. Names are self explained here. ++ */ ++#define XEMACPS_NWCTRL_OFFSET 0x00000000 /* Network Control reg */ ++#define XEMACPS_NWCFG_OFFSET 0x00000004 /* Network Config reg */ ++#define XEMACPS_NWSR_OFFSET 0x00000008 /* Network Status reg */ ++#define XEMACPS_USERIO_OFFSET 0x0000000C /* User IO reg */ ++#define XEMACPS_DMACR_OFFSET 0x00000010 /* DMA Control reg */ ++#define XEMACPS_TXSR_OFFSET 0x00000014 /* TX Status reg */ ++#define XEMACPS_RXQBASE_OFFSET 0x00000018 /* RX Q Base address reg */ ++#define XEMACPS_TXQBASE_OFFSET 0x0000001C /* TX Q Base address reg */ ++#define XEMACPS_RXSR_OFFSET 0x00000020 /* RX Status reg */ ++#define XEMACPS_ISR_OFFSET 0x00000024 /* Interrupt Status reg */ ++#define XEMACPS_IER_OFFSET 0x00000028 /* Interrupt Enable reg */ ++#define XEMACPS_IDR_OFFSET 0x0000002C /* Interrupt Disable reg */ ++#define XEMACPS_IMR_OFFSET 0x00000030 /* Interrupt Mask reg */ ++#define XEMACPS_PHYMNTNC_OFFSET 0x00000034 /* Phy Maintaince reg */ ++#define XEMACPS_RXPAUSE_OFFSET 0x00000038 /* RX Pause Time reg */ ++#define XEMACPS_TXPAUSE_OFFSET 0x0000003C /* TX Pause Time reg */ ++#define XEMACPS_HASHL_OFFSET 0x00000080 /* Hash Low address reg */ ++#define XEMACPS_HASHH_OFFSET 0x00000084 /* Hash High address reg */ ++#define XEMACPS_LADDR1L_OFFSET 0x00000088 /* Specific1 addr low */ ++#define XEMACPS_LADDR1H_OFFSET 0x0000008C /* Specific1 addr high */ ++#define XEMACPS_LADDR2L_OFFSET 0x00000090 /* Specific2 addr low */ ++#define XEMACPS_LADDR2H_OFFSET 0x00000094 /* Specific2 addr high */ ++#define XEMACPS_LADDR3L_OFFSET 0x00000098 /* Specific3 addr low */ ++#define XEMACPS_LADDR3H_OFFSET 0x0000009C /* Specific3 addr high */ ++#define XEMACPS_LADDR4L_OFFSET 0x000000A0 /* Specific4 addr low */ ++#define XEMACPS_LADDR4H_OFFSET 0x000000A4 /* Specific4 addr high */ ++#define XEMACPS_MATCH1_OFFSET 0x000000A8 /* Type ID1 Match reg */ ++#define XEMACPS_MATCH2_OFFSET 0x000000AC /* Type ID2 Match reg */ ++#define XEMACPS_MATCH3_OFFSET 0x000000B0 /* Type ID3 Match reg */ ++#define XEMACPS_MATCH4_OFFSET 0x000000B4 /* Type ID4 Match reg */ ++#define XEMACPS_WOL_OFFSET 0x000000B8 /* Wake on LAN reg */ ++#define XEMACPS_STRETCH_OFFSET 0x000000BC /* IPG Stretch reg */ ++#define XEMACPS_SVLAN_OFFSET 0x000000C0 /* Stacked VLAN reg */ ++#define XEMACPS_MODID_OFFSET 0x000000FC /* Module ID reg */ ++#define XEMACPS_OCTTXL_OFFSET 0x00000100 /* Octects transmitted Low ++ reg */ ++#define XEMACPS_OCTTXH_OFFSET 0x00000104 /* Octects transmitted High ++ reg */ ++#define XEMACPS_TXCNT_OFFSET 0x00000108 /* Error-free Frmaes ++ transmitted counter */ ++#define XEMACPS_TXBCCNT_OFFSET 0x0000010C /* Error-free Broadcast ++ Frames counter*/ ++#define XEMACPS_TXMCCNT_OFFSET 0x00000110 /* Error-free Multicast ++ Frame counter */ ++#define XEMACPS_TXPAUSECNT_OFFSET 0x00000114 /* Pause Frames Transmitted ++ Counter */ ++#define XEMACPS_TX64CNT_OFFSET 0x00000118 /* Error-free 64 byte Frames ++ Transmitted counter */ ++#define XEMACPS_TX65CNT_OFFSET 0x0000011C /* Error-free 65-127 byte ++ Frames Transmitted counter */ ++#define XEMACPS_TX128CNT_OFFSET 0x00000120 /* Error-free 128-255 byte ++ Frames Transmitted counter */ ++#define XEMACPS_TX256CNT_OFFSET 0x00000124 /* Error-free 256-511 byte ++ Frames transmitted counter */ ++#define XEMACPS_TX512CNT_OFFSET 0x00000128 /* Error-free 512-1023 byte ++ Frames transmitted counter */ ++#define XEMACPS_TX1024CNT_OFFSET 0x0000012C /* Error-free 1024-1518 byte ++ Frames transmitted counter */ ++#define XEMACPS_TX1519CNT_OFFSET 0x00000130 /* Error-free larger than ++ 1519 byte Frames transmitted ++ Counter */ ++#define XEMACPS_TXURUNCNT_OFFSET 0x00000134 /* TX under run error ++ Counter */ ++#define XEMACPS_SNGLCOLLCNT_OFFSET 0x00000138 /* Single Collision Frame ++ Counter */ ++#define XEMACPS_MULTICOLLCNT_OFFSET 0x0000013C /* Multiple Collision Frame ++ Counter */ ++#define XEMACPS_EXCESSCOLLCNT_OFFSET 0x00000140 /* Excessive Collision Frame ++ Counter */ ++#define XEMACPS_LATECOLLCNT_OFFSET 0x00000144 /* Late Collision Frame ++ Counter */ ++#define XEMACPS_TXDEFERCNT_OFFSET 0x00000148 /* Deferred Transmission ++ Frame Counter */ ++#define XEMACPS_CSENSECNT_OFFSET 0x0000014C /* Carrier Sense Error ++ Counter */ ++#define XEMACPS_OCTRXL_OFFSET 0x00000150 /* Octects Received register ++ Low */ ++#define XEMACPS_OCTRXH_OFFSET 0x00000154 /* Octects Received register ++ High */ ++#define XEMACPS_RXCNT_OFFSET 0x00000158 /* Error-free Frames ++ Received Counter */ ++#define XEMACPS_RXBROADCNT_OFFSET 0x0000015C /* Error-free Broadcast ++ Frames Received Counter */ ++#define XEMACPS_RXMULTICNT_OFFSET 0x00000160 /* Error-free Multicast ++ Frames Received Counter */ ++#define XEMACPS_RXPAUSECNT_OFFSET 0x00000164 /* Pause Frames ++ Received Counter */ ++#define XEMACPS_RX64CNT_OFFSET 0x00000168 /* Error-free 64 byte Frames ++ Received Counter */ ++#define XEMACPS_RX65CNT_OFFSET 0x0000016C /* Error-free 65-127 byte ++ Frames Received Counter */ ++#define XEMACPS_RX128CNT_OFFSET 0x00000170 /* Error-free 128-255 byte ++ Frames Received Counter */ ++#define XEMACPS_RX256CNT_OFFSET 0x00000174 /* Error-free 256-512 byte ++ Frames Received Counter */ ++#define XEMACPS_RX512CNT_OFFSET 0x00000178 /* Error-free 512-1023 byte ++ Frames Received Counter */ ++#define XEMACPS_RX1024CNT_OFFSET 0x0000017C /* Error-free 1024-1518 byte ++ Frames Received Counter */ ++#define XEMACPS_RX1519CNT_OFFSET 0x00000180 /* Error-free 1519-max byte ++ Frames Received Counter */ ++#define XEMACPS_RXUNDRCNT_OFFSET 0x00000184 /* Undersize Frames Received ++ Counter */ ++#define XEMACPS_RXOVRCNT_OFFSET 0x00000188 /* Oversize Frames Received ++ Counter */ ++#define XEMACPS_RXJABCNT_OFFSET 0x0000018C /* Jabbers Received ++ Counter */ ++#define XEMACPS_RXFCSCNT_OFFSET 0x00000190 /* Frame Check Sequence ++ Error Counter */ ++#define XEMACPS_RXLENGTHCNT_OFFSET 0x00000194 /* Length Field Error ++ Counter */ ++#define XEMACPS_RXSYMBCNT_OFFSET 0x00000198 /* Symbol Error Counter */ ++#define XEMACPS_RXALIGNCNT_OFFSET 0x0000019C /* Alignment Error ++ Counter */ ++#define XEMACPS_RXRESERRCNT_OFFSET 0x000001A0 /* Receive Resource Error ++ Counter */ ++#define XEMACPS_RXORCNT_OFFSET 0x000001A4 /* Receive Overrun */ ++#define XEMACPS_RXIPCCNT_OFFSET 0x000001A8 /* IP header Checksum Error ++ Counter */ ++#define XEMACPS_RXTCPCCNT_OFFSET 0x000001AC /* TCP Checksum Error ++ Counter */ ++#define XEMACPS_RXUDPCCNT_OFFSET 0x000001B0 /* UDP Checksum Error ++ Counter */ ++ ++#define XEMACPS_1588S_OFFSET 0x000001D0 /* 1588 Timer Seconds */ ++#define XEMACPS_1588NS_OFFSET 0x000001D4 /* 1588 Timer Nanoseconds */ ++#define XEMACPS_1588ADJ_OFFSET 0x000001D8 /* 1588 Timer Adjust */ ++#define XEMACPS_1588INC_OFFSET 0x000001DC /* 1588 Timer Increment */ ++#define XEMACPS_PTPETXS_OFFSET 0x000001E0 /* PTP Event Frame ++ Transmitted Seconds */ ++#define XEMACPS_PTPETXNS_OFFSET 0x000001E4 /* PTP Event Frame ++ Transmitted Nanoseconds */ ++#define XEMACPS_PTPERXS_OFFSET 0x000001E8 /* PTP Event Frame Received ++ Seconds */ ++#define XEMACPS_PTPERXNS_OFFSET 0x000001EC /* PTP Event Frame Received ++ Nanoseconds */ ++#define XEMACPS_PTPPTXS_OFFSET 0x000001E0 /* PTP Peer Frame ++ Transmitted Seconds */ ++#define XEMACPS_PTPPTXNS_OFFSET 0x000001E4 /* PTP Peer Frame ++ Transmitted Nanoseconds */ ++#define XEMACPS_PTPPRXS_OFFSET 0x000001E8 /* PTP Peer Frame Received ++ Seconds */ ++#define XEMACPS_PTPPRXNS_OFFSET 0x000001EC /* PTP Peer Frame Received ++ Nanoseconds */ ++ ++/* network control register bit definitions */ ++#define XEMACPS_NWCTRL_FLUSH_DPRAM_MASK 0x00040000 ++#define XEMACPS_NWCTRL_RXTSTAMP_MASK 0x00008000 /* RX Timestamp in CRC */ ++#define XEMACPS_NWCTRL_ZEROPAUSETX_MASK 0x00001000 /* Transmit zero quantum ++ pause frame */ ++#define XEMACPS_NWCTRL_PAUSETX_MASK 0x00000800 /* Transmit pause frame */ ++#define XEMACPS_NWCTRL_HALTTX_MASK 0x00000400 /* Halt transmission ++ after current frame */ ++#define XEMACPS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */ ++ ++#define XEMACPS_NWCTRL_STATWEN_MASK 0x00000080 /* Enable writing to ++ stat counters */ ++#define XEMACPS_NWCTRL_STATINC_MASK 0x00000040 /* Increment statistic ++ registers */ ++#define XEMACPS_NWCTRL_STATCLR_MASK 0x00000020 /* Clear statistic ++ registers */ ++#define XEMACPS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ ++#define XEMACPS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ ++#define XEMACPS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ ++#define XEMACPS_NWCTRL_LOOPEN_MASK 0x00000002 /* local loopback */ ++ ++/* name network configuration register bit definitions */ ++#define XEMACPS_NWCFG_BADPREAMBEN_MASK 0x20000000 /* disable rejection of ++ non-standard preamble */ ++#define XEMACPS_NWCFG_IPDSTRETCH_MASK 0x10000000 /* enable transmit IPG */ ++#define XEMACPS_NWCFG_FCSIGNORE_MASK 0x04000000 /* disable rejection of ++ FCS error */ ++#define XEMACPS_NWCFG_HDRXEN_MASK 0x02000000 /* RX half duplex */ ++#define XEMACPS_NWCFG_RXCHKSUMEN_MASK 0x01000000 /* enable RX checksum ++ offload */ ++#define XEMACPS_NWCFG_PAUSECOPYDI_MASK 0x00800000 /* Do not copy pause ++ Frames to memory */ ++#define XEMACPS_NWCFG_MDC_SHIFT_MASK 18 /* shift bits for MDC */ ++#define XEMACPS_NWCFG_MDCCLKDIV_MASK 0x001C0000 /* MDC Mask PCLK divisor */ ++#define XEMACPS_NWCFG_FCSREM_MASK 0x00020000 /* Discard FCS from ++ received frames */ ++#define XEMACPS_NWCFG_LENGTHERRDSCRD_MASK 0x00010000 ++/* RX length error discard */ ++#define XEMACPS_NWCFG_RXOFFS_MASK 0x0000C000 /* RX buffer offset */ ++#define XEMACPS_NWCFG_PAUSEEN_MASK 0x00002000 /* Enable pause TX */ ++#define XEMACPS_NWCFG_RETRYTESTEN_MASK 0x00001000 /* Retry test */ ++#define XEMACPS_NWCFG_1000_MASK 0x00000400 /* Gigbit mode */ ++#define XEMACPS_NWCFG_EXTADDRMATCHEN_MASK 0x00000200 ++/* External address match enable */ ++#define XEMACPS_NWCFG_UCASTHASHEN_MASK 0x00000080 /* Receive unicast hash ++ frames */ ++#define XEMACPS_NWCFG_MCASTHASHEN_MASK 0x00000040 /* Receive multicast hash ++ frames */ ++#define XEMACPS_NWCFG_BCASTDI_MASK 0x00000020 /* Do not receive ++ broadcast frames */ ++#define XEMACPS_NWCFG_COPYALLEN_MASK 0x00000010 /* Copy all frames */ ++ ++#define XEMACPS_NWCFG_NVLANDISC_MASK 0x00000004 /* Receive only VLAN ++ frames */ ++#define XEMACPS_NWCFG_FDEN_MASK 0x00000002 /* Full duplex */ ++#define XEMACPS_NWCFG_100_MASK 0x00000001 /* 10 or 100 Mbs */ ++ ++/* network status register bit definitaions */ ++#define XEMACPS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */ ++#define XEMACPS_NWSR_MDIO_MASK 0x00000002 /* Status of mdio_in */ ++ ++/* MAC address register word 1 mask */ ++#define XEMACPS_LADDR_MACH_MASK 0x0000FFFF /* Address bits[47:32] ++ bit[31:0] are in BOTTOM */ ++ ++/* DMA control register bit definitions */ ++#define XEMACPS_DMACR_RXBUF_MASK 0x00FF0000 /* Mask bit for RX buffer ++ size */ ++#define XEMACPS_DMACR_RXBUF_SHIFT 16 /* Shift bit for RX buffer ++ size */ ++#define XEMACPS_DMACR_TCPCKSUM_MASK 0x00000800 /* enable/disable TX ++ checksum offload */ ++#define XEMACPS_DMACR_TXSIZE_MASK 0x00000400 /* TX buffer memory size */ ++#define XEMACPS_DMACR_RXSIZE_MASK 0x00000300 /* RX buffer memory size */ ++#define XEMACPS_DMACR_ENDIAN_MASK 0x00000080 /* Endian configuration */ ++#define XEMACPS_DMACR_BLENGTH_MASK 0x0000001F /* Buffer burst length */ ++#define XEMACPS_DMACR_BLENGTH_INCR16 0x00000010 /* Buffer burst length */ ++#define XEMACPS_DMACR_BLENGTH_INCR8 0x00000008 /* Buffer burst length */ ++#define XEMACPS_DMACR_BLENGTH_INCR4 0x00000004 /* Buffer burst length */ ++#define XEMACPS_DMACR_BLENGTH_SINGLE 0x00000002 /* Buffer burst length */ ++ ++/* transmit status register bit definitions */ ++#define XEMACPS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */ ++#define XEMACPS_TXSR_COL1000_MASK 0x00000080 /* Collision Gbs mode */ ++#define XEMACPS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */ ++#define XEMACPS_TXSR_TXCOMPL_MASK 0x00000020 /* Transmit completed OK */ ++#define XEMACPS_TXSR_BUFEXH_MASK 0x00000010 /* Transmit buffs exhausted ++ mid frame */ ++#define XEMACPS_TXSR_TXGO_MASK 0x00000008 /* Status of go flag */ ++#define XEMACPS_TXSR_RXOVR_MASK 0x00000004 /* Retry limit exceeded */ ++#define XEMACPS_TXSR_COL100_MASK 0x00000002 /* Collision 10/100 mode */ ++#define XEMACPS_TXSR_USEDREAD_MASK 0x00000001 /* TX buffer used bit set */ ++ ++#define XEMACPS_TXSR_ERROR_MASK (XEMACPS_TXSR_HRESPNOK_MASK | \ ++ XEMACPS_TXSR_COL1000_MASK | \ ++ XEMACPS_TXSR_URUN_MASK | \ ++ XEMACPS_TXSR_BUFEXH_MASK | \ ++ XEMACPS_TXSR_RXOVR_MASK | \ ++ XEMACPS_TXSR_COL100_MASK | \ ++ XEMACPS_TXSR_USEDREAD_MASK) ++ ++/* receive status register bit definitions */ ++#define XEMACPS_RXSR_HRESPNOK_MASK 0x00000008 /* Receive hresp not OK */ ++#define XEMACPS_RXSR_RXOVR_MASK 0x00000004 /* Receive overrun */ ++#define XEMACPS_RXSR_FRAMERX_MASK 0x00000002 /* Frame received OK */ ++#define XEMACPS_RXSR_BUFFNA_MASK 0x00000001 /* RX buffer used bit set */ ++ ++#define XEMACPS_RXSR_ERROR_MASK (XEMACPS_RXSR_HRESPNOK_MASK | \ ++ XEMACPS_RXSR_RXOVR_MASK | \ ++ XEMACPS_RXSR_BUFFNA_MASK) ++ ++/* interrupts bit definitions ++ * Bits definitions are same in XEMACPS_ISR_OFFSET, ++ * XEMACPS_IER_OFFSET, XEMACPS_IDR_OFFSET, and XEMACPS_IMR_OFFSET ++ */ ++#define XEMACPS_IXR_PTPPSTX_MASK 0x02000000 /* PTP Psync transmitted */ ++#define XEMACPS_IXR_PTPPDRTX_MASK 0x01000000 /* PTP Pdelay_req ++ transmitted */ ++#define XEMACPS_IXR_PTPSTX_MASK 0x00800000 /* PTP Sync transmitted */ ++#define XEMACPS_IXR_PTPDRTX_MASK 0x00400000 /* PTP Delay_req ++ transmitted */ ++#define XEMACPS_IXR_PTPPSRX_MASK 0x00200000 /* PTP Psync received */ ++#define XEMACPS_IXR_PTPPDRRX_MASK 0x00100000 /* PTP Pdelay_req ++ received */ ++#define XEMACPS_IXR_PTPSRX_MASK 0x00080000 /* PTP Sync received */ ++#define XEMACPS_IXR_PTPDRRX_MASK 0x00040000 /* PTP Delay_req received */ ++#define XEMACPS_IXR_PAUSETX_MASK 0x00004000 /* Pause frame ++ transmitted */ ++#define XEMACPS_IXR_PAUSEZERO_MASK 0x00002000 /* Pause time has reached ++ zero */ ++#define XEMACPS_IXR_PAUSENZERO_MASK 0x00001000 /* Pause frame received */ ++#define XEMACPS_IXR_HRESPNOK_MASK 0x00000800 /* hresp not ok */ ++#define XEMACPS_IXR_RXOVR_MASK 0x00000400 /* Receive overrun ++ occurred */ ++#define XEMACPS_IXR_TXCOMPL_MASK 0x00000080 /* Frame transmitted ok */ ++#define XEMACPS_IXR_TXEXH_MASK 0x00000040 /* Transmit err occurred or ++ no buffers*/ ++#define XEMACPS_IXR_RETRY_MASK 0x00000020 /* Retry limit exceeded */ ++#define XEMACPS_IXR_URUN_MASK 0x00000010 /* Transmit underrun */ ++#define XEMACPS_IXR_TXUSED_MASK 0x00000008 /* Tx buffer used bit read */ ++#define XEMACPS_IXR_RXUSED_MASK 0x00000004 /* Rx buffer used bit read */ ++#define XEMACPS_IXR_FRAMERX_MASK 0x00000002 /* Frame received ok */ ++#define XEMACPS_IXR_MGMNT_MASK 0x00000001 /* PHY management complete */ ++#define XEMACPS_IXR_ALL_MASK 0x03FC7FFE /* Everything except MDIO */ ++ ++#define XEMACPS_IXR_TX_ERR_MASK (XEMACPS_IXR_TXEXH_MASK | \ ++ XEMACPS_IXR_RETRY_MASK | \ ++ XEMACPS_IXR_URUN_MASK | \ ++ XEMACPS_IXR_TXUSED_MASK) ++ ++#define XEMACPS_IXR_RX_ERR_MASK (XEMACPS_IXR_HRESPNOK_MASK | \ ++ XEMACPS_IXR_RXUSED_MASK | \ ++ XEMACPS_IXR_RXOVR_MASK) ++/* PHY Maintenance bit definitions */ ++#define XEMACPS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ ++#define XEMACPS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ ++#define XEMACPS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ ++#define XEMACPS_PHYMNTNC_ADDR_MASK 0x0F800000 /* Address bits */ ++#define XEMACPS_PHYMNTNC_REG_MASK 0x007C0000 /* register bits */ ++#define XEMACPS_PHYMNTNC_DATA_MASK 0x0000FFFF /* data bits */ ++#define XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ ++#define XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */ ++ ++/* Wake on LAN bit definition */ ++#define XEMACPS_WOL_MCAST_MASK 0x00080000 ++#define XEMACPS_WOL_SPEREG1_MASK 0x00040000 ++#define XEMACPS_WOL_ARP_MASK 0x00020000 ++#define XEMACPS_WOL_MAGIC_MASK 0x00010000 ++#define XEMACPS_WOL_ARP_ADDR_MASK 0x0000FFFF ++ ++/* Buffer descriptor status words offset */ ++#define XEMACPS_BD_ADDR_OFFSET 0x00000000 /**< word 0/addr of BDs */ ++#define XEMACPS_BD_STAT_OFFSET 0x00000004 /**< word 1/status of BDs */ ++ ++/* Transmit buffer descriptor status words bit positions. ++ * Transmit buffer descriptor consists of two 32-bit registers, ++ * the first - word0 contains a 32-bit address pointing to the location of ++ * the transmit data. ++ * The following register - word1, consists of various information to ++ * control transmit process. After transmit, this is updated with status ++ * information, whether the frame was transmitted OK or why it had failed. ++ */ ++#define XEMACPS_TXBUF_USED_MASK 0x80000000 /* Used bit. */ ++#define XEMACPS_TXBUF_WRAP_MASK 0x40000000 /* Wrap bit, last ++ descriptor */ ++#define XEMACPS_TXBUF_RETRY_MASK 0x20000000 /* Retry limit exceeded */ ++#define XEMACPS_TXBUF_EXH_MASK 0x08000000 /* Buffers exhausted */ ++#define XEMACPS_TXBUF_LAC_MASK 0x04000000 /* Late collision. */ ++#define XEMACPS_TXBUF_NOCRC_MASK 0x00010000 /* No CRC */ ++#define XEMACPS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */ ++#define XEMACPS_TXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ ++ ++#define XEMACPS_TXBUF_ERR_MASK 0x3C000000 /* Mask for length field */ ++ ++/* Receive buffer descriptor status words bit positions. ++ * Receive buffer descriptor consists of two 32-bit registers, ++ * the first - word0 contains a 32-bit word aligned address pointing to the ++ * address of the buffer. The lower two bits make up the wrap bit indicating ++ * the last descriptor and the ownership bit to indicate it has been used. ++ * The following register - word1, contains status information regarding why ++ * the frame was received (the filter match condition) as well as other ++ * useful info. ++ */ ++#define XEMACPS_RXBUF_BCAST_MASK 0x80000000 /* Broadcast frame */ ++#define XEMACPS_RXBUF_MULTIHASH_MASK 0x40000000 /* Multicast hashed frame */ ++#define XEMACPS_RXBUF_UNIHASH_MASK 0x20000000 /* Unicast hashed frame */ ++#define XEMACPS_RXBUF_EXH_MASK 0x08000000 /* buffer exhausted */ ++#define XEMACPS_RXBUF_AMATCH_MASK 0x06000000 /* Specific address ++ matched */ ++#define XEMACPS_RXBUF_IDFOUND_MASK 0x01000000 /* Type ID matched */ ++#define XEMACPS_RXBUF_IDMATCH_MASK 0x00C00000 /* ID matched mask */ ++#define XEMACPS_RXBUF_VLAN_MASK 0x00200000 /* VLAN tagged */ ++#define XEMACPS_RXBUF_PRI_MASK 0x00100000 /* Priority tagged */ ++#define XEMACPS_RXBUF_VPRI_MASK 0x000E0000 /* Vlan priority */ ++#define XEMACPS_RXBUF_CFI_MASK 0x00010000 /* CFI frame */ ++#define XEMACPS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ ++#define XEMACPS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ ++#define XEMACPS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ ++ ++#define XEMACPS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ ++#define XEMACPS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ ++#define XEMACPS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */ ++ ++#define XEAMCPS_GEN_PURPOSE_TIMER_LOAD 100 /* timeout value is msecs */ ++ ++#define XEMACPS_GMII2RGMII_FULLDPLX BMCR_FULLDPLX ++#define XEMACPS_GMII2RGMII_SPEED1000 BMCR_SPEED1000 ++#define XEMACPS_GMII2RGMII_SPEED100 BMCR_SPEED100 ++#define XEMACPS_GMII2RGMII_REG_NUM 0x10 ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++#define NS_PER_SEC 1000000000ULL /* Nanoseconds per ++ second */ ++#endif ++ ++#define xemacps_read(base, reg) \ ++ __raw_readl(((void __iomem *)(base)) + (reg)) ++#define xemacps_write(base, reg, val) \ ++ __raw_writel((val), ((void __iomem *)(base)) + (reg)) ++ ++struct ring_info { ++ struct sk_buff *skb; ++ dma_addr_t mapping; ++ size_t len; ++}; ++ ++/* DMA buffer descriptor structure. Each BD is two words */ ++struct xemacps_bd { ++ u32 addr; ++ u32 ctrl; ++}; ++ ++ ++/* Our private device data. */ ++struct net_local { ++ void __iomem *baseaddr; ++ struct clk *devclk; ++ struct clk *aperclk; ++ struct notifier_block clk_rate_change_nb; ++ ++ struct device_node *phy_node; ++ struct device_node *gmii2rgmii_phy_node; ++ struct ring_info *tx_skb; ++ struct ring_info *rx_skb; ++ ++ struct xemacps_bd *rx_bd; ++ struct xemacps_bd *tx_bd; ++ ++ dma_addr_t rx_bd_dma; /* physical address */ ++ dma_addr_t tx_bd_dma; /* physical address */ ++ ++ u32 tx_bd_ci; ++ u32 tx_bd_tail; ++ u32 rx_bd_ci; ++ ++ u32 tx_bd_freecnt; ++ ++ spinlock_t tx_lock; ++ spinlock_t rx_lock; ++ spinlock_t nwctrlreg_lock; ++ ++ struct platform_device *pdev; ++ struct net_device *ndev; /* this device */ ++ struct tasklet_struct tx_bdreclaim_tasklet; ++ struct workqueue_struct *txtimeout_handler_wq; ++ struct work_struct txtimeout_reinit; ++ ++ struct napi_struct napi; /* napi information for device */ ++ struct net_device_stats stats; /* Statistics for this device */ ++ ++ struct timer_list gen_purpose_timer; /* Used for stats update */ ++ ++ /* Manage internal timer for packet timestamping */ ++ struct cyclecounter cycles; ++ struct timecounter clock; ++ struct hwtstamp_config hwtstamp_config; ++ ++ struct mii_bus *mii_bus; ++ struct phy_device *phy_dev; ++ struct phy_device *gmii2rgmii_phy_dev; ++ phy_interface_t phy_interface; ++ unsigned int link; ++ unsigned int speed; ++ unsigned int duplex; ++ /* RX ip/tcp/udp checksum */ ++ unsigned ip_summed; ++ unsigned int enetnum; ++ unsigned int lastrxfrmscntr; ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ unsigned int ptpenetclk; ++#endif ++}; ++#define to_net_local(_nb) container_of(_nb, struct net_local,\ ++ clk_rate_change_nb) ++ ++static struct net_device_ops netdev_ops; ++ ++/** ++ * xemacps_mdio_read - Read current value of phy register indicated by ++ * phyreg. ++ * @bus: mdio bus ++ * @mii_id: mii id ++ * @phyreg: phy register to be read ++ * ++ * @return: value read from specified phy register. ++ * ++ * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys ++ * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to ++ * 0x00020000. ++ */ ++static int xemacps_mdio_read(struct mii_bus *bus, int mii_id, int phyreg) ++{ ++ struct net_local *lp = bus->priv; ++ u32 regval; ++ int value; ++ volatile u32 ipisr; ++ ++ regval = XEMACPS_PHYMNTNC_OP_MASK; ++ regval |= XEMACPS_PHYMNTNC_OP_R_MASK; ++ regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK); ++ regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK); ++ ++ xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval); ++ ++ /* wait for end of transfer */ ++ do { ++ cpu_relax(); ++ ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET); ++ } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0); ++ ++ value = xemacps_read(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET) & ++ XEMACPS_PHYMNTNC_DATA_MASK; ++ ++ return value; ++} ++ ++/** ++ * xemacps_mdio_write - Write passed in value to phy register indicated ++ * by phyreg. ++ * @bus: mdio bus ++ * @mii_id: mii id ++ * @phyreg: phy register to be configured. ++ * @value: value to be written to phy register. ++ * return 0. This API requires to be int type or compile warning generated ++ * ++ * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys ++ * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to ++ * 0x00020000. ++ */ ++static int xemacps_mdio_write(struct mii_bus *bus, int mii_id, int phyreg, ++ u16 value) ++{ ++ struct net_local *lp = bus->priv; ++ u32 regval; ++ volatile u32 ipisr; ++ ++ regval = XEMACPS_PHYMNTNC_OP_MASK; ++ regval |= XEMACPS_PHYMNTNC_OP_W_MASK; ++ regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK); ++ regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK); ++ regval |= value; ++ ++ xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval); ++ ++ /* wait for end of transfer */ ++ do { ++ cpu_relax(); ++ ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET); ++ } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0); ++ ++ return 0; ++} ++ ++ ++/** ++ * xemacps_mdio_reset - mdio reset. It seems to be required per open ++ * source documentation phy.txt. But there is no reset in this device. ++ * Provide function API for now. ++ * @bus: mdio bus ++ **/ ++static int xemacps_mdio_reset(struct mii_bus *bus) ++{ ++ return 0; ++} ++ ++/** ++ * xemacps_set_freq() - Set a clock to a new frequency ++ * @clk Pointer to the clock to change ++ * @rate New frequency in Hz ++ * @dev Pointer to the struct device ++ */ ++static void xemacps_set_freq(struct clk *clk, long rate, struct device *dev) ++{ ++ rate = clk_round_rate(clk, rate); ++ if (rate < 0) ++ return; ++ ++ dev_info(dev, "Set clk to %ld Hz\n", rate); ++ if (clk_set_rate(clk, rate)) ++ dev_err(dev, "Setting new clock rate failed.\n"); ++} ++ ++/** ++ * xemacps_adjust_link - handles link status changes, such as speed, ++ * duplex, up/down, ... ++ * @ndev: network device ++ */ ++static void xemacps_adjust_link(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct phy_device *phydev = lp->phy_dev; ++ struct phy_device *gmii2rgmii_phydev = lp->gmii2rgmii_phy_dev; ++ int status_change = 0; ++ u32 regval; ++ u16 gmii2rgmii_reg = 0; ++ ++ if (phydev->link) { ++ if ((lp->speed != phydev->speed) || ++ (lp->duplex != phydev->duplex)) { ++ regval = xemacps_read(lp->baseaddr, ++ XEMACPS_NWCFG_OFFSET); ++ regval &= ~(XEMACPS_NWCFG_FDEN_MASK | ++ XEMACPS_NWCFG_1000_MASK | ++ XEMACPS_NWCFG_100_MASK); ++ ++ if (phydev->duplex) { ++ regval |= XEMACPS_NWCFG_FDEN_MASK; ++ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_FULLDPLX; ++ } ++ ++ if (phydev->speed == SPEED_1000) { ++ regval |= XEMACPS_NWCFG_1000_MASK; ++ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED1000; ++ xemacps_set_freq(lp->devclk, 125000000, ++ &lp->pdev->dev); ++ } else if (phydev->speed == SPEED_100) { ++ regval |= XEMACPS_NWCFG_100_MASK; ++ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED100; ++ xemacps_set_freq(lp->devclk, 25000000, ++ &lp->pdev->dev); ++ } else if (phydev->speed == SPEED_10) { ++ xemacps_set_freq(lp->devclk, 2500000, ++ &lp->pdev->dev); ++ } else { ++ dev_err(&lp->pdev->dev, ++ "%s: unknown PHY speed %d\n", ++ __func__, phydev->speed); ++ return; ++ } ++ ++ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, ++ regval); ++ ++ if (gmii2rgmii_phydev != NULL) { ++ xemacps_mdio_write(lp->mii_bus, ++ gmii2rgmii_phydev->addr, ++ XEMACPS_GMII2RGMII_REG_NUM, ++ gmii2rgmii_reg); ++ } ++ ++ lp->speed = phydev->speed; ++ lp->duplex = phydev->duplex; ++ status_change = 1; ++ } ++ } ++ ++ if (phydev->link != lp->link) { ++ lp->link = phydev->link; ++ status_change = 1; ++ } ++ ++ if (status_change) { ++ if (phydev->link) ++ dev_info(&lp->pdev->dev, "link up (%d/%s)\n", ++ phydev->speed, ++ DUPLEX_FULL == phydev->duplex ? ++ "FULL" : "HALF"); ++ else ++ dev_info(&lp->pdev->dev, "link down\n"); ++ } ++} ++ ++static int xemacps_clk_notifier_cb(struct notifier_block *nb, unsigned long ++ event, void *data) ++{ ++/* ++ struct clk_notifier_data *ndata = data; ++ struct net_local *nl = to_net_local(nb); ++*/ ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* if a rate change is announced we need to check whether we ++ * can maintain the current frequency by changing the clock ++ * dividers. ++ * I don't see how this can be done using the current fmwk!? ++ * For now we always allow the rate change. Otherwise we would ++ * even prevent ourself to change the rate. ++ */ ++ return NOTIFY_OK; ++ case POST_RATE_CHANGE: ++ /* not sure this will work. actually i'm sure it does not. this ++ * callback is not allowed to call back into COMMON_CLK, what ++ * adjust_link() does... ++ */ ++ /*xemacps_adjust_link(nl->ndev); would likely lock up kernel */ ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++/** ++ * xemacps_mii_probe - probe mii bus, find the right bus_id to register ++ * phy callback function. ++ * @ndev: network interface device structure ++ * return 0 on success, negative value if error ++ **/ ++static int xemacps_mii_probe(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct phy_device *phydev = NULL; ++ ++ if (lp->phy_node) { ++ phydev = of_phy_connect(lp->ndev, ++ lp->phy_node, ++ &xemacps_adjust_link, ++ 0, ++ lp->phy_interface); ++ } ++ if (!phydev) { ++ dev_err(&lp->pdev->dev, "%s: no PHY found\n", ndev->name); ++ return -1; ++ } ++ ++ dev_dbg(&lp->pdev->dev, ++ "GEM: phydev %p, phydev->phy_id 0x%x, phydev->addr 0x%x\n", ++ phydev, phydev->phy_id, phydev->addr); ++ ++ phydev->supported &= (PHY_GBIT_FEATURES | SUPPORTED_Pause | ++ SUPPORTED_Asym_Pause); ++ phydev->advertising = phydev->supported; ++ ++ lp->link = 0; ++ lp->speed = 0; ++ lp->duplex = -1; ++ lp->phy_dev = phydev; ++ ++ phy_start(lp->phy_dev); ++ ++ dev_dbg(&lp->pdev->dev, "phy_addr 0x%x, phy_id 0x%08x\n", ++ lp->phy_dev->addr, lp->phy_dev->phy_id); ++ ++ dev_dbg(&lp->pdev->dev, "attach [%s] phy driver\n", ++ lp->phy_dev->drv->name); ++ ++ if (lp->gmii2rgmii_phy_node) { ++ phydev = of_phy_connect(lp->ndev, ++ lp->gmii2rgmii_phy_node, ++ NULL, ++ 0, 0); ++ if (!phydev) { ++ dev_err(&lp->pdev->dev, ++ "%s: no gmii to rgmii converter found\n", ++ ndev->name); ++ return -1; ++ } ++ lp->gmii2rgmii_phy_dev = phydev; ++ } else ++ lp->gmii2rgmii_phy_dev = NULL; ++ ++ return 0; ++} ++ ++/** ++ * xemacps_mii_init - Initialize and register mii bus to network device ++ * @lp: local device instance pointer ++ * return 0 on success, negative value if error ++ **/ ++static int xemacps_mii_init(struct net_local *lp) ++{ ++ int rc = -ENXIO, i; ++ struct resource res; ++ struct device_node *np = of_get_parent(lp->phy_node); ++ struct device_node *npp; ++ ++ lp->mii_bus = mdiobus_alloc(); ++ if (lp->mii_bus == NULL) { ++ rc = -ENOMEM; ++ goto err_out; ++ } ++ ++ lp->mii_bus->name = "XEMACPS mii bus"; ++ lp->mii_bus->read = &xemacps_mdio_read; ++ lp->mii_bus->write = &xemacps_mdio_write; ++ lp->mii_bus->reset = &xemacps_mdio_reset; ++ lp->mii_bus->priv = lp; ++ lp->mii_bus->parent = &lp->ndev->dev; ++ ++ lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); ++ if (!lp->mii_bus->irq) { ++ rc = -ENOMEM; ++ goto err_out_free_mdiobus; ++ } ++ ++ for (i = 0; i < PHY_MAX_ADDR; i++) ++ lp->mii_bus->irq[i] = PHY_POLL; ++ npp = of_get_parent(np); ++ of_address_to_resource(npp, 0, &res); ++ snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx", ++ (unsigned long long)res.start); ++ if (of_mdiobus_register(lp->mii_bus, np)) ++ goto err_out_free_mdio_irq; ++ ++ return 0; ++ ++err_out_free_mdio_irq: ++ kfree(lp->mii_bus->irq); ++err_out_free_mdiobus: ++ mdiobus_free(lp->mii_bus); ++err_out: ++ return rc; ++} ++ ++/** ++ * xemacps_update_hdaddr - Update device's MAC address when configured ++ * MAC address is not valid, reconfigure with a good one. ++ * @lp: local device instance pointer ++ **/ ++static void xemacps_update_hwaddr(struct net_local *lp) ++{ ++ u32 regvall; ++ u16 regvalh; ++ u8 addr[6]; ++ ++ regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET); ++ regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET); ++ addr[0] = regvall & 0xFF; ++ addr[1] = (regvall >> 8) & 0xFF; ++ addr[2] = (regvall >> 16) & 0xFF; ++ addr[3] = (regvall >> 24) & 0xFF; ++ addr[4] = regvalh & 0xFF; ++ addr[5] = (regvalh >> 8) & 0xFF; ++ ++ if (is_valid_ether_addr(addr)) { ++ memcpy(lp->ndev->dev_addr, addr, sizeof(addr)); ++ } else { ++ dev_info(&lp->pdev->dev, "invalid address, use assigned\n"); ++ random_ether_addr(lp->ndev->dev_addr); ++ dev_info(&lp->pdev->dev, ++ "MAC updated %02x:%02x:%02x:%02x:%02x:%02x\n", ++ lp->ndev->dev_addr[0], lp->ndev->dev_addr[1], ++ lp->ndev->dev_addr[2], lp->ndev->dev_addr[3], ++ lp->ndev->dev_addr[4], lp->ndev->dev_addr[5]); ++ } ++} ++ ++/** ++ * xemacps_set_hwaddr - Set device's MAC address from ndev->dev_addr ++ * @lp: local device instance pointer ++ **/ ++static void xemacps_set_hwaddr(struct net_local *lp) ++{ ++ u32 regvall = 0; ++ u16 regvalh = 0; ++#ifdef __LITTLE_ENDIAN ++ regvall = cpu_to_le32(*((u32 *)lp->ndev->dev_addr)); ++ regvalh = cpu_to_le16(*((u16 *)(lp->ndev->dev_addr + 4))); ++#endif ++#ifdef __BIG_ENDIAN ++ regvall = cpu_to_be32(*((u32 *)lp->ndev->dev_addr)); ++ regvalh = cpu_to_be16(*((u16 *)(lp->ndev->dev_addr + 4))); ++#endif ++ /* LADDRXH has to be wriiten latter than LADDRXL to enable ++ * this address even if these 16 bits are zeros. ++ */ ++ xemacps_write(lp->baseaddr, XEMACPS_LADDR1L_OFFSET, regvall); ++ xemacps_write(lp->baseaddr, XEMACPS_LADDR1H_OFFSET, regvalh); ++#ifdef DEBUG ++ regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET); ++ regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET); ++ dev_dbg(&lp->pdev->dev, ++ "MAC 0x%08x, 0x%08x, %02x:%02x:%02x:%02x:%02x:%02x\n", ++ regvall, regvalh, ++ (regvall & 0xff), ((regvall >> 8) & 0xff), ++ ((regvall >> 16) & 0xff), (regvall >> 24), ++ (regvalh & 0xff), (regvalh >> 8)); ++#endif ++} ++ ++/** ++ * xemacps_reset_hw - Helper function to reset the underlying hardware. ++ * This is called when we get into such deep trouble that we don't know ++ * how to handle otherwise. ++ * @lp: local device instance pointer ++ */ ++static void xemacps_reset_hw(struct net_local *lp) ++{ ++ u32 regisr; ++ /* make sure we have the buffer for ourselves */ ++ wmb(); ++ ++ /* Have a clean start */ ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, 0); ++ ++ /* Clear statistic counters */ ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, ++ XEMACPS_NWCTRL_STATCLR_MASK); ++ ++ /* Clear TX and RX status */ ++ xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, ~0UL); ++ xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, ~0UL); ++ ++ /* Disable all interrupts */ ++ xemacps_write(lp->baseaddr, XEMACPS_IDR_OFFSET, ~0UL); ++ synchronize_irq(lp->ndev->irq); ++ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); ++} ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ ++/** ++ * xemacps_get_hwticks - get the current value of the GEM internal timer ++ * @lp: local device instance pointer ++ * return: nothing ++ **/ ++static inline void ++xemacps_get_hwticks(struct net_local *lp, u64 *sec, u64 *nsec) ++{ ++ do { ++ *nsec = xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET); ++ *sec = xemacps_read(lp->baseaddr, XEMACPS_1588S_OFFSET); ++ } while (*nsec > xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET)); ++} ++ ++/** ++ * xemacps_read_clock - read raw cycle counter (to be used by time counter) ++ */ ++static cycle_t xemacps_read_clock(const struct cyclecounter *tc) ++{ ++ struct net_local *lp = ++ container_of(tc, struct net_local, cycles); ++ u64 stamp; ++ u64 sec, nsec; ++ ++ xemacps_get_hwticks(lp, &sec, &nsec); ++ stamp = (sec << 32) | nsec; ++ ++ return stamp; ++} ++ ++ ++/** ++ * xemacps_systim_to_hwtstamp - convert system time value to hw timestamp ++ * @adapter: board private structure ++ * @shhwtstamps: timestamp structure to update ++ * @regval: unsigned 64bit system time value. ++ * ++ * We need to convert the system time value stored in the RX/TXSTMP registers ++ * into a hwtstamp which can be used by the upper level timestamping functions ++ */ ++static void xemacps_systim_to_hwtstamp(struct net_local *lp, ++ struct skb_shared_hwtstamps *shhwtstamps, ++ u64 regval) ++{ ++ u64 ns; ++ ++ ns = timecounter_cyc2time(&lp->clock, regval); ++ timecompare_update(&lp->compare, ns); ++ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); ++ shhwtstamps->hwtstamp = ns_to_ktime(ns); ++ shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns); ++} ++ ++static void ++xemacps_rx_hwtstamp(struct net_local *lp, ++ struct sk_buff *skb, unsigned msg_type) ++{ ++ u64 time64, sec, nsec; ++ ++ if (!msg_type) { ++ /* PTP Event Frame packets */ ++ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXS_OFFSET); ++ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXNS_OFFSET); ++ } else { ++ /* PTP Peer Event Frame packets */ ++ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXS_OFFSET); ++ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXNS_OFFSET); ++ } ++ time64 = (sec << 32) | nsec; ++ xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64); ++} ++ ++static void ++xemacps_tx_hwtstamp(struct net_local *lp, ++ struct sk_buff *skb, unsigned msg_type) ++{ ++ u64 time64, sec, nsec; ++ ++ if (!msg_type) { ++ /* PTP Event Frame packets */ ++ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXS_OFFSET); ++ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXNS_OFFSET); ++ } else { ++ /* PTP Peer Event Frame packets */ ++ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXS_OFFSET); ++ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXNS_OFFSET); ++ } ++ ++ time64 = (sec << 32) | nsec; ++ xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64); ++ skb_tstamp_tx(skb, skb_hwtstamps(skb)); ++} ++ ++#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ ++ ++/** ++ * xemacps_rx - process received packets when napi called ++ * @lp: local device instance pointer ++ * @budget: NAPI budget ++ * return: number of BDs processed ++ **/ ++static int xemacps_rx(struct net_local *lp, int budget) ++{ ++ struct xemacps_bd *cur_p; ++ u32 len; ++ struct sk_buff *skb; ++ struct sk_buff *new_skb; ++ u32 new_skb_baddr; ++ unsigned int numbdfree = 0; ++ u32 size = 0; ++ u32 packets = 0; ++ u32 regval; ++ ++ cur_p = &lp->rx_bd[lp->rx_bd_ci]; ++ regval = cur_p->addr; ++ rmb(); ++ while (numbdfree < budget) { ++ if (!(regval & XEMACPS_RXBUF_NEW_MASK)) ++ break; ++ ++ new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE); ++ if (new_skb == NULL) { ++ dev_err(&lp->ndev->dev, "no memory for new sk_buff\n"); ++ break; ++ } ++ /* Get dma handle of skb->data */ ++ new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent, ++ new_skb->data, ++ XEMACPS_RX_BUF_SIZE, ++ DMA_FROM_DEVICE); ++ ++ /* the packet length */ ++ len = cur_p->ctrl & XEMACPS_RXBUF_LEN_MASK; ++ rmb(); ++ skb = lp->rx_skb[lp->rx_bd_ci].skb; ++ dma_unmap_single(lp->ndev->dev.parent, ++ lp->rx_skb[lp->rx_bd_ci].mapping, ++ lp->rx_skb[lp->rx_bd_ci].len, ++ DMA_FROM_DEVICE); ++ ++ /* setup received skb and send it upstream */ ++ skb_put(skb, len); /* Tell the skb how much data we got. */ ++ skb->protocol = eth_type_trans(skb, lp->ndev); ++ ++ skb->ip_summed = lp->ip_summed; ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ if ((lp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) && ++ (ntohs(skb->protocol) == 0x800)) { ++ unsigned ip_proto, dest_port, msg_type; ++ ++ /* While the GEM can timestamp PTP packets, it does ++ * not mark the RX descriptor to identify them. This ++ * is entirely the wrong place to be parsing UDP ++ * headers, but some minimal effort must be made. ++ * NOTE: the below parsing of ip_proto and dest_port ++ * depend on the use of Ethernet_II encapsulation, ++ * IPv4 without any options. ++ */ ++ ip_proto = *((u8 *)skb->mac_header + 14 + 9); ++ dest_port = ntohs(*(((u16 *)skb->mac_header) + ++ ((14 + 20 + 2)/2))); ++ msg_type = *((u8 *)skb->mac_header + 42); ++ if ((ip_proto == IPPROTO_UDP) && ++ (dest_port == 0x13F)) { ++ /* Timestamp this packet */ ++ xemacps_rx_hwtstamp(lp, skb, msg_type & 0x2); ++ } ++ } ++#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ ++ size += len; ++ packets++; ++ netif_receive_skb(skb); ++ ++ cur_p->addr = (cur_p->addr & ~XEMACPS_RXBUF_ADD_MASK) ++ | (new_skb_baddr); ++ lp->rx_skb[lp->rx_bd_ci].skb = new_skb; ++ lp->rx_skb[lp->rx_bd_ci].mapping = new_skb_baddr; ++ lp->rx_skb[lp->rx_bd_ci].len = XEMACPS_RX_BUF_SIZE; ++ ++ cur_p->ctrl = 0; ++ cur_p->addr &= (~XEMACPS_RXBUF_NEW_MASK); ++ wmb(); ++ ++ lp->rx_bd_ci++; ++ lp->rx_bd_ci = lp->rx_bd_ci % XEMACPS_RECV_BD_CNT; ++ cur_p = &lp->rx_bd[lp->rx_bd_ci]; ++ regval = cur_p->addr; ++ rmb(); ++ numbdfree++; ++ } ++ wmb(); ++ lp->stats.rx_packets += packets; ++ lp->stats.rx_bytes += size; ++ return numbdfree; ++} ++ ++/** ++ * xemacps_rx_poll - NAPI poll routine ++ * napi: pointer to napi struct ++ * budget: ++ **/ ++static int xemacps_rx_poll(struct napi_struct *napi, int budget) ++{ ++ struct net_local *lp = container_of(napi, struct net_local, napi); ++ int work_done = 0; ++ u32 regval; ++ ++ spin_lock(&lp->rx_lock); ++ while (1) { ++ regval = xemacps_read(lp->baseaddr, XEMACPS_RXSR_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, regval); ++ if (regval & XEMACPS_RXSR_HRESPNOK_MASK) ++ dev_err(&lp->pdev->dev, "RX error 0x%x\n", regval); ++ ++ work_done += xemacps_rx(lp, budget - work_done); ++ if (work_done >= budget) ++ break; ++ ++ napi_complete(napi); ++ /* We disabled RX interrupts in interrupt service ++ * routine, now it is time to enable it back. ++ */ ++ xemacps_write(lp->baseaddr, ++ XEMACPS_IER_OFFSET, XEMACPS_IXR_FRAMERX_MASK); ++ ++ /* If a packet has come in between the last check of the BD ++ * list and unmasking the interrupts, we may have missed the ++ * interrupt, so reschedule here. ++ */ ++ if ((lp->rx_bd[lp->rx_bd_ci].addr & XEMACPS_RXBUF_NEW_MASK) ++ && napi_reschedule(napi)) { ++ xemacps_write(lp->baseaddr, ++ XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK); ++ continue; ++ } ++ break; ++ } ++ spin_unlock(&lp->rx_lock); ++ return work_done; ++} ++ ++/** ++ * xemacps_tx_poll - tx bd reclaim tasklet handler ++ * @data: pointer to network interface device structure ++ **/ ++static void xemacps_tx_poll(unsigned long data) ++{ ++ struct net_device *ndev = (struct net_device *)data; ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ u32 len = 0; ++ unsigned int bdcount = 0; ++ unsigned int bdpartialcount = 0; ++ unsigned int sop = 0; ++ struct xemacps_bd *cur_p; ++ u32 cur_i; ++ u32 numbdstofree; ++ u32 numbdsinhw; ++ struct ring_info *rp; ++ struct sk_buff *skb; ++ unsigned long flags; ++ ++ spin_lock(&lp->tx_lock); ++ regval = xemacps_read(lp->baseaddr, XEMACPS_TXSR_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, regval); ++ dev_dbg(&lp->pdev->dev, "TX status 0x%x\n", regval); ++ if (regval & (XEMACPS_TXSR_HRESPNOK_MASK | XEMACPS_TXSR_BUFEXH_MASK)) ++ dev_err(&lp->pdev->dev, "TX error 0x%x\n", regval); ++ ++ cur_i = lp->tx_bd_ci; ++ cur_p = &lp->tx_bd[cur_i]; ++ numbdsinhw = XEMACPS_SEND_BD_CNT - lp->tx_bd_freecnt; ++ while (bdcount < numbdsinhw) { ++ if (sop == 0) { ++ if (cur_p->ctrl & XEMACPS_TXBUF_USED_MASK) ++ sop = 1; ++ else ++ break; ++ } ++ ++ bdcount++; ++ bdpartialcount++; ++ ++ /* hardware has processed this BD so check the "last" bit. ++ * If it is clear, then there are more BDs for the current ++ * packet. Keep a count of these partial packet BDs. ++ */ ++ if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) { ++ sop = 0; ++ bdpartialcount = 0; ++ } ++ ++ cur_i++; ++ cur_i = cur_i % XEMACPS_SEND_BD_CNT; ++ cur_p = &lp->tx_bd[cur_i]; ++ } ++ numbdstofree = bdcount - bdpartialcount; ++ lp->tx_bd_freecnt += numbdstofree; ++ numbdsinhw -= numbdstofree; ++ if (!numbdstofree) ++ goto tx_poll_out; ++ ++ cur_p = &lp->tx_bd[lp->tx_bd_ci]; ++ while (numbdstofree) { ++ rp = &lp->tx_skb[lp->tx_bd_ci]; ++ skb = rp->skb; ++ ++ len += (cur_p->ctrl & XEMACPS_TXBUF_LEN_MASK); ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ if ((lp->hwtstamp_config.tx_type == HWTSTAMP_TX_ON) && ++ (ntohs(skb->protocol) == 0x800)) { ++ unsigned ip_proto, dest_port, msg_type; ++ ++ skb_reset_mac_header(skb); ++ ++ ip_proto = *((u8 *)skb->mac_header + 14 + 9); ++ dest_port = ntohs(*(((u16 *)skb->mac_header) + ++ ((14 + 20 + 2)/2))); ++ msg_type = *((u8 *)skb->mac_header + 42); ++ if ((ip_proto == IPPROTO_UDP) && ++ (dest_port == 0x13F)) { ++ /* Timestamp this packet */ ++ xemacps_tx_hwtstamp(lp, skb, msg_type & 0x2); ++ } ++ } ++#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ ++ ++ dma_unmap_single(&lp->pdev->dev, rp->mapping, rp->len, ++ DMA_TO_DEVICE); ++ rp->skb = NULL; ++ dev_kfree_skb(skb); ++ /* log tx completed packets and bytes, errors logs ++ * are in other error counters. ++ */ ++ if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) { ++ lp->stats.tx_packets++; ++ lp->stats.tx_bytes += len; ++ len = 0; ++ } ++ ++ /* Set used bit, preserve wrap bit; clear everything else. */ ++ cur_p->ctrl |= XEMACPS_TXBUF_USED_MASK; ++ cur_p->ctrl &= (XEMACPS_TXBUF_USED_MASK | ++ XEMACPS_TXBUF_WRAP_MASK); ++ ++ lp->tx_bd_ci++; ++ lp->tx_bd_ci = lp->tx_bd_ci % XEMACPS_SEND_BD_CNT; ++ cur_p = &lp->tx_bd[lp->tx_bd_ci]; ++ numbdstofree--; ++ } ++ wmb(); ++ ++ if (numbdsinhw) { ++ spin_lock_irqsave(&lp->nwctrlreg_lock, flags); ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); ++ regval |= XEMACPS_NWCTRL_STARTTX_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); ++ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); ++ } ++ ++ netif_wake_queue(ndev); ++ ++tx_poll_out: ++ spin_unlock(&lp->tx_lock); ++} ++ ++/** ++ * xemacps_interrupt - interrupt main service routine ++ * @irq: interrupt number ++ * @dev_id: pointer to a network device structure ++ * return IRQ_HANDLED or IRQ_NONE ++ **/ ++static irqreturn_t xemacps_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = dev_id; ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regisr; ++ u32 regctrl; ++ ++ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); ++ if (unlikely(!regisr)) ++ return IRQ_NONE; ++ ++ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); ++ ++ while (regisr) { ++ if (regisr & (XEMACPS_IXR_TXCOMPL_MASK | ++ XEMACPS_IXR_TX_ERR_MASK)) { ++ tasklet_schedule(&lp->tx_bdreclaim_tasklet); ++ } ++ ++ if (regisr & XEMACPS_IXR_RXUSED_MASK) { ++ spin_lock(&lp->nwctrlreg_lock); ++ regctrl = xemacps_read(lp->baseaddr, ++ XEMACPS_NWCTRL_OFFSET); ++ regctrl |= XEMACPS_NWCTRL_FLUSH_DPRAM_MASK; ++ xemacps_write(lp->baseaddr, ++ XEMACPS_NWCTRL_OFFSET, regctrl); ++ spin_unlock(&lp->nwctrlreg_lock); ++ } ++ ++ if (regisr & XEMACPS_IXR_FRAMERX_MASK) { ++ xemacps_write(lp->baseaddr, ++ XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK); ++ napi_schedule(&lp->napi); ++ } ++ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* Free all packets presently in the descriptor rings. */ ++static void xemacps_clean_rings(struct net_local *lp) ++{ ++ int i; ++ ++ for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) { ++ if (lp->rx_skb && lp->rx_skb[i].skb) { ++ dma_unmap_single(lp->ndev->dev.parent, ++ lp->rx_skb[i].mapping, ++ lp->rx_skb[i].len, ++ DMA_FROM_DEVICE); ++ ++ dev_kfree_skb(lp->rx_skb[i].skb); ++ lp->rx_skb[i].skb = NULL; ++ lp->rx_skb[i].mapping = 0; ++ } ++ } ++ ++ for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) { ++ if (lp->tx_skb && lp->tx_skb[i].skb) { ++ dma_unmap_single(lp->ndev->dev.parent, ++ lp->tx_skb[i].mapping, ++ lp->tx_skb[i].len, ++ DMA_TO_DEVICE); ++ ++ dev_kfree_skb(lp->tx_skb[i].skb); ++ lp->tx_skb[i].skb = NULL; ++ lp->tx_skb[i].mapping = 0; ++ } ++ } ++} ++ ++/** ++ * xemacps_descriptor_free - Free allocated TX and RX BDs ++ * @lp: local device instance pointer ++ **/ ++static void xemacps_descriptor_free(struct net_local *lp) ++{ ++ int size; ++ ++ xemacps_clean_rings(lp); ++ ++ /* kfree(NULL) is safe, no need to check here */ ++ kfree(lp->tx_skb); ++ lp->tx_skb = NULL; ++ kfree(lp->rx_skb); ++ lp->rx_skb = NULL; ++ ++ size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd); ++ if (lp->rx_bd) { ++ dma_free_coherent(&lp->pdev->dev, size, ++ lp->rx_bd, lp->rx_bd_dma); ++ lp->rx_bd = NULL; ++ } ++ ++ size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd); ++ if (lp->tx_bd) { ++ dma_free_coherent(&lp->pdev->dev, size, ++ lp->tx_bd, lp->tx_bd_dma); ++ lp->tx_bd = NULL; ++ } ++} ++ ++/** ++ * xemacps_descriptor_init - Allocate both TX and RX BDs ++ * @lp: local device instance pointer ++ * return 0 on success, negative value if error ++ **/ ++static int xemacps_descriptor_init(struct net_local *lp) ++{ ++ int size; ++ struct sk_buff *new_skb; ++ u32 new_skb_baddr; ++ u32 i; ++ struct xemacps_bd *cur_p; ++ u32 regval; ++ ++ lp->tx_skb = NULL; ++ lp->rx_skb = NULL; ++ lp->rx_bd = NULL; ++ lp->tx_bd = NULL; ++ ++ /* Reset the indexes which are used for accessing the BDs */ ++ lp->tx_bd_ci = 0; ++ lp->tx_bd_tail = 0; ++ lp->rx_bd_ci = 0; ++ ++ size = XEMACPS_SEND_BD_CNT * sizeof(struct ring_info); ++ lp->tx_skb = kzalloc(size, GFP_KERNEL); ++ if (!lp->tx_skb) ++ goto err_out; ++ size = XEMACPS_RECV_BD_CNT * sizeof(struct ring_info); ++ lp->rx_skb = kzalloc(size, GFP_KERNEL); ++ if (!lp->rx_skb) ++ goto err_out; ++ ++ /* Set up RX buffer descriptors. */ ++ ++ size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd); ++ lp->rx_bd = dma_alloc_coherent(&lp->pdev->dev, size, ++ &lp->rx_bd_dma, GFP_KERNEL); ++ if (!lp->rx_bd) ++ goto err_out; ++ dev_dbg(&lp->pdev->dev, "RX ring %d bytes at 0x%x mapped %p\n", ++ size, lp->rx_bd_dma, lp->rx_bd); ++ ++ for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) { ++ cur_p = &lp->rx_bd[i]; ++ ++ new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE); ++ if (new_skb == NULL) { ++ dev_err(&lp->ndev->dev, "alloc_skb error %d\n", i); ++ goto err_out; ++ } ++ ++ /* Get dma handle of skb->data */ ++ new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent, ++ new_skb->data, ++ XEMACPS_RX_BUF_SIZE, ++ DMA_FROM_DEVICE); ++ ++ /* set wrap bit for last BD */ ++ regval = (new_skb_baddr & XEMACPS_RXBUF_ADD_MASK); ++ if (i == XEMACPS_RECV_BD_CNT - 1) ++ regval |= XEMACPS_RXBUF_WRAP_MASK; ++ cur_p->addr = regval; ++ cur_p->ctrl = 0; ++ wmb(); ++ ++ lp->rx_skb[i].skb = new_skb; ++ lp->rx_skb[i].mapping = new_skb_baddr; ++ lp->rx_skb[i].len = XEMACPS_RX_BUF_SIZE; ++ } ++ ++ /* Set up TX buffer descriptors. */ ++ ++ size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd); ++ lp->tx_bd = dma_alloc_coherent(&lp->pdev->dev, size, ++ &lp->tx_bd_dma, GFP_KERNEL); ++ if (!lp->tx_bd) ++ goto err_out; ++ dev_dbg(&lp->pdev->dev, "TX ring %d bytes at 0x%x mapped %p\n", ++ size, lp->tx_bd_dma, lp->tx_bd); ++ ++ for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) { ++ cur_p = &lp->tx_bd[i]; ++ /* set wrap bit for last BD */ ++ cur_p->addr = 0; ++ regval = XEMACPS_TXBUF_USED_MASK; ++ if (i == XEMACPS_SEND_BD_CNT - 1) ++ regval |= XEMACPS_TXBUF_WRAP_MASK; ++ cur_p->ctrl = regval; ++ } ++ wmb(); ++ ++ lp->tx_bd_freecnt = XEMACPS_SEND_BD_CNT; ++ ++ dev_dbg(&lp->pdev->dev, ++ "lp->tx_bd %p lp->tx_bd_dma %p lp->tx_skb %p\n", ++ lp->tx_bd, (void *)lp->tx_bd_dma, lp->tx_skb); ++ dev_dbg(&lp->pdev->dev, ++ "lp->rx_bd %p lp->rx_bd_dma %p lp->rx_skb %p\n", ++ lp->rx_bd, (void *)lp->rx_bd_dma, lp->rx_skb); ++ ++ return 0; ++ ++err_out: ++ xemacps_descriptor_free(lp); ++ return -ENOMEM; ++} ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++/* ++ * Initialize the GEM Time Stamp Unit ++ */ ++static void xemacps_init_tsu(struct net_local *lp) ++{ ++ ++ memset(&lp->cycles, 0, sizeof(lp->cycles)); ++ lp->cycles.read = xemacps_read_clock; ++ lp->cycles.mask = CLOCKSOURCE_MASK(64); ++ lp->cycles.mult = 1; ++ lp->cycles.shift = 0; ++ ++ /* Set registers so that rollover occurs soon to test this. */ ++ xemacps_write(lp->baseaddr, XEMACPS_1588NS_OFFSET, 0x00000000); ++ xemacps_write(lp->baseaddr, XEMACPS_1588S_OFFSET, 0xFF800000); ++ ++ /* program the timer increment register with the numer of nanoseconds ++ * per clock tick. ++ * ++ * Note: The value is calculated based on the current operating ++ * frequency 50MHz ++ */ ++ xemacps_write(lp->baseaddr, XEMACPS_1588INC_OFFSET, ++ (NS_PER_SEC/lp->ptpenetclk)); ++ ++ timecounter_init(&lp->clock, &lp->cycles, ++ ktime_to_ns(ktime_get_real())); ++ /* ++ * Synchronize our NIC clock against system wall clock. ++ */ ++ memset(&lp->compare, 0, sizeof(lp->compare)); ++ lp->compare.source = &lp->clock; ++ lp->compare.target = ktime_get_real; ++ lp->compare.num_samples = 10; ++ timecompare_update(&lp->compare, 0); ++ ++ /* Initialize hwstamp config */ ++ lp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; ++ lp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; ++ ++} ++#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ ++ ++/** ++ * xemacps_init_hw - Initialize hardware to known good state ++ * @lp: local device instance pointer ++ **/ ++static void xemacps_init_hw(struct net_local *lp) ++{ ++ u32 regval; ++ ++ xemacps_reset_hw(lp); ++ xemacps_set_hwaddr(lp); ++ ++ /* network configuration */ ++ regval = 0; ++ regval |= XEMACPS_NWCFG_FDEN_MASK; ++ regval |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; ++ regval |= XEMACPS_NWCFG_PAUSECOPYDI_MASK; ++ regval |= XEMACPS_NWCFG_FCSREM_MASK; ++ regval |= XEMACPS_NWCFG_PAUSEEN_MASK; ++ regval |= XEMACPS_NWCFG_100_MASK; ++ regval |= XEMACPS_NWCFG_HDRXEN_MASK; ++ ++ regval |= (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK); ++ if (lp->ndev->flags & IFF_PROMISC) /* copy all */ ++ regval |= XEMACPS_NWCFG_COPYALLEN_MASK; ++ if (!(lp->ndev->flags & IFF_BROADCAST)) /* No broadcast */ ++ regval |= XEMACPS_NWCFG_BCASTDI_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); ++ ++ /* Init TX and RX DMA Q address */ ++ xemacps_write(lp->baseaddr, XEMACPS_RXQBASE_OFFSET, lp->rx_bd_dma); ++ xemacps_write(lp->baseaddr, XEMACPS_TXQBASE_OFFSET, lp->tx_bd_dma); ++ ++ /* DMACR configurations */ ++ regval = (((XEMACPS_RX_BUF_SIZE / XEMACPS_RX_BUF_UNIT) + ++ ((XEMACPS_RX_BUF_SIZE % XEMACPS_RX_BUF_UNIT) ? 1 : 0)) << ++ XEMACPS_DMACR_RXBUF_SHIFT); ++ regval |= XEMACPS_DMACR_RXSIZE_MASK; ++ regval |= XEMACPS_DMACR_TXSIZE_MASK; ++ regval |= XEMACPS_DMACR_TCPCKSUM_MASK; ++#ifdef __LITTLE_ENDIAN ++ regval &= ~XEMACPS_DMACR_ENDIAN_MASK; ++#endif ++#ifdef __BIG_ENDIAN ++ regval |= XEMACPS_DMACR_ENDIAN_MASK; ++#endif ++ regval |= XEMACPS_DMACR_BLENGTH_INCR16; ++ xemacps_write(lp->baseaddr, XEMACPS_DMACR_OFFSET, regval); ++ ++ /* Enable TX, RX and MDIO port */ ++ regval = 0; ++ regval |= XEMACPS_NWCTRL_MDEN_MASK; ++ regval |= XEMACPS_NWCTRL_TXEN_MASK; ++ regval |= XEMACPS_NWCTRL_RXEN_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ /* Initialize the Time Stamp Unit */ ++ xemacps_init_tsu(lp); ++#endif ++ ++ /* Enable interrupts */ ++ regval = XEMACPS_IXR_ALL_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_IER_OFFSET, regval); ++} ++ ++/** ++ * xemacps_resetrx_for_no_rxdata - Resets the Rx if there is no data ++ * for a while (presently 100 msecs) ++ * @data: Used for net_local instance pointer ++ **/ ++static void xemacps_resetrx_for_no_rxdata(unsigned long data) ++{ ++ struct net_local *lp = (struct net_local *)data; ++ unsigned long regctrl; ++ unsigned long tempcntr; ++ unsigned long flags; ++ ++ tempcntr = xemacps_read(lp->baseaddr, XEMACPS_RXCNT_OFFSET); ++ if ((!tempcntr) && (!(lp->lastrxfrmscntr))) { ++ spin_lock_irqsave(&lp->nwctrlreg_lock, flags); ++ regctrl = xemacps_read(lp->baseaddr, ++ XEMACPS_NWCTRL_OFFSET); ++ regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK); ++ xemacps_write(lp->baseaddr, ++ XEMACPS_NWCTRL_OFFSET, regctrl); ++ regctrl = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); ++ regctrl |= (XEMACPS_NWCTRL_RXEN_MASK); ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regctrl); ++ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); ++ } ++ lp->lastrxfrmscntr = tempcntr; ++} ++ ++/** ++ * xemacps_update_stats - Update the statistic structure entries from ++ * the corresponding emacps hardware statistic registers ++ * @data: Used for net_local instance pointer ++ **/ ++static void xemacps_update_stats(unsigned long data) ++{ ++ struct net_local *lp = (struct net_local *)data; ++ struct net_device_stats *nstat = &lp->stats; ++ u32 cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXUNDRCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_length_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXOVRCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_length_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXJABCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_length_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXFCSCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_crc_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXLENGTHCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_length_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXALIGNCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_frame_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXRESERRCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_missed_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXORCNT_OFFSET); ++ nstat->rx_errors += cnt; ++ nstat->rx_fifo_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_TXURUNCNT_OFFSET); ++ nstat->tx_errors += cnt; ++ nstat->tx_fifo_errors += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_SNGLCOLLCNT_OFFSET); ++ nstat->collisions += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_MULTICOLLCNT_OFFSET); ++ nstat->collisions += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_EXCESSCOLLCNT_OFFSET); ++ nstat->tx_errors += cnt; ++ nstat->tx_aborted_errors += cnt; ++ nstat->collisions += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_LATECOLLCNT_OFFSET); ++ nstat->tx_errors += cnt; ++ nstat->collisions += cnt; ++ ++ cnt = xemacps_read(lp->baseaddr, XEMACPS_CSENSECNT_OFFSET); ++ nstat->tx_errors += cnt; ++ nstat->tx_carrier_errors += cnt; ++} ++ ++/** ++ * xemacps_gen_purpose_timerhandler - Timer handler that is called at regular ++ * intervals upon expiry of the gen_purpose_timer defined in net_local struct. ++ * @data: Used for net_local instance pointer ++ * ++ * This timer handler is used to update the statistics by calling the API ++ * xemacps_update_stats. The statistics register can typically overflow pretty ++ * quickly under heavy load conditions. This timer is used to periodically ++ * read the stats registers and update the corresponding stats structure ++ * entries. The stats registers when read reset to 0. ++ **/ ++static void xemacps_gen_purpose_timerhandler(unsigned long data) ++{ ++ struct net_local *lp = (struct net_local *)data; ++ ++ xemacps_update_stats(data); ++ xemacps_resetrx_for_no_rxdata(data); ++ mod_timer(&(lp->gen_purpose_timer), ++ jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD)); ++} ++ ++/** ++ * xemacps_open - Called when a network device is made active ++ * @ndev: network interface device structure ++ * return 0 on success, negative value if error ++ * ++ * The open entry point is called when a network interface is made active ++ * by the system (IFF_UP). At this point all resources needed for transmit ++ * and receive operations are allocated, the interrupt handler is ++ * registered with OS, the watchdog timer is started, and the stack is ++ * notified that the interface is ready. ++ * ++ * note: if error(s), allocated resources before error require to be ++ * released or system issues (such as memory) leak might happen. ++ **/ ++static int xemacps_open(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ int rc; ++ ++ dev_dbg(&lp->pdev->dev, "open\n"); ++ if (!is_valid_ether_addr(ndev->dev_addr)) ++ return -EADDRNOTAVAIL; ++ ++ rc = xemacps_descriptor_init(lp); ++ if (rc) { ++ dev_err(&lp->pdev->dev, ++ "Unable to allocate DMA memory, rc %d\n", rc); ++ return rc; ++ } ++ ++ rc = pm_runtime_get_sync(&lp->pdev->dev); ++ if (rc < 0) { ++ dev_err(&lp->pdev->dev, ++ "pm_runtime_get_sync() failed, rc %d\n", rc); ++ goto err_free_rings; ++ } ++ ++ xemacps_init_hw(lp); ++ rc = xemacps_mii_probe(ndev); ++ if (rc != 0) { ++ dev_err(&lp->pdev->dev, ++ "%s mii_probe fail.\n", lp->mii_bus->name); ++ if (rc == (-2)) { ++ mdiobus_unregister(lp->mii_bus); ++ kfree(lp->mii_bus->irq); ++ mdiobus_free(lp->mii_bus); ++ } ++ rc = -ENXIO; ++ goto err_pm_put; ++ } ++ ++ setup_timer(&(lp->gen_purpose_timer), xemacps_gen_purpose_timerhandler, ++ (unsigned long)lp); ++ mod_timer(&(lp->gen_purpose_timer), ++ jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD)); ++ ++ napi_enable(&lp->napi); ++ netif_carrier_on(ndev); ++ netif_start_queue(ndev); ++ tasklet_enable(&lp->tx_bdreclaim_tasklet); ++ ++ return 0; ++ ++err_pm_put: ++ xemacps_reset_hw(lp); ++ pm_runtime_put(&lp->pdev->dev); ++err_free_rings: ++ xemacps_descriptor_free(lp); ++ ++ return rc; ++} ++ ++/** ++ * xemacps_close - disable a network interface ++ * @ndev: network interface device structure ++ * return 0 ++ * ++ * The close entry point is called when a network interface is de-activated ++ * by OS. The hardware is still under the driver control, but needs to be ++ * disabled. A global MAC reset is issued to stop the hardware, and all ++ * transmit and receive resources are freed. ++ **/ ++static int xemacps_close(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ ++ del_timer_sync(&(lp->gen_purpose_timer)); ++ netif_stop_queue(ndev); ++ napi_disable(&lp->napi); ++ tasklet_disable(&lp->tx_bdreclaim_tasklet); ++ netif_carrier_off(ndev); ++ if (lp->phy_dev) ++ phy_disconnect(lp->phy_dev); ++ if (lp->gmii2rgmii_phy_node) ++ phy_disconnect(lp->gmii2rgmii_phy_dev); ++ xemacps_reset_hw(lp); ++ mdelay(500); ++ xemacps_descriptor_free(lp); ++ ++ pm_runtime_put(&lp->pdev->dev); ++ ++ return 0; ++} ++ ++/** ++ * xemacps_reinit_for_txtimeout - work queue scheduled for the tx timeout ++ * handling. ++ * @ndev: queue work structure ++ **/ ++static void xemacps_reinit_for_txtimeout(struct work_struct *data) ++{ ++ struct net_local *lp = container_of(data, struct net_local, ++ txtimeout_reinit); ++ int rc; ++ ++ netif_stop_queue(lp->ndev); ++ napi_disable(&lp->napi); ++ tasklet_disable(&lp->tx_bdreclaim_tasklet); ++ spin_lock_bh(&lp->tx_lock); ++ xemacps_reset_hw(lp); ++ spin_unlock_bh(&lp->tx_lock); ++ ++ if (lp->phy_dev) ++ phy_stop(lp->phy_dev); ++ ++ xemacps_descriptor_free(lp); ++ rc = xemacps_descriptor_init(lp); ++ if (rc) { ++ dev_err(&lp->pdev->dev, ++ "Unable to allocate DMA memory, rc %d\n", rc); ++ return; ++ } ++ ++ xemacps_init_hw(lp); ++ ++ lp->link = 0; ++ lp->speed = 0; ++ lp->duplex = -1; ++ ++ if (lp->phy_dev) ++ phy_start(lp->phy_dev); ++ ++ napi_enable(&lp->napi); ++ tasklet_enable(&lp->tx_bdreclaim_tasklet); ++ lp->ndev->trans_start = jiffies; ++ netif_wake_queue(lp->ndev); ++} ++ ++/** ++ * xemacps_tx_timeout - callback used when the transmitter has not made ++ * any progress for dev->watchdog ticks. ++ * @ndev: network interface device structure ++ **/ ++static void xemacps_tx_timeout(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ ++ dev_err(&lp->pdev->dev, "transmit timeout %lu ms, reseting...\n", ++ TX_TIMEOUT * 1000UL / HZ); ++ queue_work(lp->txtimeout_handler_wq, &lp->txtimeout_reinit); ++} ++ ++/** ++ * xemacps_set_mac_address - set network interface mac address ++ * @ndev: network interface device structure ++ * @addr: pointer to MAC address ++ * return 0 on success, negative value if error ++ **/ ++static int xemacps_set_mac_address(struct net_device *ndev, void *addr) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct sockaddr *hwaddr = (struct sockaddr *)addr; ++ ++ if (netif_running(ndev)) ++ return -EBUSY; ++ ++ if (!is_valid_ether_addr(hwaddr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ dev_dbg(&lp->pdev->dev, "hwaddr 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", ++ hwaddr->sa_data[0], hwaddr->sa_data[1], hwaddr->sa_data[2], ++ hwaddr->sa_data[3], hwaddr->sa_data[4], hwaddr->sa_data[5]); ++ ++ memcpy(ndev->dev_addr, hwaddr->sa_data, ndev->addr_len); ++ ++ xemacps_set_hwaddr(lp); ++ return 0; ++} ++ ++/** ++ * xemacps_clear_csum - Clear the csum field for transport protocols ++ * @skb: socket buffer ++ * @ndev: network interface device structure ++ * return 0 on success, other value if error ++ **/ ++static int xemacps_clear_csum(struct sk_buff *skb, struct net_device *ndev) ++{ ++ /* Only run for packets requiring a checksum. */ ++ if (skb->ip_summed != CHECKSUM_PARTIAL) ++ return 0; ++ ++ if (unlikely(skb_cow_head(skb, 0))) ++ return -1; ++ ++ *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; ++ ++ return 0; ++} ++ ++/** ++ * xemacps_start_xmit - transmit a packet (called by kernel) ++ * @skb: socket buffer ++ * @ndev: network interface device structure ++ * return 0 on success, other value if error ++ **/ ++static int xemacps_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ dma_addr_t mapping; ++ unsigned int nr_frags, len; ++ int i; ++ u32 regval; ++ void *virt_addr; ++ skb_frag_t *frag; ++ struct xemacps_bd *cur_p; ++ unsigned long flags; ++ u32 bd_tail; ++ ++ nr_frags = skb_shinfo(skb)->nr_frags + 1; ++ spin_lock_bh(&lp->tx_lock); ++ ++ if (nr_frags > lp->tx_bd_freecnt) { ++ netif_stop_queue(ndev); /* stop send queue */ ++ spin_unlock_bh(&lp->tx_lock); ++ return NETDEV_TX_BUSY; ++ } ++ ++ if (xemacps_clear_csum(skb, ndev)) { ++ spin_unlock_bh(&lp->tx_lock); ++ kfree(skb); ++ return NETDEV_TX_OK; ++ } ++ ++ bd_tail = lp->tx_bd_tail; ++ cur_p = &lp->tx_bd[bd_tail]; ++ lp->tx_bd_freecnt -= nr_frags; ++ frag = &skb_shinfo(skb)->frags[0]; ++ ++ for (i = 0; i < nr_frags; i++) { ++ if (i == 0) { ++ len = skb_headlen(skb); ++ mapping = dma_map_single(&lp->pdev->dev, skb->data, ++ len, DMA_TO_DEVICE); ++ } else { ++ len = skb_frag_size(frag); ++ virt_addr = skb_frag_address(frag); ++ mapping = dma_map_single(&lp->pdev->dev, virt_addr, ++ len, DMA_TO_DEVICE); ++ frag++; ++ skb_get(skb); ++ } ++ ++ lp->tx_skb[lp->tx_bd_tail].skb = skb; ++ lp->tx_skb[lp->tx_bd_tail].mapping = mapping; ++ lp->tx_skb[lp->tx_bd_tail].len = len; ++ cur_p->addr = mapping; ++ ++ /* preserve critical status bits */ ++ regval = cur_p->ctrl; ++ regval &= (XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK); ++ /* update length field */ ++ regval |= ((regval & ~XEMACPS_TXBUF_LEN_MASK) | len); ++ /* commit second to last buffer to hardware */ ++ if (i != 0) ++ regval &= ~XEMACPS_TXBUF_USED_MASK; ++ /* last fragment of this packet? */ ++ if (i == (nr_frags - 1)) ++ regval |= XEMACPS_TXBUF_LAST_MASK; ++ cur_p->ctrl = regval; ++ ++ lp->tx_bd_tail++; ++ lp->tx_bd_tail = lp->tx_bd_tail % XEMACPS_SEND_BD_CNT; ++ cur_p = &(lp->tx_bd[lp->tx_bd_tail]); ++ } ++ wmb(); ++ ++ /* commit first buffer to hardware -- do this after ++ * committing the other buffers to avoid an underrun */ ++ cur_p = &lp->tx_bd[bd_tail]; ++ regval = cur_p->ctrl; ++ regval &= ~XEMACPS_TXBUF_USED_MASK; ++ cur_p->ctrl = regval; ++ wmb(); ++ ++ spin_lock_irqsave(&lp->nwctrlreg_lock, flags); ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, ++ (regval | XEMACPS_NWCTRL_STARTTX_MASK)); ++ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); ++ ++ spin_unlock_bh(&lp->tx_lock); ++ ndev->trans_start = jiffies; ++ return 0; ++} ++ ++/* Get the MAC Address bit from the specified position */ ++static unsigned get_bit(u8 *mac, unsigned bit) ++{ ++ unsigned byte; ++ ++ byte = mac[bit / 8]; ++ byte >>= (bit & 0x7); ++ byte &= 1; ++ ++ return byte; ++} ++ ++/* Calculate a GEM MAC Address hash index */ ++static unsigned calc_mac_hash(u8 *mac) ++{ ++ int index_bit, mac_bit; ++ unsigned hash_index; ++ ++ hash_index = 0; ++ mac_bit = 5; ++ for (index_bit = 5; index_bit >= 0; index_bit--) { ++ hash_index |= (get_bit(mac, mac_bit) ^ ++ get_bit(mac, mac_bit + 6) ^ ++ get_bit(mac, mac_bit + 12) ^ ++ get_bit(mac, mac_bit + 18) ^ ++ get_bit(mac, mac_bit + 24) ^ ++ get_bit(mac, mac_bit + 30) ^ ++ get_bit(mac, mac_bit + 36) ^ ++ get_bit(mac, mac_bit + 42)) ++ << index_bit; ++ mac_bit--; ++ } ++ ++ return hash_index; ++} ++ ++/** ++ * xemacps_set_hashtable - Add multicast addresses to the internal ++ * multicast-hash table. Called from xemac_set_rx_mode(). ++ * @ndev: network interface device structure ++ * ++ * The hash address register is 64 bits long and takes up two ++ * locations in the memory map. The least significant bits are stored ++ * in EMAC_HSL and the most significant bits in EMAC_HSH. ++ * ++ * The unicast hash enable and the multicast hash enable bits in the ++ * network configuration register enable the reception of hash matched ++ * frames. The destination address is reduced to a 6 bit index into ++ * the 64 bit hash register using the following hash function. The ++ * hash function is an exclusive or of every sixth bit of the ++ * destination address. ++ * ++ * hi[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47] ++ * hi[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46] ++ * hi[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45] ++ * hi[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44] ++ * hi[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43] ++ * hi[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42] ++ * ++ * da[0] represents the least significant bit of the first byte ++ * received, that is, the multicast/unicast indicator, and da[47] ++ * represents the most significant bit of the last byte received. If ++ * the hash index, hi[n], points to a bit that is set in the hash ++ * register then the frame will be matched according to whether the ++ * frame is multicast or unicast. A multicast match will be signalled ++ * if the multicast hash enable bit is set, da[0] is 1 and the hash ++ * index points to a bit set in the hash register. A unicast match ++ * will be signalled if the unicast hash enable bit is set, da[0] is 0 ++ * and the hash index points to a bit set in the hash register. To ++ * receive all multicast frames, the hash register should be set with ++ * all ones and the multicast hash enable bit should be set in the ++ * network configuration register. ++ **/ ++static void xemacps_set_hashtable(struct net_device *ndev) ++{ ++ struct netdev_hw_addr *curr; ++ u32 regvalh, regvall, hash_index; ++ u8 *mc_addr; ++ struct net_local *lp; ++ ++ lp = netdev_priv(ndev); ++ ++ regvalh = regvall = 0; ++ ++ netdev_for_each_mc_addr(curr, ndev) { ++ if (!curr) /* end of list */ ++ break; ++ mc_addr = curr->addr; ++ hash_index = calc_mac_hash(mc_addr); ++ ++ if (hash_index >= XEMACPS_MAX_HASH_BITS) { ++ dev_err(&lp->pdev->dev, ++ "hash calculation out of range %d\n", ++ hash_index); ++ break; ++ } ++ if (hash_index < 32) ++ regvall |= (1 << hash_index); ++ else ++ regvalh |= (1 << (hash_index - 32)); ++ } ++ ++ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, regvall); ++ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, regvalh); ++} ++ ++/** ++ * xemacps_set_rx_mode - enable/disable promiscuous and multicast modes ++ * @ndev: network interface device structure ++ **/ ++static void xemacps_set_rx_mode(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); ++ ++ /* promisc mode */ ++ if (ndev->flags & IFF_PROMISC) ++ regval |= XEMACPS_NWCFG_COPYALLEN_MASK; ++ if (!(ndev->flags & IFF_PROMISC)) ++ regval &= ~XEMACPS_NWCFG_COPYALLEN_MASK; ++ ++ /* All multicast mode */ ++ if (ndev->flags & IFF_ALLMULTI) { ++ regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, ~0UL); ++ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, ~0UL); ++ /* Specific multicast mode */ ++ } else if ((ndev->flags & IFF_MULTICAST) ++ && (netdev_mc_count(ndev) > 0)) { ++ regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK; ++ xemacps_set_hashtable(ndev); ++ /* Disable multicast mode */ ++ } else { ++ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, 0x0); ++ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, 0x0); ++ regval &= ~XEMACPS_NWCFG_MCASTHASHEN_MASK; ++ } ++ ++ /* broadcast mode */ ++ if (ndev->flags & IFF_BROADCAST) ++ regval &= ~XEMACPS_NWCFG_BCASTDI_MASK; ++ /* No broadcast */ ++ if (!(ndev->flags & IFF_BROADCAST)) ++ regval |= XEMACPS_NWCFG_BCASTDI_MASK; ++ ++ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); ++} ++ ++#define MIN_MTU 60 ++#define MAX_MTU 1500 ++/** ++ * xemacps_change_mtu - Change maximum transfer unit ++ * @ndev: network interface device structure ++ * @new_mtu: new vlaue for maximum frame size ++ * return: 0 on success, negative value if error. ++ **/ ++static int xemacps_change_mtu(struct net_device *ndev, int new_mtu) ++{ ++ if ((new_mtu < MIN_MTU) || ++ ((new_mtu + ndev->hard_header_len) > MAX_MTU)) ++ return -EINVAL; ++ ++ ndev->mtu = new_mtu; /* change mtu in net_device structure */ ++ return 0; ++} ++ ++/** ++ * xemacps_get_settings - get device specific settings. ++ * Usage: Issue "ethtool ethX" under linux prompt. ++ * @ndev: network device ++ * @ecmd: ethtool command structure ++ * return: 0 on success, negative value if error. ++ **/ ++static int ++xemacps_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct phy_device *phydev = lp->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_gset(phydev, ecmd); ++} ++ ++/** ++ * xemacps_set_settings - set device specific settings. ++ * Usage: Issue "ethtool -s ethX speed 1000" under linux prompt ++ * to change speed ++ * @ndev: network device ++ * @ecmd: ethtool command structure ++ * return: 0 on success, negative value if error. ++ **/ ++static int ++xemacps_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct phy_device *phydev = lp->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_sset(phydev, ecmd); ++} ++ ++/** ++ * xemacps_get_drvinfo - report driver information ++ * Usage: Issue "ethtool -i ethX" under linux prompt ++ * @ndev: network device ++ * @ed: device driver information structure ++ **/ ++static void ++xemacps_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ ++ memset(ed, 0, sizeof(struct ethtool_drvinfo)); ++ strcpy(ed->driver, lp->pdev->dev.driver->name); ++ strcpy(ed->version, DRIVER_VERSION); ++} ++ ++/** ++ * xemacps_get_ringparam - get device dma ring information. ++ * Usage: Issue "ethtool -g ethX" under linux prompt ++ * @ndev: network device ++ * @erp: ethtool ring parameter structure ++ **/ ++static void ++xemacps_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *erp) ++{ ++ memset(erp, 0, sizeof(struct ethtool_ringparam)); ++ ++ erp->rx_max_pending = XEMACPS_RECV_BD_CNT; ++ erp->tx_max_pending = XEMACPS_SEND_BD_CNT; ++ erp->rx_pending = 0; ++ erp->tx_pending = 0; ++} ++ ++/** ++ * xemacps_get_wol - get device wake on lan status ++ * Usage: Issue "ethtool ethX" under linux prompt ++ * @ndev: network device ++ * @ewol: wol status ++ **/ ++static void ++xemacps_get_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ ++ ewol->supported = WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST; ++ ++ regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET); ++ if (regval | XEMACPS_WOL_MCAST_MASK) ++ ewol->wolopts |= WAKE_MCAST; ++ if (regval | XEMACPS_WOL_ARP_MASK) ++ ewol->wolopts |= WAKE_ARP; ++ if (regval | XEMACPS_WOL_SPEREG1_MASK) ++ ewol->wolopts |= WAKE_UCAST; ++ if (regval | XEMACPS_WOL_MAGIC_MASK) ++ ewol->wolopts |= WAKE_MAGIC; ++ ++} ++ ++/** ++ * xemacps_set_wol - set device wake on lan configuration ++ * Usage: Issue "ethtool -s ethX wol u|m|b|g" under linux prompt to enable ++ * specified type of packet. ++ * Usage: Issue "ethtool -s ethX wol d" under linux prompt to disable ++ * this feature. ++ * @ndev: network device ++ * @ewol: wol status ++ * return 0 on success, negative value if not supported ++ **/ ++static int ++xemacps_set_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ ++ if (ewol->wolopts & ~(WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST)) ++ return -EOPNOTSUPP; ++ ++ regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET); ++ regval &= ~(XEMACPS_WOL_MCAST_MASK | XEMACPS_WOL_ARP_MASK | ++ XEMACPS_WOL_SPEREG1_MASK | XEMACPS_WOL_MAGIC_MASK); ++ ++ if (ewol->wolopts & WAKE_MAGIC) ++ regval |= XEMACPS_WOL_MAGIC_MASK; ++ if (ewol->wolopts & WAKE_ARP) ++ regval |= XEMACPS_WOL_ARP_MASK; ++ if (ewol->wolopts & WAKE_UCAST) ++ regval |= XEMACPS_WOL_SPEREG1_MASK; ++ if (ewol->wolopts & WAKE_MCAST) ++ regval |= XEMACPS_WOL_MCAST_MASK; ++ ++ xemacps_write(lp->baseaddr, XEMACPS_WOL_OFFSET, regval); ++ ++ return 0; ++} ++ ++/** ++ * xemacps_get_pauseparam - get device pause status ++ * Usage: Issue "ethtool -a ethX" under linux prompt ++ * @ndev: network device ++ * @epauseparam: pause parameter ++ * ++ * note: hardware supports only tx flow control ++ **/ ++static void ++xemacps_get_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *epauseparm) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ ++ epauseparm->autoneg = 0; ++ epauseparm->rx_pause = 0; ++ ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); ++ epauseparm->tx_pause = regval & XEMACPS_NWCFG_PAUSEEN_MASK; ++} ++ ++/** ++ * xemacps_set_pauseparam - set device pause parameter(flow control) ++ * Usage: Issue "ethtool -A ethX tx on|off" under linux prompt ++ * @ndev: network device ++ * @epauseparam: pause parameter ++ * return 0 on success, negative value if not supported ++ * ++ * note: hardware supports only tx flow control ++ **/ ++static int ++xemacps_set_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *epauseparm) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ u32 regval; ++ ++ if (netif_running(ndev)) { ++ dev_err(&lp->pdev->dev, ++ "Please stop netif before apply configruation\n"); ++ return -EFAULT; ++ } ++ ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); ++ ++ if (epauseparm->tx_pause) ++ regval |= XEMACPS_NWCFG_PAUSEEN_MASK; ++ if (!(epauseparm->tx_pause)) ++ regval &= ~XEMACPS_NWCFG_PAUSEEN_MASK; ++ ++ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); ++ return 0; ++} ++ ++/** ++ * xemacps_get_stats - get device statistic raw data in 64bit mode ++ * @ndev: network device ++ **/ ++static struct net_device_stats ++*xemacps_get_stats(struct net_device *ndev) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct net_device_stats *nstat = &lp->stats; ++ ++ xemacps_update_stats((unsigned long)lp); ++ return nstat; ++} ++ ++static struct ethtool_ops xemacps_ethtool_ops = { ++ .get_settings = xemacps_get_settings, ++ .set_settings = xemacps_set_settings, ++ .get_drvinfo = xemacps_get_drvinfo, ++ .get_link = ethtool_op_get_link, /* ethtool default */ ++ .get_ringparam = xemacps_get_ringparam, ++ .get_wol = xemacps_get_wol, ++ .set_wol = xemacps_set_wol, ++ .get_pauseparam = xemacps_get_pauseparam, ++ .set_pauseparam = xemacps_set_pauseparam, ++}; ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++static int xemacps_hwtstamp_ioctl(struct net_device *netdev, ++ struct ifreq *ifr, int cmd) ++{ ++ struct hwtstamp_config config; ++ struct net_local *lp; ++ u32 regval; ++ ++ lp = netdev_priv(netdev); ++ ++ if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) ++ return -EFAULT; ++ ++ /* reserved for future extensions */ ++ if (config.flags) ++ return -EINVAL; ++ ++ if ((config.tx_type != HWTSTAMP_TX_OFF) && ++ (config.tx_type != HWTSTAMP_TX_ON)) ++ return -ERANGE; ++ ++ switch (config.rx_filter) { ++ case HWTSTAMP_FILTER_NONE: ++ break; ++ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: ++ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: ++ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: ++ case HWTSTAMP_FILTER_ALL: ++ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: ++ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: ++ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: ++ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: ++ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: ++ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: ++ case HWTSTAMP_FILTER_PTP_V2_EVENT: ++ case HWTSTAMP_FILTER_PTP_V2_SYNC: ++ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: ++ config.rx_filter = HWTSTAMP_FILTER_ALL; ++ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, ++ (regval | XEMACPS_NWCTRL_RXTSTAMP_MASK)); ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ config.tx_type = HWTSTAMP_TX_ON; ++ lp->hwtstamp_config = config; ++ ++ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? ++ -EFAULT : 0; ++} ++#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ ++ ++/** ++ * xemacps_ioctl - ioctl entry point ++ * @ndev: network device ++ * @rq: interface request ioctl ++ * @cmd: command code ++ * ++ * Called when user issues an ioctl request to the network device. ++ **/ ++static int xemacps_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ++{ ++ struct net_local *lp = netdev_priv(ndev); ++ struct phy_device *phydev = lp->phy_dev; ++ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ switch (cmd) { ++ case SIOCGMIIPHY: ++ case SIOCGMIIREG: ++ case SIOCSMIIREG: ++ return phy_mii_ioctl(phydev, rq, cmd); ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ case SIOCSHWTSTAMP: ++ return xemacps_hwtstamp_ioctl(ndev, rq, cmd); ++#endif ++ default: ++ dev_info(&lp->pdev->dev, "ioctl %d not implemented.\n", cmd); ++ return -EOPNOTSUPP; ++ } ++ ++} ++ ++/** ++ * xemacps_probe - Platform driver probe ++ * @pdev: Pointer to platform device structure ++ * ++ * Return 0 on success, negative value if error ++ */ ++static int xemacps_probe(struct platform_device *pdev) ++{ ++ struct resource *r_mem = NULL; ++ struct resource *r_irq = NULL; ++ struct net_device *ndev; ++ struct net_local *lp; ++ u32 regval = 0; ++ int rc = -ENXIO; ++ ++ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!r_mem || !r_irq) { ++ dev_err(&pdev->dev, "no IO resource defined.\n"); ++ return -ENXIO; ++ } ++ ++ ndev = alloc_etherdev(sizeof(*lp)); ++ if (!ndev) { ++ dev_err(&pdev->dev, "etherdev allocation failed.\n"); ++ return -ENOMEM; ++ } ++ ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ lp = netdev_priv(ndev); ++ lp->pdev = pdev; ++ lp->ndev = ndev; ++ ++ spin_lock_init(&lp->tx_lock); ++ spin_lock_init(&lp->rx_lock); ++ spin_lock_init(&lp->nwctrlreg_lock); ++ ++ lp->baseaddr = devm_ioremap_resource(&pdev->dev, r_mem); ++ if (IS_ERR(lp->baseaddr)) { ++ dev_err(&pdev->dev, "failed to map baseaddress.\n"); ++ rc = PTR_ERR(lp->baseaddr); ++ goto err_out_free_netdev; ++ } ++ ++ dev_dbg(&lp->pdev->dev, "BASEADDRESS hw: %p virt: %p\n", ++ (void *)r_mem->start, lp->baseaddr); ++ ++ ndev->irq = platform_get_irq(pdev, 0); ++ ++ ndev->netdev_ops = &netdev_ops; ++ ndev->watchdog_timeo = TX_TIMEOUT; ++ ndev->ethtool_ops = &xemacps_ethtool_ops; ++ ndev->base_addr = r_mem->start; ++ ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG; ++ netif_napi_add(ndev, &lp->napi, xemacps_rx_poll, XEMACPS_NAPI_WEIGHT); ++ ++ lp->ip_summed = CHECKSUM_UNNECESSARY; ++ ++ rc = register_netdev(ndev); ++ if (rc) { ++ dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); ++ goto err_out_free_netdev; ++ } ++ ++ if (ndev->irq == 54) ++ lp->enetnum = 0; ++ else ++ lp->enetnum = 1; ++ ++ lp->aperclk = devm_clk_get(&pdev->dev, "aper_clk"); ++ if (IS_ERR(lp->aperclk)) { ++ dev_err(&pdev->dev, "aper_clk clock not found.\n"); ++ rc = PTR_ERR(lp->aperclk); ++ goto err_out_unregister_netdev; ++ } ++ lp->devclk = devm_clk_get(&pdev->dev, "ref_clk"); ++ if (IS_ERR(lp->devclk)) { ++ dev_err(&pdev->dev, "ref_clk clock not found.\n"); ++ rc = PTR_ERR(lp->devclk); ++ goto err_out_unregister_netdev; ++ } ++ ++ rc = clk_prepare_enable(lp->aperclk); ++ if (rc) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto err_out_unregister_netdev; ++ } ++ rc = clk_prepare_enable(lp->devclk); ++ if (rc) { ++ dev_err(&pdev->dev, "Unable to enable device clock.\n"); ++ goto err_out_clk_dis_aper; ++ } ++ ++ lp->clk_rate_change_nb.notifier_call = xemacps_clk_notifier_cb; ++ lp->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(lp->devclk, &lp->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, ++ "Unable to register clock notifier.\n"); ++ ++#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP ++ prop = of_get_property(lp->pdev->dev.of_node, ++ "xlnx,ptp-enet-clock", NULL); ++ if (prop) ++ lp->ptpenetclk = (u32)be32_to_cpup(prop); ++ else ++ lp->ptpenetclk = 133333328; ++#endif ++ ++ lp->phy_node = of_parse_phandle(lp->pdev->dev.of_node, ++ "phy-handle", 0); ++ lp->gmii2rgmii_phy_node = of_parse_phandle(lp->pdev->dev.of_node, ++ "gmii2rgmii-phy-handle", 0); ++ rc = of_get_phy_mode(lp->pdev->dev.of_node); ++ if (rc < 0) { ++ dev_err(&lp->pdev->dev, "error in getting phy i/f\n"); ++ goto err_out_unregister_clk_notifier; ++ } ++ ++ lp->phy_interface = rc; ++ ++ /* Set MDIO clock divider */ ++ regval = (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK); ++ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); ++ ++ ++ regval = XEMACPS_NWCTRL_MDEN_MASK; ++ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); ++ ++ rc = xemacps_mii_init(lp); ++ if (rc) { ++ dev_err(&lp->pdev->dev, "error in xemacps_mii_init\n"); ++ goto err_out_unregister_clk_notifier; ++ } ++ ++ xemacps_update_hwaddr(lp); ++ tasklet_init(&lp->tx_bdreclaim_tasklet, xemacps_tx_poll, ++ (unsigned long) ndev); ++ tasklet_disable(&lp->tx_bdreclaim_tasklet); ++ ++ lp->txtimeout_handler_wq = create_singlethread_workqueue(DRIVER_NAME); ++ INIT_WORK(&lp->txtimeout_reinit, xemacps_reinit_for_txtimeout); ++ ++ platform_set_drvdata(pdev, ndev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ ++ dev_info(&lp->pdev->dev, "pdev->id %d, baseaddr 0x%08lx, irq %d\n", ++ pdev->id, ndev->base_addr, ndev->irq); ++ ++ rc = devm_request_irq(&pdev->dev, ndev->irq, &xemacps_interrupt, 0, ++ ndev->name, ndev); ++ if (rc) { ++ dev_err(&lp->pdev->dev, "Unable to request IRQ %p, error %d\n", ++ r_irq, rc); ++ goto err_out_unregister_clk_notifier; ++ } ++ ++ return 0; ++ ++err_out_unregister_clk_notifier: ++ clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb); ++ clk_disable_unprepare(lp->devclk); ++err_out_clk_dis_aper: ++ clk_disable_unprepare(lp->aperclk); ++err_out_unregister_netdev: ++ unregister_netdev(ndev); ++err_out_free_netdev: ++ free_netdev(ndev); ++ platform_set_drvdata(pdev, NULL); ++ return rc; ++} ++ ++/** ++ * xemacps_remove - called when platform driver is unregistered ++ * @pdev: Pointer to the platform device structure ++ * ++ * return: 0 on success ++ */ ++static int xemacps_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct net_local *lp; ++ ++ if (ndev) { ++ lp = netdev_priv(ndev); ++ ++ mdiobus_unregister(lp->mii_bus); ++ kfree(lp->mii_bus->irq); ++ mdiobus_free(lp->mii_bus); ++ unregister_netdev(ndev); ++ ++ clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb); ++ if (!pm_runtime_suspended(&pdev->dev)) { ++ clk_disable_unprepare(lp->devclk); ++ clk_disable_unprepare(lp->aperclk); ++ } else { ++ clk_unprepare(lp->devclk); ++ clk_unprepare(lp->aperclk); ++ } ++ ++ free_netdev(ndev); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP ++/** ++ * xemacps_suspend - Suspend event ++ * @device: Pointer to device structure ++ * ++ * Return 0 ++ */ ++static int xemacps_suspend(struct device *device) ++{ ++ struct platform_device *pdev = container_of(device, ++ struct platform_device, dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct net_local *lp = netdev_priv(ndev); ++ ++ netif_device_detach(ndev); ++ if (!pm_runtime_suspended(device)) { ++ clk_disable(lp->devclk); ++ clk_disable(lp->aperclk); ++ } ++ return 0; ++} ++ ++/** ++ * xemacps_resume - Resume after previous suspend ++ * @pdev: Pointer to platform device structure ++ * ++ * Returns 0 on success, errno otherwise. ++ */ ++static int xemacps_resume(struct device *device) ++{ ++ struct platform_device *pdev = container_of(device, ++ struct platform_device, dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct net_local *lp = netdev_priv(ndev); ++ ++ if (!pm_runtime_suspended(device)) { ++ int ret; ++ ++ ret = clk_enable(lp->aperclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_enable(lp->devclk); ++ if (ret) { ++ clk_disable(lp->aperclk); ++ return ret; ++ } ++ } ++ netif_device_attach(ndev); ++ return 0; ++} ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++#ifdef CONFIG_PM_RUNTIME ++static int xemacps_runtime_idle(struct device *dev) ++{ ++ return pm_schedule_suspend(dev, 1); ++} ++ ++static int xemacps_runtime_resume(struct device *device) ++{ ++ int ret; ++ struct platform_device *pdev = container_of(device, ++ struct platform_device, dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct net_local *lp = netdev_priv(ndev); ++ ++ ret = clk_enable(lp->aperclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_enable(lp->devclk); ++ if (ret) { ++ clk_disable(lp->aperclk); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int xemacps_runtime_suspend(struct device *device) ++{ ++ struct platform_device *pdev = container_of(device, ++ struct platform_device, dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct net_local *lp = netdev_priv(ndev); ++ ++ clk_disable(lp->devclk); ++ clk_disable(lp->aperclk); ++ return 0; ++} ++#endif /* CONFIG_PM_RUNTIME */ ++ ++static const struct dev_pm_ops xemacps_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(xemacps_suspend, xemacps_resume) ++ SET_RUNTIME_PM_OPS(xemacps_runtime_suspend, xemacps_runtime_resume, ++ xemacps_runtime_idle) ++}; ++#define XEMACPS_PM (&xemacps_dev_pm_ops) ++#else /* ! CONFIG_PM */ ++#define XEMACPS_PM NULL ++#endif /* ! CONFIG_PM */ ++ ++static struct net_device_ops netdev_ops = { ++ .ndo_open = xemacps_open, ++ .ndo_stop = xemacps_close, ++ .ndo_start_xmit = xemacps_start_xmit, ++ .ndo_set_rx_mode = xemacps_set_rx_mode, ++ .ndo_set_mac_address = xemacps_set_mac_address, ++ .ndo_do_ioctl = xemacps_ioctl, ++ .ndo_change_mtu = xemacps_change_mtu, ++ .ndo_tx_timeout = xemacps_tx_timeout, ++ .ndo_get_stats = xemacps_get_stats, ++}; ++ ++static struct of_device_id xemacps_of_match[] = { ++ { .compatible = "xlnx,ps7-ethernet-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xemacps_of_match); ++ ++static struct platform_driver xemacps_driver = { ++ .probe = xemacps_probe, ++ .remove = xemacps_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = xemacps_of_match, ++ .pm = XEMACPS_PM, ++ }, ++}; ++ ++module_platform_driver(xemacps_driver); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx Ethernet driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/patches.zynq/0005-gpio-xilinx-merge-Xilinx-gpio-support-into-LTSI-3.10.patch b/patches.zynq/0005-gpio-xilinx-merge-Xilinx-gpio-support-into-LTSI-3.10.patch new file mode 100644 index 0000000..f7d7d0d --- /dev/null +++ b/patches.zynq/0005-gpio-xilinx-merge-Xilinx-gpio-support-into-LTSI-3.10.patch @@ -0,0 +1,1207 @@ +From 142b902012ca81b1dfb65700f203aab9be6929aa Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Fri, 20 Dec 2013 15:24:18 +0900 +Subject: gpio: xilinx: merge Xilinx gpio support into LTSI 3.10.y + +This commits merges support for the GPIO bus from the Xilinx +master branch (commit efc27505715e64526653f35274717c0fc56491e3 in +master branch). + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/gpio/Kconfig | 12 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-xilinx.c | 321 +++++++++++++++++-- + drivers/gpio/gpio-xilinxps.c | 722 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1026 insertions(+), 30 deletions(-) + create mode 100644 drivers/gpio/gpio-xilinxps.c + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -234,9 +234,17 @@ config GPIO_TS5500 + + config GPIO_XILINX + bool "Xilinx GPIO support" +- depends on PPC_OF || MICROBLAZE ++ depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ ++ select GENERIC_IRQ_CHIP + help +- Say yes here to support the Xilinx FPGA GPIO device ++ Say yes here to support the Xilinx AXI/XPS GPIO device ++ ++config GPIO_XILINX_PS ++ tristate "Xilinx GPIO PS" ++ depends on ARCH_ZYNQ ++ select GENERIC_IRQ_CHIP ++ help ++ Say yes here to support Xilinx GPIO PS controller + + config GPIO_VR41XX + tristate "NEC VR4100 series General-purpose I/O Uint support" +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -87,3 +87,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x + obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o + obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o + obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o ++obj-$(CONFIG_GPIO_XILINX_PS) += gpio-xilinxps.o +--- a/drivers/gpio/gpio-xilinx.c ++++ b/drivers/gpio/gpio-xilinx.c +@@ -17,15 +17,25 @@ + #include + #include + #include ++#include + #include + #include ++#include + #include ++#include ++#include ++#include + #include + #include + + /* Register Offset Definitions */ +-#define XGPIO_DATA_OFFSET (0x0) /* Data register */ +-#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ ++#define XGPIO_DATA_OFFSET 0x0 /* Data register */ ++#define XGPIO_TRI_OFFSET 0x4 /* I/O direction register */ ++#define XGPIO_GIER_OFFSET 0x11c /* Global Interrupt Enable */ ++#define XGPIO_GIER_IE BIT(31) ++ ++#define XGPIO_IPISR_OFFSET 0x120 /* IP Interrupt Status */ ++#define XGPIO_IPIER_OFFSET 0x128 /* IP Interrupt Enable */ + + #define XGPIO_CHANNEL_OFFSET 0x8 + +@@ -40,18 +50,24 @@ + + /** + * struct xgpio_instance - Stores information about GPIO device +- * struct of_mm_gpio_chip mmchip: OF GPIO chip for memory mapped banks +- * gpio_state: GPIO state shadow register +- * gpio_dir: GPIO direction shadow register +- * offset: GPIO channel offset +- * gpio_lock: Lock used for synchronization ++ * @mmchip: OF GPIO chip for memory mapped banks ++ * @gpio_state: GPIO state shadow register ++ * @gpio_dir: GPIO direction shadow register ++ * @offset: GPIO channel offset ++ * @irq_base: GPIO channel irq base address ++ * @irq_enable: GPIO irq enable/disable bitfield ++ * @gpio_lock: Lock used for synchronization ++ * @irq_domain: irq_domain of the controller + */ + struct xgpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; + u32 gpio_dir; + u32 offset; ++ int irq_base; ++ u32 irq_enable; + spinlock_t gpio_lock; ++ struct irq_domain *irq_domain; + }; + + /** +@@ -59,8 +75,11 @@ struct xgpio_instance { + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * +- * This function reads the specified signal of the GPIO device. It returns 0 if +- * the signal clear, 1 if signal is set or negative value on error. ++ * This function reads the specified signal of the GPIO device. ++ * ++ * Return: ++ * 0 if direction of GPIO signals is set as input otherwise it ++ * returns negative error value. + */ + static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) + { +@@ -110,8 +129,10 @@ static void xgpio_set(struct gpio_chip * + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. +- * It returns 0 if direction of GPIO signals is set as input otherwise it +- * returns negative error value. ++ * ++ * Return: ++ * 0 - if direction of GPIO signals is set as input ++ * otherwise it returns negative error value. + */ + static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) + { +@@ -138,8 +159,10 @@ static int xgpio_dir_in(struct gpio_chip + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * +- * This function sets the direction of specified GPIO signal as output. If all +- * GPIO signals of GPIO chip is configured as input then it returns ++ * This function sets the direction of specified GPIO signal as output. ++ * ++ * Return: ++ * If all GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ + static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +@@ -171,7 +194,7 @@ static int xgpio_dir_out(struct gpio_chi + + /** + * xgpio_save_regs - Set initial values of GPIO pins +- * @mm_gc: pointer to memory mapped GPIO chip structure ++ * @mm_gc: Pointer to memory mapped GPIO chip structure + */ + static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) + { +@@ -185,20 +208,245 @@ static void xgpio_save_regs(struct of_mm + } + + /** ++ * xgpio_xlate - Set initial values of GPIO pins ++ * @gc: Pointer to gpio_chip device structure. ++ * @gpiospec: gpio specifier as found in the device tree ++ * @flags: A flags pointer based on binding ++ * ++ * Return: ++ * irq number otherwise -EINVAL ++ */ ++static int xgpio_xlate(struct gpio_chip *gc, ++ const struct of_phandle_args *gpiospec, u32 *flags) ++{ ++ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); ++ struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, ++ mmchip); ++ ++ if (gpiospec->args[1] == chip->offset) ++ return gpiospec->args[0]; ++ ++ return -EINVAL; ++} ++ ++/** ++ * xgpio_irq_mask - Write the specified signal of the GPIO device. ++ * @irq_data: per irq and chip data passed down to chip functions ++ */ ++static void xgpio_irq_mask(struct irq_data *irq_data) ++{ ++ unsigned long flags; ++ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); ++ struct of_mm_gpio_chip *mm_gc = &chip->mmchip; ++ u32 offset = irq_data->irq - chip->irq_base; ++ u32 temp; ++ ++ pr_debug("%s: Disable %d irq, irq_enable_mask 0x%x\n", ++ __func__, offset, chip->irq_enable); ++ ++ spin_lock_irqsave(&chip->gpio_lock, flags); ++ ++ chip->irq_enable &= ~BIT(offset); ++ ++ if (!chip->irq_enable) { ++ /* Enable per channel interrupt */ ++ temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET); ++ temp &= chip->offset / XGPIO_CHANNEL_OFFSET + 1; ++ xgpio_writereg(mm_gc->regs + XGPIO_IPIER_OFFSET, temp); ++ ++ /* Disable global interrupt if channel interrupts are unused */ ++ temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET); ++ if (!temp) ++ xgpio_writereg(mm_gc->regs + XGPIO_GIER_OFFSET, ++ ~XGPIO_GIER_IE); ++ ++ } ++ spin_unlock_irqrestore(&chip->gpio_lock, flags); ++} ++ ++/** ++ * xgpio_irq_unmask - Write the specified signal of the GPIO device. ++ * @irq_data: per irq and chip data passed down to chip functions ++ */ ++static void xgpio_irq_unmask(struct irq_data *irq_data) ++{ ++ unsigned long flags; ++ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); ++ struct of_mm_gpio_chip *mm_gc = &chip->mmchip; ++ u32 offset = irq_data->irq - chip->irq_base; ++ u32 temp; ++ ++ pr_debug("%s: Enable %d irq, irq_enable_mask 0x%x\n", ++ __func__, offset, chip->irq_enable); ++ ++ /* Setup pin as input */ ++ xgpio_dir_in(&mm_gc->gc, offset); ++ ++ spin_lock_irqsave(&chip->gpio_lock, flags); ++ ++ chip->irq_enable |= BIT(offset); ++ ++ if (chip->irq_enable) { ++ ++ /* Enable per channel interrupt */ ++ temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET); ++ temp |= chip->offset / XGPIO_CHANNEL_OFFSET + 1; ++ xgpio_writereg(mm_gc->regs + XGPIO_IPIER_OFFSET, temp); ++ ++ /* Enable global interrupts */ ++ xgpio_writereg(mm_gc->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE); ++ } ++ ++ spin_unlock_irqrestore(&chip->gpio_lock, flags); ++} ++ ++/** ++ * xgpio_set_irq_type - Write the specified signal of the GPIO device. ++ * @irq_data: Per irq and chip data passed down to chip functions ++ * @type: Interrupt type that is to be set for the gpio pin ++ * ++ * Return: ++ * 0 if interrupt type is supported otherwise otherwise -EINVAL ++ */ ++static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) ++{ ++ /* Only rising edge case is supported now */ ++ if (type == IRQ_TYPE_EDGE_RISING) ++ return 0; ++ ++ return -EINVAL; ++} ++ ++/* irq chip descriptor */ ++static struct irq_chip xgpio_irqchip = { ++ .name = "xgpio", ++ .irq_mask = xgpio_irq_mask, ++ .irq_unmask = xgpio_irq_unmask, ++ .irq_set_type = xgpio_set_irq_type, ++}; ++ ++/** ++ * xgpio_to_irq - Find out gpio to Linux irq mapping ++ * @gc: Pointer to gpio_chip device structure. ++ * @offset: Gpio pin offset ++ * ++ * Return: ++ * irq number otherwise -EINVAL ++ */ ++static int xgpio_to_irq(struct gpio_chip *gc, unsigned offset) ++{ ++ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); ++ struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, ++ mmchip); ++ ++ return irq_find_mapping(chip->irq_domain, offset); ++} ++ ++/** ++ * xgpio_irqhandler - Gpio interrupt service routine ++ * @irq: gpio irq number ++ * @desc: Pointer to interrupt description ++ */ ++static void xgpio_irqhandler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct xgpio_instance *chip = (struct xgpio_instance *) ++ irq_get_handler_data(irq); ++ struct of_mm_gpio_chip *mm_gc = &chip->mmchip; ++ struct irq_chip *irqchip = irq_desc_get_chip(desc); ++ int offset; ++ unsigned long val; ++ ++ chained_irq_enter(irqchip, desc); ++ ++ val = xgpio_readreg(mm_gc->regs + chip->offset); ++ /* Only rising edge is supported */ ++ val &= chip->irq_enable; ++ ++ for_each_set_bit(offset, &val, chip->mmchip.gc.ngpio) { ++ generic_handle_irq(chip->irq_base + offset); ++ } ++ ++ xgpio_writereg(mm_gc->regs + XGPIO_IPISR_OFFSET, ++ chip->offset / XGPIO_CHANNEL_OFFSET + 1); ++ ++ chained_irq_exit(irqchip, desc); ++} ++ ++static struct lock_class_key gpio_lock_class; ++ ++/** ++ * xgpio_irq_setup - Allocate irq for gpio and setup appropriate functions ++ * @np: Device node of the GPIO chip ++ * @chip: Pointer to private gpio channel structure ++ * ++ * Return: ++ * 0 if success, otherwise -1 ++ */ ++static int xgpio_irq_setup(struct device_node *np, struct xgpio_instance *chip) ++{ ++ u32 pin_num; ++ struct resource res; ++ ++ int ret = of_irq_to_resource(np, 0, &res); ++ if (!ret) { ++ pr_info("GPIO IRQ not connected\n"); ++ return 0; ++ } ++ ++ chip->mmchip.gc.of_xlate = xgpio_xlate; ++ chip->mmchip.gc.of_gpio_n_cells = 2; ++ chip->mmchip.gc.to_irq = xgpio_to_irq; ++ ++ chip->irq_base = irq_alloc_descs(-1, 0, chip->mmchip.gc.ngpio, 0); ++ if (chip->irq_base < 0) { ++ pr_err("Couldn't allocate IRQ numbers\n"); ++ return -1; ++ } ++ chip->irq_domain = irq_domain_add_legacy(np, chip->mmchip.gc.ngpio, ++ chip->irq_base, 0, ++ &irq_domain_simple_ops, NULL); ++ ++ /* ++ * set the irq chip, handler and irq chip data for callbacks for ++ * each pin ++ */ ++ for (pin_num = 0; pin_num < chip->mmchip.gc.ngpio; pin_num++) { ++ u32 gpio_irq = irq_find_mapping(chip->irq_domain, pin_num); ++ irq_set_lockdep_class(gpio_irq, &gpio_lock_class); ++ pr_debug("IRQ Base: %d, Pin %d = IRQ %d\n", ++ chip->irq_base, pin_num, gpio_irq); ++ irq_set_chip_and_handler(gpio_irq, &xgpio_irqchip, ++ handle_simple_irq); ++ irq_set_chip_data(gpio_irq, (void *)chip); ++#ifdef CONFIG_ARCH_ZYNQ ++ set_irq_flags(gpio_irq, IRQF_VALID); ++#endif ++ } ++ irq_set_handler_data(res.start, (void *)chip); ++ irq_set_chained_handler(res.start, xgpio_irqhandler); ++ ++ return 0; ++} ++ ++/** + * xgpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the +- * driver data structure. It returns 0, if the driver is bound to the GPIO +- * device, or a negative value if there is an error. ++ * driver data structure. ++ * ++ * Return: ++ * It returns 0, if the driver is bound to the GPIO device, or ++ * a negative value if there is an error. + */ +-static int xgpio_of_probe(struct device_node *np) ++static int xgpio_of_probe(struct platform_device *pdev) + { ++ struct device_node *np = pdev->dev.of_node; + struct xgpio_instance *chip; + int status = 0; + const u32 *tree_info; + +- chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + +@@ -230,18 +478,24 @@ static int xgpio_of_probe(struct device_ + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { +- kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + ++ status = xgpio_irq_setup(np, chip); ++ if (status) { ++ pr_err("%s: GPIO IRQ initialization failed %d\n", ++ np->full_name, status); ++ return status; ++ } ++ + pr_info("XGpio: %s: registered, base is %d\n", np->full_name, + chip->mmchip.gc.base); + + tree_info = of_get_property(np, "xlnx,is-dual", NULL); + if (tree_info && be32_to_cpup(tree_info)) { +- chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + +@@ -274,12 +528,18 @@ static int xgpio_of_probe(struct device_ + + chip->mmchip.save_regs = xgpio_save_regs; + ++ status = xgpio_irq_setup(np, chip); ++ if (status) { ++ pr_err("%s: GPIO IRQ initialization failed %d\n", ++ np->full_name, status); ++ return status; ++ } ++ + /* Call the OF gpio helper to setup and register the GPIO dev */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { +- kfree(chip); + pr_err("%s: error in probe function with status %d\n", +- np->full_name, status); ++ np->full_name, status); + return status; + } + pr_info("XGpio: %s: dual channel registered, base is %d\n", +@@ -293,15 +553,20 @@ static struct of_device_id xgpio_of_matc + { .compatible = "xlnx,xps-gpio-1.00.a", }, + { /* end of list */ }, + }; ++MODULE_DEVICE_TABLE(of, xgpio_of_match); ++ ++static struct platform_driver xilinx_gpio_driver = { ++ .probe = xgpio_of_probe, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "xilinx-gpio", ++ .of_match_table = xgpio_of_match, ++ }, ++}; + + static int __init xgpio_init(void) + { +- struct device_node *np; +- +- for_each_matching_node(np, xgpio_of_match) +- xgpio_of_probe(np); +- +- return 0; ++ return platform_driver_register(&xilinx_gpio_driver); + } + + /* Make sure we get initialized before anyone else tries to use us */ +--- /dev/null ++++ b/drivers/gpio/gpio-xilinxps.c +@@ -0,0 +1,722 @@ ++/* ++ * Xilinx PS GPIO device driver ++ * ++ * 2009-2011 (c) Xilinx, Inc. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 675 Mass ++ * Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "xgpiops" ++#define XGPIOPS_NR_GPIOS 118 ++ ++static struct irq_domain *irq_domain; ++ ++/* Register offsets for the GPIO device */ ++ ++#define XGPIOPS_DATA_LSW_OFFSET(BANK) (0x000 + (8 * BANK)) /* LSW Mask & ++ Data -WO */ ++#define XGPIOPS_DATA_MSW_OFFSET(BANK) (0x004 + (8 * BANK)) /* MSW Mask & ++ Data -WO */ ++#define XGPIOPS_DATA_OFFSET(BANK) (0x040 + (4 * BANK)) /* Data Register ++ -RW */ ++#define XGPIOPS_DIRM_OFFSET(BANK) (0x204 + (0x40 * BANK)) /* Direction ++ mode reg-RW */ ++#define XGPIOPS_OUTEN_OFFSET(BANK) (0x208 + (0x40 * BANK)) /* Output ++ enable reg-RW ++ */ ++#define XGPIOPS_INTMASK_OFFSET(BANK) (0x20C + (0x40 * BANK)) /* Interrupt ++ mask reg-RO */ ++#define XGPIOPS_INTEN_OFFSET(BANK) (0x210 + (0x40 * BANK)) /* Interrupt ++ enable reg-WO ++ */ ++#define XGPIOPS_INTDIS_OFFSET(BANK) (0x214 + (0x40 * BANK)) /* Interrupt ++ disable reg-WO ++ */ ++#define XGPIOPS_INTSTS_OFFSET(BANK) (0x218 + (0x40 * BANK)) /* Interrupt ++ status reg-RO ++ */ ++#define XGPIOPS_INTTYPE_OFFSET(BANK) (0x21C + (0x40 * BANK)) /* Interrupt ++ type reg-RW ++ */ ++#define XGPIOPS_INTPOL_OFFSET(BANK) (0x220 + (0x40 * BANK)) /* Interrupt ++ polarity reg ++ -RW */ ++#define XGPIOPS_INTANY_OFFSET(BANK) (0x224 + (0x40 * BANK)) /* Interrupt on ++ any, reg-RW */ ++ ++/* Read/Write access to the GPIO PS registers */ ++#define xgpiops_readreg(offset) __raw_readl(offset) ++#define xgpiops_writereg(val, offset) __raw_writel(val, offset) ++ ++static unsigned int xgpiops_pin_table[] = { ++ 31, /* 0 - 31 */ ++ 53, /* 32 - 53 */ ++ 85, /* 54 - 85 */ ++ 117 /* 86 - 117 */ ++}; ++ ++/** ++ * struct xgpiops - gpio device private data structure ++ * @chip: instance of the gpio_chip ++ * @base_addr: base address of the GPIO device ++ * @gpio_lock: lock used for synchronization ++ */ ++struct xgpiops { ++ struct gpio_chip chip; ++ void __iomem *base_addr; ++ unsigned int irq; ++ unsigned int irq_base; ++ struct clk *clk; ++ spinlock_t gpio_lock; ++}; ++ ++/** ++ * xgpiops_get_bank_pin - Get the bank number and pin number within that bank ++ * for a given pin in the GPIO device ++ * @pin_num: gpio pin number within the device ++ * @bank_num: an output parameter used to return the bank number of the gpio ++ * pin ++ * @bank_pin_num: an output parameter used to return pin number within a bank ++ * for the given gpio pin ++ * ++ * Returns the bank number. ++ */ ++static inline void xgpiops_get_bank_pin(unsigned int pin_num, ++ unsigned int *bank_num, ++ unsigned int *bank_pin_num) ++{ ++ for (*bank_num = 0; *bank_num < 4; (*bank_num)++) ++ if (pin_num <= xgpiops_pin_table[*bank_num]) ++ break; ++ ++ if (*bank_num == 0) ++ *bank_pin_num = pin_num; ++ else ++ *bank_pin_num = pin_num % ++ (xgpiops_pin_table[*bank_num - 1] + 1); ++} ++ ++/** ++ * xgpiops_get_value - Get the state of the specified pin of GPIO device ++ * @chip: gpio_chip instance to be worked on ++ * @pin: gpio pin number within the device ++ * ++ * This function reads the state of the specified pin of the GPIO device. ++ * It returns 0 if the pin is low, 1 if pin is high. ++ */ ++static int xgpiops_get_value(struct gpio_chip *chip, unsigned int pin) ++{ ++ unsigned int bank_num, bank_pin_num; ++ struct xgpiops *gpio = container_of(chip, struct xgpiops, chip); ++ ++ xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num); ++ ++ return (xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_DATA_OFFSET(bank_num)) >> ++ bank_pin_num) & 1; ++} ++ ++/** ++ * xgpiops_set_value - Modify the state of the pin with specified value ++ * @chip: gpio_chip instance to be worked on ++ * @pin: gpio pin number within the device ++ * @state: value used to modify the state of the specified pin ++ * ++ * This function calculates the register offset (i.e to lower 16 bits or ++ * upper 16 bits) based on the given pin number and sets the state of a ++ * gpio pin to the specified value. The state is either 0 or non-zero. ++ */ ++static void xgpiops_set_value(struct gpio_chip *chip, unsigned int pin, ++ int state) ++{ ++ unsigned long flags; ++ unsigned int reg_offset; ++ unsigned int bank_num, bank_pin_num; ++ struct xgpiops *gpio = container_of(chip, struct xgpiops, chip); ++ ++ xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num); ++ ++ if (bank_pin_num >= 16) { ++ bank_pin_num -= 16; /* only 16 data bits in bit maskable reg */ ++ reg_offset = XGPIOPS_DATA_MSW_OFFSET(bank_num); ++ } else { ++ reg_offset = XGPIOPS_DATA_LSW_OFFSET(bank_num); ++ } ++ ++ /* ++ * get the 32 bit value to be written to the mask/data register where ++ * the upper 16 bits is the mask and lower 16 bits is the data ++ */ ++ if (state) ++ state = 1; ++ state = ~(1 << (bank_pin_num + 16)) & ((state << bank_pin_num) | ++ 0xFFFF0000); ++ ++ spin_lock_irqsave(&gpio->gpio_lock, flags); ++ xgpiops_writereg(state, gpio->base_addr + reg_offset); ++ spin_unlock_irqrestore(&gpio->gpio_lock, flags); ++} ++ ++/** ++ * xgpiops_dir_in - Set the direction of the specified GPIO pin as input ++ * @chip: gpio_chip instance to be worked on ++ * @pin: gpio pin number within the device ++ * ++ * This function uses the read-modify-write sequence to set the direction of ++ * the gpio pin as input. Returns 0 always. ++ */ ++static int xgpiops_dir_in(struct gpio_chip *chip, unsigned int pin) ++{ ++ unsigned int reg, bank_num, bank_pin_num; ++ struct xgpiops *gpio = container_of(chip, struct xgpiops, chip); ++ ++ xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num); ++ /* clear the bit in direction mode reg to set the pin as input */ ++ reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num)); ++ reg &= ~(1 << bank_pin_num); ++ xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num)); ++ ++ return 0; ++} ++ ++/** ++ * xgpiops_dir_out - Set the direction of the specified GPIO pin as output ++ * @chip: gpio_chip instance to be worked on ++ * @pin: gpio pin number within the device ++ * @state: value to be written to specified pin ++ * ++ * This function sets the direction of specified GPIO pin as output, configures ++ * the Output Enable register for the pin and uses xgpiops_set to set the state ++ * of the pin to the value specified. Returns 0 always. ++ */ ++static int xgpiops_dir_out(struct gpio_chip *chip, unsigned int pin, int state) ++{ ++ struct xgpiops *gpio = container_of(chip, struct xgpiops, chip); ++ unsigned int reg, bank_num, bank_pin_num; ++ ++ xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num); ++ ++ /* set the GPIO pin as output */ ++ reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num)); ++ reg |= 1 << bank_pin_num; ++ xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num)); ++ ++ /* configure the output enable reg for the pin */ ++ reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num)); ++ reg |= 1 << bank_pin_num; ++ xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num)); ++ ++ /* set the state of the pin */ ++ xgpiops_set_value(chip, pin, state); ++ return 0; ++} ++ ++static int xgpiops_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ return irq_find_mapping(irq_domain, offset); ++} ++ ++/** ++ * xgpiops_irq_ack - Acknowledge the interrupt of a gpio pin ++ * @irq_data: irq data containing irq number of gpio pin for the irq to ack ++ * ++ * This function calculates gpio pin number from irq number and sets the bit ++ * in the Interrupt Status Register of the corresponding bank, to ACK the irq. ++ */ ++static void xgpiops_irq_ack(struct irq_data *irq_data) ++{ ++ struct xgpiops *gpio = (struct xgpiops *) ++ irq_data_get_irq_chip_data(irq_data); ++ unsigned int device_pin_num, bank_num, bank_pin_num; ++ ++ device_pin_num = irq_data->hwirq; ++ xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); ++ xgpiops_writereg(1 << bank_pin_num, gpio->base_addr + ++ (XGPIOPS_INTSTS_OFFSET(bank_num))); ++} ++ ++/** ++ * xgpiops_irq_mask - Disable the interrupts for a gpio pin ++ * @irq: irq number of gpio pin for which interrupt is to be disabled ++ * ++ * This function calculates gpio pin number from irq number and sets the ++ * bit in the Interrupt Disable register of the corresponding bank to disable ++ * interrupts for that pin. ++ */ ++static void xgpiops_irq_mask(struct irq_data *irq_data) ++{ ++ struct xgpiops *gpio = (struct xgpiops *) ++ irq_data_get_irq_chip_data(irq_data); ++ unsigned int device_pin_num, bank_num, bank_pin_num; ++ ++ device_pin_num = irq_data->hwirq; ++ xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); ++ xgpiops_writereg(1 << bank_pin_num, ++ gpio->base_addr + XGPIOPS_INTDIS_OFFSET(bank_num)); ++} ++ ++/** ++ * xgpiops_irq_unmask - Enable the interrupts for a gpio pin ++ * @irq_data: irq data containing irq number of gpio pin for the irq to enable ++ * ++ * This function calculates the gpio pin number from irq number and sets the ++ * bit in the Interrupt Enable register of the corresponding bank to enable ++ * interrupts for that pin. ++ */ ++static void xgpiops_irq_unmask(struct irq_data *irq_data) ++{ ++ struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data); ++ unsigned int device_pin_num, bank_num, bank_pin_num; ++ ++ device_pin_num = irq_data->hwirq; ++ xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); ++ xgpiops_writereg(1 << bank_pin_num, ++ gpio->base_addr + XGPIOPS_INTEN_OFFSET(bank_num)); ++} ++ ++/** ++ * xgpiops_set_irq_type - Set the irq type for a gpio pin ++ * @irq_data: irq data containing irq number of gpio pin ++ * @type: interrupt type that is to be set for the gpio pin ++ * ++ * This function gets the gpio pin number and its bank from the gpio pin number ++ * and configures the INT_TYPE, INT_POLARITY and INT_ANY registers. Returns 0, ++ * negative error otherwise. ++ * TYPE-EDGE_RISING, INT_TYPE - 1, INT_POLARITY - 1, INT_ANY - 0; ++ * TYPE-EDGE_FALLING, INT_TYPE - 1, INT_POLARITY - 0, INT_ANY - 0; ++ * TYPE-EDGE_BOTH, INT_TYPE - 1, INT_POLARITY - NA, INT_ANY - 1; ++ * TYPE-LEVEL_HIGH, INT_TYPE - 0, INT_POLARITY - 1, INT_ANY - NA; ++ * TYPE-LEVEL_LOW, INT_TYPE - 0, INT_POLARITY - 0, INT_ANY - NA ++ */ ++static int xgpiops_set_irq_type(struct irq_data *irq_data, unsigned int type) ++{ ++ struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data); ++ unsigned int device_pin_num, bank_num, bank_pin_num; ++ unsigned int int_type, int_pol, int_any; ++ ++ device_pin_num = irq_data->hwirq; ++ xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); ++ ++ int_type = xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_INTTYPE_OFFSET(bank_num)); ++ int_pol = xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_INTPOL_OFFSET(bank_num)); ++ int_any = xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_INTANY_OFFSET(bank_num)); ++ ++ /* ++ * based on the type requested, configure the INT_TYPE, INT_POLARITY ++ * and INT_ANY registers ++ */ ++ switch (type) { ++ case IRQ_TYPE_EDGE_RISING: ++ int_type |= (1 << bank_pin_num); ++ int_pol |= (1 << bank_pin_num); ++ int_any &= ~(1 << bank_pin_num); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ int_type |= (1 << bank_pin_num); ++ int_pol &= ~(1 << bank_pin_num); ++ int_any &= ~(1 << bank_pin_num); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ int_type |= (1 << bank_pin_num); ++ int_any |= (1 << bank_pin_num); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ int_type &= ~(1 << bank_pin_num); ++ int_pol |= (1 << bank_pin_num); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ int_type &= ~(1 << bank_pin_num); ++ int_pol &= ~(1 << bank_pin_num); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ xgpiops_writereg(int_type, ++ gpio->base_addr + XGPIOPS_INTTYPE_OFFSET(bank_num)); ++ xgpiops_writereg(int_pol, ++ gpio->base_addr + XGPIOPS_INTPOL_OFFSET(bank_num)); ++ xgpiops_writereg(int_any, ++ gpio->base_addr + XGPIOPS_INTANY_OFFSET(bank_num)); ++ return 0; ++} ++ ++static int xgpiops_set_wake(struct irq_data *data, unsigned int on) ++{ ++ if (on) ++ xgpiops_irq_unmask(data); ++ else ++ xgpiops_irq_mask(data); ++ ++ return 0; ++} ++ ++/* irq chip descriptor */ ++static struct irq_chip xgpiops_irqchip = { ++ .name = DRIVER_NAME, ++ .irq_ack = xgpiops_irq_ack, ++ .irq_mask = xgpiops_irq_mask, ++ .irq_unmask = xgpiops_irq_unmask, ++ .irq_set_type = xgpiops_set_irq_type, ++ .irq_set_wake = xgpiops_set_wake, ++}; ++ ++/** ++ * xgpiops_irqhandler - IRQ handler for the gpio banks of a gpio device ++ * @irq: irq number of the gpio bank where interrupt has occurred ++ * @desc: irq descriptor instance of the 'irq' ++ * ++ * This function reads the Interrupt Status Register of each bank to get the ++ * gpio pin number which has triggered an interrupt. It then acks the triggered ++ * interrupt and calls the pin specific handler set by the higher layer ++ * application for that pin. ++ * Note: A bug is reported if no handler is set for the gpio pin. ++ */ ++static void xgpiops_irqhandler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct xgpiops *gpio = (struct xgpiops *)irq_get_handler_data(irq); ++ int gpio_irq = gpio->irq_base; ++ unsigned int int_sts, int_enb, bank_num; ++ struct irq_desc *gpio_irq_desc; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ ++ chained_irq_enter(chip, desc); ++ ++ for (bank_num = 0; bank_num < 4; bank_num++) { ++ int_sts = xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_INTSTS_OFFSET(bank_num)); ++ int_enb = xgpiops_readreg(gpio->base_addr + ++ XGPIOPS_INTMASK_OFFSET(bank_num)); ++ int_sts &= ~int_enb; ++ ++ for (; int_sts != 0; int_sts >>= 1, gpio_irq++) { ++ if ((int_sts & 1) == 0) ++ continue; ++ gpio_irq_desc = irq_to_desc(gpio_irq); ++ BUG_ON(!gpio_irq_desc); ++ chip = irq_desc_get_chip(gpio_irq_desc); ++ BUG_ON(!chip); ++ chip->irq_ack(&gpio_irq_desc->irq_data); ++ ++ /* call the pin specific handler */ ++ generic_handle_irq(gpio_irq); ++ } ++ /* shift to first virtual irq of next bank */ ++ gpio_irq = gpio->irq_base + xgpiops_pin_table[bank_num] + 1; ++ } ++ ++ chip = irq_desc_get_chip(desc); ++ chained_irq_exit(chip, desc); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int xgpiops_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ if (!device_may_wakeup(dev)) { ++ if (!pm_runtime_suspended(dev)) ++ clk_disable(gpio->clk); ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static int xgpiops_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ if (!device_may_wakeup(dev)) { ++ if (!pm_runtime_suspended(dev)) ++ return clk_enable(gpio->clk); ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_RUNTIME ++static int xgpiops_runtime_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ clk_disable(gpio->clk); ++ ++ return 0; ++} ++ ++static int xgpiops_runtime_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ return clk_enable(gpio->clk); ++} ++ ++static int xgpiops_idle(struct device *dev) ++{ ++ return pm_schedule_suspend(dev, 1); ++} ++ ++static int xgpiops_request(struct gpio_chip *chip, unsigned offset) ++{ ++ int ret; ++ ++ ret = pm_runtime_get_sync(chip->dev); ++ ++ /* ++ * If the device is already active pm_runtime_get() will return 1 on ++ * success, but gpio_request still needs to return 0. ++ */ ++ return ret < 0 ? ret : 0; ++} ++ ++static void xgpiops_free(struct gpio_chip *chip, unsigned offset) ++{ ++ pm_runtime_put_sync(chip->dev); ++} ++ ++static void xgpiops_pm_runtime_init(struct platform_device *pdev) ++{ ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ clk_disable(gpio->clk); ++ pm_runtime_enable(&pdev->dev); ++} ++ ++#else /* ! CONFIG_PM_RUNTIME */ ++#define xgpiops_request NULL ++#define xgpiops_free NULL ++static void xgpiops_pm_runtime_init(struct platform_device *pdev) {} ++#endif /* ! CONFIG_PM_RUNTIME */ ++ ++#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP) ++static const struct dev_pm_ops xgpiops_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(xgpiops_suspend, xgpiops_resume) ++ SET_RUNTIME_PM_OPS(xgpiops_runtime_suspend, xgpiops_runtime_resume, ++ xgpiops_idle) ++}; ++#define XGPIOPS_PM (&xgpiops_dev_pm_ops) ++ ++#else /*! CONFIG_PM_RUNTIME || ! CONFIG_PM_SLEEP */ ++#define XGPIOPS_PM NULL ++#endif /*! CONFIG_PM_RUNTIME */ ++ ++/** ++ * xgpiops_probe - Initialization method for a xgpiops device ++ * @pdev: platform device instance ++ * ++ * This function allocates memory resources for the gpio device and registers ++ * all the banks of the device. It will also set up interrupts for the gpio ++ * pins. ++ * Note: Interrupts are disabled for all the banks during initialization. ++ * Returns 0 on success, negative error otherwise. ++ */ ++static int xgpiops_probe(struct platform_device *pdev) ++{ ++ int ret; ++ unsigned int irq_num; ++ struct xgpiops *gpio; ++ struct gpio_chip *chip; ++ resource_size_t remap_size; ++ struct resource *mem_res = NULL; ++ int pin_num, bank_num, gpio_irq; ++ ++ gpio = kzalloc(sizeof(struct xgpiops), GFP_KERNEL); ++ if (!gpio) { ++ dev_err(&pdev->dev, ++ "couldn't allocate memory for gpio private data\n"); ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&gpio->gpio_lock); ++ ++ platform_set_drvdata(pdev, gpio); ++ ++ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!mem_res) { ++ dev_err(&pdev->dev, "No memory resource\n"); ++ ret = -ENODEV; ++ goto err_free_gpio; ++ } ++ ++ remap_size = mem_res->end - mem_res->start + 1; ++ if (!request_mem_region(mem_res->start, remap_size, pdev->name)) { ++ dev_err(&pdev->dev, "Cannot request IO\n"); ++ ret = -ENXIO; ++ goto err_free_gpio; ++ } ++ ++ gpio->base_addr = ioremap(mem_res->start, remap_size); ++ if (gpio->base_addr == NULL) { ++ dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n", ++ (unsigned long)mem_res->start); ++ ret = -ENOMEM; ++ goto err_release_region; ++ } ++ ++ irq_num = platform_get_irq(pdev, 0); ++ gpio->irq = irq_num; ++ ++ /* configure the gpio chip */ ++ chip = &gpio->chip; ++ chip->label = "xgpiops"; ++ chip->owner = THIS_MODULE; ++ chip->dev = &pdev->dev; ++ chip->get = xgpiops_get_value; ++ chip->set = xgpiops_set_value; ++ chip->request = xgpiops_request; ++ chip->free = xgpiops_free; ++ chip->direction_input = xgpiops_dir_in; ++ chip->direction_output = xgpiops_dir_out; ++ chip->to_irq = xgpiops_to_irq; ++ chip->dbg_show = NULL; ++ chip->base = 0; /* default pin base */ ++ chip->ngpio = XGPIOPS_NR_GPIOS; ++ chip->can_sleep = 0; ++ ++ gpio->irq_base = irq_alloc_descs(-1, 0, chip->ngpio, 0); ++ if (gpio->irq_base < 0) { ++ dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); ++ ret = -ENODEV; ++ goto err_iounmap; ++ } ++ ++ irq_domain = irq_domain_add_legacy(pdev->dev.of_node, ++ chip->ngpio, gpio->irq_base, 0, ++ &irq_domain_simple_ops, NULL); ++ ++ /* report a bug if gpio chip registration fails */ ++ ret = gpiochip_add(chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "gpio chip registration failed\n"); ++ goto err_iounmap; ++ } else { ++ dev_info(&pdev->dev, "gpio at 0x%08lx mapped to 0x%08lx\n", ++ (unsigned long)mem_res->start, ++ (unsigned long)gpio->base_addr); ++ } ++ ++ /* Enable GPIO clock */ ++ gpio->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(gpio->clk)) { ++ dev_err(&pdev->dev, "input clock not found.\n"); ++ ret = PTR_ERR(gpio->clk); ++ goto err_chip_remove; ++ } ++ ret = clk_prepare_enable(gpio->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable clock.\n"); ++ goto err_clk_put; ++ } ++ ++ /* disable interrupts for all banks */ ++ for (bank_num = 0; bank_num < 4; bank_num++) { ++ xgpiops_writereg(0xffffffff, gpio->base_addr + ++ XGPIOPS_INTDIS_OFFSET(bank_num)); ++ } ++ ++ /* ++ * set the irq chip, handler and irq chip data for callbacks for ++ * each pin ++ */ ++ for (pin_num = 0; pin_num < min_t(int, XGPIOPS_NR_GPIOS, ++ (int)chip->ngpio); pin_num++) { ++ gpio_irq = irq_find_mapping(irq_domain, pin_num); ++ irq_set_chip_and_handler(gpio_irq, &xgpiops_irqchip, ++ handle_simple_irq); ++ irq_set_chip_data(gpio_irq, (void *)gpio); ++ set_irq_flags(gpio_irq, IRQF_VALID); ++ } ++ ++ irq_set_handler_data(irq_num, (void *)gpio); ++ irq_set_chained_handler(irq_num, xgpiops_irqhandler); ++ ++ xgpiops_pm_runtime_init(pdev); ++ ++ device_set_wakeup_capable(&pdev->dev, 1); ++ ++ return 0; ++ ++err_clk_put: ++ clk_put(gpio->clk); ++err_chip_remove: ++ gpiochip_remove(chip); ++err_iounmap: ++ iounmap(gpio->base_addr); ++err_release_region: ++ release_mem_region(mem_res->start, remap_size); ++err_free_gpio: ++ platform_set_drvdata(pdev, NULL); ++ kfree(gpio); ++ ++ return ret; ++} ++ ++static int xgpiops_remove(struct platform_device *pdev) ++{ ++ struct xgpiops *gpio = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(gpio->clk); ++ clk_put(gpio->clk); ++ device_set_wakeup_capable(&pdev->dev, 0); ++ return 0; ++} ++ ++static struct of_device_id xgpiops_of_match[] = { ++ { .compatible = "xlnx,ps7-gpio-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xgpiops_of_match); ++ ++static struct platform_driver xgpiops_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = XGPIOPS_PM, ++ .of_match_table = xgpiops_of_match, ++ }, ++ .probe = xgpiops_probe, ++ .remove = xgpiops_remove, ++}; ++ ++/** ++ * xgpiops_init - Initial driver registration call ++ */ ++static int __init xgpiops_init(void) ++{ ++ return platform_driver_register(&xgpiops_driver); ++} ++ ++postcore_initcall(xgpiops_init); diff --git a/patches.zynq/0006-spi-xilinx-merge-qspi-support-from-xilinx-repository.patch b/patches.zynq/0006-spi-xilinx-merge-qspi-support-from-xilinx-repository.patch new file mode 100644 index 0000000..bb8e527 --- /dev/null +++ b/patches.zynq/0006-spi-xilinx-merge-qspi-support-from-xilinx-repository.patch @@ -0,0 +1,2303 @@ +From 99e292bf8f472ce9475a68b37eb89ef201ff8acb Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Tue, 24 Dec 2013 09:10:07 +0900 +Subject: spi: xilinx: merge qspi support from xilinx repository + +This merges support for Xilinx QSPI from the Xilinx repository +(based on commit efc27505715e64526653f35274717c0fc56491e3 in master +branch). This has been tested to read the QSPI flash in the +Zynq 702 board. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/spi/Kconfig | 24 + drivers/spi/Makefile | 2 + drivers/spi/spi-xilinx-ps.c | 927 ++++++++++++++++++++++++++++++++ + drivers/spi/spi-xilinx-qps.c | 1220 +++++++++++++++++++++++++++++++++++++++++++ + drivers/spi/spi-xilinx.c | 27 + include/linux/spi/spi.h | 2 + 6 files changed, 2196 insertions(+), 6 deletions(-) + create mode 100644 drivers/spi/spi-xilinx-ps.c + create mode 100644 drivers/spi/spi-xilinx-qps.c + +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -474,12 +474,32 @@ config SPI_XILINX + select SPI_BITBANG + help + This exposes the SPI controller IP from the Xilinx EDK. +- + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" + Product Specification document (DS464) for hardware details. +- + Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" + ++config SPI_XILINX_PS_QSPI ++ tristate "Xilinx PS QSPI controller" ++ depends on ARCH_ZYNQ ++ depends on SPI_MASTER ++ help ++ This selects the PS Quad SPI controller master driver from the Xilinx. ++ ++config SPI_XILINX_PS_QSPI_DUAL_STACKED ++ bool "Xilinx PS QSPI Dual stacked configuration" ++ depends on SPI_XILINX_PS_QSPI ++ help ++ This selects the PS Quad SPI controller in dual stacked mode. ++ Enable this option if your hw design is using dual stacked ++ configuration. ++ ++config SPI_XILINX_PS_SPI ++ tristate "Xilinx PS SPI controller" ++ depends on ARCH_ZYNQ ++ depends on SPI_MASTER ++ help ++ This selects the PS SPI controller master driver from the Xilinx. ++ + config SPI_NUC900 + tristate "Nuvoton NUC900 series SPI" + depends on ARCH_W90X900 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -74,3 +74,5 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-t + obj-$(CONFIG_SPI_TXX9) += spi-txx9.o + obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o + obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o ++obj-$(CONFIG_SPI_XILINX_PS_SPI) += spi-xilinx-ps.o ++obj-$(CONFIG_SPI_XILINX_PS_QSPI) += spi-xilinx-qps.o +--- /dev/null ++++ b/drivers/spi/spi-xilinx-ps.c +@@ -0,0 +1,927 @@ ++/* ++ * ++ * Xilinx PS SPI controller driver (master mode only) ++ * ++ * (c) 2008-2011 Xilinx, Inc. ++ * ++ * based on Blackfin On-Chip SPI Driver (spi_bfin5xx.c) ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Name of this driver ++ */ ++#define XSPIPS_NAME "xspips" ++ ++/* ++ * Register offset definitions ++ */ ++#define XSPIPS_CR_OFFSET 0x00 /* Configuration Register, RW */ ++#define XSPIPS_ISR_OFFSET 0x04 /* Interrupt Status Register, RO */ ++#define XSPIPS_IER_OFFSET 0x08 /* Interrupt Enable Register, WO */ ++#define XSPIPS_IDR_OFFSET 0x0c /* Interrupt Disable Register, WO */ ++#define XSPIPS_IMR_OFFSET 0x10 /* Interrupt Enabled Mask Register, RO */ ++#define XSPIPS_ER_OFFSET 0x14 /* Enable/Disable Register, RW */ ++#define XSPIPS_DR_OFFSET 0x18 /* Delay Register, RW */ ++#define XSPIPS_TXD_OFFSET 0x1C /* Data Transmit Register, WO */ ++#define XSPIPS_RXD_OFFSET 0x20 /* Data Receive Register, RO */ ++#define XSPIPS_SICR_OFFSET 0x24 /* Slave Idle Count Register, RW */ ++#define XSPIPS_THLD_OFFSET 0x28 /* Transmit FIFO Watermark Register,RW */ ++ ++/* ++ * SPI Configuration Register bit Masks ++ * ++ * This register contains various control bits that affect the operation ++ * of the SPI controller ++ */ ++#define XSPIPS_CR_MANSTRT_MASK 0x00010000 /* Manual TX Start */ ++#define XSPIPS_CR_CPHA_MASK 0x00000004 /* Clock Phase Control */ ++#define XSPIPS_CR_CPOL_MASK 0x00000002 /* Clock Polarity Control */ ++#define XSPIPS_CR_SSCTRL_MASK 0x00003C00 /* Slave Select Mask */ ++ ++/* ++ * SPI Interrupt Registers bit Masks ++ * ++ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same ++ * bit definitions. ++ */ ++#define XSPIPS_IXR_TXOW_MASK 0x00000004 /* SPI TX FIFO Overwater */ ++#define XSPIPS_IXR_MODF_MASK 0x00000002 /* SPI Mode Fault */ ++#define XSPIPS_IXR_RXNEMTY_MASK 0x00000010 /* SPI RX FIFO Not Empty */ ++#define XSPIPS_IXR_ALL_MASK (XSPIPS_IXR_TXOW_MASK | XSPIPS_IXR_MODF_MASK) ++ ++/* ++ * SPI Enable Register bit Masks ++ * ++ * This register is used to enable or disable the SPI controller ++ */ ++#define XSPIPS_ER_ENABLE_MASK 0x00000001 /* SPI Enable Bit Mask */ ++ ++/* ++ * Definitions for the status of queue ++ */ ++#define XSPIPS_QUEUE_STOPPED 0 ++#define XSPIPS_QUEUE_RUNNING 1 ++ ++/* ++ * Macros for the SPI controller read/write ++ */ ++#define xspips_read(addr) __raw_readl(addr) ++#define xspips_write(addr, val) __raw_writel((val), (addr)) ++ ++ ++/** ++ * struct xspips - This definition defines spi driver instance ++ * @workqueue: Queue of all the transfers ++ * @work: Information about current transfer ++ * @queue: Head of the queue ++ * @queue_state: Queue status ++ * @regs: Virtual address of the SPI controller registers ++ * @devclk: Pointer to the peripheral clock ++ * @aperclk: Pointer to the APER clock ++ * @clk_rate_change_nb: Notifier block for clock frequency change callback ++ * @irq: IRQ number ++ * @speed_hz: Current SPI bus clock speed in Hz ++ * @trans_queue_lock: Lock used for accessing transfer queue ++ * @ctrl_reg_lock: Lock used for accessing configuration register ++ * @txbuf: Pointer to the TX buffer ++ * @rxbuf: Pointer to the RX buffer ++ * @remaining_bytes: Number of bytes left to transfer ++ * @dev_busy: Device busy flag ++ * @done: Transfer complete status ++ */ ++struct xspips { ++ struct workqueue_struct *workqueue; ++ struct work_struct work; ++ struct list_head queue; ++ int queue_state; ++ void __iomem *regs; ++ struct clk *devclk; ++ struct clk *aperclk; ++ struct notifier_block clk_rate_change_nb; ++ int irq; ++ u32 speed_hz; ++ spinlock_t trans_queue_lock; ++ spinlock_t ctrl_reg_lock; ++ const u8 *txbuf; ++ u8 *rxbuf; ++ int remaining_bytes; ++ u8 dev_busy; ++ struct completion done; ++}; ++ ++ ++/** ++ * xspips_init_hw - Initialize the hardware and configure the SPI controller ++ * @regs_base: Base address of SPI controller ++ * ++ * On reset the SPI controller is configured to be in master mode, baud rate ++ * divisor is set to 2, threshold value for TX FIFO not full interrupt is set ++ * to 1 and size of the word to be transferred as 8 bit. ++ * This function initializes the SPI controller to disable and clear all the ++ * interrupts, enable manual slave select and manual start, deselect all the ++ * chip select lines, and enable the SPI controller. ++ */ ++static void xspips_init_hw(void __iomem *regs_base) ++{ ++ xspips_write(regs_base + XSPIPS_ER_OFFSET, ~XSPIPS_ER_ENABLE_MASK); ++ xspips_write(regs_base + XSPIPS_IDR_OFFSET, 0x7F); ++ ++ /* Clear the RX FIFO */ ++ while (xspips_read(regs_base + XSPIPS_ISR_OFFSET) & ++ XSPIPS_IXR_RXNEMTY_MASK) ++ xspips_read(regs_base + XSPIPS_RXD_OFFSET); ++ ++ xspips_write(regs_base + XSPIPS_ISR_OFFSET, 0x7F); ++ xspips_write(regs_base + XSPIPS_CR_OFFSET, 0x0000FC01); ++ xspips_write(regs_base + XSPIPS_ER_OFFSET, XSPIPS_ER_ENABLE_MASK); ++} ++ ++/** ++ * xspips_chipselect - Select or deselect the chip select line ++ * @spi: Pointer to the spi_device structure ++ * @is_on: Select(1) or deselect (0) the chip select line ++ */ ++static void xspips_chipselect(struct spi_device *spi, int is_on) ++{ ++ struct xspips *xspi = spi_master_get_devdata(spi->master); ++ u32 ctrl_reg; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xspi->ctrl_reg_lock, flags); ++ ++ ctrl_reg = xspips_read(xspi->regs + XSPIPS_CR_OFFSET); ++ ++ if (is_on) { ++ /* Select the slave */ ++ ctrl_reg &= ~XSPIPS_CR_SSCTRL_MASK; ++ ctrl_reg |= (((~(0x0001 << spi->chip_select)) << 10) & ++ XSPIPS_CR_SSCTRL_MASK); ++ } else { ++ /* Deselect the slave */ ++ ctrl_reg |= XSPIPS_CR_SSCTRL_MASK; ++ } ++ ++ xspips_write(xspi->regs + XSPIPS_CR_OFFSET, ctrl_reg); ++ ++ spin_unlock_irqrestore(&xspi->ctrl_reg_lock, flags); ++} ++ ++/** ++ * xspips_setup_transfer - Configure SPI controller for specified transfer ++ * @spi: Pointer to the spi_device structure ++ * @transfer: Pointer to the spi_transfer structure which provides information ++ * about next transfer setup parameters ++ * ++ * Sets the operational mode of SPI controller for the next SPI transfer and ++ * sets the requested clock frequency. ++ * ++ * returns: 0 on success and error value on error ++ * ++ * Note: If the requested frequency is not an exact match with what can be ++ * obtained using the prescalar value the driver sets the clock frequency which ++ * is lower than the requested frequency (maximum lower) for the transfer. If ++ * the requested frequency is higher or lower than that is supported by the SPI ++ * controller the driver will set the highest or lowest frequency supported by ++ * controller. ++ */ ++static int xspips_setup_transfer(struct spi_device *spi, ++ struct spi_transfer *transfer) ++{ ++ struct xspips *xspi = spi_master_get_devdata(spi->master); ++ u8 bits_per_word; ++ u32 ctrl_reg; ++ u32 req_hz; ++ u32 baud_rate_val; ++ unsigned long flags, frequency; ++ ++ bits_per_word = (transfer) ? ++ transfer->bits_per_word : spi->bits_per_word; ++ req_hz = (transfer) ? transfer->speed_hz : spi->max_speed_hz; ++ ++ if (bits_per_word != 8) { ++ dev_err(&spi->dev, "%s, unsupported bits per word %x\n", ++ __func__, spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ frequency = clk_get_rate(xspi->devclk); ++ ++ spin_lock_irqsave(&xspi->ctrl_reg_lock, flags); ++ ++ xspips_write(xspi->regs + XSPIPS_ER_OFFSET, ~XSPIPS_ER_ENABLE_MASK); ++ ctrl_reg = xspips_read(xspi->regs + XSPIPS_CR_OFFSET); ++ ++ /* Set the SPI clock phase and clock polarity */ ++ ctrl_reg &= (~XSPIPS_CR_CPHA_MASK) & (~XSPIPS_CR_CPOL_MASK); ++ if (spi->mode & SPI_CPHA) ++ ctrl_reg |= XSPIPS_CR_CPHA_MASK; ++ if (spi->mode & SPI_CPOL) ++ ctrl_reg |= XSPIPS_CR_CPOL_MASK; ++ ++ /* Set the clock frequency */ ++ if (xspi->speed_hz != req_hz) { ++ baud_rate_val = 0; ++ while ((baud_rate_val < 8) && (frequency / ++ (2 << baud_rate_val)) > req_hz) ++ baud_rate_val++; ++ ++ ctrl_reg &= 0xFFFFFFC7; ++ ctrl_reg |= (baud_rate_val << 3); ++ ++ xspi->speed_hz = (frequency / (2 << baud_rate_val)); ++ } ++ ++ xspips_write(xspi->regs + XSPIPS_CR_OFFSET, ctrl_reg); ++ xspips_write(xspi->regs + XSPIPS_ER_OFFSET, XSPIPS_ER_ENABLE_MASK); ++ ++ spin_unlock_irqrestore(&xspi->ctrl_reg_lock, flags); ++ ++ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u clock speed\n", ++ __func__, spi->mode, spi->bits_per_word, ++ xspi->speed_hz); ++ ++ return 0; ++} ++ ++/** ++ * xspips_setup - Configure the SPI controller ++ * @spi: Pointer to the spi_device structure ++ * ++ * Sets the operational mode of SPI controller for the next SPI transfer, sets ++ * the baud rate and divisor value to setup the requested spi clock. ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_setup(struct spi_device *spi) ++{ ++ if (!spi->max_speed_hz) ++ return -EINVAL; ++ ++ if (!spi->bits_per_word) ++ spi->bits_per_word = 8; ++ ++ return xspips_setup_transfer(spi, NULL); ++} ++ ++/** ++ * xspips_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible ++ * @xspi: Pointer to the xspips structure ++ */ ++static void xspips_fill_tx_fifo(struct xspips *xspi) ++{ ++ while ((xspips_read(xspi->regs + XSPIPS_ISR_OFFSET) & 0x00000008) == 0 ++ && (xspi->remaining_bytes > 0)) { ++ if (xspi->txbuf) ++ xspips_write(xspi->regs + XSPIPS_TXD_OFFSET, ++ *xspi->txbuf++); ++ else ++ xspips_write(xspi->regs + XSPIPS_TXD_OFFSET, 0); ++ ++ xspi->remaining_bytes--; ++ } ++} ++ ++/** ++ * xspips_irq - Interrupt service routine of the SPI controller ++ * @irq: IRQ number ++ * @dev_id: Pointer to the xspi structure ++ * ++ * This function handles TX empty and Mode Fault interrupts only. ++ * On TX empty interrupt this function reads the received data from RX FIFO and ++ * fills the TX FIFO if there is any data remaining to be transferred. ++ * On Mode Fault interrupt this function indicates that transfer is completed, ++ * the SPI subsystem will identify the error as the remaining bytes to be ++ * transferred is non-zero. ++ * ++ * returns: IRQ_HANDLED always ++ */ ++static irqreturn_t xspips_irq(int irq, void *dev_id) ++{ ++ struct xspips *xspi = dev_id; ++ u32 intr_status; ++ ++ intr_status = xspips_read(xspi->regs + XSPIPS_ISR_OFFSET); ++ xspips_write(xspi->regs + XSPIPS_ISR_OFFSET, intr_status); ++ xspips_write(xspi->regs + XSPIPS_IDR_OFFSET, XSPIPS_IXR_ALL_MASK); ++ ++ if (intr_status & XSPIPS_IXR_MODF_MASK) { ++ /* Indicate that transfer is completed, the SPI subsystem will ++ * identify the error as the remaining bytes to be ++ * transferred is non-zero */ ++ complete(&xspi->done); ++ } else if (intr_status & XSPIPS_IXR_TXOW_MASK) { ++ u32 ctrl_reg; ++ ++ /* Read out the data from the RX FIFO */ ++ while (xspips_read(xspi->regs + XSPIPS_ISR_OFFSET) & ++ XSPIPS_IXR_RXNEMTY_MASK) { ++ u8 data; ++ ++ data = xspips_read(xspi->regs + XSPIPS_RXD_OFFSET); ++ if (xspi->rxbuf) ++ *xspi->rxbuf++ = data; ++ ++ /* Data memory barrier is placed here to ensure that ++ * data read operation is completed before the status ++ * read is initiated. Without dmb, there are chances ++ * that data and status reads will appear at the SPI ++ * peripheral back-to-back which results in an ++ * incorrect status read. ++ */ ++ dmb(); ++ } ++ ++ if (xspi->remaining_bytes) { ++ /* There is more data to send */ ++ xspips_fill_tx_fifo(xspi); ++ ++ xspips_write(xspi->regs + XSPIPS_IER_OFFSET, ++ XSPIPS_IXR_ALL_MASK); ++ ++ spin_lock(&xspi->ctrl_reg_lock); ++ ++ ctrl_reg = xspips_read(xspi->regs + XSPIPS_CR_OFFSET); ++ ctrl_reg |= XSPIPS_CR_MANSTRT_MASK; ++ xspips_write(xspi->regs + XSPIPS_CR_OFFSET, ctrl_reg); ++ ++ spin_unlock(&xspi->ctrl_reg_lock); ++ } else { ++ /* Transfer is completed */ ++ complete(&xspi->done); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * xspips_start_transfer - Initiates the SPI transfer ++ * @spi: Pointer to the spi_device structure ++ * @transfer: Pointer to the spi_transfer structure which provide information ++ * about next transfer parameters ++ * ++ * This function fills the TX FIFO, starts the SPI transfer, and waits for the ++ * transfer to be completed. ++ * ++ * returns: Number of bytes transferred in the last transfer ++ */ ++static int xspips_start_transfer(struct spi_device *spi, ++ struct spi_transfer *transfer) ++{ ++ struct xspips *xspi = spi_master_get_devdata(spi->master); ++ u32 ctrl_reg; ++ unsigned long flags; ++ ++ xspi->txbuf = transfer->tx_buf; ++ xspi->rxbuf = transfer->rx_buf; ++ xspi->remaining_bytes = transfer->len; ++ INIT_COMPLETION(xspi->done); ++ ++ xspips_fill_tx_fifo(xspi); ++ ++ xspips_write(xspi->regs + XSPIPS_IER_OFFSET, XSPIPS_IXR_ALL_MASK); ++ ++ spin_lock_irqsave(&xspi->ctrl_reg_lock, flags); ++ ++ /* Start the transfer by enabling manual start bit */ ++ ctrl_reg = xspips_read(xspi->regs + XSPIPS_CR_OFFSET); ++ ctrl_reg |= XSPIPS_CR_MANSTRT_MASK; ++ xspips_write(xspi->regs + XSPIPS_CR_OFFSET, ctrl_reg); ++ ++ spin_unlock_irqrestore(&xspi->ctrl_reg_lock, flags); ++ ++ wait_for_completion(&xspi->done); ++ ++ return (transfer->len) - (xspi->remaining_bytes); ++} ++ ++/** ++ * xspips_work_queue - Get the transfer request from queue to perform transfers ++ * @work: Pointer to the work_struct structure ++ */ ++static void xspips_work_queue(struct work_struct *work) ++{ ++ struct xspips *xspi = container_of(work, struct xspips, work); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ xspi->dev_busy = 1; ++ ++ if (list_empty(&xspi->queue) || ++ xspi->queue_state == XSPIPS_QUEUE_STOPPED) { ++ xspi->dev_busy = 0; ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ return; ++ } ++ ++ while (!list_empty(&xspi->queue)) { ++ struct spi_message *msg; ++ struct spi_device *spi; ++ struct spi_transfer *transfer = NULL; ++ unsigned cs_change = 1; ++ int status = 0; ++ ++ msg = container_of(xspi->queue.next, struct spi_message, queue); ++ list_del_init(&msg->queue); ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ spi = msg->spi; ++ ++ list_for_each_entry(transfer, &msg->transfers, transfer_list) { ++ if ((transfer->bits_per_word || transfer->speed_hz) && ++ cs_change) { ++ status = xspips_setup_transfer(spi, transfer); ++ if (status < 0) ++ break; ++ } ++ ++ if (cs_change) ++ xspips_chipselect(spi, 1); ++ ++ cs_change = transfer->cs_change; ++ ++ if (!transfer->tx_buf && !transfer->rx_buf && ++ transfer->len) { ++ status = -EINVAL; ++ break; ++ } ++ ++ if (transfer->len) ++ status = xspips_start_transfer(spi, transfer); ++ ++ if (status != transfer->len) { ++ if (status > 0) ++ status = -EMSGSIZE; ++ break; ++ } ++ msg->actual_length += status; ++ status = 0; ++ ++ if (transfer->delay_usecs) ++ udelay(transfer->delay_usecs); ++ ++ if (!cs_change) ++ continue; ++ if (transfer->transfer_list.next == &msg->transfers) ++ break; ++ ++ xspips_chipselect(spi, 0); ++ } ++ ++ msg->status = status; ++ msg->complete(msg->context); ++ ++ if (!(status == 0 && cs_change)) ++ xspips_chipselect(spi, 0); ++ ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ } ++ xspi->dev_busy = 0; ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++} ++ ++/** ++ * xspips_transfer - Add a new transfer request at the tail of work queue ++ * @spi: Pointer to the spi_device structure ++ * @message: Pointer to the spi_transfer structure which provide information ++ * about next transfer parameters ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_transfer(struct spi_device *spi, struct spi_message *message) ++{ ++ struct xspips *xspi = spi_master_get_devdata(spi->master); ++ struct spi_transfer *transfer; ++ unsigned long flags; ++ ++ if (xspi->queue_state == XSPIPS_QUEUE_STOPPED) ++ return -ESHUTDOWN; ++ ++ message->actual_length = 0; ++ message->status = -EINPROGRESS; ++ ++ /* Check each transfer's parameters */ ++ list_for_each_entry(transfer, &message->transfers, transfer_list) { ++ u8 bits_per_word = ++ transfer->bits_per_word ? : spi->bits_per_word; ++ ++ bits_per_word = bits_per_word ? : 8; ++ if (!transfer->tx_buf && !transfer->rx_buf && transfer->len) ++ return -EINVAL; ++ if (bits_per_word != 8) ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ list_add_tail(&message->queue, &xspi->queue); ++ if (!xspi->dev_busy) ++ queue_work(xspi->workqueue, &xspi->work); ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * xspips_start_queue - Starts the queue of the SPI driver ++ * @xspi: Pointer to the xspips structure ++ * ++ * returns: 0 on success and error value on error ++ */ ++static inline int xspips_start_queue(struct xspips *xspi) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ ++ if (xspi->queue_state == XSPIPS_QUEUE_RUNNING || xspi->dev_busy) { ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ return -EBUSY; ++ } ++ ++ xspi->queue_state = XSPIPS_QUEUE_RUNNING; ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * xspips_stop_queue - Stops the queue of the SPI driver ++ * @xspi: Pointer to the xspips structure ++ * ++ * This function waits till queue is empty and then stops the queue. ++ * Maximum time out is set to 5 seconds. ++ * ++ * returns: 0 on success and error value on error ++ */ ++static inline int xspips_stop_queue(struct xspips *xspi) ++{ ++ unsigned long flags; ++ unsigned limit = 500; ++ int ret = 0; ++ ++ if (xspi->queue_state != XSPIPS_QUEUE_RUNNING) ++ return ret; ++ ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ ++ while ((!list_empty(&xspi->queue) || xspi->dev_busy) && limit--) { ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ msleep(10); ++ spin_lock_irqsave(&xspi->trans_queue_lock, flags); ++ } ++ ++ if (!list_empty(&xspi->queue) || xspi->dev_busy) ++ ret = -EBUSY; ++ ++ if (ret == 0) ++ xspi->queue_state = XSPIPS_QUEUE_STOPPED; ++ ++ spin_unlock_irqrestore(&xspi->trans_queue_lock, flags); ++ ++ return ret; ++} ++ ++/** ++ * xspips_destroy_queue - Destroys the queue of the SPI driver ++ * @xspi: Pointer to the xspips structure ++ * ++ * returns: 0 on success and error value on error ++ */ ++static inline int xspips_destroy_queue(struct xspips *xspi) ++{ ++ int ret; ++ ++ ret = xspips_stop_queue(xspi); ++ if (ret != 0) ++ return ret; ++ ++ destroy_workqueue(xspi->workqueue); ++ ++ return 0; ++} ++ ++static int xspips_clk_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* if a rate change is announced we need to check whether we can ++ * maintain the current frequency by changing the clock ++ * dividers. And we may have to suspend operation and return ++ * after the rate change or its abort ++ */ ++ return NOTIFY_OK; ++ case POST_RATE_CHANGE: ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++/** ++ * xspips_probe - Probe method for the SPI driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function initializes the driver data structures and the hardware. ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct spi_master *master; ++ struct xspips *xspi; ++ struct resource *res; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*xspi)); ++ if (master == NULL) ++ return -ENOMEM; ++ ++ xspi = spi_master_get_devdata(master); ++ master->dev.of_node = pdev->dev.of_node; ++ platform_set_drvdata(pdev, master); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ xspi->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(xspi->regs)) { ++ ret = PTR_ERR(xspi->regs); ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ goto remove_master; ++ } ++ ++ xspi->irq = platform_get_irq(pdev, 0); ++ if (xspi->irq < 0) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "irq number is negative\n"); ++ goto remove_master; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, xspi->irq, xspips_irq, ++ 0, pdev->name, xspi); ++ if (ret != 0) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "request_irq failed\n"); ++ goto remove_master; ++ } ++ ++ xspi->aperclk = clk_get(&pdev->dev, "aper_clk"); ++ if (IS_ERR(xspi->aperclk)) { ++ dev_err(&pdev->dev, "aper_clk clock not found.\n"); ++ ret = PTR_ERR(xspi->aperclk); ++ goto remove_master; ++ } ++ ++ xspi->devclk = clk_get(&pdev->dev, "ref_clk"); ++ if (IS_ERR(xspi->devclk)) { ++ dev_err(&pdev->dev, "ref_clk clock not found.\n"); ++ ret = PTR_ERR(xspi->devclk); ++ goto clk_put_aper; ++ } ++ ++ ret = clk_prepare_enable(xspi->aperclk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto clk_put; ++ } ++ ++ ret = clk_prepare_enable(xspi->devclk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable device clock.\n"); ++ goto clk_dis_aper; ++ } ++ ++ xspi->clk_rate_change_nb.notifier_call = xspips_clk_notifier_cb; ++ xspi->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(xspi->devclk, &xspi->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ ++ /* SPI controller initializations */ ++ xspips_init_hw(xspi->regs); ++ ++ init_completion(&xspi->done); ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "num-chip-select", ++ (u32 *)&master->num_chipselect); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "couldn't determine num-chip-select\n"); ++ goto clk_notif_unreg; ++ } ++ master->setup = xspips_setup; ++ master->transfer = xspips_transfer; ++ master->mode_bits = SPI_CPOL | SPI_CPHA; ++ ++ xspi->speed_hz = clk_get_rate(xspi->devclk) / 2; ++ ++ xspi->dev_busy = 0; ++ ++ INIT_LIST_HEAD(&xspi->queue); ++ spin_lock_init(&xspi->trans_queue_lock); ++ spin_lock_init(&xspi->ctrl_reg_lock); ++ ++ xspi->queue_state = XSPIPS_QUEUE_STOPPED; ++ xspi->dev_busy = 0; ++ ++ INIT_WORK(&xspi->work, xspips_work_queue); ++ xspi->workqueue = ++ create_singlethread_workqueue(dev_name(&pdev->dev)); ++ if (!xspi->workqueue) { ++ ret = -ENOMEM; ++ dev_err(&pdev->dev, "problem initializing queue\n"); ++ goto clk_notif_unreg; ++ } ++ ++ ret = xspips_start_queue(xspi); ++ if (ret != 0) { ++ dev_err(&pdev->dev, "problem starting queue\n"); ++ goto remove_queue; ++ } ++ ++ ret = spi_register_master(master); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_register_master failed\n"); ++ goto remove_queue; ++ } ++ ++ dev_info(&pdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", res->start, ++ (u32 __force)xspi->regs, xspi->irq); ++ ++ return ret; ++ ++remove_queue: ++ (void)xspips_destroy_queue(xspi); ++clk_notif_unreg: ++ clk_notifier_unregister(xspi->devclk, &xspi->clk_rate_change_nb); ++ clk_disable_unprepare(xspi->devclk); ++clk_dis_aper: ++ clk_disable_unprepare(xspi->aperclk); ++clk_put: ++ clk_put(xspi->devclk); ++clk_put_aper: ++ clk_put(xspi->aperclk); ++remove_master: ++ spi_master_put(master); ++ return ret; ++} ++ ++/** ++ * xspips_remove - Remove method for the SPI driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function is called if a device is physically removed from the system or ++ * if the driver module is being unloaded. It frees all resources allocated to ++ * the device. ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xspips *xspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = xspips_destroy_queue(xspi); ++ if (ret != 0) ++ return ret; ++ ++ xspips_write(xspi->regs + XSPIPS_ER_OFFSET, ~XSPIPS_ER_ENABLE_MASK); ++ ++ clk_notifier_unregister(xspi->devclk, &xspi->clk_rate_change_nb); ++ clk_disable_unprepare(xspi->devclk); ++ clk_disable_unprepare(xspi->aperclk); ++ clk_put(xspi->devclk); ++ clk_put(xspi->aperclk); ++ ++ spi_unregister_master(master); ++ spi_master_put(master); ++ ++ dev_dbg(&pdev->dev, "remove succeeded\n"); ++ return 0; ++ ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * xspips_suspend - Suspend method for the SPI driver ++ * @dev: Address of the platform_device structure ++ * ++ * This function stops the SPI driver queue and disables the SPI controller ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = container_of(dev, ++ struct platform_device, dev); ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xspips *xspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = xspips_stop_queue(xspi); ++ if (ret != 0) ++ return ret; ++ ++ xspips_write(xspi->regs + XSPIPS_ER_OFFSET, ~XSPIPS_ER_ENABLE_MASK); ++ ++ clk_disable(xspi->devclk); ++ clk_disable(xspi->aperclk); ++ ++ dev_dbg(&pdev->dev, "suspend succeeded\n"); ++ return 0; ++} ++ ++/** ++ * xspips_resume - Resume method for the SPI driver ++ * @dev: Address of the platform_device structure ++ * ++ * This function starts the SPI driver queue and initializes the SPI controller ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xspips_resume(struct device *dev) ++{ ++ struct platform_device *pdev = container_of(dev, ++ struct platform_device, dev); ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xspips *xspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = clk_enable(xspi->aperclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable APER clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_enable(xspi->devclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable device clock.\n"); ++ clk_disable(xspi->aperclk); ++ return ret; ++ } ++ ++ xspips_init_hw(xspi->regs); ++ ++ ret = xspips_start_queue(xspi); ++ if (ret != 0) { ++ dev_err(&pdev->dev, "problem starting queue (%d)\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(&pdev->dev, "resume succeeded\n"); ++ return 0; ++} ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(xspips_dev_pm_ops, xspips_suspend, xspips_resume); ++ ++/* Work with hotplug and coldplug */ ++MODULE_ALIAS("platform:" XSPIPS_NAME); ++ ++static struct of_device_id xspips_of_match[] = { ++ { .compatible = "xlnx,ps7-spi-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xspips_of_match); ++ ++/* ++ * xspips_driver - This structure defines the SPI subsystem platform driver ++ */ ++static struct platform_driver xspips_driver = { ++ .probe = xspips_probe, ++ .remove = xspips_remove, ++ .driver = { ++ .name = XSPIPS_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = xspips_of_match, ++ .pm = &xspips_dev_pm_ops, ++ }, ++}; ++ ++module_platform_driver(xspips_driver); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx PS SPI driver"); ++MODULE_LICENSE("GPL"); ++ +--- /dev/null ++++ b/drivers/spi/spi-xilinx-qps.c +@@ -0,0 +1,1220 @@ ++/* ++ * ++ * Xilinx PS Quad-SPI (QSPI) controller driver (master mode only) ++ * ++ * (c) 2009-2011 Xilinx, Inc. ++ * ++ * based on Xilinx PS SPI Driver (xspips.c) ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Name of this driver ++ */ ++#define DRIVER_NAME "xqspips" ++ ++/* ++ * Register offset definitions ++ */ ++#define XQSPIPS_CONFIG_OFFSET 0x00 /* Configuration Register, RW */ ++#define XQSPIPS_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO */ ++#define XQSPIPS_IEN_OFFSET 0x08 /* Interrupt Enable Register, WO */ ++#define XQSPIPS_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO */ ++#define XQSPIPS_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask Reg,RO */ ++#define XQSPIPS_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW */ ++#define XQSPIPS_DELAY_OFFSET 0x18 /* Delay Register, RW */ ++#define XQSPIPS_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */ ++#define XQSPIPS_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */ ++#define XQSPIPS_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */ ++#define XQSPIPS_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */ ++#define XQSPIPS_RXD_OFFSET 0x20 /* Data Receive Register, RO */ ++#define XQSPIPS_SIC_OFFSET 0x24 /* Slave Idle Count Register, RW */ ++#define XQSPIPS_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, RW */ ++#define XQSPIPS_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, RW */ ++#define XQSPIPS_GPIO_OFFSET 0x30 /* GPIO Register, RW */ ++#define XQSPIPS_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, RW */ ++#define XQSPIPS_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */ ++ ++/* ++ * QSPI Configuration Register bit Masks ++ * ++ * This register contains various control bits that effect the operation ++ * of the QSPI controller ++ */ ++#define XQSPIPS_CONFIG_MANSRT_MASK 0x00010000 /* Manual TX Start */ ++#define XQSPIPS_CONFIG_CPHA_MASK 0x00000004 /* Clock Phase Control */ ++#define XQSPIPS_CONFIG_CPOL_MASK 0x00000002 /* Clock Polarity Control */ ++#define XQSPIPS_CONFIG_SSCTRL_MASK 0x00003C00 /* Slave Select Mask */ ++ ++/* ++ * QSPI Interrupt Registers bit Masks ++ * ++ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same ++ * bit definitions. ++ */ ++#define XQSPIPS_IXR_TXNFULL_MASK 0x00000004 /* QSPI TX FIFO Overflow */ ++#define XQSPIPS_IXR_TXFULL_MASK 0x00000008 /* QSPI TX FIFO is full */ ++#define XQSPIPS_IXR_RXNEMTY_MASK 0x00000010 /* QSPI RX FIFO Not Empty */ ++#define XQSPIPS_IXR_ALL_MASK (XQSPIPS_IXR_TXNFULL_MASK) ++ ++/* ++ * QSPI Enable Register bit Masks ++ * ++ * This register is used to enable or disable the QSPI controller ++ */ ++#define XQSPIPS_ENABLE_ENABLE_MASK 0x00000001 /* QSPI Enable Bit Mask */ ++ ++/* ++ * QSPI Linear Configuration Register ++ * ++ * It is named Linear Configuration but it controls other modes when not in ++ * linear mode also. ++ */ ++#define XQSPIPS_LCFG_TWO_MEM_MASK 0x40000000 /* LQSPI Two memories Mask */ ++#define XQSPIPS_LCFG_SEP_BUS_MASK 0x20000000 /* LQSPI Separate bus Mask */ ++#define XQSPIPS_LCFG_U_PAGE_MASK 0x10000000 /* LQSPI Upper Page Mask */ ++ ++#define XQSPIPS_LCFG_DUMMY_SHIFT 8 ++ ++#define XQSPIPS_FAST_READ_QOUT_CODE 0x6B /* read instruction code */ ++ ++/* ++ * The modebits configurable by the driver to make the SPI support different ++ * data formats ++ */ ++#define MODEBITS (SPI_CPOL | SPI_CPHA) ++ ++/* ++ * Definitions for the status of queue ++ */ ++#define XQSPIPS_QUEUE_STOPPED 0 ++#define XQSPIPS_QUEUE_RUNNING 1 ++ ++/* ++ * Definitions of the flash commands ++ */ ++/* Flash opcodes in ascending order */ ++#define XQSPIPS_FLASH_OPCODE_WRSR 0x01 /* Write status register */ ++#define XQSPIPS_FLASH_OPCODE_PP 0x02 /* Page program */ ++#define XQSPIPS_FLASH_OPCODE_NORM_READ 0x03 /* Normal read data bytes */ ++#define XQSPIPS_FLASH_OPCODE_WRDS 0x04 /* Write disable */ ++#define XQSPIPS_FLASH_OPCODE_RDSR1 0x05 /* Read status register 1 */ ++#define XQSPIPS_FLASH_OPCODE_WREN 0x06 /* Write enable */ ++#define XQSPIPS_FLASH_OPCODE_BRRD 0x16 /* Bank Register Read */ ++#define XQSPIPS_FLASH_OPCODE_BRWR 0x17 /* Bank Register Write */ ++#define XQSPIPS_FLASH_OPCODE_EXTADRD 0xC8 /* Micron - Bank Reg Read */ ++#define XQSPIPS_FLASH_OPCODE_EXTADWR 0xC5 /* Micron - Bank Reg Write */ ++#define XQSPIPS_FLASH_OPCODE_FAST_READ 0x0B /* Fast read data bytes */ ++#define XQSPIPS_FLASH_OPCODE_BE_4K 0x20 /* Erase 4KiB block */ ++#define XQSPIPS_FLASH_OPCODE_RDSR2 0x35 /* Read status register 2 */ ++#define XQSPIPS_FLASH_OPCODE_RDFSR 0x70 /* Read flag status register */ ++#define XQSPIPS_FLASH_OPCODE_DUAL_READ 0x3B /* Dual read data bytes */ ++#define XQSPIPS_FLASH_OPCODE_BE_32K 0x52 /* Erase 32KiB block */ ++#define XQSPIPS_FLASH_OPCODE_QUAD_READ 0x6B /* Quad read data bytes */ ++#define XQSPIPS_FLASH_OPCODE_ERASE_SUS 0x75 /* Erase suspend */ ++#define XQSPIPS_FLASH_OPCODE_ERASE_RES 0x7A /* Erase resume */ ++#define XQSPIPS_FLASH_OPCODE_RDID 0x9F /* Read JEDEC ID */ ++#define XQSPIPS_FLASH_OPCODE_BE 0xC7 /* Erase whole flash block */ ++#define XQSPIPS_FLASH_OPCODE_SE 0xD8 /* Sector erase (usually 64KB)*/ ++#define XQSPIPS_FLASH_OPCODE_QPP 0x32 /* Quad page program */ ++ ++/* ++ * Macros for the QSPI controller read/write ++ */ ++#define xqspips_read(addr) readl(addr) ++#define xqspips_write(addr, val) writel((val), (addr)) ++ ++/** ++ * struct xqspips - Defines qspi driver instance ++ * @workqueue: Queue of all the transfers ++ * @work: Information about current transfer ++ * @queue: Head of the queue ++ * @queue_state: Queue status ++ * @regs: Virtual address of the QSPI controller registers ++ * @devclk: Pointer to the peripheral clock ++ * @aperclk: Pointer to the APER clock ++ * @clk_rate_change_nb: Notifier block for clock frequency change callback ++ * @irq: IRQ number ++ * @speed_hz: Current QSPI bus clock speed in Hz ++ * @trans_queue_lock: Lock used for accessing transfer queue ++ * @config_reg_lock: Lock used for accessing configuration register ++ * @txbuf: Pointer to the TX buffer ++ * @rxbuf: Pointer to the RX buffer ++ * @bytes_to_transfer: Number of bytes left to transfer ++ * @bytes_to_receive: Number of bytes left to receive ++ * @dev_busy: Device busy flag ++ * @done: Transfer complete status ++ * @is_inst: Flag to indicate the first message in a Transfer request ++ * @is_dual: Flag to indicate whether dual flash memories are used ++ */ ++struct xqspips { ++ struct workqueue_struct *workqueue; ++ struct work_struct work; ++ struct list_head queue; ++ u8 queue_state; ++ void __iomem *regs; ++ struct clk *devclk; ++ struct clk *aperclk; ++ struct notifier_block clk_rate_change_nb; ++ int irq; ++ u32 speed_hz; ++ spinlock_t trans_queue_lock; ++ spinlock_t config_reg_lock; ++ const void *txbuf; ++ void *rxbuf; ++ int bytes_to_transfer; ++ int bytes_to_receive; ++ u8 dev_busy; ++ struct completion done; ++ bool is_inst; ++ u32 is_dual; ++}; ++ ++/** ++ * struct xqspips_inst_format - Defines qspi flash instruction format ++ * @opcode: Operational code of instruction ++ * @inst_size: Size of the instruction including address bytes ++ * @offset: Register address where instruction has to be written ++ */ ++struct xqspips_inst_format { ++ u8 opcode; ++ u8 inst_size; ++ u8 offset; ++}; ++ ++/* ++ * List of all the QSPI instructions and its format ++ */ ++static struct xqspips_inst_format flash_inst[] = { ++ { XQSPIPS_FLASH_OPCODE_WREN, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_WRDS, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_RDSR1, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_RDSR2, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_WRSR, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_RDFSR, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_PP, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_SE, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_BE_32K, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_BE_4K, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_BE, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_ERASE_SUS, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_ERASE_RES, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_RDID, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_NORM_READ, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_FAST_READ, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_DUAL_READ, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_QUAD_READ, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_BRRD, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_BRWR, 2, XQSPIPS_TXD_00_10_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_EXTADRD, 1, XQSPIPS_TXD_00_01_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_EXTADWR, 2, XQSPIPS_TXD_00_10_OFFSET }, ++ { XQSPIPS_FLASH_OPCODE_QPP, 4, XQSPIPS_TXD_00_00_OFFSET }, ++ /* Add all the instructions supported by the flash device */ ++}; ++ ++/** ++ * xqspips_init_hw - Initialize the hardware ++ * @regs_base: Base address of QSPI controller ++ * @is_dual: Indicates whether dual memories are used ++ * ++ * The default settings of the QSPI controller's configurable parameters on ++ * reset are ++ * - Master mode ++ * - Baud rate divisor is set to 2 ++ * - Threshold value for TX FIFO not full interrupt is set to 1 ++ * - Flash memory interface mode enabled ++ * - Size of the word to be transferred as 8 bit ++ * This function performs the following actions ++ * - Disable and clear all the interrupts ++ * - Enable manual slave select ++ * - Enable manual start ++ * - Deselect all the chip select lines ++ * - Set the size of the word to be transferred as 32 bit ++ * - Set the little endian mode of TX FIFO and ++ * - Enable the QSPI controller ++ */ ++static void xqspips_init_hw(void __iomem *regs_base, int is_dual) ++{ ++ u32 config_reg; ++ ++ xqspips_write(regs_base + XQSPIPS_ENABLE_OFFSET, ++ ~XQSPIPS_ENABLE_ENABLE_MASK); ++ xqspips_write(regs_base + XQSPIPS_IDIS_OFFSET, 0x7F); ++ ++ /* Disable linear mode as the boot loader may have used it */ ++ xqspips_write(regs_base + XQSPIPS_LINEAR_CFG_OFFSET, 0); ++ ++ /* Clear the RX FIFO */ ++ while (xqspips_read(regs_base + XQSPIPS_STATUS_OFFSET) & ++ XQSPIPS_IXR_RXNEMTY_MASK) ++ xqspips_read(regs_base + XQSPIPS_RXD_OFFSET); ++ ++ xqspips_write(regs_base + XQSPIPS_STATUS_OFFSET , 0x7F); ++ config_reg = xqspips_read(regs_base + XQSPIPS_CONFIG_OFFSET); ++ config_reg &= 0xFBFFFFFF; /* Set little endian mode of TX FIFO */ ++ config_reg |= 0x8000FCC1; ++ xqspips_write(regs_base + XQSPIPS_CONFIG_OFFSET, config_reg); ++ ++ if (is_dual == 1) ++ /* Enable two memories on seperate buses */ ++ xqspips_write(regs_base + XQSPIPS_LINEAR_CFG_OFFSET, ++ (XQSPIPS_LCFG_TWO_MEM_MASK | ++ XQSPIPS_LCFG_SEP_BUS_MASK | ++ (1 << XQSPIPS_LCFG_DUMMY_SHIFT) | ++ XQSPIPS_FAST_READ_QOUT_CODE)); ++#ifdef CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED ++ /* Enable two memories on shared bus */ ++ xqspips_write(regs_base + XQSPIPS_LINEAR_CFG_OFFSET, ++ (XQSPIPS_LCFG_TWO_MEM_MASK | ++ (1 << XQSPIPS_LCFG_DUMMY_SHIFT) | ++ XQSPIPS_FAST_READ_QOUT_CODE)); ++#endif ++ xqspips_write(regs_base + XQSPIPS_ENABLE_OFFSET, ++ XQSPIPS_ENABLE_ENABLE_MASK); ++} ++ ++/** ++ * xqspips_copy_read_data - Copy data to RX buffer ++ * @xqspi: Pointer to the xqspips structure ++ * @data: The 32 bit variable where data is stored ++ * @size: Number of bytes to be copied from data to RX buffer ++ */ ++static void xqspips_copy_read_data(struct xqspips *xqspi, u32 data, u8 size) ++{ ++ if (xqspi->rxbuf) { ++ data >>= (4 - size) * 8; ++ data = le32_to_cpu(data); ++ memcpy((u8 *)xqspi->rxbuf, &data, size); ++ xqspi->rxbuf += size; ++ } ++ xqspi->bytes_to_receive -= size; ++ if (xqspi->bytes_to_receive < 0) ++ xqspi->bytes_to_receive = 0; ++} ++ ++/** ++ * xqspips_copy_write_data - Copy data from TX buffer ++ * @xqspi: Pointer to the xqspips structure ++ * @data: Pointer to the 32 bit variable where data is to be copied ++ * @size: Number of bytes to be copied from TX buffer to data ++ */ ++static void xqspips_copy_write_data(struct xqspips *xqspi, u32 *data, u8 size) ++{ ++ ++ if (xqspi->txbuf) { ++ switch (size) { ++ case 1: ++ *data = *((u8 *)xqspi->txbuf); ++ xqspi->txbuf += 1; ++ *data |= 0xFFFFFF00; ++ break; ++ case 2: ++ *data = *((u16 *)xqspi->txbuf); ++ xqspi->txbuf += 2; ++ *data |= 0xFFFF0000; ++ break; ++ case 3: ++ *data = *((u16 *)xqspi->txbuf); ++ xqspi->txbuf += 2; ++ *data |= (*((u8 *)xqspi->txbuf) << 16); ++ xqspi->txbuf += 1; ++ *data |= 0xFF000000; ++ break; ++ case 4: ++ *data = *((u32 *)xqspi->txbuf); ++ xqspi->txbuf += 4; ++ break; ++ default: ++ /* This will never execute */ ++ break; ++ } ++ } else { ++ *data = 0; ++ } ++ ++ xqspi->bytes_to_transfer -= size; ++ if (xqspi->bytes_to_transfer < 0) ++ xqspi->bytes_to_transfer = 0; ++} ++ ++/** ++ * xqspips_chipselect - Select or deselect the chip select line ++ * @qspi: Pointer to the spi_device structure ++ * @is_on: Select(1) or deselect (0) the chip select line ++ */ ++static void xqspips_chipselect(struct spi_device *qspi, int is_on) ++{ ++ struct xqspips *xqspi = spi_master_get_devdata(qspi->master); ++ u32 config_reg; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xqspi->config_reg_lock, flags); ++ ++ config_reg = xqspips_read(xqspi->regs + XQSPIPS_CONFIG_OFFSET); ++ ++ if (is_on) { ++ /* Select the slave */ ++ config_reg &= ~XQSPIPS_CONFIG_SSCTRL_MASK; ++ config_reg |= (((~(0x0001 << qspi->chip_select)) << 10) & ++ XQSPIPS_CONFIG_SSCTRL_MASK); ++ } else { ++ /* Deselect the slave */ ++ config_reg |= XQSPIPS_CONFIG_SSCTRL_MASK; ++ } ++ ++ xqspips_write(xqspi->regs + XQSPIPS_CONFIG_OFFSET, config_reg); ++ ++ spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); ++} ++ ++/** ++ * xqspips_setup_transfer - Configure QSPI controller for specified transfer ++ * @qspi: Pointer to the spi_device structure ++ * @transfer: Pointer to the spi_transfer structure which provides information ++ * about next transfer setup parameters ++ * ++ * Sets the operational mode of QSPI controller for the next QSPI transfer and ++ * sets the requested clock frequency. ++ * ++ * returns: 0 on success and -EINVAL on invalid input parameter ++ * ++ * Note: If the requested frequency is not an exact match with what can be ++ * obtained using the prescalar value, the driver sets the clock frequency which ++ * is lower than the requested frequency (maximum lower) for the transfer. If ++ * the requested frequency is higher or lower than that is supported by the QSPI ++ * controller the driver will set the highest or lowest frequency supported by ++ * controller. ++ */ ++static int xqspips_setup_transfer(struct spi_device *qspi, ++ struct spi_transfer *transfer) ++{ ++ struct xqspips *xqspi = spi_master_get_devdata(qspi->master); ++ u32 config_reg; ++ u32 req_hz; ++ u32 baud_rate_val = 0; ++ unsigned long flags; ++ int update_baud = 0; ++ ++ req_hz = (transfer) ? transfer->speed_hz : qspi->max_speed_hz; ++ ++ if (qspi->mode & ~MODEBITS) { ++ dev_err(&qspi->dev, "%s, unsupported mode bits %x\n", ++ __func__, qspi->mode & ~MODEBITS); ++ return -EINVAL; ++ } ++ ++ if (transfer && (transfer->speed_hz == 0)) ++ req_hz = qspi->max_speed_hz; ++ ++ /* Set the clock frequency */ ++ if (xqspi->speed_hz != req_hz) { ++ while ((baud_rate_val < 8) && ++ (clk_get_rate(xqspi->devclk) / (2 << baud_rate_val)) > ++ req_hz) ++ baud_rate_val++; ++ xqspi->speed_hz = req_hz; ++ update_baud = 1; ++ } ++ ++ spin_lock_irqsave(&xqspi->config_reg_lock, flags); ++ ++ config_reg = xqspips_read(xqspi->regs + XQSPIPS_CONFIG_OFFSET); ++ ++ /* Set the QSPI clock phase and clock polarity */ ++ config_reg &= (~XQSPIPS_CONFIG_CPHA_MASK) & ++ (~XQSPIPS_CONFIG_CPOL_MASK); ++ if (qspi->mode & SPI_CPHA) ++ config_reg |= XQSPIPS_CONFIG_CPHA_MASK; ++ if (qspi->mode & SPI_CPOL) ++ config_reg |= XQSPIPS_CONFIG_CPOL_MASK; ++ ++ if (update_baud) { ++ config_reg &= 0xFFFFFFC7; ++ config_reg |= (baud_rate_val << 3); ++ } ++ ++ xqspips_write(xqspi->regs + XQSPIPS_CONFIG_OFFSET, config_reg); ++ ++ spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); ++ ++ dev_dbg(&qspi->dev, "%s, mode %d, %u bits/w, %u clock speed\n", ++ __func__, qspi->mode & MODEBITS, qspi->bits_per_word, ++ xqspi->speed_hz); ++ ++ return 0; ++} ++ ++/** ++ * xqspips_setup - Configure the QSPI controller ++ * @qspi: Pointer to the spi_device structure ++ * ++ * Sets the operational mode of QSPI controller for the next QSPI transfer, baud ++ * rate and divisor value to setup the requested qspi clock. ++ * ++ * returns: 0 on success and error value on failure ++ */ ++static int xqspips_setup(struct spi_device *qspi) ++{ ++ ++ if (qspi->mode & SPI_LSB_FIRST) ++ return -EINVAL; ++ ++ if (!qspi->max_speed_hz) ++ return -EINVAL; ++ ++ if (!qspi->bits_per_word) ++ qspi->bits_per_word = 32; ++ ++ return xqspips_setup_transfer(qspi, NULL); ++} ++ ++/** ++ * xqspips_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible ++ * @xqspi: Pointer to the xqspips structure ++ */ ++static void xqspips_fill_tx_fifo(struct xqspips *xqspi) ++{ ++ u32 data = 0; ++ ++ while ((!(xqspips_read(xqspi->regs + XQSPIPS_STATUS_OFFSET) & ++ XQSPIPS_IXR_TXFULL_MASK)) && (xqspi->bytes_to_transfer >= 4)) { ++ xqspips_copy_write_data(xqspi, &data, 4); ++ xqspips_write(xqspi->regs + XQSPIPS_TXD_00_00_OFFSET, data); ++ } ++} ++ ++/** ++ * xqspips_irq - Interrupt service routine of the QSPI controller ++ * @irq: IRQ number ++ * @dev_id: Pointer to the xqspi structure ++ * ++ * This function handles TX empty only. ++ * On TX empty interrupt this function reads the received data from RX FIFO and ++ * fills the TX FIFO if there is any data remaining to be transferred. ++ * ++ * returns: IRQ_HANDLED always ++ */ ++static irqreturn_t xqspips_irq(int irq, void *dev_id) ++{ ++ struct xqspips *xqspi = dev_id; ++ u32 intr_status; ++ u8 offset[3] = {XQSPIPS_TXD_00_01_OFFSET, XQSPIPS_TXD_00_10_OFFSET, ++ XQSPIPS_TXD_00_11_OFFSET}; ++ ++ intr_status = xqspips_read(xqspi->regs + XQSPIPS_STATUS_OFFSET); ++ xqspips_write(xqspi->regs + XQSPIPS_STATUS_OFFSET , intr_status); ++ xqspips_write(xqspi->regs + XQSPIPS_IDIS_OFFSET, ++ XQSPIPS_IXR_ALL_MASK); ++ ++ if ((intr_status & XQSPIPS_IXR_TXNFULL_MASK) || ++ (intr_status & XQSPIPS_IXR_RXNEMTY_MASK)) { ++ /* This bit is set when Tx FIFO has < THRESHOLD entries. We have ++ the THRESHOLD value set to 1, so this bit indicates Tx FIFO ++ is empty */ ++ u32 config_reg; ++ u32 data; ++ ++ /* Read out the data from the RX FIFO */ ++ while (xqspips_read(xqspi->regs + XQSPIPS_STATUS_OFFSET) & ++ XQSPIPS_IXR_RXNEMTY_MASK) { ++ ++ data = xqspips_read(xqspi->regs + XQSPIPS_RXD_OFFSET); ++ ++ if (xqspi->bytes_to_receive < 4 && !xqspi->is_dual) ++ xqspips_copy_read_data(xqspi, data, ++ xqspi->bytes_to_receive); ++ else ++ xqspips_copy_read_data(xqspi, data, 4); ++ } ++ ++ if (xqspi->bytes_to_transfer) { ++ if (xqspi->bytes_to_transfer >= 4) { ++ /* There is more data to send */ ++ xqspips_fill_tx_fifo(xqspi); ++ } else { ++ int tmp; ++ tmp = xqspi->bytes_to_transfer; ++ xqspips_copy_write_data(xqspi, &data, ++ xqspi->bytes_to_transfer); ++ if (xqspi->is_dual) ++ xqspips_write(xqspi->regs + ++ XQSPIPS_TXD_00_00_OFFSET, data); ++ else ++ xqspips_write(xqspi->regs + ++ offset[tmp - 1], data); ++ } ++ xqspips_write(xqspi->regs + XQSPIPS_IEN_OFFSET, ++ XQSPIPS_IXR_ALL_MASK); ++ ++ spin_lock(&xqspi->config_reg_lock); ++ config_reg = xqspips_read(xqspi->regs + ++ XQSPIPS_CONFIG_OFFSET); ++ ++ config_reg |= XQSPIPS_CONFIG_MANSRT_MASK; ++ xqspips_write(xqspi->regs + XQSPIPS_CONFIG_OFFSET, ++ config_reg); ++ spin_unlock(&xqspi->config_reg_lock); ++ } else { ++ /* If transfer and receive is completed then only send ++ * complete signal */ ++ if (xqspi->bytes_to_receive) { ++ /* There is still some data to be received. ++ Enable Rx not empty interrupt */ ++ xqspips_write(xqspi->regs + XQSPIPS_IEN_OFFSET, ++ XQSPIPS_IXR_RXNEMTY_MASK); ++ } else { ++ xqspips_write(xqspi->regs + XQSPIPS_IDIS_OFFSET, ++ XQSPIPS_IXR_RXNEMTY_MASK); ++ complete(&xqspi->done); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * xqspips_start_transfer - Initiates the QSPI transfer ++ * @qspi: Pointer to the spi_device structure ++ * @transfer: Pointer to the spi_transfer structure which provide information ++ * about next transfer parameters ++ * ++ * This function fills the TX FIFO, starts the QSPI transfer, and waits for the ++ * transfer to be completed. ++ * ++ * returns: Number of bytes transferred in the last transfer ++ */ ++static int xqspips_start_transfer(struct spi_device *qspi, ++ struct spi_transfer *transfer) ++{ ++ struct xqspips *xqspi = spi_master_get_devdata(qspi->master); ++ u32 config_reg; ++ unsigned long flags; ++ u32 data = 0; ++ u8 instruction = 0; ++ u8 index; ++ struct xqspips_inst_format *curr_inst; ++ ++ xqspi->txbuf = transfer->tx_buf; ++ xqspi->rxbuf = transfer->rx_buf; ++ xqspi->bytes_to_transfer = transfer->len; ++ xqspi->bytes_to_receive = transfer->len; ++ ++ if (xqspi->txbuf) ++ instruction = *(u8 *)xqspi->txbuf; ++ ++ INIT_COMPLETION(xqspi->done); ++ if (instruction && xqspi->is_inst) { ++ for (index = 0; index < ARRAY_SIZE(flash_inst); index++) ++ if (instruction == flash_inst[index].opcode) ++ break; ++ ++ /* Instruction might have already been transmitted. This is a ++ * 'data only' transfer */ ++ if (index == ARRAY_SIZE(flash_inst)) ++ goto xfer_data; ++ ++ curr_inst = &flash_inst[index]; ++ ++ /* Get the instruction */ ++ data = 0; ++ xqspips_copy_write_data(xqspi, &data, ++ curr_inst->inst_size); ++ ++ /* Write the instruction to LSB of the FIFO. The core is ++ * designed such that it is not necessary to check whether the ++ * write FIFO is full before writing. However, write would be ++ * delayed if the user tries to write when write FIFO is full ++ */ ++ xqspips_write(xqspi->regs + curr_inst->offset, data); ++ goto xfer_start; ++ } ++ ++xfer_data: ++ /* In case of Fast, Dual and Quad reads, transmit the instruction first. ++ * Address and dummy byte will be transmitted in interrupt handler, ++ * after instruction is transmitted */ ++ if (((xqspi->is_inst == 0) && (xqspi->bytes_to_transfer >= 4)) || ++ ((xqspi->bytes_to_transfer >= 4) && ++ (instruction != XQSPIPS_FLASH_OPCODE_FAST_READ) && ++ (instruction != XQSPIPS_FLASH_OPCODE_DUAL_READ) && ++ (instruction != XQSPIPS_FLASH_OPCODE_QUAD_READ))) ++ xqspips_fill_tx_fifo(xqspi); ++ ++xfer_start: ++ xqspips_write(xqspi->regs + XQSPIPS_IEN_OFFSET, ++ XQSPIPS_IXR_ALL_MASK); ++ /* Start the transfer by enabling manual start bit */ ++ spin_lock_irqsave(&xqspi->config_reg_lock, flags); ++ config_reg = xqspips_read(xqspi->regs + ++ XQSPIPS_CONFIG_OFFSET) | XQSPIPS_CONFIG_MANSRT_MASK; ++ xqspips_write(xqspi->regs + XQSPIPS_CONFIG_OFFSET, config_reg); ++ spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); ++ ++ wait_for_completion(&xqspi->done); ++ ++ return (transfer->len) - (xqspi->bytes_to_transfer); ++} ++ ++/** ++ * xqspips_work_queue - Get the request from queue to perform transfers ++ * @work: Pointer to the work_struct structure ++ */ ++static void xqspips_work_queue(struct work_struct *work) ++{ ++ struct xqspips *xqspi = container_of(work, struct xqspips, work); ++ unsigned long flags; ++#ifdef CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED ++ u32 lqspi_cfg_reg; ++#endif ++ ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ xqspi->dev_busy = 1; ++ ++ /* Check if list is empty or queue is stoped */ ++ if (list_empty(&xqspi->queue) || ++ xqspi->queue_state == XQSPIPS_QUEUE_STOPPED) { ++ xqspi->dev_busy = 0; ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ return; ++ } ++ ++ /* Keep requesting transfer till list is empty */ ++ while (!list_empty(&xqspi->queue)) { ++ struct spi_message *msg; ++ struct spi_device *qspi; ++ struct spi_transfer *transfer = NULL; ++ unsigned cs_change = 1; ++ int status = 0; ++ ++ msg = container_of(xqspi->queue.next, struct spi_message, ++ queue); ++ list_del_init(&msg->queue); ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ qspi = msg->spi; ++ ++#ifdef CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED ++ lqspi_cfg_reg = xqspips_read(xqspi->regs + ++ XQSPIPS_LINEAR_CFG_OFFSET); ++ if (qspi->master->flags & SPI_MASTER_U_PAGE) ++ lqspi_cfg_reg |= XQSPIPS_LCFG_U_PAGE_MASK; ++ else ++ lqspi_cfg_reg &= ~XQSPIPS_LCFG_U_PAGE_MASK; ++ xqspips_write(xqspi->regs + XQSPIPS_LINEAR_CFG_OFFSET, ++ lqspi_cfg_reg); ++#endif ++ ++ list_for_each_entry(transfer, &msg->transfers, transfer_list) { ++ if (transfer->bits_per_word || transfer->speed_hz) { ++ status = xqspips_setup_transfer(qspi, transfer); ++ if (status < 0) ++ break; ++ } ++ ++ /* Select the chip if required */ ++ if (cs_change) { ++ xqspips_chipselect(qspi, 1); ++ xqspi->is_inst = 1; ++ } ++ ++ cs_change = transfer->cs_change; ++ ++ if (!transfer->tx_buf && !transfer->rx_buf && ++ transfer->len) { ++ status = -EINVAL; ++ break; ++ } ++ ++ /* Request the transfer */ ++ if (transfer->len) { ++ status = xqspips_start_transfer(qspi, transfer); ++ xqspi->is_inst = 0; ++ } ++ ++ if (status != transfer->len) { ++ if (status > 0) ++ status = -EMSGSIZE; ++ break; ++ } ++ msg->actual_length += status; ++ status = 0; ++ ++ if (transfer->delay_usecs) ++ udelay(transfer->delay_usecs); ++ ++ if (cs_change) ++ /* Deselect the chip */ ++ xqspips_chipselect(qspi, 0); ++ ++ if (transfer->transfer_list.next == &msg->transfers) ++ break; ++ } ++ ++ msg->status = status; ++ msg->complete(msg->context); ++ ++ xqspips_setup_transfer(qspi, NULL); ++ ++ if (!(status == 0 && cs_change)) ++ xqspips_chipselect(qspi, 0); ++ ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ } ++ xqspi->dev_busy = 0; ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++} ++ ++/** ++ * xqspips_transfer - Add a new transfer request at the tail of work queue ++ * @qspi: Pointer to the spi_device structure ++ * @message: Pointer to the spi_transfer structure which provides information ++ * about next transfer parameters ++ * ++ * returns: 0 on success, -EINVAL on invalid input parameter and ++ * -ESHUTDOWN if queue is stopped by module unload function ++ */ ++static int xqspips_transfer(struct spi_device *qspi, ++ struct spi_message *message) ++{ ++ struct xqspips *xqspi = spi_master_get_devdata(qspi->master); ++ struct spi_transfer *transfer; ++ unsigned long flags; ++ ++ if (xqspi->queue_state == XQSPIPS_QUEUE_STOPPED) ++ return -ESHUTDOWN; ++ ++ message->actual_length = 0; ++ message->status = -EINPROGRESS; ++ ++ /* Check each transfer's parameters */ ++ list_for_each_entry(transfer, &message->transfers, transfer_list) { ++ if (!transfer->tx_buf && !transfer->rx_buf && transfer->len) ++ return -EINVAL; ++ /* QSPI controller supports only 32 bit transfers whereas higher ++ * layer drivers request 8 bit transfers. Re-visit at a later ++ * time */ ++ /* if (bits_per_word != 32) ++ return -EINVAL; */ ++ } ++ ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ list_add_tail(&message->queue, &xqspi->queue); ++ if (!xqspi->dev_busy) ++ queue_work(xqspi->workqueue, &xqspi->work); ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * xqspips_start_queue - Starts the queue of the QSPI driver ++ * @xqspi: Pointer to the xqspips structure ++ * ++ * returns: 0 on success and -EBUSY if queue is already running or device is ++ * busy ++ */ ++static inline int xqspips_start_queue(struct xqspips *xqspi) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ ++ if (xqspi->queue_state == XQSPIPS_QUEUE_RUNNING || xqspi->dev_busy) { ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ return -EBUSY; ++ } ++ ++ xqspi->queue_state = XQSPIPS_QUEUE_RUNNING; ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * xqspips_stop_queue - Stops the queue of the QSPI driver ++ * @xqspi: Pointer to the xqspips structure ++ * ++ * This function waits till queue is empty and then stops the queue. ++ * Maximum time out is set to 5 seconds. ++ * ++ * returns: 0 on success and -EBUSY if queue is not empty or device is busy ++ */ ++static inline int xqspips_stop_queue(struct xqspips *xqspi) ++{ ++ unsigned long flags; ++ unsigned limit = 500; ++ int ret = 0; ++ ++ if (xqspi->queue_state != XQSPIPS_QUEUE_RUNNING) ++ return ret; ++ ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ ++ while ((!list_empty(&xqspi->queue) || xqspi->dev_busy) && limit--) { ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ msleep(10); ++ spin_lock_irqsave(&xqspi->trans_queue_lock, flags); ++ } ++ ++ if (!list_empty(&xqspi->queue) || xqspi->dev_busy) ++ ret = -EBUSY; ++ ++ if (ret == 0) ++ xqspi->queue_state = XQSPIPS_QUEUE_STOPPED; ++ ++ spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); ++ ++ return ret; ++} ++ ++/** ++ * xqspips_destroy_queue - Destroys the queue of the QSPI driver ++ * @xqspi: Pointer to the xqspips structure ++ * ++ * returns: 0 on success and error value on failure ++ */ ++static inline int xqspips_destroy_queue(struct xqspips *xqspi) ++{ ++ int ret; ++ ++ ret = xqspips_stop_queue(xqspi); ++ if (ret != 0) ++ return ret; ++ ++ destroy_workqueue(xqspi->workqueue); ++ ++ return 0; ++} ++ ++static int xqspips_clk_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* if a rate change is announced we need to check whether we can ++ * maintain the current frequency by changing the clock ++ * dividers. And we may have to suspend operation and return ++ * after the rate change or its abort ++ */ ++ return NOTIFY_OK; ++ case POST_RATE_CHANGE: ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * xqspips_suspend - Suspend method for the QSPI driver ++ * @_dev: Address of the platform_device structure ++ * ++ * This function stops the QSPI driver queue and disables the QSPI controller ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xqspips_suspend(struct device *_dev) ++{ ++ struct platform_device *pdev = container_of(_dev, ++ struct platform_device, dev); ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xqspips *xqspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = xqspips_stop_queue(xqspi); ++ if (ret != 0) ++ return ret; ++ ++ xqspips_write(xqspi->regs + XQSPIPS_ENABLE_OFFSET, ++ ~XQSPIPS_ENABLE_ENABLE_MASK); ++ ++ clk_disable(xqspi->devclk); ++ clk_disable(xqspi->aperclk); ++ ++ dev_dbg(&pdev->dev, "suspend succeeded\n"); ++ return 0; ++} ++ ++/** ++ * xqspips_resume - Resume method for the QSPI driver ++ * @dev: Address of the platform_device structure ++ * ++ * The function starts the QSPI driver queue and initializes the QSPI controller ++ * ++ * returns: 0 on success and error value on error ++ */ ++static int xqspips_resume(struct device *dev) ++{ ++ struct platform_device *pdev = container_of(dev, ++ struct platform_device, dev); ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xqspips *xqspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = clk_enable(xqspi->aperclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable APER clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_enable(xqspi->devclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable device clock.\n"); ++ clk_disable(xqspi->aperclk); ++ return ret; ++ } ++ ++ xqspips_init_hw(xqspi->regs, xqspi->is_dual); ++ ++ ret = xqspips_start_queue(xqspi); ++ if (ret != 0) { ++ dev_err(&pdev->dev, "problem starting queue (%d)\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(&pdev->dev, "resume succeeded\n"); ++ return 0; ++} ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(xqspips_dev_pm_ops, xqspips_suspend, xqspips_resume); ++ ++/** ++ * xqspips_probe - Probe method for the QSPI driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function initializes the driver data structures and the hardware. ++ * ++ * returns: 0 on success and error value on failure ++ */ ++static int xqspips_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct spi_master *master; ++ struct xqspips *xqspi; ++ struct resource *res; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); ++ if (master == NULL) ++ return -ENOMEM; ++ ++ xqspi = spi_master_get_devdata(master); ++ master->dev.of_node = pdev->dev.of_node; ++ platform_set_drvdata(pdev, master); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ xqspi->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(xqspi->regs)) { ++ ret = PTR_ERR(xqspi->regs); ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ goto remove_master; ++ } ++ ++ xqspi->irq = platform_get_irq(pdev, 0); ++ if (xqspi->irq < 0) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "irq resource not found\n"); ++ goto remove_master; ++ } ++ ret = devm_request_irq(&pdev->dev, xqspi->irq, xqspips_irq, ++ 0, pdev->name, xqspi); ++ if (ret != 0) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "request_irq failed\n"); ++ goto remove_master; ++ } ++ ++ if (of_property_read_u32(pdev->dev.of_node, "is-dual", &xqspi->is_dual)) ++ dev_warn(&pdev->dev, "couldn't determine configuration info " ++ "about dual memories. defaulting to single memory\n"); ++ ++ xqspi->aperclk = clk_get(&pdev->dev, "aper_clk"); ++ if (IS_ERR(xqspi->aperclk)) { ++ dev_err(&pdev->dev, "aper_clk clock not found.\n"); ++ ret = PTR_ERR(xqspi->aperclk); ++ goto remove_master; ++ } ++ ++ xqspi->devclk = clk_get(&pdev->dev, "ref_clk"); ++ if (IS_ERR(xqspi->devclk)) { ++ dev_err(&pdev->dev, "ref_clk clock not found.\n"); ++ ret = PTR_ERR(xqspi->devclk); ++ goto clk_put_aper; ++ } ++ ++ ret = clk_prepare_enable(xqspi->aperclk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto clk_put; ++ } ++ ++ ret = clk_prepare_enable(xqspi->devclk); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to enable device clock.\n"); ++ goto clk_dis_aper; ++ } ++ ++ xqspi->clk_rate_change_nb.notifier_call = xqspips_clk_notifier_cb; ++ xqspi->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(xqspi->devclk, &xqspi->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ ++ ++ /* QSPI controller initializations */ ++ xqspips_init_hw(xqspi->regs, xqspi->is_dual); ++ ++ init_completion(&xqspi->done); ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "num-chip-select", ++ (u32 *)&master->num_chipselect); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "couldn't determine num-chip-select\n"); ++ goto clk_unreg_notif; ++ } ++ ++ master->setup = xqspips_setup; ++ master->transfer = xqspips_transfer; ++ master->flags = SPI_MASTER_QUAD_MODE; ++ ++ xqspi->speed_hz = clk_get_rate(xqspi->devclk) / 2; ++ ++ xqspi->dev_busy = 0; ++ ++ INIT_LIST_HEAD(&xqspi->queue); ++ spin_lock_init(&xqspi->trans_queue_lock); ++ spin_lock_init(&xqspi->config_reg_lock); ++ ++ xqspi->queue_state = XQSPIPS_QUEUE_STOPPED; ++ xqspi->dev_busy = 0; ++ ++ INIT_WORK(&xqspi->work, xqspips_work_queue); ++ xqspi->workqueue = ++ create_singlethread_workqueue(dev_name(&pdev->dev)); ++ if (!xqspi->workqueue) { ++ ret = -ENOMEM; ++ dev_err(&pdev->dev, "problem initializing queue\n"); ++ goto clk_unreg_notif; ++ } ++ ++ ret = xqspips_start_queue(xqspi); ++ if (ret != 0) { ++ dev_err(&pdev->dev, "problem starting queue\n"); ++ goto remove_queue; ++ } ++ ++ ret = spi_register_master(master); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_register_master failed\n"); ++ goto remove_queue; ++ } ++ ++ dev_info(&pdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", res->start, ++ (u32 __force)xqspi->regs, xqspi->irq); ++ ++ return ret; ++ ++remove_queue: ++ (void)xqspips_destroy_queue(xqspi); ++clk_unreg_notif: ++ clk_notifier_unregister(xqspi->devclk, &xqspi->clk_rate_change_nb); ++ clk_disable_unprepare(xqspi->devclk); ++clk_dis_aper: ++ clk_disable_unprepare(xqspi->aperclk); ++clk_put: ++ clk_put(xqspi->devclk); ++clk_put_aper: ++ clk_put(xqspi->aperclk); ++remove_master: ++ spi_master_put(master); ++ return ret; ++} ++ ++/** ++ * xqspips_remove - Remove method for the QSPI driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function is called if a device is physically removed from the system or ++ * if the driver module is being unloaded. It frees all resources allocated to ++ * the device. ++ * ++ * returns: 0 on success and error value on failure ++ */ ++static int xqspips_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct xqspips *xqspi = spi_master_get_devdata(master); ++ int ret = 0; ++ ++ ret = xqspips_destroy_queue(xqspi); ++ if (ret != 0) ++ return ret; ++ ++ xqspips_write(xqspi->regs + XQSPIPS_ENABLE_OFFSET, ++ ~XQSPIPS_ENABLE_ENABLE_MASK); ++ ++ clk_notifier_unregister(xqspi->devclk, &xqspi->clk_rate_change_nb); ++ clk_disable_unprepare(xqspi->devclk); ++ clk_disable_unprepare(xqspi->aperclk); ++ clk_put(xqspi->devclk); ++ clk_put(xqspi->aperclk); ++ ++ ++ spi_unregister_master(master); ++ spi_master_put(master); ++ ++ ++ dev_dbg(&pdev->dev, "remove succeeded\n"); ++ return 0; ++} ++ ++/* Work with hotplug and coldplug */ ++MODULE_ALIAS("platform:" DRIVER_NAME); ++ ++static struct of_device_id xqspips_of_match[] = { ++ { .compatible = "xlnx,ps7-qspi-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xqspips_of_match); ++ ++/* ++ * xqspips_driver - This structure defines the QSPI platform driver ++ */ ++static struct platform_driver xqspips_driver = { ++ .probe = xqspips_probe, ++ .remove = xqspips_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = xqspips_of_match, ++ .pm = &xqspips_dev_pm_ops, ++ }, ++}; ++ ++module_platform_driver(xqspips_driver); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx PS QSPI driver"); ++MODULE_LICENSE("GPL"); +--- a/drivers/spi/spi-xilinx.c ++++ b/drivers/spi/spi-xilinx.c +@@ -82,7 +82,7 @@ struct xilinx_spi { + struct completion done; + void __iomem *regs; /* virt. address of the control registers */ + +- int irq; ++ int irq; + + u8 *rx_ptr; /* pointer in the Tx buffer */ + const u8 *tx_ptr; /* pointer in the Rx buffer */ +@@ -232,6 +232,21 @@ static int xilinx_spi_setup_transfer(str + return 0; + } + ++static int xilinx_spi_setup(struct spi_device *spi) ++{ ++ /* always return 0, we can not check the number of bits. ++ * There are cases when SPI setup is called before any driver is ++ * there, in that case the SPI core defaults to 8 bits, which we ++ * do not support in some cases. But if we return an error, the ++ * SPI device would not be registered and no driver can get hold of it ++ * When the driver is there, it will call SPI setup again with the ++ * correct number of bits per transfer. ++ * If a driver setups with the wrong bit number, it will fail when ++ * it tries to do a transfer ++ */ ++ return 0; ++} ++ + static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) + { + u8 sr; +@@ -300,7 +315,7 @@ static int xilinx_spi_txrx_bufs(struct s + } + + /* See if there is more data to send */ +- if (xspi->remaining_bytes <= 0) ++ if (!xspi->remaining_bytes > 0) + break; + } + +@@ -349,7 +364,7 @@ static int xilinx_spi_probe(struct platf + u32 tmp; + u8 i; + +- pdata = dev_get_platdata(&pdev->dev); ++ pdata = pdev->dev.platform_data; + if (pdata) { + num_cs = pdata->num_chipselect; + bits_per_word = pdata->bits_per_word; +@@ -368,14 +383,18 @@ static int xilinx_spi_probe(struct platf + if (!master) + return -ENODEV; + ++ /* clear the dma_mask, to try to disable use of dma */ ++ master->dev.dma_mask = 0; ++ + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA; + + xspi = spi_master_get_devdata(master); +- xspi->bitbang.master = master; ++ xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.chipselect = xilinx_spi_chipselect; + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; ++ xspi->bitbang.master->setup = xilinx_spi_setup; + init_completion(&xspi->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -314,6 +314,8 @@ struct spi_master { + #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ + #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ + #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ ++#define SPI_MASTER_U_PAGE BIT(3) /* select upper flash */ ++#define SPI_MASTER_QUAD_MODE BIT(4) /* support quad mode */ + + /* lock and mutex for SPI bus locking */ + spinlock_t bus_lock_spinlock; diff --git a/patches.zynq/0007-mtd-xilinx-merge-nand-flash-support-from-xilinx-repo.patch b/patches.zynq/0007-mtd-xilinx-merge-nand-flash-support-from-xilinx-repo.patch new file mode 100644 index 0000000..2f1c62f --- /dev/null +++ b/patches.zynq/0007-mtd-xilinx-merge-nand-flash-support-from-xilinx-repo.patch @@ -0,0 +1,1876 @@ +From 74eaf4cb9dfdfc0deab5c9a1b8e3de82916ca94b Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Tue, 24 Dec 2013 09:18:18 +0900 +Subject: mtd: xilinx: merge nand flash support from xilinx repository + +This merges support for the Xilinx nand flash support from the +Xilinx repository (commit efc27505715e64526653f35274717c0fc56491e3 +in master branch). It has been tested by reading the QSPI flash +in the Zynq 702 board. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/mtd/devices/m25p80.c | 417 +++++++++++++-- + drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_base.c | 28 + + drivers/mtd/nand/xilinx_nandps.c | 1064 +++++++++++++++++++++++++++++++++++++++ + 5 files changed, 1469 insertions(+), 48 deletions(-) + create mode 100644 drivers/mtd/nand/xilinx_nandps.c + +--- a/drivers/mtd/devices/m25p80.c ++++ b/drivers/mtd/devices/m25p80.c +@@ -41,12 +41,16 @@ + #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ + #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ + #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ ++#define OPCODE_QUAD_READ 0x6b /* Quad read command */ + #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ ++#define OPCODE_QPP 0x32 /* Quad page program */ + #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ + #define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ + #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ + #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ + #define OPCODE_RDID 0x9f /* Read JEDEC ID */ ++#define OPCODE_RDFSR 0x70 /* Read Flag Status Register */ ++#define OPCODE_WREAR 0xc5 /* Write Extended Address Register */ + + /* Used for SST flashes only. */ + #define OPCODE_BP 0x02 /* Byte program */ +@@ -59,6 +63,7 @@ + + /* Used for Spansion flashes only. */ + #define OPCODE_BRWR 0x17 /* Bank register write */ ++#define OPCODE_BRRD 0x16 /* Bank register read */ + + /* Status Register bits. */ + #define SR_WIP 1 /* Write in progress */ +@@ -69,8 +74,11 @@ + #define SR_BP2 0x10 /* Block protect 2 */ + #define SR_SRWD 0x80 /* SR write protect */ + ++/* Flag Status Register bits. */ ++#define FSR_RDY 0x80 /* Ready/Busy program erase ++ controller */ + /* Define max times to check status register before we give up. */ +-#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ ++#define MAX_READY_WAIT_JIFFIES (480 * HZ) /* N25Q specs 480s max chip erase */ + #define MAX_CMD_SIZE 5 + + #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) +@@ -86,6 +94,15 @@ struct m25p { + u8 erase_opcode; + u8 *command; + bool fast_read; ++ u16 curbank; ++ u32 jedec_id; ++ bool check_fsr; ++ bool shift; ++ bool isparallel; ++ bool isstacked; ++ u8 read_opcode; ++ u8 prog_opcode; ++ u8 dummycount; + }; + + static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) +@@ -100,21 +117,19 @@ static inline struct m25p *mtd_to_m25p(s + */ + + /* +- * Read the status register, returning its value in the location +- * Return the status register value. ++ * Read register, returning its value in the location + * Returns negative if error occurred. + */ +-static int read_sr(struct m25p *flash) ++static inline int read_spi_reg(struct m25p *flash, u8 code, const char *name) + { + ssize_t retval; +- u8 code = OPCODE_RDSR; + u8 val; + + retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); + + if (retval < 0) { +- dev_err(&flash->spi->dev, "error %d reading SR\n", +- (int) retval); ++ dev_err(&flash->spi->dev, "error %d reading %s\n", ++ (int) retval, name); + return retval; + } + +@@ -122,6 +137,26 @@ static int read_sr(struct m25p *flash) + } + + /* ++ * Read flag status register, returning its value in the location ++ * Return flag status register value. ++ * Returns negative if error occurred. ++ */ ++static int read_fsr(struct m25p *flash) ++{ ++ return read_spi_reg(flash, OPCODE_RDFSR, "FSR"); ++} ++ ++/* ++ * Read the status register, returning its value in the location ++ * Return the status register value. ++ * Returns negative if error occurred. ++ */ ++static int read_sr(struct m25p *flash) ++{ ++ return read_spi_reg(flash, OPCODE_RDSR, "SR"); ++} ++ ++/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +@@ -159,6 +194,9 @@ static inline int write_disable(struct m + */ + static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) + { ++ int ret; ++ u8 val; ++ + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: +@@ -168,7 +206,19 @@ static inline int set_4byte(struct m25p + /* Spansion style */ + flash->command[0] = OPCODE_BRWR; + flash->command[1] = enable << 7; +- return spi_write(flash->spi, flash->command, 2); ++ ret = spi_write(flash->spi, flash->command, 2); ++ ++ /* verify the 4 byte mode is enabled */ ++ flash->command[0] = OPCODE_BRRD; ++ spi_write_then_read(flash->spi, flash->command, 1, &val, 1); ++ if (val != enable << 7) { ++ dev_warn(&flash->spi->dev, ++ "fallback to 3-byte address mode\n"); ++ dev_warn(&flash->spi->dev, ++ "maximum accessible size is 16MB\n"); ++ flash->addr_width = 3; ++ } ++ return ret; + } + } + +@@ -179,15 +229,21 @@ static inline int set_4byte(struct m25p + static int wait_till_ready(struct m25p *flash) + { + unsigned long deadline; +- int sr; ++ int sr, fsr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + if ((sr = read_sr(flash)) < 0) + break; +- else if (!(sr & SR_WIP)) ++ else if (!(sr & SR_WIP)) { ++ if (flash->check_fsr) { ++ fsr = read_fsr(flash); ++ if (!(fsr & FSR_RDY)) ++ return 1; ++ } + return 0; ++ } + + cond_resched(); + +@@ -197,6 +253,48 @@ static int wait_till_ready(struct m25p * + } + + /* ++ * Update Extended Address/bank selection Register. ++ * Call with flash->lock locked. ++ */ ++static int write_ear(struct m25p *flash, u32 addr) ++{ ++ u8 ear; ++ int ret; ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) ++ return 1; ++ ++ if (flash->mtd.size <= (0x1000000) << flash->shift) ++ return 0; ++ ++ addr = addr % (u32) flash->mtd.size; ++ ear = addr >> 24; ++ ++ if ((!flash->isstacked) && (ear == flash->curbank)) ++ return 0; ++ ++ if (flash->isstacked && (flash->mtd.size <= 0x2000000)) ++ return 0; ++ ++ if (JEDEC_MFR(flash->jedec_id) == 0x01) ++ flash->command[0] = OPCODE_BRWR; ++ if (JEDEC_MFR(flash->jedec_id) == 0x20) { ++ write_enable(flash); ++ flash->command[0] = OPCODE_WREAR; ++ } ++ flash->command[1] = ear; ++ ++ ret = spi_write(flash->spi, flash->command, 2); ++ if (ret) ++ return ret; ++ ++ flash->curbank = ear; ++ ++ return 0; ++} ++ ++/* + * Erase the whole flash memory + * + * Returns 0 if successful, non-zero otherwise. +@@ -210,6 +308,9 @@ static int erase_chip(struct m25p *flash + if (wait_till_ready(flash)) + return 1; + ++ if (flash->isstacked) ++ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE; ++ + /* Send write enable, then erase commands. */ + write_enable(flash); + +@@ -218,16 +319,32 @@ static int erase_chip(struct m25p *flash + + spi_write(flash->spi, flash->command, 1); + ++ if (flash->isstacked) { ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) ++ return 1; ++ ++ flash->spi->master->flags |= SPI_MASTER_U_PAGE; ++ ++ /* Send write enable, then erase commands. */ ++ write_enable(flash); ++ ++ /* Set up command buffer. */ ++ flash->command[0] = OPCODE_CHIP_ERASE; ++ ++ spi_write(flash->spi, flash->command, 1); ++ } ++ + return 0; + } + + static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) + { ++ int i; ++ + /* opcode is in cmd[0] */ +- cmd[1] = addr >> (flash->addr_width * 8 - 8); +- cmd[2] = addr >> (flash->addr_width * 8 - 16); +- cmd[3] = addr >> (flash->addr_width * 8 - 24); +- cmd[4] = addr >> (flash->addr_width * 8 - 32); ++ for (i = 1; i <= flash->addr_width; i++) ++ cmd[i] = addr >> (flash->addr_width * 8 - i * 8); + } + + static int m25p_cmdsz(struct m25p *flash) +@@ -250,6 +367,10 @@ static int erase_sector(struct m25p *fla + if (wait_till_ready(flash)) + return 1; + ++ /* update Extended Address Register */ ++ if (write_ear(flash, offset)) ++ return 1; ++ + /* Send write enable, then erase commands. */ + write_enable(flash); + +@@ -275,7 +396,7 @@ static int erase_sector(struct m25p *fla + static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) + { + struct m25p *flash = mtd_to_m25p(mtd); +- u32 addr,len; ++ u32 addr, len, offset; + uint32_t rem; + + pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), +@@ -307,7 +428,19 @@ static int m25p80_erase(struct mtd_info + /* "sector"-at-a-time erase */ + } else { + while (len) { +- if (erase_sector(flash, addr)) { ++ offset = addr; ++ if (flash->isparallel == 1) ++ offset /= 2; ++ if (flash->isstacked == 1) { ++ if (offset >= (flash->mtd.size / 2)) { ++ offset = offset - (flash->mtd.size / 2); ++ flash->spi->master->flags |= ++ SPI_MASTER_U_PAGE; ++ } else ++ flash->spi->master->flags &= ++ ~SPI_MASTER_U_PAGE; ++ } ++ if (erase_sector(flash, offset)) { + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&flash->lock); + return -EIO; +@@ -336,7 +469,6 @@ static int m25p80_read(struct mtd_info * + struct m25p *flash = mtd_to_m25p(mtd); + struct spi_transfer t[2]; + struct spi_message m; +- uint8_t opcode; + + pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), + __func__, (u32)from, len); +@@ -349,21 +481,17 @@ static int m25p80_read(struct mtd_info * + * Should add 1 byte DUMMY_BYTE. + */ + t[0].tx_buf = flash->command; +- t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); ++ t[0].len = m25p_cmdsz(flash) + flash->dummycount; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + +- mutex_lock(&flash->lock); +- + /* Wait till previous write/erase is done. */ +- if (wait_till_ready(flash)) { ++ if (wait_till_ready(flash)) + /* REVISIT status return?? */ +- mutex_unlock(&flash->lock); + return 1; +- } + + /* FIXME switch to OPCODE_FAST_READ. It's required for higher + * clocks; and at this writing, every chip this driver handles +@@ -371,17 +499,65 @@ static int m25p80_read(struct mtd_info * + */ + + /* Set up the write data buffer. */ +- opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; +- flash->command[0] = opcode; ++ flash->command[0] = flash->read_opcode; + m25p_addr2cmd(flash, from, flash->command); + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - m25p_cmdsz(flash) - +- (flash->fast_read ? 1 : 0); ++ flash->dummycount; + +- mutex_unlock(&flash->lock); ++ return 0; ++} ++ ++static int m25p80_read_ext(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct m25p *flash = mtd_to_m25p(mtd); ++ u32 addr = from; ++ u32 offset = from; ++ u32 read_len = 0; ++ u32 actual_len = 0; ++ u32 read_count = 0; ++ u32 rem_bank_len = 0; ++ u8 bank = 0; ++ ++#define OFFSET_16_MB 0x1000000 ++ ++ mutex_lock(&flash->lock); ++ ++ while (len) { ++ bank = addr / (OFFSET_16_MB << flash->shift); ++ rem_bank_len = ((OFFSET_16_MB << flash->shift) * (bank + 1)) - ++ addr; ++ offset = addr; ++ if (flash->isparallel == 1) ++ offset /= 2; ++ if (flash->isstacked == 1) { ++ if (offset >= (flash->mtd.size / 2)) { ++ offset = offset - (flash->mtd.size / 2); ++ flash->spi->master->flags |= SPI_MASTER_U_PAGE; ++ } else { ++ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE; ++ } ++ } ++ write_ear(flash, offset); ++ if (len < rem_bank_len) ++ read_len = len; ++ else ++ read_len = rem_bank_len; ++ ++ m25p80_read(mtd, offset, read_len, &actual_len, buf); + ++ addr += actual_len; ++ len -= actual_len; ++ buf += actual_len; ++ read_count += actual_len; ++ } ++ ++ *retlen = read_count; ++ ++ mutex_unlock(&flash->lock); + return 0; + } + +@@ -411,19 +587,15 @@ static int m25p80_write(struct mtd_info + t[1].tx_buf = buf; + spi_message_add_tail(&t[1], &m); + +- mutex_lock(&flash->lock); +- + /* Wait until finished previous write command. */ +- if (wait_till_ready(flash)) { +- mutex_unlock(&flash->lock); ++ if (wait_till_ready(flash)) + return 1; +- } + + write_enable(flash); + + /* Set up the opcode in the write buffer. */ +- flash->command[0] = OPCODE_PP; +- m25p_addr2cmd(flash, to, flash->command); ++ flash->command[0] = flash->prog_opcode; ++ m25p_addr2cmd(flash, (to >> flash->shift), flash->command); + + page_offset = to & (flash->page_size - 1); + +@@ -452,12 +624,14 @@ static int m25p80_write(struct mtd_info + page_size = flash->page_size; + + /* write the next page to flash */ +- m25p_addr2cmd(flash, to + i, flash->command); ++ m25p_addr2cmd(flash, ((to + i) >> flash->shift), ++ flash->command); + + t[1].tx_buf = buf + i; + t[1].len = page_size; + +- wait_till_ready(flash); ++ if (wait_till_ready(flash)) ++ return 1; + + write_enable(flash); + +@@ -467,8 +641,55 @@ static int m25p80_write(struct mtd_info + } + } + +- mutex_unlock(&flash->lock); ++ return 0; ++} ++ ++static int m25p80_write_ext(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct m25p *flash = mtd_to_m25p(mtd); ++ u32 addr = to; ++ u32 offset = to; ++ u32 write_len = 0; ++ u32 actual_len = 0; ++ u32 write_count = 0; ++ u32 rem_bank_len = 0; ++ u8 bank = 0; ++ ++#define OFFSET_16_MB 0x1000000 ++ ++ mutex_lock(&flash->lock); ++ while (len) { ++ bank = addr / (OFFSET_16_MB << flash->shift); ++ rem_bank_len = ((OFFSET_16_MB << flash->shift) * (bank + 1)) - ++ addr; ++ offset = addr; ++ ++ if (flash->isstacked == 1) { ++ if (offset >= (flash->mtd.size / 2)) { ++ offset = offset - (flash->mtd.size / 2); ++ flash->spi->master->flags |= SPI_MASTER_U_PAGE; ++ } else { ++ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE; ++ } ++ } ++ write_ear(flash, (offset >> flash->shift)); ++ if (len < rem_bank_len) ++ write_len = len; ++ else ++ write_len = rem_bank_len; + ++ m25p80_write(mtd, offset, write_len, &actual_len, buf); ++ ++ addr += actual_len; ++ len -= actual_len; ++ buf += actual_len; ++ write_count += actual_len; ++ } ++ ++ *retlen = write_count; ++ ++ mutex_unlock(&flash->lock); + return 0; + } + +@@ -682,6 +903,8 @@ struct flash_info { + #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ + #define M25P_NO_ERASE 0x02 /* No erase command needed */ + #define SST_WRITE 0x04 /* use SST byte programming */ ++#define SECT_32K 0x10 /* OPCODE_BE_32K */ ++#define E_FSR 0x08 /* Flag SR exists for flash */ + }; + + #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ +@@ -761,6 +984,15 @@ static const struct spi_device_id m25p_i + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, ++ /* Numonyx flash n25q128 - FIXME check the name */ ++ { "n25q128", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, ++ { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, E_FSR) }, ++ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, E_FSR) }, ++ { "n25q256a13", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | E_FSR) }, ++ { "n25q256a11", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | E_FSR) }, ++ { "n25q512a13", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | E_FSR) }, ++ { "n25q512a11", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | E_FSR) }, ++ { "n25q00aa13", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | E_FSR) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). +@@ -781,7 +1013,11 @@ static const struct spi_device_id m25p_i + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, +- { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, ++ /* s25fl064k supports 4KiB, 32KiB and 64KiB sectors erase size. */ ++ /* To support JFFS2, the minimum erase size is 8KiB(>4KiB). */ ++ /* And thus, the sector size of s25fl064k is set to 32KiB for */ ++ /* JFFS2 support. */ ++ { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_32K) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, +@@ -789,10 +1025,11 @@ static const struct spi_device_id m25p_i + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, +- { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, +- { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, +- { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, +- { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, ++ { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, ++ { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, ++ { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, ++ { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, ++ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, +@@ -839,7 +1076,12 @@ static const struct spi_device_id m25p_i + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, +- { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, ++ /* Winbond -- w25q "blocks" are 64K, "sectors" are 32KiB */ ++ /* w25q64 supports 4KiB, 32KiB and 64KiB sectors erase size. */ ++ /* To support JFFS2, the minimum erase size is 8KiB(>4KiB). */ ++ /* And thus, the sector size of w25q64 is set to 32KiB for */ ++ /* JFFS2 support. */ ++ { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_32K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, +@@ -995,8 +1237,54 @@ static int m25p_probe(struct spi_device + flash->mtd.writesize = 1; + flash->mtd.flags = MTD_CAP_NORFLASH; + flash->mtd.size = info->sector_size * info->n_sectors; ++ ++ { ++#ifdef CONFIG_OF ++ const char *comp_str; ++ u32 is_dual; ++ np = of_get_next_parent(spi->dev.of_node); ++ of_property_read_string(np, "compatible", &comp_str); ++ if (!strcmp(comp_str, "xlnx,ps7-qspi-1.00.a")) { ++ if (of_property_read_u32(np, "is-dual", &is_dual) < 0) { ++ /* Default to single if prop not defined */ ++ flash->shift = 0; ++ flash->isstacked = 0; ++ flash->isparallel = 0; ++ } else { ++ if (is_dual == 1) { ++ /* dual parallel */ ++ flash->shift = 1; ++ info->sector_size <<= flash->shift; ++ info->page_size <<= flash->shift; ++ flash->mtd.size <<= flash->shift; ++ flash->isparallel = 1; ++ flash->isstacked = 0; ++ } else { ++#ifdef CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED ++ /* dual stacked */ ++ flash->shift = 0; ++ flash->mtd.size <<= 1; ++ flash->isstacked = 1; ++ flash->isparallel = 0; ++#else ++ /* single */ ++ flash->shift = 0; ++ flash->isstacked = 0; ++ flash->isparallel = 0; ++#endif ++ } ++ } ++ } ++#else ++ /* Default to single */ ++ flash->shift = 0; ++ flash->isstacked = 0; ++ flash->isparallel = 0; ++#endif ++ } ++ + flash->mtd._erase = m25p80_erase; +- flash->mtd._read = m25p80_read; ++ flash->mtd._read = m25p80_read_ext; + + /* flash protection support for STmicro chips */ + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { +@@ -1008,20 +1296,31 @@ static int m25p_probe(struct spi_device + if (info->flags & SST_WRITE) + flash->mtd._write = sst_write; + else +- flash->mtd._write = m25p80_write; ++ flash->mtd._write = m25p80_write_ext; + + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + flash->erase_opcode = OPCODE_BE_4K; +- flash->mtd.erasesize = 4096; ++ flash->mtd.erasesize = 4096 << flash->shift; ++ } else if (info->flags & SECT_32K) { ++ flash->erase_opcode = OPCODE_BE_32K; ++ flash->mtd.erasesize = 32768 << flash->shift; + } else { + flash->erase_opcode = OPCODE_SE; + flash->mtd.erasesize = info->sector_size; + } + ++ flash->read_opcode = OPCODE_NORM_READ; ++ flash->prog_opcode = OPCODE_PP; ++ flash->dummycount = 0; ++ + if (info->flags & M25P_NO_ERASE) + flash->mtd.flags |= MTD_NO_ERASE; + ++ if (info->flags & E_FSR) ++ flash->check_fsr = 1; ++ ++ flash->jedec_id = info->jedec_id; + ppdata.of_node = spi->dev.of_node; + flash->mtd.dev.parent = &spi->dev; + flash->page_size = info->page_size; +@@ -1036,14 +1335,36 @@ static int m25p_probe(struct spi_device + #ifdef CONFIG_M25PXX_USE_FAST_READ + flash->fast_read = true; + #endif ++ if (flash->fast_read) { ++ flash->read_opcode = OPCODE_FAST_READ; ++ flash->dummycount = 1; ++ } ++ ++ if (spi->master->flags & SPI_MASTER_QUAD_MODE) { ++ flash->read_opcode = OPCODE_QUAD_READ; ++ flash->prog_opcode = OPCODE_QPP; ++ flash->dummycount = 1; ++ } + + if (info->addr_width) + flash->addr_width = info->addr_width; + else { + /* enable 4-byte addressing if the device exceeds 16MiB */ + if (flash->mtd.size > 0x1000000) { +- flash->addr_width = 4; +- set_4byte(flash, info->jedec_id, 1); ++#ifdef CONFIG_OF ++ const char *comp_str; ++ np = of_get_next_parent(spi->dev.of_node); ++ of_property_read_string(np, "compatible", &comp_str); ++ if (!strcmp(comp_str, "xlnx,ps7-qspi-1.00.a")) { ++ flash->addr_width = 3; ++ set_4byte(flash, info->jedec_id, 0); ++ } else { ++#endif ++ flash->addr_width = 4; ++ set_4byte(flash, info->jedec_id, 1); ++#ifdef CONFIG_OF ++ } ++#endif + } else + flash->addr_width = 3; + } +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -523,6 +523,13 @@ config MTD_NAND_NUC900 + This enables the driver for the NAND Flash on evaluation board based + on w90p910 / NUC9xx. + ++config MTD_NAND_XILINX_PS ++ tristate "Xilinx Zynq NAND flash driver" ++ depends on MTD_NAND && ARCH_ZYNQ ++ select ZYNQ_SMC ++ help ++ This enables access to the NAND flash device on Xilinx Zynq. ++ + config MTD_NAND_JZ4740 + tristate "Support for JZ4740 SoC NAND controller" + depends on MACH_JZ4740 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -44,6 +44,7 @@ obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand. + obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o + obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o + obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o ++obj-$(CONFIG_MTD_NAND_XILINX_PS) += xilinx_nandps.o + obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o + obj-$(CONFIG_MTD_NAND_RICOH) += r852.o + obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -814,6 +814,11 @@ static int nand_wait(struct mtd_info *mt + int status, state = chip->state; + unsigned long timeo = (state == FL_ERASING ? 400 : 20); + ++#if defined(ARCH_ZYNQ) && (CONFIG_HZ == 20) ++ /* Xilinx Zynq NAND work around for HZ=20 */ ++ timeo += 1; ++#endif ++ + led_trigger_event(nand_led_trigger, LED_FULL); + + /* +@@ -2858,11 +2863,18 @@ static int nand_flash_detect_onfi(struct + int i; + int val; + ++#ifdef CONFIG_MTD_NAND_XILINX_PS ++ uint8_t *buf; ++ unsigned int options; ++ int j; ++#endif ++ + /* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */ + if (chip->options & NAND_BUSWIDTH_16) { + pr_err("Trying ONFI probe in 16 bits mode, aborting !\n"); + return 0; + } ++ + /* Try ONFI for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); + if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || +@@ -2871,7 +2883,13 @@ static int nand_flash_detect_onfi(struct + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + for (i = 0; i < 3; i++) { ++#ifdef CONFIG_MTD_NAND_XILINX_PS ++ buf = (uint8_t *)p; ++ for (j = 0; j < 256; j++) ++ buf[j] = chip->read_byte(mtd); ++#else + chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); ++#endif + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + le16_to_cpu(p->crc)) { + pr_info("ONFI param page %d valid\n", i); +@@ -2924,7 +2942,17 @@ static int nand_flash_detect_onfi(struct + if (le16_to_cpu(p->features) & 1) + *busw = NAND_BUSWIDTH_16; + ++#ifdef CONFIG_MTD_NAND_XILINX_PS ++ /* Read the chip options before clearing the bits */ ++ options = chip->options; ++#endif ++ + pr_info("ONFI flash detected\n"); ++#ifdef CONFIG_MTD_NAND_XILINX_PS ++ /* set the bus width option */ ++ if (options & NAND_BUSWIDTH_16) ++ chip->options |= NAND_BUSWIDTH_16; ++#endif + return 1; + } + +--- /dev/null ++++ b/drivers/mtd/nand/xilinx_nandps.c +@@ -0,0 +1,1064 @@ ++/* ++ * Xilinx Zynq NAND Flash Controller Driver ++ * ++ * Copyright (C) 2009 Xilinx, Inc. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ * This driver is based on plat_nand.c and mxc_nand.c drivers ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define XNANDPS_DRIVER_NAME "xilinx_nandps" ++ ++/* NAND flash driver defines */ ++#define XNANDPS_CMD_PHASE 1 /* End command valid in command phase */ ++#define XNANDPS_DATA_PHASE 2 /* End command valid in data phase */ ++#define XNANDPS_ECC_SIZE 512 /* Size of data for ECC operation */ ++ ++/* Flash memory controller operating parameters */ ++ ++#define XNANDPS_ECC_CONFIG ((1 << 4) | /* ECC read at end of page */ \ ++ (0 << 5)) /* No Jumping */ ++ ++/* AXI Address definitions */ ++#define START_CMD_SHIFT 3 ++#define END_CMD_SHIFT 11 ++#define END_CMD_VALID_SHIFT 20 ++#define ADDR_CYCLES_SHIFT 21 ++#define CLEAR_CS_SHIFT 21 ++#define ECC_LAST_SHIFT 10 ++#define COMMAND_PHASE (0 << 19) ++#define DATA_PHASE (1 << 19) ++ ++#define XNANDPS_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */ ++#define XNANDPS_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */ ++ ++#define ONDIE_ECC_FEATURE_ADDR 0x90 ++ ++/* Macros for the NAND controller register read/write */ ++#define xnandps_write32(addr, val) __raw_writel((val), (addr)) ++ ++ ++/** ++ * struct xnandps_command_format - Defines NAND flash command format ++ * @start_cmd: First cycle command (Start command) ++ * @end_cmd: Second cycle command (Last command) ++ * @addr_cycles: Number of address cycles required to send the address ++ * @end_cmd_valid: The second cycle command is valid for cmd or data phase ++ **/ ++struct xnandps_command_format { ++ int start_cmd; ++ int end_cmd; ++ u8 addr_cycles; ++ u8 end_cmd_valid; ++}; ++ ++/** ++ * struct xnandps_info - Defines the NAND flash driver instance ++ * @chip: NAND chip information structure ++ * @mtd: MTD information structure ++ * @parts: Pointer to the mtd_partition structure ++ * @nand_base: Virtual address of the NAND flash device ++ * @end_cmd_pending: End command is pending ++ * @end_cmd: End command ++ **/ ++struct xnandps_info { ++ struct nand_chip chip; ++ struct mtd_info mtd; ++ struct mtd_partition *parts; ++ void __iomem *nand_base; ++ unsigned long end_cmd_pending; ++ unsigned long end_cmd; ++}; ++ ++/* ++ * The NAND flash operations command format ++ */ ++static const struct xnandps_command_format xnandps_commands[] = { ++ {NAND_CMD_READ0, NAND_CMD_READSTART, 5, XNANDPS_CMD_PHASE}, ++ {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, XNANDPS_CMD_PHASE}, ++ {NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE}, ++ {NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE}, ++ {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, XNANDPS_DATA_PHASE}, ++ {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE}, ++ {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, XNANDPS_CMD_PHASE}, ++ {NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE}, ++ {NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE}, ++ {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE}, ++ {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE}, ++ {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0}, ++ /* Add all the flash commands supported by the flash device and Linux */ ++ /* The cache program command is not supported by driver because driver ++ * cant differentiate between page program and cached page program from ++ * start command, these commands can be differentiated through end ++ * command, which doesn't fit in to the driver design. The cache program ++ * command is not supported by NAND subsystem also, look at 1612 line ++ * number (in nand_write_page function) of nand_base.c file. ++ * {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, XNANDPS_YES}, */ ++}; ++ ++/* Define default oob placement schemes for large and small page devices */ ++static struct nand_ecclayout nand_oob_16 = { ++ .eccbytes = 3, ++ .eccpos = {0, 1, 2}, ++ .oobfree = { ++ {.offset = 8, ++ . length = 8} } ++}; ++ ++static struct nand_ecclayout nand_oob_64 = { ++ .eccbytes = 12, ++ .eccpos = { ++ 52, 53, 54, 55, 56, 57, ++ 58, 59, 60, 61, 62, 63}, ++ .oobfree = { ++ {.offset = 2, ++ .length = 50} } ++}; ++ ++static struct nand_ecclayout ondie_nand_oob_64 = { ++ .eccbytes = 32, ++ ++ .eccpos = { ++ 8, 9, 10, 11, 12, 13, 14, 15, ++ 24, 25, 26, 27, 28, 29, 30, 31, ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 56, 57, 58, 59, 60, 61, 62, 63 ++ }, ++ ++ .oobfree = { ++ { .offset = 4, .length = 4 }, ++ { .offset = 20, .length = 4 }, ++ { .offset = 36, .length = 4 }, ++ { .offset = 52, .length = 4 } ++ } ++}; ++ ++/* Generic flash bbt decriptors ++*/ ++static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; ++static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 4, ++ .len = 4, ++ .veroffs = 20, ++ .maxblocks = 4, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 4, ++ .len = 4, ++ .veroffs = 20, ++ .maxblocks = 4, ++ .pattern = mirror_pattern ++}; ++ ++/** ++ * xnandps_calculate_hwecc - Calculate Hardware ECC ++ * @mtd: Pointer to the mtd_info structure ++ * @data: Pointer to the page data ++ * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored ++ * ++ * This function retrieves the Hardware ECC data from the controller and returns ++ * ECC data back to the MTD subsystem. ++ * ++ * returns: 0 on success or error value on failure ++ **/ ++static int ++xnandps_calculate_hwecc(struct mtd_info *mtd, const u8 *data, u8 *ecc_code) ++{ ++ u32 ecc_value = 0; ++ u8 ecc_reg, ecc_byte; ++ u32 ecc_status; ++ ++ /* Wait till the ECC operation is complete */ ++ while (xsmcps_ecc_is_busy()) ++ cpu_relax(); ++ ++ for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) { ++ /* Read ECC value for each block */ ++ ecc_value = xsmcps_get_ecc_val(ecc_reg); ++ ecc_status = (ecc_value >> 24) & 0xFF; ++ /* ECC value valid */ ++ if (ecc_status & 0x40) { ++ for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) { ++ /* Copy ECC bytes to MTD buffer */ ++ *ecc_code = ecc_value & 0xFF; ++ ecc_value = ecc_value >> 8; ++ ecc_code++; ++ } ++ } else { ++ /* TO DO */ ++ /* dev_warn(&pdev->dev, "pl350: ecc status failed\n"); ++ * */ ++ } ++ } ++ return 0; ++} ++ ++/** ++ * onehot - onehot function ++ * @value: value to check for onehot ++ * ++ * This function checks whether a value is onehot or not. ++ * onehot is if and only if onebit is set. ++ * ++ **/ ++static int onehot(unsigned short value) ++{ ++ return ((value & (value-1)) == 0); ++} ++ ++/** ++ * xnandps_correct_data - ECC correction function ++ * @mtd: Pointer to the mtd_info structure ++ * @buf: Pointer to the page data ++ * @read_ecc: Pointer to the ECC value read from spare data area ++ * @calc_ecc: Pointer to the calculated ECC value ++ * ++ * This function corrects the ECC single bit errors & detects 2-bit errors. ++ * ++ * returns: 0 if no ECC errors found ++ * 1 if single bit error found and corrected. ++ * -1 if multiple ECC errors found. ++ **/ ++static int xnandps_correct_data(struct mtd_info *mtd, unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc) ++{ ++ unsigned char bit_addr; ++ unsigned int byte_addr; ++ unsigned short ecc_odd, ecc_even; ++ unsigned short read_ecc_lower, read_ecc_upper; ++ unsigned short calc_ecc_lower, calc_ecc_upper; ++ ++ read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff; ++ read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; ++ ++ calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff; ++ calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; ++ ++ ecc_odd = read_ecc_lower ^ calc_ecc_lower; ++ ecc_even = read_ecc_upper ^ calc_ecc_upper; ++ ++ if ((ecc_odd == 0) && (ecc_even == 0)) ++ return 0; /* no error */ ++ ++ if (ecc_odd == (~ecc_even & 0xfff)) { ++ /* bits [11:3] of error code is byte offset */ ++ byte_addr = (ecc_odd >> 3) & 0x1ff; ++ /* bits [2:0] of error code is bit offset */ ++ bit_addr = ecc_odd & 0x7; ++ /* Toggling error bit */ ++ buf[byte_addr] ^= (1 << bit_addr); ++ return 1; ++ } ++ ++ if (onehot(ecc_odd | ecc_even) == 1) ++ return 1; /* one error in parity */ ++ ++ return -1; /* Uncorrectable error */ ++} ++ ++/** ++ * xnandps_read_oob - [REPLACABLE] the most common OOB data read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to read ++ */ ++static int xnandps_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ unsigned long data_width = 4; ++ unsigned long data_phase_addr = 0; ++ uint8_t *p; ++ ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ ++ p = chip->oob_poi; ++ chip->read_buf(mtd, p, (mtd->oobsize - data_width)); ++ p += (mtd->oobsize - data_width); ++ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; ++ chip->read_buf(mtd, p, data_width); ++ ++ return 0; ++} ++ ++/** ++ * xnandps_write_oob - [REPLACABLE] the most common OOB data write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to write ++ */ ++static int xnandps_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ int status = 0; ++ const uint8_t *buf = chip->oob_poi; ++ unsigned long data_width = 4; ++ unsigned long data_phase_addr = 0; ++ ++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); ++ ++ chip->write_buf(mtd, buf, (mtd->oobsize - data_width)); ++ buf += (mtd->oobsize - data_width); ++ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ data_phase_addr |= (1 << END_CMD_VALID_SHIFT); ++ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr; ++ chip->write_buf(mtd, buf, data_width); ++ ++ /* Send command to program the OOB data */ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/** ++ * xnandps_read_page_raw - [Intern] read raw page data without ecc ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @page: page number to read ++ * ++ */ ++static int xnandps_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ unsigned long data_width = 4; ++ unsigned long data_phase_addr = 0; ++ uint8_t *p; ++ ++ chip->read_buf(mtd, buf, mtd->writesize); ++ ++ p = chip->oob_poi; ++ chip->read_buf(mtd, p, (mtd->oobsize - data_width)); ++ p += (mtd->oobsize - data_width); ++ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; ++ ++ chip->read_buf(mtd, p, data_width); ++ return 0; ++} ++ ++/** ++ * xnandps_write_page_raw - [Intern] raw page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * ++ */ ++static int xnandps_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required) ++{ ++ unsigned long data_width = 4; ++ unsigned long data_phase_addr = 0; ++ uint8_t *p; ++ ++ chip->write_buf(mtd, buf, mtd->writesize); ++ ++ p = chip->oob_poi; ++ chip->write_buf(mtd, p, (mtd->oobsize - data_width)); ++ p += (mtd->oobsize - data_width); ++ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ data_phase_addr |= (1 << END_CMD_VALID_SHIFT); ++ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr; ++ ++ chip->write_buf(mtd, p, data_width); ++ ++ return 0; ++} ++ ++/** ++ * nand_write_page_hwecc - Hardware ECC based page write function ++ * @mtd: Pointer to the mtd info structure ++ * @chip: Pointer to the NAND chip info structure ++ * @buf: Pointer to the data buffer ++ * ++ * This functions writes data and hardware generated ECC values in to the page. ++ */ ++static int xnandps_write_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, int oob_required) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ const uint8_t *p = buf; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ unsigned long data_phase_addr = 0; ++ unsigned long data_width = 4; ++ uint8_t *oob_ptr; ++ ++ for (; (eccsteps - 1); eccsteps--) { ++ chip->write_buf(mtd, p, eccsize); ++ p += eccsize; ++ } ++ chip->write_buf(mtd, p, (eccsize - data_width)); ++ p += (eccsize - data_width); ++ ++ /* Set ECC Last bit to 1 */ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; ++ data_phase_addr |= XNANDPS_ECC_LAST; ++ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr; ++ chip->write_buf(mtd, p, data_width); ++ ++ /* Wait for ECC to be calculated and read the error values */ ++ p = buf; ++ chip->ecc.calculate(mtd, p, &ecc_calc[0]); ++ ++ for (i = 0; i < chip->ecc.total; i++) ++ chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]); ++ ++ /* Clear ECC last bit */ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; ++ data_phase_addr &= ~XNANDPS_ECC_LAST; ++ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr; ++ ++ /* Write the spare area with ECC bytes */ ++ oob_ptr = chip->oob_poi; ++ chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); ++ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ data_phase_addr |= (1 << END_CMD_VALID_SHIFT); ++ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr; ++ oob_ptr += (mtd->oobsize - data_width); ++ chip->write_buf(mtd, oob_ptr, data_width); ++ ++ return 0; ++} ++ ++/** ++ * xnandps_write_page_swecc - [REPLACABLE] software ecc based page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ */ ++static int xnandps_write_page_swecc(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, int oob_required) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ const uint8_t *p = buf; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ ++ /* Software ecc calculation */ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ for (i = 0; i < chip->ecc.total; i++) ++ chip->oob_poi[eccpos[i]] = ecc_calc[i]; ++ ++ chip->ecc.write_page_raw(mtd, chip, buf, 1); ++ ++ return 0; ++} ++ ++/** ++ * xnandps_read_page_hwecc - Hardware ECC based page read function ++ * @mtd: Pointer to the mtd info structure ++ * @chip: Pointer to the NAND chip info structure ++ * @buf: Pointer to the buffer to store read data ++ * @page: page number to read ++ * ++ * This functions reads data and checks the data integrity by comparing hardware ++ * generated ECC values and read ECC values from spare area. ++ * ++ * returns: 0 always and updates ECC operation status in to MTD structure ++ */ ++static int xnandps_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int i, stat, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ unsigned long data_phase_addr = 0; ++ unsigned long data_width = 4; ++ uint8_t *oob_ptr; ++ ++ for (; (eccsteps - 1); eccsteps--) { ++ chip->read_buf(mtd, p, eccsize); ++ p += eccsize; ++ } ++ chip->read_buf(mtd, p, (eccsize - data_width)); ++ p += (eccsize - data_width); ++ ++ /* Set ECC Last bit to 1 */ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; ++ data_phase_addr |= XNANDPS_ECC_LAST; ++ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; ++ chip->read_buf(mtd, p, data_width); ++ ++ /* Read the calculated ECC value */ ++ p = buf; ++ chip->ecc.calculate(mtd, p, &ecc_calc[0]); ++ ++ /* Clear ECC last bit */ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; ++ data_phase_addr &= ~XNANDPS_ECC_LAST; ++ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; ++ ++ /* Read the stored ECC value */ ++ oob_ptr = chip->oob_poi; ++ chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); ++ ++ /* de-assert chip select */ ++ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R; ++ data_phase_addr |= XNANDPS_CLEAR_CS; ++ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr; ++ ++ oob_ptr += (mtd->oobsize - data_width); ++ chip->read_buf(mtd, oob_ptr, data_width); ++ ++ for (i = 0; i < chip->ecc.total; i++) ++ ecc_code[i] = ~(chip->oob_poi[eccpos[i]]); ++ ++ eccsteps = chip->ecc.steps; ++ p = buf; ++ ++ /* Check ECC error for all blocks and correct if it is correctable */ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ return 0; ++} ++ ++/** ++ * xnandps_read_page_swecc - [REPLACABLE] software ecc based page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @page: page number to read ++ */ ++static int xnandps_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ ++ chip->ecc.read_page_raw(mtd, chip, buf, page, 1); ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ for (i = 0; i < chip->ecc.total; i++) ++ ecc_code[i] = chip->oob_poi[eccpos[i]]; ++ ++ eccsteps = chip->ecc.steps; ++ p = buf; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ return 0; ++} ++ ++/** ++ * xnandps_select_chip - Select the flash device ++ * @mtd: Pointer to the mtd_info structure ++ * @chip: Chip number to be selected ++ * ++ * This function is empty as the NAND controller handles chip select line ++ * internally based on the chip address passed in command and data phase. ++ **/ ++static void xnandps_select_chip(struct mtd_info *mtd, int chip) ++{ ++ return; ++} ++ ++/** ++ * xnandps_cmd_function - Send command to NAND device ++ * @mtd: Pointer to the mtd_info structure ++ * @command: The command to be sent to the flash device ++ * @column: The column address for this command, -1 if none ++ * @page_addr: The page address for this command, -1 if none ++ */ ++static void xnandps_cmd_function(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ const struct xnandps_command_format *curr_cmd = NULL; ++ struct xnandps_info *xnand = ++ container_of(mtd, struct xnandps_info, mtd); ++ void __iomem *cmd_addr; ++ unsigned long cmd_data = 0; ++ unsigned long cmd_phase_addr = 0; ++ unsigned long data_phase_addr = 0; ++ unsigned long end_cmd = 0; ++ unsigned long end_cmd_valid = 0; ++ unsigned long i; ++ ++ if (xnand->end_cmd_pending) { ++ /* Check for end command if this command request is same as the ++ * pending command then return */ ++ if (xnand->end_cmd == command) { ++ xnand->end_cmd = 0; ++ xnand->end_cmd_pending = 0; ++ return; ++ } ++ } ++ ++ /* Emulate NAND_CMD_READOOB for large page device */ ++ if ((mtd->writesize > XNANDPS_ECC_SIZE) && ++ (command == NAND_CMD_READOOB)) { ++ column += mtd->writesize; ++ command = NAND_CMD_READ0; ++ } ++ ++ /* Get the command format */ ++ for (i = 0; (xnandps_commands[i].start_cmd != NAND_CMD_NONE || ++ xnandps_commands[i].end_cmd != NAND_CMD_NONE); i++) { ++ if (command == xnandps_commands[i].start_cmd) ++ curr_cmd = &xnandps_commands[i]; ++ } ++ ++ if (curr_cmd == NULL) ++ return; ++ ++ /* Clear interrupt */ ++ xsmcps_clr_nand_int(); ++ ++ /* Get the command phase address */ ++ if (curr_cmd->end_cmd_valid == XNANDPS_CMD_PHASE) ++ end_cmd_valid = 1; ++ ++ if (curr_cmd->end_cmd == NAND_CMD_NONE) ++ end_cmd = 0x0; ++ else ++ end_cmd = curr_cmd->end_cmd; ++ ++ cmd_phase_addr = (unsigned long __force)xnand->nand_base | ++ (curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT) | ++ (end_cmd_valid << END_CMD_VALID_SHIFT) | ++ (COMMAND_PHASE) | ++ (end_cmd << END_CMD_SHIFT) | ++ (curr_cmd->start_cmd << START_CMD_SHIFT); ++ ++ cmd_addr = (void __iomem * __force)cmd_phase_addr; ++ ++ /* Get the data phase address */ ++ end_cmd_valid = 0; ++ ++ data_phase_addr = (unsigned long __force)xnand->nand_base | ++ (0x0 << CLEAR_CS_SHIFT) | ++ (end_cmd_valid << END_CMD_VALID_SHIFT) | ++ (DATA_PHASE) | ++ (end_cmd << END_CMD_SHIFT) | ++ (0x0 << ECC_LAST_SHIFT); ++ ++ chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; ++ chip->IO_ADDR_W = chip->IO_ADDR_R; ++ ++ /* Command phase AXI write */ ++ /* Read & Write */ ++ if (column != -1 && page_addr != -1) { ++ /* Adjust columns for 16 bit bus width */ ++ if (chip->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ cmd_data = column; ++ if (mtd->writesize > XNANDPS_ECC_SIZE) { ++ cmd_data |= page_addr << 16; ++ /* Another address cycle for devices > 128MiB */ ++ if (chip->chipsize > (128 << 20)) { ++ xnandps_write32(cmd_addr, cmd_data); ++ cmd_data = (page_addr >> 16); ++ } ++ } else { ++ cmd_data |= page_addr << 8; ++ } ++ } else if (page_addr != -1) { ++ /* Erase */ ++ cmd_data = page_addr; ++ } else if (column != -1) { ++ /* Change read/write column, read id etc */ ++ /* Adjust columns for 16 bit bus width */ ++ if ((chip->options & NAND_BUSWIDTH_16) && ++ ((command == NAND_CMD_READ0) || ++ (command == NAND_CMD_SEQIN) || ++ (command == NAND_CMD_RNDOUT) || ++ (command == NAND_CMD_RNDIN))) ++ column >>= 1; ++ cmd_data = column; ++ } ++ ++ xnandps_write32(cmd_addr, cmd_data); ++ ++ if (curr_cmd->end_cmd_valid) { ++ xnand->end_cmd = curr_cmd->end_cmd; ++ xnand->end_cmd_pending = 1; ++ } ++ ++ ndelay(100); ++ ++ if ((command == NAND_CMD_READ0) || ++ (command == NAND_CMD_RESET) || ++ (command == NAND_CMD_PARAM) || ++ (command == NAND_CMD_GET_FEATURES)) { ++ ++ while (!chip->dev_ready(mtd)) ++ ; ++ return; ++ } ++} ++ ++/** ++ * xnandps_read_buf - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ */ ++static void xnandps_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd->priv; ++ unsigned long *ptr = (unsigned long *)buf; ++ ++ len >>= 2; ++ for (i = 0; i < len; i++) ++ ptr[i] = readl(chip->IO_ADDR_R); ++} ++ ++/** ++ * xnandps_write_buf - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ */ ++static void xnandps_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd->priv; ++ unsigned long *ptr = (unsigned long *)buf; ++ len >>= 2; ++ ++ for (i = 0; i < len; i++) ++ writel(ptr[i], chip->IO_ADDR_W); ++} ++ ++/** ++ * xnandps_device_ready - Check device ready/busy line ++ * @mtd: Pointer to the mtd_info structure ++ * ++ * returns: 0 on busy or 1 on ready state ++ **/ ++static int xnandps_device_ready(struct mtd_info *mtd) ++{ ++ if (xsmcps_get_nand_int_status_raw()) { ++ xsmcps_clr_nand_int(); ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * xnandps_probe - Probe method for the NAND driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function initializes the driver data structures and the hardware. ++ * ++ * returns: 0 on success or error value on failure ++ **/ ++static int xnandps_probe(struct platform_device *pdev) ++{ ++ struct xnandps_info *xnand; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ struct resource *nand_res; ++ u8 maf_id, dev_id, i; ++ u8 get_feature; ++ u8 set_feature[4] = {0x08, 0x00, 0x00, 0x00}; ++ int ondie_ecc_enabled = 0; ++ struct mtd_part_parser_data ppdata; ++ const unsigned int *prop; ++ u32 options = 0; ++ ++ xnand = devm_kzalloc(&pdev->dev, sizeof(struct xnandps_info), ++ GFP_KERNEL); ++ if (!xnand) ++ return -ENOMEM; ++ ++ /* Map physical address of NAND flash */ ++ nand_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ xnand->nand_base = devm_ioremap_resource(&pdev->dev, nand_res); ++ if (IS_ERR(xnand->nand_base)) { ++ dev_err(&pdev->dev, "ioremap for NAND failed\n"); ++ return PTR_ERR(xnand->nand_base); ++ } ++ ++ /* Get x8 or x16 mode from device tree */ ++ prop = of_get_property(pdev->dev.of_node, "xlnx,nand-width", NULL); ++ if (prop) { ++ if (be32_to_cpup(prop) == 16) { ++ options |= NAND_BUSWIDTH_16; ++ } else if (be32_to_cpup(prop) == 8) { ++ options &= ~NAND_BUSWIDTH_16; ++ } else { ++ dev_info(&pdev->dev, "xlnx,nand-width not valid, using 8"); ++ options &= ~NAND_BUSWIDTH_16; ++ } ++ } else { ++ dev_info(&pdev->dev, "xlnx,nand-width not in device tree, using 8"); ++ options &= ~NAND_BUSWIDTH_16; ++ } ++ ++ /* Link the private data with the MTD structure */ ++ mtd = &xnand->mtd; ++ nand_chip = &xnand->chip; ++ ++ nand_chip->priv = xnand; ++ mtd->priv = nand_chip; ++ mtd->owner = THIS_MODULE; ++ mtd->name = "xilinx_nand"; ++ ++ /* Set address of NAND IO lines */ ++ nand_chip->IO_ADDR_R = xnand->nand_base; ++ nand_chip->IO_ADDR_W = xnand->nand_base; ++ ++ /* Set the driver entry points for MTD */ ++ nand_chip->cmdfunc = xnandps_cmd_function; ++ nand_chip->dev_ready = xnandps_device_ready; ++ nand_chip->select_chip = xnandps_select_chip; ++ ++ /* If we don't set this delay driver sets 20us by default */ ++ nand_chip->chip_delay = 30; ++ ++ /* Buffer read/write routines */ ++ nand_chip->read_buf = xnandps_read_buf; ++ nand_chip->write_buf = xnandps_write_buf; ++ ++ /* Set the device option and flash width */ ++ nand_chip->options = options; ++ nand_chip->bbt_options = NAND_BBT_USE_FLASH; ++ ++ platform_set_drvdata(pdev, xnand); ++ ++ /* first scan to find the device and get the page size */ ++ if (nand_scan_ident(mtd, 1, NULL)) { ++ dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n"); ++ return -ENXIO; ++ } ++ ++ /* Check if On-Die ECC flash */ ++ nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); ++ ++ /* Read manufacturer and device IDs */ ++ maf_id = nand_chip->read_byte(mtd); ++ dev_id = nand_chip->read_byte(mtd); ++ ++ if ((maf_id == 0x2c) && ++ ((dev_id == 0xf1) || (dev_id == 0xa1) || ++ (dev_id == 0xb1) || ++ (dev_id == 0xaa) || (dev_id == 0xba) || ++ (dev_id == 0xda) || (dev_id == 0xca) || ++ (dev_id == 0xac) || (dev_id == 0xbc) || ++ (dev_id == 0xdc) || (dev_id == 0xcc) || ++ (dev_id == 0xa3) || (dev_id == 0xb3) || ++ (dev_id == 0xd3) || (dev_id == 0xc3))) { ++ ++ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, ++ ONDIE_ECC_FEATURE_ADDR, -1); ++ get_feature = nand_chip->read_byte(mtd); ++ ++ if (get_feature & 0x08) { ++ ondie_ecc_enabled = 1; ++ } else { ++ nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, ++ ONDIE_ECC_FEATURE_ADDR, -1); ++ for (i = 0; i < 4; i++) ++ writeb(set_feature[i], nand_chip->IO_ADDR_W); ++ ++ ndelay(1000); ++ ++ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, ++ ONDIE_ECC_FEATURE_ADDR, -1); ++ get_feature = nand_chip->read_byte(mtd); ++ ++ if (get_feature & 0x08) ++ ondie_ecc_enabled = 1; ++ } ++ } ++ ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.read_oob = xnandps_read_oob; ++ nand_chip->ecc.read_page_raw = xnandps_read_page_raw; ++ nand_chip->ecc.strength = 1; ++ nand_chip->ecc.write_oob = xnandps_write_oob; ++ nand_chip->ecc.write_page_raw = xnandps_write_page_raw; ++ if (ondie_ecc_enabled) { ++ /* bypass the controller ECC block */ ++ xsmcps_set_ecc_mode(XSMCPS_ECCMODE_BYPASS); ++ ++ /* The software ECC routines won't work with the ++ SMC controller */ ++ nand_chip->ecc.bytes = 0; ++ nand_chip->ecc.layout = &ondie_nand_oob_64; ++ nand_chip->ecc.read_page = xnandps_read_page_raw; ++ nand_chip->ecc.write_page = xnandps_write_page_raw; ++ nand_chip->ecc.size = mtd->writesize; ++ /* On-Die ECC spare bytes offset 8 is used for ECC codes */ ++ /* Use the BBT pattern descriptors */ ++ nand_chip->bbt_td = &bbt_main_descr; ++ nand_chip->bbt_md = &bbt_mirror_descr; ++ } else { ++ /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */ ++ nand_chip->ecc.bytes = 3; ++ nand_chip->ecc.calculate = xnandps_calculate_hwecc; ++ nand_chip->ecc.correct = xnandps_correct_data; ++ nand_chip->ecc.hwctl = NULL; ++ nand_chip->ecc.read_page = xnandps_read_page_hwecc; ++ nand_chip->ecc.size = XNANDPS_ECC_SIZE; ++ nand_chip->ecc.write_page = xnandps_write_page_hwecc; ++ ++ xsmcps_set_ecc_pg_size(mtd->writesize); ++ switch (mtd->writesize) { ++ case 512: ++ case 1024: ++ case 2048: ++ xsmcps_set_ecc_mode(XSMCPS_ECCMODE_APB); ++ break; ++ default: ++ /* The software ECC routines won't work with the ++ SMC controller */ ++ nand_chip->ecc.calculate = nand_calculate_ecc; ++ nand_chip->ecc.correct = nand_correct_data; ++ nand_chip->ecc.read_page = xnandps_read_page_swecc; ++ /* nand_chip->ecc.read_subpage = nand_read_subpage; */ ++ nand_chip->ecc.write_page = xnandps_write_page_swecc; ++ nand_chip->ecc.size = 256; ++ break; ++ } ++ ++ if (mtd->oobsize == 16) ++ nand_chip->ecc.layout = &nand_oob_16; ++ else if (mtd->oobsize == 64) ++ nand_chip->ecc.layout = &nand_oob_64; ++ } ++ ++ /* second phase scan */ ++ if (nand_scan_tail(mtd)) { ++ dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n"); ++ return -ENXIO; ++ } ++ ++ ppdata.of_node = pdev->dev.of_node; ++ ++ mtd_device_parse_register(&xnand->mtd, NULL, &ppdata, ++ NULL, 0); ++ ++ return 0; ++} ++ ++/** ++ * xnandps_remove - Remove method for the NAND driver ++ * @pdev: Pointer to the platform_device structure ++ * ++ * This function is called if the driver module is being unloaded. It frees all ++ * resources allocated to the device. ++ * ++ * returns: 0 on success or error value on failure ++ **/ ++static int xnandps_remove(struct platform_device *pdev) ++{ ++ struct xnandps_info *xnand = platform_get_drvdata(pdev); ++ ++ /* Release resources, unregister device */ ++ nand_release(&xnand->mtd); ++ /* kfree(NULL) is safe */ ++ kfree(xnand->parts); ++ ++ return 0; ++} ++ ++/* Match table for device tree binding */ ++static const struct of_device_id xnandps_of_match[] = { ++ { .compatible = "xlnx,ps7-nand-1.00.a" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, xnandps_of_match); ++ ++/* ++ * xnandps_driver - This structure defines the NAND subsystem platform driver ++ */ ++static struct platform_driver xnandps_driver = { ++ .probe = xnandps_probe, ++ .remove = xnandps_remove, ++ .driver = { ++ .name = XNANDPS_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = xnandps_of_match, ++ }, ++}; ++ ++module_platform_driver(xnandps_driver); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_ALIAS("platform:" XNANDPS_DRIVER_NAME); ++MODULE_DESCRIPTION("Xilinx PS NAND Flash Driver"); ++MODULE_LICENSE("GPL"); diff --git a/patches.zynq/0008-watchdog-xilinx-merge-support-for-xilinx-watchdog.patch b/patches.zynq/0008-watchdog-xilinx-merge-support-for-xilinx-watchdog.patch new file mode 100644 index 0000000..4e8af98 --- /dev/null +++ b/patches.zynq/0008-watchdog-xilinx-merge-support-for-xilinx-watchdog.patch @@ -0,0 +1,619 @@ +From 55f1815cb899dc0bb48c250bf753c12ead069f27 Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Tue, 24 Dec 2013 09:22:35 +0900 +Subject: watchdog: xilinx: merge support for xilinx watchdog + +This merges support for the Xilinx watchdong from the Xilinx +repository (commit efc27505715e64526653f35274717c0fc56491e3 in +master branch). It has been tested by using the watchdog +command. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/watchdog/Kconfig | 11 + drivers/watchdog/Makefile | 1 + drivers/watchdog/of_xilinx_wdt.c | 1 + drivers/watchdog/xilinx_wdtps.c | 545 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 557 insertions(+), 1 deletion(-) + create mode 100644 drivers/watchdog/xilinx_wdtps.c + +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -224,6 +224,7 @@ config DW_WATCHDOG + config MPCORE_WATCHDOG + tristate "MPcore watchdog" + depends on HAVE_ARM_TWD ++ select WATCHDOG_CORE + help + Watchdog timer embedded into the MPcore system. + +@@ -337,6 +338,14 @@ config NUC900_WATCHDOG + To compile this driver as a module, choose M here: the + module will be called nuc900_wdt. + ++config XILINX_PS_WATCHDOG ++ tristate "Xilinx PS Watchdog Timer" ++ depends on ARCH_ZYNQ ++ select WATCHDOG_CORE ++ help ++ Say Y here if you want to include support for the watchdog ++ timer in the Xilinx PS. ++ + config TS72XX_WATCHDOG + tristate "TS-72XX SBC Watchdog" + depends on MACH_TS72XX +@@ -976,7 +985,7 @@ config M54xx_WATCHDOG + + config XILINX_WATCHDOG + tristate "Xilinx Watchdog timer" +- depends on MICROBLAZE ++ depends on MICROBLAZE || ARCH_ZYNQ + ---help--- + Watchdog driver for the xps_timebase_wdt ip core. + +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wd + obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o + obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o + obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o ++obj-$(CONFIG_XILINX_PS_WATCHDOG) += xilinx_wdtps.o + obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o + obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o + obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o +--- a/drivers/watchdog/of_xilinx_wdt.c ++++ b/drivers/watchdog/of_xilinx_wdt.c +@@ -405,3 +405,4 @@ module_platform_driver(xwdt_driver); + MODULE_AUTHOR("Alejandro Cabrera "); + MODULE_DESCRIPTION("Xilinx Watchdog driver"); + MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +--- /dev/null ++++ b/drivers/watchdog/xilinx_wdtps.c +@@ -0,0 +1,545 @@ ++/* ++ * Xilinx Zynq WDT driver ++ * ++ * Copyright (c) 2010-2013 Xilinx Inc. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public ++ * License along with this program; if not, write to the Free ++ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA ++ * 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define XWDTPS_DEFAULT_TIMEOUT 10 ++/* Supports 1 - 516 sec */ ++#define XWDTPS_MIN_TIMEOUT 1 ++#define XWDTPS_MAX_TIMEOUT 516 ++ ++static int wdt_timeout = XWDTPS_DEFAULT_TIMEOUT; ++static int nowayout = WATCHDOG_NOWAYOUT; ++ ++module_param(wdt_timeout, int, 0); ++MODULE_PARM_DESC(wdt_timeout, ++ "Watchdog time in seconds. (default=" ++ __MODULE_STRING(XWDTPS_DEFAULT_TIMEOUT) ")"); ++ ++module_param(nowayout, int, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++/** ++ * struct xwdtps - Watchdog device structure. ++ * @regs: baseaddress of device. ++ * @busy: flag for the device. ++ * ++ * Structure containing parameters specific to ps watchdog. ++ */ ++struct xwdtps { ++ void __iomem *regs; /* Base address */ ++ unsigned long busy; /* Device Status */ ++ int rst; /* Reset flag */ ++ struct clk *clk; ++ u32 prescalar; ++ u32 ctrl_clksel; ++ spinlock_t io_lock; ++}; ++static struct xwdtps *wdt; ++ ++/* ++ * Info structure used to indicate the features supported by the device ++ * to the upper layers. This is defined in watchdog.h header file. ++ */ ++static struct watchdog_info xwdtps_info = { ++ .identity = "xwdtps watchdog", ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | ++ WDIOF_MAGICCLOSE, ++}; ++ ++/* Write access to Registers */ ++#define xwdtps_writereg(val, offset) __raw_writel(val, (wdt->regs) + offset) ++ ++/*************************Register Map**************************************/ ++ ++/* Register Offsets for the WDT */ ++#define XWDTPS_ZMR_OFFSET 0x0 /* Zero Mode Register */ ++#define XWDTPS_CCR_OFFSET 0x4 /* Counter Control Register */ ++#define XWDTPS_RESTART_OFFSET 0x8 /* Restart Register */ ++#define XWDTPS_SR_OFFSET 0xC /* Status Register */ ++ ++/* ++ * Zero Mode Register - This register controls how the time out is indicated ++ * and also contains the access code to allow writes to the register (0xABC). ++ */ ++#define XWDTPS_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ ++#define XWDTPS_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ ++#define XWDTPS_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ ++#define XWDTPS_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ ++#define XWDTPS_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ ++/* ++ * Counter Control register - This register controls how fast the timer runs ++ * and the reset value and also contains the access code to allow writes to ++ * the register. ++ */ ++#define XWDTPS_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ ++ ++/** ++ * xwdtps_stop - Stop the watchdog. ++ * ++ * Read the contents of the ZMR register, clear the WDEN bit ++ * in the register and set the access key for successful write. ++ */ ++static int xwdtps_stop(struct watchdog_device *wdd) ++{ ++ spin_lock(&wdt->io_lock); ++ xwdtps_writereg((XWDTPS_ZMR_ZKEY_VAL & (~XWDTPS_ZMR_WDEN_MASK)), ++ XWDTPS_ZMR_OFFSET); ++ spin_unlock(&wdt->io_lock); ++ return 0; ++} ++ ++/** ++ * xwdtps_reload - Reload the watchdog timer (i.e. pat the watchdog). ++ * ++ * Write the restart key value (0x00001999) to the restart register. ++ */ ++static int xwdtps_reload(struct watchdog_device *wdd) ++{ ++ spin_lock(&wdt->io_lock); ++ xwdtps_writereg(0x00001999, XWDTPS_RESTART_OFFSET); ++ spin_unlock(&wdt->io_lock); ++ return 0; ++} ++ ++/** ++ * xwdtps_start - Enable and start the watchdog. ++ * ++ * The counter value is calculated according to the formula: ++ * calculated count = (timeout * clock) / prescalar + 1. ++ * The calculated count is divided by 0x1000 to obtain the field value ++ * to write to counter control register. ++ * Clears the contents of prescalar and counter reset value. Sets the ++ * prescalar to 4096 and the calculated count and access key ++ * to write to CCR Register. ++ * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) ++ * or Interrupt signal(IRQEN) with a specified cycles and the access ++ * key to write to ZMR Register. ++ */ ++static int xwdtps_start(struct watchdog_device *wdd) ++{ ++ unsigned int data = 0; ++ unsigned short count; ++ unsigned long clock_f = clk_get_rate(wdt->clk); ++ ++ /* ++ * 0x1000 - Counter Value Divide, to obtain the value of counter ++ * reset to write to control register. ++ */ ++ count = (wdd->timeout * (clock_f / (wdt->prescalar))) / 0x1000 + 1; ++ ++ /* Check for boundary conditions of counter value */ ++ if (count > 0xFFF) ++ count = 0xFFF; ++ ++ spin_lock(&wdt->io_lock); ++ xwdtps_writereg(XWDTPS_ZMR_ZKEY_VAL, XWDTPS_ZMR_OFFSET); ++ ++ /* Shift the count value to correct bit positions */ ++ count = (count << 2) & XWDTPS_CCR_CRV_MASK; ++ ++ /* 0x00920000 - Counter register key value. */ ++ data = (count | 0x00920000 | (wdt->ctrl_clksel)); ++ xwdtps_writereg(data, XWDTPS_CCR_OFFSET); ++ data = XWDTPS_ZMR_WDEN_MASK | XWDTPS_ZMR_RSTLEN_16 | ++ XWDTPS_ZMR_ZKEY_VAL; ++ ++ /* Reset on timeout if specified in device tree. */ ++ if (wdt->rst) { ++ data |= XWDTPS_ZMR_RSTEN_MASK; ++ data &= ~XWDTPS_ZMR_IRQEN_MASK; ++ } else { ++ data &= ~XWDTPS_ZMR_RSTEN_MASK; ++ data |= XWDTPS_ZMR_IRQEN_MASK; ++ } ++ xwdtps_writereg(data, XWDTPS_ZMR_OFFSET); ++ spin_unlock(&wdt->io_lock); ++ xwdtps_writereg(0x00001999, XWDTPS_RESTART_OFFSET); ++ return 0; ++} ++ ++/** ++ * xwdtps_settimeout - Set a new timeout value for the watchdog device. ++ * ++ * @new_time: new timeout value that needs to be set. ++ * Returns 0 on success. ++ * ++ * Update the watchdog_device timeout with new value which is used when ++ * xwdtps_start is called. ++ */ ++static int xwdtps_settimeout(struct watchdog_device *wdd, unsigned int new_time) ++{ ++ wdd->timeout = new_time; ++ return xwdtps_start(wdd); ++} ++ ++/** ++ * xwdtps_irq_handler - Notifies of watchdog timeout. ++ * ++ * @irq: interrupt number ++ * @dev_id: pointer to a platform device structure ++ * Returns IRQ_HANDLED ++ * ++ * The handler is invoked when the watchdog times out and a ++ * reset on timeout has not been enabled. ++ */ ++static irqreturn_t xwdtps_irq_handler(int irq, void *dev_id) ++{ ++ struct platform_device *pdev = dev_id; ++ dev_info(&pdev->dev, "Watchdog timed out.\n"); ++ return IRQ_HANDLED; ++} ++ ++/* Watchdog Core Ops */ ++static struct watchdog_ops xwdtps_ops = { ++ .owner = THIS_MODULE, ++ .start = xwdtps_start, ++ .stop = xwdtps_stop, ++ .ping = xwdtps_reload, ++ .set_timeout = xwdtps_settimeout, ++}; ++ ++/* Watchdog Core Device */ ++static struct watchdog_device xwdtps_device = { ++ .info = &xwdtps_info, ++ .ops = &xwdtps_ops, ++ .timeout = XWDTPS_DEFAULT_TIMEOUT, ++ .min_timeout = XWDTPS_MIN_TIMEOUT, ++ .max_timeout = XWDTPS_MAX_TIMEOUT, ++}; ++ ++/** ++ * xwdtps_notify_sys - Notifier for reboot or shutdown. ++ * ++ * @this: handle to notifier block. ++ * @code: turn off indicator. ++ * @unused: unused. ++ * Returns NOTIFY_DONE. ++ * ++ * This notifier is invoked whenever the system reboot or shutdown occur ++ * because we need to disable the WDT before system goes down as WDT might ++ * reset on the next boot. ++ */ ++static int xwdtps_notify_sys(struct notifier_block *this, unsigned long code, ++ void *unused) ++{ ++ if (code == SYS_DOWN || code == SYS_HALT) ++ /* Stop the watchdog */ ++ xwdtps_stop(&xwdtps_device); ++ return NOTIFY_DONE; ++} ++ ++/* Notifier Structure */ ++static struct notifier_block xwdtps_notifier = { ++ .notifier_call = xwdtps_notify_sys, ++}; ++ ++/************************Platform Operations*****************************/ ++/** ++ * xwdtps_probe - Probe call for the device. ++ * ++ * @pdev: handle to the platform device structure. ++ * Returns 0 on success, negative error otherwise. ++ * ++ * It does all the memory allocation and registration for the device. ++ */ ++static int xwdtps_probe(struct platform_device *pdev) ++{ ++ struct resource *regs; ++ int res; ++ const void *prop; ++ int irq; ++ unsigned long clock_f; ++ ++ /* Check whether WDT is in use, just for safety */ ++ if (wdt) { ++ dev_err(&pdev->dev, ++ "Device Busy, only 1 xwdtps instance supported.\n"); ++ return -EBUSY; ++ } ++ ++ /* Get the device base address */ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(&pdev->dev, "Unable to locate mmio resource\n"); ++ return -ENODEV; ++ } ++ ++ /* Allocate an instance of the xwdtps structure */ ++ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); ++ if (!wdt) { ++ dev_err(&pdev->dev, "No memory for wdt structure\n"); ++ return -ENOMEM; ++ } ++ ++ wdt->regs = ioremap(regs->start, regs->end - regs->start + 1); ++ if (!wdt->regs) { ++ res = -ENOMEM; ++ dev_err(&pdev->dev, "Could not map I/O memory\n"); ++ goto err_free; ++ } ++ ++ /* Register the reboot notifier */ ++ res = register_reboot_notifier(&xwdtps_notifier); ++ if (res != 0) { ++ dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", ++ res); ++ goto err_iounmap; ++ } ++ ++ /* Register the interrupt */ ++ prop = of_get_property(pdev->dev.of_node, "reset", NULL); ++ wdt->rst = prop ? be32_to_cpup(prop) : 0; ++ irq = platform_get_irq(pdev, 0); ++ if (!wdt->rst && irq >= 0) { ++ res = request_irq(irq, xwdtps_irq_handler, 0, pdev->name, pdev); ++ if (res) { ++ dev_err(&pdev->dev, ++ "cannot register interrupt handler err=%d\n", ++ res); ++ goto err_notifier; ++ } ++ } ++ ++ /* Initialize the members of xwdtps structure */ ++ xwdtps_device.parent = &pdev->dev; ++ prop = of_get_property(pdev->dev.of_node, "timeout", NULL); ++ if (prop) { ++ xwdtps_device.timeout = be32_to_cpup(prop); ++ } else if (wdt_timeout < XWDTPS_MAX_TIMEOUT && ++ wdt_timeout > XWDTPS_MIN_TIMEOUT) { ++ xwdtps_device.timeout = wdt_timeout; ++ } else { ++ dev_info(&pdev->dev, ++ "timeout limited to 1 - %d sec, using default=%d\n", ++ XWDTPS_MAX_TIMEOUT, XWDTPS_DEFAULT_TIMEOUT); ++ xwdtps_device.timeout = XWDTPS_DEFAULT_TIMEOUT; ++ } ++ ++ watchdog_set_nowayout(&xwdtps_device, nowayout); ++ watchdog_set_drvdata(&xwdtps_device, &wdt); ++ ++ wdt->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(wdt->clk)) { ++ dev_err(&pdev->dev, "input clock not found\n"); ++ res = PTR_ERR(wdt->clk); ++ goto err_irq; ++ } ++ ++ res = clk_prepare_enable(wdt->clk); ++ if (res) { ++ dev_err(&pdev->dev, "unable to enable clock\n"); ++ goto err_clk_put; ++ } ++ ++ clock_f = clk_get_rate(wdt->clk); ++ if (clock_f <= 10000000) {/* For PEEP */ ++ wdt->prescalar = 64; ++ wdt->ctrl_clksel = 1; ++ } else if (clock_f <= 75000000) { ++ wdt->prescalar = 256; ++ wdt->ctrl_clksel = 2; ++ } else { /* For Zynq */ ++ wdt->prescalar = 4096; ++ wdt->ctrl_clksel = 3; ++ } ++ ++ /* Initialize the busy flag to zero */ ++ clear_bit(0, &wdt->busy); ++ spin_lock_init(&wdt->io_lock); ++ ++ /* Register the WDT */ ++ res = watchdog_register_device(&xwdtps_device); ++ if (res) { ++ dev_err(&pdev->dev, "Failed to register wdt device\n"); ++ goto err_clk_disable; ++ } ++ platform_set_drvdata(pdev, wdt); ++ ++ dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", ++ wdt->regs, xwdtps_device.timeout, nowayout ? ", nowayout" : ""); ++ ++ return 0; ++ ++err_clk_disable: ++ clk_disable_unprepare(wdt->clk); ++err_clk_put: ++ clk_put(wdt->clk); ++err_irq: ++ free_irq(irq, pdev); ++err_notifier: ++ unregister_reboot_notifier(&xwdtps_notifier); ++err_iounmap: ++ iounmap(wdt->regs); ++err_free: ++ kfree(wdt); ++ wdt = NULL; ++ return res; ++} ++ ++/** ++ * xwdtps_remove - Probe call for the device. ++ * ++ * @pdev: handle to the platform device structure. ++ * Returns 0 on success, otherwise negative error. ++ * ++ * Unregister the device after releasing the resources. ++ * Stop is allowed only when nowayout is disabled. ++ */ ++static int __exit xwdtps_remove(struct platform_device *pdev) ++{ ++ int res = 0; ++ int irq; ++ ++ if (wdt && !nowayout) { ++ xwdtps_stop(&xwdtps_device); ++ watchdog_unregister_device(&xwdtps_device); ++ unregister_reboot_notifier(&xwdtps_notifier); ++ irq = platform_get_irq(pdev, 0); ++ free_irq(irq, pdev); ++ iounmap(wdt->regs); ++ clk_disable_unprepare(wdt->clk); ++ clk_put(wdt->clk); ++ kfree(wdt); ++ wdt = NULL; ++ platform_set_drvdata(pdev, NULL); ++ } else { ++ dev_err(&pdev->dev, "Cannot stop watchdog, still ticking\n"); ++ return -ENOTSUPP; ++ } ++ return res; ++} ++ ++/** ++ * xwdtps_shutdown - Stop the device. ++ * ++ * @pdev: handle to the platform structure. ++ * ++ */ ++static void xwdtps_shutdown(struct platform_device *pdev) ++{ ++ /* Stop the device */ ++ xwdtps_stop(&xwdtps_device); ++ clk_disable_unprepare(wdt->clk); ++ clk_put(wdt->clk); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * xwdtps_suspend - Stop the device. ++ * ++ * @dev: handle to the device structure. ++ * Returns 0 always. ++ */ ++static int xwdtps_suspend(struct device *dev) ++{ ++ /* Stop the device */ ++ xwdtps_stop(&xwdtps_device); ++ clk_disable(wdt->clk); ++ return 0; ++} ++ ++/** ++ * xwdtps_resume - Resume the device. ++ * ++ * @dev: handle to the device structure. ++ * Returns 0 on success, errno otherwise. ++ */ ++static int xwdtps_resume(struct device *dev) ++{ ++ int ret; ++ ++ ret = clk_enable(wdt->clk); ++ if (ret) { ++ dev_err(dev, "unable to enable clock\n"); ++ return ret; ++ } ++ /* Start the device */ ++ xwdtps_start(&xwdtps_device); ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(xwdtps_pm_ops, xwdtps_suspend, xwdtps_resume); ++ ++static struct of_device_id xwdtps_of_match[] = { ++ { .compatible = "xlnx,ps7-wdt-1.00.a", }, ++ { /* end of table */} ++}; ++MODULE_DEVICE_TABLE(of, xwdtps_of_match); ++ ++/* Driver Structure */ ++static struct platform_driver xwdtps_driver = { ++ .probe = xwdtps_probe, ++ .remove = xwdtps_remove, ++ .shutdown = xwdtps_shutdown, ++ .driver = { ++ .name = "xwdtps", ++ .owner = THIS_MODULE, ++ .of_match_table = xwdtps_of_match, ++ .pm = &xwdtps_pm_ops, ++ }, ++}; ++ ++/** ++ * xwdtps_init - Register the WDT. ++ * ++ * Returns 0 on success, otherwise negative error. ++ * ++ * If using noway out, the use count will be incremented. ++ * This will prevent unloading the module. An attempt to ++ * unload the module will result in a warning from the kernel. ++ */ ++static int __init xwdtps_init(void) ++{ ++ int res = platform_driver_register(&xwdtps_driver); ++ if (!res && nowayout) ++ try_module_get(THIS_MODULE); ++ return res; ++} ++ ++/** ++ * xwdtps_exit - Unregister the WDT. ++ */ ++static void __exit xwdtps_exit(void) ++{ ++ platform_driver_unregister(&xwdtps_driver); ++} ++ ++module_init(xwdtps_init); ++module_exit(xwdtps_exit); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Watchdog driver for PS WDT"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform: xwdtps"); diff --git a/patches.zynq/0009-usb-zynq-merge-usb-support-for-xilinx-zynq-soc.patch b/patches.zynq/0009-usb-zynq-merge-usb-support-for-xilinx-zynq-soc.patch new file mode 100644 index 0000000..bef62e7 --- /dev/null +++ b/patches.zynq/0009-usb-zynq-merge-usb-support-for-xilinx-zynq-soc.patch @@ -0,0 +1,3821 @@ +From 485381435a50c8de2cee2720cee12434437a9354 Mon Sep 17 00:00:00 2001 +From: Michal Simek +Date: Tue, 24 Dec 2013 09:27:30 +0900 +Subject: usb: zynq: merge usb support for xilinx zynq soc + +This merges support for the Zynq's USB from the Xilinx repository +(commit efc27505715e64526653f35274717c0fc56491e3 in master branch). +It has been tested by connecting a USB storage device into a +Zynq 702 board. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/usb/Kconfig | 1 + drivers/usb/core/hub.c | 4 + drivers/usb/host/Kconfig | 15 + drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-hcd.c | 52 + drivers/usb/host/ehci-hub.c | 22 + drivers/usb/host/ehci-xilinx-of.c | 17 + drivers/usb/host/ehci-xilinx-usbps.c | 531 ++++++++ + drivers/usb/host/ehci-xilinx-usbps.h | 33 + drivers/usb/host/ehci.h | 8 + drivers/usb/host/xusbps-dr-of.c | 331 +++++ + drivers/usb/phy/Kconfig | 12 + drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-zynq-usb.c | 2305 +++++++++++++++++++++++++++++++++++ + include/linux/usb/xilinx_usbps_otg.h | 216 +++ + include/linux/xilinx_devices.h | 70 + + 16 files changed, 3617 insertions(+), 2 deletions(-) + create mode 100644 drivers/usb/host/ehci-xilinx-usbps.c + create mode 100644 drivers/usb/host/ehci-xilinx-usbps.h + create mode 100644 drivers/usb/host/xusbps-dr-of.c + create mode 100644 drivers/usb/phy/phy-zynq-usb.c + create mode 100644 include/linux/usb/xilinx_usbps_otg.h + create mode 100644 include/linux/xilinx_devices.h + +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -45,6 +45,7 @@ config USB_ARCH_HAS_EHCI + default y if PLAT_S5P + default y if ARCH_MSM + default y if MICROBLAZE ++ default y if ARCH_ZYNQ + default y if SPARC_LEON + default y if ARCH_MMP + default y if MACH_LOONGSON1 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -1685,7 +1685,11 @@ static int hub_probe(struct usb_interfac + pm_runtime_set_autosuspend_delay(&hdev->dev, 0); + + /* Hubs have proper suspend/resume support. */ ++#ifdef CONFIG_USB_ZYNQ_PHY ++ usb_disable_autosuspend(hdev); ++#else + usb_enable_autosuspend(hdev); ++#endif + + if (hdev->level == MAX_TOPO_LEVEL) { + dev_err(&intf->dev, +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -132,6 +132,21 @@ config XPS_USB_HCD_XILINX + support both high speed and full speed devices, or high speed + devices only. + ++config USB_XUSBPS_DR_OF ++ tristate ++ select USB_PHY ++ select USB_ULPI ++ select USB_ULPI_VIEWPORT ++ ++config USB_EHCI_XUSBPS ++ bool "Support for Xilinx PS EHCI USB controller" ++ depends on USB_EHCI_HCD && ARCH_ZYNQ ++ select USB_EHCI_ROOT_HUB_TT ++ select USB_XUSBPS_DR_OF ++ ---help--- ++ Xilinx PS USB host controller core is EHCI compilant and has ++ transaction translator built-in. ++ + config USB_EHCI_FSL + bool "Support for Freescale PPC on-chip EHCI USB controller" + depends on FSL_SOC +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -49,6 +49,7 @@ obj-$(CONFIG_USB_ISP1760_HCD) += isp1760 + obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o + obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o ++obj-$(CONFIG_USB_XUSBPS_DR_OF) += xusbps-dr-of.o + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o + obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -335,11 +335,21 @@ static void ehci_turn_off_all_ports(stru + */ + static void ehci_silence_controller(struct ehci_hcd *ehci) + { ++#ifdef CONFIG_USB_ZYNQ_PHY ++ struct usb_hcd *hcd = ehci_to_hcd(ehci); ++#endif ++ + ehci_halt(ehci); + + spin_lock_irq(&ehci->lock); + ehci->rh_state = EHCI_RH_HALTED; ++#ifdef CONFIG_USB_ZYNQ_PHY ++ /* turn off for non-otg port */ ++ if (!hcd->phy) ++ ehci_turn_off_all_ports(ehci); ++#else + ehci_turn_off_all_ports(ehci); ++#endif + + /* make BIOS/etc use companion controller during reboot */ + ehci_writel(ehci, 0, &ehci->regs->configured_flag); +@@ -422,7 +432,12 @@ static void ehci_stop (struct usb_hcd *h + + ehci_quiesce(ehci); + ehci_silence_controller(ehci); ++#ifdef CONFIG_USB_ZYNQ_PHY ++ if (!hcd->phy) ++ ehci_reset(ehci); ++#else + ehci_reset (ehci); ++#endif + + hrtimer_cancel(&ehci->hrtimer); + remove_sysfs_files(ehci); +@@ -569,6 +584,9 @@ static int ehci_run (struct usb_hcd *hcd + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 temp; + u32 hcc_params; ++#if defined(CONFIG_ARCH_ZYNQ) ++ void __iomem *non_ehci = hcd->regs; ++#endif + + hcd->uses_new_polling = 1; + +@@ -638,7 +656,11 @@ static int ehci_run (struct usb_hcd *hcd + + ehci_writel(ehci, INTR_MASK, + &ehci->regs->intr_enable); /* Turn On Interrupts */ +- ++#if defined(CONFIG_ARCH_ZYNQ) ++ /* Modifying FIFO Burst Threshold value from 2 to 8 */ ++ temp = readl(non_ehci + 0x164); ++ ehci_writel(ehci, 0x00080000, non_ehci + 0x164); ++#endif + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. +@@ -691,6 +713,29 @@ static irqreturn_t ehci_irq (struct usb_ + + status = ehci_readl(ehci, &ehci->regs->status); + ++#ifdef CONFIG_USB_ZYNQ_PHY ++ if (hcd->phy) { ++ /* A device */ ++ if (hcd->phy->otg->default_a && ++ (hcd->phy->state == OTG_STATE_A_PERIPHERAL)) { ++ spin_unlock(&ehci->lock); ++ return IRQ_NONE; ++ } ++ /* B device */ ++ if (!hcd->phy->otg->default_a && ++ ((hcd->phy->state != OTG_STATE_B_WAIT_ACON) && ++ (hcd->phy->state != OTG_STATE_B_HOST))) { ++ spin_unlock(&ehci->lock); ++ return IRQ_NONE; ++ } ++ /* If HABA is set and B-disconnect occurs, don't process ++ * that interrupt */ ++ if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) { ++ spin_unlock(&ehci->lock); ++ return IRQ_NONE; ++ } ++ } ++#endif + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + ehci_dbg (ehci, "device removed\n"); +@@ -1246,6 +1291,11 @@ MODULE_LICENSE ("GPL"); + #define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver + #endif + ++#ifdef CONFIG_USB_EHCI_XUSBPS ++#include "ehci-xilinx-usbps.c" ++#define PLATFORM_DRIVER ehci_xusbps_driver ++#endif ++ + #ifdef CONFIG_USB_W90X900_EHCI + #include "ehci-w90x900.c" + #define PLATFORM_DRIVER ehci_hcd_w90x900_driver +--- a/drivers/usb/host/ehci-hub.c ++++ b/drivers/usb/host/ehci-hub.c +@@ -1018,9 +1018,22 @@ static int ehci_hub_control ( + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ ++#ifdef CONFIG_USB_ZYNQ_PHY ++ if (hcd->phy && (hcd->self.otg_port == (wIndex + 1)) ++ && (hcd->self.b_hnp_enable || ++ hcd->self.is_b_host)) ++ ehci->start_hnp(ehci); ++ else { ++ temp &= ~PORT_WKCONN_E; ++ temp |= PORT_WKDISC_E | PORT_WKOC_E; ++ ehci_writel(ehci, temp | PORT_SUSPEND, ++ status_reg); ++ } ++#else + temp &= ~PORT_WKCONN_E; + temp |= PORT_WKDISC_E | PORT_WKOC_E; + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); ++#endif + if (ehci->has_hostpc) { + spin_unlock_irqrestore(&ehci->lock, flags); + msleep(5);/* 5ms for HCD enter low pwr mode */ +@@ -1036,9 +1049,18 @@ static int ehci_hub_control ( + set_bit(wIndex, &ehci->suspended_ports); + break; + case USB_PORT_FEAT_POWER: ++#ifdef CONFIG_USB_ZYNQ_PHY ++ /* Check if otg is enabled */ ++ if (!hcd->phy) { ++ if (HCS_PPC(ehci->hcs_params)) ++ ehci_writel(ehci, temp | PORT_POWER, ++ status_reg); ++ } ++#else + if (HCS_PPC (ehci->hcs_params)) + ehci_writel(ehci, temp | PORT_POWER, + status_reg); ++#endif + break; + case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) +--- a/drivers/usb/host/ehci-xilinx-of.c ++++ b/drivers/usb/host/ehci-xilinx-of.c +@@ -220,6 +220,21 @@ static int ehci_hcd_xilinx_of_remove(str + return 0; + } + ++/** ++ * ehci_hcd_xilinx_of_shutdown - shutdown the hcd ++ * @op: pointer to platform_device structure that is to be removed ++ * ++ * Properly shutdown the hcd, call driver's shutdown routine. ++ */ ++static void ehci_hcd_xilinx_of_shutdown(struct platform_device *op) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(op); ++ ++ if (hcd->driver->shutdown) ++ hcd->driver->shutdown(hcd); ++} ++ ++ + static const struct of_device_id ehci_hcd_xilinx_of_match[] = { + {.compatible = "xlnx,xps-usb-host-1.00.a",}, + {}, +@@ -229,7 +244,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_ + static struct platform_driver ehci_hcd_xilinx_of_driver = { + .probe = ehci_hcd_xilinx_of_probe, + .remove = ehci_hcd_xilinx_of_remove, +- .shutdown = usb_hcd_platform_shutdown, ++ .shutdown = ehci_hcd_xilinx_of_shutdown, + .driver = { + .name = "xilinx-of-ehci", + .owner = THIS_MODULE, +--- /dev/null ++++ b/drivers/usb/host/ehci-xilinx-usbps.c +@@ -0,0 +1,531 @@ ++/* ++ * Xilinx PS USB Host Controller Driver. ++ * ++ * Copyright (C) 2011 Xilinx, Inc. ++ * ++ * This file is based on ehci-fsl.c file with few minor modifications ++ * to support Xilinx PS USB controller. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ehci-xilinx-usbps.h" ++ ++#ifdef CONFIG_USB_ZYNQ_PHY ++/******************************************************************** ++ * OTG related functions ++ ********************************************************************/ ++static int ehci_xusbps_reinit(struct ehci_hcd *ehci); ++ ++/* This connection event is useful when a OTG test device is connected. ++ In that case, the device connect notify event will not be generated ++ since the device will be suspended before complete enumeration. ++*/ ++static int ehci_xusbps_update_device(struct usb_hcd *hcd, struct usb_device ++ *udev) ++{ ++ struct xusbps_otg *xotg = xceiv_to_xotg(hcd->phy); ++ ++ if (udev->portnum == hcd->self.otg_port) { ++ /* HNP test device */ ++ if ((le16_to_cpu(udev->descriptor.idVendor) == 0x1a0a && ++ le16_to_cpu(udev->descriptor.idProduct) == 0xbadd)) { ++ if (xotg->otg.otg->default_a == 1) ++ xotg->hsm.b_conn = 1; ++ else ++ xotg->hsm.a_conn = 1; ++ xusbps_update_transceiver(); ++ } ++ } ++ return 0; ++} ++ ++static void ehci_xusbps_start_hnp(struct ehci_hcd *ehci) ++{ ++ const unsigned port = ehci_to_hcd(ehci)->self.otg_port - 1; ++ struct usb_hcd *hcd = ehci_to_hcd(ehci); ++ unsigned long flags; ++ u32 portsc; ++ ++ local_irq_save(flags); ++ portsc = ehci_readl(ehci, &ehci->regs->port_status[port]); ++ portsc |= PORT_SUSPEND; ++ ehci_writel(ehci, portsc, &ehci->regs->port_status[port]); ++ local_irq_restore(flags); ++ ++ otg_start_hnp(hcd->phy->otg); ++} ++ ++static int ehci_xusbps_otg_start_host(struct usb_phy *otg) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(otg->otg->host); ++ struct xusbps_otg *xotg = ++ xceiv_to_xotg(hcd->phy); ++ ++ usb_add_hcd(hcd, xotg->irq, IRQF_SHARED); ++ return 0; ++} ++ ++static int ehci_xusbps_otg_stop_host(struct usb_phy *otg) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(otg->otg->host); ++ ++ usb_remove_hcd(hcd); ++ return 0; ++} ++#endif ++ ++static int xusbps_ehci_clk_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* if a rate change is announced we need to check whether we can ++ * maintain the current frequency by changing the clock ++ * dividers. ++ */ ++ /* fall through */ ++ case POST_RATE_CHANGE: ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++/* configure so an HC device and id are always provided */ ++/* always called with process context; sleeping is OK */ ++ ++/** ++ * usb_hcd_xusbps_probe - initialize XUSBPS-based HCDs ++ * @driver: Driver to be used for this HCD ++ * @pdev: USB Host Controller being probed ++ * Context: !in_interrupt() ++ * ++ * Allocates basic resources for this USB host controller. ++ * ++ */ ++static int usb_hcd_xusbps_probe(const struct hc_driver *driver, ++ struct platform_device *pdev) ++{ ++ struct xusbps_usb2_platform_data *pdata; ++ struct usb_hcd *hcd; ++ int irq; ++ int retval; ++ ++ pr_debug("initializing XUSBPS-SOC USB Controller\n"); ++ ++ /* Need platform data for setup */ ++ pdata = (struct xusbps_usb2_platform_data *)pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, ++ "No platform data for %s.\n", dev_name(&pdev->dev)); ++ return -ENODEV; ++ } ++ ++ /* ++ * This is a host mode driver, verify that we're supposed to be ++ * in host mode. ++ */ ++ if (!((pdata->operating_mode == XUSBPS_USB2_DR_HOST) || ++ (pdata->operating_mode == XUSBPS_USB2_MPH_HOST) || ++ (pdata->operating_mode == XUSBPS_USB2_DR_OTG))) { ++ dev_err(&pdev->dev, "Non Host Mode configured for %s. Wrong \ ++ driver linked.\n", dev_name(&pdev->dev)); ++ return -ENODEV; ++ } ++ ++ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err1; ++ } ++ ++ irq = pdata->irq; ++ hcd->regs = pdata->regs; ++ ++ if (hcd->regs == NULL) { ++ dev_dbg(&pdev->dev, "error mapping memory\n"); ++ retval = -EFAULT; ++ goto err2; ++ } ++ ++ retval = clk_prepare_enable(pdata->clk); ++ if (retval) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto err2; ++ } ++ ++ pdata->clk_rate_change_nb.notifier_call = xusbps_ehci_clk_notifier_cb; ++ pdata->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(pdata->clk, &pdata->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ ++ ++ /* ++ * do platform specific init: check the clock, grab/config pins, etc. ++ */ ++ if (pdata->init && pdata->init(pdev)) { ++ retval = -ENODEV; ++ goto err_out_clk_unreg_notif; ++ } ++ ++#ifdef CONFIG_USB_ZYNQ_PHY ++ if (pdata->otg) { ++ struct xusbps_otg *xotg; ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ ++ hcd->self.otg_port = 1; ++ hcd->phy = pdata->otg; ++ retval = otg_set_host(hcd->phy->otg, ++ &ehci_to_hcd(ehci)->self); ++ if (retval) ++ goto err_out_clk_unreg_notif; ++ xotg = xceiv_to_xotg(hcd->phy); ++ ehci->start_hnp = ehci_xusbps_start_hnp; ++ xotg->start_host = ehci_xusbps_otg_start_host; ++ xotg->stop_host = ehci_xusbps_otg_stop_host; ++ /* inform otg driver about host driver */ ++ xusbps_update_transceiver(); ++ } else { ++ retval = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (retval) ++ goto err_out_clk_unreg_notif; ++ ++ /* ++ * Enable vbus on ULPI - zedboard requirement ++ * to get host mode to work ++ */ ++ if (pdata->ulpi) ++ otg_set_vbus(pdata->ulpi->otg, 1); ++ } ++#else ++ /* Don't need to set host mode here. It will be done by tdi_reset() */ ++ retval = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (retval) ++ goto err_out_clk_unreg_notif; ++#endif ++ return retval; ++ ++err_out_clk_unreg_notif: ++ clk_notifier_unregister(pdata->clk, &pdata->clk_rate_change_nb); ++ clk_disable_unprepare(pdata->clk); ++err2: ++ usb_put_hcd(hcd); ++err1: ++ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); ++ if (pdata->exit) ++ pdata->exit(pdev); ++ ++ return retval; ++} ++ ++/* may be called without controller electrically present */ ++/* may be called with controller, bus, and devices active */ ++ ++/** ++ * usb_hcd_xusbps_remove - shutdown processing for XUSBPS-based HCDs ++ * @dev: USB Host Controller being removed ++ * Context: !in_interrupt() ++ * ++ * Reverses the effect of usb_hcd_xusbps_probe(). ++ * ++ */ ++static void usb_hcd_xusbps_remove(struct usb_hcd *hcd, ++ struct platform_device *pdev) ++{ ++ struct xusbps_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ usb_remove_hcd(hcd); ++ ++ /* ++ * do platform specific un-initialization: ++ * release iomux pins, disable clock, etc. ++ */ ++ if (pdata->exit) ++ pdata->exit(pdev); ++ usb_put_hcd(hcd); ++ clk_notifier_unregister(pdata->clk, &pdata->clk_rate_change_nb); ++ clk_disable_unprepare(pdata->clk); ++} ++ ++static void ehci_xusbps_setup_phy(struct ehci_hcd *ehci, ++ enum xusbps_usb2_phy_modes phy_mode, ++ unsigned int port_offset) ++{ ++ u32 portsc; ++ ++ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); ++ portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); ++ ++ switch (phy_mode) { ++ case XUSBPS_USB2_PHY_ULPI: ++ portsc |= PORT_PTS_ULPI; ++ break; ++ case XUSBPS_USB2_PHY_SERIAL: ++ portsc |= PORT_PTS_SERIAL; ++ break; ++ case XUSBPS_USB2_PHY_UTMI_WIDE: ++ portsc |= PORT_PTS_PTW; ++ /* fall through */ ++ case XUSBPS_USB2_PHY_UTMI: ++ portsc |= PORT_PTS_UTMI; ++ break; ++ case XUSBPS_USB2_PHY_NONE: ++ break; ++ } ++ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); ++} ++ ++static void ehci_xusbps_usb_setup(struct ehci_hcd *ehci) ++{ ++ struct usb_hcd *hcd = ehci_to_hcd(ehci); ++ struct xusbps_usb2_platform_data *pdata; ++ ++ pdata = hcd->self.controller->platform_data; ++ ++ if ((pdata->operating_mode == XUSBPS_USB2_DR_HOST) || ++ (pdata->operating_mode == XUSBPS_USB2_DR_OTG)) ++ ehci_xusbps_setup_phy(ehci, pdata->phy_mode, 0); ++ ++ if (pdata->operating_mode == XUSBPS_USB2_MPH_HOST) { ++ if (pdata->port_enables & XUSBPS_USB2_PORT0_ENABLED) ++ ehci_xusbps_setup_phy(ehci, pdata->phy_mode, 0); ++ if (pdata->port_enables & XUSBPS_USB2_PORT1_ENABLED) ++ ehci_xusbps_setup_phy(ehci, pdata->phy_mode, 1); ++ } ++} ++ ++/* ++ * FIXME USB: EHCI: remove ehci_port_power() routine ++ *(sha1: c73cee717e7d5da0698acb720ad1219646fe4f46) ++ */ ++static void ehci_port_power(struct ehci_hcd *ehci, int is_on) ++{ ++ unsigned port; ++ ++ if (!HCS_PPC(ehci->hcs_params)) ++ return; ++ ++ ehci_dbg(ehci, "...power%s ports...\n", is_on ? "up" : "down"); ++ for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) ++ (void) ehci_hub_control(ehci_to_hcd(ehci), ++ is_on ? SetPortFeature : ClearPortFeature, ++ USB_PORT_FEAT_POWER, ++ port--, NULL, 0); ++ /* Flush those writes */ ++ ehci_readl(ehci, &ehci->regs->command); ++ msleep(20); ++} ++ ++/* called after powerup, by probe or system-pm "wakeup" */ ++static int ehci_xusbps_reinit(struct ehci_hcd *ehci) ++{ ++#ifdef CONFIG_USB_ZYNQ_PHY ++ struct usb_hcd *hcd = ehci_to_hcd(ehci); ++#endif ++ ++ ehci_xusbps_usb_setup(ehci); ++#ifdef CONFIG_USB_ZYNQ_PHY ++ /* Don't turn off port power in OTG mode */ ++ if (!hcd->phy) ++#endif ++ ehci_port_power(ehci, 0); ++ ++ return 0; ++} ++ ++struct ehci_xusbps { ++ struct ehci_hcd ehci; ++ ++#ifdef CONFIG_PM ++ /* Saved USB PHY settings, need to restore after deep sleep. */ ++ u32 usb_ctrl; ++#endif ++}; ++ ++/* called during probe() after chip reset completes */ ++static int ehci_xusbps_setup(struct usb_hcd *hcd) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int retval; ++ ++ /* EHCI registers start at offset 0x100 */ ++ ehci->caps = hcd->regs + 0x100; ++ ehci->regs = hcd->regs + 0x100 + ++ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ++ dbg_hcs_params(ehci, "reset"); ++ dbg_hcc_params(ehci, "reset"); ++ ++ /* cache this readonly data; minimize chip reads */ ++ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ ++ hcd->has_tt = 1; ++ ++ /* data structure init */ ++ retval = ehci_init(hcd); ++ if (retval) ++ return retval; ++ ++ retval = ehci_halt(ehci); ++ if (retval) ++ return retval; ++ ++ ehci->sbrn = 0x20; ++ ++ ehci_reset(ehci); ++ ++ retval = ehci_xusbps_reinit(ehci); ++ return retval; ++} ++ ++static void ehci_xusbps_shutdown(struct usb_hcd *hcd) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ ++ if (ehci->regs) ++ ehci_shutdown(hcd); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int ehci_xusbps_drv_suspend(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct xusbps_usb2_platform_data *pdata = dev->platform_data; ++ ++ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), ++ device_may_wakeup(dev)); ++ ++ clk_disable(pdata->clk); ++ ++ return 0; ++} ++ ++static int ehci_xusbps_drv_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ struct xusbps_usb2_platform_data *pdata = dev->platform_data; ++ int ret; ++ ++ ret = clk_enable(pdata->clk); ++ if (ret) { ++ dev_err(dev, "cannot enable clock. resume failed\n"); ++ return ret; ++ } ++ ++ ehci_prepare_ports_for_controller_resume(ehci); ++ ++ usb_root_hub_lost_power(hcd->self.root_hub); ++ ++ ehci_reset(ehci); ++ ehci_xusbps_reinit(ehci); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops ehci_xusbps_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(ehci_xusbps_drv_suspend, ehci_xusbps_drv_resume) ++}; ++#define EHCI_XUSBPS_PM_OPS (&ehci_xusbps_pm_ops) ++ ++#else /* ! CONFIG_PM_SLEEP */ ++#define EHCI_XUSBPS_PM_OPS NULL ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++ ++static const struct hc_driver ehci_xusbps_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Xilinx PS USB EHCI Host Controller", ++ .hcd_priv_size = sizeof(struct ehci_xusbps), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = ehci_irq, ++ .flags = HCD_USB2 | HCD_MEMORY, ++ ++ /* ++ * basic lifecycle operations ++ */ ++ .reset = ehci_xusbps_setup, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_xusbps_shutdown, ++ ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ .endpoint_reset = ehci_endpoint_reset, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = ehci_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++ .bus_suspend = ehci_bus_suspend, ++ .bus_resume = ehci_bus_resume, ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++ ++ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, ++#ifdef CONFIG_USB_ZYNQ_PHY ++ .update_device = ehci_xusbps_update_device, ++#endif ++}; ++ ++static int ehci_xusbps_drv_probe(struct platform_device *pdev) ++{ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ /* FIXME we only want one one probe() not two */ ++ return usb_hcd_xusbps_probe(&ehci_xusbps_hc_driver, pdev); ++} ++ ++static int ehci_xusbps_drv_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ ++ /* FIXME we only want one one remove() not two */ ++ usb_hcd_xusbps_remove(hcd, pdev); ++ return 0; ++} ++ ++MODULE_ALIAS("platform:xusbps-ehci"); ++ ++static struct platform_driver ehci_xusbps_driver = { ++ .probe = ehci_xusbps_drv_probe, ++ .remove = ehci_xusbps_drv_remove, ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .name = "xusbps-ehci", ++ .pm = EHCI_XUSBPS_PM_OPS, ++ }, ++}; +--- /dev/null ++++ b/drivers/usb/host/ehci-xilinx-usbps.h +@@ -0,0 +1,33 @@ ++/* ++ * Xilinx PS USB Host Controller Driver Header file. ++ * ++ * Copyright (C) 2011 Xilinx, Inc. ++ * ++ * This file is based on ehci-fsl.h file with few minor modifications ++ * to support Xilinx PS USB controller. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _EHCI_XILINX_XUSBPS_H ++#define _EHCI_XILINX_XUSBPS_H ++ ++#include ++ ++/* offsets for the non-ehci registers in the XUSBPS SOC USB controller */ ++#define XUSBPS_SOC_USB_ULPIVP 0x170 ++#define XUSBPS_SOC_USB_PORTSC1 0x184 ++#define PORT_PTS_MSK (3<<30) ++#define PORT_PTS_UTMI (0<<30) ++#define PORT_PTS_ULPI (2<<30) ++#define PORT_PTS_SERIAL (3<<30) ++#define PORT_PTS_PTW (1<<28) ++#define XUSBPS_SOC_USB_PORTSC2 0x188 ++ ++#endif /* _EHCI_XILINX_XUSBPS_H */ +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -176,6 +176,14 @@ struct ehci_hcd { /* one per controlle + unsigned long resuming_ports; /* which ports have + started to resume */ + ++#ifdef CONFIG_USB_ZYNQ_PHY ++ /* ++ * OTG controllers and transceivers need software interaction; ++ * other external transceivers should be software-transparent ++ */ ++ void (*start_hnp)(struct ehci_hcd *ehci); ++#endif ++ + /* per-HC memory pools (could be per-bus, but ...) */ + struct dma_pool *qh_pool; /* qh per active urb */ + struct dma_pool *qtd_pool; /* one or more per qh */ +--- /dev/null ++++ b/drivers/usb/host/xusbps-dr-of.c +@@ -0,0 +1,331 @@ ++/* ++ * Xilinx PS USB Driver for device tree support. ++ * ++ * Copyright (C) 2011 Xilinx, Inc. ++ * ++ * This file is based on fsl-mph-dr-of.c file with few minor modifications ++ * to support Xilinx PS USB controller. ++ * ++ * Setup platform devices needed by the dual-role USB controller modules ++ * based on the description in flat device tree. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ehci-xilinx-usbps.h" ++ ++static u64 dma_mask = 0xFFFFFFF0; ++ ++struct xusbps_dev_data { ++ char *dr_mode; /* controller mode */ ++ char *drivers[3]; /* drivers to instantiate for this mode */ ++ enum xusbps_usb2_operating_modes op_mode; /* operating mode */ ++}; ++ ++struct xusbps_host_data { ++ struct clk *clk; ++}; ++ ++static struct xusbps_dev_data dr_mode_data[] = { ++ { ++ .dr_mode = "host", ++ .drivers = { "xusbps-ehci", NULL, NULL, }, ++ .op_mode = XUSBPS_USB2_DR_HOST, ++ }, ++ { ++ .dr_mode = "otg", ++ .drivers = { "xusbps-otg", "xusbps-ehci", "xusbps-udc", }, ++ .op_mode = XUSBPS_USB2_DR_OTG, ++ }, ++ { ++ .dr_mode = "peripheral", ++ .drivers = { "xusbps-udc", NULL, NULL, }, ++ .op_mode = XUSBPS_USB2_DR_DEVICE, ++ }, ++}; ++ ++static struct xusbps_dev_data *get_dr_mode_data( ++ struct device_node *np) ++{ ++ const unsigned char *prop; ++ int i; ++ ++ prop = of_get_property(np, "dr_mode", NULL); ++ if (prop) { ++ for (i = 0; i < ARRAY_SIZE(dr_mode_data); i++) { ++ if (!strcmp(prop, dr_mode_data[i].dr_mode)) ++ return &dr_mode_data[i]; ++ } ++ } ++ pr_warn("%s: Invalid 'dr_mode' property, fallback to host mode\n", ++ np->full_name); ++ return &dr_mode_data[0]; /* mode not specified, use host */ ++} ++ ++static enum xusbps_usb2_phy_modes determine_usb_phy(const char *phy_type) ++{ ++ if (!phy_type) ++ return XUSBPS_USB2_PHY_NONE; ++ if (!strcasecmp(phy_type, "ulpi")) ++ return XUSBPS_USB2_PHY_ULPI; ++ if (!strcasecmp(phy_type, "utmi")) ++ return XUSBPS_USB2_PHY_UTMI; ++ if (!strcasecmp(phy_type, "utmi_wide")) ++ return XUSBPS_USB2_PHY_UTMI_WIDE; ++ if (!strcasecmp(phy_type, "serial")) ++ return XUSBPS_USB2_PHY_SERIAL; ++ ++ return XUSBPS_USB2_PHY_NONE; ++} ++ ++static struct platform_device *xusbps_device_register( ++ struct platform_device *ofdev, ++ struct xusbps_usb2_platform_data *pdata, ++ const char *name, int id) ++{ ++ struct platform_device *pdev; ++ const struct resource *res = ofdev->resource; ++ unsigned int num = ofdev->num_resources; ++ struct xusbps_usb2_platform_data *pdata1; ++ int retval; ++ ++ pdev = platform_device_alloc(name, id); ++ if (!pdev) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ pdev->dev.parent = &ofdev->dev; ++ ++ pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; ++ pdev->dev.dma_mask = &dma_mask; ++ ++ retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); ++ if (retval) ++ goto error; ++ ++ if (num) { ++ retval = platform_device_add_resources(pdev, res, num); ++ if (retval) ++ goto error; ++ } ++ ++ retval = platform_device_add(pdev); ++ if (retval) ++ goto error; ++ ++ pdata1 = pdev->dev.platform_data; ++ /* Copy the otg transceiver pointer into host/device platform data */ ++ if (pdata1->otg) ++ pdata->otg = pdata1->otg; ++ ++ return pdev; ++ ++error: ++ platform_device_put(pdev); ++ return ERR_PTR(retval); ++} ++ ++static int xusbps_dr_of_probe(struct platform_device *ofdev) ++{ ++ struct device_node *np = ofdev->dev.of_node; ++ struct platform_device *usb_dev; ++ struct xusbps_usb2_platform_data data, *pdata; ++ struct xusbps_dev_data *dev_data; ++ struct xusbps_host_data *hdata; ++ const unsigned char *prop; ++ static unsigned int idx; ++ struct resource *res; ++ int i, phy_init; ++ int ret; ++ ++ pdata = &data; ++ memset(pdata, 0, sizeof(data)); ++ ++ res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0); ++ if (IS_ERR(res)) { ++ dev_err(&ofdev->dev, ++ "IRQ not found\n"); ++ return PTR_ERR(res); ++ } ++ pdata->irq = res->start; ++ ++ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); ++ pdata->regs = devm_ioremap_resource(&ofdev->dev, res); ++ if (IS_ERR(pdata->regs)) { ++ dev_err(&ofdev->dev, "unable to iomap registers\n"); ++ return PTR_ERR(pdata->regs); ++ } ++ ++ dev_data = get_dr_mode_data(np); ++ pdata->operating_mode = dev_data->op_mode; ++ ++ prop = of_get_property(np, "phy_type", NULL); ++ pdata->phy_mode = determine_usb_phy(prop); ++ ++ hdata = devm_kzalloc(&ofdev->dev, sizeof(*hdata), GFP_KERNEL); ++ if (!hdata) ++ return -ENOMEM; ++ platform_set_drvdata(ofdev, hdata); ++ ++ hdata->clk = devm_clk_get(&ofdev->dev, NULL); ++ if (IS_ERR(hdata->clk)) { ++ dev_err(&ofdev->dev, "input clock not found.\n"); ++ return PTR_ERR(hdata->clk); ++ } ++ ++ ret = clk_prepare_enable(hdata->clk); ++ if (ret) { ++ dev_err(&ofdev->dev, "Unable to enable APER clock.\n"); ++ return ret; ++ } ++ ++ pdata->clk = hdata->clk; ++ ++ /* If ULPI phy type, set it up */ ++ if (pdata->phy_mode == XUSBPS_USB2_PHY_ULPI) { ++ pdata->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, ++ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); ++ if (pdata->ulpi) { ++ pdata->ulpi->io_priv = pdata->regs + ++ XUSBPS_SOC_USB_ULPIVP; ++ ++ phy_init = usb_phy_init(pdata->ulpi); ++ if (phy_init) { ++ dev_err(&ofdev->dev, ++ "Unable to init USB phy, missing?\n"); ++ ret = -ENODEV; ++ goto err_out_clk_disable; ++ } ++ } else { ++ dev_err(&ofdev->dev, ++ "Unable to create ULPI transceiver\n"); ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { ++ if (!dev_data->drivers[i]) ++ continue; ++ usb_dev = xusbps_device_register(ofdev, pdata, ++ dev_data->drivers[i], idx); ++ if (IS_ERR(usb_dev)) { ++ dev_err(&ofdev->dev, "Can't register usb device\n"); ++ ret = PTR_ERR(usb_dev); ++ goto err_out_clk_disable; ++ } ++ } ++ idx++; ++ return 0; ++ ++err_out_clk_disable: ++ clk_disable_unprepare(hdata->clk); ++ ++ return ret; ++} ++ ++static int __unregister_subdev(struct device *dev, void *d) ++{ ++ platform_device_unregister(to_platform_device(dev)); ++ return 0; ++} ++ ++static int xusbps_dr_of_remove(struct platform_device *ofdev) ++{ ++ struct xusbps_host_data *hdata = platform_get_drvdata(ofdev); ++ ++ device_for_each_child(&ofdev->dev, NULL, __unregister_subdev); ++ clk_disable_unprepare(hdata->clk); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int xusbps_dr_of_suspend(struct device *dev) ++{ ++ struct xusbps_host_data *hdata = dev_get_drvdata(dev); ++ ++ clk_disable(hdata->clk); ++ ++ return 0; ++} ++ ++static int xusbps_dr_of_resume(struct device *dev) ++{ ++ struct xusbps_host_data *hdata = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_enable(hdata->clk); ++ if (ret) { ++ dev_err(dev, "cannot enable clock. resume failed\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(xusbps_pm_ops, xusbps_dr_of_suspend, ++ xusbps_dr_of_resume); ++ ++static const struct of_device_id xusbps_dr_of_match[] = { ++ { .compatible = "xlnx,ps7-usb-1.00.a" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, xusbps_dr_of_match); ++ ++static struct platform_driver xusbps_dr_driver = { ++ .driver = { ++ .name = "xusbps-dr", ++ .owner = THIS_MODULE, ++ .of_match_table = xusbps_dr_of_match, ++ .pm = &xusbps_pm_ops, ++ }, ++ .probe = xusbps_dr_of_probe, ++ .remove = xusbps_dr_of_remove, ++}; ++ ++#ifdef CONFIG_USB_ZYNQ_PHY ++extern struct platform_driver xusbps_otg_driver; ++ ++static int __init xusbps_dr_init(void) ++{ ++ int retval; ++ ++ /* Register otg driver first */ ++ retval = platform_driver_register(&xusbps_otg_driver); ++ if (retval != 0) ++ return retval; ++ ++ return platform_driver_register(&xusbps_dr_driver); ++} ++module_init(xusbps_dr_init); ++ ++static void __exit xusbps_dr_exit(void) ++{ ++ platform_driver_unregister(&xusbps_dr_driver); ++} ++module_exit(xusbps_dr_exit); ++#else ++module_platform_driver(xusbps_dr_driver); ++#endif ++ ++MODULE_DESCRIPTION("XUSBPS DR OF devices driver"); ++MODULE_AUTHOR("Xilinx"); ++MODULE_LICENSE("GPL"); +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -210,4 +210,16 @@ config USB_ULPI_VIEWPORT + Provides read/write operations to the ULPI phy register set for + controllers with a viewport register (e.g. Chipidea/ARC controllers). + ++config USB_ZYNQ_PHY ++ tristate "Xilinx Zynq USB OTG dual-role support" ++ depends on USB && ARCH_ZYNQ && USB_EHCI_XUSBPS && USB_GADGET_XUSBPS && USB_OTG ++ select USB_OTG_UTILS ++ help ++ Say Y here if you want to build Xilinx USB PS OTG ++ driver in kernel. This driver implements role ++ switch between EHCI host driver and USB gadget driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called xilinx_usbps_otg. ++ + endif # USB_PHY +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -31,3 +31,4 @@ obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-us + obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o + obj-$(CONFIG_USB_ULPI) += phy-ulpi.o + obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o ++obj-$(CONFIG_USB_ZYNQ_PHY) += phy-zynq-usb.o +--- /dev/null ++++ b/drivers/usb/phy/phy-zynq-usb.c +@@ -0,0 +1,2305 @@ ++/* ++ * Xilinx PS USB otg driver. ++ * ++ * Copyright 2011 Xilinx, Inc. ++ * ++ * This file is based on langwell_otg.c file with few minor modifications ++ * to support Xilinx PS USB controller. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* This driver helps to switch Xilinx OTG controller function between host ++ * and peripheral. It works with EHCI driver and Xilinx client controller ++ * driver together. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../core/usb.h" ++ ++#include ++#include ++ ++#define DRIVER_NAME "xusbps-otg" ++ ++static const char driver_name[] = DRIVER_NAME; ++ ++/* HSM timers */ ++static inline struct xusbps_otg_timer *otg_timer_initializer ++(void (*function)(unsigned long), unsigned long expires, unsigned long data) ++{ ++ struct xusbps_otg_timer *timer; ++ timer = kmalloc(sizeof(struct xusbps_otg_timer), GFP_KERNEL); ++ if (timer == NULL) ++ return timer; ++ ++ timer->function = function; ++ timer->expires = expires; ++ timer->data = data; ++ return timer; ++} ++ ++static struct xusbps_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, ++ *b_se0_srp_tmr, *b_srp_init_tmr; ++ ++static struct list_head active_timers; ++ ++static struct xusbps_otg *the_transceiver; ++ ++/* host/client notify transceiver when event affects HNP state */ ++void xusbps_update_transceiver(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ dev_dbg(xotg->dev, "transceiver is updated\n"); ++ ++ if (!xotg->qwork) ++ return; ++ ++ queue_work(xotg->qwork, &xotg->work); ++} ++EXPORT_SYMBOL(xusbps_update_transceiver); ++ ++static int xusbps_otg_set_host(struct usb_otg *otg, ++ struct usb_bus *host) ++{ ++ otg->host = host; ++ ++ if (host) { ++ if (otg->default_a) ++ host->is_b_host = 0; ++ else ++ host->is_b_host = 1; ++ } ++ ++ return 0; ++} ++ ++static int xusbps_otg_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->gadget = gadget; ++ ++ if (gadget) { ++ if (otg->default_a) ++ gadget->is_a_peripheral = 1; ++ else ++ gadget->is_a_peripheral = 0; ++ } ++ ++ return 0; ++} ++ ++static int xusbps_otg_set_power(struct usb_phy *otg, ++ unsigned mA) ++{ ++ return 0; ++} ++ ++/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ ++static int xusbps_otg_set_vbus(struct usb_otg *otg, bool enabled) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ dev_dbg(xotg->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off"); ++ ++ /* Enable ulpi VBUS if required */ ++ if (xotg->ulpi) ++ otg_set_vbus(xotg->ulpi->otg, enabled); ++ ++ val = readl(xotg->base + CI_PORTSC1); ++ ++ if (enabled) ++ writel((val | PORTSC_PP), xotg->base + CI_PORTSC1); ++ else ++ writel((val & ~PORTSC_PP), xotg->base + CI_PORTSC1); ++ ++ dev_dbg(xotg->dev, "%s --->\n", __func__); ++ ++ return 0; ++} ++ ++/* Charge vbus for VBUS pulsing in SRP */ ++static void xusbps_otg_chrg_vbus(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ val = readl(xotg->base + CI_OTGSC) & ~OTGSC_INTSTS_MASK; ++ ++ if (on) ++ /* stop discharging, start charging */ ++ val = (val & ~OTGSC_VD) | OTGSC_VC; ++ else ++ /* stop charging */ ++ val &= ~OTGSC_VC; ++ ++ writel(val, xotg->base + CI_OTGSC); ++} ++ ++#if 0 ++ ++/* Discharge vbus through a resistor to ground */ ++static void xusbps_otg_dischrg_vbus(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ val = readl(xotg->base + CI_OTGSC) & ~OTGSC_INTSTS_MASK; ++ ++ if (on) ++ /* stop charging, start discharging */ ++ val = (val & ~OTGSC_VC) | OTGSC_VD; ++ else ++ val &= ~OTGSC_VD; ++ ++ writel(val, xotg->base + CI_OTGSC); ++} ++ ++#endif ++ ++/* Start SRP */ ++static int xusbps_otg_start_srp(struct usb_otg *otg) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ dev_warn(xotg->dev, "Starting SRP...\n"); ++ dev_dbg(xotg->dev, "%s --->\n", __func__); ++ ++ val = readl(xotg->base + CI_OTGSC); ++ ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, ++ xotg->base + CI_OTGSC); ++ ++ /* Check if the data plus is finished or not */ ++ msleep(8); ++ val = readl(xotg->base + CI_OTGSC); ++ if (val & (OTGSC_HADP | OTGSC_DP)) ++ dev_dbg(xotg->dev, "DataLine SRP Error\n"); ++ ++ /* If Vbus is valid, then update the hsm */ ++ if (val & OTGSC_BSV) { ++ dev_dbg(xotg->dev, "no b_sess_vld interrupt\n"); ++ ++ xotg->hsm.b_sess_vld = 1; ++ xusbps_update_transceiver(); ++ return 0; ++ } ++ ++ dev_warn(xotg->dev, "Starting VBUS Pulsing...\n"); ++ ++ /* Disable interrupt - b_sess_vld */ ++ val = readl(xotg->base + CI_OTGSC); ++ val &= (~(OTGSC_BSVIE | OTGSC_BSEIE)); ++ writel(val, xotg->base + CI_OTGSC); ++ ++ /* Start VBus SRP, drive vbus to generate VBus pulse */ ++ xusbps_otg_chrg_vbus(1); ++ msleep(15); ++ xusbps_otg_chrg_vbus(0); ++ ++ /* Enable interrupt - b_sess_vld*/ ++ val = readl(xotg->base + CI_OTGSC); ++ dev_dbg(xotg->dev, "after VBUS pulse otgsc = %x\n", val); ++ ++ val |= (OTGSC_BSVIE | OTGSC_BSEIE); ++ writel(val, xotg->base + CI_OTGSC); ++ ++ /* If Vbus is valid, then update the hsm */ ++ if (val & OTGSC_BSV) { ++ dev_dbg(xotg->dev, "no b_sess_vld interrupt\n"); ++ ++ xotg->hsm.b_sess_vld = 1; ++ xusbps_update_transceiver(); ++ } ++ ++ dev_dbg(xotg->dev, "%s <---\n", __func__); ++ return 0; ++} ++ ++/* Start HNP */ ++static int xusbps_otg_start_hnp(struct usb_otg *otg) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ unsigned long flag = 0; ++ ++ dev_warn(xotg->dev, "Starting HNP...\n"); ++ dev_dbg(xotg->dev, "%s --->\n", __func__); ++ ++ if (xotg->otg.otg->default_a && xotg->otg.otg->host && ++ xotg->otg.otg->host->b_hnp_enable) { ++ xotg->hsm.a_suspend_req = 1; ++ flag = 1; ++ } ++ ++ if (!xotg->otg.otg->default_a && xotg->otg.otg->host && ++ xotg->hsm.b_bus_req) { ++ xotg->hsm.b_bus_req = 0; ++ flag = 1; ++ } ++ ++ if (flag) { ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ } else ++ dev_warn(xotg->dev, "HNP not supported\n"); ++ ++ dev_dbg(xotg->dev, "%s <---\n", __func__); ++ return 0; ++} ++ ++/* stop SOF via bus_suspend */ ++static void xusbps_otg_loc_sof(int on) ++{ ++ /* Not used */ ++} ++ ++static void xusbps_otg_phy_low_power(int on) ++{ ++ /* Not used */ ++} ++ ++/* After drv vbus, add 2 ms delay to set PHCD */ ++static void xusbps_otg_phy_low_power_wait(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ dev_dbg(xotg->dev, "add 2ms delay before programing PHCD\n"); ++ ++ mdelay(2); ++ xusbps_otg_phy_low_power(on); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/* Enable/Disable OTG interrupt */ ++static void xusbps_otg_intr(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ dev_dbg(xotg->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); ++ ++ val = readl(xotg->base + CI_OTGSC); ++ ++ /* OTGSC_INT_MASK doesn't contains 1msInt */ ++ if (on) { ++ val = val | (OTGSC_INT_MASK); ++ writel(val, xotg->base + CI_OTGSC); ++ } else { ++ val = val & ~(OTGSC_INT_MASK); ++ writel(val, xotg->base + CI_OTGSC); ++ } ++ ++ dev_dbg(xotg->dev, "%s <---\n", __func__); ++} ++#endif ++ ++/* set HAAR: Hardware Assist Auto-Reset */ ++static void xusbps_otg_HAAR(int on) ++{ ++ /* Not used */ ++} ++ ++/* set HABA: Hardware Assist B-Disconnect to A-Connect */ ++static void xusbps_otg_HABA(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ dev_dbg(xotg->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); ++ ++ val = readl(xotg->base + CI_OTGSC); ++ if (on) ++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, ++ xotg->base + CI_OTGSC); ++ else ++ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, ++ xotg->base + CI_OTGSC); ++ ++ dev_dbg(xotg->dev, "%s <---\n", __func__); ++} ++ ++static int xusbps_otg_check_se0_srp(int on) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ int delay_time = TB_SE0_SRP * 10; ++ u32 val; ++ ++ dev_dbg(xotg->dev, "%s --->\n", __func__); ++ ++ do { ++ udelay(100); ++ if (!delay_time--) ++ break; ++ val = readl(xotg->base + CI_PORTSC1); ++ val &= PORTSC_LS; ++ } while (!val); ++ ++ dev_dbg(xotg->dev, "%s <---\n", __func__); ++ return val; ++} ++ ++/* The timeout callback function to set time out bit */ ++static void set_tmout(unsigned long indicator) ++{ ++ *(int *)indicator = 1; ++} ++ ++static void xusbps_otg_msg(unsigned long indicator) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ switch (indicator) { ++ case 2: ++ case 4: ++ case 6: ++ case 7: ++ dev_warn(xotg->dev, ++ "OTG:%lu - deivce not responding\n", indicator); ++ break; ++ case 3: ++ dev_warn(xotg->dev, ++ "OTG:%lu - deivce not supported\n", indicator); ++ break; ++ default: ++ dev_warn(xotg->dev, "Do not have this msg\n"); ++ break; ++ } ++} ++ ++/* Initialize timers */ ++static int xusbps_otg_init_timers(struct otg_hsm *hsm) ++{ ++ /* HSM used timers */ ++ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, ++ (unsigned long)&hsm->a_wait_vrise_tmout); ++ if (a_wait_vrise_tmr == NULL) ++ return -ENOMEM; ++ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, ++ (unsigned long)&hsm->a_aidl_bdis_tmout); ++ if (a_aidl_bdis_tmr == NULL) ++ return -ENOMEM; ++ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, ++ (unsigned long)&hsm->b_se0_srp); ++ if (b_se0_srp_tmr == NULL) ++ return -ENOMEM; ++ b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, ++ (unsigned long)&hsm->b_srp_init_tmout); ++ if (b_srp_init_tmr == NULL) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* Free timers */ ++static void xusbps_otg_free_timers(void) ++{ ++ kfree(a_wait_vrise_tmr); ++ kfree(a_aidl_bdis_tmr); ++ kfree(b_se0_srp_tmr); ++ kfree(b_srp_init_tmr); ++} ++ ++/* The timeout callback function to set time out bit */ ++static void xusbps_otg_timer_fn(unsigned long indicator) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ *(int *)indicator = 1; ++ ++ dev_dbg(xotg->dev, "kernel timer - timeout\n"); ++ ++ xusbps_update_transceiver(); ++} ++ ++/* kernel timer used instead of HW based interrupt */ ++static void xusbps_otg_add_ktimer(enum xusbps_otg_timer_type timers) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ unsigned long j = jiffies; ++ unsigned long data, time; ++ ++ switch (timers) { ++ case TA_WAIT_VRISE_TMR: ++ xotg->hsm.a_wait_vrise_tmout = 0; ++ data = (unsigned long)&xotg->hsm.a_wait_vrise_tmout; ++ time = TA_WAIT_VRISE; ++ break; ++ case TA_WAIT_BCON_TMR: ++ xotg->hsm.a_wait_bcon_tmout = 0; ++ data = (unsigned long)&xotg->hsm.a_wait_bcon_tmout; ++ time = TA_WAIT_BCON; ++ break; ++ case TA_AIDL_BDIS_TMR: ++ xotg->hsm.a_aidl_bdis_tmout = 0; ++ data = (unsigned long)&xotg->hsm.a_aidl_bdis_tmout; ++ time = TA_AIDL_BDIS; ++ break; ++ case TB_ASE0_BRST_TMR: ++ xotg->hsm.b_ase0_brst_tmout = 0; ++ data = (unsigned long)&xotg->hsm.b_ase0_brst_tmout; ++ time = TB_ASE0_BRST; ++ break; ++ case TB_SRP_INIT_TMR: ++ xotg->hsm.b_srp_init_tmout = 0; ++ data = (unsigned long)&xotg->hsm.b_srp_init_tmout; ++ time = TB_SRP_INIT; ++ break; ++ case TB_SRP_FAIL_TMR: ++ xotg->hsm.b_srp_fail_tmout = 0; ++ data = (unsigned long)&xotg->hsm.b_srp_fail_tmout; ++ time = TB_SRP_FAIL; ++ break; ++ case TB_BUS_SUSPEND_TMR: ++ xotg->hsm.b_bus_suspend_tmout = 0; ++ data = (unsigned long)&xotg->hsm.b_bus_suspend_tmout; ++ time = TB_BUS_SUSPEND; ++ break; ++ default: ++ dev_dbg(xotg->dev, "unkown timer, cannot enable it\n"); ++ return; ++ } ++ ++ xotg->hsm_timer.data = data; ++ xotg->hsm_timer.function = xusbps_otg_timer_fn; ++ xotg->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ ++ ++ add_timer(&xotg->hsm_timer); ++ ++ dev_dbg(xotg->dev, "add timer successfully\n"); ++} ++ ++/* Add timer to timer list */ ++static void xusbps_otg_add_timer(void *gtimer) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ struct xusbps_otg_timer *timer = (struct xusbps_otg_timer *)gtimer; ++ struct xusbps_otg_timer *tmp_timer; ++ u32 val32; ++ ++ /* Check if the timer is already in the active list, ++ * if so update timer count ++ */ ++ list_for_each_entry(tmp_timer, &active_timers, list) ++ if (tmp_timer == timer) { ++ timer->count = timer->expires; ++ return; ++ } ++ timer->count = timer->expires; ++ ++ if (list_empty(&active_timers)) { ++ val32 = readl(xotg->base + CI_OTGSC); ++ writel(val32 | OTGSC_1MSE, xotg->base + CI_OTGSC); ++ } ++ ++ list_add_tail(&timer->list, &active_timers); ++} ++ ++/* Remove timer from the timer list; clear timeout status */ ++static void xusbps_otg_del_timer(void *gtimer) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ struct xusbps_otg_timer *timer = (struct xusbps_otg_timer *)gtimer; ++ struct xusbps_otg_timer *tmp_timer, *del_tmp; ++ u32 val32; ++ ++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) ++ if (tmp_timer == timer) ++ list_del(&timer->list); ++ ++ if (list_empty(&active_timers)) { ++ val32 = readl(xotg->base + CI_OTGSC); ++ writel(val32 & ~OTGSC_1MSE, xotg->base + CI_OTGSC); ++ } ++} ++ ++/* Reduce timer count by 1, and find timeout conditions.*/ ++static int xusbps_otg_tick_timer(u32 *int_sts) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ struct xusbps_otg_timer *tmp_timer, *del_tmp; ++ int expired = 0; ++ ++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { ++ tmp_timer->count--; ++ /* check if timer expires */ ++ if (!tmp_timer->count) { ++ list_del(&tmp_timer->list); ++ tmp_timer->function(tmp_timer->data); ++ expired = 1; ++ } ++ } ++ ++ if (list_empty(&active_timers)) { ++ dev_dbg(xotg->dev, "tick timer: disable 1ms int\n"); ++ *int_sts = *int_sts & ~OTGSC_1MSE; ++ } ++ return expired; ++} ++ ++static void reset_otg(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ int delay_time = 1000; ++ u32 val; ++ ++ dev_dbg(xotg->dev, "reseting OTG controller ...\n"); ++ val = readl(xotg->base + CI_USBCMD); ++ writel(val | USBCMD_RST, xotg->base + CI_USBCMD); ++ do { ++ udelay(100); ++ if (!delay_time--) ++ dev_dbg(xotg->dev, "reset timeout\n"); ++ val = readl(xotg->base + CI_USBCMD); ++ val &= USBCMD_RST; ++ } while (val != 0); ++ dev_dbg(xotg->dev, "reset done.\n"); ++} ++ ++static void set_host_mode(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ reset_otg(); ++ val = readl(xotg->base + CI_USBMODE); ++ val = (val & (~USBMODE_CM)) | USBMODE_HOST; ++ writel(val, xotg->base + CI_USBMODE); ++} ++ ++static void set_client_mode(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val; ++ ++ reset_otg(); ++ val = readl(xotg->base + CI_USBMODE); ++ val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; ++ writel(val, xotg->base + CI_USBMODE); ++} ++ ++static void init_hsm(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val32; ++ ++ /* read OTGSC after reset */ ++ val32 = readl(xotg->base + CI_OTGSC); ++ ++ /* set init state */ ++ if (val32 & OTGSC_ID) { ++ xotg->hsm.id = 1; ++ xotg->otg.otg->default_a = 0; ++ set_client_mode(); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ } else { ++ xotg->hsm.id = 0; ++ xotg->otg.otg->default_a = 1; ++ set_host_mode(); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ } ++ ++ /* set session indicator */ ++ if (!xotg->otg.otg->default_a) { ++ if (val32 & OTGSC_BSE) ++ xotg->hsm.b_sess_end = 1; ++ if (val32 & OTGSC_BSV) ++ xotg->hsm.b_sess_vld = 1; ++ } else { ++ if (val32 & OTGSC_ASV) ++ xotg->hsm.a_sess_vld = 1; ++ if (val32 & OTGSC_AVV) ++ xotg->hsm.a_vbus_vld = 1; ++ } ++ ++ /* defautly power the bus */ ++ xotg->hsm.a_bus_req = 0; ++ xotg->hsm.a_bus_drop = 0; ++ /* defautly don't request bus as B device */ ++ xotg->hsm.b_bus_req = 0; ++ /* no system error */ ++ xotg->hsm.a_clr_err = 0; ++ ++ xusbps_otg_phy_low_power_wait(1); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void update_hsm(void) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ u32 val32; ++ ++ /* read OTGSC */ ++ val32 = readl(xotg->base + CI_OTGSC); ++ ++ xotg->hsm.id = !!(val32 & OTGSC_ID); ++ if (!xotg->otg.otg->default_a) { ++ xotg->hsm.b_sess_end = !!(val32 & OTGSC_BSE); ++ xotg->hsm.b_sess_vld = !!(val32 & OTGSC_BSV); ++ } else { ++ xotg->hsm.a_sess_vld = !!(val32 & OTGSC_ASV); ++ xotg->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV); ++ } ++} ++#endif ++ ++static irqreturn_t otg_dummy_irq(int irq, void *_dev) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ void __iomem *reg_base = _dev; ++ u32 val; ++ u32 int_mask = 0; ++ ++ val = readl(reg_base + CI_USBMODE); ++ if ((val & USBMODE_CM) != USBMODE_DEVICE) ++ return IRQ_NONE; ++ ++ val = readl(reg_base + CI_USBSTS); ++ int_mask = val & INTR_DUMMY_MASK; ++ ++ if (int_mask == 0) ++ return IRQ_NONE; ++ ++ /* Clear interrupts */ ++ writel(int_mask, reg_base + CI_USBSTS); ++ ++ /* clear hsm.b_conn here since host driver can't detect it ++ * otg_dummy_irq called means B-disconnect happened. ++ */ ++ if (xotg->hsm.b_conn) { ++ xotg->hsm.b_conn = 0; ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t otg_irq(int irq, void *_dev) ++{ ++ struct xusbps_otg *xotg = _dev; ++ u32 int_sts, int_en; ++ u32 int_mask = 0; ++ int flag = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xotg->lock, flags); ++ int_sts = readl(xotg->base + CI_OTGSC); ++ int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; ++ int_mask = int_sts & int_en; ++ ++ if (int_mask == 0) { ++ spin_unlock_irqrestore(&xotg->lock, flags); ++ return IRQ_NONE; ++ } ++ ++ writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, ++ xotg->base + CI_OTGSC); ++ if (int_mask & OTGSC_IDIS) { ++ dev_dbg(xotg->dev, "%s: id change int\n", __func__); ++ xotg->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; ++ dev_dbg(xotg->dev, "id = %d\n", xotg->hsm.id); ++ flag = 1; ++ } ++ if (int_mask & OTGSC_DPIS) { ++ dev_dbg(xotg->dev, "%s: data pulse int\n", __func__); ++ if (xotg->otg.otg->default_a) ++ xotg->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; ++ dev_dbg(xotg->dev, "data pulse = %d\n", xotg->hsm.a_srp_det); ++ flag = 1; ++ } ++ if (int_mask & OTGSC_BSEIS) { ++ dev_dbg(xotg->dev, "%s: b session end int\n", __func__); ++ if (!xotg->otg.otg->default_a) ++ xotg->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; ++ dev_dbg(xotg->dev, "b_sess_end = %d\n", xotg->hsm.b_sess_end); ++ flag = 1; ++ } ++ if (int_mask & OTGSC_BSVIS) { ++ dev_dbg(xotg->dev, "%s: b session valid int\n", __func__); ++ if (!xotg->otg.otg->default_a) ++ xotg->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; ++ dev_dbg(xotg->dev, "b_sess_vld = %d\n", xotg->hsm.b_sess_vld); ++ flag = 1; ++ } ++ if (int_mask & OTGSC_ASVIS) { ++ dev_dbg(xotg->dev, "%s: a session valid int\n", __func__); ++ if (xotg->otg.otg->default_a) ++ xotg->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; ++ dev_dbg(xotg->dev, "a_sess_vld = %d\n", xotg->hsm.a_sess_vld); ++ flag = 1; ++ } ++ if (int_mask & OTGSC_AVVIS) { ++ dev_dbg(xotg->dev, "%s: a vbus valid int\n", __func__); ++ if (xotg->otg.otg->default_a) ++ xotg->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; ++ dev_dbg(xotg->dev, "a_vbus_vld = %d\n", xotg->hsm.a_vbus_vld); ++ flag = 1; ++ } ++ ++ if (int_mask & OTGSC_1MSS) { ++ /* need to schedule otg_work if any timer is expired */ ++ if (xusbps_otg_tick_timer(&int_sts)) ++ flag = 1; ++ } ++ ++ if (flag) ++ xusbps_update_transceiver(); ++ ++ spin_unlock_irqrestore(&xotg->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * xotg_usbdev_notify - Notifier function called by usb core. ++ * @self: Pointer to notifier_block structure ++ * @action: action which caused the notifier function call. ++ * @dev: Pointer to the usb device structure. ++ * ++ * This function is a call back function used by usb core to notify ++ * device attach/detach events. This is used by OTG state machine. ++ * ++ * returns: Always returns NOTIFY_OK. ++ **/ ++static int xotg_usbdev_notify(struct notifier_block *self, ++ unsigned long action, void *dev) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ struct usb_phy *otg = &xotg->otg; ++ unsigned long otg_port; ++ struct usb_device *udev_otg = NULL; ++ struct usb_device *udev; ++ u32 flag; ++ ++ udev = (struct usb_device *)dev; ++ ++ if (!otg->otg->host) ++ return NOTIFY_OK; ++ ++ otg_port = otg->otg->host->otg_port; ++ ++ if (otg->otg->host->root_hub) ++ udev_otg = usb_hub_find_child(otg->otg->host->root_hub, ++ otg_port - 1); ++ ++ /* Not otg device notification */ ++ if (udev != udev_otg) ++ return NOTIFY_OK; ++ ++ switch (action) { ++ case USB_DEVICE_ADD: ++ if (xotg->otg.otg->default_a == 1) ++ xotg->hsm.b_conn = 1; ++ else ++ xotg->hsm.a_conn = 1; ++ flag = 1; ++ break; ++ case USB_DEVICE_REMOVE: ++ if (xotg->otg.otg->default_a == 1) ++ xotg->hsm.b_conn = 0; ++ else ++ xotg->hsm.a_conn = 0; ++ flag = 1; ++ break; ++ } ++ if (flag) ++ xusbps_update_transceiver(); ++ ++ return NOTIFY_OK; ++} ++ ++static void xusbps_otg_work(struct work_struct *work) ++{ ++ struct xusbps_otg *xotg; ++ int retval; ++ ++ xotg = container_of(work, struct xusbps_otg, work); ++ ++ dev_dbg(xotg->dev, "%s: old state = %s\n", __func__, ++ usb_otg_state_string(xotg->otg.state)); ++ ++ switch (xotg->otg.state) { ++ case OTG_STATE_UNDEFINED: ++ case OTG_STATE_B_IDLE: ++ if (!xotg->hsm.id) { ++ xusbps_otg_del_timer(b_srp_init_tmr); ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xotg->otg.otg->default_a = 1; ++ xotg->hsm.a_srp_det = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.b_sess_vld) { ++ xusbps_otg_del_timer(b_srp_init_tmr); ++ del_timer_sync(&xotg->hsm_timer); ++ xotg->hsm.b_bus_req = 0; ++ xotg->hsm.b_sess_end = 0; ++ xotg->hsm.a_bus_suspend = 0; ++ xusbps_otg_chrg_vbus(0); ++ ++ if (xotg->start_peripheral) { ++ xotg->start_peripheral(&xotg->otg); ++ xotg->otg.state = OTG_STATE_B_PERIPHERAL; ++ } else ++ dev_dbg(xotg->dev, ++ "client driver not loaded\n"); ++ } else if (xotg->hsm.b_srp_init_tmout) { ++ xotg->hsm.b_srp_init_tmout = 0; ++ dev_warn(xotg->dev, "SRP init timeout\n"); ++ } else if (xotg->hsm.b_srp_fail_tmout) { ++ xotg->hsm.b_srp_fail_tmout = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ /* No silence failure */ ++ xusbps_otg_msg(6); ++ dev_warn(xotg->dev, "SRP failed\n"); ++ } else if (xotg->hsm.b_bus_req && xotg->hsm.b_sess_end) { ++ del_timer_sync(&xotg->hsm_timer); ++ /* workaround for b_se0_srp detection */ ++ retval = xusbps_otg_check_se0_srp(0); ++ if (retval) { ++ xotg->hsm.b_bus_req = 0; ++ dev_dbg(xotg->dev, "LS isn't SE0, try later\n"); ++ } else { ++ /* clear the PHCD before start srp */ ++ xusbps_otg_phy_low_power(0); ++ ++ /* Start SRP */ ++ xusbps_otg_add_timer(b_srp_init_tmr); ++ xotg->otg.otg->start_srp(xotg->otg.otg); ++ xusbps_otg_del_timer(b_srp_init_tmr); ++ xusbps_otg_add_ktimer(TB_SRP_FAIL_TMR); ++ ++ /* reset PHY low power mode here */ ++ xusbps_otg_phy_low_power_wait(1); ++ } ++ } ++ break; ++ case OTG_STATE_B_SRP_INIT: ++ if (!xotg->hsm.id) { ++ xotg->otg.otg->default_a = 1; ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_chrg_vbus(0); ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.b_sess_vld) { ++ xusbps_otg_chrg_vbus(0); ++ if (xotg->start_peripheral) { ++ xotg->start_peripheral(&xotg->otg); ++ xotg->otg.state = OTG_STATE_B_PERIPHERAL; ++ } else ++ dev_dbg(xotg->dev, ++ "client driver not loaded\n"); ++ } ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (!xotg->hsm.id) { ++ xotg->otg.otg->default_a = 1; ++ xotg->hsm.a_srp_det = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.b_sess_vld) { ++ xotg->hsm.b_hnp_enable = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ } else if (xotg->hsm.b_bus_req && xotg->otg.otg->gadget && ++ xotg->otg.otg->gadget->b_hnp_enable && ++ xotg->hsm.a_bus_suspend) { ++ dev_warn(xotg->dev, "HNP detected\n"); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ xusbps_otg_HAAR(1); ++ xotg->hsm.a_conn = 0; ++ ++ xotg->otg.state = OTG_STATE_B_WAIT_ACON; ++ if (xotg->start_host) { ++ xotg->start_host(&xotg->otg); ++ } else ++ dev_dbg(xotg->dev, ++ "host driver not loaded.\n"); ++ ++ xotg->hsm.a_bus_resume = 0; ++ xusbps_otg_add_ktimer(TB_ASE0_BRST_TMR); ++ } ++ break; ++ ++ case OTG_STATE_B_WAIT_ACON: ++ if (!xotg->hsm.id) { ++ /* delete hsm timer for b_ase0_brst_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xotg->otg.otg->default_a = 1; ++ xotg->hsm.a_srp_det = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ ++ xusbps_otg_HAAR(0); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.b_sess_vld) { ++ /* delete hsm timer for b_ase0_brst_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xotg->hsm.b_hnp_enable = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ xusbps_otg_HAAR(0); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ } else if (xotg->hsm.a_conn) { ++ /* delete hsm timer for b_ase0_brst_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xusbps_otg_HAAR(0); ++ xotg->otg.state = OTG_STATE_B_HOST; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.a_bus_resume || ++ xotg->hsm.b_ase0_brst_tmout) { ++ dev_warn(xotg->dev, "A device connect failed\n"); ++ /* delete hsm timer for b_ase0_brst_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xusbps_otg_HAAR(0); ++ xusbps_otg_msg(7); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ xotg->hsm.a_bus_suspend = 0; ++ xotg->hsm.b_bus_req = 0; ++ xotg->otg.state = OTG_STATE_B_PERIPHERAL; ++ if (xotg->start_peripheral) ++ xotg->start_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver not loaded.\n"); ++ } ++ break; ++ ++ case OTG_STATE_B_HOST: ++ if (!xotg->hsm.id) { ++ xotg->otg.otg->default_a = 1; ++ xotg->hsm.a_srp_det = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.b_sess_vld) { ++ xotg->hsm.b_hnp_enable = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ } else if ((!xotg->hsm.b_bus_req) || ++ (!xotg->hsm.a_conn)) { ++ xotg->hsm.b_bus_req = 0; ++ xusbps_otg_loc_sof(0); ++ ++ /* Fix: The kernel crash in usb_port_suspend ++ during HNP */ ++ msleep(20); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ xotg->hsm.a_bus_suspend = 0; ++ xotg->otg.state = OTG_STATE_B_PERIPHERAL; ++ if (xotg->start_peripheral) ++ xotg->start_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver not loaded.\n"); ++ } ++ break; ++ ++ case OTG_STATE_A_IDLE: ++ xotg->otg.otg->default_a = 1; ++ if (xotg->hsm.id) { ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.b_bus_req = 0; ++ xotg->hsm.vbus_srp_up = 0; ++ ++ xusbps_otg_chrg_vbus(0); ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.a_bus_drop && ++ (xotg->hsm.a_srp_det || xotg->hsm.a_bus_req)) { ++ dev_warn(xotg->dev, ++ "SRP detected or User has requested for the Bus\n"); ++ xusbps_otg_phy_low_power(0); ++ ++ /* Turn on VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, true); ++ ++ xotg->hsm.vbus_srp_up = 0; ++ xotg->hsm.a_wait_vrise_tmout = 0; ++ xusbps_otg_add_timer(a_wait_vrise_tmr); ++ xotg->otg.state = OTG_STATE_A_WAIT_VRISE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.a_bus_drop && xotg->hsm.a_sess_vld) { ++ xotg->hsm.vbus_srp_up = 1; ++ } else if (!xotg->hsm.a_sess_vld && xotg->hsm.vbus_srp_up) { ++ msleep(10); ++ xusbps_otg_phy_low_power(0); ++ ++ /* Turn on VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, true); ++ xotg->hsm.a_srp_det = 1; ++ xotg->hsm.vbus_srp_up = 0; ++ xotg->hsm.a_wait_vrise_tmout = 0; ++ xusbps_otg_add_timer(a_wait_vrise_tmr); ++ xotg->otg.state = OTG_STATE_A_WAIT_VRISE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.a_sess_vld && ++ !xotg->hsm.vbus_srp_up) { ++ xusbps_otg_phy_low_power(1); ++ } ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ if (xotg->hsm.id) { ++ xusbps_otg_del_timer(a_wait_vrise_tmr); ++ xotg->hsm.b_bus_req = 0; ++ xotg->otg.otg->default_a = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ set_client_mode(); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ } else if (xotg->hsm.a_vbus_vld) { ++ xusbps_otg_del_timer(a_wait_vrise_tmr); ++ xotg->hsm.b_conn = 0; ++ if (xotg->start_host) ++ xotg->start_host(&xotg->otg); ++ else { ++ dev_dbg(xotg->dev, "host driver not loaded.\n"); ++ break; ++ } ++ xusbps_otg_add_ktimer(TA_WAIT_BCON_TMR); ++ xotg->otg.state = OTG_STATE_A_WAIT_BCON; ++ } else if (xotg->hsm.a_wait_vrise_tmout) { ++ xotg->hsm.b_conn = 0; ++ if (xotg->hsm.a_vbus_vld) { ++ if (xotg->start_host) ++ xotg->start_host(&xotg->otg); ++ else { ++ dev_dbg(xotg->dev, ++ "host driver not loaded.\n"); ++ break; ++ } ++ xusbps_otg_add_ktimer(TA_WAIT_BCON_TMR); ++ xotg->otg.state = OTG_STATE_A_WAIT_BCON; ++ } else { ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_A_VBUS_ERR; ++ } ++ } ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ if (xotg->hsm.id) { ++ /* delete hsm timer for a_wait_bcon_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ set_client_mode(); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.a_vbus_vld) { ++ /* delete hsm timer for a_wait_bcon_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (xotg->hsm.a_bus_drop || ++ (xotg->hsm.a_wait_bcon_tmout && ++ !xotg->hsm.a_bus_req)) { ++ dev_warn(xotg->dev, "B connect timeout\n"); ++ /* delete hsm timer for a_wait_bcon_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (xotg->hsm.b_conn) { ++ /* delete hsm timer for a_wait_bcon_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xotg->hsm.a_suspend_req = 0; ++ /* Make it zero as it should not be used by driver */ ++ xotg->hsm.a_bus_req = 0; ++ xotg->hsm.a_srp_det = 0; ++ xotg->otg.state = OTG_STATE_A_HOST; ++ } ++ break; ++ case OTG_STATE_A_HOST: ++ if (xotg->hsm.id) { ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ set_client_mode(); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.a_bus_drop || ++ (xotg->otg.otg->host && ++ !xotg->otg.otg->host->b_hnp_enable && ++ !xotg->hsm.a_bus_req)) { ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (!xotg->hsm.a_vbus_vld) { ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (xotg->otg.otg->host && ++ xotg->otg.otg->host->b_hnp_enable && ++ (!xotg->hsm.a_bus_req || ++ xotg->hsm.a_suspend_req)) { ++ /* Set HABA to enable hardware assistance to signal ++ * A-connect after receiver B-disconnect. Hardware ++ * will then set client mode and enable URE, SLE and ++ * PCE after the assistance. otg_dummy_irq is used to ++ * clean these ints when client driver is not resumed. ++ */ ++ if (request_irq(xotg->irq, otg_dummy_irq, IRQF_SHARED, ++ driver_name, xotg->base) != 0) { ++ dev_dbg(xotg->dev, ++ "request interrupt %d failed\n", ++ xotg->irq); ++ } ++ /* set HABA */ ++ xusbps_otg_HABA(1); ++ xotg->hsm.b_bus_resume = 0; ++ xotg->hsm.a_aidl_bdis_tmout = 0; ++ xusbps_otg_loc_sof(0); ++ /* clear PHCD to enable HW timer */ ++ xusbps_otg_phy_low_power(0); ++ xusbps_otg_add_timer(a_aidl_bdis_tmr); ++ xotg->otg.state = OTG_STATE_A_SUSPEND; ++ } else if (!xotg->hsm.b_conn || !xotg->hsm.a_bus_req) { ++ xusbps_otg_add_ktimer(TA_WAIT_BCON_TMR); ++ xotg->otg.state = OTG_STATE_A_WAIT_BCON; ++ } ++ break; ++ case OTG_STATE_A_SUSPEND: ++ if (xotg->hsm.id) { ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ free_irq(xotg->irq, xotg->base); ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.b_bus_req = 0; ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.a_bus_req || ++ xotg->hsm.b_bus_resume) { ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ free_irq(xotg->irq, xotg->base); ++ xotg->hsm.a_suspend_req = 0; ++ xusbps_otg_loc_sof(1); ++ xotg->otg.state = OTG_STATE_A_HOST; ++ } else if (xotg->hsm.a_aidl_bdis_tmout || ++ xotg->hsm.a_bus_drop) { ++ dev_warn(xotg->dev, "B disconnect timeout\n"); ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ free_irq(xotg->irq, xotg->base); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (!xotg->hsm.b_conn && xotg->otg.otg->host && ++ xotg->otg.otg->host->b_hnp_enable) { ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ free_irq(xotg->irq, xotg->base); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ xotg->hsm.b_bus_suspend = 0; ++ xotg->hsm.b_bus_suspend_vld = 0; ++ ++ xotg->otg.state = OTG_STATE_A_PERIPHERAL; ++ /* msleep(200); */ ++ if (xotg->start_peripheral) ++ xotg->start_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver not loaded.\n"); ++ xusbps_otg_add_ktimer(TB_BUS_SUSPEND_TMR); ++ break; ++ } else if (!xotg->hsm.a_vbus_vld) { ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ free_irq(xotg->irq, xotg->base); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_A_VBUS_ERR; ++ } ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ if (xotg->hsm.id) { ++ /* delete hsm timer for b_bus_suspend_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.b_bus_req = 0; ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ set_client_mode(); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (!xotg->hsm.a_vbus_vld) { ++ /* delete hsm timer for b_bus_suspend_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xusbps_otg_phy_low_power_wait(1); ++ xotg->otg.state = OTG_STATE_A_VBUS_ERR; ++ } else if (xotg->hsm.a_bus_drop) { ++ /* delete hsm timer for b_bus_suspend_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_WAIT_VFALL; ++ } else if (xotg->hsm.b_bus_suspend) { ++ dev_warn(xotg->dev, "HNP detected\n"); ++ /* delete hsm timer for b_bus_suspend_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ xotg->otg.state = OTG_STATE_A_WAIT_BCON; ++ if (xotg->start_host) ++ xotg->start_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver not loaded.\n"); ++ xusbps_otg_add_ktimer(TA_WAIT_BCON_TMR); ++ } else if (xotg->hsm.b_bus_suspend_tmout) { ++ u32 val; ++ val = readl(xotg->base + CI_PORTSC1); ++ if (!(val & PORTSC_SUSP)) ++ break; ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "client driver has been removed.\n"); ++ ++ xotg->otg.state = OTG_STATE_A_WAIT_BCON; ++ if (xotg->start_host) ++ xotg->start_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, ++ "host driver not loaded.\n"); ++ xusbps_otg_add_ktimer(TA_WAIT_BCON_TMR); ++ } ++ break; ++ case OTG_STATE_A_VBUS_ERR: ++ if (xotg->hsm.id) { ++ xotg->otg.otg->default_a = 0; ++ xotg->hsm.a_clr_err = 0; ++ xotg->hsm.a_srp_det = 0; ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.a_clr_err) { ++ xotg->hsm.a_clr_err = 0; ++ xotg->hsm.a_srp_det = 0; ++ reset_otg(); ++ init_hsm(); ++ if (xotg->otg.state == OTG_STATE_A_IDLE) ++ xusbps_update_transceiver(); ++ } else { ++ /* FW will clear PHCD bit when any VBus ++ * event detected. Reset PHCD to 1 again */ ++ xusbps_otg_phy_low_power(1); ++ } ++ break; ++ case OTG_STATE_A_WAIT_VFALL: ++ if (xotg->hsm.id) { ++ xotg->otg.otg->default_a = 0; ++ set_client_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ xusbps_update_transceiver(); ++ } else if (xotg->hsm.a_bus_req) { ++ ++ /* Turn on VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, true); ++ xotg->hsm.a_wait_vrise_tmout = 0; ++ xusbps_otg_add_timer(a_wait_vrise_tmr); ++ xotg->otg.state = OTG_STATE_A_WAIT_VRISE; ++ } else if (!xotg->hsm.a_sess_vld) { ++ xotg->hsm.a_srp_det = 0; ++ set_host_mode(); ++ xusbps_otg_phy_low_power(1); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ dev_dbg(xotg->dev, "%s: new state = %s\n", __func__, ++ usb_otg_state_string(xotg->otg.state)); ++} ++ ++static ssize_t ++show_registers(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ char *next; ++ unsigned size, t; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, ++ "\n" ++ "USBCMD = 0x%08x\n" ++ "USBSTS = 0x%08x\n" ++ "USBINTR = 0x%08x\n" ++ "ASYNCLISTADDR = 0x%08x\n" ++ "PORTSC1 = 0x%08x\n" ++ "OTGSC = 0x%08x\n" ++ "USBMODE = 0x%08x\n", ++ readl(xotg->base + 0x140), ++ readl(xotg->base + 0x144), ++ readl(xotg->base + 0x148), ++ readl(xotg->base + 0x158), ++ readl(xotg->base + 0x184), ++ readl(xotg->base + 0x1a4), ++ readl(xotg->base + 0x1a8) ++ ); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); ++ ++static ssize_t ++show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ char *next; ++ unsigned size, t; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ ++ if (xotg->otg.otg->host) ++ xotg->hsm.a_set_b_hnp_en = xotg->otg.otg->host->b_hnp_enable; ++ ++ if (xotg->otg.otg->gadget) ++ xotg->hsm.b_hnp_enable = xotg->otg.otg->gadget->b_hnp_enable; ++ ++ t = scnprintf(next, size, ++ "\n" ++ "current state = %s\n" ++ "a_bus_resume = \t%d\n" ++ "a_bus_suspend = \t%d\n" ++ "a_conn = \t%d\n" ++ "a_sess_vld = \t%d\n" ++ "a_srp_det = \t%d\n" ++ "a_vbus_vld = \t%d\n" ++ "b_bus_resume = \t%d\n" ++ "b_bus_suspend = \t%d\n" ++ "b_conn = \t%d\n" ++ "b_se0_srp = \t%d\n" ++ "b_sess_end = \t%d\n" ++ "b_sess_vld = \t%d\n" ++ "id = \t%d\n" ++ "a_set_b_hnp_en = \t%d\n" ++ "b_srp_done = \t%d\n" ++ "b_hnp_enable = \t%d\n" ++ "a_wait_vrise_tmout = \t%d\n" ++ "a_wait_bcon_tmout = \t%d\n" ++ "a_aidl_bdis_tmout = \t%d\n" ++ "b_ase0_brst_tmout = \t%d\n" ++ "a_bus_drop = \t%d\n" ++ "a_bus_req = \t%d\n" ++ "a_clr_err = \t%d\n" ++ "a_suspend_req = \t%d\n" ++ "b_bus_req = \t%d\n" ++ "b_bus_suspend_tmout = \t%d\n" ++ "b_bus_suspend_vld = \t%d\n", ++ usb_otg_state_string(xotg->otg.state), ++ xotg->hsm.a_bus_resume, ++ xotg->hsm.a_bus_suspend, ++ xotg->hsm.a_conn, ++ xotg->hsm.a_sess_vld, ++ xotg->hsm.a_srp_det, ++ xotg->hsm.a_vbus_vld, ++ xotg->hsm.b_bus_resume, ++ xotg->hsm.b_bus_suspend, ++ xotg->hsm.b_conn, ++ xotg->hsm.b_se0_srp, ++ xotg->hsm.b_sess_end, ++ xotg->hsm.b_sess_vld, ++ xotg->hsm.id, ++ xotg->hsm.a_set_b_hnp_en, ++ xotg->hsm.b_srp_done, ++ xotg->hsm.b_hnp_enable, ++ xotg->hsm.a_wait_vrise_tmout, ++ xotg->hsm.a_wait_bcon_tmout, ++ xotg->hsm.a_aidl_bdis_tmout, ++ xotg->hsm.b_ase0_brst_tmout, ++ xotg->hsm.a_bus_drop, ++ xotg->hsm.a_bus_req, ++ xotg->hsm.a_clr_err, ++ xotg->hsm.a_suspend_req, ++ xotg->hsm.b_bus_req, ++ xotg->hsm.b_bus_suspend_tmout, ++ xotg->hsm.b_bus_suspend_vld ++ ); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); ++ ++static ssize_t ++get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ char *next; ++ unsigned size, t; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", xotg->hsm.a_bus_req); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_a_bus_req(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ if (!xotg->otg.otg->default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ xotg->hsm.a_bus_req = 0; ++ dev_dbg(xotg->dev, "User request: a_bus_req = 0\n"); ++ } else if (buf[0] == '1') { ++ /* If a_bus_drop is TRUE, a_bus_req can't be set */ ++ if (xotg->hsm.a_bus_drop) ++ return -1; ++ xotg->hsm.a_bus_req = 1; ++ dev_dbg(xotg->dev, "User request: a_bus_req = 1\n"); ++ } ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); ++ ++static ssize_t ++get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ char *next; ++ unsigned size, t; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", xotg->hsm.a_bus_drop); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_a_bus_drop(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ if (!xotg->otg.otg->default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ xotg->hsm.a_bus_drop = 0; ++ dev_dbg(xotg->dev, "User request: a_bus_drop = 0\n"); ++ } else if (buf[0] == '1') { ++ xotg->hsm.a_bus_drop = 1; ++ xotg->hsm.a_bus_req = 0; ++ dev_dbg(xotg->dev, "User request: a_bus_drop = 1\n"); ++ dev_dbg(xotg->dev, "User request: and a_bus_req = 0\n"); ++ } ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, ++ set_a_bus_drop); ++ ++static ssize_t ++get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ char *next; ++ unsigned size, t; ++ ++ next = buf; ++ size = PAGE_SIZE; ++ ++ t = scnprintf(next, size, "%d", xotg->hsm.b_bus_req); ++ size -= t; ++ next += t; ++ ++ return PAGE_SIZE - size; ++} ++ ++static ssize_t ++set_b_bus_req(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ if (xotg->otg.otg->default_a) ++ return -1; ++ ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '0') { ++ xotg->hsm.b_bus_req = 0; ++ dev_dbg(xotg->dev, "User request: b_bus_req = 0\n"); ++ } else if (buf[0] == '1') { ++ xotg->hsm.b_bus_req = 1; ++ dev_dbg(xotg->dev, "User request: b_bus_req = 1\n"); ++ } ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); ++ ++static ssize_t ++set_a_clr_err(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ if (!xotg->otg.otg->default_a) ++ return -1; ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '1') { ++ xotg->hsm.a_clr_err = 1; ++ dev_dbg(xotg->dev, "User request: a_clr_err = 1\n"); ++ } ++ if (spin_trylock(&xotg->wq_lock)) { ++ xusbps_update_transceiver(); ++ spin_unlock(&xotg->wq_lock); ++ } ++ return count; ++} ++static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); ++ ++/** ++ * suspend_otg_device - suspend the otg device. ++ * ++ * @otg: Pointer to the otg transceiver structure. ++ * ++ * This function suspends usb devices connected to the otg port ++ * of the host controller. ++ * ++ * returns: 0 on success or error value on failure ++ **/ ++static int suspend_otg_device(struct usb_phy *otg) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ unsigned long otg_port = otg->otg->host->otg_port; ++ struct usb_device *udev; ++ int err; ++ ++ udev = usb_hub_find_child(otg->otg->host->root_hub, otg_port - 1); ++ ++ if (udev) { ++ err = usb_port_suspend(udev, PMSG_SUSPEND); ++ if (err < 0) ++ dev_dbg(xotg->dev, "HNP fail, %d\n", err); ++ ++ /* Change the state of the usb device if HNP is successful */ ++ usb_set_device_state(udev, USB_STATE_NOTATTACHED); ++ } else { ++ err = -ENODEV; ++ dev_dbg(xotg->dev, "No device connected to roothub\n"); ++ } ++ return err; ++} ++ ++static ssize_t ++do_hnp(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ unsigned long ret; ++ ++ if (count > 2) ++ return -1; ++ ++ if (buf[0] == '1') { ++ if (xotg->otg.otg->default_a && xotg->otg.otg->host && ++ xotg->otg.otg->host->b_hnp_enable && ++ (xotg->otg.state == OTG_STATE_A_HOST)) { ++ ret = suspend_otg_device(&xotg->otg); ++ if (ret) ++ return -1; ++ } ++ ++ if (!xotg->otg.otg->default_a && xotg->otg.otg->host && ++ xotg->hsm.b_bus_req) { ++ ret = suspend_otg_device(&xotg->otg); ++ if (ret) ++ return -1; ++ } ++ } ++ return count; ++} ++static DEVICE_ATTR(do_hnp, S_IWUSR, NULL, do_hnp); ++ ++static int xusbps_otg_clk_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* if a rate change is announced we need to check whether we can ++ * maintain the current frequency by changing the clock ++ * dividers. ++ */ ++ /* fall through */ ++ case POST_RATE_CHANGE: ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++static struct attribute *inputs_attrs[] = { ++ &dev_attr_a_bus_req.attr, ++ &dev_attr_a_bus_drop.attr, ++ &dev_attr_b_bus_req.attr, ++ &dev_attr_a_clr_err.attr, ++ &dev_attr_do_hnp.attr, ++ NULL, ++}; ++ ++static struct attribute_group debug_dev_attr_group = { ++ .name = "inputs", ++ .attrs = inputs_attrs, ++}; ++ ++static int xusbps_otg_remove(struct platform_device *pdev) ++{ ++ struct xusbps_otg *xotg = the_transceiver; ++ ++ if (xotg->qwork) { ++ flush_workqueue(xotg->qwork); ++ destroy_workqueue(xotg->qwork); ++ } ++ xusbps_otg_free_timers(); ++ ++ /* disable OTGSC interrupt as OTGSC doesn't change in reset */ ++ writel(0, xotg->base + CI_OTGSC); ++ ++ usb_remove_phy(&xotg->otg); ++ sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); ++ device_remove_file(&pdev->dev, &dev_attr_hsm); ++ device_remove_file(&pdev->dev, &dev_attr_registers); ++ clk_notifier_unregister(xotg->clk, &xotg->clk_rate_change_nb); ++ clk_disable_unprepare(xotg->clk); ++ ++ return 0; ++} ++ ++static int xusbps_otg_probe(struct platform_device *pdev) ++{ ++ int retval; ++ u32 val32; ++ struct xusbps_otg *xotg; ++ char qname[] = "xusbps_otg_queue"; ++ struct xusbps_usb2_platform_data *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) ++ return -ENODEV; ++ ++ dev_dbg(&pdev->dev, "\notg controller is detected.\n"); ++ ++ xotg = devm_kzalloc(&pdev->dev, sizeof(*xotg), GFP_KERNEL); ++ if (xotg == NULL) ++ return -ENOMEM; ++ ++ the_transceiver = xotg; ++ ++ /* Setup ulpi phy for OTG */ ++ xotg->ulpi = pdata->ulpi; ++ ++ xotg->otg.otg = devm_kzalloc(sizeof(struct usb_otg), GFP_KERNEL); ++ if (!xotg->otg.otg) ++ return -ENOMEM; ++ ++ xotg->base = pdata->regs; ++ xotg->irq = pdata->irq; ++ if (!xotg->base || !xotg->irq) { ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ xotg->qwork = create_singlethread_workqueue(qname); ++ if (!xotg->qwork) { ++ dev_dbg(&pdev->dev, "cannot create workqueue %s\n", qname); ++ retval = -ENOMEM; ++ goto err; ++ } ++ INIT_WORK(&xotg->work, xusbps_otg_work); ++ ++ xotg->clk = pdata->clk; ++ retval = clk_prepare_enable(xotg->clk); ++ if (retval) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto err; ++ } ++ ++ xotg->clk_rate_change_nb.notifier_call = xusbps_otg_clk_notifier_cb; ++ xotg->clk_rate_change_nb.next = NULL; ++ if (clk_notifier_register(xotg->clk, &xotg->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ ++ /* OTG common part */ ++ xotg->dev = &pdev->dev; ++ xotg->otg.dev = xotg->dev; ++ xotg->otg.label = driver_name; ++ xotg->otg.otg->set_host = xusbps_otg_set_host; ++ xotg->otg.otg->set_peripheral = xusbps_otg_set_peripheral; ++ xotg->otg.set_power = xusbps_otg_set_power; ++ xotg->otg.otg->set_vbus = xusbps_otg_set_vbus; ++ xotg->otg.otg->start_srp = xusbps_otg_start_srp; ++ xotg->otg.otg->start_hnp = xusbps_otg_start_hnp; ++ xotg->otg.state = OTG_STATE_UNDEFINED; ++ ++ if (usb_add_phy(&xotg->otg, USB_PHY_TYPE_USB2)) { ++ dev_dbg(xotg->dev, "can't set transceiver\n"); ++ retval = -EBUSY; ++ goto err_out_clk_disable; ++ } ++ ++ pdata->otg = &xotg->otg; ++ reset_otg(); ++ init_hsm(); ++ ++ spin_lock_init(&xotg->lock); ++ spin_lock_init(&xotg->wq_lock); ++ INIT_LIST_HEAD(&active_timers); ++ retval = xusbps_otg_init_timers(&xotg->hsm); ++ if (retval) { ++ dev_dbg(&pdev->dev, "Failed to init timers\n"); ++ goto err_out_clk_disable; ++ } ++ ++ init_timer(&xotg->hsm_timer); ++ ++ xotg->xotg_notifier.notifier_call = xotg_usbdev_notify; ++ usb_register_notify((struct notifier_block *) ++ &xotg->xotg_notifier.notifier_call); ++ ++ retval = devm_request_irq(&pdev->dev, xotg->irq, otg_irq, IRQF_SHARED, ++ driver_name, xotg); ++ if (retval) { ++ dev_dbg(xotg->dev, "request interrupt %d failed\n", xotg->irq); ++ retval = -EBUSY; ++ goto err_out_clk_disable; ++ } ++ ++ /* enable OTGSC int */ ++ val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | ++ OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; ++ writel(val32, xotg->base + CI_OTGSC); ++ ++ retval = device_create_file(&pdev->dev, &dev_attr_registers); ++ if (retval < 0) { ++ dev_dbg(xotg->dev, ++ "Can't register sysfs attribute: %d\n", retval); ++ goto err_out_clk_disable; ++ } ++ ++ retval = device_create_file(&pdev->dev, &dev_attr_hsm); ++ if (retval < 0) { ++ dev_dbg(xotg->dev, "Can't hsm sysfs attribute: %d\n", retval); ++ goto err_out_clk_disable; ++ } ++ ++ retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); ++ if (retval < 0) { ++ dev_dbg(xotg->dev, ++ "Can't register sysfs attr group: %d\n", retval); ++ goto err_out_clk_disable; ++ } ++ ++ if (xotg->otg.state == OTG_STATE_A_IDLE) ++ xusbps_update_transceiver(); ++ ++ return 0; ++ ++err_out_clk_disable: ++ clk_notifier_unregister(xotg->clk, &xotg->clk_rate_change_nb); ++ clk_disable_unprepare(xotg->clk); ++err: ++ xusbps_otg_remove(pdev); ++ ++ return retval; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void transceiver_suspend(struct platform_device *pdev) ++{ ++ xusbps_otg_phy_low_power(1); ++} ++ ++static int xusbps_otg_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xusbps_otg *xotg = the_transceiver; ++ int ret = 0; ++ ++ /* Disbale OTG interrupts */ ++ xusbps_otg_intr(0); ++ ++ if (xotg->irq) ++ free_irq(xotg->irq, xotg); ++ ++ /* Prevent more otg_work */ ++ flush_workqueue(xotg->qwork); ++ destroy_workqueue(xotg->qwork); ++ xotg->qwork = NULL; ++ ++ /* start actions */ ++ switch (xotg->otg.state) { ++ case OTG_STATE_A_WAIT_VFALL: ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ case OTG_STATE_A_IDLE: ++ case OTG_STATE_B_IDLE: ++ case OTG_STATE_A_VBUS_ERR: ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ xusbps_otg_del_timer(a_wait_vrise_tmr); ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ del_timer_sync(&xotg->hsm_timer); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, "host driver has been removed.\n"); ++ ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_HOST: ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, "host driver has been removed.\n"); ++ ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_SUSPEND: ++ xusbps_otg_del_timer(a_aidl_bdis_tmr); ++ xusbps_otg_HABA(0); ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(xotg->dev, "host driver has been removed.\n"); ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ del_timer_sync(&xotg->hsm_timer); ++ ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, ++ "client driver has been removed.\n"); ++ xotg->hsm.a_srp_det = 0; ++ ++ /* Turn off VBus */ ++ xotg->otg.otg->set_vbus(xotg->otg.otg, false); ++ xotg->otg.state = OTG_STATE_A_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_B_HOST: ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, "host driver has been removed.\n"); ++ xotg->hsm.b_bus_req = 0; ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (xotg->stop_peripheral) ++ xotg->stop_peripheral(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, ++ "client driver has been removed.\n"); ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ case OTG_STATE_B_WAIT_ACON: ++ /* delete hsm timer for b_ase0_brst_tmr */ ++ del_timer_sync(&xotg->hsm_timer); ++ ++ xusbps_otg_HAAR(0); ++ ++ if (xotg->stop_host) ++ xotg->stop_host(&xotg->otg); ++ else ++ dev_dbg(&pdev->dev, "host driver has been removed.\n"); ++ xotg->hsm.b_bus_req = 0; ++ xotg->otg.state = OTG_STATE_B_IDLE; ++ transceiver_suspend(pdev); ++ break; ++ default: ++ dev_dbg(xotg->dev, "error state before suspend\n"); ++ break; ++ } ++ ++ if (!ret) ++ clk_disable(xotg->clk); ++ return ret; ++} ++ ++static void transceiver_resume(struct platform_device *pdev) ++{ ++ /* Not used */ ++} ++ ++static int xusbps_otg_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct xusbps_otg *xotg = the_transceiver; ++ int ret = 0; ++ ++ ret = clk_enable(xotg->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot enable clock. resume failed.\n"); ++ return ret; ++ } ++ ++ transceiver_resume(pdev); ++ ++ xotg->qwork = create_singlethread_workqueue("xusbps_otg_queue"); ++ if (!xotg->qwork) { ++ dev_dbg(&pdev->dev, "cannot create xusbps otg workqueuen"); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ if (request_irq(xotg->irq, otg_irq, IRQF_SHARED, ++ driver_name, xotg) != 0) { ++ dev_dbg(&pdev->dev, "request interrupt %d failed\n", xotg->irq); ++ ret = -EBUSY; ++ goto error; ++ } ++ ++ /* enable OTG interrupts */ ++ xusbps_otg_intr(1); ++ ++ update_hsm(); ++ ++ xusbps_update_transceiver(); ++ ++ return ret; ++error: ++ xusbps_otg_intr(0); ++ transceiver_suspend(pdev); ++ return ret; ++} ++ ++static const struct dev_pm_ops xusbps_otg_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(xusbps_otg_suspend, xusbps_otg_resume) ++}; ++#define XUSBPS_OTG_PM (&xusbps_otg_dev_pm_ops) ++ ++#else /* ! CONFIG_PM_SLEEP */ ++#define XUSBPS_OTG_PM NULL ++#endif /* ! CONFIG_PM_SLEEP */ ++ ++#ifndef CONFIG_USB_XUSBPS_DR_OF ++static struct platform_driver xusbps_otg_driver = { ++#else ++struct platform_driver xusbps_otg_driver = { ++#endif ++ .probe = xusbps_otg_probe, ++ .remove = xusbps_otg_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = DRIVER_NAME, ++ .pm = XUSBPS_OTG_PM, ++ }, ++}; ++ ++#ifndef CONFIG_USB_XUSBPS_DR_OF ++module_platform_driver(xusbps_otg_driver); ++#endif ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx PS USB OTG driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRIVER_NAME); +--- /dev/null ++++ b/include/linux/usb/xilinx_usbps_otg.h +@@ -0,0 +1,216 @@ ++/* ++ * Xilinx PS USB OTG Driver Header file. ++ * ++ * Copyright 2011 Xilinx, Inc. ++ * ++ * This file is based on langwell_otg.h file with few minor modifications ++ * to support Xilinx PS USB controller. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __XILINX_XUSBPS_OTG_H ++#define __XILINX_XUSBPS_OTG_H ++ ++#define CI_USBCMD 0x140 ++# define USBCMD_RST BIT(1) ++# define USBCMD_RS BIT(0) ++#define CI_USBSTS 0x144 ++# define USBSTS_SLI BIT(8) ++# define USBSTS_URI BIT(6) ++# define USBSTS_PCI BIT(2) ++#define CI_PORTSC1 0x184 ++# define PORTSC_PP BIT(12) ++# define PORTSC_LS (BIT(11) | BIT(10)) ++# define PORTSC_SUSP BIT(7) ++# define PORTSC_CCS BIT(0) ++#define CI_OTGSC 0x1a4 ++# define OTGSC_DPIE BIT(30) ++# define OTGSC_1MSE BIT(29) ++# define OTGSC_BSEIE BIT(28) ++# define OTGSC_BSVIE BIT(27) ++# define OTGSC_ASVIE BIT(26) ++# define OTGSC_AVVIE BIT(25) ++# define OTGSC_IDIE BIT(24) ++# define OTGSC_DPIS BIT(22) ++# define OTGSC_1MSS BIT(21) ++# define OTGSC_BSEIS BIT(20) ++# define OTGSC_BSVIS BIT(19) ++# define OTGSC_ASVIS BIT(18) ++# define OTGSC_AVVIS BIT(17) ++# define OTGSC_IDIS BIT(16) ++# define OTGSC_DPS BIT(14) ++# define OTGSC_1MST BIT(13) ++# define OTGSC_BSE BIT(12) ++# define OTGSC_BSV BIT(11) ++# define OTGSC_ASV BIT(10) ++# define OTGSC_AVV BIT(9) ++# define OTGSC_ID BIT(8) ++# define OTGSC_HABA BIT(7) ++# define OTGSC_HADP BIT(6) ++# define OTGSC_IDPU BIT(5) ++# define OTGSC_DP BIT(4) ++# define OTGSC_OT BIT(3) ++# define OTGSC_HAAR BIT(2) ++# define OTGSC_VC BIT(1) ++# define OTGSC_VD BIT(0) ++# define OTGSC_INTEN_MASK (0x7f << 24) ++# define OTGSC_INT_MASK (0x5f << 24) ++# define OTGSC_INTSTS_MASK (0x7f << 16) ++#define CI_USBMODE 0x1a8 ++# define USBMODE_CM (BIT(1) | BIT(0)) ++# define USBMODE_IDLE 0 ++# define USBMODE_DEVICE 0x2 ++# define USBMODE_HOST 0x3 ++ ++#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) ++ ++enum xusbps_otg_timer_type { ++ TA_WAIT_VRISE_TMR, ++ TA_WAIT_BCON_TMR, ++ TA_AIDL_BDIS_TMR, ++ TB_ASE0_BRST_TMR, ++ TB_SE0_SRP_TMR, ++ TB_SRP_INIT_TMR, ++ TB_SRP_FAIL_TMR, ++ TB_BUS_SUSPEND_TMR ++}; ++ ++#define TA_WAIT_VRISE 100 ++#define TA_WAIT_BCON 30000 ++#define TA_AIDL_BDIS 15000 ++#define TB_ASE0_BRST 5000 ++#define TB_SE0_SRP 2 ++#define TB_SRP_INIT 100 ++#define TB_SRP_FAIL 5500 ++#define TB_BUS_SUSPEND 500 ++ ++struct xusbps_otg_timer { ++ unsigned long expires; /* Number of count increase to timeout */ ++ unsigned long count; /* Tick counter */ ++ void (*function)(unsigned long); /* Timeout function */ ++ unsigned long data; /* Data passed to function */ ++ struct list_head list; ++}; ++ ++/* This is a common data structure to ++ * save values of the OTG state machine */ ++struct otg_hsm { ++ /* Input */ ++ int a_bus_resume; ++ int a_bus_suspend; ++ int a_conn; ++ int a_sess_vld; ++ int a_srp_det; ++ int a_vbus_vld; ++ int b_bus_resume; ++ int b_bus_suspend; ++ int b_conn; ++ int b_se0_srp; ++ int b_ssend_srp; ++ int b_sess_end; ++ int b_sess_vld; ++ int id; ++/* id values */ ++#define ID_B 0x05 ++#define ID_A 0x04 ++#define ID_ACA_C 0x03 ++#define ID_ACA_B 0x02 ++#define ID_ACA_A 0x01 ++ int power_up; ++ int adp_change; ++ int test_device; ++ ++ /* Internal variables */ ++ int a_set_b_hnp_en; ++ int b_srp_done; ++ int b_hnp_enable; ++ int hnp_poll_enable; ++ ++ /* Timeout indicator for timers */ ++ int a_wait_vrise_tmout; ++ int a_wait_bcon_tmout; ++ int a_aidl_bdis_tmout; ++ int a_bidl_adis_tmout; ++ int a_bidl_adis_tmr; ++ int a_wait_vfall_tmout; ++ int b_ase0_brst_tmout; ++ int b_bus_suspend_tmout; ++ int b_srp_init_tmout; ++ int b_srp_fail_tmout; ++ int b_srp_fail_tmr; ++ int b_adp_sense_tmout; ++ ++ /* Informative variables */ ++ int a_bus_drop; ++ int a_bus_req; ++ int a_clr_err; ++ int b_bus_req; ++ int a_suspend_req; ++ int b_bus_suspend_vld; ++ ++ /* Output */ ++ int drv_vbus; ++ int loc_conn; ++ int loc_sof; ++ ++ /* Others */ ++ int vbus_srp_up; ++}; ++ ++struct xusbps_otg { ++ struct usb_phy otg; ++ struct usb_phy *ulpi; ++ ++ struct otg_hsm hsm; ++ ++ /* base address */ ++ void __iomem *base; ++ ++ /* irq */ ++ int irq; ++ ++ /* clk */ ++ struct clk *clk; ++ struct notifier_block clk_rate_change_nb; ++ ++ /* atomic notifier for interrupt context */ ++ struct atomic_notifier_head otg_notifier; ++ ++ /* start/stop USB Host function */ ++ int (*start_host)(struct usb_phy *otg); ++ int (*stop_host)(struct usb_phy *otg); ++ ++ /* start/stop USB Peripheral function */ ++ int (*start_peripheral)(struct usb_phy *otg); ++ int (*stop_peripheral)(struct usb_phy *otg); ++ ++ struct device *dev; ++ ++ unsigned region; ++ ++ struct work_struct work; ++ struct workqueue_struct *qwork; ++ struct timer_list hsm_timer; ++ ++ spinlock_t lock; ++ spinlock_t wq_lock; ++ ++ struct notifier_block xotg_notifier; ++}; ++ ++static inline ++struct xusbps_otg *xceiv_to_xotg(struct usb_phy *otg) ++{ ++ return container_of(otg, struct xusbps_otg, otg); ++} ++ ++void xusbps_update_transceiver(void); ++ ++#endif /* __XILINX_XUSBPS_OTG_H__ */ +--- /dev/null ++++ b/include/linux/xilinx_devices.h +@@ -0,0 +1,70 @@ ++/* ++ * include/linux/xilinx_devices.h ++ * ++ * Definitions for any platform device related flags or structures for ++ * Xilinx EDK IPs ++ * ++ * Author: MontaVista Software, Inc. ++ * source@mvista.com ++ * ++ * 2002-2005 (c) MontaVista Software, Inc. This file is licensed under the ++ * terms of the GNU General Public License version 2. This program is licensed ++ * "as is" without any warranty of any kind, whether express or implied. ++ */ ++ ++#ifdef __KERNEL__ ++#ifndef _XILINX_DEVICE_H_ ++#define _XILINX_DEVICE_H_ ++ ++#include ++#include ++#include ++ ++/*- PS USB Controller IP -*/ ++enum xusbps_usb2_operating_modes { ++ XUSBPS_USB2_MPH_HOST, ++ XUSBPS_USB2_DR_HOST, ++ XUSBPS_USB2_DR_DEVICE, ++ XUSBPS_USB2_DR_OTG, ++}; ++ ++enum xusbps_usb2_phy_modes { ++ XUSBPS_USB2_PHY_NONE, ++ XUSBPS_USB2_PHY_ULPI, ++ XUSBPS_USB2_PHY_UTMI, ++ XUSBPS_USB2_PHY_UTMI_WIDE, ++ XUSBPS_USB2_PHY_SERIAL, ++}; ++ ++struct clk; ++struct platform_device; ++ ++struct xusbps_usb2_platform_data { ++ /* board specific information */ ++ enum xusbps_usb2_operating_modes operating_mode; ++ enum xusbps_usb2_phy_modes phy_mode; ++ unsigned int port_enables; ++ unsigned int workaround; ++ ++ int (*init)(struct platform_device *); ++ void (*exit)(struct platform_device *); ++ void __iomem *regs; /* ioremap'd register base */ ++ struct usb_phy *otg; ++ struct usb_phy *ulpi; ++ int irq; ++ struct clk *clk; ++ struct notifier_block clk_rate_change_nb; ++ unsigned big_endian_mmio:1; ++ unsigned big_endian_desc:1; ++ unsigned es:1; /* need USBMODE:ES */ ++ unsigned le_setup_buf:1; ++ unsigned have_sysif_regs:1; ++ unsigned invert_drvvbus:1; ++ unsigned invert_pwr_fault:1; ++}; ++ ++#define XUSBPS_USB2_PORT0_ENABLED 0x00000001 ++#define XUSBPS_USB2_PORT1_ENABLED 0x00000002 ++ ++#endif /* _XILINX_DEVICE_H_ */ ++#endif /* __KERNEL__ */ diff --git a/patches.zynq/0010-memory-zynq-merge-driver-for-Zynq-SMC.patch b/patches.zynq/0010-memory-zynq-merge-driver-for-Zynq-SMC.patch new file mode 100644 index 0000000..2663945 --- /dev/null +++ b/patches.zynq/0010-memory-zynq-merge-driver-for-Zynq-SMC.patch @@ -0,0 +1,706 @@ +From 010897260c3b143bb9a958fc4e5eeba9de07bdf2 Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Tue, 24 Dec 2013 10:13:37 +0900 +Subject: memory: zynq: merge driver for Zynq SMC + +This adds a driver for Zynq's static memory controller. +The SMC (compatible with ARM's PL353) supports NAND, NOR and SRAM memory. +(commit efc27505715e64526653f35274717c0fc56491e3 from master branch) + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + drivers/memory/Kconfig | 7 + drivers/memory/Makefile | 1 + drivers/memory/zynq-smc.c | 627 ++++++++++++++++++++++++++++++++++++++++ + include/linux/memory/zynq-smc.h | 32 ++ + 4 files changed, 667 insertions(+) + create mode 100644 drivers/memory/zynq-smc.c + create mode 100644 include/linux/memory/zynq-smc.h + +--- a/drivers/memory/Kconfig ++++ b/drivers/memory/Kconfig +@@ -40,4 +40,11 @@ config TEGRA30_MC + analysis, especially for IOMMU/SMMU(System Memory Management + Unit) module. + ++config ZYNQ_SMC ++ bool "Zynq Static Memory Controller(SMC) driver" ++ default y ++ depends on ARCH_ZYNQ ++ help ++ This driver is for the Static Memory Controller(SMC) module available ++ in Zynq SoCs. + endif +--- a/drivers/memory/Makefile ++++ b/drivers/memory/Makefile +@@ -8,3 +8,4 @@ endif + obj-$(CONFIG_TI_EMIF) += emif.o + obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o + obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o ++obj-$(CONFIG_ZYNQ_SMC) += zynq-smc.o +--- /dev/null ++++ b/drivers/memory/zynq-smc.c +@@ -0,0 +1,627 @@ ++/* ++ * Xilinx Zynq SMC Driver ++ * ++ * Copyright (C) 2012 - 2013 Xilinx, Inc. ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Currently only a single SMC instance is supported. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Register definitions */ ++#define XSMCPS_MEMC_STATUS_OFFS 0 /* Controller status reg, RO */ ++#define XSMCPS_CFG_CLR_OFFS 0xC /* Clear config reg, WO */ ++#define XSMCPS_DIRECT_CMD_OFFS 0x10 /* Direct command reg, WO */ ++#define XSMCPS_SET_CYCLES_OFFS 0x14 /* Set cycles register, WO */ ++#define XSMCPS_SET_OPMODE_OFFS 0x18 /* Set opmode register, WO */ ++#define XSMCPS_ECC_STATUS_OFFS 0x400 /* ECC status register */ ++#define XSMCPS_ECC_MEMCFG_OFFS 0x404 /* ECC mem config reg */ ++#define XSMCPS_ECC_MEMCMD1_OFFS 0x408 /* ECC mem cmd1 reg */ ++#define XSMCPS_ECC_MEMCMD2_OFFS 0x40C /* ECC mem cmd2 reg */ ++#define XSMCPS_ECC_VALUE0_OFFS 0x418 /* ECC value 0 reg */ ++ ++#define XSMCPS_CFG_CLR_INT_1 0x10 ++#define XSMCPS_ECC_STATUS_BUSY (1 << 6) ++#define XSMCPS_DC_UPT_NAND_REGS ((4 << 23) | /* CS: NAND chip */ \ ++ (2 << 21)) /* UpdateRegs operation */ ++ ++#define XNANDPS_ECC_CMD1 ((0x80) | /* Write command */ \ ++ (0 << 8) | /* Read command */ \ ++ (0x30 << 16) | /* Read End command */ \ ++ (1 << 24)) /* Read End command calid */ ++ ++#define XNANDPS_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ ++ (5 << 8) | /* Read col change cmd */ \ ++ (0xE0 << 16) | /* Read col change end cmd */ \ ++ (1 << 24)) /* Read col change end cmd valid */ ++/** ++ * struct xsmcps_data ++ * @devclk Pointer to the peripheral clock ++ * @aperclk Pointer to the APER clock ++ * @clk_rate_change_nb Notifier block for clock frequency change callback ++ */ ++struct xsmcps_data { ++ struct clk *devclk; ++ struct clk *aperclk; ++ struct notifier_block clk_rate_change_nb; ++ struct resource *res; ++}; ++ ++/* SMC virtual register base */ ++static void __iomem *xsmcps_base; ++static DEFINE_SPINLOCK(xsmcps_lock); ++ ++/** ++ * xsmcps_set_buswidth - Set memory buswidth ++ * @bw Memory buswidth (8 | 16) ++ * Returns 0 on success or negative errno. ++ * ++ * Must be called with xsmcps_lock held. ++ */ ++static int xsmcps_set_buswidth(unsigned int bw) ++{ ++ u32 reg; ++ ++ if (bw != 8 && bw != 16) ++ return -EINVAL; ++ ++ reg = readl(xsmcps_base + XSMCPS_SET_OPMODE_OFFS); ++ reg &= ~3; ++ if (bw == 16) ++ reg |= 1; ++ writel(reg, xsmcps_base + XSMCPS_SET_OPMODE_OFFS); ++ ++ return 0; ++} ++ ++/** ++ * xsmcps_set_cycles - Set memory timing parameters ++ * @t0 t_rc read cycle time ++ * @t1 t_wc write cycle time ++ * @t2 t_rea/t_ceoe output enable assertion delay ++ * @t3 t_wp write enable deassertion delay ++ * @t4 t_clr/t_pc page cycle time ++ * @t5 t_ar/t_ta ID read time/turnaround time ++ * @t6 t_rr busy to RE timing ++ * ++ * Sets NAND chip specific timing parameters. ++ * ++ * Must be called with xsmcps_lock held. ++ */ ++static void xsmcps_set_cycles(u32 t0, u32 t1, u32 t2, u32 t3, u32 ++ t4, u32 t5, u32 t6) ++{ ++ t0 &= 0xf; ++ t1 = (t1 & 0xf) << 4; ++ t2 = (t2 & 7) << 8; ++ t3 = (t3 & 7) << 11; ++ t4 = (t4 & 7) << 14; ++ t5 = (t5 & 7) << 17; ++ t6 = (t6 & 0xf) << 20; ++ ++ t0 |= t1 | t2 | t3 | t4 | t5 | t6; ++ ++ writel(t0, xsmcps_base + XSMCPS_SET_CYCLES_OFFS); ++} ++ ++/** ++ * xsmcps_ecc_is_busy_noirq - Read ecc busy flag ++ * Returns the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle ++ * ++ * Must be called with xsmcps_lock held. ++ */ ++static int xsmcps_ecc_is_busy_noirq(void) ++{ ++ return !!(readl(xsmcps_base + XSMCPS_ECC_STATUS_OFFS) & ++ XSMCPS_ECC_STATUS_BUSY); ++} ++ ++/** ++ * xsmcps_ecc_is_busy - Read ecc busy flag ++ * Returns the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle ++ */ ++int xsmcps_ecc_is_busy(void) ++{ ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ ret = xsmcps_ecc_is_busy_noirq(); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(xsmcps_ecc_is_busy); ++ ++/** ++ * xsmcps_get_ecc_val - Read ecc_valueN registers ++ * @ecc_reg Index of the ecc_value reg (0..3) ++ * Returns the content of the requested ecc_value register. ++ * ++ * There are four valid ecc_value registers. The argument is truncated to stay ++ * within this valid boundary. ++ */ ++u32 xsmcps_get_ecc_val(int ecc_reg) ++{ ++ u32 reg; ++ u32 addr; ++ unsigned long flags; ++ ++ ecc_reg &= 3; ++ addr = XSMCPS_ECC_VALUE0_OFFS + (ecc_reg << 2); ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ reg = readl(xsmcps_base + addr); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ ++ return reg; ++} ++EXPORT_SYMBOL_GPL(xsmcps_get_ecc_val); ++ ++/** ++ * xsmcps_get_nand_int_status_raw - Get NAND interrupt status bit ++ * Returns the raw_int_status1 bit from the memc_status register ++ */ ++int xsmcps_get_nand_int_status_raw(void) ++{ ++ u32 reg; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ reg = readl(xsmcps_base + XSMCPS_MEMC_STATUS_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ ++ reg >>= 6; ++ reg &= 1; ++ ++ return reg; ++} ++EXPORT_SYMBOL_GPL(xsmcps_get_nand_int_status_raw); ++ ++/** ++ * xsmcps_clr_nand_int - Clear NAND interrupt ++ */ ++void xsmcps_clr_nand_int(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ writel(XSMCPS_CFG_CLR_INT_1, xsmcps_base + XSMCPS_CFG_CLR_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++} ++EXPORT_SYMBOL_GPL(xsmcps_clr_nand_int); ++ ++/** ++ * xsmcps_set_ecc_mode - Set SMC ECC mode ++ * @mode ECC mode (BYPASS, APB, MEM) ++ * Returns 0 on success or negative errno. ++ */ ++int xsmcps_set_ecc_mode(enum xsmcps_ecc_mode mode) ++{ ++ u32 reg; ++ unsigned long flags; ++ int ret = 0; ++ ++ switch (mode) { ++ case XSMCPS_ECCMODE_BYPASS: ++ case XSMCPS_ECCMODE_APB: ++ case XSMCPS_ECCMODE_MEM: ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ reg = readl(xsmcps_base + XSMCPS_ECC_MEMCFG_OFFS); ++ reg &= ~0xc; ++ reg |= mode << 2; ++ writel(reg, xsmcps_base + XSMCPS_ECC_MEMCFG_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(xsmcps_set_ecc_mode); ++ ++/** ++ * xsmcps_set_ecc_pg_size - Set SMC ECC page size ++ * @pg_sz ECC page size ++ * Returns 0 on success or negative errno. ++ */ ++int xsmcps_set_ecc_pg_size(unsigned int pg_sz) ++{ ++ u32 reg; ++ u32 sz; ++ unsigned long flags; ++ ++ switch (pg_sz) { ++ case 0: ++ sz = 0; ++ break; ++ case 512: ++ sz = 1; ++ break; ++ case 1024: ++ sz = 2; ++ break; ++ case 2048: ++ sz = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ reg = readl(xsmcps_base + XSMCPS_ECC_MEMCFG_OFFS); ++ reg &= ~3; ++ reg |= sz; ++ writel(reg, xsmcps_base + XSMCPS_ECC_MEMCFG_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(xsmcps_set_ecc_pg_size); ++ ++static int xsmcps_clk_notifier_cb(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* ++ * if a rate change is announced we need to check whether we can ++ * run under the changed conditions ++ */ ++ /* fall through */ ++ case POST_RATE_CHANGE: ++ return NOTIFY_OK; ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int xsmcps_suspend(struct device *dev) ++{ ++ struct xsmcps_data *xsmcps = dev_get_drvdata(dev); ++ ++ clk_disable(xsmcps->devclk); ++ clk_disable(xsmcps->aperclk); ++ ++ return 0; ++} ++ ++static int xsmcps_resume(struct device *dev) ++{ ++ int ret; ++ struct xsmcps_data *xsmcps = dev_get_drvdata(dev); ++ ++ ret = clk_enable(xsmcps->aperclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable APER clock.\n"); ++ return ret; ++ } ++ ++ ret = clk_enable(xsmcps->devclk); ++ if (ret) { ++ dev_err(dev, "Cannot enable device clock.\n"); ++ clk_disable(xsmcps->aperclk); ++ return ret; ++ } ++ return ret; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(xsmcps_dev_pm_ops, xsmcps_suspend, xsmcps_resume); ++ ++/** ++ * xsmcps_init_nand_interface - Initialize the NAND interface ++ * @pdev Pointer to the platform_device struct ++ * @nand_node Pointer to the xnandps device_node struct ++ */ ++static void xsmcps_init_nand_interface(struct platform_device *pdev, ++ struct device_node *nand_node) ++{ ++ u32 t_rc, t_wc, t_rea, t_wp, t_clr, t_ar, t_rr; ++ unsigned int bw; ++ int err; ++ unsigned long flags; ++ ++ err = of_property_read_u32(nand_node, "xlnx,nand-width", &bw); ++ if (err) { ++ dev_warn(&pdev->dev, ++ "xlnx,nand-width not in device tree, using 8"); ++ bw = 8; ++ } ++ /* nand-cycle- property is refer to the NAND flash timing ++ * mapping between dts and the NAND flash AC timing ++ * X : AC timing name ++ * t0 : t_rc ++ * t1 : t_wc ++ * t2 : t_rea ++ * t3 : t_wp ++ * t4 : t_clr ++ * t5 : t_ar ++ * t6 : t_rr ++ */ ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t0", &t_rc); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t0 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t1", &t_wc); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t1 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t2", &t_rea); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t2 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t3", &t_wp); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t3 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t4", &t_clr); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t4 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t5", &t_ar); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t5 not in device tree"); ++ goto default_nand_timing; ++ } ++ err = of_property_read_u32(nand_node, "xlnx,nand-cycle-t6", &t_rr); ++ if (err) { ++ dev_warn(&pdev->dev, "xlnx,nand-cycle-t6 not in device tree"); ++ goto default_nand_timing; ++ } ++ ++default_nand_timing: ++ if (err) { ++ /* set default NAND flash timing property */ ++ dev_warn(&pdev->dev, "Using default timing for"); ++ dev_warn(&pdev->dev, "2Gb Numonyx MT29F2G08ABAEAWP NAND flash"); ++ dev_warn(&pdev->dev, "t_wp, t_clr, t_ar are set to 4"); ++ dev_warn(&pdev->dev, "t_rc, t_wc, t_rr are set to 2"); ++ dev_warn(&pdev->dev, "t_rea is set to 1"); ++ t_rc = t_wc = t_rr = 4; ++ t_rea = 1; ++ t_wp = t_clr = t_ar = 2; ++ } ++ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ if (xsmcps_set_buswidth(bw)) { ++ dev_warn(&pdev->dev, "xlnx,nand-width not valid, using 8"); ++ xsmcps_set_buswidth(8); ++ } ++ ++ /* ++ * Default assume 50MHz clock (20ns cycle time) and 3V operation ++ * The SET_CYCLES_REG register value depends on the flash device. ++ * Look in to the device datasheet and change its value, This value ++ * is for 2Gb Numonyx flash. ++ */ ++ xsmcps_set_cycles(t_rc, t_wc, t_rea, t_wp, t_clr, t_ar, t_rr); ++ writel(XSMCPS_CFG_CLR_INT_1, xsmcps_base + XSMCPS_CFG_CLR_OFFS); ++ writel(XSMCPS_DC_UPT_NAND_REGS, xsmcps_base + XSMCPS_DIRECT_CMD_OFFS); ++ /* Wait till the ECC operation is complete */ ++ while (xsmcps_ecc_is_busy_noirq()) ++ cpu_relax(); ++ /* Set the command1 and command2 register */ ++ writel(XNANDPS_ECC_CMD1, xsmcps_base + XSMCPS_ECC_MEMCMD1_OFFS); ++ writel(XNANDPS_ECC_CMD2, xsmcps_base + XSMCPS_ECC_MEMCMD2_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++} ++ ++const struct of_device_id matches_nor[] = { ++ {.compatible = "cfi-flash"}, ++ {} ++}; ++const struct of_device_id matches_nand[] = { ++ {.compatible = "xlnx,ps7-nand-1.00.a"}, ++ {} ++}; ++ ++static int xsmcps_probe(struct platform_device *pdev) ++{ ++ struct xsmcps_data *xsmcps; ++ struct device_node *child; ++ unsigned long flags; ++ int err; ++ struct device_node *of_node = pdev->dev.of_node; ++ const struct of_device_id *matches = NULL; ++ ++ xsmcps = kzalloc(sizeof(*xsmcps), GFP_KERNEL); ++ if (!xsmcps) { ++ dev_err(&pdev->dev, "unable to allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ xsmcps->aperclk = clk_get(&pdev->dev, "aper_clk"); ++ if (IS_ERR(xsmcps->aperclk)) { ++ dev_err(&pdev->dev, "aper_clk clock not found.\n"); ++ err = PTR_ERR(xsmcps->aperclk); ++ goto out_free; ++ } ++ ++ xsmcps->devclk = clk_get(&pdev->dev, "ref_clk"); ++ if (IS_ERR(xsmcps->devclk)) { ++ dev_err(&pdev->dev, "ref_clk clock not found.\n"); ++ err = PTR_ERR(xsmcps->devclk); ++ goto out_clk_put_aper; ++ } ++ ++ err = clk_prepare_enable(xsmcps->aperclk); ++ if (err) { ++ dev_err(&pdev->dev, "Unable to enable APER clock.\n"); ++ goto out_clk_put; ++ } ++ ++ err = clk_prepare_enable(xsmcps->devclk); ++ if (err) { ++ dev_err(&pdev->dev, "Unable to enable device clock.\n"); ++ goto out_clk_dis_aper; ++ } ++ ++ platform_set_drvdata(pdev, xsmcps); ++ ++ xsmcps->clk_rate_change_nb.notifier_call = xsmcps_clk_notifier_cb; ++ if (clk_notifier_register(xsmcps->devclk, &xsmcps->clk_rate_change_nb)) ++ dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); ++ ++ /* Get the NAND controller virtual address */ ++ xsmcps->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!xsmcps->res) { ++ err = -ENODEV; ++ dev_err(&pdev->dev, "platform_get_resource failed\n"); ++ goto out_clk_disable; ++ } ++ xsmcps->res = request_mem_region(xsmcps->res->start, ++ resource_size(xsmcps->res), pdev->name); ++ if (!xsmcps->res) { ++ err = -ENOMEM; ++ dev_err(&pdev->dev, "request_mem_region failed\n"); ++ goto out_clk_disable; ++ } ++ ++ xsmcps_base = ioremap(xsmcps->res->start, resource_size(xsmcps->res)); ++ if (!xsmcps_base) { ++ err = -EIO; ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ goto out_release_mem_region; ++ } ++ ++ /* clear interrupts */ ++ spin_lock_irqsave(&xsmcps_lock, flags); ++ ++ writel(0x52, xsmcps_base + XSMCPS_CFG_CLR_OFFS); ++ ++ spin_unlock_irqrestore(&xsmcps_lock, flags); ++ ++ /* Find compatible children. Only a single child is supported */ ++ for_each_available_child_of_node(of_node, child) { ++ if (of_match_node(matches_nand, child)) { ++ xsmcps_init_nand_interface(pdev, child); ++ if (!matches) { ++ matches = matches_nand; ++ } else { ++ dev_err(&pdev->dev, ++ "incompatible configuration\n"); ++ goto out_release_mem_region; ++ } ++ } ++ ++ if (of_match_node(matches_nor, child)) { ++ static int counts = 0; ++ if (!matches) { ++ matches = matches_nor; ++ } else { ++ if (matches != matches_nor || counts > 1) { ++ dev_err(&pdev->dev, ++ "incompatible configuration\n"); ++ goto out_release_mem_region; ++ } ++ } ++ counts++; ++ } ++ } ++ ++ if (matches) ++ of_platform_populate(of_node, matches, NULL, &pdev->dev); ++ ++ return 0; ++ ++out_release_mem_region: ++ release_mem_region(xsmcps->res->start, resource_size(xsmcps->res)); ++ kfree(xsmcps->res); ++out_clk_disable: ++ clk_disable_unprepare(xsmcps->devclk); ++out_clk_dis_aper: ++ clk_disable_unprepare(xsmcps->aperclk); ++out_clk_put: ++ clk_put(xsmcps->devclk); ++out_clk_put_aper: ++ clk_put(xsmcps->aperclk); ++out_free: ++ kfree(xsmcps); ++ ++ return err; ++} ++ ++static int xsmcps_remove(struct platform_device *pdev) ++{ ++ struct xsmcps_data *xsmcps = platform_get_drvdata(pdev); ++ ++ clk_notifier_unregister(xsmcps->devclk, &xsmcps->clk_rate_change_nb); ++ release_mem_region(xsmcps->res->start, resource_size(xsmcps->res)); ++ kfree(xsmcps->res); ++ iounmap(xsmcps_base); ++ clk_disable_unprepare(xsmcps->devclk); ++ clk_disable_unprepare(xsmcps->aperclk); ++ clk_put(xsmcps->devclk); ++ clk_put(xsmcps->aperclk); ++ kfree(xsmcps); ++ ++ return 0; ++} ++ ++/* Match table for device tree binding */ ++static const struct of_device_id xsmcps_of_match[] = { ++ {.compatible = "xlnx,ps7-smc"}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, xsmcps_of_match); ++ ++static struct platform_driver xsmcps_driver = { ++ .probe = xsmcps_probe, ++ .remove = xsmcps_remove, ++ .driver = { ++ .name = "xsmcps", ++ .owner = THIS_MODULE, ++ .pm = &xsmcps_dev_pm_ops, ++ .of_match_table = xsmcps_of_match, ++ }, ++}; ++ ++module_platform_driver(xsmcps_driver); ++ ++MODULE_AUTHOR("Xilinx, Inc."); ++MODULE_DESCRIPTION("Xilinx PS SMC Driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/memory/zynq-smc.h +@@ -0,0 +1,32 @@ ++/* ++ * Xilinx Zynq SMC Driver Header ++ * ++ * Copyright (C) 2012 Xilinx, Inc. ++ * ++ * This program 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; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __LINUX_MEMORY_ZYNQ_SMC_H ++#define __LINUX_MEMORY_ZYNQ_SMC_H ++ ++enum xsmcps_ecc_mode { ++ XSMCPS_ECCMODE_BYPASS = 0, ++ XSMCPS_ECCMODE_APB = 1, ++ XSMCPS_ECCMODE_MEM = 2 ++}; ++ ++u32 xsmcps_get_ecc_val(int ecc_reg); ++int xsmcps_ecc_is_busy(void); ++int xsmcps_get_nand_int_status_raw(void); ++void xsmcps_clr_nand_int(void); ++int xsmcps_set_ecc_mode(enum xsmcps_ecc_mode mode); ++int xsmcps_set_ecc_pg_size(unsigned int pg_sz); ++ ++#endif diff --git a/patches.zynq/0011-arm-dts-zynq-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch b/patches.zynq/0011-arm-dts-zynq-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch new file mode 100644 index 0000000..5c2182c --- /dev/null +++ b/patches.zynq/0011-arm-dts-zynq-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch @@ -0,0 +1,418 @@ +From 5bf18a6e06c0906459605ebb45077d8ab710ebde Mon Sep 17 00:00:00 2001 +From: Soren Brinkmann +Date: Mon, 13 May 2013 10:46:38 -0700 +Subject: arm: dts: zynq: Merge zynq-zc702.dts with Xilinx repository + +This patch updates the zynq zc702 device tree by merging some parts +from the corresponding file in the Xilinx repository +(commit efc27505715e64526653f35274717c0fc56491e3 in master branch) + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + arch/arm/boot/dts/zynq-zc702.dts | 389 ++++++++++++++++++++++++++++++++++++--- + 1 file changed, 366 insertions(+), 23 deletions(-) + +--- a/arch/arm/boot/dts/zynq-zc702.dts ++++ b/arch/arm/boot/dts/zynq-zc702.dts +@@ -1,34 +1,377 @@ + /* +- * Copyright (C) 2011 Xilinx +- * Copyright (C) 2012 National Instruments Corp. ++ * Device Tree Generator version: 1.1 + * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. ++ * (C) Copyright 2007-2013 Xilinx, Inc. ++ * (C) Copyright 2007-2013 Michal Simek ++ * (C) Copyright 2007-2012 PetaLogix Qld Pty Ltd ++ * ++ * Michal SIMEK ++ * ++ * CAUTION: This file is automatically generated by libgen. ++ * Version: Xilinx EDK 14.5 EDK_P.58f + * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. + */ +-/dts-v1/; +-/include/ "zynq-7000.dtsi" + ++/dts-v1/; + / { +- model = "Zynq ZC702 Development Board"; +- compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000"; +- +- memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "xlnx,zynq-7000"; ++ model = "Xilinx Zynq"; ++ aliases { ++ ethernet0 = &ps7_ethernet_0; ++ i2c0 = &ps7_i2c_0; ++ serial0 = &ps7_uart_1; ++ spi0 = &ps7_qspi_0; ++ } ; ++ chosen { ++ bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 initrd=0x2000000,8M rw rootwait earlyprintk"; ++ linux,stdout-path = "/amba@0/serial@e0001000"; ++ } ; ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ps7_cortexa9_0: cpu@0 { ++ bus-handle = <&ps7_axi_interconnect_0>; ++ compatible = "arm,cortex-a9"; ++ d-cache-line-size = <0x20>; ++ d-cache-size = <0x8000>; ++ device_type = "cpu"; ++ i-cache-line-size = <0x20>; ++ i-cache-size = <0x8000>; ++ interrupt-handle = <&ps7_scugic_0>; ++ reg = <0x0>; ++ } ; ++ ps7_cortexa9_1: cpu@1 { ++ bus-handle = <&ps7_axi_interconnect_0>; ++ compatible = "arm,cortex-a9"; ++ d-cache-line-size = <0x20>; ++ d-cache-size = <0x8000>; ++ device_type = "cpu"; ++ i-cache-line-size = <0x20>; ++ i-cache-size = <0x8000>; ++ interrupt-handle = <&ps7_scugic_0>; ++ reg = <0x1>; ++ } ; ++ } ; ++ pmu { ++ compatible = "arm,cortex-a9-pmu"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 5 4>, <0 6 4>; ++ reg = <0xf8891000 0x1000>, <0xf8893000 0x1000>; ++ reg-names = "cpu0", "cpu1"; ++ } ; ++ ps7_ddr_0: memory@0 { + device_type = "memory"; + reg = <0x0 0x40000000>; +- }; ++ } ; ++ ps7_axi_interconnect_0: amba@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "xlnx,ps7-axi-interconnect-1.00.a", "simple-bus"; ++ ranges ; ++ ps7_dma_s: ps7-dma@f8003000 { ++ #dma-cells = <1>; ++ #dma-channels = <8>; ++ #dma-requests = <4>; ++ arm,primecell-periphid = <0x41330>; ++ clock-names = "apb_pclk"; ++ clocks = <&clkc 27>; ++ compatible = "xlnx,ps7-dma-1.00.a", "arm,primecell", "arm,pl330"; ++ interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", ++ "dma4", "dma5", "dma6", "dma7"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 13 4>, <0 14 4>, <0 15 4>, <0 16 4>, <0 17 4>, <0 40 4>, <0 41 4>, <0 42 4>, <0 43 4>; ++ reg = <0xf8003000 0x1000>; ++ } ; ++ ps7_ethernet_0: ps7-ethernet@e000b000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ clock-names = "ref_clk", "aper_clk"; ++ clocks = <&clkc 13>, <&clkc 30>; ++ compatible = "xlnx,ps7-ethernet-1.00.a"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 22 4>; ++ local-mac-address = [00 0a 35 00 00 00]; ++ phy-handle = <&phy0>; ++ phy-mode = "rgmii-id"; ++ reg = <0xe000b000 0x1000>; ++ xlnx,enet-reset = "MIO 11"; ++ xlnx,eth-mode = <0x1>; ++ xlnx,has-mdio = <0x1>; ++ xlnx,ptp-enet-clock = <111111115>; ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ phy0: phy@7 { ++ compatible = "marvell,88e1116r"; ++ device_type = "ethernet-phy"; ++ reg = <7>; ++ } ; ++ } ; ++ } ; ++ ps7_gpio_0: ps7-gpio@e000a000 { ++ #gpio-cells = <2>; ++ clocks = <&clkc 42>; ++ compatible = "xlnx,ps7-gpio-1.00.a"; ++ emio-gpio-width = <64>; ++ gpio-controller ; ++ gpio-mask-high = <0x0>; ++ gpio-mask-low = <0x5600>; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 20 4>; ++ reg = <0xe000a000 0x1000>; ++ } ; ++ ps7_i2c_0: ps7-i2c@e0004000 { ++ bus-id = <0>; ++ clocks = <&clkc 38>; ++ compatible = "xlnx,ps7-i2c-1.00.a"; ++ i2c-clk = <400000>; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 25 4>; ++ reg = <0xe0004000 0x1000>; ++ xlnx,has-interrupt = <0x0>; ++ xlnx,i2c-reset = "MIO 13"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ i2cswitch@74 { ++ compatible = "nxp,pca9548"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x74>; + +- chosen { +- bootargs = "console=ttyPS0,115200 earlyprintk"; +- }; ++ i2c@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ osc@5d { ++ compatible = "si570"; ++ reg = <0x5d>; ++ factory-fout = <156250000>; ++ initial-fout = <148500000>; ++ }; ++ }; ++ ++ i2c@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ eeprom@54 { ++ compatible = "at,24c08"; ++ reg = <0x54>; ++ }; ++ }; ++ ++ i2c@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ gpio@21 { ++ compatible = "ti,tca6416"; ++ reg = <0x21>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ }; ++ ++ i2c@4 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <4>; ++ rtc@54 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ }; ++ }; ++ ++ i2c@7 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <7>; ++ hwmon@52 { ++ compatible = "pmbus,ucd9248"; ++ reg = <52>; ++ }; ++ hwmon@53 { ++ compatible = "pmbus,ucd9248"; ++ reg = <53>; ++ }; ++ hwmon@54 { ++ compatible = "pmbus,ucd9248"; ++ reg = <54>; ++ }; ++ }; ++ }; + +-}; ++ } ; ++ ps7_iop_bus_config_0: ps7-iop-bus-config@e0200000 { ++ compatible = "xlnx,ps7-iop-bus-config-1.00.a"; ++ reg = <0xe0200000 0x1000>; ++ } ; ++ ps7_pl310_0: ps7-pl310@f8f02000 { ++ arm,data-latency = <3 2 2>; ++ arm,tag-latency = <2 2 2>; ++ cache-level = <2>; ++ cache-unified ; ++ compatible = "xlnx,ps7-pl310-1.00.a", "arm,pl310-cache"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 2 4>; ++ reg = <0xf8f02000 0x1000>; ++ } ; ++ ps7_qspi_0: ps7-qspi@e000d000 { ++ clock-names = "ref_clk", "aper_clk"; ++ clocks = <&clkc 10>, <&clkc 43>; ++ compatible = "xlnx,ps7-qspi-1.00.a"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 19 4>; ++ is-dual = <0>; ++ num-chip-select = <1>; ++ reg = <0xe000d000 0x1000>; ++ xlnx,fb-clk = <0x1>; ++ xlnx,qspi-mode = <0x0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ flash@0 { ++ compatible = "n25q128"; ++ reg = <0x0>; ++ spi-max-frequency = <50000000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ partition@qspi-fsbl-uboot { ++ label = "qspi-fsbl-uboot"; ++ reg = <0x0 0x100000>; ++ }; ++ partition@qspi-linux { ++ label = "qspi-linux"; ++ reg = <0x100000 0x500000>; ++ }; ++ partition@qspi-device-tree { ++ label = "qspi-device-tree"; ++ reg = <0x600000 0x20000>; ++ }; ++ partition@qspi-rootfs { ++ label = "qspi-rootfs"; ++ reg = <0x620000 0x5E0000>; ++ }; ++ partition@qspi-bitstream { ++ label = "qspi-bitstream"; ++ reg = <0xC00000 0x400000>; ++ }; ++ }; + +-&uart1 { +- status = "okay"; +-}; ++ } ; ++ ps7_qspi_linear_0: ps7-qspi-linear@fc000000 { ++ clock-names = "ref_clk", "aper_clk"; ++ clocks = <&clkc 10>, <&clkc 43>; ++ compatible = "xlnx,ps7-qspi-linear-1.00.a"; ++ reg = <0xfc000000 0x1000000>; ++ } ; ++ ps7_ram_0: ps7-ram@0 { ++ compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 3 4>; ++ reg = <0xfffc0000 0x40000>; ++ } ; ++ ps7_scugic_0: ps7-scugic@f8f01000 { ++ #address-cells = <2>; ++ #interrupt-cells = <3>; ++ #size-cells = <1>; ++ compatible = "xlnx,ps7-scugic-1.00.a", "arm,cortex-a9-gic", "arm,gic"; ++ interrupt-controller ; ++ num_cpus = <2>; ++ num_interrupts = <96>; ++ reg = <0xf8f01000 0x1000>, <0xf8f00100 0x100>; ++ } ; ++ ps7_scutimer_0: ps7-scutimer@f8f00600 { ++ clocks = <&clkc 4>; ++ compatible = "xlnx,ps7-scutimer-1.00.a", "arm,cortex-a9-twd-timer"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <1 13 0x301>; ++ reg = <0xf8f00600 0x20>; ++ } ; ++ ps7_scuwdt_0: ps7-scuwdt@f8f00620 { ++ clocks = <&clkc 4>; ++ compatible = "xlnx,ps7-scuwdt-1.00.a"; ++ device_type = "watchdog"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <1 14 0x301>; ++ reg = <0xf8f00620 0xe0>; ++ } ; ++ ps7_sd_0: ps7-sdio@e0100000 { ++ clock-frequency = <50000000>; ++ clock-names = "clk_xin", "clk_ahb"; ++ clocks = <&clkc 21>, <&clkc 32>; ++ compatible = "xlnx,ps7-sdio-1.00.a", "generic-sdhci", "arasan,sdhci-8.9a"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 24 4>; ++ reg = <0xe0100000 0x1000>; ++ xlnx,has-cd = <0x1>; ++ xlnx,has-power = <0x0>; ++ xlnx,has-wp = <0x1>; ++ } ; ++ ps7_slcr_0: ps7-slcr@f8000000 { ++ compatible = "xlnx,ps7-slcr-1.00.a", "xlnx,zynq-slcr"; ++ reg = <0xf8000000 0x1000>; ++ clocks { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ clkc: clkc { ++ #clock-cells = <1>; ++ clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", ++ "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", ++ "lqspi", "smc", "pcap", "gem0", "gem1", ++ "fclk0", "fclk1", "fclk2", "fclk3", "can0", ++ "can1", "sdio0", "sdio1", "uart0", "uart1", ++ "spi0", "spi1", "dma", "usb0_aper", "usb1_aper", ++ "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper", ++ "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper", ++ "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper", ++ "swdt", "dbg_trc", "dbg_apb"; ++ compatible = "xlnx,ps7-clkc"; ++ fclk-enable = <0xf>; ++ ps-clk-frequency = <33333333>; ++ } ; ++ } ; ++ } ; ++ ps7_ttc_0: ps7-ttc@f8001000 { ++ clocks = <&clkc 6>; ++ compatible = "xlnx,ps7-ttc-1.00.a", "cdns,ttc"; ++ interrupt-names = "ttc0", "ttc1", "ttc2"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 10 4>, <0 11 4>, <0 12 4>; ++ reg = <0xf8001000 0x1000>; ++ } ; ++ ps7_uart_1: serial@e0001000 { ++ clock-names = "ref_clk", "aper_clk"; ++ clocks = <&clkc 24>, <&clkc 41>; ++ compatible = "xlnx,ps7-uart-1.00.a", "xlnx,xuartps"; ++ current-speed = <115200>; ++ device_type = "serial"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 50 4>; ++ port-number = <0>; ++ reg = <0xe0001000 0x1000>; ++ xlnx,has-modem = <0x0>; ++ } ; ++ ps7_usb_0: ps7-usb@e0002000 { ++ clocks = <&clkc 28>; ++ compatible = "xlnx,ps7-usb-1.00.a"; ++ dr_mode = "host"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 21 4>; ++ phy_type = "ulpi"; ++ reg = <0xe0002000 0x1000>; ++ xlnx,usb-reset = "MIO 7"; ++ } ; ++ ps7_wdt_0: ps7-wdt@f8005000 { ++ clocks = <&clkc 45>; ++ compatible = "xlnx,ps7-wdt-1.00.a"; ++ device_type = "watchdog"; ++ interrupt-parent = <&ps7_scugic_0>; ++ interrupts = <0 9 1>; ++ reg = <0xf8005000 0x1000>; ++ reset = <0>; ++ timeout = <10>; ++ } ; ++ } ; ++} ; diff --git a/patches.zynq/0012-defconfig-zynq-merge-xilinx-zynq-defconfig-from-xili.patch b/patches.zynq/0012-defconfig-zynq-merge-xilinx-zynq-defconfig-from-xili.patch new file mode 100644 index 0000000..9fa9297 --- /dev/null +++ b/patches.zynq/0012-defconfig-zynq-merge-xilinx-zynq-defconfig-from-xili.patch @@ -0,0 +1,2477 @@ +From cda1e37742c76b3dc9ca4154279c517c669dd1ba Mon Sep 17 00:00:00 2001 +From: Michal Simek +Date: Tue, 24 Dec 2013 09:47:53 +0900 +Subject: defconfig: zynq: merge xilinx zynq defconfig from xilinx branch + +Copy the xilinx zynq defconfig from the Xilinx repository (commit +efc27505715e64526653f35274717c0fc56491e3 in master branch) and select +a few options for the LTSI 3.10.y kernel, such as the asaran SDCard +driver. We have tested it on the Zynq 702 board. + +Signed-off-by: Daniel Sangorrin +Signed-off-by: Yoshitake Kobayashi +--- + arch/arm/configs/xilinx_zynq702_defconfig | 2457 ++++++++++++++++++++++++++++++ + 1 file changed, 2457 insertions(+) + create mode 100644 arch/arm/configs/xilinx_zynq702_defconfig + +--- /dev/null ++++ b/arch/arm/configs/xilinx_zynq702_defconfig +@@ -0,0 +1,2457 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.10.24 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_NO_IOPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="-xilinx" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_FHANDLE is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_CHIP=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_IRQ_DOMAIN_DEBUG=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_PREEMPT_RCU=y ++CONFIG_PREEMPT_RCU=y ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_BOOST is not set ++# CONFIG_RCU_NOCB_CPU is not set ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++CONFIG_UIDGID_CONVERTED=y ++# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_SYSFS_DEPRECATED_V2=y ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_HOTPLUG=y ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++CONFIG_PERF_EVENTS=y ++# CONFIG_DEBUG_PERF_USE_VMALLOC is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_COMPAT_BRK=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_USE_GENERIC_SMP_HELPERS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_HW_BREAKPOINT=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++# CONFIG_GCOV_KERNEL is not set ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++# CONFIG_MODULES is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++CONFIG_EFI_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++# CONFIG_DEFAULT_DEADLINE is not set ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++CONFIG_UNINLINE_SPIN_UNLOCK=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++CONFIG_ARCH_VEXPRESS=y ++ ++# ++# Versatile Express platform type ++# ++CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA=y ++# CONFIG_ARCH_VEXPRESS_CA9X4 is not set ++CONFIG_PLAT_VERSATILE_CLCD=y ++CONFIG_PLAT_VERSATILE_SCHED_CLOCK=y ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_WM8850 is not set ++CONFIG_ARCH_ZYNQ=y ++CONFIG_PLAT_VERSATILE=y ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++CONFIG_CACHE_PL310=y ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_ARM_NR_BANKS=8 ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++CONFIG_PL310_ERRATA_588369=y ++# CONFIG_ARM_ERRATA_643719 is not set ++CONFIG_ARM_ERRATA_720789=y ++CONFIG_PL310_ERRATA_727915=y ++CONFIG_PL310_ERRATA_753970=y ++CONFIG_ARM_ERRATA_754322=y ++CONFIG_ARM_ERRATA_754327=y ++CONFIG_ARM_ERRATA_764369=y ++CONFIG_PL310_ERRATA_769419=y ++CONFIG_ARM_ERRATA_775420=y ++# CONFIG_ARM_ERRATA_798181 is not set ++CONFIG_ICST=y ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++CONFIG_SCHED_SMT=y ++CONFIG_HAVE_ARM_SCU=y ++# CONFIG_HAVE_ARM_ARCH_TIMER is not set ++CONFIG_HAVE_ARM_TWD=y ++# CONFIG_MCPM is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_LOCAL_TIMERS=y ++CONFIG_ARCH_NR_GPIO=0 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_COUNT=y ++CONFIG_HZ=100 ++CONFIG_SCHED_HRTICK=y ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_HW_PERF_EVENTS=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_COMPACTION is not set ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++# CONFIG_CC_STACKPROTECTOR is not set ++# CONFIG_XEN is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++# CONFIG_ARM_APPENDED_DTB is not set ++CONFIG_CMDLINE="console=ttyPS0,115200n8 root=/dev/ram rw initrd=0x00800000,16M earlyprintk mtdparts=physmap-flash.0:512K(nor-fsbl),512K(nor-u-boot),5M(nor-linux),9M(nor-user),1M(nor-scratch),-(nor-rootfs)" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++CONFIG_PM_RUNTIME=y ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=y ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=y ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=y ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_PRIVACY is not set ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=y ++CONFIG_INET6_XFRM_MODE_TUNNEL=y ++CONFIG_INET6_XFRM_MODE_BEET=y ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++CONFIG_IPV6_SIT=y ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++CONFIG_VLAN_8021Q=y ++# CONFIG_VLAN_8021Q_GVRP is not set ++# CONFIG_VLAN_8021Q_MVRP is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++CONFIG_BQL=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++CONFIG_FW_LOADER_USER_HELPER=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_DMA_SHARED_BUFFER is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++CONFIG_CMA_AREAS=7 ++ ++# ++# Bus devices ++# ++CONFIG_CONNECTOR=y ++CONFIG_PROC_EVENTS=y ++CONFIG_MTD=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++CONFIG_MTD_CFI=y ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_GEN_PROBE=y ++# CONFIG_MTD_CFI_ADV_OPTIONS is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_CFI_INTELEXT is not set ++CONFIG_MTD_CFI_AMDSTD=y ++# CONFIG_MTD_CFI_STAA is not set ++CONFIG_MTD_CFI_UTIL=y ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++CONFIG_MTD_PHYSMAP=y ++# CONFIG_MTD_PHYSMAP_COMPAT is not set ++CONFIG_MTD_PHYSMAP_OF=y ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++CONFIG_MTD_M25P80=y ++# CONFIG_M25PXX_USE_FAST_READ is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_XILINX_PS=y ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++CONFIG_PROC_DEVICETREE=y ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_DEVICE=y ++CONFIG_OF_I2C=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=16384 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ATMEL_SSC is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_ARM_CHARLCD is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++CONFIG_SI570=y ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++CONFIG_SRAM=y ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++CONFIG_EEPROM_AT24=y ++CONFIG_EEPROM_AT25=y ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++CONFIG_CHR_DEV_SG=y ++# CONFIG_CHR_DEV_SCH is not set ++CONFIG_SCSI_MULTI_LUN=y ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_ISCSI_BOOT_SYSFS is not set ++# CONFIG_SCSI_UFSHCD is not set ++# CONFIG_LIBFC is not set ++# CONFIG_LIBFCOE is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HAVE_PATA_PLATFORM=y ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++CONFIG_MII=y ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++CONFIG_ETHERNET=y ++CONFIG_NET_CADENCE=y ++# CONFIG_ARM_AT91_ETHER is not set ++CONFIG_MACB=y ++CONFIG_NET_VENDOR_BROADCOM=y ++# CONFIG_B44 is not set ++# CONFIG_NET_CALXEDA_XGMAC is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_INTEL=y ++CONFIG_NET_VENDOR_I825XX=y ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_NET_VENDOR_XILINX=y ++CONFIG_XILINX_EMACLITE=y ++CONFIG_XILINX_AXI_EMAC=y ++CONFIG_XILINX_PS_EMAC=y ++# CONFIG_XILINX_PS_EMAC_HWTSTAMP is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++CONFIG_MARVELL_PHY=y ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++CONFIG_VITESSE_PHY=y ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++CONFIG_MDIO_BITBANG=y ++# CONFIG_MDIO_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++CONFIG_INPUT_SPARSEKMAP=y ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++# CONFIG_SERIAL_AMBA_PL011 is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++CONFIG_SERIAL_XILINX_PS_UART=y ++CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++CONFIG_I2C_MUX_PCA954x=y ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_VERSATILE is not set ++CONFIG_I2C_XILINX_PS=y ++CONFIG_I2C_XILINX=y ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PL022 is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++CONFIG_SPI_XILINX=y ++CONFIG_SPI_XILINX_PS_QSPI=y ++# CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED is not set ++CONFIG_SPI_XILINX_PS_SPI=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_SPIDEV is not set ++# CONFIG_SPI_TLE62X0 is not set ++ ++# ++# Qualcomm MSM SSBI bus support ++# ++# CONFIG_SSBI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++CONFIG_PPS=y ++# CONFIG_PPS_DEBUG is not set ++ ++# ++# PPS clients support ++# ++# CONFIG_PPS_CLIENT_KTIMER is not set ++# CONFIG_PPS_CLIENT_LDISC is not set ++# CONFIG_PPS_CLIENT_GPIO is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++CONFIG_PTP_1588_CLOCK=y ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++# CONFIG_PTP_1588_CLOCK_PCH is not set ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_GPIOLIB=y ++CONFIG_OF_GPIO=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_PL061 is not set ++# CONFIG_GPIO_RCAR is not set ++# CONFIG_GPIO_TS5500 is not set ++CONFIG_GPIO_XILINX=y ++CONFIG_GPIO_XILINX_PS=y ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_ISP1704 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++# CONFIG_BATTERY_GOLDFISH is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_RESTART is not set ++CONFIG_POWER_RESET_VEXPRESS=y ++# CONFIG_POWER_AVS is not set ++CONFIG_HWMON=y ++# CONFIG_HWMON_VID is not set ++# CONFIG_HWMON_DEBUG_CHIP is not set ++ ++# ++# Native drivers ++# ++# CONFIG_SENSORS_AD7314 is not set ++# CONFIG_SENSORS_AD7414 is not set ++# CONFIG_SENSORS_AD7418 is not set ++# CONFIG_SENSORS_ADCXX is not set ++# CONFIG_SENSORS_ADM1021 is not set ++# CONFIG_SENSORS_ADM1025 is not set ++# CONFIG_SENSORS_ADM1026 is not set ++# CONFIG_SENSORS_ADM1029 is not set ++# CONFIG_SENSORS_ADM1031 is not set ++# CONFIG_SENSORS_ADM9240 is not set ++# CONFIG_SENSORS_ADT7310 is not set ++# CONFIG_SENSORS_ADT7410 is not set ++# CONFIG_SENSORS_ADT7411 is not set ++# CONFIG_SENSORS_ADT7462 is not set ++# CONFIG_SENSORS_ADT7470 is not set ++# CONFIG_SENSORS_ADT7475 is not set ++# CONFIG_SENSORS_ASC7621 is not set ++# CONFIG_SENSORS_ATXP1 is not set ++# CONFIG_SENSORS_DS620 is not set ++# CONFIG_SENSORS_DS1621 is not set ++# CONFIG_SENSORS_F71805F is not set ++# CONFIG_SENSORS_F71882FG is not set ++# CONFIG_SENSORS_F75375S is not set ++# CONFIG_SENSORS_G760A is not set ++# CONFIG_SENSORS_GL518SM is not set ++# CONFIG_SENSORS_GL520SM is not set ++# CONFIG_SENSORS_GPIO_FAN is not set ++# CONFIG_SENSORS_HIH6130 is not set ++# CONFIG_SENSORS_IT87 is not set ++# CONFIG_SENSORS_JC42 is not set ++# CONFIG_SENSORS_LINEAGE is not set ++# CONFIG_SENSORS_LM63 is not set ++# CONFIG_SENSORS_LM70 is not set ++# CONFIG_SENSORS_LM73 is not set ++# CONFIG_SENSORS_LM75 is not set ++# CONFIG_SENSORS_LM77 is not set ++# CONFIG_SENSORS_LM78 is not set ++# CONFIG_SENSORS_LM80 is not set ++# CONFIG_SENSORS_LM83 is not set ++# CONFIG_SENSORS_LM85 is not set ++# CONFIG_SENSORS_LM87 is not set ++# CONFIG_SENSORS_LM90 is not set ++# CONFIG_SENSORS_LM92 is not set ++# CONFIG_SENSORS_LM93 is not set ++# CONFIG_SENSORS_LTC4151 is not set ++# CONFIG_SENSORS_LTC4215 is not set ++# CONFIG_SENSORS_LTC4245 is not set ++# CONFIG_SENSORS_LTC4261 is not set ++# CONFIG_SENSORS_LM95234 is not set ++# CONFIG_SENSORS_LM95241 is not set ++# CONFIG_SENSORS_LM95245 is not set ++# CONFIG_SENSORS_MAX1111 is not set ++# CONFIG_SENSORS_MAX16065 is not set ++# CONFIG_SENSORS_MAX1619 is not set ++# CONFIG_SENSORS_MAX1668 is not set ++# CONFIG_SENSORS_MAX197 is not set ++# CONFIG_SENSORS_MAX6639 is not set ++# CONFIG_SENSORS_MAX6642 is not set ++# CONFIG_SENSORS_MAX6650 is not set ++# CONFIG_SENSORS_MAX6697 is not set ++# CONFIG_SENSORS_MCP3021 is not set ++# CONFIG_SENSORS_NCT6775 is not set ++# CONFIG_SENSORS_PC87360 is not set ++# CONFIG_SENSORS_PC87427 is not set ++# CONFIG_SENSORS_PCF8591 is not set ++# CONFIG_PMBUS is not set ++# CONFIG_SENSORS_SHT15 is not set ++# CONFIG_SENSORS_SHT21 is not set ++# CONFIG_SENSORS_SMM665 is not set ++# CONFIG_SENSORS_DME1737 is not set ++# CONFIG_SENSORS_EMC1403 is not set ++# CONFIG_SENSORS_EMC2103 is not set ++# CONFIG_SENSORS_EMC6W201 is not set ++# CONFIG_SENSORS_SMSC47M1 is not set ++# CONFIG_SENSORS_SMSC47M192 is not set ++# CONFIG_SENSORS_SMSC47B397 is not set ++# CONFIG_SENSORS_SCH56XX_COMMON is not set ++# CONFIG_SENSORS_SCH5627 is not set ++# CONFIG_SENSORS_SCH5636 is not set ++# CONFIG_SENSORS_ADS1015 is not set ++# CONFIG_SENSORS_ADS7828 is not set ++# CONFIG_SENSORS_ADS7871 is not set ++# CONFIG_SENSORS_AMC6821 is not set ++# CONFIG_SENSORS_INA209 is not set ++# CONFIG_SENSORS_INA2XX is not set ++# CONFIG_SENSORS_THMC50 is not set ++# CONFIG_SENSORS_TMP102 is not set ++# CONFIG_SENSORS_TMP401 is not set ++# CONFIG_SENSORS_TMP421 is not set ++# CONFIG_SENSORS_VEXPRESS is not set ++# CONFIG_SENSORS_VT1211 is not set ++# CONFIG_SENSORS_W83781D is not set ++# CONFIG_SENSORS_W83791D is not set ++# CONFIG_SENSORS_W83792D is not set ++# CONFIG_SENSORS_W83793 is not set ++# CONFIG_SENSORS_W83795 is not set ++# CONFIG_SENSORS_W83L785TS is not set ++# CONFIG_SENSORS_W83L786NG is not set ++# CONFIG_SENSORS_W83627HF is not set ++# CONFIG_SENSORS_W83627EHF is not set ++# CONFIG_THERMAL is not set ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_ARM_SP805_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++CONFIG_MPCORE_WATCHDOG=y ++CONFIG_XILINX_PS_WATCHDOG=y ++# CONFIG_MAX63XX_WATCHDOG is not set ++CONFIG_XILINX_WATCHDOG=y ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_SYSCON is not set ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_VEXPRESS_CONFIG=y ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=y ++ ++# ++# Multimedia core support ++# ++# CONFIG_MEDIA_CAMERA_SUPPORT is not set ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++ ++# ++# Customise DVB Frontends ++# ++# CONFIG_DVB_TUNER_DIB0070 is not set ++# CONFIG_DVB_TUNER_DIB0090 is not set ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++# CONFIG_TEGRA_HOST1X is not set ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_UVESA is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_GOLDFISH is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++# CONFIG_LOGO is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO_TPKBD is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++# CONFIG_HID_MONTEREY is not set ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_PS3REMOTE is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SONY is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++CONFIG_USB_ARCH_HAS_EHCI=y ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++CONFIG_USB_OTG=y ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_ROOT_HUB_TT=y ++# CONFIG_USB_EHCI_TT_NEWSCHED is not set ++CONFIG_USB_XUSBPS_DR_OF=y ++CONFIG_USB_EHCI_XUSBPS=y ++# CONFIG_USB_EHCI_HCD_PLATFORM is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_RENESAS_USBHS is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++CONFIG_USB_PHY=y ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_OMAP_CONTROL_USB is not set ++# CONFIG_OMAP_USB3 is not set ++# CONFIG_SAMSUNG_USBPHY is not set ++# CONFIG_SAMSUNG_USB2PHY is not set ++# CONFIG_SAMSUNG_USB3PHY is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_RCAR_PHY is not set ++CONFIG_USB_ULPI=y ++CONFIG_USB_ULPI_VIEWPORT=y ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=y ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=y ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++# CONFIG_USB_MASS_STORAGE is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++# CONFIG_MMC_CLKGATE is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_OF_ARASAN=y ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_EDAC=y ++CONFIG_EDAC_LEGACY_SYSFS=y ++# CONFIG_EDAC_DEBUG is not set ++CONFIG_EDAC_MM_EDAC=y ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_SYSTOHC=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8523 is not set ++CONFIG_RTC_DRV_PCF8563=y ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_DS3234 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++# CONFIG_RTC_DRV_RX4581 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++# CONFIG_RTC_DRV_DS2404 is not set ++ ++# ++# on-CPU RTC drivers ++# ++# CONFIG_RTC_DRV_PL030 is not set ++# CONFIG_RTC_DRV_PL031 is not set ++# CONFIG_RTC_DRV_SNVS is not set ++ ++# ++# HID Sensor RTC drivers ++# ++# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set ++CONFIG_DMADEVICES=y ++# CONFIG_DMADEVICES_DEBUG is not set ++ ++# ++# DMA Devices ++# ++# CONFIG_AMBA_PL08X is not set ++# CONFIG_DW_DMAC is not set ++# CONFIG_TIMB_DMA is not set ++CONFIG_PL330_DMA=y ++CONFIG_DMA_ENGINE=y ++CONFIG_DMA_OF=y ++ ++# ++# DMA Clients ++# ++# CONFIG_NET_DMA is not set ++# CONFIG_ASYNC_TX_DMA is not set ++# CONFIG_DMATEST is not set ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=y ++# CONFIG_UIO_PDRV is not set ++CONFIG_UIO_PDRV_GENIRQ=y ++# CONFIG_UIO_DMEM_GENIRQ is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++CONFIG_COMMON_CLK_DEBUG=y ++CONFIG_COMMON_CLK_VERSATILE=y ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_AXI_CLKGEN is not set ++ ++# ++# Hardware Spinlock drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_CADENCE_TTC_TIMER=y ++# CONFIG_MAILBOX is not set ++CONFIG_IOMMU_SUPPORT=y ++CONFIG_OF_IOMMU=y ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++CONFIG_MEMORY=y ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++# CONFIG_RESET_CONTROLLER is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++CONFIG_EXT2_FS=y ++# CONFIG_EXT2_FS_XATTR is not set ++# CONFIG_EXT2_FS_XIP is not set ++CONFIG_EXT3_FS=y ++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set ++CONFIG_EXT3_FS_XATTR=y ++# CONFIG_EXT3_FS_POSIX_ACL is not set ++# CONFIG_EXT3_FS_SECURITY is not set ++CONFIG_EXT4_FS=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++# CONFIG_DNOTIFY is not set ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=y ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++CONFIG_JFFS2_SUMMARY=y ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++# CONFIG_NFS_V3_ACL is not set ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++CONFIG_ROOT_NFS=y ++# CONFIG_NFSD is not set ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_DEBUG_SLAB is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_PREEMPT is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_PROVE_RCU_DELAY is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_VERBOSE is not set ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_LKDTM is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++CONFIG_DYNAMIC_DEBUG=y ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++CONFIG_DEBUG_LL=y ++# CONFIG_DEBUG_ZYNQ_UART0 is not set ++CONFIG_DEBUG_ZYNQ_UART1=y ++# CONFIG_DEBUG_VEXPRESS_UART0_DETECT is not set ++# CONFIG_DEBUG_VEXPRESS_UART0_CA9 is not set ++# CONFIG_DEBUG_VEXPRESS_UART0_RS1 is not set ++# CONFIG_DEBUG_ICEDCC is not set ++# CONFIG_DEBUG_SEMIHOSTING is not set ++CONFIG_DEBUG_LL_INCLUDE="debug/zynq.S" ++CONFIG_DEBUG_UNCOMPRESS=y ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++CONFIG_EARLY_PRINTK=y ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++# CONFIG_CRYPTO_FIPS is not set ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++CONFIG_CRYPTO_ANSI_CPRNG=y ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_VIRTUALIZATION=y ++CONFIG_KVM_ARM_MAX_VCPUS=0 ++ ++# ++# Virtio drivers ++# diff --git a/series b/series index 50c5c55..a2d6c59 100644 --- a/series +++ b/series @@ -2241,6 +2241,18 @@ patches.zynq/0104-video-xilinxfb-Fix-for-Use-standard-variable-name-co.patch #patches.zynq/0106-i2c-move-OF-helpers-into-the-core.patch patches.zynq/0107-i2c-xiic-Remove-casting-the-return-value-which-is-a-.patch patches.zynq/0108-i2c-Include-linux-of.h-header.patch +patches.zynq/0001-i2c-xilinx-merge-i2c-driver-from-Xilinx-repository-i.patch +patches.zynq/0002-i2c-si570-merge-support-for-si570-clock-generator.patch +patches.zynq/0003-mmc-arasan-add-a-driver-for-Arasan-s-SDHCI-controlle.patch +patches.zynq/0004-net-ethernet-xilinx-Merge-driver-from-Xilinx-reposit.patch +patches.zynq/0005-gpio-xilinx-merge-Xilinx-gpio-support-into-LTSI-3.10.patch +patches.zynq/0006-spi-xilinx-merge-qspi-support-from-xilinx-repository.patch +patches.zynq/0007-mtd-xilinx-merge-nand-flash-support-from-xilinx-repo.patch +patches.zynq/0008-watchdog-xilinx-merge-support-for-xilinx-watchdog.patch +patches.zynq/0009-usb-zynq-merge-usb-support-for-xilinx-zynq-soc.patch +patches.zynq/0010-memory-zynq-merge-driver-for-Zynq-SMC.patch +patches.zynq/0011-arm-dts-zynq-Merge-zynq-zc702.dts-with-Xilinx-reposi.patch +patches.zynq/0012-defconfig-zynq-merge-xilinx-zynq-defconfig-from-xili.patch ############################################################################# -- 2.7.4