From f0a34615add3912bf8f5354c02998aa506f4d15b Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 Nov 2013 11:05:52 +0400 Subject: xtensa: clean up include/asm/vectors.h - drop unused PHYSICAL_MEMORY_ADDRESS and XC_PADDR - fix NMI_VECTOR_VADDR and INTLEVEL7_VECTOR_VADDR definitions: there should be no XCHAL_ prefix in them; - fix the following warning seen with gcc-4.8.1: arch/xtensa/include/asm/vectors.h:71:5: warning: "XCHAL_HAVE_VECBASE" is not defined [-Wundef] Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index c52b656..37e073b 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -30,11 +30,9 @@ #if defined(XCHAL_HAVE_PTP_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY /* MMU v3 - XCHAL_HAVE_PTP_MMU == 1 */ - #define PHYSICAL_MEMORY_ADDRESS 0x00000000 #define LOAD_MEMORY_ADDRESS 0x00003000 #else /* MMU V2 - XCHAL_HAVE_PTP_MMU == 0 */ - #define PHYSICAL_MEMORY_ADDRESS 0xD0000000 #define LOAD_MEMORY_ADDRESS 0xD0003000 #endif @@ -46,7 +44,6 @@ /* Location of the start of the kernel text, _start */ #define KERNELOFFSET 0x00003000 - #define PHYSICAL_MEMORY_ADDRESS 0x00000000 /* Loaded just above possibly live vectors */ #define LOAD_MEMORY_ADDRESS 0x00003000 @@ -54,7 +51,6 @@ #endif /* CONFIG_MMU */ #define XC_VADDR(offset) (VIRTUAL_MEMORY_ADDRESS + offset) -#define XC_PADDR(offset) (PHYSICAL_MEMORY_ADDRESS + offset) /* Used to set VECBASE register */ #define VECBASE_RESET_VADDR VIRTUAL_MEMORY_ADDRESS @@ -67,7 +63,7 @@ VECBASE_RESET_VADDR) #define RESET_VECTOR1_VADDR XC_VADDR(RESET_VECTOR1_VECOFS) -#if XCHAL_HAVE_VECBASE +#if defined(XCHAL_HAVE_VECBASE) && XCHAL_HAVE_VECBASE #define USER_VECTOR_VADDR XC_VADDR(XCHAL_USER_VECOFS) #define KERNEL_VECTOR_VADDR XC_VADDR(XCHAL_KERNEL_VECOFS) @@ -81,11 +77,9 @@ #define DEBUG_VECTOR_VADDR XC_VADDR(XCHAL_DEBUG_VECOFS) -#undef XCHAL_NMI_VECTOR_VADDR -#define XCHAL_NMI_VECTOR_VADDR XC_VADDR(XCHAL_NMI_VECOFS) +#define NMI_VECTOR_VADDR XC_VADDR(XCHAL_NMI_VECOFS) -#undef XCHAL_INTLEVEL7_VECTOR_VADDR -#define XCHAL_INTLEVEL7_VECTOR_VADDR XC_VADDR(XCHAL_INTLEVEL7_VECOFS) +#define INTLEVEL7_VECTOR_VADDR XC_VADDR(XCHAL_INTLEVEL7_VECOFS) /* * These XCHAL_* #defines from varian/core.h -- cgit v0.10.2 From ea1d3ed3cfde34914a517f97320b80d9ccee7507 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 Nov 2013 11:05:53 +0400 Subject: xtensa: remove trailing colons in asm statement Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 73cc3f4..736b9d2 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -18,7 +18,7 @@ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ - : "=r"(a0), "=r"(a1) : : ); \ + : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); }) #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -- cgit v0.10.2 From 3f3cd60bbd376e52ef91c92d0dba95b8942ebfcc Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:44 +0400 Subject: xtensa: ISS: clean up iss-network driver No functional changes, remove dead/unused code, clean checkpatch warnings, replace strlen of constant strings with sizeof. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index e9e1aad..6d9cc45 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -57,7 +57,6 @@ static LIST_HEAD(devices); struct tuntap_info { char dev_name[IFNAMSIZ]; int fixed_config; - unsigned char gw[ETH_ALEN]; int fd; }; @@ -67,7 +66,6 @@ struct tuntap_info { /* This structure contains out private information for the driver. */ struct iss_net_private { - struct list_head device_list; struct list_head opened_list; @@ -118,7 +116,7 @@ static char *split_if_spec(char *str, ...) *arg = str; if (end == NULL) return NULL; - *end ++ = '\0'; + *end++ = '\0'; str = end; } va_end(ap); @@ -126,25 +124,6 @@ static char *split_if_spec(char *str, ...) } -#if 0 -/* Adjust SKB. */ - -struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) -{ - if ((skb != NULL) && (skb_tailroom(skb) < extra)) { - struct sk_buff *skb2; - - skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); - dev_kfree_skb(skb); - skb = skb2; - } - if (skb != NULL) - skb_put(skb, extra); - - return skb; -} -#endif - /* Return the IP address as a string for a given device. */ static void dev_ip_addr(void *d, char *buf, char *bin_buf) @@ -154,11 +133,13 @@ static void dev_ip_addr(void *d, char *buf, char *bin_buf) struct in_ifaddr *in; __be32 addr; - if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) { - printk(KERN_WARNING "Device not assigned an IP address!\n"); + if (ip == NULL || ip->ifa_list == NULL) { + pr_warn("Device not assigned an IP address!\n"); return; } + in = ip->ifa_list; + addr = in->ifa_address; sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, addr >> 24); @@ -173,7 +154,7 @@ static void dev_ip_addr(void *d, char *buf, char *bin_buf) /* Set Ethernet address of the specified device. */ -static void inline set_ether_mac(void *d, unsigned char *addr) +static inline void set_ether_mac(void *d, unsigned char *addr) { struct net_device *dev = d; memcpy(dev->dev_addr, addr, ETH_ALEN); @@ -194,19 +175,21 @@ static int tuntap_open(struct iss_net_private *lp) if (!lp->tp.info.tuntap.fixed_config) return -EINVAL; - if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */ - printk("Failed to open /dev/net/tun, returned %d " - "(errno = %d)\n", fd, errno); + fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */ + if (fd < 0) { + pr_err("Failed to open /dev/net/tun, returned %d (errno = %d)\n", + fd, errno); return fd; } - memset(&ifr, 0, sizeof ifr); + memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name); + strlcpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name)); - if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) { - printk("Failed to set interface, returned %d " - "(errno = %d)\n", err, errno); + err = simc_ioctl(fd, TUNSETIFF, &ifr); + if (err < 0) { + pr_err("Failed to set interface, returned %d (errno = %d)\n", + err, errno); simc_close(fd); return err; } @@ -217,27 +200,17 @@ static int tuntap_open(struct iss_net_private *lp) static void tuntap_close(struct iss_net_private *lp) { -#if 0 - if (lp->tp.info.tuntap.fixed_config) - iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name); -#endif simc_close(lp->tp.info.tuntap.fd); lp->tp.info.tuntap.fd = -1; } -static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb) +static int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb) { -#if 0 - *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); - if (*skb == NULL) - return -ENOMEM; -#endif - return simc_read(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER); } -static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb) +static int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb) { return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); } @@ -259,40 +232,36 @@ static int tuntap_poll(struct iss_net_private *lp) static int tuntap_probe(struct iss_net_private *lp, int index, char *init) { - const int len = strlen(TRANSPORT_TUNTAP_NAME); char *dev_name = NULL, *mac_str = NULL, *rem = NULL; /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ - if (strncmp(init, TRANSPORT_TUNTAP_NAME, len)) + if (strncmp(init, TRANSPORT_TUNTAP_NAME, + sizeof(TRANSPORT_TUNTAP_NAME) - 1)) return 0; - if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') { - if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) { - printk("Extra garbage on specification : '%s'\n", rem); + init += sizeof(TRANSPORT_TUNTAP_NAME) - 1; + if (*init == ',') { + rem = split_if_spec(init + 1, &mac_str, &dev_name); + if (rem != NULL) { + pr_err("Extra garbage on specification : '%s'\n", rem); return 0; } } else if (*init != '\0') { - printk("Invalid argument: %s. Skipping device!\n", init); + pr_err("Invalid argument: %s. Skipping device!\n", init); return 0; } if (dev_name) { - strncpy(lp->tp.info.tuntap.dev_name, dev_name, - sizeof lp->tp.info.tuntap.dev_name); + strlcpy(lp->tp.info.tuntap.dev_name, dev_name, + sizeof(lp->tp.info.tuntap.dev_name)); lp->tp.info.tuntap.fixed_config = 1; } else strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); -#if 0 - if (setup_etheraddr(mac_str, lp->mac)) - lp->have_mac = 1; -#endif lp->mtu = TRANSPORT_TUNTAP_MTU; - //lp->info.tuntap.gate_addr = gate_addr; - lp->tp.info.tuntap.fd = -1; lp->tp.open = tuntap_open; @@ -302,12 +271,7 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) lp->tp.protocol = tuntap_protocol; lp->tp.poll = tuntap_poll; - printk("TUN/TAP backend - "); -#if 0 - if (lp->host.gate_addr != NULL) - printk("IP = %s", lp->host.gate_addr); -#endif - printk("\n"); + pr_info("TUN/TAP backend -\n"); return 1; } @@ -327,7 +291,8 @@ static int iss_net_rx(struct net_device *dev) /* Try to allocate memory, if it fails, try again next round. */ - if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) { + skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER); + if (skb == NULL) { lp->stats.rx_dropped++; return 0; } @@ -347,7 +312,6 @@ static int iss_net_rx(struct net_device *dev) lp->stats.rx_bytes += skb->len; lp->stats.rx_packets++; - // netif_rx(skb); netif_rx_ni(skb); return pkt_len; } @@ -378,11 +342,11 @@ static int iss_net_poll(void) spin_unlock(&lp->lock); if (err < 0) { - printk(KERN_ERR "Device '%s' read returned %d, " - "shutting it down\n", lp->dev->name, err); + pr_err("Device '%s' read returned %d, shutting it down\n", + lp->dev->name, err); dev_close(lp->dev); } else { - // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); + /* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */ } } @@ -393,14 +357,11 @@ static int iss_net_poll(void) static void iss_net_timer(unsigned long priv) { - struct iss_net_private* lp = (struct iss_net_private*) priv; + struct iss_net_private *lp = (struct iss_net_private *)priv; spin_lock(&lp->lock); - iss_net_poll(); - mod_timer(&lp->timer, jiffies + lp->timer_val); - spin_unlock(&lp->lock); } @@ -408,12 +369,13 @@ static void iss_net_timer(unsigned long priv) static int iss_net_open(struct net_device *dev) { struct iss_net_private *lp = netdev_priv(dev); - char addr[sizeof "255.255.255.255\0"]; + char addr[sizeof("255.255.255.255\0")]; int err; spin_lock(&lp->lock); - if ((err = lp->tp.open(lp)) < 0) + err = lp->tp.open(lp); + if (err < 0) goto out; if (!lp->have_mac) { @@ -448,7 +410,6 @@ out: static int iss_net_close(struct net_device *dev) { struct iss_net_private *lp = netdev_priv(dev); -printk("iss_net_close!\n"); netif_stop_queue(dev); spin_lock(&lp->lock); @@ -490,7 +451,7 @@ static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) } else { netif_start_queue(dev); - printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len); + pr_err("iss_net_start_xmit: failed(%d)\n", len); } spin_unlock_irqrestore(&lp->lock, flags); @@ -508,22 +469,10 @@ static struct net_device_stats *iss_net_get_stats(struct net_device *dev) static void iss_net_set_multicast_list(struct net_device *dev) { -#if 0 - if (dev->flags & IFF_PROMISC) - return; - else if (!netdev_mc_empty(dev)) - dev->flags |= IFF_ALLMULTI; - else - dev->flags &= ~IFF_ALLMULTI; -#endif } static void iss_net_tx_timeout(struct net_device *dev) { -#if 0 - dev->trans_start = jiffies; - netif_wake_queue(dev); -#endif } static int iss_net_set_mac(struct net_device *dev, void *addr) @@ -542,22 +491,6 @@ static int iss_net_set_mac(struct net_device *dev, void *addr) static int iss_net_change_mtu(struct net_device *dev, int new_mtu) { -#if 0 - struct iss_net_private *lp = netdev_priv(dev); - int err = 0; - - spin_lock(&lp->lock); - - // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user); - - if (new_mtu < 0) - err = new_mtu; - else - dev->mtu = new_mtu; - - spin_unlock(&lp->lock); - return err; -#endif return -EINVAL; } @@ -582,7 +515,6 @@ static const struct net_device_ops iss_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = iss_net_change_mtu, .ndo_set_mac_address = iss_net_set_mac, - //.ndo_do_ioctl = iss_net_ioctl, .ndo_tx_timeout = iss_net_tx_timeout, .ndo_set_rx_mode = iss_net_set_multicast_list, }; @@ -593,24 +525,24 @@ static int iss_net_configure(int index, char *init) struct iss_net_private *lp; int err; - if ((dev = alloc_etherdev(sizeof *lp)) == NULL) { - printk(KERN_ERR "eth_configure: failed to allocate device\n"); + dev = alloc_etherdev(sizeof(*lp)); + if (dev == NULL) { + pr_err("eth_configure: failed to allocate device\n"); return 1; } /* Initialize private element. */ lp = netdev_priv(dev); - *lp = ((struct iss_net_private) { + *lp = (struct iss_net_private) { .device_list = LIST_HEAD_INIT(lp->device_list), .opened_list = LIST_HEAD_INIT(lp->opened_list), .lock = __SPIN_LOCK_UNLOCKED(lp.lock), .dev = dev, .index = index, - //.fd = -1, .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }, .have_mac = 0, - }); + }; /* * Try all transport protocols. @@ -618,7 +550,7 @@ static int iss_net_configure(int index, char *init) */ if (!tuntap_probe(lp, index, init)) { - printk("Invalid arguments. Skipping device!\n"); + pr_err("Invalid arguments. Skipping device!\n"); goto errout; } @@ -641,14 +573,14 @@ static int iss_net_configure(int index, char *init) lp->pdev.id = index; lp->pdev.name = DRIVER_NAME; platform_device_register(&lp->pdev); - SET_NETDEV_DEV(dev,&lp->pdev.dev); + SET_NETDEV_DEV(dev, &lp->pdev.dev); /* * If this name ends up conflicting with an existing registered * netdevice, that is OK, register_netdev{,ice}() will notice this * and fail. */ - snprintf(dev->name, sizeof dev->name, "eth%d", index); + snprintf(dev->name, sizeof(dev->name), "eth%d", index); dev->netdev_ops = &iss_netdev_ops; dev->mtu = lp->mtu; @@ -660,7 +592,7 @@ static int iss_net_configure(int index, char *init) rtnl_unlock(); if (err) { - printk("Error registering net device!\n"); + pr_err("Error registering net device!\n"); /* XXX: should we call ->remove() here? */ free_netdev(dev); return 1; @@ -669,16 +601,11 @@ static int iss_net_configure(int index, char *init) init_timer(&lp->tl); lp->tl.function = iss_net_user_timer_expire; -#if 0 - if (lp->have_mac) - set_ether_mac(dev, lp->mac); -#endif return 0; errout: - // FIXME: unregister; free, etc.. + /* FIXME: unregister; free, etc.. */ return -EIO; - } /* ------------------------------------------------------------------------- */ @@ -717,7 +644,8 @@ static int __init iss_net_setup(char *str) printk(ERR "Device %d is negative\n", n); return 1; } - if (*(str = end) != '=') { + str = end; + if (*str != '=') { printk(ERR "Expected '=' after device number\n"); return 1; } @@ -739,7 +667,7 @@ static int __init iss_net_setup(char *str) new = alloc_bootmem(sizeof(*new)); if (new == NULL) { - printk("Alloc_bootmem failed\n"); + printk(ERR "Alloc_bootmem failed\n"); return 1; } -- cgit v0.10.2 From 8c8ad85f1f3ee1c2096a6b7b0892cabe4a956e31 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:45 +0400 Subject: xtensa: ISS: fix command line parameter name Now 'ethX=...' syntax can actually be used in kernel command line to specify network interfaces. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 6d9cc45..0c7c1d4 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -681,7 +681,7 @@ static int __init iss_net_setup(char *str) #undef ERR -__setup("eth=", iss_net_setup); +__setup("eth", iss_net_setup); /* * Initialize all ISS Ethernet devices previously registered in iss_net_setup. -- cgit v0.10.2 From 358b181003dbb30985331a77e753e1b05c05d87d Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:46 +0400 Subject: xtensa: ISS: init network interface name before the probe Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 0c7c1d4..3718cff 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -545,6 +545,13 @@ static int iss_net_configure(int index, char *init) }; /* + * If this name ends up conflicting with an existing registered + * netdevice, that is OK, register_netdev{,ice}() will notice this + * and fail. + */ + snprintf(dev->name, sizeof(dev->name), "eth%d", index); + + /* * Try all transport protocols. * Note: more protocols can be added by adding '&& !X_init(lp, eth)'. */ @@ -575,13 +582,6 @@ static int iss_net_configure(int index, char *init) platform_device_register(&lp->pdev); SET_NETDEV_DEV(dev, &lp->pdev.dev); - /* - * If this name ends up conflicting with an existing registered - * netdevice, that is OK, register_netdev{,ice}() will notice this - * and fail. - */ - snprintf(dev->name, sizeof(dev->name), "eth%d", index); - dev->netdev_ops = &iss_netdev_ops; dev->mtu = lp->mtu; dev->watchdog_timeo = (HZ >> 1); -- cgit v0.10.2 From 8991fd88354f924cc3f4e6386c619fc39fb48f21 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 08:48:34 +0400 Subject: xtensa: ISS: drop IP setup, clean up MAC setup Without proper MAC setup network device doesn't work. IP setup is redundant and may be done with 'ip=...' kernel parameter. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 3718cff..f7709c7 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -81,9 +81,6 @@ struct iss_net_private { int index; int mtu; - unsigned char mac[ETH_ALEN]; - int have_mac; - struct { union { struct tuntap_info tuntap; @@ -123,44 +120,41 @@ static char *split_if_spec(char *str, ...) return str; } +/* Set Ethernet address of the specified device. */ -/* Return the IP address as a string for a given device. */ - -static void dev_ip_addr(void *d, char *buf, char *bin_buf) +static void setup_etheraddr(struct net_device *dev, char *str) { - struct net_device *dev = d; - struct in_device *ip = dev->ip_ptr; - struct in_ifaddr *in; - __be32 addr; - - if (ip == NULL || ip->ifa_list == NULL) { - pr_warn("Device not assigned an IP address!\n"); - return; - } + unsigned char *addr = dev->dev_addr; - in = ip->ifa_list; + if (str == NULL) + goto random; - addr = in->ifa_address; - sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, - (addr >> 16) & 0xff, addr >> 24); - - if (bin_buf) { - bin_buf[0] = addr & 0xff; - bin_buf[1] = (addr >> 8) & 0xff; - bin_buf[2] = (addr >> 16) & 0xff; - bin_buf[3] = addr >> 24; + if (!mac_pton(str, addr)) { + pr_err("%s: failed to parse '%s' as an ethernet address\n", + dev->name, str); + goto random; } + if (is_multicast_ether_addr(addr)) { + pr_err("%s: attempt to assign a multicast ethernet address\n", + dev->name); + goto random; + } + if (!is_valid_ether_addr(addr)) { + pr_err("%s: attempt to assign an invalid ethernet address\n", + dev->name); + goto random; + } + if (!is_local_ether_addr(addr)) + pr_warn("%s: assigning a globally valid ethernet address\n", + dev->name); + return; + +random: + pr_info("%s: choosing a random ethernet address\n", + dev->name); + eth_hw_addr_random(dev); } -/* Set Ethernet address of the specified device. */ - -static inline void set_ether_mac(void *d, unsigned char *addr) -{ - struct net_device *dev = d; - memcpy(dev->dev_addr, addr, ETH_ALEN); -} - - /* ======================= TUNTAP TRANSPORT INTERFACE ====================== */ static int tuntap_open(struct iss_net_private *lp) @@ -232,6 +226,7 @@ static int tuntap_poll(struct iss_net_private *lp) static int tuntap_probe(struct iss_net_private *lp, int index, char *init) { + struct net_device *dev = lp->dev; char *dev_name = NULL, *mac_str = NULL, *rem = NULL; /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ @@ -259,6 +254,7 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) } else strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); + setup_etheraddr(dev, mac_str); lp->mtu = TRANSPORT_TUNTAP_MTU; @@ -369,7 +365,6 @@ static void iss_net_timer(unsigned long priv) static int iss_net_open(struct net_device *dev) { struct iss_net_private *lp = netdev_priv(dev); - char addr[sizeof("255.255.255.255\0")]; int err; spin_lock(&lp->lock); @@ -378,11 +373,6 @@ static int iss_net_open(struct net_device *dev) if (err < 0) goto out; - if (!lp->have_mac) { - dev_ip_addr(dev, addr, &lp->mac[2]); - set_ether_mac(dev, lp->mac); - } - netif_start_queue(dev); /* clear buffer - it can happen that the host side of the interface @@ -540,8 +530,6 @@ static int iss_net_configure(int index, char *init) .lock = __SPIN_LOCK_UNLOCKED(lp.lock), .dev = dev, .index = index, - .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }, - .have_mac = 0, }; /* @@ -561,10 +549,7 @@ static int iss_net_configure(int index, char *init) goto errout; } - printk(KERN_INFO "Netdevice %d ", index); - if (lp->have_mac) - printk("(%pM) ", lp->mac); - printk(": "); + pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr); /* sysfs register */ -- cgit v0.10.2 From f6ac5a177db24379e7a7a7d44b1ae7d1036042ec Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:48 +0400 Subject: xtensa: ISS: enable iss_net_set_mac This allows changing MAC address of the device. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index f7709c7..b33ed0c 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -467,15 +467,14 @@ static void iss_net_tx_timeout(struct net_device *dev) static int iss_net_set_mac(struct net_device *dev, void *addr) { -#if 0 struct iss_net_private *lp = netdev_priv(dev); struct sockaddr *hwaddr = addr; + if (!is_valid_ether_addr(hwaddr->sa_data)) + return -EADDRNOTAVAIL; spin_lock(&lp->lock); memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); spin_unlock(&lp->lock); -#endif - return 0; } -- cgit v0.10.2 From 35e14b443aa906ffb4ff8bd884e393c1bbdb6670 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:49 +0400 Subject: xtensa: ISS: always use fixed tuntap config The code doesn't handle dynamic TAP interface allocation, so there's no point tracking this case. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index b33ed0c..668ed62 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -56,7 +56,6 @@ static LIST_HEAD(devices); struct tuntap_info { char dev_name[IFNAMSIZ]; - int fixed_config; int fd; }; @@ -164,11 +163,6 @@ static int tuntap_open(struct iss_net_private *lp) int err = -EINVAL; int fd; - /* We currently only support a fixed configuration. */ - - if (!lp->tp.info.tuntap.fixed_config) - return -EINVAL; - fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */ if (fd < 0) { pr_err("Failed to open /dev/net/tun, returned %d (errno = %d)\n", @@ -220,8 +214,7 @@ static int tuntap_poll(struct iss_net_private *lp) } /* - * Currently only a device name is supported. - * ethX=tuntap[,[mac address][,[device name]]] + * ethX=tuntap,[mac address],device name */ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) @@ -247,12 +240,13 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) return 0; } - if (dev_name) { - strlcpy(lp->tp.info.tuntap.dev_name, dev_name, - sizeof(lp->tp.info.tuntap.dev_name)); - lp->tp.info.tuntap.fixed_config = 1; - } else - strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); + if (!dev_name) { + pr_err("%s: missing tuntap device name\n", dev->name); + return 0; + } + + strlcpy(lp->tp.info.tuntap.dev_name, dev_name, + sizeof(lp->tp.info.tuntap.dev_name)); setup_etheraddr(dev, mac_str); -- cgit v0.10.2 From a6e16b9aaf0df4b1de8853cad97f387ade9fa8f5 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 16:05:51 +0400 Subject: xtensa: ISS: clean up diagnostic, increase verbosity Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 668ed62..3aff430 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -165,8 +165,8 @@ static int tuntap_open(struct iss_net_private *lp) fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */ if (fd < 0) { - pr_err("Failed to open /dev/net/tun, returned %d (errno = %d)\n", - fd, errno); + pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n", + lp->dev->name, fd, errno); return fd; } @@ -176,8 +176,8 @@ static int tuntap_open(struct iss_net_private *lp) err = simc_ioctl(fd, TUNSETIFF, &ifr); if (err < 0) { - pr_err("Failed to set interface, returned %d (errno = %d)\n", - err, errno); + pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n", + lp->dev->name, dev_name, err, errno); simc_close(fd); return err; } @@ -232,11 +232,13 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) if (*init == ',') { rem = split_if_spec(init + 1, &mac_str, &dev_name); if (rem != NULL) { - pr_err("Extra garbage on specification : '%s'\n", rem); + pr_err("%s: extra garbage on specification : '%s'\n", + dev->name, rem); return 0; } } else if (*init != '\0') { - pr_err("Invalid argument: %s. Skipping device!\n", init); + pr_err("%s: invalid argument: %s. Skipping device!\n", + dev->name, init); return 0; } @@ -261,8 +263,6 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init) lp->tp.protocol = tuntap_protocol; lp->tp.poll = tuntap_poll; - pr_info("TUN/TAP backend -\n"); - return 1; } @@ -435,7 +435,7 @@ static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) } else { netif_start_queue(dev); - pr_err("iss_net_start_xmit: failed(%d)\n", len); + pr_err("%s: %s failed(%d)\n", dev->name, __func__, len); } spin_unlock_irqrestore(&lp->lock, flags); @@ -538,7 +538,8 @@ static int iss_net_configure(int index, char *init) */ if (!tuntap_probe(lp, index, init)) { - pr_err("Invalid arguments. Skipping device!\n"); + pr_err("%s: invalid arguments. Skipping device!\n", + dev->name); goto errout; } @@ -570,7 +571,7 @@ static int iss_net_configure(int index, char *init) rtnl_unlock(); if (err) { - pr_err("Error registering net device!\n"); + pr_err("%s: error registering net device!\n", dev->name); /* XXX: should we call ->remove() here? */ free_netdev(dev); return 1; -- cgit v0.10.2 From 220c062688c31bbf992921fca172ddd1e609f8f6 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 18 Nov 2013 06:46:43 +0200 Subject: xtensa: implement robust futex atomic uaccess ops This enables the set_robust_list(2) system call. Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index 228d6ae..5851db2 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild @@ -8,7 +8,6 @@ generic-y += emergency-restart.h generic-y += errno.h generic-y += exec.h generic-y += fcntl.h -generic-y += futex.h generic-y += hardirq.h generic-y += ioctl.h generic-y += irq_regs.h diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h new file mode 100644 index 0000000..b39531b --- /dev/null +++ b/arch/xtensa/include/asm/futex.h @@ -0,0 +1,147 @@ +/* + * Atomic futex routines + * + * Based on the PowerPC implementataion + * + * 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. + * + * Copyright (C) 2013 TangoTec Ltd. + * + * Baruch Siach + */ + +#ifndef _ASM_XTENSA_FUTEX_H +#define _ASM_XTENSA_FUTEX_H + +#ifdef __KERNEL__ + +#include +#include +#include + +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ + __asm__ __volatile( \ + "1: l32i %0, %2, 0\n" \ + insn "\n" \ + " wsr %0, scompare1\n" \ + "2: s32c1i %1, %2, 0\n" \ + " bne %1, %0, 1b\n" \ + " movi %1, 0\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 4\n" \ + "4: .long 3b\n" \ + "5: l32r %0, 4b\n" \ + " movi %1, %3\n" \ + " jx %0\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .long 1b,5b,2b,5b\n" \ + " .previous\n" \ + : "=&r" (oldval), "=&r" (ret) \ + : "r" (uaddr), "I" (-EFAULT), "r" (oparg) \ + : "memory") + +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + +#if !XCHAL_HAVE_S32C1I + return -ENOSYS; +#endif + + pagefault_disable(); + + switch (op) { + case FUTEX_OP_SET: + __futex_atomic_op("mov %1, %4", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("add %1, %0, %4", ret, oldval, uaddr, + oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("or %1, %0, %4", ret, oldval, uaddr, + oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("and %1, %0, %4", ret, oldval, uaddr, + ~oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("xor %1, %0, %4", ret, oldval, uaddr, + oparg); + break; + default: + ret = -ENOSYS; + } + + pagefault_enable(); + + if (ret) + return ret; + + switch (cmp) { + case FUTEX_OP_CMP_EQ: return (oldval == cmparg); + case FUTEX_OP_CMP_NE: return (oldval != cmparg); + case FUTEX_OP_CMP_LT: return (oldval < cmparg); + case FUTEX_OP_CMP_GE: return (oldval >= cmparg); + case FUTEX_OP_CMP_LE: return (oldval <= cmparg); + case FUTEX_OP_CMP_GT: return (oldval > cmparg); + } + + return -ENOSYS; +} + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + int ret = 0; + u32 prev; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + +#if !XCHAL_HAVE_S32C1I + return -ENOSYS; +#endif + + __asm__ __volatile__ ( + " # futex_atomic_cmpxchg_inatomic\n" + "1: l32i %1, %3, 0\n" + " mov %0, %5\n" + " wsr %1, scompare1\n" + "2: s32c1i %0, %3, 0\n" + "3:\n" + " .section .fixup,\"ax\"\n" + " .align 4\n" + "4: .long 3b\n" + "5: l32r %1, 4b\n" + " movi %0, %6\n" + " jx %1\n" + " .previous\n" + " .section __ex_table,\"a\"\n" + " .long 1b,5b,2b,5b\n" + " .previous\n" + : "+r" (ret), "=&r" (prev), "+m" (*uaddr) + : "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT) + : "memory"); + + *uval = prev; + return ret; +} + +#endif /* __KERNEL__ */ +#endif /* _ASM_XTENSA_FUTEX_H */ -- cgit v0.10.2 From c4ee0af3fa0dc65f690fc908f02b8355f9576ea0 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:12 +0400 Subject: xtensa: remove NO_IRQ definitions These definitions (-1) were correct when used as a hardware IRQ number, but are incorrect as a mapped IRQ number. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h index 4416773..4b43ff1 100644 --- a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h +++ b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h @@ -15,10 +15,6 @@ #ifndef __XTENSA_XTAVNET_HARDWARE_H #define __XTENSA_XTAVNET_HARDWARE_H -/* By default NO_IRQ is defined to 0 in Linux, but we use the - interrupt 0 for UART... */ -#define NO_IRQ -1 - /* Memory configuration. */ #define PLATFORM_DEFAULT_MEM_START 0x00000000 diff --git a/arch/xtensa/variants/s6000/include/variant/irq.h b/arch/xtensa/variants/s6000/include/variant/irq.h index 97d6fc4..39ca751a 100644 --- a/arch/xtensa/variants/s6000/include/variant/irq.h +++ b/arch/xtensa/variants/s6000/include/variant/irq.h @@ -1,7 +1,6 @@ #ifndef _XTENSA_S6000_IRQ_H #define _XTENSA_S6000_IRQ_H -#define NO_IRQ (-1) #define VARIANT_NR_IRQS 8 /* GPIO interrupts */ extern void variant_irq_enable(unsigned int irq); -- cgit v0.10.2 From ff3849059d787ab2072032800e553c9022c9ab54 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:13 +0400 Subject: xtensa: fix build warning in 64-bit build environment This fixes the following build warnings: arch/xtensa/mm/misc.S: Assembler messages: arch/xtensa/mm/misc.S:143: Warning: value 0xffffffff30000106 truncated to 0x30000106 arch/xtensa/mm/misc.S:197: Warning: value 0xffffffff30000106 truncated to 0x30000106 Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S index d97ed1b..1f68558 100644 --- a/arch/xtensa/mm/misc.S +++ b/arch/xtensa/mm/misc.S @@ -140,7 +140,7 @@ ENTRY(clear_user_page) /* Setup a temporary DTLB with the color of the VPN */ - movi a4, -PAGE_OFFSET + (PAGE_KERNEL | _PAGE_HW_WRITE) + movi a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff movi a5, TLBTEMP_BASE_1 # virt add a6, a2, a4 # ppn add a2, a5, a3 # add 'color' @@ -194,7 +194,7 @@ ENTRY(copy_user_page) or a9, a9, a8 slli a4, a4, PAGE_SHIFT s32i a9, a5, PAGE_FLAGS - movi a5, -PAGE_OFFSET + (PAGE_KERNEL | _PAGE_HW_WRITE) + movi a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff beqz a6, 1f -- cgit v0.10.2 From 5515daa9ca5a0992cc1c55d4c6c7ddb116e829b4 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:15 +0400 Subject: xtensa: fix arch spinlock function names Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/spinlock.h b/arch/xtensa/include/asm/spinlock.h index 0397590..1d95fa5 100644 --- a/arch/xtensa/include/asm/spinlock.h +++ b/arch/xtensa/include/asm/spinlock.h @@ -28,13 +28,13 @@ * 1 somebody owns the spinlock */ -#define __raw_spin_is_locked(x) ((x)->slock != 0) -#define __raw_spin_unlock_wait(lock) \ - do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0) +#define arch_spin_is_locked(x) ((x)->slock != 0) +#define arch_spin_unlock_wait(lock) \ + do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0) -#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) +#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock) -static inline void __raw_spin_lock(raw_spinlock_t *lock) +static inline void arch_spin_lock(arch_spinlock_t *lock) { unsigned long tmp; @@ -51,7 +51,7 @@ static inline void __raw_spin_lock(raw_spinlock_t *lock) /* Returns 1 if the lock is obtained, 0 otherwise. */ -static inline int __raw_spin_trylock(raw_spinlock_t *lock) +static inline int arch_spin_trylock(arch_spinlock_t *lock) { unsigned long tmp; @@ -67,7 +67,7 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock) return tmp == 0 ? 1 : 0; } -static inline void __raw_spin_unlock(raw_spinlock_t *lock) +static inline void arch_spin_unlock(arch_spinlock_t *lock) { unsigned long tmp; @@ -96,9 +96,9 @@ static inline void __raw_spin_unlock(raw_spinlock_t *lock) * 0x80000000 one writer owns the rwlock, no other writers, no readers */ -#define __raw_write_can_lock(x) ((x)->lock == 0) +#define arch_write_can_lock(x) ((x)->lock == 0) -static inline void __raw_write_lock(raw_rwlock_t *rw) +static inline void arch_write_lock(arch_rwlock_t *rw) { unsigned long tmp; @@ -116,7 +116,7 @@ static inline void __raw_write_lock(raw_rwlock_t *rw) /* Returns 1 if the lock is obtained, 0 otherwise. */ -static inline int __raw_write_trylock(raw_rwlock_t *rw) +static inline int arch_write_trylock(arch_rwlock_t *rw) { unsigned long tmp; @@ -133,7 +133,7 @@ static inline int __raw_write_trylock(raw_rwlock_t *rw) return tmp == 0 ? 1 : 0; } -static inline void __raw_write_unlock(raw_rwlock_t *rw) +static inline void arch_write_unlock(arch_rwlock_t *rw) { unsigned long tmp; @@ -145,7 +145,7 @@ static inline void __raw_write_unlock(raw_rwlock_t *rw) : "memory"); } -static inline void __raw_read_lock(raw_rwlock_t *rw) +static inline void arch_read_lock(arch_rwlock_t *rw) { unsigned long tmp; unsigned long result; @@ -164,7 +164,7 @@ static inline void __raw_read_lock(raw_rwlock_t *rw) /* Returns 1 if the lock is obtained, 0 otherwise. */ -static inline int __raw_read_trylock(raw_rwlock_t *rw) +static inline int arch_read_trylock(arch_rwlock_t *rw) { unsigned long result; unsigned long tmp; @@ -184,7 +184,7 @@ static inline int __raw_read_trylock(raw_rwlock_t *rw) return result == 0; } -static inline void __raw_read_unlock(raw_rwlock_t *rw) +static inline void arch_read_unlock(arch_rwlock_t *rw) { unsigned long tmp1, tmp2; @@ -199,4 +199,7 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw) : "memory"); } +#define arch_read_lock_flags(lock, flags) arch_read_lock(lock) +#define arch_write_lock_flags(lock, flags) arch_write_lock(lock) + #endif /* _XTENSA_SPINLOCK_H */ diff --git a/arch/xtensa/include/asm/spinlock_types.h b/arch/xtensa/include/asm/spinlock_types.h new file mode 100644 index 0000000..7ec5ce1 --- /dev/null +++ b/arch/xtensa/include/asm/spinlock_types.h @@ -0,0 +1,20 @@ +#ifndef __ASM_SPINLOCK_TYPES_H +#define __ASM_SPINLOCK_TYPES_H + +#ifndef __LINUX_SPINLOCK_TYPES_H +# error "please don't include this file directly" +#endif + +typedef struct { + volatile unsigned int slock; +} arch_spinlock_t; + +#define __ARCH_SPIN_LOCK_UNLOCKED { 0 } + +typedef struct { + volatile unsigned int lock; +} arch_rwlock_t; + +#define __ARCH_RW_LOCK_UNLOCKED { 0 } + +#endif -- cgit v0.10.2 From 01e3b3cb2d70ca9d95769e863ca71a85f84c3917 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:16 +0400 Subject: xtensa: fix __delay for small loop count Avoid __delay counter underflow for loop counts < 2. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 3899610..742b89f 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -19,9 +19,12 @@ extern unsigned long loops_per_jiffy; static inline void __delay(unsigned long loops) { - /* 2 cycles per loop. */ - __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b" - : "=r" (loops) : "0" (loops)); + if (__builtin_constant_p(loops) && loops < 2) + __asm__ __volatile__ ("nop"); + else if (loops >= 2) + /* 2 cycles per loop. */ + __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b" + : "+r" (loops)); } /* For SMP/NUMA systems, change boot_cpu_data to something like -- cgit v0.10.2 From 496543c4f1afd0eb1548054838058bf30464ccc9 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:17 +0400 Subject: xtensa: enable HAVE_IRQ_TIME_ACCOUNTING Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 8d24dcb..fb140ae 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -19,6 +19,7 @@ config XTENSA select IRQ_DOMAIN select HAVE_OPROFILE select HAVE_FUNCTION_TRACER + select HAVE_IRQ_TIME_ACCOUNTING help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both -- cgit v0.10.2 From 0fb4040e6ed69b62e64ec781694882309edbf33c Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 17 Oct 2013 02:42:18 +0400 Subject: xtensa: mark ccount as continuous clocksource This allows ccount to be used as highres timer. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 9af3dd8..26eb6a9e 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -46,6 +46,7 @@ static struct clocksource ccount_clocksource = { .rating = 200, .read = ccount_read, .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static int ccount_timer_set_next_event(unsigned long delta, -- cgit v0.10.2 From 6235153170db777296b6a47181056dd30a027d03 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:19 +0400 Subject: xtensa: update clockevent setup for SMP Provide per-cpu ccount_timer objects and use them appropriately. Extract per-cpu clockevent setup function. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 26eb6a9e..4ce7aae 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -53,18 +53,12 @@ static int ccount_timer_set_next_event(unsigned long delta, struct clock_event_device *dev); static void ccount_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt); -static struct ccount_timer_t { +struct ccount_timer { struct clock_event_device evt; int irq_enabled; -} ccount_timer = { - .evt = { - .name = "ccount_clockevent", - .features = CLOCK_EVT_FEAT_ONESHOT, - .rating = 300, - .set_next_event = ccount_timer_set_next_event, - .set_mode = ccount_timer_set_mode, - }, + char name[24]; }; +static DEFINE_PER_CPU(struct ccount_timer, ccount_timer); static int ccount_timer_set_next_event(unsigned long delta, struct clock_event_device *dev) @@ -85,8 +79,8 @@ static int ccount_timer_set_next_event(unsigned long delta, static void ccount_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - struct ccount_timer_t *timer = - container_of(evt, struct ccount_timer_t, evt); + struct ccount_timer *timer = + container_of(evt, struct ccount_timer, evt); /* * There is no way to disable the timer interrupt at the device level, @@ -118,9 +112,28 @@ static struct irqaction timer_irqaction = { .handler = timer_interrupt, .flags = IRQF_TIMER, .name = "timer", - .dev_id = &ccount_timer, }; +void local_timer_setup(unsigned cpu) +{ + struct ccount_timer *timer = &per_cpu(ccount_timer, cpu); + struct clock_event_device *clockevent = &timer->evt; + + timer->irq_enabled = 1; + clockevent->name = timer->name; + snprintf(timer->name, sizeof(timer->name), "ccount_clockevent_%u", cpu); + clockevent->features = CLOCK_EVT_FEAT_ONESHOT; + clockevent->rating = 300; + clockevent->set_next_event = ccount_timer_set_next_event; + clockevent->set_mode = ccount_timer_set_mode; + clockevent->cpumask = cpumask_of(cpu); + clockevent->irq = irq_create_mapping(NULL, LINUX_TIMER_INT); + if (WARN(!clockevent->irq, "error: can't map timer irq")) + return; + clockevents_config_and_register(clockevent, ccount_freq, + 0xf, 0xffffffff); +} + void __init time_init(void) { #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT @@ -132,16 +145,8 @@ void __init time_init(void) ccount_freq = CONFIG_XTENSA_CPU_CLOCK*1000000UL; #endif clocksource_register_hz(&ccount_clocksource, ccount_freq); - - ccount_timer.evt.cpumask = cpumask_of(0); - ccount_timer.evt.irq = irq_create_mapping(NULL, LINUX_TIMER_INT); - if (WARN(!ccount_timer.evt.irq, "error: can't map timer irq")) - return; - clockevents_config_and_register(&ccount_timer.evt, ccount_freq, 0xf, - 0xffffffff); - setup_irq(ccount_timer.evt.irq, &timer_irqaction); - ccount_timer.irq_enabled = 1; - + local_timer_setup(0); + setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction); setup_sched_clock(ccount_sched_clock_read, 32, ccount_freq); } @@ -149,10 +154,9 @@ void __init time_init(void) * The timer interrupt is called HZ times per second. */ -irqreturn_t timer_interrupt (int irq, void *dev_id) +irqreturn_t timer_interrupt(int irq, void *dev_id) { - struct ccount_timer_t *timer = dev_id; - struct clock_event_device *evt = &timer->evt; + struct clock_event_device *evt = &this_cpu_ptr(&ccount_timer)->evt; evt->event_handler(evt); -- cgit v0.10.2 From 59970753536d98d33efcbe4f119ad8e3e399a46b Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:20 +0400 Subject: xtensa: call check_s32c1i after trap_init Otherwise exceptions may occur prior to exception handling mechanism initialization, resulting in silently dead system. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 6e2b663..9bc6f06 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -354,7 +354,8 @@ static inline int probed_compare_swap(int *v, int cmp, int set) /* Handle probed exception */ -void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause) +static void __init do_probed_exception(struct pt_regs *regs, + unsigned long exccause) { if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ regs->pc += 3; /* skip the s32c1i instruction */ @@ -366,7 +367,7 @@ void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause) /* Simple test of S32C1I (soc bringup assist) */ -void __init check_s32c1i(void) +static int __init check_s32c1i(void) { int n, cause1, cause2; void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ @@ -421,24 +422,21 @@ void __init check_s32c1i(void) trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); + return 0; } #else /* XCHAL_HAVE_S32C1I */ /* This condition should not occur with a commercially deployed processor. Display reminder for early engr test or demo chips / FPGA bitstreams */ -void __init check_s32c1i(void) +static int __init check_s32c1i(void) { pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); + return 0; } #endif /* XCHAL_HAVE_S32C1I */ -#else /* CONFIG_S32C1I_SELFTEST */ - -void __init check_s32c1i(void) -{ -} - +early_initcall(check_s32c1i); #endif /* CONFIG_S32C1I_SELFTEST */ @@ -447,8 +445,6 @@ void __init setup_arch(char **cmdline_p) strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; - check_s32c1i(); - /* Reserve some memory regions */ #ifdef CONFIG_BLK_DEV_INITRD -- cgit v0.10.2 From c8f3a7dc0102fc9f89cc373c6dd7beb49916a695 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:21 +0400 Subject: xtensa: move init_mmu declaration to mmu_context.h Secondary CPUs need this declaration to initialize their MMUs. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/mmu_context.h b/arch/xtensa/include/asm/mmu_context.h index d43525a..86292c2 100644 --- a/arch/xtensa/include/asm/mmu_context.h +++ b/arch/xtensa/include/asm/mmu_context.h @@ -49,6 +49,12 @@ extern unsigned long asid_cache; #define ASID_MASK ((1 << XCHAL_MMU_ASID_BITS) - 1) #define ASID_INSERT(x) (0x03020001 | (((x) & ASID_MASK) << 8)) +#ifdef CONFIG_MMU +void init_mmu(void); +#else +static inline void init_mmu(void) { } +#endif + static inline void set_rasid_register (unsigned long val) { __asm__ __volatile__ (" wsr %0, rasid\n\t" diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 9bc6f06..3d35314 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -37,6 +37,7 @@ #endif #include +#include #include #include #include @@ -85,12 +86,6 @@ static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; sysmem_info_t __initdata sysmem; -#ifdef CONFIG_MMU -extern void init_mmu(void); -#else -static inline void init_mmu(void) { } -#endif - extern int mem_reserve(unsigned long, unsigned long, int); extern void bootmem_init(void); extern void zones_init(void); -- cgit v0.10.2 From cbd1de2e8e46207cead11034f92ea8a018b11189 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 1 Dec 2013 12:59:49 +0400 Subject: xtensa: move built-in PIC to drivers/irqchip Extract xtensa built-in interrupt controller implementation from xtensa/kernel/irq.c and move it to other irqchips, providing way to instantiate it from the device tree. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/boot/dts/xtfpga.dtsi b/arch/xtensa/boot/dts/xtfpga.dtsi index 7eda6ec..b042c3f 100644 --- a/arch/xtensa/boot/dts/xtfpga.dtsi +++ b/arch/xtensa/boot/dts/xtfpga.dtsi @@ -26,7 +26,7 @@ }; pic: pic { - compatible = "xtensa,pic"; + compatible = "cdns,xtensa-pic"; /* one cell: internal irq number, * two cells: second cell == 0: internal irq number * second cell == 1: external irq number diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 4c0ccc9..16464f2 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -43,5 +43,12 @@ static __inline__ int irq_canonicalize(int irq) } struct irqaction; +struct irq_domain; + +int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, + unsigned long int_irq, unsigned long ext_irq, + unsigned long *out_hwirq, unsigned int *out_type); +int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw); +unsigned xtensa_map_ext_irq(unsigned ext_irq); #endif /* _XTENSA_IRQ_H */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 6f4f974..ada1e48 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -18,28 +18,20 @@ #include #include #include +#include +#include #include #include #include #include -static unsigned int cached_irq_mask; - atomic_t irq_err_count; -static struct irq_domain *root_domain; - -/* - * do_IRQ handles all normal device IRQ's (the special - * SMP cross-CPU interrupts have their own specific - * handlers). - */ - asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); - int irq = irq_find_mapping(root_domain, hwirq); + int irq = irq_find_mapping(NULL, hwirq); if (hwirq >= NR_IRQS) { printk(KERN_EMERG "%s: cannot handle IRQ %d\n", @@ -74,83 +66,57 @@ int arch_show_interrupts(struct seq_file *p, int prec) return 0; } -static void xtensa_irq_mask(struct irq_data *d) -{ - cached_irq_mask &= ~(1 << d->hwirq); - set_sr (cached_irq_mask, intenable); -} - -static void xtensa_irq_unmask(struct irq_data *d) -{ - cached_irq_mask |= 1 << d->hwirq; - set_sr (cached_irq_mask, intenable); -} - -static void xtensa_irq_enable(struct irq_data *d) -{ - variant_irq_enable(d->hwirq); - xtensa_irq_unmask(d); -} - -static void xtensa_irq_disable(struct irq_data *d) -{ - xtensa_irq_mask(d); - variant_irq_disable(d->hwirq); -} - -static void xtensa_irq_ack(struct irq_data *d) -{ - set_sr(1 << d->hwirq, intclear); -} - -static int xtensa_irq_retrigger(struct irq_data *d) +int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, + unsigned long int_irq, unsigned long ext_irq, + unsigned long *out_hwirq, unsigned int *out_type) { - set_sr(1 << d->hwirq, intset); - return 1; + if (WARN_ON(intsize < 1 || intsize > 2)) + return -EINVAL; + if (intsize == 2 && intspec[1] == 1) { + int_irq = xtensa_map_ext_irq(ext_irq); + if (int_irq < XCHAL_NUM_INTERRUPTS) + *out_hwirq = int_irq; + else + return -EINVAL; + } else { + *out_hwirq = int_irq; + } + *out_type = IRQ_TYPE_NONE; + return 0; } -static struct irq_chip xtensa_irq_chip = { - .name = "xtensa", - .irq_enable = xtensa_irq_enable, - .irq_disable = xtensa_irq_disable, - .irq_mask = xtensa_irq_mask, - .irq_unmask = xtensa_irq_unmask, - .irq_ack = xtensa_irq_ack, - .irq_retrigger = xtensa_irq_retrigger, -}; - -static int xtensa_irq_map(struct irq_domain *d, unsigned int irq, +int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { + struct irq_chip *irq_chip = d->host_data; u32 mask = 1 << hw; if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + irq_set_chip_and_handler_name(irq, irq_chip, handle_simple_irq, "level"); irq_set_status_flags(irq, IRQ_LEVEL); } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + irq_set_chip_and_handler_name(irq, irq_chip, handle_edge_irq, "edge"); irq_clear_status_flags(irq, IRQ_LEVEL); } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + irq_set_chip_and_handler_name(irq, irq_chip, handle_level_irq, "level"); irq_set_status_flags(irq, IRQ_LEVEL); } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, - handle_edge_irq, "edge"); + irq_set_chip_and_handler_name(irq, irq_chip, + handle_percpu_irq, "timer"); irq_clear_status_flags(irq, IRQ_LEVEL); } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ /* XCHAL_INTTYPE_MASK_NMI */ - - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + irq_set_chip_and_handler_name(irq, irq_chip, handle_level_irq, "level"); irq_set_status_flags(irq, IRQ_LEVEL); } return 0; } -static unsigned map_ext_irq(unsigned ext_irq) +unsigned xtensa_map_ext_irq(unsigned ext_irq) { unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | XCHAL_INTTYPE_MASK_EXTERN_LEVEL; @@ -163,55 +129,12 @@ static unsigned map_ext_irq(unsigned ext_irq) return XCHAL_NUM_INTERRUPTS; } -/* - * Device Tree IRQ specifier translation function which works with one or - * two cell bindings. First cell value maps directly to the hwirq number. - * Second cell if present specifies whether hwirq number is external (1) or - * internal (0). - */ -int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, unsigned int *out_type) -{ - if (WARN_ON(intsize < 1 || intsize > 2)) - return -EINVAL; - if (intsize == 2 && intspec[1] == 1) { - unsigned int_irq = map_ext_irq(intspec[0]); - if (int_irq < XCHAL_NUM_INTERRUPTS) - *out_hwirq = int_irq; - else - return -EINVAL; - } else { - *out_hwirq = intspec[0]; - } - *out_type = IRQ_TYPE_NONE; - return 0; -} - -static const struct irq_domain_ops xtensa_irq_domain_ops = { - .xlate = xtensa_irq_domain_xlate, - .map = xtensa_irq_map, -}; - void __init init_IRQ(void) { - struct device_node *intc = NULL; - - cached_irq_mask = 0; - set_sr(~0, intclear); - #ifdef CONFIG_OF - /* The interrupt controller device node is mandatory */ - intc = of_find_compatible_node(NULL, NULL, "xtensa,pic"); - BUG_ON(!intc); - - root_domain = irq_domain_add_linear(intc, NR_IRQS, - &xtensa_irq_domain_ops, NULL); + irqchip_init(); #else - root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, - &xtensa_irq_domain_ops, NULL); + xtensa_pic_init_legacy(NULL); #endif - irq_set_default_host(root_domain); - variant_init_irq(); } diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c60b901..c81a7f3 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o +obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c new file mode 100644 index 0000000..7d71126 --- /dev/null +++ b/drivers/irqchip/irq-xtensa-pic.c @@ -0,0 +1,108 @@ +/* + * Xtensa built-in interrupt controller + * + * Copyright (C) 2002 - 2013 Tensilica, Inc. + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Chris Zankel + * Kevin Chea + */ + +#include +#include +#include +#include + +#include "irqchip.h" + +unsigned int cached_irq_mask; + +/* + * Device Tree IRQ specifier translation function which works with one or + * two cell bindings. First cell value maps directly to the hwirq number. + * Second cell if present specifies whether hwirq number is external (1) or + * internal (0). + */ +static int xtensa_pic_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + return xtensa_irq_domain_xlate(intspec, intsize, + intspec[0], intspec[0], + out_hwirq, out_type); +} + +static const struct irq_domain_ops xtensa_irq_domain_ops = { + .xlate = xtensa_pic_irq_domain_xlate, + .map = xtensa_irq_map, +}; + +static void xtensa_irq_mask(struct irq_data *d) +{ + cached_irq_mask &= ~(1 << d->hwirq); + set_sr(cached_irq_mask, intenable); +} + +static void xtensa_irq_unmask(struct irq_data *d) +{ + cached_irq_mask |= 1 << d->hwirq; + set_sr(cached_irq_mask, intenable); +} + +static void xtensa_irq_enable(struct irq_data *d) +{ + variant_irq_enable(d->hwirq); + xtensa_irq_unmask(d); +} + +static void xtensa_irq_disable(struct irq_data *d) +{ + xtensa_irq_mask(d); + variant_irq_disable(d->hwirq); +} + +static void xtensa_irq_ack(struct irq_data *d) +{ + set_sr(1 << d->hwirq, intclear); +} + +static int xtensa_irq_retrigger(struct irq_data *d) +{ + set_sr(1 << d->hwirq, intset); + return 1; +} + +static struct irq_chip xtensa_irq_chip = { + .name = "xtensa", + .irq_enable = xtensa_irq_enable, + .irq_disable = xtensa_irq_disable, + .irq_mask = xtensa_irq_mask, + .irq_unmask = xtensa_irq_unmask, + .irq_ack = xtensa_irq_ack, + .irq_retrigger = xtensa_irq_retrigger, +}; + +int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) +{ + struct irq_domain *root_domain = + irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, + &xtensa_irq_domain_ops, &xtensa_irq_chip); + irq_set_default_host(root_domain); + return 0; +} + +static int __init xtensa_pic_init(struct device_node *np, + struct device_node *interrupt_parent) +{ + struct irq_domain *root_domain = + irq_domain_add_linear(np, NR_IRQS, &xtensa_irq_domain_ops, + &xtensa_irq_chip); + irq_set_default_host(root_domain); + return 0; +} +IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic", xtensa_pic_init); diff --git a/include/linux/irqchip/xtensa-pic.h b/include/linux/irqchip/xtensa-pic.h new file mode 100644 index 0000000..48718ae --- /dev/null +++ b/include/linux/irqchip/xtensa-pic.h @@ -0,0 +1,18 @@ +/* + * Xtensa built-in interrupt controller + * + * Copyright (C) 2002 - 2013 Tensilica, Inc. + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __LINUX_IRQCHIP_XTENSA_PIC_H +#define __LINUX_IRQCHIP_XTENSA_PIC_H + +struct device_node; +int xtensa_pic_init_legacy(struct device_node *interrupt_parent); + +#endif /* __LINUX_IRQCHIP_XTENSA_PIC_H */ -- cgit v0.10.2 From 996232393bcdfff49de31e1bc1c431fd8bce9ccb Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:23 +0400 Subject: xtensa: clean up do_interrupt/do_IRQ - set up irq registers and call irq_enter/irq_exit once for each kernel entry due to interrupt; - don't attempt to clear current IRQ in the do_interrupt, IRQ handler will take care of it; - find pending interrupt with highest priority before every ISR invocation. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index ada1e48..3cef58e 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -30,7 +30,6 @@ atomic_t irq_err_count; asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) { - struct pt_regs *old_regs = set_irq_regs(regs); int irq = irq_find_mapping(NULL, hwirq); if (hwirq >= NR_IRQS) { @@ -38,8 +37,6 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) __func__, hwirq); } - irq_enter(); - #ifdef CONFIG_DEBUG_STACKOVERFLOW /* Debugging check for stack overflow: is there less than 1KB free? */ { @@ -54,9 +51,6 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) } #endif generic_handle_irq(irq); - - irq_exit(); - set_irq_regs(old_regs); } int arch_show_interrupts(struct seq_file *p, int prec) diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 3e8a05c..3dbe864 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -212,6 +212,9 @@ void do_interrupt(struct pt_regs *regs) XCHAL_INTLEVEL6_MASK, XCHAL_INTLEVEL7_MASK, }; + struct pt_regs *old_regs = set_irq_regs(regs); + + irq_enter(); for (;;) { unsigned intread = get_sr(interrupt); @@ -227,21 +230,13 @@ void do_interrupt(struct pt_regs *regs) } if (level == 0) - return; - - /* - * Clear the interrupt before processing, in case it's - * edge-triggered or software-generated - */ - while (int_at_level) { - unsigned i = __ffs(int_at_level); - unsigned mask = 1 << i; - - int_at_level ^= mask; - set_sr(mask, intclear); - do_IRQ(i, regs); - } + break; + + do_IRQ(__ffs(int_at_level), regs); } + + irq_exit(); + set_irq_regs(old_regs); } /* -- cgit v0.10.2 From bae07f8a9dfaf6268f2fba5522b70bce6fc7d718 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:24 +0400 Subject: xtensa: clear timer IRQ unconditionally in its handler PIC irq_ack doesn't clear timer IRQ, because timer interrupt handler usually set up new timer by writing to ccompare register and thus clearing timer IRQ. However timer may not be set up in the IRQ handler, e.g. with tickless idle on SMP, or when CPU is going offline, leaving timer IRQ raised and making do_interrupt attempting to handle it forever. To fix this always write current value of ccompare SR chosen to be linux timer back to that SR on entry to timer interrupt handler. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 4ce7aae..60dcb3f 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -158,6 +158,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = &this_cpu_ptr(&ccount_timer)->evt; + set_linux_timer(get_linux_timer()); evt->event_handler(evt); /* Allow platform to do something useful (Wdog). */ -- cgit v0.10.2 From 26a8e96a8b37e8070fa9dcb1b7490cf4d4492d50 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 1 Dec 2013 12:04:57 +0400 Subject: xtensa: add MX irqchip MX is an interrupt distributor used in some SMP-capable xtensa configurations. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 16464f2..7d194d4 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -50,5 +50,6 @@ int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type); int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw); unsigned xtensa_map_ext_irq(unsigned ext_irq); +unsigned xtensa_get_ext_irq_no(unsigned irq); #endif /* _XTENSA_IRQ_H */ diff --git a/arch/xtensa/include/asm/mxregs.h b/arch/xtensa/include/asm/mxregs.h new file mode 100644 index 0000000..73dcc54 --- /dev/null +++ b/arch/xtensa/include/asm/mxregs.h @@ -0,0 +1,46 @@ +/* + * Xtensa MX interrupt distributor + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 - 2013 Tensilica Inc. + */ + +#ifndef _XTENSA_MXREGS_H +#define _XTENSA_MXREGS_H + +/* + * RER/WER at, as Read/write external register + * at: value + * as: address + * + * Address Value + * 00nn 0...0p..p Interrupt Routing, route IRQ n to processor p + * 01pp 0...0d..d 16 bits (d) 'ored' as single IPI to processor p + * 0180 0...0m..m Clear enable specified by mask (m) + * 0184 0...0m..m Set enable specified by mask (m) + * 0190 0...0x..x 8-bit IPI partition register + * VVVVVVVVPPPPUUUUUUUUUUUUUUUUU + * V (10-bit) Release/Version + * P ( 4-bit) Number of cores - 1 + * U (18-bit) ID + * 01a0 i.......i 32-bit ConfigID + * 0200 0...0m..m RunStall core 'n' + * 0220 c Cache coherency enabled + */ + +#define MIROUT(irq) (0x000 + (irq)) +#define MIPICAUSE(cpu) (0x100 + (cpu)) +#define MIPISET(cause) (0x140 + (cause)) +#define MIENG 0x180 +#define MIENGSET 0x184 +#define MIASG 0x188 /* Read Global Assert Register */ +#define MIASGSET 0x18c /* Set Global Addert Regiter */ +#define MIPIPART 0x190 +#define SYSCFGID 0x1a0 +#define MPSCORE 0x200 +#define CCON 0x220 + +#endif /* _XTENSA_MXREGS_H */ diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 7e409a5..abb5970 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -191,5 +191,25 @@ extern unsigned long get_wchan(struct task_struct *p); #define set_sr(x,sr) ({unsigned int v=(unsigned int)x; WSR(v,sr);}) #define get_sr(sr) ({unsigned int v; RSR(v,sr); v; }) +#ifndef XCHAL_HAVE_EXTERN_REGS +#define XCHAL_HAVE_EXTERN_REGS 0 +#endif + +#if XCHAL_HAVE_EXTERN_REGS + +static inline void set_er(unsigned long value, unsigned long addr) +{ + asm volatile ("wer %0, %1" : : "a" (value), "a" (addr) : "memory"); +} + +static inline unsigned long get_er(unsigned long addr) +{ + register unsigned long value; + asm volatile ("rer %0, %1" : "=a" (value) : "a" (addr) : "memory"); + return value; +} + +#endif /* XCHAL_HAVE_EXTERN_REGS */ + #endif /* __ASSEMBLY__ */ #endif /* _XTENSA_PROCESSOR_H */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 3cef58e..7d49730 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -123,6 +123,14 @@ unsigned xtensa_map_ext_irq(unsigned ext_irq) return XCHAL_NUM_INTERRUPTS; } +unsigned xtensa_get_ext_irq_no(unsigned irq) +{ + unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL) & + ((1u << irq) - 1); + return hweight32(mask); +} + void __init init_IRQ(void) { #ifdef CONFIG_OF diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3792a1a..07bc79c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -61,3 +61,7 @@ config VERSATILE_FPGA_IRQ_NR int default 4 depends on VERSATILE_FPGA_IRQ + +config XTENSA_MX + bool + select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c81a7f3..66913f9 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o +obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c new file mode 100644 index 0000000..f693f1b --- /dev/null +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -0,0 +1,164 @@ +/* + * Xtensa MX interrupt distributor + * + * Copyright (C) 2002 - 2013 Tensilica, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include + +#include + +#include "irqchip.h" + +#define HW_IRQ_IPI_COUNT 2 +#define HW_IRQ_MX_BASE 2 +#define HW_IRQ_EXTERN_BASE 3 + +static DEFINE_PER_CPU(unsigned int, cached_irq_mask); + +static int xtensa_mx_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + if (hw < HW_IRQ_IPI_COUNT) { + struct irq_chip *irq_chip = d->host_data; + irq_set_chip_and_handler_name(irq, irq_chip, + handle_percpu_irq, "ipi"); + irq_set_status_flags(irq, IRQ_LEVEL); + return 0; + } + return xtensa_irq_map(d, irq, hw); +} + +/* + * Device Tree IRQ specifier translation function which works with one or + * two cell bindings. First cell value maps directly to the hwirq number. + * Second cell if present specifies whether hwirq number is external (1) or + * internal (0). + */ +static int xtensa_mx_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + return xtensa_irq_domain_xlate(intspec, intsize, + intspec[0], intspec[0] + HW_IRQ_EXTERN_BASE, + out_hwirq, out_type); +} + +static const struct irq_domain_ops xtensa_mx_irq_domain_ops = { + .xlate = xtensa_mx_irq_domain_xlate, + .map = xtensa_mx_irq_map, +}; + +void secondary_init_irq(void) +{ + __this_cpu_write(cached_irq_mask, + XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL); + set_sr(XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL, intenable); +} + +static void xtensa_mx_irq_mask(struct irq_data *d) +{ + unsigned int mask = 1u << d->hwirq; + + if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { + set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) - + HW_IRQ_MX_BASE), MIENG); + } else { + mask = __this_cpu_read(cached_irq_mask) & ~mask; + __this_cpu_write(cached_irq_mask, mask); + set_sr(mask, intenable); + } +} + +static void xtensa_mx_irq_unmask(struct irq_data *d) +{ + unsigned int mask = 1u << d->hwirq; + + if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { + set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) - + HW_IRQ_MX_BASE), MIENGSET); + } else { + mask |= __this_cpu_read(cached_irq_mask); + __this_cpu_write(cached_irq_mask, mask); + set_sr(mask, intenable); + } +} + +static void xtensa_mx_irq_enable(struct irq_data *d) +{ + variant_irq_enable(d->hwirq); + xtensa_mx_irq_unmask(d); +} + +static void xtensa_mx_irq_disable(struct irq_data *d) +{ + xtensa_mx_irq_mask(d); + variant_irq_disable(d->hwirq); +} + +static void xtensa_mx_irq_ack(struct irq_data *d) +{ + set_sr(1 << d->hwirq, intclear); +} + +static int xtensa_mx_irq_retrigger(struct irq_data *d) +{ + set_sr(1 << d->hwirq, intset); + return 1; +} + +static int xtensa_mx_irq_set_affinity(struct irq_data *d, + const struct cpumask *dest, bool force) +{ + unsigned mask = 1u << cpumask_any(dest); + + set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE)); + return 0; + +} + +static struct irq_chip xtensa_mx_irq_chip = { + .name = "xtensa-mx", + .irq_enable = xtensa_mx_irq_enable, + .irq_disable = xtensa_mx_irq_disable, + .irq_mask = xtensa_mx_irq_mask, + .irq_unmask = xtensa_mx_irq_unmask, + .irq_ack = xtensa_mx_irq_ack, + .irq_retrigger = xtensa_mx_irq_retrigger, + .irq_set_affinity = xtensa_mx_irq_set_affinity, +}; + +int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent) +{ + struct irq_domain *root_domain = + irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, + &xtensa_mx_irq_domain_ops, + &xtensa_mx_irq_chip); + irq_set_default_host(root_domain); + secondary_init_irq(); + return 0; +} + +static int __init xtensa_mx_init(struct device_node *np, + struct device_node *interrupt_parent) +{ + struct irq_domain *root_domain = + irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops, + &xtensa_mx_irq_chip); + irq_set_default_host(root_domain); + secondary_init_irq(); + return 0; +} +IRQCHIP_DECLARE(xtensa_mx_irq_chip, "cdns,xtensa-mx", xtensa_mx_init); diff --git a/include/linux/irqchip/xtensa-mx.h b/include/linux/irqchip/xtensa-mx.h new file mode 100644 index 0000000..9c3b6ec --- /dev/null +++ b/include/linux/irqchip/xtensa-mx.h @@ -0,0 +1,17 @@ +/* + * Xtensa MX interrupt distributor + * + * Copyright (C) 2002 - 2013 Tensilica, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __LINUX_IRQCHIP_XTENSA_MX_H +#define __LINUX_IRQCHIP_XTENSA_MX_H + +struct device_node; +int xtensa_mx_init_legacy(struct device_node *interrupt_parent); + +#endif /* __LINUX_IRQCHIP_XTENSA_MX_H */ -- cgit v0.10.2 From f615136c06a791364f5afa8b8ba965315a6440f1 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:26 +0400 Subject: xtensa: add SMP support This is largely based on SMP code from the xtensa-2.6.29-smp tree by Piet Delaney, Marc Gauthier, Joe Taylor, Christian Zankel (and possibly other Tensilica folks). Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index fb140ae..4b09c60 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -9,7 +9,6 @@ config XTENSA select GENERIC_CLOCKEVENTS select VIRT_TO_BUS select GENERIC_IRQ_SHOW - select GENERIC_CPU_DEVICES select GENERIC_SCHED_CLOCK select MODULES_USE_ELF_RELA select GENERIC_PCI_IOMAP @@ -65,6 +64,9 @@ config MMU config VARIANT_IRQ_SWITCH def_bool n +config MAY_HAVE_SMP + def_bool n + menu "Processor type and features" choice @@ -105,6 +107,39 @@ config XTENSA_UNALIGNED_USER source "kernel/Kconfig.preempt" +config HAVE_SMP + bool "System Supports SMP (MX)" + depends on MAY_HAVE_SMP + select XTENSA_MX + help + This option is use to indicate that the system-on-a-chip (SOC) + supports Multiprocessing. Multiprocessor support implemented above + the CPU core definition and currently needs to be selected manually. + + Multiprocessor support in implemented with external cache and + interrupt controlers. + + The MX interrupt distributer adds Interprocessor Interrupts + and causes the IRQ numbers to be increased by 4 for devices + like the open cores ethernet driver and the serial interface. + + You still have to select "Enable SMP" to enable SMP on this SOC. + +config SMP + bool "Enable Symmetric multi-processing support" + depends on HAVE_SMP + select USE_GENERIC_SMP_HELPERS + select GENERIC_SMP_IDLE_THREAD + help + Enabled SMP Software; allows more than one CPU/CORE + to be activated during startup. + +config NR_CPUS + depends on SMP + int "Maximum number of CPUs (2-32)" + range 2 32 + default "4" + config MATH_EMULATION bool "Math emulation" help diff --git a/arch/xtensa/include/asm/barrier.h b/arch/xtensa/include/asm/barrier.h index ef02167..8e5e5c9 100644 --- a/arch/xtensa/include/asm/barrier.h +++ b/arch/xtensa/include/asm/barrier.h @@ -17,7 +17,9 @@ #define wmb() mb() #ifdef CONFIG_SMP -#error smp_* not defined +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() #else #define smp_mb() barrier() #define smp_rmb() barrier() diff --git a/arch/xtensa/include/asm/bitops.h b/arch/xtensa/include/asm/bitops.h index 84afe58d..7b6873a 100644 --- a/arch/xtensa/include/asm/bitops.h +++ b/arch/xtensa/include/asm/bitops.h @@ -22,12 +22,8 @@ #include #include -#ifdef CONFIG_SMP -# error SMP not supported on this architecture -#endif - -#define smp_mb__before_clear_bit() barrier() -#define smp_mb__after_clear_bit() barrier() +#define smp_mb__before_clear_bit() smp_mb() +#define smp_mb__after_clear_bit() smp_mb() #include diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h index 127cd48..555a98a 100644 --- a/arch/xtensa/include/asm/cacheflush.h +++ b/arch/xtensa/include/asm/cacheflush.h @@ -1,18 +1,14 @@ /* - * include/asm-xtensa/cacheflush.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * (C) 2001 - 2007 Tensilica Inc. + * (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_CACHEFLUSH_H #define _XTENSA_CACHEFLUSH_H -#ifdef __KERNEL__ - #include #include #include @@ -51,7 +47,6 @@ extern void __invalidate_icache_page(unsigned long); extern void __invalidate_icache_range(unsigned long, unsigned long); extern void __invalidate_dcache_range(unsigned long, unsigned long); - #if XCHAL_DCACHE_IS_WRITEBACK extern void __flush_invalidate_dcache_all(void); extern void __flush_dcache_page(unsigned long); @@ -87,9 +82,22 @@ static inline void __invalidate_icache_page_alias(unsigned long virt, * (see also Documentation/cachetlb.txt) */ -#if (DCACHE_WAY_SIZE > PAGE_SIZE) +#if (DCACHE_WAY_SIZE > PAGE_SIZE) || defined(CONFIG_SMP) + +#ifdef CONFIG_SMP +void flush_cache_all(void); +void flush_cache_range(struct vm_area_struct*, ulong, ulong); +void flush_icache_range(unsigned long start, unsigned long end); +void flush_cache_page(struct vm_area_struct*, + unsigned long, unsigned long); +#else +#define flush_cache_all local_flush_cache_all +#define flush_cache_range local_flush_cache_range +#define flush_icache_range local_flush_icache_range +#define flush_cache_page local_flush_cache_page +#endif -#define flush_cache_all() \ +#define local_flush_cache_all() \ do { \ __flush_invalidate_dcache_all(); \ __invalidate_icache_all(); \ @@ -103,9 +111,11 @@ static inline void __invalidate_icache_page_alias(unsigned long virt, #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 extern void flush_dcache_page(struct page*); -extern void flush_cache_range(struct vm_area_struct*, ulong, ulong); -extern void flush_cache_page(struct vm_area_struct*, - unsigned long, unsigned long); + +void local_flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); +void local_flush_cache_page(struct vm_area_struct *vma, + unsigned long address, unsigned long pfn); #else @@ -119,13 +129,14 @@ extern void flush_cache_page(struct vm_area_struct*, #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 #define flush_dcache_page(page) do { } while (0) -#define flush_cache_page(vma,addr,pfn) do { } while (0) -#define flush_cache_range(vma,start,end) do { } while (0) +#define flush_icache_range local_flush_icache_range +#define flush_cache_page(vma, addr, pfn) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) #endif /* Ensure consistency between data and instruction cache. */ -#define flush_icache_range(start,end) \ +#define local_flush_icache_range(start, end) \ do { \ __flush_dcache_range(start, (end) - (start)); \ __invalidate_icache_range(start,(end) - (start)); \ @@ -253,5 +264,4 @@ static inline void flush_invalidate_dcache_unaligned(u32 addr, u32 size) } } -#endif /* __KERNEL__ */ #endif /* _XTENSA_CACHEFLUSH_H */ diff --git a/arch/xtensa/include/asm/mmu.h b/arch/xtensa/include/asm/mmu.h index 8554b2c..71afe41 100644 --- a/arch/xtensa/include/asm/mmu.h +++ b/arch/xtensa/include/asm/mmu.h @@ -1,11 +1,9 @@ /* - * include/asm-xtensa/mmu.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_MMU_H @@ -15,8 +13,10 @@ #include #else -/* Default "unsigned long" context */ -typedef unsigned long mm_context_t; +typedef struct { + unsigned long asid[NR_CPUS]; + unsigned int cpu; +} mm_context_t; #endif /* CONFIG_MMU */ #endif /* _XTENSA_MMU_H */ diff --git a/arch/xtensa/include/asm/mmu_context.h b/arch/xtensa/include/asm/mmu_context.h index 86292c2..d33c71a 100644 --- a/arch/xtensa/include/asm/mmu_context.h +++ b/arch/xtensa/include/asm/mmu_context.h @@ -1,13 +1,11 @@ /* - * include/asm-xtensa/mmu_context.h - * * Switch an MMU context. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_MMU_CONTEXT_H @@ -20,22 +18,25 @@ #include #include -#include +#include #include #include #include #include +#include #if (XCHAL_HAVE_TLBS != 1) # error "Linux must have an MMU!" #endif -extern unsigned long asid_cache; +DECLARE_PER_CPU(unsigned long, asid_cache); +#define cpu_asid_cache(cpu) per_cpu(asid_cache, cpu) /* * NO_CONTEXT is the invalid ASID value that we don't ever assign to - * any user or kernel context. + * any user or kernel context. We use the reserved values in the + * ASID_INSERT macro below. * * 0 invalid * 1 kernel @@ -68,64 +69,77 @@ static inline unsigned long get_rasid_register (void) return tmp; } -static inline void -__get_new_mmu_context(struct mm_struct *mm) +static inline void get_new_mmu_context(struct mm_struct *mm, unsigned int cpu) +{ + unsigned long asid = cpu_asid_cache(cpu); + if ((++asid & ASID_MASK) == 0) { + /* + * Start new asid cycle; continue counting with next + * incarnation bits; skipping over 0, 1, 2, 3. + */ + local_flush_tlb_all(); + asid += ASID_USER_FIRST; + } + cpu_asid_cache(cpu) = asid; + mm->context.asid[cpu] = asid; + mm->context.cpu = cpu; +} + +static inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu) { - extern void flush_tlb_all(void); - if (! (++asid_cache & ASID_MASK) ) { - flush_tlb_all(); /* start new asid cycle */ - asid_cache += ASID_USER_FIRST; + /* + * Check if our ASID is of an older version and thus invalid. + */ + + if (mm) { + unsigned long asid = mm->context.asid[cpu]; + + if (asid == NO_CONTEXT || + ((asid ^ cpu_asid_cache(cpu)) & ~ASID_MASK)) + get_new_mmu_context(mm, cpu); } - mm->context = asid_cache; } -static inline void -__load_mmu_context(struct mm_struct *mm) +static inline void activate_context(struct mm_struct *mm, unsigned int cpu) { - set_rasid_register(ASID_INSERT(mm->context)); + get_mmu_context(mm, cpu); + set_rasid_register(ASID_INSERT(mm->context.asid[cpu])); invalidate_page_directory(); } /* * Initialize the context related info for a new mm_struct - * instance. + * instance. Valid cpu values are 0..(NR_CPUS-1), so initializing + * to -1 says the process has never run on any core. */ -static inline int -init_new_context(struct task_struct *tsk, struct mm_struct *mm) +static inline int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) { - mm->context = NO_CONTEXT; + int cpu; + for_each_possible_cpu(cpu) { + mm->context.asid[cpu] = NO_CONTEXT; + } + mm->context.cpu = -1; return 0; } -/* - * After we have set current->mm to a new value, this activates - * the context for the new mm so we see the new mappings. - */ -static inline void -activate_mm(struct mm_struct *prev, struct mm_struct *next) -{ - /* Unconditionally get a new ASID. */ - - __get_new_mmu_context(next); - __load_mmu_context(next); -} - - static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - unsigned long asid = asid_cache; - - /* Check if our ASID is of an older version and thus invalid */ - - if (next->context == NO_CONTEXT || ((next->context^asid) & ~ASID_MASK)) - __get_new_mmu_context(next); - - __load_mmu_context(next); + unsigned int cpu = smp_processor_id(); + int migrated = next->context.cpu != cpu; + /* Flush the icache if we migrated to a new core. */ + if (migrated) { + __invalidate_icache_all(); + next->context.cpu = cpu; + } + if (migrated || prev != next) + activate_context(next, cpu); } -#define deactivate_mm(tsk, mm) do { } while(0) +#define activate_mm(prev, next) switch_mm((prev), (next), NULL) +#define deactivate_mm(tsk, mm) do { } while (0) /* * Destroy context related info for an mm_struct that is about diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h index 81f31bc..598e752 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -59,9 +59,17 @@ struct pt_regs { (task_stack_page(tsk) + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4) - 1) # define user_mode(regs) (((regs)->ps & 0x00000020)!=0) # define instruction_pointer(regs) ((regs)->pc) +# define return_pointer(regs) (MAKE_PC_FROM_RA((regs)->areg[0], \ + (regs)->areg[1])) # ifndef CONFIG_SMP # define profile_pc(regs) instruction_pointer(regs) +# else +# define profile_pc(regs) \ + ({ \ + in_lock_functions(instruction_pointer(regs)) ? \ + return_pointer(regs) : instruction_pointer(regs); \ + }) # endif #define user_stack_pointer(regs) ((regs)->areg[1]) diff --git a/arch/xtensa/include/asm/smp.h b/arch/xtensa/include/asm/smp.h index 83c569e..30ac58c 100644 --- a/arch/xtensa/include/asm/smp.h +++ b/arch/xtensa/include/asm/smp.h @@ -1,27 +1,34 @@ /* - * include/asm-xtensa/smp.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_SMP_H #define _XTENSA_SMP_H -extern struct xtensa_cpuinfo boot_cpu_data; +#ifdef CONFIG_SMP -#define cpu_data (&boot_cpu_data) -#define current_cpu_data boot_cpu_data +#define raw_smp_processor_id() (current_thread_info()->cpu) +#define cpu_logical_map(cpu) (cpu) -struct xtensa_cpuinfo { - unsigned long *pgd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; +struct start_info { + unsigned long stack; }; +extern struct start_info start_info; -#define cpu_logical_map(cpu) (cpu) +struct cpumask; +void arch_send_call_function_ipi_mask(const struct cpumask *mask); +void arch_send_call_function_single_ipi(int cpu); + +void smp_init_cpus(void); +void secondary_init_irq(void); +void ipi_init(void); +struct seq_file; +void show_ipi_list(struct seq_file *p, int prec); + +#endif /* CONFIG_SMP */ #endif /* _XTENSA_SMP_H */ diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h index 27fa3c1..ca929e6 100644 --- a/arch/xtensa/include/asm/timex.h +++ b/arch/xtensa/include/asm/timex.h @@ -1,18 +1,14 @@ /* - * include/asm-xtensa/timex.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2008 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_TIMEX_H #define _XTENSA_TIMEX_H -#ifdef __KERNEL__ - #include #include @@ -39,14 +35,9 @@ extern unsigned long ccount_freq; typedef unsigned long long cycles_t; -/* - * Only used for SMP. - */ - -extern cycles_t cacheflush_time; - #define get_cycles() (0) +void local_timer_setup(unsigned cpu); /* * Register access. @@ -81,5 +72,4 @@ static inline void set_linux_timer (unsigned long ccompare) WSR_CCOMPARE(LINUX_TIMER, ccompare); } -#endif /* __KERNEL__ */ #endif /* _XTENSA_TIMEX_H */ diff --git a/arch/xtensa/include/asm/tlbflush.h b/arch/xtensa/include/asm/tlbflush.h index 43dd348..fc34274 100644 --- a/arch/xtensa/include/asm/tlbflush.h +++ b/arch/xtensa/include/asm/tlbflush.h @@ -1,18 +1,14 @@ /* - * include/asm-xtensa/tlbflush.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_TLBFLUSH_H #define _XTENSA_TLBFLUSH_H -#ifdef __KERNEL__ - #include #include @@ -34,12 +30,37 @@ * - flush_tlb_range(mm, start, end) flushes a range of pages */ -extern void flush_tlb_all(void); -extern void flush_tlb_mm(struct mm_struct*); -extern void flush_tlb_page(struct vm_area_struct*,unsigned long); -extern void flush_tlb_range(struct vm_area_struct*,unsigned long,unsigned long); +void local_flush_tlb_all(void); +void local_flush_tlb_mm(struct mm_struct *mm); +void local_flush_tlb_page(struct vm_area_struct *vma, + unsigned long page); +void local_flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); + +#ifdef CONFIG_SMP + +void flush_tlb_all(void); +void flush_tlb_mm(struct mm_struct *); +void flush_tlb_page(struct vm_area_struct *, unsigned long); +void flush_tlb_range(struct vm_area_struct *, unsigned long, + unsigned long); + +static inline void flush_tlb_kernel_range(unsigned long start, + unsigned long end) +{ + flush_tlb_all(); +} + +#else /* !CONFIG_SMP */ + +#define flush_tlb_all() local_flush_tlb_all() +#define flush_tlb_mm(mm) local_flush_tlb_mm(mm) +#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page) +#define flush_tlb_range(vma, vmaddr, end) local_flush_tlb_range(vma, vmaddr, \ + end) +#define flush_tlb_kernel_range(start, end) local_flush_tlb_all() -#define flush_tlb_kernel_range(start,end) flush_tlb_all() +#endif /* CONFIG_SMP */ /* TLB operations. */ @@ -187,5 +208,4 @@ static inline unsigned long read_itlb_translation (int way) } #endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ #endif /* _XTENSA_TLBFLUSH_H */ diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h index 917488a..8c194f6 100644 --- a/arch/xtensa/include/asm/traps.h +++ b/arch/xtensa/include/asm/traps.h @@ -19,6 +19,7 @@ */ extern void * __init trap_set_handler(int cause, void *handler); extern void do_unhandled(struct pt_regs *regs, unsigned long exccause); +void secondary_trap_init(void); static inline void spill_registers(void) { diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index f90265e..18d962a 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_KGDB) += xtensa-stub.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o +obj-$(CONFIG_SMP) += smp.o mxhead.o AFLAGS_head.o += -mtext-section-literals diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 7d740eb..74ec62c 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,7 @@ ENTRY(_start) /* Preserve the pointer to the boot parameter list in EXCSAVE_1 */ wsr a2, excsave1 - _j _SetupMMU + _j _SetupOCD .align 4 .literal_position @@ -62,6 +63,23 @@ ENTRY(_start) .word _startup .align 4 +_SetupOCD: + /* + * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions). + * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow + * xt-gdb to single step via DEBUG exceptions received directly + * by ocd. + */ + movi a1, 1 + movi a0, 0 + wsr a1, windowstart + wsr a0, windowbase + rsync + + movi a1, LOCKLEVEL + wsr a1, ps + rsync + .global _SetupMMU _SetupMMU: Offset = _SetupMMU - _start @@ -90,19 +108,6 @@ ENDPROC(_start) ENTRY(_startup) - /* Disable interrupts and exceptions. */ - - movi a0, LOCKLEVEL - wsr a0, ps - - /* Start with a fresh windowbase and windowstart. */ - - movi a1, 1 - movi a0, 0 - wsr a1, windowstart - wsr a0, windowbase - rsync - /* Set a0 to 0 for the remaining initialization. */ movi a0, 0 @@ -154,17 +159,6 @@ ENTRY(_startup) wsr a0, cpenable #endif - /* Set PS.INTLEVEL=LOCKLEVEL, PS.WOE=0, kernel stack, PS.EXCM=0 - * - * Note: PS.EXCM must be cleared before using any loop - * instructions; otherwise, they are silently disabled, and - * at most one iteration of the loop is executed. - */ - - movi a1, LOCKLEVEL - wsr a1, ps - rsync - /* Initialize the caches. * a2, a3 are just working registers (clobbered). */ @@ -182,6 +176,37 @@ ENTRY(_startup) isync +#ifdef CONFIG_HAVE_SMP + movi a2, CCON # MX External Register to Configure Cache + movi a3, 1 + wer a3, a2 +#endif + + /* Setup stack and enable window exceptions (keep irqs disabled) */ + + movi a1, start_info + l32i a1, a1, 0 + + movi a2, (1 << PS_WOE_BIT) | LOCKLEVEL + # WOE=1, INTLEVEL=LOCKLEVEL, UM=0 + wsr a2, ps # (enable reg-windows; progmode stack) + rsync + + /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/ + + movi a2, debug_exception + wsr a2, SREG_EXCSAVE + XCHAL_DEBUGLEVEL + +#ifdef CONFIG_SMP + /* + * Notice that we assume with SMP that cores have PRID + * supported by the cores. + */ + rsr a2, prid + bnez a2, .Lboot_secondary + +#endif /* CONFIG_SMP */ + /* Unpack data sections * * The linker script used to build the Linux kernel image @@ -234,24 +259,7 @@ ENTRY(_startup) ___invalidate_icache_all a2 a3 isync - /* Setup stack and enable window exceptions (keep irqs disabled) */ - - movi a1, init_thread_union - addi a1, a1, KERNEL_STACK_SIZE - - movi a2, (1 << PS_WOE_BIT) | LOCKLEVEL - # WOE=1, INTLEVEL=LOCKLEVEL, UM=0 - wsr a2, ps # (enable reg-windows; progmode stack) - rsync - - /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/ - - movi a2, debug_exception - wsr a2, SREG_EXCSAVE + XCHAL_DEBUGLEVEL - - /* Set up EXCSAVE[1] to point to the exc_table. */ - - movi a6, exc_table + movi a6, 0 xsr a6, excsave1 /* init_arch kick-starts the linux kernel */ @@ -265,9 +273,45 @@ ENTRY(_startup) should_never_return: j should_never_return +#ifdef CONFIG_SMP +.Lboot_secondary: + + movi a2, cpu_start_ccount +1: + l32i a3, a2, 0 + beqi a3, 0, 1b + movi a3, 0 + s32i a3, a2, 0 + memw +1: + l32i a3, a2, 0 + beqi a3, 0, 1b + wsr a3, ccount + movi a3, 0 + s32i a3, a2, 0 + memw + + movi a6, 0 + wsr a6, excsave1 + + movi a4, secondary_start_kernel + callx4 a4 + j should_never_return + +#endif /* CONFIG_SMP */ + ENDPROC(_startup) /* + * DATA section + */ + + .section ".data.init.refok" + .align 4 +ENTRY(start_info) + .long init_thread_union + KERNEL_STACK_SIZE + +/* * BSS section */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 7d49730..fad9e00 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -4,7 +4,7 @@ * Xtensa built-in interrupt controller and some generic functions copied * from i386. * - * Copyright (C) 2002 - 2006 Tensilica, Inc. + * Copyright (C) 2002 - 2013 Tensilica, Inc. * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar * * @@ -19,10 +19,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -55,6 +57,9 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) int arch_show_interrupts(struct seq_file *p, int prec) { +#ifdef CONFIG_SMP + show_ipi_list(p, prec); +#endif seq_printf(p, "%*s: ", prec, "ERR"); seq_printf(p, "%10u\n", atomic_read(&irq_err_count)); return 0; @@ -136,7 +141,15 @@ void __init init_IRQ(void) #ifdef CONFIG_OF irqchip_init(); #else +#ifdef CONFIG_HAVE_SMP + xtensa_mx_init_legacy(NULL); +#else xtensa_pic_init_legacy(NULL); #endif +#endif + +#ifdef CONFIG_SMP + ipi_init(); +#endif variant_init_irq(); } diff --git a/arch/xtensa/kernel/mxhead.S b/arch/xtensa/kernel/mxhead.S new file mode 100644 index 0000000..77a161a --- /dev/null +++ b/arch/xtensa/kernel/mxhead.S @@ -0,0 +1,85 @@ +/* + * Xtensa Secondary Processors startup code. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 - 2013 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Marc Gauthier + * Pete Delaney + */ + +#include + +#include +#include +#include +#include + + + .section .SecondaryResetVector.text, "ax" + + +ENTRY(_SecondaryResetVector) + _j _SetupOCD + + .begin no-absolute-literals + .literal_position + +_SetupOCD: + /* + * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions). + * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow + * xt-gdb to single step via DEBUG exceptions received directly + * by ocd. + */ + movi a1, 1 + movi a0, 0 + wsr a1, windowstart + wsr a0, windowbase + rsync + + movi a1, LOCKLEVEL + wsr a1, ps + rsync + +_SetupMMU: + Offset = _SetupMMU - _SecondaryResetVector + +#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX + initialize_mmu +#endif + + /* + * Start Secondary Processors with NULL pointer to boot params. + */ + movi a2, 0 # a2 == NULL + movi a3, _startup + jx a3 + + .end no-absolute-literals + + + .section .SecondaryResetVector.remapped_text, "ax" + .global _RemappedSecondaryResetVector + + .org 0 # Need to do org before literals + +_RemappedSecondaryResetVector: + .begin no-absolute-literals + .literal_position + + _j _RemappedSetupMMU + . = _RemappedSecondaryResetVector + Offset + +_RemappedSetupMMU: + +#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX + initialize_mmu +#endif + + .end no-absolute-literals diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 3d35314..dfd8f52 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -46,6 +48,7 @@ #include #include #include +#include #include @@ -496,6 +499,10 @@ void __init setup_arch(char **cmdline_p) platform_setup(cmdline_p); +#ifdef CONFIG_SMP + smp_init_cpus(); +#endif + paging_init(); zones_init(); @@ -512,6 +519,21 @@ void __init setup_arch(char **cmdline_p) #endif } +static DEFINE_PER_CPU(struct cpu, cpu_data); + +static int __init topology_init(void) +{ + int i; + + for_each_possible_cpu(i) { + struct cpu *cpu = &per_cpu(cpu_data, i); + register_cpu(cpu, i); + } + + return 0; +} +subsys_initcall(topology_init); + void machine_restart(char * cmd) { platform_restart(); @@ -537,21 +559,27 @@ void machine_power_off(void) static int c_show(struct seq_file *f, void *slot) { + char buf[NR_CPUS * 5]; + + cpulist_scnprintf(buf, sizeof(buf), cpu_online_mask); /* high-level stuff */ - seq_printf(f,"processor\t: 0\n" - "vendor_id\t: Tensilica\n" - "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n" - "core ID\t\t: " XCHAL_CORE_ID "\n" - "build ID\t: 0x%x\n" - "byte order\t: %s\n" - "cpu MHz\t\t: %lu.%02lu\n" - "bogomips\t: %lu.%02lu\n", - XCHAL_BUILD_UNIQUE_ID, - XCHAL_HAVE_BE ? "big" : "little", - ccount_freq/1000000, - (ccount_freq/10000) % 100, - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); + seq_printf(f, "CPU count\t: %u\n" + "CPU list\t: %s\n" + "vendor_id\t: Tensilica\n" + "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n" + "core ID\t\t: " XCHAL_CORE_ID "\n" + "build ID\t: 0x%x\n" + "byte order\t: %s\n" + "cpu MHz\t\t: %lu.%02lu\n" + "bogomips\t: %lu.%02lu\n", + num_online_cpus(), + buf, + XCHAL_BUILD_UNIQUE_ID, + XCHAL_HAVE_BE ? "big" : "little", + ccount_freq/1000000, + (ccount_freq/10000) % 100, + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); seq_printf(f,"flags\t\t: " #if XCHAL_HAVE_NMI @@ -663,7 +691,7 @@ c_show(struct seq_file *f, void *slot) static void * c_start(struct seq_file *f, loff_t *pos) { - return (void *) ((*pos == 0) ? (void *)1 : NULL); + return (*pos == 0) ? (void *)1 : NULL; } static void * @@ -679,10 +707,10 @@ c_stop(struct seq_file *f, void *v) const struct seq_operations cpuinfo_op = { - start: c_start, - next: c_next, - stop: c_stop, - show: c_show + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show, }; #endif /* CONFIG_PROC_FS */ diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c new file mode 100644 index 0000000..46bdd14 --- /dev/null +++ b/arch/xtensa/kernel/smp.c @@ -0,0 +1,465 @@ +/* + * Xtensa SMP support functions. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 - 2013 Tensilica Inc. + * + * Chris Zankel + * Joe Taylor + * Pete Delaney +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SMP +# if XCHAL_HAVE_S32C1I == 0 +# error "The S32C1I option is required for SMP." +# endif +#endif + +/* IPI (Inter Process Interrupt) */ + +#define IPI_IRQ 0 + +static irqreturn_t ipi_interrupt(int irq, void *dev_id); +static struct irqaction ipi_irqaction = { + .handler = ipi_interrupt, + .flags = IRQF_PERCPU, + .name = "ipi", +}; + +void ipi_init(void) +{ + unsigned irq = irq_create_mapping(NULL, IPI_IRQ); + setup_irq(irq, &ipi_irqaction); +} + +static inline unsigned int get_core_count(void) +{ + /* Bits 18..21 of SYSCFGID contain the core count minus 1. */ + unsigned int syscfgid = get_er(SYSCFGID); + return ((syscfgid >> 18) & 0xf) + 1; +} + +static inline int get_core_id(void) +{ + /* Bits 0...18 of SYSCFGID contain the core id */ + unsigned int core_id = get_er(SYSCFGID); + return core_id & 0x3fff; +} + +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + unsigned i; + + for (i = 0; i < max_cpus; ++i) + set_cpu_present(i, true); +} + +void __init smp_init_cpus(void) +{ + unsigned i; + unsigned int ncpus = get_core_count(); + unsigned int core_id = get_core_id(); + + pr_info("%s: Core Count = %d\n", __func__, ncpus); + pr_info("%s: Core Id = %d\n", __func__, core_id); + + for (i = 0; i < ncpus; ++i) + set_cpu_possible(i, true); +} + +void __init smp_prepare_boot_cpu(void) +{ + unsigned int cpu = smp_processor_id(); + BUG_ON(cpu != 0); + cpu_asid_cache(cpu) = ASID_USER_FIRST; +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ +} + +static int boot_secondary_processors = 1; /* Set with xt-gdb via .xt-gdb */ +static DECLARE_COMPLETION(cpu_running); + +void __init secondary_start_kernel(void) +{ + struct mm_struct *mm = &init_mm; + unsigned int cpu = smp_processor_id(); + + init_mmu(); + +#ifdef CONFIG_DEBUG_KERNEL + if (boot_secondary_processors == 0) { + pr_debug("%s: boot_secondary_processors:%d; Hanging cpu:%d\n", + __func__, boot_secondary_processors, cpu); + for (;;) + __asm__ __volatile__ ("waiti " __stringify(LOCKLEVEL)); + } + + pr_debug("%s: boot_secondary_processors:%d; Booting cpu:%d\n", + __func__, boot_secondary_processors, cpu); +#endif + /* Init EXCSAVE1 */ + + secondary_trap_init(); + + /* All kernel threads share the same mm context. */ + + atomic_inc(&mm->mm_users); + atomic_inc(&mm->mm_count); + current->active_mm = mm; + cpumask_set_cpu(cpu, mm_cpumask(mm)); + enter_lazy_tlb(mm, current); + + preempt_disable(); + trace_hardirqs_off(); + + calibrate_delay(); + + notify_cpu_starting(cpu); + + secondary_init_irq(); + local_timer_setup(cpu); + + local_irq_enable(); + + set_cpu_online(cpu, true); + complete(&cpu_running); + + cpu_startup_entry(CPUHP_ONLINE); +} + +static void mx_cpu_start(void *p) +{ + unsigned cpu = (unsigned)p; + unsigned long run_stall_mask = get_er(MPSCORE); + + set_er(run_stall_mask & ~(1u << cpu), MPSCORE); + pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n", + __func__, cpu, run_stall_mask, get_er(MPSCORE)); +} + +static void mx_cpu_stop(void *p) +{ + unsigned cpu = (unsigned)p; + unsigned long run_stall_mask = get_er(MPSCORE); + + set_er(run_stall_mask | (1u << cpu), MPSCORE); + pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n", + __func__, cpu, run_stall_mask, get_er(MPSCORE)); +} + +unsigned long cpu_start_ccount; + +static int boot_secondary(unsigned int cpu, struct task_struct *ts) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + unsigned long ccount; + int i; + + smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1); + + for (i = 0; i < 2; ++i) { + do + ccount = get_ccount(); + while (!ccount); + + cpu_start_ccount = ccount; + + while (time_before(jiffies, timeout)) { + mb(); + if (!cpu_start_ccount) + break; + } + + if (cpu_start_ccount) { + smp_call_function_single(0, mx_cpu_stop, + (void *)cpu, 1); + cpu_start_ccount = 0; + return -EIO; + } + } + return 0; +} + +int __cpu_up(unsigned int cpu, struct task_struct *idle) +{ + int ret = 0; + + if (cpu_asid_cache(cpu) == 0) + cpu_asid_cache(cpu) = ASID_USER_FIRST; + + start_info.stack = (unsigned long)task_pt_regs(idle); + wmb(); + + pr_debug("%s: Calling wakeup_secondary(cpu:%d, idle:%p, sp: %08lx)\n", + __func__, cpu, idle, start_info.stack); + + ret = boot_secondary(cpu, idle); + if (ret == 0) { + wait_for_completion_timeout(&cpu_running, + msecs_to_jiffies(1000)); + if (!cpu_online(cpu)) + ret = -EIO; + } + + if (ret) + pr_err("CPU %u failed to boot\n", cpu); + + return ret; +} + +enum ipi_msg_type { + IPI_RESCHEDULE = 0, + IPI_CALL_FUNC, + IPI_CPU_STOP, + IPI_MAX +}; + +static const struct { + const char *short_text; + const char *long_text; +} ipi_text[] = { + { .short_text = "RES", .long_text = "Rescheduling interrupts" }, + { .short_text = "CAL", .long_text = "Function call interrupts" }, + { .short_text = "DIE", .long_text = "CPU shutdown interrupts" }, +}; + +struct ipi_data { + unsigned long ipi_count[IPI_MAX]; +}; + +static DEFINE_PER_CPU(struct ipi_data, ipi_data); + +static void send_ipi_message(const struct cpumask *callmask, + enum ipi_msg_type msg_id) +{ + int index; + unsigned long mask = 0; + + for_each_cpu(index, callmask) + if (index != smp_processor_id()) + mask |= 1 << index; + + set_er(mask, MIPISET(msg_id)); +} + +void arch_send_call_function_ipi_mask(const struct cpumask *mask) +{ + send_ipi_message(mask, IPI_CALL_FUNC); +} + +void arch_send_call_function_single_ipi(int cpu) +{ + send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC); +} + +void smp_send_reschedule(int cpu) +{ + send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); +} + +void smp_send_stop(void) +{ + struct cpumask targets; + + cpumask_copy(&targets, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &targets); + send_ipi_message(&targets, IPI_CPU_STOP); +} + +static void ipi_cpu_stop(unsigned int cpu) +{ + set_cpu_online(cpu, false); + machine_halt(); +} + +irqreturn_t ipi_interrupt(int irq, void *dev_id) +{ + unsigned int cpu = smp_processor_id(); + struct ipi_data *ipi = &per_cpu(ipi_data, cpu); + unsigned int msg; + unsigned i; + + msg = get_er(MIPICAUSE(cpu)); + for (i = 0; i < IPI_MAX; i++) + if (msg & (1 << i)) { + set_er(1 << i, MIPICAUSE(cpu)); + ++ipi->ipi_count[i]; + } + + if (msg & (1 << IPI_RESCHEDULE)) + scheduler_ipi(); + if (msg & (1 << IPI_CALL_FUNC)) + generic_smp_call_function_interrupt(); + if (msg & (1 << IPI_CPU_STOP)) + ipi_cpu_stop(cpu); + + return IRQ_HANDLED; +} + +void show_ipi_list(struct seq_file *p, int prec) +{ + unsigned int cpu; + unsigned i; + + for (i = 0; i < IPI_MAX; ++i) { + seq_printf(p, "%*s:", prec, ipi_text[i].short_text); + for_each_online_cpu(cpu) + seq_printf(p, " %10lu", + per_cpu(ipi_data, cpu).ipi_count[i]); + seq_printf(p, " %s\n", ipi_text[i].long_text); + } +} + +int setup_profiling_timer(unsigned int multiplier) +{ + pr_debug("setup_profiling_timer %d\n", multiplier); + return 0; +} + +/* TLB flush functions */ + +struct flush_data { + struct vm_area_struct *vma; + unsigned long addr1; + unsigned long addr2; +}; + +static void ipi_flush_tlb_all(void *arg) +{ + local_flush_tlb_all(); +} + +void flush_tlb_all(void) +{ + on_each_cpu(ipi_flush_tlb_all, NULL, 1); +} + +static void ipi_flush_tlb_mm(void *arg) +{ + local_flush_tlb_mm(arg); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + on_each_cpu(ipi_flush_tlb_mm, mm, 1); +} + +static void ipi_flush_tlb_page(void *arg) +{ + struct flush_data *fd = arg; + local_flush_tlb_page(fd->vma, fd->addr1); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) +{ + struct flush_data fd = { + .vma = vma, + .addr1 = addr, + }; + on_each_cpu(ipi_flush_tlb_page, &fd, 1); +} + +static void ipi_flush_tlb_range(void *arg) +{ + struct flush_data *fd = arg; + local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2); +} + +void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct flush_data fd = { + .vma = vma, + .addr1 = start, + .addr2 = end, + }; + on_each_cpu(ipi_flush_tlb_range, &fd, 1); +} + +/* Cache flush functions */ + +static void ipi_flush_cache_all(void *arg) +{ + local_flush_cache_all(); +} + +void flush_cache_all(void) +{ + on_each_cpu(ipi_flush_cache_all, NULL, 1); +} + +static void ipi_flush_cache_page(void *arg) +{ + struct flush_data *fd = arg; + local_flush_cache_page(fd->vma, fd->addr1, fd->addr2); +} + +void flush_cache_page(struct vm_area_struct *vma, + unsigned long address, unsigned long pfn) +{ + struct flush_data fd = { + .vma = vma, + .addr1 = address, + .addr2 = pfn, + }; + on_each_cpu(ipi_flush_cache_page, &fd, 1); +} + +static void ipi_flush_cache_range(void *arg) +{ + struct flush_data *fd = arg; + local_flush_cache_range(fd->vma, fd->addr1, fd->addr2); +} + +void flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct flush_data fd = { + .vma = vma, + .addr1 = start, + .addr2 = end, + }; + on_each_cpu(ipi_flush_cache_range, &fd, 1); +} + +static void ipi_flush_icache_range(void *arg) +{ + struct flush_data *fd = arg; + local_flush_icache_range(fd->addr1, fd->addr2); +} + +void flush_icache_range(unsigned long start, unsigned long end) +{ + struct flush_data fd = { + .addr1 = start, + .addr2 = end, + }; + on_each_cpu(ipi_flush_icache_range, &fd, 1); +} diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 3dbe864..3c0ff57 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -157,7 +157,7 @@ COPROCESSOR(7), * 2. it is a temporary memory buffer for the exception handlers. */ -unsigned long exc_table[EXC_TABLE_SIZE/4]; +DEFINE_PER_CPU(unsigned long, exc_table[EXC_TABLE_SIZE/4]); void die(const char*, struct pt_regs*, long); @@ -313,17 +313,31 @@ do_debug(struct pt_regs *regs) } +static void set_handler(int idx, void *handler) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + per_cpu(exc_table, cpu)[idx] = (unsigned long)handler; +} + /* Set exception C handler - for temporary use when probing exceptions */ void * __init trap_set_handler(int cause, void *handler) { - unsigned long *entry = &exc_table[EXC_TABLE_DEFAULT / 4 + cause]; - void *previous = (void *)*entry; - *entry = (unsigned long)handler; + void *previous = (void *)per_cpu(exc_table, 0)[ + EXC_TABLE_DEFAULT / 4 + cause]; + set_handler(EXC_TABLE_DEFAULT / 4 + cause, handler); return previous; } +static void __init trap_init_excsave(void) +{ + unsigned long excsave1 = (unsigned long)this_cpu_ptr(exc_table); + __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1)); +} + /* * Initialize dispatch tables. * @@ -337,8 +351,6 @@ void * __init trap_set_handler(int cause, void *handler) * See vectors.S for more details. */ -#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler)) - void __init trap_init(void) { int i; @@ -368,10 +380,15 @@ void __init trap_init(void) } /* Initialize EXCSAVE_1 to hold the address of the exception table. */ + trap_init_excsave(); +} - i = (unsigned long)exc_table; - __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (i)); +#ifdef CONFIG_SMP +void __init secondary_trap_init(void) +{ + trap_init_excsave(); } +#endif /* * This function dumps the current valid window frame and other base registers. diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S index 21acd11..ee32c00 100644 --- a/arch/xtensa/kernel/vmlinux.lds.S +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -165,6 +165,13 @@ SECTIONS .DoubleExceptionVector.text); RELOCATE_ENTRY(_DebugInterruptVector_text, .DebugInterruptVector.text); +#if defined(CONFIG_SMP) + RELOCATE_ENTRY(_SecondaryResetVector_literal, + .SecondaryResetVector.literal); + RELOCATE_ENTRY(_SecondaryResetVector_text, + .SecondaryResetVector.text); +#endif + __boot_reloc_table_end = ABSOLUTE(.) ; @@ -272,6 +279,25 @@ SECTIONS .DoubleExceptionVector.literal) . = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3; + +#if defined(CONFIG_SMP) + + SECTION_VECTOR (_SecondaryResetVector_literal, + .SecondaryResetVector.literal, + RESET_VECTOR1_VADDR - 4, + SIZEOF(.DoubleExceptionVector.text), + .DoubleExceptionVector.text) + + SECTION_VECTOR (_SecondaryResetVector_text, + .SecondaryResetVector.text, + RESET_VECTOR1_VADDR, + 4, + .SecondaryResetVector.literal) + + . = LOADADDR(.SecondaryResetVector.text)+SIZEOF(.SecondaryResetVector.text); + +#endif + . = ALIGN(PAGE_SIZE); __init_end = .; diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index 81edeab..ba4c47f 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c @@ -118,7 +118,7 @@ void flush_dcache_page(struct page *page) * For now, flush the whole cache. FIXME?? */ -void flush_cache_range(struct vm_area_struct* vma, +void local_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { __flush_invalidate_dcache_all(); @@ -132,7 +132,7 @@ void flush_cache_range(struct vm_area_struct* vma, * alias versions of the cache flush functions. */ -void flush_cache_page(struct vm_area_struct* vma, unsigned long address, +void local_flush_cache_page(struct vm_area_struct *vma, unsigned long address, unsigned long pfn) { /* Note that we have to use the 'alias' address to avoid multi-hit */ @@ -159,8 +159,7 @@ update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep) /* Invalidate old entry in TLBs */ - invalidate_itlb_mapping(addr); - invalidate_dtlb_mapping(addr); + flush_tlb_page(vma, addr); #if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index 70fa7bc..b57c4f9 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -21,7 +21,7 @@ #include #include -unsigned long asid_cache = ASID_USER_FIRST; +DEFINE_PER_CPU(unsigned long, asid_cache) = ASID_USER_FIRST; void bad_page_fault(struct pt_regs*, unsigned long, int); #undef DEBUG_PAGE_FAULT diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c index c43771c..5bb8e3c 100644 --- a/arch/xtensa/mm/mmu.c +++ b/arch/xtensa/mm/mmu.c @@ -22,7 +22,7 @@ void __init paging_init(void) /* * Flush the mmu and reset associated register to default values. */ -void __init init_mmu(void) +void init_mmu(void) { #if !(XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY) /* @@ -37,7 +37,7 @@ void __init init_mmu(void) set_itlbcfg_register(0); set_dtlbcfg_register(0); #endif - flush_tlb_all(); + local_flush_tlb_all(); /* Set rasid register to a known value. */ diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index ca9d236..ade62382 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -48,7 +48,7 @@ static inline void __flush_dtlb_all (void) } -void flush_tlb_all (void) +void local_flush_tlb_all(void) { __flush_itlb_all(); __flush_dtlb_all(); @@ -60,19 +60,23 @@ void flush_tlb_all (void) * a new context will be assigned to it. */ -void flush_tlb_mm(struct mm_struct *mm) +void local_flush_tlb_mm(struct mm_struct *mm) { + int cpu = smp_processor_id(); + if (mm == current->active_mm) { unsigned long flags; local_irq_save(flags); - __get_new_mmu_context(mm); - __load_mmu_context(mm); + mm->context.asid[cpu] = NO_CONTEXT; + activate_context(mm, cpu); local_irq_restore(flags); + } else { + mm->context.asid[cpu] = NO_CONTEXT; + mm->context.cpu = -1; } - else - mm->context = 0; } + #define _ITLB_ENTRIES (ITLB_ARF_WAYS << XCHAL_ITLB_ARF_ENTRIES_LOG2) #define _DTLB_ENTRIES (DTLB_ARF_WAYS << XCHAL_DTLB_ARF_ENTRIES_LOG2) #if _ITLB_ENTRIES > _DTLB_ENTRIES @@ -81,24 +85,26 @@ void flush_tlb_mm(struct mm_struct *mm) # define _TLB_ENTRIES _DTLB_ENTRIES #endif -void flush_tlb_range (struct vm_area_struct *vma, - unsigned long start, unsigned long end) +void local_flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) { + int cpu = smp_processor_id(); struct mm_struct *mm = vma->vm_mm; unsigned long flags; - if (mm->context == NO_CONTEXT) + if (mm->context.asid[cpu] == NO_CONTEXT) return; #if 0 printk("[tlbrange<%02lx,%08lx,%08lx>]\n", - (unsigned long)mm->context, start, end); + (unsigned long)mm->context.asid[cpu], start, end); #endif local_irq_save(flags); if (end-start + (PAGE_SIZE-1) <= _TLB_ENTRIES << PAGE_SHIFT) { int oldpid = get_rasid_register(); - set_rasid_register (ASID_INSERT(mm->context)); + + set_rasid_register(ASID_INSERT(mm->context.asid[cpu])); start &= PAGE_MASK; if (vma->vm_flags & VM_EXEC) while(start < end) { @@ -114,24 +120,25 @@ void flush_tlb_range (struct vm_area_struct *vma, set_rasid_register(oldpid); } else { - flush_tlb_mm(mm); + local_flush_tlb_mm(mm); } local_irq_restore(flags); } -void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { + int cpu = smp_processor_id(); struct mm_struct* mm = vma->vm_mm; unsigned long flags; int oldpid; - if(mm->context == NO_CONTEXT) + if (mm->context.asid[cpu] == NO_CONTEXT) return; local_irq_save(flags); oldpid = get_rasid_register(); - set_rasid_register(ASID_INSERT(mm->context)); + set_rasid_register(ASID_INSERT(mm->context.asid[cpu])); if (vma->vm_flags & VM_EXEC) invalidate_itlb_mapping(page); diff --git a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h index 4b43ff1..aeb316b 100644 --- a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h +++ b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h @@ -26,7 +26,7 @@ /* Default assignment of LX60 devices to external interrupts. */ -#ifdef CONFIG_ARCH_HAS_SMP +#ifdef CONFIG_XTENSA_MX #define DUART16552_INTNUM XCHAL_EXTINT3_NUM #define OETH_IRQ XCHAL_EXTINT4_NUM #else -- cgit v0.10.2 From 49b424fedaf88d0fa9913082b8c1ccd012a8a972 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:28 +0400 Subject: xtensa: implement CPU hotplug Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 4b09c60..70a160b 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -140,6 +140,15 @@ config NR_CPUS range 2 32 default "4" +config HOTPLUG_CPU + bool "Enable CPU hotplug support" + depends on SMP + help + Say Y here to allow turning CPUs off and on. CPUs can be + controlled through /sys/devices/system/cpu. + + Say N if you want to disable CPU hotplug. + config MATH_EMULATION bool "Math emulation" help diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 7d194d4..f71f88e 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -45,6 +45,7 @@ static __inline__ int irq_canonicalize(int irq) struct irqaction; struct irq_domain; +void migrate_irqs(void); int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, unsigned long int_irq, unsigned long ext_irq, unsigned long *out_hwirq, unsigned int *out_type); diff --git a/arch/xtensa/include/asm/smp.h b/arch/xtensa/include/asm/smp.h index 30ac58c..4e43f56 100644 --- a/arch/xtensa/include/asm/smp.h +++ b/arch/xtensa/include/asm/smp.h @@ -29,6 +29,15 @@ void ipi_init(void); struct seq_file; void show_ipi_list(struct seq_file *p, int prec); +#ifdef CONFIG_HOTPLUG_CPU + +void __cpu_die(unsigned int cpu); +int __cpu_disable(void); +void cpu_die(void); +void cpu_restart(void); + +#endif /* CONFIG_HOTPLUG_CPU */ + #endif /* CONFIG_SMP */ #endif /* _XTENSA_SMP_H */ diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 74ec62c..aeeb3cc 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -103,7 +103,7 @@ _SetupMMU: ENDPROC(_start) - __INIT + __REF .literal_position ENTRY(_startup) @@ -302,6 +302,55 @@ should_never_return: ENDPROC(_startup) +#ifdef CONFIG_HOTPLUG_CPU + +ENTRY(cpu_restart) + +#if XCHAL_DCACHE_IS_WRITEBACK + ___flush_invalidate_dcache_all a2 a3 +#else + ___invalidate_dcache_all a2 a3 +#endif + memw + movi a2, CCON # MX External Register to Configure Cache + movi a3, 0 + wer a3, a2 + extw + + rsr a0, prid + neg a2, a0 + movi a3, cpu_start_id + s32i a2, a3, 0 +#if XCHAL_DCACHE_IS_WRITEBACK + dhwbi a3, 0 +#endif +1: + l32i a2, a3, 0 + dhi a3, 0 + bne a2, a0, 1b + + /* + * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions). + * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow + * xt-gdb to single step via DEBUG exceptions received directly + * by ocd. + */ + movi a1, 1 + movi a0, 0 + wsr a1, windowstart + wsr a0, windowbase + rsync + + movi a1, LOCKLEVEL + wsr a1, ps + rsync + + j _startup + +ENDPROC(cpu_restart) + +#endif /* CONFIG_HOTPLUG_CPU */ + /* * DATA section */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index fad9e00..482868a 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -153,3 +153,52 @@ void __init init_IRQ(void) #endif variant_init_irq(); } + +#ifdef CONFIG_HOTPLUG_CPU +static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct irq_chip *chip = irq_data_get_irq_chip(data); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + if (chip->irq_set_affinity) + chip->irq_set_affinity(data, cpumask_of(cpu), false); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * The CPU has been marked offline. Migrate IRQs off this CPU. If + * the affinity settings do not allow other CPUs, force them onto any + * available CPU. + */ +void migrate_irqs(void) +{ + unsigned int i, cpu = smp_processor_id(); + struct irq_desc *desc; + + for_each_irq_desc(i, desc) { + struct irq_data *data = irq_desc_get_irq_data(desc); + unsigned int newcpu; + + if (irqd_is_per_cpu(data)) + continue; + + if (!cpumask_test_cpu(cpu, data->affinity)) + continue; + + newcpu = cpumask_any_and(data->affinity, cpu_online_mask); + + if (newcpu >= nr_cpu_ids) { + pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n", + i, cpu); + + cpumask_setall(data->affinity); + newcpu = cpumask_any_and(data->affinity, + cpu_online_mask); + } + + route_irq(data, i, newcpu); + } +} +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index dfd8f52..d21bfa7 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -527,6 +527,7 @@ static int __init topology_init(void) for_each_possible_cpu(i) { struct cpu *cpu = &per_cpu(cpu_data, i); + cpu->hotpluggable = !!i; register_cpu(cpu, i); } diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c index 46bdd14..1c7a209 100644 --- a/arch/xtensa/kernel/smp.c +++ b/arch/xtensa/kernel/smp.c @@ -40,6 +40,11 @@ # endif #endif +static void system_invalidate_dcache_range(unsigned long start, + unsigned long size); +static void system_flush_invalidate_dcache_range(unsigned long start, + unsigned long size); + /* IPI (Inter Process Interrupt) */ #define IPI_IRQ 0 @@ -106,7 +111,7 @@ void __init smp_cpus_done(unsigned int max_cpus) static int boot_secondary_processors = 1; /* Set with xt-gdb via .xt-gdb */ static DECLARE_COMPLETION(cpu_running); -void __init secondary_start_kernel(void) +void secondary_start_kernel(void) { struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id(); @@ -174,6 +179,9 @@ static void mx_cpu_stop(void *p) __func__, cpu, run_stall_mask, get_er(MPSCORE)); } +#ifdef CONFIG_HOTPLUG_CPU +unsigned long cpu_start_id __cacheline_aligned; +#endif unsigned long cpu_start_ccount; static int boot_secondary(unsigned int cpu, struct task_struct *ts) @@ -182,6 +190,11 @@ static int boot_secondary(unsigned int cpu, struct task_struct *ts) unsigned long ccount; int i; +#ifdef CONFIG_HOTPLUG_CPU + cpu_start_id = cpu; + system_flush_invalidate_dcache_range( + (unsigned long)&cpu_start_id, sizeof(cpu_start_id)); +#endif smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1); for (i = 0; i < 2; ++i) { @@ -234,6 +247,85 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) return ret; } +#ifdef CONFIG_HOTPLUG_CPU + +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + + /* + * Take this CPU offline. Once we clear this, we can't return, + * and we must not schedule until we're ready to give up the cpu. + */ + set_cpu_online(cpu, false); + + /* + * OK - migrate IRQs away from this CPU + */ + migrate_irqs(); + + /* + * Flush user cache and TLB mappings, and then remove this CPU + * from the vm mask set of all processes. + */ + local_flush_cache_all(); + local_flush_tlb_all(); + invalidate_page_directory(); + + clear_tasks_mm_cpumask(cpu); + + return 0; +} + +static void platform_cpu_kill(unsigned int cpu) +{ + smp_call_function_single(0, mx_cpu_stop, (void *)cpu, true); +} + +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpu_die(unsigned int cpu) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + while (time_before(jiffies, timeout)) { + system_invalidate_dcache_range((unsigned long)&cpu_start_id, + sizeof(cpu_start_id)); + if (cpu_start_id == -cpu) { + platform_cpu_kill(cpu); + return; + } + } + pr_err("CPU%u: unable to kill\n", cpu); +} + +void arch_cpu_idle_dead(void) +{ + cpu_die(); +} +/* + * Called from the idle thread for the CPU which has been shutdown. + * + * Note that we disable IRQs here, but do not re-enable them + * before returning to the caller. This is also the behaviour + * of the other hotplug-cpu capable cores, so presumably coming + * out of idle fixes this. + */ +void __ref cpu_die(void) +{ + idle_task_exit(); + local_irq_disable(); + __asm__ __volatile__( + " movi a2, cpu_restart\n" + " jx a2\n"); +} + +#endif /* CONFIG_HOTPLUG_CPU */ + enum ipi_msg_type { IPI_RESCHEDULE = 0, IPI_CALL_FUNC, @@ -463,3 +555,37 @@ void flush_icache_range(unsigned long start, unsigned long end) }; on_each_cpu(ipi_flush_icache_range, &fd, 1); } + +/* ------------------------------------------------------------------------- */ + +static void ipi_invalidate_dcache_range(void *arg) +{ + struct flush_data *fd = arg; + __invalidate_dcache_range(fd->addr1, fd->addr2); +} + +static void system_invalidate_dcache_range(unsigned long start, + unsigned long size) +{ + struct flush_data fd = { + .addr1 = start, + .addr2 = size, + }; + on_each_cpu(ipi_invalidate_dcache_range, &fd, 1); +} + +static void ipi_flush_invalidate_dcache_range(void *arg) +{ + struct flush_data *fd = arg; + __flush_invalidate_dcache_range(fd->addr1, fd->addr2); +} + +static void system_flush_invalidate_dcache_range(unsigned long start, + unsigned long size) +{ + struct flush_data fd = { + .addr1 = start, + .addr2 = size, + }; + on_each_cpu(ipi_flush_invalidate_dcache_range, &fd, 1); +} diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 3c0ff57..eebbfd8 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -332,7 +332,7 @@ void * __init trap_set_handler(int cause, void *handler) } -static void __init trap_init_excsave(void) +static void trap_init_excsave(void) { unsigned long excsave1 = (unsigned long)this_cpu_ptr(exc_table); __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1)); @@ -384,7 +384,7 @@ void __init trap_init(void) } #ifdef CONFIG_SMP -void __init secondary_trap_init(void) +void secondary_trap_init(void) { trap_init_excsave(); } -- cgit v0.10.2 From 3ade4f81ae0c32c0c82fad93de92df07f24a85a4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 13 Dec 2013 01:43:58 -0800 Subject: xtensa: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Signed-off-by: Stephen Boyd Acked-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 60dcb3f..1fb49a3 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -36,7 +36,7 @@ static cycle_t ccount_read(struct clocksource *cs) return (cycle_t)get_ccount(); } -static u32 notrace ccount_sched_clock_read(void) +static u64 notrace ccount_sched_clock_read(void) { return get_ccount(); } @@ -147,7 +147,7 @@ void __init time_init(void) clocksource_register_hz(&ccount_clocksource, ccount_freq); local_timer_setup(0); setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction); - setup_sched_clock(ccount_sched_clock_read, 32, ccount_freq); + sched_clock_register(ccount_sched_clock_read, 32, ccount_freq); } /* -- cgit v0.10.2 From 8be54d770bcf6f7b203a705aefed2ec845e1b748 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 1 Dec 2013 10:16:56 +0400 Subject: xtensa: ISS: avoid simple_strtoul usage Signed-off-by: Max Filippov Signed-off-by: Chris Zankel diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 3aff430..7f84b5c 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -612,22 +612,22 @@ static int __init iss_net_setup(char *str) struct iss_net_init *new; struct list_head *ele; char *end; - int n; + int rc; + unsigned n; - n = simple_strtoul(str, &end, 0); - if (end == str) { - printk(ERR "Failed to parse '%s'\n", str); + end = strchr(str, '='); + if (!end) { + printk(ERR "Expected '=' after device number\n"); return 1; } - if (n < 0) { - printk(ERR "Device %d is negative\n", n); + *end = 0; + rc = kstrtouint(str, 0, &n); + *end = '='; + if (rc < 0) { + printk(ERR "Failed to parse '%s'\n", str); return 1; } str = end; - if (*str != '=') { - printk(ERR "Expected '=' after device number\n"); - return 1; - } spin_lock(&devices_lock); @@ -640,7 +640,7 @@ static int __init iss_net_setup(char *str) spin_unlock(&devices_lock); if (device && device->index == n) { - printk(ERR "Device %d already configured\n", n); + printk(ERR "Device %u already configured\n", n); return 1; } -- cgit v0.10.2 From fa72e6bbada08055e30e5b4213ba9c83458f3ef3 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 4 Nov 2013 10:32:33 +0100 Subject: xtensa: remove unused XTENSA_ISS_NETWORK Kconfig parameter This removes the XTENSA_ISS_NETWORK Kconfig parameter, which was no longer used anywhere in the source code and Makefiles. Signed-off-by: Michael Opdenacker Signed-off-by: Max Filippov diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 70a160b..49d2e84 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -195,9 +195,6 @@ config XTENSA_CALIBRATE_CCOUNT config SERIAL_CONSOLE def_bool n -config XTENSA_ISS_NETWORK - def_bool n - menu "Bus options" config PCI @@ -224,7 +221,6 @@ config XTENSA_PLATFORM_ISS depends on TTY select XTENSA_CALIBRATE_CCOUNT select SERIAL_CONSOLE - select XTENSA_ISS_NETWORK help ISS is an acronym for Tensilica's Instruction Set Simulator. -- cgit v0.10.2 From 306ab5448fe1d002db8ca96c7d885e9f633b9121 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 10 Nov 2013 08:59:43 +0400 Subject: xtensa: ISS: raise network polling rate to 10 times/sec Signed-off-by: Max Filippov diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 7f84b5c..d05f8fe 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -38,7 +38,7 @@ #define DRIVER_NAME "iss-netdev" #define ETH_MAX_PACKET 1500 #define ETH_HEADER_OTHER 14 -#define ISS_NET_TIMER_VALUE (2 * HZ) +#define ISS_NET_TIMER_VALUE (HZ / 10) static DEFINE_SPINLOCK(opened_lock); -- cgit v0.10.2 From abf0ea65e0adede5f40a836e6af7f62e7c850546 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 12 Dec 2013 17:41:01 +0400 Subject: xtensa: Enable irqs after cpu is set online there is a small possibility that wake_up of softirq thread happens between local_irq_enable() and set_cpu_online(). In this case affinity of the thread changes to fallback affinity (i.e. CPU0). This may be a source of problems. The patch kills that possibility. Signed-off-by: Kirill Tkhai Signed-off-by: Max Filippov diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c index 1c7a209..aa8bd87 100644 --- a/arch/xtensa/kernel/smp.c +++ b/arch/xtensa/kernel/smp.c @@ -151,9 +151,10 @@ void secondary_start_kernel(void) secondary_init_irq(); local_timer_setup(cpu); + set_cpu_online(cpu, true); + local_irq_enable(); - set_cpu_online(cpu, true); complete(&cpu_running); cpu_startup_entry(CPUHP_ONLINE); -- cgit v0.10.2 From 5f42146e7599e120ee3f5b07d03ea45c633a955a Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 11 Nov 2013 17:15:17 +0200 Subject: xtensa: fix ATOMCTL register documentation Make the WT entry match table 4-52 of the Xtensa ISA RM (RD-2012.5). Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/Documentation/xtensa/atomctl.txt b/Documentation/xtensa/atomctl.txt index 10a8d1f..1da783a 100644 --- a/Documentation/xtensa/atomctl.txt +++ b/Documentation/xtensa/atomctl.txt @@ -40,5 +40,5 @@ See Section 4.3.12.4 of ISA; Bits: --------- --------------- ----------------- ---------------- 0 Exception Exception Exception 1 RCW Transaction RCW Transaction RCW Transaction - 2 Internal Operation Exception Reserved + 2 Internal Operation Internal Operation Reserved 3 Reserved Reserved Reserved -- cgit v0.10.2 From 4809bb468f140d561dfbb785e8dcdda5bae8c1f2 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 17 Nov 2013 08:14:54 +0200 Subject: xtensa: avoid duplicate of IO range definitions Define IO addresses in a single place, to make it easier to change for non-standard memory maps. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h index 722553f..a2078a2 100644 --- a/arch/xtensa/include/asm/initialize_mmu.h +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -123,13 +123,13 @@ wdtlb a4, a5 witlb a4, a5 - movi a5, 0xe0000006 - movi a4, 0xf0000000 + CA_WRITEBACK + movi a5, XCHAL_KIO_CACHED_VADDR + 6 + movi a4, XCHAL_KIO_PADDR + CA_WRITEBACK wdtlb a4, a5 witlb a4, a5 - movi a5, 0xf0000006 - movi a4, 0xf0000000 + CA_BYPASS + movi a5, XCHAL_KIO_BYPASS_VADDR + 6 + movi a4, XCHAL_KIO_PADDR + CA_BYPASS wdtlb a4, a5 witlb a4, a5 diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index 700c2e6..1482a363 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -14,16 +14,12 @@ #ifdef __KERNEL__ #include #include +#include #include #include #include -#define XCHAL_KIO_CACHED_VADDR 0xe0000000 -#define XCHAL_KIO_BYPASS_VADDR 0xf0000000 -#define XCHAL_KIO_PADDR 0xf0000000 -#define XCHAL_KIO_SIZE 0x10000000 - #define IOADDR(x) (XCHAL_KIO_BYPASS_VADDR + (x)) #define IO_SPACE_LIMIT ~0 diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index 37e073b..221a60d 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -20,6 +20,11 @@ #include +#define XCHAL_KIO_CACHED_VADDR 0xe0000000 +#define XCHAL_KIO_BYPASS_VADDR 0xf0000000 +#define XCHAL_KIO_PADDR 0xf0000000 +#define XCHAL_KIO_SIZE 0x10000000 + #if defined(CONFIG_MMU) /* Will Become VECBASE */ -- cgit v0.10.2 From 42beb7628e7a901501e39f520beec67bac3157b1 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 1 Dec 2013 10:13:33 +0200 Subject: xtensa: standardize devicetree cpu compatible strings The recommended compatible string format, according to the ePAPR v1.1 standard, is "manufacturer,model". Change the xtensa cpu compatible strings to "cdns,xtensa-cpu". Also, change the boards compatible strings in a similar way. The pic compatible string will be dealt with in a separate patch. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/arch/xtensa/boot/dts/lx60.dts b/arch/xtensa/boot/dts/lx60.dts index 2eab365..a0f8b8a 100644 --- a/arch/xtensa/boot/dts/lx60.dts +++ b/arch/xtensa/boot/dts/lx60.dts @@ -3,7 +3,7 @@ /include/ "xtfpga-flash-4m.dtsi" / { - compatible = "xtensa,lx60"; + compatible = "cdns,xtensa-lx60"; memory@0 { device_type = "memory"; reg = <0x00000000 0x04000000>; diff --git a/arch/xtensa/boot/dts/ml605.dts b/arch/xtensa/boot/dts/ml605.dts index 6ed51d6..905c3a5 100644 --- a/arch/xtensa/boot/dts/ml605.dts +++ b/arch/xtensa/boot/dts/ml605.dts @@ -3,7 +3,7 @@ /include/ "xtfpga-flash-16m.dtsi" / { - compatible = "xtensa,ml605"; + compatible = "cdns,xtensa-ml605"; memory@0 { device_type = "memory"; reg = <0x00000000 0x08000000>; diff --git a/arch/xtensa/boot/dts/xtfpga.dtsi b/arch/xtensa/boot/dts/xtfpga.dtsi index b042c3f..46b4f5e 100644 --- a/arch/xtensa/boot/dts/xtfpga.dtsi +++ b/arch/xtensa/boot/dts/xtfpga.dtsi @@ -1,5 +1,5 @@ / { - compatible = "xtensa,xtfpga"; + compatible = "cdns,xtensa-xtfpga"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&pic>; @@ -17,7 +17,7 @@ #address-cells = <1>; #size-cells = <0>; cpu@0 { - compatible = "xtensa,cpu"; + compatible = "cdns,xtensa-cpu"; reg = <0>; /* Filled in by platform_setup from FPGA register * clock-frequency = <100000000>; diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c index 74bb74f..25d936e 100644 --- a/arch/xtensa/platforms/xtfpga/setup.c +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -168,7 +168,7 @@ void __init platform_calibrate_ccount(void) long clk_freq = 0; #ifdef CONFIG_OF struct device_node *cpu = - of_find_compatible_node(NULL, NULL, "xtensa,cpu"); + of_find_compatible_node(NULL, NULL, "cdns,xtensa-cpu"); if (cpu) { u32 freq; update_clock_frequency(cpu); -- cgit v0.10.2 From a558d99263936b8a67d4eff8918745a77bfd8c31 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 25 Dec 2013 05:20:36 +0400 Subject: xtensa: xtfpga: fix definitions of platform devices Remove __initdata attribute, as the devices may be used after init sections are freed. Cc: stable@vger.kernel.org Signed-off-by: Max Filippov diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c index 25d936e..8002278 100644 --- a/arch/xtensa/platforms/xtfpga/setup.c +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -194,7 +194,7 @@ void __init platform_calibrate_ccount(void) * Ethernet -- OpenCores Ethernet MAC (ethoc driver) */ -static struct resource ethoc_res[] __initdata = { +static struct resource ethoc_res[] = { [0] = { /* register space */ .start = OETH_REGS_PADDR, .end = OETH_REGS_PADDR + OETH_REGS_SIZE - 1, @@ -212,7 +212,7 @@ static struct resource ethoc_res[] __initdata = { }, }; -static struct ethoc_platform_data ethoc_pdata __initdata = { +static struct ethoc_platform_data ethoc_pdata = { /* * The MAC address for these boards is 00:50:c2:13:6f:xx. * The last byte (here as zero) is read from the DIP switches on the @@ -222,7 +222,7 @@ static struct ethoc_platform_data ethoc_pdata __initdata = { .phy_id = -1, }; -static struct platform_device ethoc_device __initdata = { +static struct platform_device ethoc_device = { .name = "ethoc", .id = -1, .num_resources = ARRAY_SIZE(ethoc_res), @@ -236,13 +236,13 @@ static struct platform_device ethoc_device __initdata = { * UART */ -static struct resource serial_resource __initdata = { +static struct resource serial_resource = { .start = DUART16552_PADDR, .end = DUART16552_PADDR + 0x1f, .flags = IORESOURCE_MEM, }; -static struct plat_serial8250_port serial_platform_data[] __initdata = { +static struct plat_serial8250_port serial_platform_data[] = { [0] = { .mapbase = DUART16552_PADDR, .irq = DUART16552_INTNUM, @@ -255,7 +255,7 @@ static struct plat_serial8250_port serial_platform_data[] __initdata = { { }, }; -static struct platform_device xtavnet_uart __initdata = { +static struct platform_device xtavnet_uart = { .name = "serial8250", .id = PLAT8250_DEV_PLATFORM, .dev = { -- cgit v0.10.2 From b087ab7f257022027077cc02d8056b64828c6ae4 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 23 Dec 2013 20:49:56 +0200 Subject: xtensa: initialize device tree clock sources Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 1fb49a3..08b769d 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -148,6 +148,7 @@ void __init time_init(void) local_timer_setup(0); setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction); sched_clock_register(ccount_sched_clock_read, 32, ccount_freq); + clocksource_of_init(); } /* -- cgit v0.10.2 From f8935f307fe46faa1a829a6b76ee95ca9ba67d20 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 23 Dec 2013 20:49:57 +0200 Subject: xtensa: support default device tree buses This currently includes simple-bus. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index d21bfa7..f38bade 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -239,7 +239,7 @@ void __init early_init_devtree(void *params) static int __init xtensa_device_probe(void) { - of_platform_populate(NULL, NULL, NULL, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); return 0; } -- cgit v0.10.2 From 6cb971114f633a0bf240c20b681d989b45e3ec56 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 29 Dec 2013 11:03:30 +0200 Subject: xtensa: remap io area defined in device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the simple-bus node to discover the io area, and remap the cached and bypass io ranges. The parent-bus-address value of the first triplet in the "ranges" property is used. This value is rounded down to the nearest 256MB boundary. The length of the io area is fixed at 256MB; the "ranges" property length value is ignored. Other limitations: (1) only the first simple-bus node is considered, and (2) only the first triplet of the "ranges" property is considered. See ePAPR 1.1 §6.5 for the simple-bus node description, and §2.3.8 for the "ranges" property description. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/Documentation/xtensa/mmu.txt b/Documentation/xtensa/mmu.txt index 2b1af76..0312fe6 100644 --- a/Documentation/xtensa/mmu.txt +++ b/Documentation/xtensa/mmu.txt @@ -44,3 +44,21 @@ After step 4, we jump to intended (linked) address of this code. 40..5F -> 40 40..5F -> pc -> pc 40..5F -> pc 20..3F -> 20 -> 20 20..3F -> 20 00..1F -> 00 -> 00 00..1F -> 00 + +The default location of IO peripherals is above 0xf0000000. This may change +using a "ranges" property in a device tree simple-bus node. See ePAPR 1.1, §6.5 +for details on the syntax and semantic of simple-bus nodes. The following +limitations apply: + +1. Only top level simple-bus nodes are considered + +2. Only one (first) simple-bus node is considered + +3. Empty "ranges" properties are not supported + +4. Only the first triplet in the "ranges" property is considered + +5. The parent-bus-address value is rounded down to the nearest 256MB boundary + +6. The IO area covers the entire 256MB segment of parent-bus-address; the + "ranges" triplet length field is ignored diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h index a2078a2..600781e 100644 --- a/arch/xtensa/include/asm/initialize_mmu.h +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -26,6 +26,9 @@ #include #include +#define CA_BYPASS (_PAGE_CA_BYPASS | _PAGE_HW_WRITE | _PAGE_HW_EXEC) +#define CA_WRITEBACK (_PAGE_CA_WB | _PAGE_HW_WRITE | _PAGE_HW_EXEC) + #ifdef __ASSEMBLY__ #define XTENSA_HWVERSION_RC_2009_0 230000 @@ -80,8 +83,6 @@ /* Step 2: map 0x40000000..0x47FFFFFF to paddr containing this code * and jump to the new mapping. */ -#define CA_BYPASS (_PAGE_CA_BYPASS | _PAGE_HW_WRITE | _PAGE_HW_EXEC) -#define CA_WRITEBACK (_PAGE_CA_WB | _PAGE_HW_WRITE | _PAGE_HW_EXEC) srli a3, a0, 27 slli a3, a3, 27 @@ -124,12 +125,12 @@ witlb a4, a5 movi a5, XCHAL_KIO_CACHED_VADDR + 6 - movi a4, XCHAL_KIO_PADDR + CA_WRITEBACK + movi a4, XCHAL_KIO_DEFAULT_PADDR + CA_WRITEBACK wdtlb a4, a5 witlb a4, a5 movi a5, XCHAL_KIO_BYPASS_VADDR + 6 - movi a4, XCHAL_KIO_PADDR + CA_BYPASS + movi a4, XCHAL_KIO_DEFAULT_PADDR + CA_BYPASS wdtlb a4, a5 witlb a4, a5 diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index 1482a363..2a042d4 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -24,6 +24,16 @@ #define IO_SPACE_LIMIT ~0 #ifdef CONFIG_MMU + +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF +extern unsigned long xtensa_kio_paddr; + +static inline unsigned long xtensa_get_kio_paddr(void) +{ + return xtensa_kio_paddr; +} +#endif + /* * Return the virtual address for the specified bus memory. * Note that we currently don't support any address outside the KIO segment. diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index 221a60d..5791b45 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -22,9 +22,15 @@ #define XCHAL_KIO_CACHED_VADDR 0xe0000000 #define XCHAL_KIO_BYPASS_VADDR 0xf0000000 -#define XCHAL_KIO_PADDR 0xf0000000 +#define XCHAL_KIO_DEFAULT_PADDR 0xf0000000 #define XCHAL_KIO_SIZE 0x10000000 +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF +#define XCHAL_KIO_PADDR xtensa_get_kio_paddr() +#else +#define XCHAL_KIO_PADDR XCHAL_KIO_DEFAULT_PADDR +#endif + #if defined(CONFIG_MMU) /* Will Become VECBASE */ diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index f38bade..7d12af1 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -212,6 +212,42 @@ static int __init parse_bootparam(const bp_tag_t* tag) #ifdef CONFIG_OF bool __initdata dt_memory_scan = false; +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY +unsigned long xtensa_kio_paddr = XCHAL_KIO_DEFAULT_PADDR; +EXPORT_SYMBOL(xtensa_kio_paddr); + +static int __init xtensa_dt_io_area(unsigned long node, const char *uname, + int depth, void *data) +{ + const __be32 *ranges; + unsigned long len; + + if (depth > 1) + return 0; + + if (!of_flat_dt_is_compatible(node, "simple-bus")) + return 0; + + ranges = of_get_flat_dt_prop(node, "ranges", &len); + if (!ranges) + return 1; + if (len == 0) + return 1; + + xtensa_kio_paddr = of_read_ulong(ranges+1, 1); + /* round down to nearest 256MB boundary */ + xtensa_kio_paddr &= 0xf0000000; + + return 1; +} +#else +static int __init xtensa_dt_io_area(unsigned long node, const char *uname, + int depth, void *data) +{ + return 1; +} +#endif + void __init early_init_dt_add_memory_arch(u64 base, u64 size) { if (!dt_memory_scan) @@ -232,6 +268,7 @@ void __init early_init_devtree(void *params) dt_memory_scan = true; early_init_dt_scan(params); + of_scan_flat_dt(xtensa_dt_io_area, NULL); if (!command_line[0]) strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c index 5bb8e3c..36ec171 100644 --- a/arch/xtensa/mm/mmu.c +++ b/arch/xtensa/mm/mmu.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include void __init paging_init(void) { @@ -37,6 +39,20 @@ void init_mmu(void) set_itlbcfg_register(0); set_dtlbcfg_register(0); #endif +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF + /* + * Update the IO area mapping in case xtensa_kio_paddr has changed + */ + write_dtlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), + XCHAL_KIO_CACHED_VADDR + 6); + write_itlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), + XCHAL_KIO_CACHED_VADDR + 6); + write_dtlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), + XCHAL_KIO_BYPASS_VADDR + 6); + write_itlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), + XCHAL_KIO_BYPASS_VADDR + 6); +#endif + local_flush_tlb_all(); /* Set rasid register to a known value. */ -- cgit v0.10.2 From a6f3eefad8d1c9abd98fa03ba2b3abeb708e3b95 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 28 Nov 2013 18:00:04 +0200 Subject: xtensa: enable HAVE_PERF_EVENTS This allows the perf tool to monitor kernel tracepoint events. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 49d2e84..c3ccf37 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -19,6 +19,7 @@ config XTENSA select HAVE_OPROFILE select HAVE_FUNCTION_TRACER select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_PERF_EVENTS help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both diff --git a/arch/xtensa/include/asm/perf_event.h b/arch/xtensa/include/asm/perf_event.h new file mode 100644 index 0000000..5aa4590 --- /dev/null +++ b/arch/xtensa/include/asm/perf_event.h @@ -0,0 +1,4 @@ +#ifndef __ASM_XTENSA_PERF_EVENT_H +#define __ASM_XTENSA_PERF_EVENT_H + +#endif /* __ASM_XTENSA_PERF_EVENT_H */ -- cgit v0.10.2 From 58f60c222e8d4503a4be654cbd6d4e190dc1a7d8 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 10 Jan 2014 12:02:18 +0400 Subject: xtensa: clean up udelay Replace division with shift, use ccount_freq instead of loops_per_jiffy. Introduce __MAX_UDELAY and (undefined) __bad_udelay, break build for too big udelay constants. Remove irrelevant comment, clean up code style. Signed-off-by: Max Filippov diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 742b89f..69ba462 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -27,18 +27,27 @@ static inline void __delay(unsigned long loops) : "+r" (loops)); } -/* For SMP/NUMA systems, change boot_cpu_data to something like - * local_cpu_data->... where local_cpu_data points to the current - * cpu. */ +/* Undefined function to get compile-time error */ +void __bad_udelay(void); -static __inline__ void udelay (unsigned long usecs) +#define __MAX_UDELAY 30000 + +static inline void __udelay(unsigned long usecs) { unsigned long start = get_ccount(); - unsigned long cycles = usecs * (loops_per_jiffy / (1000000UL / HZ)); + unsigned long cycles = (usecs * (ccount_freq >> 15)) >> 5; /* Note: all variables are unsigned (can wrap around)! */ while (((unsigned long)get_ccount()) - start < cycles) - ; + cpu_relax(); +} + +static inline void udelay(unsigned long usec) +{ + if (__builtin_constant_p(usec) && usec >= __MAX_UDELAY) + __bad_udelay(); + else + __udelay(usec); } #endif -- cgit v0.10.2 From 9ed82c6866e2ab671935a75ea454047e8bddb177 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 10 Jan 2014 12:03:35 +0400 Subject: xtensa: implement ndelay Proper ndelay implementation allows for faster IO rate with drivers that use ndelay to access their device registers, as otherwise ndelay is emulated with udelay. Signed-off-by: Max Filippov diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 69ba462..24304b3 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -29,8 +29,10 @@ static inline void __delay(unsigned long loops) /* Undefined function to get compile-time error */ void __bad_udelay(void); +void __bad_ndelay(void); #define __MAX_UDELAY 30000 +#define __MAX_NDELAY 30000 static inline void __udelay(unsigned long usecs) { @@ -50,4 +52,24 @@ static inline void udelay(unsigned long usec) __udelay(usec); } +static inline void __ndelay(unsigned long nsec) +{ + /* + * Inner shift makes sure multiplication doesn't overflow + * for legitimate nsec values + */ + unsigned long cycles = (nsec * (ccount_freq >> 15)) >> 15; + __delay(cycles); +} + +#define ndelay(n) ndelay(n) + +static inline void ndelay(unsigned long nsec) +{ + if (__builtin_constant_p(nsec) && nsec >= __MAX_NDELAY) + __bad_ndelay(); + else + __ndelay(nsec); +} + #endif -- cgit v0.10.2