13fafe9c2SBenjamin Herrenschmidt /* 23fafe9c2SBenjamin Herrenschmidt * PowerNV LPC bus handling. 33fafe9c2SBenjamin Herrenschmidt * 43fafe9c2SBenjamin Herrenschmidt * Copyright 2013 IBM Corp. 53fafe9c2SBenjamin Herrenschmidt * 63fafe9c2SBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or 73fafe9c2SBenjamin Herrenschmidt * modify it under the terms of the GNU General Public License 83fafe9c2SBenjamin Herrenschmidt * as published by the Free Software Foundation; either version 93fafe9c2SBenjamin Herrenschmidt * 2 of the License, or (at your option) any later version. 103fafe9c2SBenjamin Herrenschmidt */ 113fafe9c2SBenjamin Herrenschmidt 123fafe9c2SBenjamin Herrenschmidt #include <linux/kernel.h> 133fafe9c2SBenjamin Herrenschmidt #include <linux/of.h> 143fafe9c2SBenjamin Herrenschmidt #include <linux/bug.h> 153fafe9c2SBenjamin Herrenschmidt 163fafe9c2SBenjamin Herrenschmidt #include <asm/machdep.h> 173fafe9c2SBenjamin Herrenschmidt #include <asm/firmware.h> 183fafe9c2SBenjamin Herrenschmidt #include <asm/xics.h> 193fafe9c2SBenjamin Herrenschmidt #include <asm/opal.h> 203fafe9c2SBenjamin Herrenschmidt 213fafe9c2SBenjamin Herrenschmidt static int opal_lpc_chip_id = -1; 223fafe9c2SBenjamin Herrenschmidt 233fafe9c2SBenjamin Herrenschmidt static u8 opal_lpc_inb(unsigned long port) 243fafe9c2SBenjamin Herrenschmidt { 253fafe9c2SBenjamin Herrenschmidt int64_t rc; 263fafe9c2SBenjamin Herrenschmidt uint32_t data; 273fafe9c2SBenjamin Herrenschmidt 283fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xffff) 293fafe9c2SBenjamin Herrenschmidt return 0xff; 303fafe9c2SBenjamin Herrenschmidt rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 1); 313fafe9c2SBenjamin Herrenschmidt return rc ? 0xff : data; 323fafe9c2SBenjamin Herrenschmidt } 333fafe9c2SBenjamin Herrenschmidt 343fafe9c2SBenjamin Herrenschmidt static __le16 __opal_lpc_inw(unsigned long port) 353fafe9c2SBenjamin Herrenschmidt { 363fafe9c2SBenjamin Herrenschmidt int64_t rc; 373fafe9c2SBenjamin Herrenschmidt uint32_t data; 383fafe9c2SBenjamin Herrenschmidt 393fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xfffe) 403fafe9c2SBenjamin Herrenschmidt return 0xffff; 413fafe9c2SBenjamin Herrenschmidt if (port & 1) 423fafe9c2SBenjamin Herrenschmidt return (__le16)opal_lpc_inb(port) << 8 | opal_lpc_inb(port + 1); 433fafe9c2SBenjamin Herrenschmidt rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 2); 443fafe9c2SBenjamin Herrenschmidt return rc ? 0xffff : data; 453fafe9c2SBenjamin Herrenschmidt } 463fafe9c2SBenjamin Herrenschmidt static u16 opal_lpc_inw(unsigned long port) 473fafe9c2SBenjamin Herrenschmidt { 483fafe9c2SBenjamin Herrenschmidt return le16_to_cpu(__opal_lpc_inw(port)); 493fafe9c2SBenjamin Herrenschmidt } 503fafe9c2SBenjamin Herrenschmidt 513fafe9c2SBenjamin Herrenschmidt static __le32 __opal_lpc_inl(unsigned long port) 523fafe9c2SBenjamin Herrenschmidt { 533fafe9c2SBenjamin Herrenschmidt int64_t rc; 543fafe9c2SBenjamin Herrenschmidt uint32_t data; 553fafe9c2SBenjamin Herrenschmidt 563fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xfffc) 573fafe9c2SBenjamin Herrenschmidt return 0xffffffff; 583fafe9c2SBenjamin Herrenschmidt if (port & 3) 593fafe9c2SBenjamin Herrenschmidt return (__le32)opal_lpc_inb(port ) << 24 | 603fafe9c2SBenjamin Herrenschmidt (__le32)opal_lpc_inb(port + 1) << 16 | 613fafe9c2SBenjamin Herrenschmidt (__le32)opal_lpc_inb(port + 2) << 8 | 623fafe9c2SBenjamin Herrenschmidt opal_lpc_inb(port + 3); 633fafe9c2SBenjamin Herrenschmidt rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 4); 643fafe9c2SBenjamin Herrenschmidt return rc ? 0xffffffff : data; 653fafe9c2SBenjamin Herrenschmidt } 663fafe9c2SBenjamin Herrenschmidt 673fafe9c2SBenjamin Herrenschmidt static u32 opal_lpc_inl(unsigned long port) 683fafe9c2SBenjamin Herrenschmidt { 693fafe9c2SBenjamin Herrenschmidt return le32_to_cpu(__opal_lpc_inl(port)); 703fafe9c2SBenjamin Herrenschmidt } 713fafe9c2SBenjamin Herrenschmidt 723fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outb(u8 val, unsigned long port) 733fafe9c2SBenjamin Herrenschmidt { 743fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xffff) 753fafe9c2SBenjamin Herrenschmidt return; 763fafe9c2SBenjamin Herrenschmidt opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 1); 773fafe9c2SBenjamin Herrenschmidt } 783fafe9c2SBenjamin Herrenschmidt 793fafe9c2SBenjamin Herrenschmidt static void __opal_lpc_outw(__le16 val, unsigned long port) 803fafe9c2SBenjamin Herrenschmidt { 813fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xfffe) 823fafe9c2SBenjamin Herrenschmidt return; 833fafe9c2SBenjamin Herrenschmidt if (port & 1) { 843fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val >> 8, port); 853fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val , port + 1); 863fafe9c2SBenjamin Herrenschmidt return; 873fafe9c2SBenjamin Herrenschmidt } 883fafe9c2SBenjamin Herrenschmidt opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 2); 893fafe9c2SBenjamin Herrenschmidt } 903fafe9c2SBenjamin Herrenschmidt 913fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outw(u16 val, unsigned long port) 923fafe9c2SBenjamin Herrenschmidt { 933fafe9c2SBenjamin Herrenschmidt __opal_lpc_outw(cpu_to_le16(val), port); 943fafe9c2SBenjamin Herrenschmidt } 953fafe9c2SBenjamin Herrenschmidt 963fafe9c2SBenjamin Herrenschmidt static void __opal_lpc_outl(__le32 val, unsigned long port) 973fafe9c2SBenjamin Herrenschmidt { 983fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0 || port > 0xfffc) 993fafe9c2SBenjamin Herrenschmidt return; 1003fafe9c2SBenjamin Herrenschmidt if (port & 3) { 1013fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val >> 24, port); 1023fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val >> 16, port + 1); 1033fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val >> 8, port + 2); 1043fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(val , port + 3); 1053fafe9c2SBenjamin Herrenschmidt return; 1063fafe9c2SBenjamin Herrenschmidt } 1073fafe9c2SBenjamin Herrenschmidt opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 4); 1083fafe9c2SBenjamin Herrenschmidt } 1093fafe9c2SBenjamin Herrenschmidt 1103fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outl(u32 val, unsigned long port) 1113fafe9c2SBenjamin Herrenschmidt { 1123fafe9c2SBenjamin Herrenschmidt __opal_lpc_outl(cpu_to_le32(val), port); 1133fafe9c2SBenjamin Herrenschmidt } 1143fafe9c2SBenjamin Herrenschmidt 1153fafe9c2SBenjamin Herrenschmidt static void opal_lpc_insb(unsigned long p, void *b, unsigned long c) 1163fafe9c2SBenjamin Herrenschmidt { 1173fafe9c2SBenjamin Herrenschmidt u8 *ptr = b; 1183fafe9c2SBenjamin Herrenschmidt 1193fafe9c2SBenjamin Herrenschmidt while(c--) 1203fafe9c2SBenjamin Herrenschmidt *(ptr++) = opal_lpc_inb(p); 1213fafe9c2SBenjamin Herrenschmidt } 1223fafe9c2SBenjamin Herrenschmidt 1233fafe9c2SBenjamin Herrenschmidt static void opal_lpc_insw(unsigned long p, void *b, unsigned long c) 1243fafe9c2SBenjamin Herrenschmidt { 1253fafe9c2SBenjamin Herrenschmidt __le16 *ptr = b; 1263fafe9c2SBenjamin Herrenschmidt 1273fafe9c2SBenjamin Herrenschmidt while(c--) 1283fafe9c2SBenjamin Herrenschmidt *(ptr++) = __opal_lpc_inw(p); 1293fafe9c2SBenjamin Herrenschmidt } 1303fafe9c2SBenjamin Herrenschmidt 1313fafe9c2SBenjamin Herrenschmidt static void opal_lpc_insl(unsigned long p, void *b, unsigned long c) 1323fafe9c2SBenjamin Herrenschmidt { 1333fafe9c2SBenjamin Herrenschmidt __le32 *ptr = b; 1343fafe9c2SBenjamin Herrenschmidt 1353fafe9c2SBenjamin Herrenschmidt while(c--) 1363fafe9c2SBenjamin Herrenschmidt *(ptr++) = __opal_lpc_inl(p); 1373fafe9c2SBenjamin Herrenschmidt } 1383fafe9c2SBenjamin Herrenschmidt 1393fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outsb(unsigned long p, const void *b, unsigned long c) 1403fafe9c2SBenjamin Herrenschmidt { 1413fafe9c2SBenjamin Herrenschmidt const u8 *ptr = b; 1423fafe9c2SBenjamin Herrenschmidt 1433fafe9c2SBenjamin Herrenschmidt while(c--) 1443fafe9c2SBenjamin Herrenschmidt opal_lpc_outb(*(ptr++), p); 1453fafe9c2SBenjamin Herrenschmidt } 1463fafe9c2SBenjamin Herrenschmidt 1473fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outsw(unsigned long p, const void *b, unsigned long c) 1483fafe9c2SBenjamin Herrenschmidt { 1493fafe9c2SBenjamin Herrenschmidt const __le16 *ptr = b; 1503fafe9c2SBenjamin Herrenschmidt 1513fafe9c2SBenjamin Herrenschmidt while(c--) 1523fafe9c2SBenjamin Herrenschmidt __opal_lpc_outw(*(ptr++), p); 1533fafe9c2SBenjamin Herrenschmidt } 1543fafe9c2SBenjamin Herrenschmidt 1553fafe9c2SBenjamin Herrenschmidt static void opal_lpc_outsl(unsigned long p, const void *b, unsigned long c) 1563fafe9c2SBenjamin Herrenschmidt { 1573fafe9c2SBenjamin Herrenschmidt const __le32 *ptr = b; 1583fafe9c2SBenjamin Herrenschmidt 1593fafe9c2SBenjamin Herrenschmidt while(c--) 1603fafe9c2SBenjamin Herrenschmidt __opal_lpc_outl(*(ptr++), p); 1613fafe9c2SBenjamin Herrenschmidt } 1623fafe9c2SBenjamin Herrenschmidt 1633fafe9c2SBenjamin Herrenschmidt static const struct ppc_pci_io opal_lpc_io = { 1643fafe9c2SBenjamin Herrenschmidt .inb = opal_lpc_inb, 1653fafe9c2SBenjamin Herrenschmidt .inw = opal_lpc_inw, 1663fafe9c2SBenjamin Herrenschmidt .inl = opal_lpc_inl, 1673fafe9c2SBenjamin Herrenschmidt .outb = opal_lpc_outb, 1683fafe9c2SBenjamin Herrenschmidt .outw = opal_lpc_outw, 1693fafe9c2SBenjamin Herrenschmidt .outl = opal_lpc_outl, 1703fafe9c2SBenjamin Herrenschmidt .insb = opal_lpc_insb, 1713fafe9c2SBenjamin Herrenschmidt .insw = opal_lpc_insw, 1723fafe9c2SBenjamin Herrenschmidt .insl = opal_lpc_insl, 1733fafe9c2SBenjamin Herrenschmidt .outsb = opal_lpc_outsb, 1743fafe9c2SBenjamin Herrenschmidt .outsw = opal_lpc_outsw, 1753fafe9c2SBenjamin Herrenschmidt .outsl = opal_lpc_outsl, 1763fafe9c2SBenjamin Herrenschmidt }; 1773fafe9c2SBenjamin Herrenschmidt 1783fafe9c2SBenjamin Herrenschmidt void opal_lpc_init(void) 1793fafe9c2SBenjamin Herrenschmidt { 1803fafe9c2SBenjamin Herrenschmidt struct device_node *np; 1813fafe9c2SBenjamin Herrenschmidt 1823fafe9c2SBenjamin Herrenschmidt /* 1833fafe9c2SBenjamin Herrenschmidt * Look for a Power8 LPC bus tagged as "primary", 1843fafe9c2SBenjamin Herrenschmidt * we currently support only one though the OPAL APIs 1853fafe9c2SBenjamin Herrenschmidt * support any number. 1863fafe9c2SBenjamin Herrenschmidt */ 1873fafe9c2SBenjamin Herrenschmidt for_each_compatible_node(np, NULL, "ibm,power8-lpc") { 1883fafe9c2SBenjamin Herrenschmidt if (!of_device_is_available(np)) 1893fafe9c2SBenjamin Herrenschmidt continue; 1903fafe9c2SBenjamin Herrenschmidt if (!of_get_property(np, "primary", NULL)) 1913fafe9c2SBenjamin Herrenschmidt continue; 1923fafe9c2SBenjamin Herrenschmidt opal_lpc_chip_id = of_get_ibm_chip_id(np); 1933fafe9c2SBenjamin Herrenschmidt break; 1943fafe9c2SBenjamin Herrenschmidt } 1953fafe9c2SBenjamin Herrenschmidt if (opal_lpc_chip_id < 0) 1963fafe9c2SBenjamin Herrenschmidt return; 1973fafe9c2SBenjamin Herrenschmidt 1983fafe9c2SBenjamin Herrenschmidt /* Setup special IO ops */ 1993fafe9c2SBenjamin Herrenschmidt ppc_pci_io = opal_lpc_io; 2003fafe9c2SBenjamin Herrenschmidt isa_io_special = true; 2013fafe9c2SBenjamin Herrenschmidt 2023fafe9c2SBenjamin Herrenschmidt pr_info("OPAL: Power8 LPC bus found, chip ID %d\n", opal_lpc_chip_id); 2033fafe9c2SBenjamin Herrenschmidt } 204