* [PATCH] [V2] net: add Xilinx emac lite device driver
From: John Linn @ 2009-08-19 12:29 UTC (permalink / raw)
To: netdev, linuxppc-dev, jgarzik, davem
Cc: Sadanand M, Michal Simek, John Linn, John Williams
This patch adds support for the Xilinx Ethernet Lite device. The
soft logic core from Xilinx is typically used on Virtex and Spartan
designs attached to either a PowerPC or a Microblaze processor.
CC: Grant Likely <grant.likely@secretlab.ca>
CC: Josh Boyer <jwboyer@linux.vnet.ibm.com>
CC: John Williams <john.williams@petalogix.com>
CC: Michal Simek <michal.simek@petalogix.com>
Signed-off-by: Sadanand M <sadanan@xilinx.com>
Signed-off-by: John Linn <john.linn@xilinx.com>
---
V2 - cleanup based on review, added depends for ppc and mb in Kconfig
---
drivers/net/Kconfig | 6 +
drivers/net/Makefile | 1 +
drivers/net/xilinx_emaclite.c | 1040 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1047 insertions(+), 0 deletions(-)
create mode 100755 drivers/net/xilinx_emaclite.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 5f6509a..ec77b69 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1926,6 +1926,12 @@ config ATL2
To compile this driver as a module, choose M here. The module
will be called atl2.
+config XILINX_EMACLITE
+ tristate "Xilinx 10/100 Ethernet Lite support"
+ depends on PPC32 || MICROBLAZE
+ help
+ This driver supports the 10/100 Ethernet Lite from Xilinx.
+
source "drivers/net/fs_enet/Kconfig"
endif # NET_ETHERNET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index ead8cab..99ae6d7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -142,6 +142,7 @@ obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
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_QLA3XXX) += qla3xxx.o
obj-$(CONFIG_QLGE) += qlge/
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
new file mode 100755
index 0000000..3716e20
--- /dev/null
+++ b/drivers/net/xilinx_emaclite.c
@@ -0,0 +1,1040 @@
+/*
+ * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device.
+ *
+ * This is a new flat driver which is based on the original emac_lite
+ * driver from John Williams <john.williams@petalogix.com>.
+ *
+ * 2007-2009 (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.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/io.h>
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#define DRIVER_NAME "xilinx_emaclite"
+
+/* Register offsets for the EmacLite Core */
+#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */
+#define XEL_GIER_OFFSET 0x07F8 /* GIE Register */
+#define XEL_TSR_OFFSET 0x07FC /* Tx status */
+#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */
+
+#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */
+#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 */
+
+/* Global Interrupt Enable Register (GIER) Bit Masks */
+#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */
+
+/* Transmit Status Register (TSR) Bit Masks */
+#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */
+#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */
+#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */
+#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit
+ * only. This is not documented
+ * in the HW spec */
+
+/* Define for programming the MAC address into the EmacLite */
+#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK)
+
+/* Receive Status Register (RSR) */
+#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */
+#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */
+
+/* Transmit Packet Length Register (TPLR) */
+#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */
+
+/* Receive Packet Length Register (RPLR) */
+#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */
+
+#define XEL_HEADER_OFFSET 12 /* Offset to length field */
+#define XEL_HEADER_SHIFT 16 /* Shift value for length */
+
+/* General Ethernet Definitions */
+#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */
+#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */
+
+
+
+#define TX_TIMEOUT (60*HZ) /* Tx timeout is 60 seconds. */
+#define ALIGNMENT 4
+
+/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */
+#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
+
+/**
+ * struct net_local - Our private per device data
+ * @ndev: instance of the network device
+ * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW
+ * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW
+ * @next_tx_buf_to_use: next Tx buffer to write to
+ * @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
+ */
+struct net_local {
+
+ struct net_device *ndev;
+
+ bool tx_ping_pong;
+ bool rx_ping_pong;
+ u32 next_tx_buf_to_use;
+ u32 next_rx_buf_to_use;
+ void __iomem *base_addr;
+
+ spinlock_t reset_lock;
+ struct sk_buff *deferred_skb;
+};
+
+
+/*************************/
+/* EmacLite driver calls */
+/*************************/
+
+/**
+ * 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.
+ */
+static void xemaclite_enable_interrupts(struct net_local *drvdata)
+{
+ u32 reg_data;
+
+ /* Enable the Tx interrupts for the first Buffer */
+ reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
+ reg_data | XEL_TSR_XMIT_IE_MASK);
+
+ /* Enable the Tx interrupts for the second Buffer if
+ * configured in HW */
+ if (drvdata->tx_ping_pong != 0) {
+ reg_data = in_be32(drvdata->base_addr +
+ XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET,
+ reg_data | XEL_TSR_XMIT_IE_MASK);
+ }
+
+ /* Enable the Rx interrupts for the first buffer */
+ reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
+ reg_data | XEL_RSR_RECV_IE_MASK);
+
+ /* Enable the Rx interrupts for the second Buffer if
+ * configured in HW */
+ if (drvdata->rx_ping_pong != 0) {
+ reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_RSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_RSR_OFFSET,
+ reg_data | XEL_RSR_RECV_IE_MASK);
+ }
+
+ /* Enable the Global Interrupt Enable */
+ out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+}
+
+/**
+ * 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,
+ * along with the Global Interrupt Enable.
+ */
+static void xemaclite_disable_interrupts(struct net_local *drvdata)
+{
+ u32 reg_data;
+
+ /* Disable the Global Interrupt Enable */
+ out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+
+ /* Disable the Tx interrupts for the first buffer */
+ reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
+ reg_data & (~XEL_TSR_XMIT_IE_MASK));
+
+ /* Disable the Tx interrupts for the second Buffer
+ * if configured in HW */
+ if (drvdata->tx_ping_pong != 0) {
+ reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET,
+ reg_data & (~XEL_TSR_XMIT_IE_MASK));
+ }
+
+ /* Disable the Rx interrupts for the first buffer */
+ reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
+ reg_data & (~XEL_RSR_RECV_IE_MASK));
+
+ /* Disable the Rx interrupts for the second buffer
+ * if configured in HW */
+ if (drvdata->rx_ping_pong != 0) {
+
+ reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_RSR_OFFSET);
+ out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_RSR_OFFSET,
+ reg_data & (~XEL_RSR_RECV_IE_MASK));
+ }
+}
+
+/**
+ * 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
+ *
+ * This function writes data from a 16-bit aligned buffer to a 32-bit aligned
+ * address in the EmacLite device.
+ */
+static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr,
+ unsigned length)
+{
+ u32 align_buffer;
+ u32 *to_u32_ptr;
+ u16 *from_u16_ptr, *to_u16_ptr;
+
+ to_u32_ptr = dest_ptr;
+ from_u16_ptr = (u16 *) src_ptr;
+ align_buffer = 0;
+
+ for (; length > 3; length -= 4) {
+ to_u16_ptr = (u16 *) ((void *) &align_buffer);
+ *to_u16_ptr++ = *from_u16_ptr++;
+ *to_u16_ptr++ = *from_u16_ptr++;
+
+ /* Output a word */
+ *to_u32_ptr++ = align_buffer;
+ }
+ if (length) {
+ u8 *from_u8_ptr, *to_u8_ptr;
+
+ /* Set up to output the remaining data */
+ align_buffer = 0;
+ to_u8_ptr = (u8 *) &align_buffer;
+ from_u8_ptr = (u8 *) from_u16_ptr;
+
+ /* Output the remaining data */
+ for (; length > 0; length--)
+ *to_u8_ptr++ = *from_u8_ptr++;
+
+ *to_u32_ptr = align_buffer;
+ }
+}
+
+/**
+ * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer
+ * @src_ptr: Pointer to the 32-bit aligned source address
+ * @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.
+ */
+static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr,
+ unsigned length)
+{
+ u16 *to_u16_ptr, *from_u16_ptr;
+ u32 *from_u32_ptr;
+ u32 align_buffer;
+
+ from_u32_ptr = src_ptr;
+ to_u16_ptr = (u16 *) dest_ptr;
+
+ for (; length > 3; length -= 4) {
+ /* Copy each word into the temporary buffer */
+ align_buffer = *from_u32_ptr++;
+ from_u16_ptr = (u16 *)&align_buffer;
+
+ /* Read data from source */
+ *to_u16_ptr++ = *from_u16_ptr++;
+ *to_u16_ptr++ = *from_u16_ptr++;
+ }
+
+ if (length) {
+ u8 *to_u8_ptr, *from_u8_ptr;
+
+ /* Set up to read the remaining data */
+ to_u8_ptr = (u8 *) to_u16_ptr;
+ align_buffer = *from_u32_ptr++;
+ from_u8_ptr = (u8 *) &align_buffer;
+
+ /* Read the remaining data */
+ for (; length > 0; length--)
+ *to_u8_ptr = *from_u8_ptr;
+ }
+}
+
+/**
+ * xemaclite_send_data - Send an Ethernet frame
+ * @drvdata: Pointer to the Emaclite device private data
+ * @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.
+ *
+ * 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.
+ */
+static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
+ unsigned int byte_count)
+{
+ u32 reg_data;
+ void __iomem *addr;
+
+ /* Determine the expected Tx buffer address */
+ addr = drvdata->base_addr + drvdata->next_tx_buf_to_use;
+
+ /* If the length is too large, truncate it */
+ if (byte_count > ETH_FRAME_LEN)
+ byte_count = ETH_FRAME_LEN;
+
+ /* Check if the expected buffer is available */
+ reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
+ XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
+
+ /* Switch to next buffer if configured */
+ if (drvdata->tx_ping_pong != 0)
+ drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET;
+ } else if (drvdata->tx_ping_pong != 0) {
+ /* If the expected buffer is full, try the other buffer,
+ * if it is configured in HW */
+
+ addr = (void __iomem __force *)((u32 __force)addr ^
+ XEL_BUFFER_OFFSET);
+ reg_data = in_be32(addr + XEL_TSR_OFFSET);
+
+ if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
+ XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
+ return -1; /* Buffers were full, return failure */
+ } else
+ return -1; /* Buffer was full, return failure */
+
+ /* Write the frame to the buffer */
+ xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
+
+ out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK));
+
+ /* Update the Tx Status Register to indicate that there is a
+ * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
+ * is used by the interrupt handler to check whether a frame
+ * has been transmitted */
+ reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
+ out_be32(addr + XEL_TSR_OFFSET, reg_data);
+
+ return 0;
+}
+
+/**
+ * xemaclite_recv_data - Receive a frame
+ * @drvdata: Pointer to the Emaclite device private data
+ * @data: Address where the data is to be received
+ *
+ * This function is intended to be called from the interrupt context or
+ * with a wrapper which waits for the receive frame to be available.
+ *
+ * Return: Total number of bytes received
+ */
+static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
+{
+ void __iomem *addr;
+ u16 length, proto_type;
+ u32 reg_data;
+
+ /* Determine the expected buffer address */
+ addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
+
+ /* Verify which buffer has valid data */
+ reg_data = in_be32(addr + XEL_RSR_OFFSET);
+
+ if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
+ if (drvdata->rx_ping_pong != 0)
+ drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET;
+ } else {
+ /* The instance is out of sync, try other buffer if other
+ * buffer is configured, return 0 otherwise. If the instance is
+ * out of sync, do not update the 'next_rx_buf_to_use' since it
+ * will correct on subsequent calls */
+ if (drvdata->rx_ping_pong != 0)
+ addr = (void __iomem __force *)((u32 __force)addr ^
+ XEL_BUFFER_OFFSET);
+ else
+ return 0; /* No data was available */
+
+ /* Verify that buffer has valid data */
+ reg_data = in_be32(addr + XEL_RSR_OFFSET);
+ if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
+ XEL_RSR_RECV_DONE_MASK)
+ return 0; /* No data was available */
+ }
+
+ /* Get the protocol type of the ethernet frame that arrived */
+ proto_type = ((in_be32(addr + XEL_HEADER_OFFSET +
+ XEL_RXBUFF_OFFSET) >> XEL_HEADER_SHIFT) &
+ XEL_RPLR_LENGTH_MASK);
+
+ /* Check if received ethernet frame is a raw ethernet frame
+ * or an IP packet or an ARP packet */
+ if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
+
+ if (proto_type == ETH_P_IP) {
+ length = ((in_be32(addr +
+ XEL_HEADER_IP_LENGTH_OFFSET +
+ XEL_RXBUFF_OFFSET) >>
+ XEL_HEADER_SHIFT) &
+ XEL_RPLR_LENGTH_MASK);
+ length += ETH_HLEN + ETH_FCS_LEN;
+
+ } else if (proto_type == ETH_P_ARP)
+ length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN;
+ else
+ /* Field contains type other than IP or ARP, use max
+ * frame size and let user parse it */
+ length = ETH_FRAME_LEN + ETH_FCS_LEN;
+ } else
+ /* Use the length in the frame, plus the header and trailer */
+ length = proto_type + ETH_HLEN + ETH_FCS_LEN;
+
+ /* Read from the EmacLite device */
+ xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
+ data, length);
+
+ /* Acknowledge the frame */
+ reg_data = in_be32(addr + XEL_RSR_OFFSET);
+ reg_data &= ~XEL_RSR_RECV_DONE_MASK;
+ out_be32(addr + XEL_RSR_OFFSET, reg_data);
+
+ return length;
+}
+
+/**
+ * xemaclite_set_mac_address - Set the MAC address for this device
+ * @drvdata: Pointer to the Emaclite device private data
+ * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value)
+ *
+ * Tx must be idle and Rx should be idle for deterministic results.
+ * It is recommended that this function should be called after the
+ * initialization and before transmission of any packets from the device.
+ * The MAC address can be programmed using any of the two transmit
+ * buffers (if configured).
+ */
+static void xemaclite_set_mac_address(struct net_local *drvdata,
+ u8 *address_ptr)
+{
+ void __iomem *addr;
+ u32 reg_data;
+
+ /* Determine the expected Tx buffer address */
+ addr = drvdata->base_addr + drvdata->next_tx_buf_to_use;
+
+ xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
+
+ out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN);
+
+ /* Update the MAC address in the EmacLite */
+ reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR);
+
+ /* Wait for EmacLite to finish with the MAC address update */
+ while ((in_be32(addr + XEL_TSR_OFFSET) &
+ XEL_TSR_PROG_MAC_ADDR) != 0)
+ ;
+}
+
+/**
+ * xemaclite_tx_timeout - Callback for Tx Timeout
+ * @dev: Pointer to the network device
+ *
+ * This function is called when Tx time out occurs for Emaclite device.
+ */
+static void xemaclite_tx_timeout(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ unsigned long flags;
+
+ dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n",
+ TX_TIMEOUT * 1000UL / HZ);
+
+ dev->stats.tx_errors++;
+
+ /* Reset the device */
+ spin_lock_irqsave(&lp->reset_lock, flags);
+
+ /* Shouldn't really be necessary, but shouldn't hurt */
+ netif_stop_queue(dev);
+
+ xemaclite_disable_interrupts(lp);
+ xemaclite_enable_interrupts(lp);
+
+ if (lp->deferred_skb) {
+ dev_kfree_skb(lp->deferred_skb);
+ lp->deferred_skb = NULL;
+ dev->stats.tx_errors++;
+ }
+
+ /* To exclude tx timeout */
+ dev->trans_start = 0xffffffff - TX_TIMEOUT - TX_TIMEOUT;
+
+ /* We're all ready to go. Start the queue */
+ netif_wake_queue(dev);
+ spin_unlock_irqrestore(&lp->reset_lock, flags);
+}
+
+/**********************/
+/* Interrupt Handlers */
+/**********************/
+
+/**
+ * xemaclite_tx_handler - Interrupt handler for frames sent
+ * @dev: Pointer to the network device
+ *
+ * This function updates the number of packets transmitted and handles the
+ * deferred skb, if there is one.
+ */
+static void xemaclite_tx_handler(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+
+ dev->stats.tx_packets++;
+ if (lp->deferred_skb) {
+ if (xemaclite_send_data(lp,
+ (u8 *) lp->deferred_skb->data,
+ lp->deferred_skb->len) != 0)
+ return;
+ else {
+ dev->stats.tx_bytes += lp->deferred_skb->len;
+ dev_kfree_skb_irq(lp->deferred_skb);
+ lp->deferred_skb = NULL;
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+/**
+ * xemaclite_rx_handler- Interrupt handler for frames received
+ * @dev: Pointer to the network device
+ *
+ * This function allocates memory for a socket buffer, fills it with data
+ * received and hands it over to the TCP/IP stack.
+ */
+static void xemaclite_rx_handler(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ struct sk_buff *skb;
+ unsigned int align;
+ u32 len;
+
+ len = ETH_FRAME_LEN + ETH_FCS_LEN;
+ skb = dev_alloc_skb(len + ALIGNMENT);
+ if (!skb) {
+ /* Couldn't get memory. */
+ dev->stats.rx_dropped++;
+ dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n");
+ return;
+ }
+
+ /*
+ * A new skb should have the data halfword aligned, but this code is
+ * here just in case that isn't true. Calculate how many
+ * bytes we should reserve to get the data to start on a word
+ * boundary */
+ align = BUFFER_ALIGN(skb->data);
+ if (align)
+ skb_reserve(skb, align);
+
+ skb_reserve(skb, 2);
+
+ len = xemaclite_recv_data(lp, (u8 *) skb->data);
+
+ if (!len) {
+ dev->stats.rx_errors++;
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ skb_put(skb, len); /* Tell the skb how much data we got */
+ skb->dev = dev; /* Fill out required meta-data */
+
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+ dev->last_rx = jiffies;
+
+ netif_rx(skb); /* Send the packet upstream */
+}
+
+/**
+ * xemaclite_interrupt - Interrupt handler for this driver
+ * @irq: Irq of the Emaclite device
+ * @dev_id: Void pointer to the network device instance used as callback
+ * reference
+ *
+ * This function handles the Tx and Rx interrupts of the EmacLite device.
+ */
+static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
+{
+ bool tx_complete = 0;
+ struct net_device *dev = dev_id;
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ void __iomem *base_addr = lp->base_addr;
+ u32 tx_status;
+
+ /* Check if there is Rx Data available */
+ if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) ||
+ (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+ & XEL_RSR_RECV_DONE_MASK))
+
+ xemaclite_rx_handler(dev);
+
+ /* Check if the Transmission for the first buffer is completed */
+ tx_status = in_be32(base_addr + XEL_TSR_OFFSET);
+ if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
+ (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
+
+ tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
+ out_be32(base_addr + XEL_TSR_OFFSET, tx_status);
+
+ tx_complete = 1;
+ }
+
+ /* Check if the Transmission for the second buffer is completed */
+ tx_status = in_be32(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) {
+
+ tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
+ out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET,
+ tx_status);
+
+ tx_complete = 1;
+ }
+
+ /* If there was a Tx interrupt, call the Tx Handler */
+ if (tx_complete != 0)
+ xemaclite_tx_handler(dev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * xemaclite_open - Open the network device
+ * @dev: Pointer to the network device
+ *
+ * This function sets the MAC address, requests an IRQ and enables interrupts
+ * for the Emaclite device and starts the Tx queue.
+ */
+static int xemaclite_open(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ int retval;
+
+ /* Just to be safe, stop the device first */
+ xemaclite_disable_interrupts(lp);
+
+ /* Set the MAC address each time opened */
+ xemaclite_set_mac_address(lp, dev->dev_addr);
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, &xemaclite_interrupt, 0, dev->name, dev);
+ if (retval) {
+ dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n",
+ dev->irq);
+ return retval;
+ }
+
+ /* Enable Interrupts */
+ xemaclite_enable_interrupts(lp);
+
+ /* We're ready to go */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/**
+ * xemaclite_close - Close the network device
+ * @dev: Pointer to the network device
+ *
+ * This function stops the Tx queue, disables interrupts and frees the IRQ for
+ * the Emaclite device.
+ */
+static int xemaclite_close(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ xemaclite_disable_interrupts(lp);
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+/**
+ * xemaclite_get_stats - Get the stats for the net_device
+ * @dev: Pointer to the network device
+ *
+ * This function returns the address of the 'net_device_stats' structure for the
+ * given network device. This structure holds usage statistics for the network
+ * device.
+ *
+ * Return: Pointer to the net_device_stats structure.
+ */
+static struct net_device_stats *xemaclite_get_stats(struct net_device *dev)
+{
+ return &dev->stats;
+}
+
+/**
+ * xemaclite_send - Transmit a frame
+ * @orig_skb: Pointer to the socket buffer to be transmitted
+ * @dev: Pointer to the network device
+ *
+ * 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 from socket buffer data,
+ * updates the stats and frees the socket buffer. The Tx completion is signaled
+ * by an interrupt. If the Tx buffer isn't free, then the socket buffer is
+ * deferred and the Tx queue is stopped so that the deferred socket buffer can
+ * be transmitted when the Emaclite device is free to transmit data.
+ *
+ * Return: 0, always.
+ */
+static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ struct sk_buff *new_skb;
+ unsigned int len;
+ unsigned long flags;
+
+ len = orig_skb->len;
+
+ new_skb = orig_skb;
+
+ spin_lock_irqsave(&lp->reset_lock, flags);
+ if (xemaclite_send_data(lp, (u8 *) new_skb->data, len) != 0) {
+ /* If the Emaclite Tx buffer is busy, stop the Tx queue and
+ * defer the skb for transmission at a later point when the
+ * current transmission is complete */
+ netif_stop_queue(dev);
+ lp->deferred_skb = new_skb;
+ spin_unlock_irqrestore(&lp->reset_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&lp->reset_lock, flags);
+
+ dev->stats.tx_bytes += len;
+ dev_kfree_skb(new_skb);
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/**
+ * xemaclite_ioctl - Perform IO Control operations on the network device
+ * @dev: Pointer to the network device
+ * @rq: Pointer to the interface request structure
+ * @cmd: IOCTL command
+ *
+ * The only IOCTL operation supported by this function is setting the MAC
+ * address. An error is reported if any other operations are requested.
+ *
+ * Return: 0 to indicate success, or a negative error for failure.
+ */
+static int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct net_local *lp = (struct net_local *) netdev_priv(dev);
+ struct hw_addr_data *hw_addr = (struct hw_addr_data *) &rq->ifr_hwaddr;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ return -EIO;
+
+ case SIOCSIFHWADDR:
+ dev_err(&lp->ndev->dev, "SIOCSIFHWADDR\n");
+
+ /* Copy MAC address in from user space */
+ copy_from_user((void __force *) dev->dev_addr,
+ (void __user __force *) hw_addr,
+ IFHWADDRLEN);
+ xemaclite_set_mac_address(lp, dev->dev_addr);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * xemaclite_remove_ndev - Free the network device
+ * @ndev: Pointer to the network device to be freed
+ *
+ * This function un maps the IO region of the Emaclite device and frees the net
+ * device.
+ */
+static void xemaclite_remove_ndev(struct net_device *ndev)
+{
+ if (ndev) {
+ struct net_local *lp = (struct net_local *) netdev_priv(ndev);
+
+ if (lp->base_addr)
+ iounmap((void __iomem __force *) (lp->base_addr));
+ free_netdev(ndev);
+ }
+}
+
+/**
+ * get_bool - Get a parameter from the OF device
+ * @ofdev: Pointer to OF device structure
+ * @s: Property to be retrieved
+ *
+ * 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
+ */
+static bool get_bool(struct of_device *ofdev, const char *s)
+{
+ u32 *p = (u32 *)of_get_property(ofdev->node, s, NULL);
+
+ if (p) {
+ return (bool)*p;
+ } else {
+ dev_warn(&ofdev->dev, "Parameter %s not found,"
+ "defaulting to false\n", s);
+ return 0;
+ }
+}
+
+static struct net_device_ops xemaclite_netdev_ops;
+
+/**
+ * xemaclite_of_probe - Probe method for the Emaclite device.
+ * @ofdev: Pointer to OF device structure
+ * @match: Pointer to the structure used for matching a device
+ *
+ * This function probes for the Emaclite device in the device tree.
+ * It initializes the driver data structure and the hardware, sets the MAC
+ * address and registers the network device.
+ *
+ * Return: 0, if the driver is bound to the Emaclite device, or
+ * a negative error if there is failure.
+ */
+static int __devinit xemaclite_of_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct resource r_irq; /* Interrupt resources */
+ struct resource r_mem; /* IO mem resources */
+ struct net_device *ndev = NULL;
+ struct net_local *lp = NULL;
+ struct device *dev = &ofdev->dev;
+ const void *mac_address;
+
+ int rc = 0;
+
+ dev_info(dev, "Device Tree Probing\n");
+
+ /* Get iospace for the device */
+ rc = of_address_to_resource(ofdev->node, 0, &r_mem);
+ if (rc) {
+ dev_err(dev, "invalid address\n");
+ return rc;
+ }
+
+ /* Get IRQ for the device */
+ rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
+ if (rc == NO_IRQ) {
+ dev_err(dev, "no IRQ found\n");
+ return rc;
+ }
+
+ /* Create an ethernet device instance */
+ ndev = alloc_etherdev(sizeof(struct net_local));
+ if (!ndev) {
+ dev_err(dev, "Could not allocate network device\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(dev, ndev);
+
+ ndev->irq = r_irq.start;
+ ndev->mem_start = r_mem.start;
+ ndev->mem_end = r_mem.end;
+
+ lp = netdev_priv(ndev);
+ lp->ndev = ndev;
+
+ if (!request_mem_region(ndev->mem_start,
+ ndev->mem_end - ndev->mem_start + 1,
+ DRIVER_NAME)) {
+ dev_err(dev, "Couldn't lock memory region at %p\n",
+ (void *)ndev->mem_start);
+ rc = -EBUSY;
+ goto error2;
+ }
+
+ /* Get the virtual base address for the device */
+ lp->base_addr = ioremap(r_mem.start, r_mem.end - r_mem.start + 1);
+ if (NULL == lp->base_addr) {
+ dev_err(dev, "EmacLite: Could not allocate iomem\n");
+ rc = -EIO;
+ goto error1;
+ }
+
+ lp->next_tx_buf_to_use = 0x0;
+ lp->next_rx_buf_to_use = 0x0;
+ lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong");
+ lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong");
+ mac_address = of_get_mac_address(ofdev->node);
+
+ if (mac_address)
+ /* Set the MAC address. */
+ memcpy(ndev->dev_addr, mac_address, 6);
+ else
+ dev_warn(dev, "No MAC address found\n");
+
+ /* Clear the Tx CSR's in case this is a restart */
+ out_be32(lp->base_addr + XEL_TSR_OFFSET, 0);
+ out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0);
+
+ /* Set the MAC address in the EmacLite device */
+ xemaclite_set_mac_address(lp, ndev->dev_addr);
+
+ dev_info(dev,
+ "MAC address is now %2x:%2x:%2x:%2x:%2x:%2x\n",
+ ndev->dev_addr[0], ndev->dev_addr[1],
+ ndev->dev_addr[2], ndev->dev_addr[3],
+ ndev->dev_addr[4], ndev->dev_addr[5]);
+
+ ndev->netdev_ops = &xemaclite_netdev_ops;
+ ndev->flags &= ~IFF_MULTICAST;
+ ndev->watchdog_timeo = TX_TIMEOUT;
+
+ /* Finally, register the device */
+ rc = register_netdev(ndev);
+ if (rc) {
+ dev_err(dev,
+ "Cannot register network device, aborting\n");
+ goto error1;
+ }
+
+ dev_info(dev,
+ "Xilinx EmacLite at 0x%08X mapped to 0x%08X, irq=%d\n",
+ (unsigned int __force)ndev->mem_start,
+ (unsigned int __force)lp->base_addr, ndev->irq);
+ return 0;
+
+error1:
+ release_mem_region(ndev->mem_start, r_mem.end - r_mem.start + 1);
+
+error2:
+ xemaclite_remove_ndev(ndev);
+ return rc;
+}
+
+/**
+ * xemaclite_of_remove - Unbind the driver from the Emaclite device.
+ * @of_dev: Pointer to OF 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 any resources allocated to
+ * the device.
+ *
+ * Return: 0, always.
+ */
+static int __devexit xemaclite_of_remove(struct of_device *of_dev)
+{
+ struct device *dev = &of_dev->dev;
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ unregister_netdev(ndev);
+
+ release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1);
+
+ xemaclite_remove_ndev(ndev);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct net_device_ops xemaclite_netdev_ops = {
+ .ndo_open = xemaclite_open,
+ .ndo_stop = xemaclite_close,
+ .ndo_start_xmit = xemaclite_send,
+ .ndo_do_ioctl = xemaclite_ioctl,
+ .ndo_tx_timeout = xemaclite_tx_timeout,
+ .ndo_get_stats = xemaclite_get_stats,
+};
+
+/* Match table for OF platform binding */
+static struct of_device_id xemaclite_of_match[] __devinitdata = {
+ { .compatible = "xlnx,opb-ethernetlite-1.01.a", },
+ { .compatible = "xlnx,opb-ethernetlite-1.01.b", },
+ { .compatible = "xlnx,xps-ethernetlite-1.00.a", },
+ { .compatible = "xlnx,xps-ethernetlite-2.00.a", },
+ { .compatible = "xlnx,xps-ethernetlite-2.01.a", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xemaclite_of_match);
+
+static struct of_platform_driver xemaclite_of_driver = {
+ .name = DRIVER_NAME,
+ .match_table = xemaclite_of_match,
+ .probe = xemaclite_of_probe,
+ .remove = __devexit_p(xemaclite_of_remove),
+};
+
+/**
+ * xgpiopss_init - Initial driver registration call
+ *
+ * Return: 0 upon success, or a negative error upon failure.
+ */
+static int __init xemaclite_init(void)
+{
+ /* No kernel boot options used, we just need to register the driver */
+ return of_register_platform_driver(&xemaclite_of_driver);
+}
+
+/**
+ * xemaclite_cleanup - Driver un-registration call
+ */
+static void __exit xemaclite_cleanup(void)
+{
+ of_unregister_platform_driver(&xemaclite_of_driver);
+}
+
+module_init(xemaclite_init);
+module_exit(xemaclite_cleanup);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver");
+MODULE_LICENSE("GPL");
--
1.6.2.1
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
^ permalink raw reply related
* [PATCH 0/2]: cpuidle: Introducing cpuidle infrastructure to powerpc.
From: Arun R Bharadwaj @ 2009-08-19 12:57 UTC (permalink / raw)
To: Joel Schopp, Benjamin Herrenschmidt, Shaohua Li,
Venkatesh Pallipadi, Adam Belay, Peter Zijlstra, Ingo Molnar,
Vaidyanathan Srinivasan, Dipankar Sarma, Balbir Singh,
Gautham R Shenoy, Arun R Bharadwaj
Cc: linuxppc-dev, linux-kernel
Hi,
**** RFC not for inclusion ****
"Cpuidle" is a CPU Power Management infrastrusture which helps manage
idle CPUs in a clean and efficient manner. The architecture can register
its driver (in this case, tpmd_idle driver) so that it subscribes for
cpuidle feature. Cpuidle has a set of governors (ladder and menu),
which will decide the best idle state to be chosen for the current situation,
based on heuristics, and calculates the expected residency time
for the current idle state. So based on this, the cpu is put into
the right idle state.
Currently, cpuidle infrasture is exploited by ACPI to choose between
the available ACPI C-states. This patch-set is aimed at enabling
cpuidle for powerpc and provides a sample implementation for pseries.
Currently, in the pseries_dedicated_idle_sleep(), the processor would
poll for a time period, which is called the snooze, and only then it
is ceded, which would put the processor in nap state. Cpuidle aims at
separating this into 2 different idle states. Based on the expected
residency time predicted by the cpuidle governor, the idle state is
chosen directly. So, choosing to enter the nap state directly based on
the decision made by cpuidle would avoid unnecessary snoozing before
entering nap.
This patch-set tries to achieve the above objective by introducing a
Thermal and Power Management Device module called tpmd_idle in
arch/powerpc/platform/pseries/tpmd_idle.c, which implements cpuidle
idle loop which would replace the pseries_dedicated_idle_sleep()
when cpuidle is enabled.
Patches included in this set:
PATCH 1/2 - Enable cpuidle for pSeries.
PATCH 2/2 - Implement Thermal & Power Management Devices(TPMD) idle module
Any feedback on the overall design and idea is immensely valuable.
--arun
^ permalink raw reply
* [PATCH 1/2]: pSeries: Enable cpuidle for pSeries.
From: Arun R Bharadwaj @ 2009-08-19 12:58 UTC (permalink / raw)
To: Joel Schopp, Benjamin Herrenschmidt, Shaohua Li,
Venkatesh Pallipadi, Adam Belay, Peter Zijlstra, Ingo Molnar,
Vaidyanathan Srinivasan, Dipankar Sarma, Balbir Singh,
Gautham R Shenoy, Arun Bharadwaj
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20090819125716.GA20627@linux.vnet.ibm.com>
* Arun R Bharadwaj <arun@linux.vnet.ibm.com> [2009-08-19 18:27:16]:
This patch enables the cpuidle option in Kconfig for pSeries.
It also adds the routine cpu_idle_wait.
Signed-off-by: Arun R Bharadwaj <arun@linux.vnet.ibm.com>
---
arch/powerpc/Kconfig | 18 ++++++++++++++++++
arch/powerpc/include/asm/system.h | 2 ++
arch/powerpc/platforms/pseries/setup.c | 20 ++++++++++++++++++++
drivers/cpuidle/cpuidle.c | 1 +
4 files changed, 41 insertions(+)
Index: linux.trees.git/arch/powerpc/Kconfig
===================================================================
--- linux.trees.git.orig/arch/powerpc/Kconfig
+++ linux.trees.git/arch/powerpc/Kconfig
@@ -88,6 +88,9 @@ config ARCH_HAS_ILOG2_U64
bool
default y if 64BIT
+config ARCH_HAS_CPU_IDLE_WAIT
+ def_bool y
+
config GENERIC_HWEIGHT
bool
default y
@@ -243,6 +246,21 @@ source "kernel/Kconfig.freezer"
source "arch/powerpc/sysdev/Kconfig"
source "arch/powerpc/platforms/Kconfig"
+menu "Power management options"
+
+source "drivers/cpuidle/Kconfig"
+
+config TPMD
+ tristate "TPMD power management support"
+ depends on PPC_PSERIES && CPU_IDLE
+ default y
+ help
+ Thermal and Power Management Devices (TPMD). This hooks onto cpuidle
+ infrastructure to help in idle cpu power management. Currently this
+ is enabled only for pSeries.
+
+endmenu
+
menu "Kernel options"
config HIGHMEM
Index: linux.trees.git/drivers/cpuidle/cpuidle.c
===================================================================
--- linux.trees.git.orig/drivers/cpuidle/cpuidle.c
+++ linux.trees.git/drivers/cpuidle/cpuidle.c
@@ -17,6 +17,7 @@
#include <linux/cpuidle.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
+#include <linux/pm.h>
#include "cpuidle.h"
Index: linux.trees.git/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- linux.trees.git.orig/arch/powerpc/platforms/pseries/setup.c
+++ linux.trees.git/arch/powerpc/platforms/pseries/setup.c
@@ -278,6 +278,26 @@ static struct notifier_block pci_dn_reco
.notifier_call = pci_dn_reconfig_notifier,
};
+static void do_nothing(void *unused)
+{
+}
+
+/*
+ * cpu_idle_wait - Used to ensure that all the CPUs discard old value of
+ * pm_idle and update to new pm_idle value. Required while changing pm_idle
+ * handler on SMP systems.
+ *
+ * Caller must have changed pm_idle to the new value before the call. Old
+ * pm_idle value will not be used by any CPU after the return of this function.
+ */
+void cpu_idle_wait(void)
+{
+ smp_mb();
+ /* kick all the CPUs so that they exit out of pm_idle */
+ smp_call_function(do_nothing, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(cpu_idle_wait);
+
static void __init pSeries_setup_arch(void)
{
/* Discover PIC type and setup ppc_md accordingly */
Index: linux.trees.git/arch/powerpc/include/asm/system.h
===================================================================
--- linux.trees.git.orig/arch/powerpc/include/asm/system.h
+++ linux.trees.git/arch/powerpc/include/asm/system.h
@@ -546,5 +546,7 @@ extern void account_system_vtime(struct
extern struct dentry *powerpc_debugfs_root;
+void cpu_idle_wait(void);
+
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_SYSTEM_H */
^ permalink raw reply
* [PATCH 2/2]: pSeries: Implement Thermal & Power Management Devices(TPMD) idle module.
From: Arun R Bharadwaj @ 2009-08-19 12:59 UTC (permalink / raw)
To: Joel Schopp, Benjamin Herrenschmidt, Shaohua Li,
Venkatesh Pallipadi, Adam Belay, Peter Zijlstra, Ingo Molnar,
Vaidyanathan Srinivasan, Dipankar Sarma, Balbir Singh,
Gautham R Shenoy
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20090819125716.GA20627@linux.vnet.ibm.com>
* Arun R Bharadwaj <arun@linux.vnet.ibm.com> [2009-08-19 18:27:16]:
This patch creates the Thermal & Power Management Devices module, tpmd_idle
which implements the cpuidle infrasture for pseries.
It implements a tpmd_idle_loop() which would be the main idle loop called
from cpu_idle(). It makes decision of entering either snooze or nap state
based on the decision taken by the cpuidle governor.
Signed-off-by: Arun R Bharadwaj <arun@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/tpmd.h | 10 +
arch/powerpc/platforms/pseries/tpmd_idle.c | 192 +++++++++++++++++++++++++++++
3 files changed, 203 insertions(+)
Index: linux.trees.git/arch/powerpc/platforms/pseries/tpmd_idle.c
===================================================================
--- /dev/null
+++ linux.trees.git/arch/powerpc/platforms/pseries/tpmd_idle.c
@@ -0,0 +1,192 @@
+
+/*
+ * tpmd_idle - idle state submodule to the tpmd driver
+ *
+ * Copyright (C) 2009 Arun R Bharadwaj <arun@linux.vnet.ibm.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your 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, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+
+#include <asm/paca.h>
+#include <asm/machdep.h>
+
+#include "plpar_wrappers.h"
+#include "tpmd.h"
+
+MODULE_AUTHOR("Arun R Bharadwaj");
+MODULE_DESCRIPTION("TPMD Idle State Driver");
+MODULE_LICENSE("GPL");
+
+struct cpuidle_driver tpmd_idle_driver = {
+ .name = "tpmd_idle",
+ .owner = THIS_MODULE,
+};
+
+void (*pm_idle)(void);
+EXPORT_SYMBOL(pm_idle);
+
+static void (*old_idle_power_save)(void);
+
+DEFINE_PER_CPU(struct tpmd_processor_power, power);
+
+#define IDLE_STATE_COUNT 2
+
+static int tpmd_idle_init(struct tpmd_processor_power *power)
+{
+ return cpuidle_register_device(&power->dev);
+}
+
+void tpmd_idle_exit(struct tpmd_processor_power *power)
+{
+ cpuidle_unregister_device(&power->dev);
+}
+
+static void snooze(void)
+{
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+ while (!need_resched()) {
+ HMT_low();
+ HMT_very_low();
+ }
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ local_irq_disable();
+ smp_mb();
+}
+
+static void nap(void)
+{
+ HMT_medium();
+ smp_mb();
+ cede_processor();
+}
+
+static int tpmd_idle_loop(struct cpuidle_device *dev, struct cpuidle_state *st)
+{
+ ktime_t t1, t2;
+ s64 diff;
+ int ret;
+
+ get_lppaca()->idle = 1;
+ get_lppaca()->donate_dedicated_cpu = 1;
+
+ t1 = ktime_get();
+
+ if (strcmp(st->desc, "idle") == 0)
+ snooze();
+ else
+ nap();
+
+ t2 = ktime_get();
+ diff = ktime_to_us(ktime_sub(t2, t1));
+ if (diff > INT_MAX)
+ diff = INT_MAX;
+
+ ret = (int) diff;
+
+ get_lppaca()->idle = 0;
+ get_lppaca()->donate_dedicated_cpu = 0;
+
+ return ret;
+}
+
+static int tpmd_setup_cpuidle(struct tpmd_processor_power *power)
+{
+ int i;
+ struct cpuidle_state *state;
+ struct cpuidle_device *dev = &power->dev;
+
+ dev->cpu = power->id;
+
+ dev->enabled = 0;
+ for (i = 0; i < IDLE_STATE_COUNT; i++) {
+ state = &dev->states[i];
+
+ snprintf(state->name, CPUIDLE_NAME_LEN, "TPM%d", i);
+
+ switch (i) {
+ case 0:
+ strncpy(state->desc, "idle", CPUIDLE_DESC_LEN);
+ state->exit_latency = 0;
+ state->target_residency = 0;
+ state->enter = tpmd_idle_loop;
+ break;
+
+ case 1:
+ strncpy(state->desc, "nap", CPUIDLE_DESC_LEN);
+ state->exit_latency = 1;
+ state->target_residency = 100;
+ state->enter = tpmd_idle_loop;
+ break;
+ }
+ }
+
+ power->dev.state_count = i;
+ return 0;
+}
+
+static int tpmd_processor_get_power_info(struct tpmd_processor_power *power,
+ int cpu)
+{
+ power->id = cpu;
+ power->count = 2;
+ return 0;
+}
+
+static int __init tpmd_processor_init(void)
+{
+ int cpu;
+ int result = cpuidle_register_driver(&tpmd_idle_driver);
+
+ if (result < 0)
+ return result;
+
+ printk(KERN_DEBUG "TPMD idle driver registered\n");
+
+ for_each_online_cpu(cpu) {
+ tpmd_processor_get_power_info(&per_cpu(power, cpu), cpu);
+ tpmd_setup_cpuidle(&per_cpu(power, cpu));
+ tpmd_idle_init(&per_cpu(power, cpu));
+ }
+
+ printk(KERN_DEBUG "Using cpuidle idle loop\n");
+ old_idle_power_save = ppc_md.power_save;
+ ppc_md.power_save = pm_idle;
+ return 0;
+}
+
+static void __exit tpmd_processor_exit(void)
+{
+ int cpu;
+
+ ppc_md.power_save = old_idle_power_save;
+ for_each_online_cpu(cpu)
+ tpmd_idle_exit(&per_cpu(power, cpu));
+ cpuidle_unregister_driver(&tpmd_idle_driver);
+ printk(KERN_DEBUG "TPMD idle driver removed\n");
+}
+
+module_init(tpmd_processor_init);
+module_exit(tpmd_processor_exit);
Index: linux.trees.git/arch/powerpc/platforms/pseries/tpmd.h
===================================================================
--- /dev/null
+++ linux.trees.git/arch/powerpc/platforms/pseries/tpmd.h
@@ -0,0 +1,10 @@
+#include <linux/kernel.h>
+#include <linux/cpuidle.h>
+
+struct tpmd_processor_power {
+ struct cpuidle_device dev;
+ int count;
+ int id;
+};
+
+extern struct cpuidle_driver tpmd_idle_driver;
Index: linux.trees.git/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- linux.trees.git.orig/arch/powerpc/platforms/pseries/Makefile
+++ linux.trees.git/arch/powerpc/platforms/pseries/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst
obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_DTL) += dtl.o
+obj-$(CONFIG_TPMD) += tpmd_idle.o
^ permalink raw reply
* Linux booting problem on powerpc 440
From: Sumesh Kaana @ 2009-08-19 13:01 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 5237 bytes --]
Hi all,
I am trying to boot linux kernel (2.6.30) on a custom built board.I am using simple ppc platform and attached are my dts file and boot log..
I've 16Mb of RAM,UART and UIC with powerpc 440x5 processor.Kernel Image size is less than 1 mb.
i am not using any bootloaders such as U-boot
i have a small program on reset vector which will copy linux bin image from flash to 4mb (Link Address as per wrapper script), after that execution starts from link address.
the problem that i face is kernel crashes in different places while booting for different linux images, but always mentioned that
TASK = 'swapper'
Can anyone tell what would be the problem..?
Thanks,
Sumesh.
boot log is as below:---------------------zImage starting: loaded at 0x00400000 (sp: 0x004deeb0)Allocating 0x1dad84 bytes for kernel ...gunzipping (0x00000000 <- 0x0040c000:0x004dd3f1)...done 0x1c31cc bytes
Linux/PowerPC load: console=ttyS0 root=/dev/ramFinalizing device tree... flat tree at 0x4eb300Top of RAM: 0x1000000, Total RAM: 0x1000000
Zone PFN ranges: DMA 0x00000000 -> 0x00001000 Normal 0x00001000 -> 0x00001000Movable zone start PFN for each nodeearly_node_map[1] active PFN ranges 0: 0x00000000 -> 0x00001000MMU: Allocated 1088 bytes of context maps for 255 contextsBuilt 1 zonelists in Zone order, mobility grouping off. Total pages: 4064Kernel command line: console=ttyS0 root=/dev/ramNR_IRQS:512UIC0 (32 IRQ sources) at DCR 0x1c0BUG: recent printk recursion!Oops: Kernel access of bad area, sig: 11 [#1]PREEMPT PowerPC 44x PlatformModules linked in:NIP: c010c848 LR: c010c9f8 CTR: 00000000REGS: c01bfc10 TRAP: 0300 Not tainted (2.6.30)MSR: 00021000 <ME,CE> CR: 22004042 XER: 20000000DEAR: 00000000, ESR: 00800000TASK = c01a94b8[0] 'swapper' THREAD: c01be000GPR00: 0000005a c01bfcc0 c01a94b8 c01c5eb0 c016f3db 00000002 c01c5eb1 00000000 GPR08: 00000000 c016f3dc c016f3dc 00000000 42004048 00f08000 c015276c c0152850 GPR16: c015261c c01528dc c01bfe20 ffffffff c01b8628 c01c5e8c 00000004 00000000 GPR24: ffffffff c01be000 0000000a c01c628c c01bff68 00000000 c01c5eb2 c016f3db NIP [c010c848] vsnprintf+0x75c/0xeb0LR [c010c9f8] vsnprintf+0x90c/0xeb0Call Trace:[c01bfcc0] [c010c41c] vsnprintf+0x330/0xeb0 (unreliable)[c01bfeb0] [c010d100] vscnprintf+0x18/0x38[c01bfec0] [c002ff10] vprintk+0x8c/0x350[c01bff60] [c0030224] printk+0x50/0x60[c01bffa0] [c0193830] pidhash_init+0x5c/0xd4[c01bffc0] [c018c744] start_kernel+0x150/0x284[c01bfff0] [c0000200] skpinv+0x190/0x1ccInstruction dump:540a073e 5400e13e 7d3100ae 7d7150ae 99280000 99680001 39070001 40a2ffd4 2f860005 409effc0 38000000 57ab06b0 <98080000> 7fc3f378 7f64db78 38a1003c ---[ end trace 31fd0ba7d8756001 ]---Kernel panic - not syncing: Attempted to kill the idle task!Call Trace:[c01bfaf0] [c0005d5c] show_stack+0x4c/0x16c (unreliable)[c01bfb30] [c002f17c] panic+0xa0/0x168[c01bfb80] [c0032eb8] do_exit+0x61c/0x638[c01bfbc0] [c000b60c] kernel_bad_stack+0x0/0x4c[c01bfbf0] [c000f310] bad_page_fault+0x90/0xd8[c01bfc00] [c000e184] handle_page_fault+0x7c/0x80[c01bfcc0] [c010c41c] vsnprintf+0x330/0xeb0[c01bfeb0] [c010d100] vscnprintf+0x18/0x38[c01bfec0] [c002ff10] vprintk+0x8c/0x350[c01bff60] [c0030224] printk+0x50/0x60[c01bffa0] [c0193830] pidhash_init+0x5c/0xd4[c01bffc0] [c018c744] start_kernel+0x150/0x284[c01bfff0] [c0000200] skpinv+0x190/0x1ccRebooting in 180 seconds...
device tree file as bellow:
----------------------------
/dts-v1/;
/ { model = "XXX,xxxx"; compatible = "XXX,xxxx"; #address-cells = <1>; #size-cells = <1>; dcr-parent = <&SKYBEAM_PPC>; chosen { bootargs = "console=ttyS0 root=/dev/ram"; linux,stdout-path = "/plb/serial@02080000"; } ; aliases { serial0 = &STD_UART; } ; memory { device_type = "memory"; reg = < 0x0 0x01000000 >; } ; cpus { #address-cells = <1>; #size-cells = <0>; SKYBEAM_PPC: cpu@0 { device_type = "cpu"; #address-cells = <1>; #size-cells = <1>; reg = <0>; clock-frequency = <25000000>; compatible = "PowerPC,440", "ibm,ppc440"; d-cache-line-size = <0x20>; d-cache-size = <0x8000>; dcr-access-method = "native"; dcr-controller ; i-cache-line-size = <0x20>; i-cache-size = <0x8000>; model = "PowerPC,440"; timebase-frequency = <25000000>; } ; } ; UIC0: interrupt-controller0 { compatible = "ibm,uic-440ep","ibm,uic"; interrupt-controller; cell-index = <0>; dcr-reg = <0x1c0 0x009>; #address-cells = <0>; #size-cells = <0>; #interrupt-cells = <2>; }; PLB: plb { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges ; STD_UART: serial@02080000 { device_type = "serial"; compatible = "ns16550"; reg = <0x02080000 0x00000008>; virtual-reg = <0x02080000>; clock-frequency = <125000000>; current-speed = <9600>; interrupt-parent = <&UIC0>; interrupts = <0x5 0x4>; } ; } ;} ;
_________________________________________________________________
View photos of singles in your area Click Here
http://a.ninemsn.com.au/b.aspx?URL=http%3A%2F%2Fdating%2Eninemsn%2Ecom%2Eau%2Fsearch%2Fsearch%2Easpx%3Fexec%3Dgo%26tp%3Dq%26gc%3D2%26tr%3D1%26lage%3D18%26uage%3D55%26cl%3D14%26sl%3D0%26dist%3D50%26po%3D1%26do%3D2%26trackingid%3D1046138%26r2s%3D1&_t=773166090&_r=Hotmail_Endtext&_m=EXT
[-- Attachment #2: Type: text/html, Size: 12544 bytes --]
^ permalink raw reply
* Re: [PATCH v2] qe_lib: Set gpio data before changing the direction to output
From: Michael Barkowski @ 2009-08-19 13:30 UTC (permalink / raw)
To: avorontsov; +Cc: linuxppc-dev, Timur Tabi
In-Reply-To: <20090818225607.GA29960@oksana.dev.rtsoft.ru>
Anton Vorontsov wrote:
> On Tue, Aug 18, 2009 at 05:33:00PM -0500, Timur Tabi wrote:
>> Anton Vorontsov wrote:
>>> On Tue, Aug 18, 2009 at 05:20:44PM -0400, Michael Barkowski wrote:
>>>> This avoids having a short glitch if the desired initial value is not
>>>> the same as what was previously in the data register.
>>>>
>>>> Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>
>>> Acked-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>> I don't have the time to test this patch, so I abstain from acking. :-)
>> If Anton likes it, that's good enough for me.
>
> You made me doubt for a moment. :-) Thanks for the suspiciousness.
>
> What happens if a pin was previously configured as input? Does our
> write to the data register survive? For MPC8xxx GPIO controllers
> it does. And randomly taken QE spec says:
>
> A write to CPDAT is latched, and if the corresponding CPDIR
> bits have configured the port pin as an output, the latched
> value is driven onto the respective pin. However, if the
> corresponding CPDIR bits have configured the port pin as an
> input, the latched value is prevented from reaching the pin.
>
> I guess we're safe, but Michael, could you actually test it
> (if not already)?
>
I had tested it before with the pin initially configured as "disabled".
I have now also tested it with the pin initially configured as "input".
The value written to CPDAT seems to survive and is driven onto the pin
once CPDIR is changed to 1, just as noted in the spec.
Tested on 8360, by probing with a logic analyzer.
There are lots of users of this code. I understand if you'd like it to
stay open for testing by others.
--
Michael Barkowski
^ permalink raw reply
* Re: [PATCH v2] qe_lib: Set gpio data before changing the direction to output
From: Anton Vorontsov @ 2009-08-19 13:32 UTC (permalink / raw)
To: Michael Barkowski; +Cc: linuxppc-dev, Timur Tabi
In-Reply-To: <4A8BFE6C.9030604@ruggedcom.com>
On Wed, Aug 19, 2009 at 09:30:20AM -0400, Michael Barkowski wrote:
> Anton Vorontsov wrote:
> > On Tue, Aug 18, 2009 at 05:33:00PM -0500, Timur Tabi wrote:
> >> Anton Vorontsov wrote:
> >>> On Tue, Aug 18, 2009 at 05:20:44PM -0400, Michael Barkowski wrote:
> >>>> This avoids having a short glitch if the desired initial value is not
> >>>> the same as what was previously in the data register.
> >>>>
> >>>> Signed-off-by: Michael Barkowski <michaelbarkowski@ruggedcom.com>
> >>> Acked-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> >> I don't have the time to test this patch, so I abstain from acking. :-)
> >> If Anton likes it, that's good enough for me.
> >
> > You made me doubt for a moment. :-) Thanks for the suspiciousness.
> >
> > What happens if a pin was previously configured as input? Does our
> > write to the data register survive? For MPC8xxx GPIO controllers
> > it does. And randomly taken QE spec says:
> >
> > A write to CPDAT is latched, and if the corresponding CPDIR
> > bits have configured the port pin as an output, the latched
> > value is driven onto the respective pin. However, if the
> > corresponding CPDIR bits have configured the port pin as an
> > input, the latched value is prevented from reaching the pin.
> >
> > I guess we're safe, but Michael, could you actually test it
> > (if not already)?
> >
>
> I had tested it before with the pin initially configured as "disabled".
>
> I have now also tested it with the pin initially configured as "input".
>
> The value written to CPDAT seems to survive and is driven onto the pin
> once CPDIR is changed to 1, just as noted in the spec.
>
> Tested on 8360, by probing with a logic analyzer.
Great, thanks a lot! I think the patch is perfect.
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: powerpc-next rebased
From: Kumar Gala @ 2009-08-19 13:43 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list
In-Reply-To: <1250667232.4810.20.camel@pasglop>
On Aug 19, 2009, at 2:33 AM, Benjamin Herrenschmidt wrote:
> Allright it's done, let's hope I didn't screw up :-)
You screwed it up... :)
The commits you pulled in from me you seem to have changed the author
of the commits which is NOT cool at all. Adding signed-off-by is
good, changing the committer is ok, but changing the author of the
commit is not. Please fix this ASAP.
Here's an example (in your tree):
http://git.kernel.org/?p=linux/kernel/git/benh/powerpc.git;a=commitdiff;h=87f1e94c949c28fdaa079d5115a8010c9a40569d
vs the commit in my tree:
http://git.kernel.org/?p=linux/kernel/git/galak/powerpc.git;a=commit;h=d920991f7e02788ba2b468b3023e5bbbc7a32f30
- k
^ permalink raw reply
* Re: powerpc-next rebased
From: Kumar Gala @ 2009-08-19 13:54 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev list
In-Reply-To: <2E8B90A3-A3FD-4EC8-9D41-1BA9E8B261AB@kernel.crashing.org>
On Aug 19, 2009, at 8:43 AM, Kumar Gala wrote:
>
> On Aug 19, 2009, at 2:33 AM, Benjamin Herrenschmidt wrote:
>
>> Allright it's done, let's hope I didn't screw up :-)
>
> You screwed it up... :)
>
> The commits you pulled in from me you seem to have changed the
> author of the commits which is NOT cool at all. Adding signed-off-
> by is good, changing the committer is ok, but changing the author of
> the commit is not. Please fix this ASAP.
>
> Here's an example (in your tree):
>
> http://git.kernel.org/?p=linux/kernel/git/benh/powerpc.git;a=commitdiff;h=87f1e94c949c28fdaa079d5115a8010c9a40569d
>
> vs the commit in my tree:
>
> http://git.kernel.org/?p=linux/kernel/git/galak/powerpc.git;a=commit;h=d920991f7e02788ba2b468b3023e5bbbc7a32f30
I'll leave my tree alone to allow you to try and fix this. It looks
like the stuff you pulled in from Josh's tree also has similar foobar.
- k
^ permalink raw reply
* OF modalias
From: Wolfgang Grandegger @ 2009-08-19 14:15 UTC (permalink / raw)
To: linuxppc-dev
Hello,
I'm confused by the modalias'es created by OF devices, e.g.:
# cat /sys/devices/f0000000.soc5200/f0000f00.spi/modalias
of:NspiT<NULL>Cfsl,mpc5200b-spiCfsl,mpc5200-spi
First of all, the string "<NULL>" looks like an error.
I could then dynamically load a driver module for the SPI using:
# modprobe `cat sys/devices/f0000000.soc5200/f0000f00.spi/modalias`
This does make sense for the SPI device, but there are OF devices which
cannot really be used by a module driver, e.g.:
# cat /sys/devices/f0000000.soc5200/f0000500.interrupt-controller/modalias
of:Ninterrupt-controllerT<NULL>Cfsl,mpc5200b-picCfsl,mpc5200-pic
The same for localbus, cdm, etc. Does it make sense for these
to create a modalias file?
Likely I have missed something. Thanks for clarification.
Wolfgang.
^ permalink raw reply
* [PATCH v2] powerpc: Fix __flush_icache_range on 44x
From: Josh Boyer @ 2009-08-19 14:27 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
The ptrace POKETEXT interface allows a process to modify the text pages of
a child process being ptraced, usually to insert breakpoints via trap
instructions. The kernel eventually calls copy_to_user_page, which in turn
calls __flush_icache_range to invalidate the icache lines for the child
process.
However, this function does not work on 44x due to the icache being virtually
indexed. This was noticed by a breakpoint being triggered after it had been
cleared by ltrace on a 440EPx board. The convenient solution is to do a
flash invalidate of the icache in the __flush_icache_range function.
Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
---
I tested this on powerpc-next this morning using the same testcase as before.
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 15f28e0..da9c0c4 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -342,10 +342,17 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
addi r3,r3,L1_CACHE_BYTES
bdnz 1b
sync /* wait for dcbst's to get to ram */
+#ifndef CONFIG_44x
mtctr r4
2: icbi 0,r6
addi r6,r6,L1_CACHE_BYTES
bdnz 2b
+#else
+ /* Flash invalidate on 44x because we are passed kmapped addresses and
+ this doesn't work for userspace pages due to the virtually tagged
+ icache. Sigh. */
+ iccci 0, r0
+#endif
sync /* additional sync needed on g4 */
isync
blr
^ permalink raw reply related
* Re: powerpc-next rebased
From: Kumar Gala @ 2009-08-19 14:35 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list
In-Reply-To: <5CBF10F7-8927-47AD-B4F8-1F07754207A8@kernel.crashing.org>
On Aug 19, 2009, at 8:54 AM, Kumar Gala wrote:
>
> On Aug 19, 2009, at 8:43 AM, Kumar Gala wrote:
>
>>
>> On Aug 19, 2009, at 2:33 AM, Benjamin Herrenschmidt wrote:
>>
>>> Allright it's done, let's hope I didn't screw up :-)
>>
>> You screwed it up... :)
>>
>> The commits you pulled in from me you seem to have changed the
>> author of the commits which is NOT cool at all. Adding signed-off-
>> by is good, changing the committer is ok, but changing the author
>> of the commit is not. Please fix this ASAP.
>>
>> Here's an example (in your tree):
>>
>> http://git.kernel.org/?p=linux/kernel/git/benh/powerpc.git;a=commitdiff;h=87f1e94c949c28fdaa079d5115a8010c9a40569d
>>
>> vs the commit in my tree:
>>
>> http://git.kernel.org/?p=linux/kernel/git/galak/powerpc.git;a=commit;h=d920991f7e02788ba2b468b3023e5bbbc7a32f30
>
> I'll leave my tree alone to allow you to try and fix this. It looks
> like the stuff you pulled in from Josh's tree also has similar foobar.
>
> - k
To be nice I published a 'fix-author' branch on my kernel.org tree
that should be identical to your tree up to:
commit b045513a1d657178868a1b1c5c2c56b7ef67766d
Author: Kumar Gala <galak@kernel.crashing.org>
Date: Tue Aug 18 15:21:40 2009 +0000
powerpc/mm: Fix assert_pte_locked to work properly on uniprocessor
Since the pte_lockptr is a spinlock it gets optimized away on
uniprocessor builds so using spin_is_locked is not correct. We
can use
assert_spin_locked instead and get the proper behavior between UP
and
SMP builds.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
I doesn't have any Signed-off-by from you for the commits from Josh or
I. Otherwise the Author's have been fixed.
- k
^ permalink raw reply
* Re: Linux booting problem on powerpc 440
From: Josh Boyer @ 2009-08-19 17:35 UTC (permalink / raw)
To: Sumesh Kaana; +Cc: linuxppc-dev
In-Reply-To: <BLU124-W26979C48CA968E784AEA0CB4FE0@phx.gbl>
On Wed, Aug 19, 2009 at 01:01:16PM +0000, Sumesh Kaana wrote:
>
>
>
>Hi all,
>I am trying to boot linux kernel (2.6.30) on a custom built board.I am using simple ppc platform and attached are my dts file and boot log..
>I've 16Mb of RAM,UART and UIC with powerpc 440x5 processor.Kernel Image size is less than 1 mb.
>i am not using any bootloaders such as U-boot
>i have a small program on reset vector which will copy linux bin image from flash to 4mb (Link Address as per wrapper script), after that execution starts from link address.
How large of an initial TLB are you setting up for all of this in your simple
bootloader?
>the problem that i face is kernel crashes in different places while booting for different linux images, but always mentioned that
>TASK = 'swapper'
>
>Can anyone tell what would be the problem..?
I won't quote the file itself since your email is terribly word-wrapped, but
the #address-cells = <1> in the root node looks incorrect for a 440 based CPU.
It should be 2, as 440 does 36-bit addressing. Similarly for the PLB node.
You would need to fix the memory reg property for this.
The CPU node should probably have #size-cells = <0>, though I don't think that
matters much.
The serial port appears to be at an odd address for a 440 based SoC, but I
have no idea if that is correct or not.
Outside of those issues, I can't think of anything off the top of my head that
would cause the panic you are seeing.
josh
^ permalink raw reply
* Re: [PATCH] spinlock: __raw_spin_is_locked() should return true for UP
From: Scott Wood @ 2009-08-19 18:50 UTC (permalink / raw)
To: Linus Torvalds
Cc: peterz, linux-kernel, Steven Rostedt, linuxppc-dev, mingo, tglx
In-Reply-To: <alpine.LFD.2.01.0908181640120.3158@localhost.localdomain>
On Tue, Aug 18, 2009 at 04:52:20PM -0700, Linus Torvalds wrote:
>
>
> On Tue, 18 Aug 2009, Steven Rostedt wrote:
> >
> > > The thing is, some people may assert that a lock is held, but others could
> > > easily be looping until it's not held using something like
> > >
> > > while (spin_is_locked(lock))
> > > cpu_relax();
> >
> > Wouldn't something like that be really racey? And anyone doing such a
> > thing had better have that code within an #ifdef CONFIG_SMP.
>
> Sure, it's hopefully inside a #ifdef CONFIG_SMP.
>
> And no, it's not necessarily racy. Sure, it's race in itself if that's all
> you are doing, but I could imagine writing that kind of code if I knew
> some lock was likely held, and I wanted to avoid doing a "try_lock()"
> until it got released.
So you'd basically have the effect of a spin_lock(), except with the
bonus of breaking RT hacks that do something other than spin, and
preventing arch code from doing certain types of relaxation that are only
appropriate when waiting on a lock (such as mdors on powerpc, which
de-emphasizes until a reservation is broken).
Not exactly something we should encourage, IMHO.
-Scott
^ permalink raw reply
* Re: [PATCH/RFC] powerpc/mm: Cleanup handling of execute permission
From: Becky Bruce @ 2009-08-19 20:59 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list, Kumar Gala
In-Reply-To: <1250634800.4810.1.camel@pasglop>
On Aug 18, 2009, at 5:33 PM, Benjamin Herrenschmidt wrote:
> On Tue, 2009-08-18 at 16:56 -0400, Josh Boyer wrote:
>> On Fri, Aug 14, 2009 at 05:39:42PM -0500, Becky Bruce wrote:
>>> Ben,
>>>
>>> This breaks the boot on 8572. I don't know why yet (and I'm
>>> probably
>>> not going to figure it out before I go home, because, frankly,
>>> it's late
>>> on a Friday afternoon and I need a glass of wine or, perhaps, a
>>> beer).
>>>
>>> Kumar and I will poke into this more and let you know what we find
>>> out -
>>> in the meantime, if you have any brilliant flashes, pony up!
>>
>> I tested this on a 440EPx NFS rootfs boot too. It doesn't cause
>> init itself
>> to crap out with a SIGILL like Becky's board, but it does do weird
>> things
>> and cause a SIGILL elsewhere during my boot.
>>
>> Reverting this patch from your testing branch allows things to work
>> just fine.
>
> Becky found my thinko, I'll send a new patch later today.
Ben,
FYI, I pulled your updated test branch this morning, booted, and did a
full LTP run on 8572. The results are consistent with the baseline I
have, so it looks like the issue is properly fixed.
-Becky
^ permalink raw reply
* Re: [PATCH 1/5] powerpc/mm: Add MMU features for TLB reservation & Paired MAS registers
From: Kumar Gala @ 2009-08-19 21:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1250666756.4810.16.camel@pasglop>
On Aug 19, 2009, at 2:25 AM, Benjamin Herrenschmidt wrote:
> On Wed, 2009-08-19 at 00:08 -0500, Kumar Gala wrote:
>> Support for TLB reservation (or TLB Write Conditional) and Paired MAS
>> registers are optional for a processor implementation so we handle
>> them via MMU feature sections.
>>
>> We currently only used paired MAS registers to access the full RPN
>> + perm
>> bits that are kept in MAS7||MAS3. We assume that if an
>> implementation has
>> hardware page table at this time it also implements in TLB
>> reservations.
>
> You also need to be careful with this code:
>
> virt_page_table_tlb_miss_done:
>
> /* We have overriden MAS2:EPN but currently our primary TLB miss
> * handler will always restore it so that should not be an issue,
> * if we ever optimize the primary handler to not write MAS2 on
> * some cases, we'll have to restore MAS2:EPN here based on the
> * original fault's DEAR. If we do that we have to modify the
> * ITLB miss handler to also store SRR0 in the exception frame
> * as DEAR.
> *
> * However, one nasty thing we did is we cleared the reservation
> * (well, potentially we did). We do a trick here thus if we
> * are not a level 0 exception (we interrupted the TLB miss) we
> * offset the return address by -4 in order to replay the tlbsrx
> * instruction there
> */
> subf r10,r13,r12
> cmpldi cr0,r10,PACA_EXTLB+EX_TLB_SIZE
> bne- 1f
> ld r11,PACA_EXTLB+EX_TLB_SIZE+EX_TLB_SRR0(r13)
> addi r10,r11,-4
> std r10,PACA_EXTLB+EX_TLB_SIZE+EX_TLB_SRR0(r13)
>
> You may want to make the 3 last lines conditional on having tlbsrx.
The whole thing only ever gets called if we had tlbsrx. so is there
any utility in making a part of conditional on tlbsrx?
> Right now, in the no-tlbsrx. case, what happens is that it will go
> back
> to the previous instruction, an or, which hopefully should be harmless
> -but- this code is nasty enough you really don't want to take that
> sort of chances.
>
> Feel free to add a fat comment next to the ld in the tlbsrx case
> itself
> explaining why those two instructions must be kept together and any
> change here must be reflected in the second level handler.
>
> Cheers,
> Ben.
>
^ permalink raw reply
* Re: powerpc-next rebased
From: Benjamin Herrenschmidt @ 2009-08-19 21:56 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev list
In-Reply-To: <2E8B90A3-A3FD-4EC8-9D41-1BA9E8B261AB@kernel.crashing.org>
On Wed, 2009-08-19 at 08:43 -0500, Kumar Gala wrote:
>
> You screwed it up... :)
>
> The commits you pulled in from me you seem to have changed the
> author
> of the commits which is NOT cool at all. Adding signed-off-by is
> good, changing the committer is ok, but changing the author of the
> commit is not. Please fix this ASAP.
Strange. I just rebased and ammended them, I didn't think that would
change the author :-( I'll try to figure out what went wrong and will
fix it up.
Cheers,
Ben.
> Here's an example (in your tree):
>
> http://git.kernel.org/?p=linux/kernel/git/benh/powerpc.git;a=commitdiff;h=87f1e94c949c28fdaa079d5115a8010c9a40569d
>
> vs the commit in my tree:
>
> http://git.kernel.org/?p=linux/kernel/git/galak/powerpc.git;a=commit;h=d920991f7e02788ba2b468b3023e5bbbc7a32f30
^ permalink raw reply
* Re: [PATCH/RFC] powerpc/mm: Cleanup handling of execute permission
From: Benjamin Herrenschmidt @ 2009-08-19 22:17 UTC (permalink / raw)
To: Becky Bruce; +Cc: linuxppc-dev list, Kumar Gala
In-Reply-To: <16251558-DDF6-46C3-98C2-2FC7CD8A398B@kernel.crashing.org>
On Wed, 2009-08-19 at 15:59 -0500, Becky Bruce wrote:
> On Aug 18, 2009, at 5:33 PM, Benjamin Herrenschmidt wrote:
>
> >
> FYI, I pulled your updated test branch this morning, booted, and did a
> full LTP run on 8572. The results are consistent with the baseline I
> have, so it looks like the issue is properly fixed.
Thanks !
Cheers,
Ben.
^ permalink raw reply
* NAND ECC Error with wrong SMC ording bug
From: Feng Kan @ 2009-08-19 23:16 UTC (permalink / raw)
To: linuxppc-dev; +Cc: u-boot, linux-mtd
In-Reply-To: <1250569482.19007.23.camel@pasglop>
Hi All:
It seems that the ECC correction is broken on the Linux with the 4xx
NDFC driver.
It uses the SMC order when reading the ECC code. 2-1-3
static int ndfc_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
uint32_t ecc;
uint8_t *p = (uint8_t *)&ecc;
wmb();
ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
/* The NDFC uses Smart Media (SMC) bytes order */
ecc_code[0] = p[2];
ecc_code[1] = p[1];
ecc_code[2] = p[3];
return 0;
}
However, when in the correction function, the byte address order is
again reverses
causing incorrect byte location.
* performace it does not make any difference
*/
if (eccsize_mult == 1)
byte_addr = (addressbits[b0] << 4) +
addressbits[b1];
>>>> The above really should be byte_addr = (addressbits[b1] << 4) +
addressbits[b0];
else
byte_addr = (addressbits[b2 & 0x3] << 8) +
(addressbits[b1] << 4) +
addressbits[b0];
bit_addr = addressbits[b2 >> 2];
/* flip the bit */
buf[byte_addr] ^= (1 << bit_addr);
printk(KERN_INFO "Corrected b[0] 0x%x b[1]0x%x\n", b0, b1);
printk(KERN_INFO "cal ecc b[0] 0x%x b[1]0x%x\n",
calc_ecc[0] , calc_ecc[1]);
printk(KERN_INFO "read ecc b[0] 0x%x b[1]0x%x\n",
read_ecc[0] , read_ecc[1]);
return 1;
I see other boards using SMC as well, can someone comment on the change
I am proposing.
Should I change the correction algorithm or the calculate function? If
the later is preferred
it would mean the change must be pushed in both U-Boot and Linux.
Feng Kan
AMCC Software
^ permalink raw reply
* Re: [PATCH 1/5] powerpc/mm: Add MMU features for TLB reservation & Paired MAS registers
From: Benjamin Herrenschmidt @ 2009-08-20 0:43 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <AC207041-6DB9-476E-B171-C1F6E10D3FAB@kernel.crashing.org>
On Wed, 2009-08-19 at 16:37 -0500, Kumar Gala wrote:
> On Aug 19, 2009, at 2:25 AM, Benjamin Herrenschmidt wrote:
> The whole thing only ever gets called if we had tlbsrx. so is there
> any utility in making a part of conditional on tlbsrx?
I don't think so ... this is the second level TLB miss handler when
the first level takes a hit on the virtually linear page tables, I
has nothing to do with tlbsrx... however, it does offset the return
address back into the first level handler by -4 to account for
replaying the tlbsrx instruction which you probably don't want to do.
Ben.
^ permalink raw reply
* Re: [PATCH 3/3] agp/uninorth: Unify U3 and pre-U3 insert_memory and remove_memory hooks.
From: Benjamin Herrenschmidt @ 2009-08-20 0:47 UTC (permalink / raw)
To: Michel Dänzer; +Cc: Dave Airlie, linuxppc-dev
In-Reply-To: <1250151313.4992.202.camel@thor>
On Thu, 2009-08-13 at 10:15 +0200, Michel Dänzer wrote:
> On Thu, 2009-08-13 at 17:05 +1000, Benjamin Herrenschmidt wrote:
> > On Tue, 2009-08-04 at 23:51 +0200, Michel Dänzer wrote:
> > > From: Michel Dänzer <daenzer@vmware.com>
> > >
> > > Signed-off-by: Michel Dänzer <daenzer@vmware.com>
> > > ---
> >
> > Hi Michel !
> >
> > While your two previous patches apply just fine, this one doesn't,
> > the uninorth_insert_memory() function seems to be slightly different
> > upstream. Does this depend on some separate yet unapplied patches ?
>
> I previously sent the attached patches to Dave in the course of the
> radeon KMS issues thread. Not sure which of these he's picked up yet, if
> any.
I merged the first two patches in your series, we can sort out the 3rd
one in a second pass on the merge window.
Cheers,
Ben.
>
> > I'm putting 1/3 and 2/3 into my -test branch and they should hit my
> > -next branch in a couple of days.
> >
> > Or do you prefer us to merge that via Dave ?
> >
> > The thing is, stuff in -powerpc is much more likely to get some amount
> > of testing on actual ppc hardware than stuff in random other trees :-)
>
> I'm fine with either way.
>
>
^ permalink raw reply
* Regarding TSI108 ethernet DMA issue
From: Thirumalai Pachamuthu @ 2009-08-20 4:15 UTC (permalink / raw)
To: linuxppc-dev@ozlabs.org
[-- Attachment #1: Type: text/plain, Size: 780 bytes --]
Hi all,
I am trying to port linux 2.6.30 for my TSI108 based custom board
where i am getting the following kernel panic message. I found that it was
due to dma allocation function call particularly dma_alloc_coherent() of
tsi108_open function. When we see the implementation of dma_alloc_coherent.
It was bit changed from the previous linux versions it seems.
The implementation of dma_alloc_coherent was kept on the
arch/powerpc/include/asm/dma-mapping.h file. Earlier implementation is not
considering the first parameter what the tsi108 driver is passing as NULL.
But the current implementation is considering this parameter and because of
this the panic is coming what i believe.
So kindly let me know any patches for this problem or any fixes.
Regards,
T.
[-- Attachment #2: Type: text/html, Size: 870 bytes --]
^ permalink raw reply
* Regarding TSI108 ethernet DMA issue
From: Thirumalai Pachamuthu @ 2009-08-20 4:18 UTC (permalink / raw)
To: linuxppc-dev@ozlabs.org
In-Reply-To: <e1ca2e7b0908192115o2cfa427fof21512757854901a@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2147 bytes --]
Hi all,
I am trying to port linux 2.6.30 for my TSI108 based custom board
where i am getting the following kernel panic message. I found that it was
due to dma allocation function call particularly dma_alloc_coherent() of
tsi108_open function. When we see the implementation of dma_alloc_coherent.
It was bit changed from the previous linux versions it seems.
The implementation of dma_alloc_coherent was kept on the
arch/powerpc/include/asm/dma-mapping.h file. Earlier implementation is not
considering the first parameter what the tsi108 driver is passing as NULL.
But the current implementation is considering this parameter and because of
this the panic is coming what i believe.
So kindly let me know any patches for this problem or any fixes.
------------[ cut here ]------------
Kernel BUG at c019074c [verbose debug info unavailable]
Oops: Exception in kernel mode, sig: 5 [#1]
DPVME0447
NIP: c019074c LR: c019074c CTR: c014ea48
REGS: df82bd80 TRAP: 0700 Not tainted (2.6.30)
MSR: 00029032 <EE,ME,CE,IR,DR> CR: 24000022 XER: 20000000
TASK = df82c000[1] 'swapper' THREAD: df82a000
GPR00: c019074c df82be30 df82c000 00000030 000013e0 ffffffff c014e998
00000035
GPR08: c02f2af0 c02e7bb8 000013e0 c02cd8f4 24000042 00000000 00000002
00000000
GPR16: c02c0000 00000000 00000000 00000000 00000000 0ffaa41c c0300000
00000004
GPR24: 00000000 00000000 c02f0000 c02f0000 df821030 00000000 df821000
df821300
NIP [c019074c] tsi108_open+0x5c/0x90
LR [c019074c] tsi108_open+0x5c/0x90
Call Trace:
[df82be30] [c019074c] tsi108_open+0x5c/0x90 (unreliable)
[df82be50] [c01b1564] dev_open+0xac/0x11c
[df82be70] [c01b1464] dev_change_flags+0x160/0x1b4
[df82be90] [c02bc5f8] ip_auto_config+0x18c/0xe5c
[df82bf50] [c0003c8c] do_one_initcall+0x34/0x1a8
[df82bfd0] [c02a1848] kernel_init+0x9c/0x100
[df82bff0] [c0012064] kernel_thread+0x4c/0x68
Instruction dump:
807f003c 7fc8f378 4beb8d71 7c7d1b79 40820028 80bf003c 3c60c029 7fc6f378
386327c0 90be0028 809f0040 4be90669 <0fe00000> 48000000 80bf003c 3c60c029
---[ end trace dc633a1da0ecdafb ]---
Kernel panic - not syncing: Attempted to kill init!
Rebooting in 180 seconds..
Regards,
T.
[-- Attachment #2: Type: text/html, Size: 2429 bytes --]
^ permalink raw reply
* Re: NAND ECC Error with wrong SMC ording bug
From: Sean MacLennan @ 2009-08-20 4:38 UTC (permalink / raw)
To: Feng Kan; +Cc: linuxppc-dev, linux-mtd, u-boot
In-Reply-To: <4A8C87E6.6070702@amcc.com>
On Wed, 19 Aug 2009 16:16:54 -0700
Feng Kan <fkan@amcc.com> wrote:
> I see other boards using SMC as well, can someone comment on the
> change I am proposing.
> Should I change the correction algorithm or the calculate function?
> If the later is preferred
> it would mean the change must be pushed in both U-Boot and Linux.
Odds are the calculate function is wrong. The correction algo is used
by many nand drivers, I *assume* it is correct. The calculate function
was set to agree with u-boot (1.3.0).
Cheers,
Sean
P.S. Yes, I know the u-boot is an ancient version :(
^ permalink raw reply
* powerpc-next rebased (again)
From: Benjamin Herrenschmidt @ 2009-08-20 4:40 UTC (permalink / raw)
To: linuxppc-dev list; +Cc: linuxppc-dev list, Kumar Gala
In-Reply-To: <4177854C-965A-4A26-A517-B6F0B48703BD@kernel.crashing.org>
Ok so due to screwup mentioned earlier, it's rebased agian :-(
Hopefully that's the last time for very looooooong...
While at it, I removed the lmb kmemleak patch from Michael since
we decided it wasn't a good idea and added Kumar's fix for
assert_pte_locked().
I also updated the test branch with more stuff such as Fujita
IOMMU bits and pieces.
Cheers,
Ben.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox