Path: blob/master/arch/powerpc/platforms/powernv/opal-lpc.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerNV LPC bus handling.3*4* Copyright 2013 IBM Corp.5*/67#include <linux/kernel.h>8#include <linux/of.h>9#include <linux/bug.h>10#include <linux/io.h>11#include <linux/slab.h>12#include <linux/debugfs.h>1314#include <asm/machdep.h>15#include <asm/firmware.h>16#include <asm/opal.h>17#include <asm/prom.h>18#include <linux/uaccess.h>19#include <asm/isa-bridge.h>2021static int opal_lpc_chip_id = -1;2223static u8 opal_lpc_inb(unsigned long port)24{25int64_t rc;26__be32 data;2728if (opal_lpc_chip_id < 0 || port > 0xffff)29return 0xff;30rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 1);31return rc ? 0xff : be32_to_cpu(data);32}3334static __le16 __opal_lpc_inw(unsigned long port)35{36int64_t rc;37__be32 data;3839if (opal_lpc_chip_id < 0 || port > 0xfffe)40return 0xffff;41if (port & 1)42return (__le16)opal_lpc_inb(port) << 8 | opal_lpc_inb(port + 1);43rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 2);44return rc ? 0xffff : be32_to_cpu(data);45}46static u16 opal_lpc_inw(unsigned long port)47{48return le16_to_cpu(__opal_lpc_inw(port));49}5051static __le32 __opal_lpc_inl(unsigned long port)52{53int64_t rc;54__be32 data;5556if (opal_lpc_chip_id < 0 || port > 0xfffc)57return 0xffffffff;58if (port & 3)59return (__le32)opal_lpc_inb(port ) << 24 |60(__le32)opal_lpc_inb(port + 1) << 16 |61(__le32)opal_lpc_inb(port + 2) << 8 |62opal_lpc_inb(port + 3);63rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 4);64return rc ? 0xffffffff : be32_to_cpu(data);65}6667static u32 opal_lpc_inl(unsigned long port)68{69return le32_to_cpu(__opal_lpc_inl(port));70}7172static void opal_lpc_outb(u8 val, unsigned long port)73{74if (opal_lpc_chip_id < 0 || port > 0xffff)75return;76opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 1);77}7879static void __opal_lpc_outw(__le16 val, unsigned long port)80{81if (opal_lpc_chip_id < 0 || port > 0xfffe)82return;83if (port & 1) {84opal_lpc_outb(val >> 8, port);85opal_lpc_outb(val , port + 1);86return;87}88opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 2);89}9091static void opal_lpc_outw(u16 val, unsigned long port)92{93__opal_lpc_outw(cpu_to_le16(val), port);94}9596static void __opal_lpc_outl(__le32 val, unsigned long port)97{98if (opal_lpc_chip_id < 0 || port > 0xfffc)99return;100if (port & 3) {101opal_lpc_outb(val >> 24, port);102opal_lpc_outb(val >> 16, port + 1);103opal_lpc_outb(val >> 8, port + 2);104opal_lpc_outb(val , port + 3);105return;106}107opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 4);108}109110static void opal_lpc_outl(u32 val, unsigned long port)111{112__opal_lpc_outl(cpu_to_le32(val), port);113}114115static void opal_lpc_insb(unsigned long p, void *b, unsigned long c)116{117u8 *ptr = b;118119while(c--)120*(ptr++) = opal_lpc_inb(p);121}122123static void opal_lpc_insw(unsigned long p, void *b, unsigned long c)124{125__le16 *ptr = b;126127while(c--)128*(ptr++) = __opal_lpc_inw(p);129}130131static void opal_lpc_insl(unsigned long p, void *b, unsigned long c)132{133__le32 *ptr = b;134135while(c--)136*(ptr++) = __opal_lpc_inl(p);137}138139static void opal_lpc_outsb(unsigned long p, const void *b, unsigned long c)140{141const u8 *ptr = b;142143while(c--)144opal_lpc_outb(*(ptr++), p);145}146147static void opal_lpc_outsw(unsigned long p, const void *b, unsigned long c)148{149const __le16 *ptr = b;150151while(c--)152__opal_lpc_outw(*(ptr++), p);153}154155static void opal_lpc_outsl(unsigned long p, const void *b, unsigned long c)156{157const __le32 *ptr = b;158159while(c--)160__opal_lpc_outl(*(ptr++), p);161}162163static const struct ppc_pci_io opal_lpc_io = {164.inb = opal_lpc_inb,165.inw = opal_lpc_inw,166.inl = opal_lpc_inl,167.outb = opal_lpc_outb,168.outw = opal_lpc_outw,169.outl = opal_lpc_outl,170.insb = opal_lpc_insb,171.insw = opal_lpc_insw,172.insl = opal_lpc_insl,173.outsb = opal_lpc_outsb,174.outsw = opal_lpc_outsw,175.outsl = opal_lpc_outsl,176};177178#ifdef CONFIG_DEBUG_FS179struct lpc_debugfs_entry {180enum OpalLPCAddressType lpc_type;181};182183static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf,184size_t count, loff_t *ppos)185{186struct lpc_debugfs_entry *lpc = filp->private_data;187u32 data, pos, len, todo;188int rc;189190if (!access_ok(ubuf, count))191return -EFAULT;192193todo = count;194while (todo) {195pos = *ppos;196197/*198* Select access size based on count and alignment and199* access type. IO and MEM only support byte accesses,200* FW supports all 3.201*/202len = 1;203if (lpc->lpc_type == OPAL_LPC_FW) {204if (todo > 3 && (pos & 3) == 0)205len = 4;206else if (todo > 1 && (pos & 1) == 0)207len = 2;208}209rc = opal_lpc_read(opal_lpc_chip_id, lpc->lpc_type, pos,210&data, len);211if (rc)212return -ENXIO;213214/*215* Now there is some trickery with the data returned by OPAL216* as it's the desired data right justified in a 32-bit BE217* word.218*219* This is a very bad interface and I'm to blame for it :-(220*221* So we can't just apply a 32-bit swap to what comes from OPAL,222* because user space expects the *bytes* to be in their proper223* respective positions (ie, LPC position).224*225* So what we really want to do here is to shift data right226* appropriately on a LE kernel.227*228* IE. If the LPC transaction has bytes B0, B1, B2 and B3 in that229* order, we have in memory written to by OPAL at the "data"230* pointer:231*232* Bytes: OPAL "data" LE "data"233* 32-bit: B0 B1 B2 B3 B0B1B2B3 B3B2B1B0234* 16-bit: B0 B1 0000B0B1 B1B00000235* 8-bit: B0 000000B0 B0000000236*237* So a BE kernel will have the leftmost of the above in the MSB238* and rightmost in the LSB and can just then "cast" the u32 "data"239* down to the appropriate quantity and write it.240*241* However, an LE kernel can't. It doesn't need to swap because a242* load from data followed by a store to user are going to preserve243* the byte ordering which is the wire byte order which is what the244* user wants, but in order to "crop" to the right size, we need to245* shift right first.246*/247switch(len) {248case 4:249rc = __put_user((u32)data, (u32 __user *)ubuf);250break;251case 2:252#ifdef __LITTLE_ENDIAN__253data >>= 16;254#endif255rc = __put_user((u16)data, (u16 __user *)ubuf);256break;257default:258#ifdef __LITTLE_ENDIAN__259data >>= 24;260#endif261rc = __put_user((u8)data, (u8 __user *)ubuf);262break;263}264if (rc)265return -EFAULT;266*ppos += len;267ubuf += len;268todo -= len;269}270271return count;272}273274static ssize_t lpc_debug_write(struct file *filp, const char __user *ubuf,275size_t count, loff_t *ppos)276{277struct lpc_debugfs_entry *lpc = filp->private_data;278u32 data, pos, len, todo;279int rc;280281if (!access_ok(ubuf, count))282return -EFAULT;283284todo = count;285while (todo) {286pos = *ppos;287288/*289* Select access size based on count and alignment and290* access type. IO and MEM only support byte acceses,291* FW supports all 3.292*/293len = 1;294if (lpc->lpc_type == OPAL_LPC_FW) {295if (todo > 3 && (pos & 3) == 0)296len = 4;297else if (todo > 1 && (pos & 1) == 0)298len = 2;299}300301/*302* Similarly to the read case, we have some trickery here but303* it's different to handle. We need to pass the value to OPAL in304* a register whose layout depends on the access size. We want305* to reproduce the memory layout of the user, however we aren't306* doing a load from user and a store to another memory location307* which would achieve that. Here we pass the value to OPAL via308* a register which is expected to contain the "BE" interpretation309* of the byte sequence. IE: for a 32-bit access, byte 0 should be310* in the MSB. So here we *do* need to byteswap on LE.311*312* User bytes: LE "data" OPAL "data"313* 32-bit: B0 B1 B2 B3 B3B2B1B0 B0B1B2B3314* 16-bit: B0 B1 0000B1B0 0000B0B1315* 8-bit: B0 000000B0 000000B0316*/317switch(len) {318case 4:319rc = __get_user(data, (u32 __user *)ubuf);320data = cpu_to_be32(data);321break;322case 2:323rc = __get_user(data, (u16 __user *)ubuf);324data = cpu_to_be16(data);325break;326default:327rc = __get_user(data, (u8 __user *)ubuf);328break;329}330if (rc)331return -EFAULT;332333rc = opal_lpc_write(opal_lpc_chip_id, lpc->lpc_type, pos,334data, len);335if (rc)336return -ENXIO;337*ppos += len;338ubuf += len;339todo -= len;340}341342return count;343}344345static const struct file_operations lpc_fops = {346.read = lpc_debug_read,347.write = lpc_debug_write,348.open = simple_open,349.llseek = default_llseek,350};351352static int opal_lpc_debugfs_create_type(struct dentry *folder,353const char *fname,354enum OpalLPCAddressType type)355{356struct lpc_debugfs_entry *entry;357entry = kzalloc(sizeof(*entry), GFP_KERNEL);358if (!entry)359return -ENOMEM;360entry->lpc_type = type;361debugfs_create_file(fname, 0600, folder, entry, &lpc_fops);362return 0;363}364365static int opal_lpc_init_debugfs(void)366{367struct dentry *root;368int rc = 0;369370if (opal_lpc_chip_id < 0)371return -ENODEV;372373root = debugfs_create_dir("lpc", arch_debugfs_dir);374375rc |= opal_lpc_debugfs_create_type(root, "io", OPAL_LPC_IO);376rc |= opal_lpc_debugfs_create_type(root, "mem", OPAL_LPC_MEM);377rc |= opal_lpc_debugfs_create_type(root, "fw", OPAL_LPC_FW);378return rc;379}380machine_device_initcall(powernv, opal_lpc_init_debugfs);381#endif /* CONFIG_DEBUG_FS */382383void __init opal_lpc_init(void)384{385struct device_node *np;386387/*388* Look for a Power8 LPC bus tagged as "primary",389* we currently support only one though the OPAL APIs390* support any number.391*/392for_each_compatible_node(np, NULL, "ibm,power8-lpc") {393if (!of_device_is_available(np))394continue;395if (!of_property_present(np, "primary"))396continue;397opal_lpc_chip_id = of_get_ibm_chip_id(np);398of_node_put(np);399break;400}401if (opal_lpc_chip_id < 0)402return;403404/* Does it support direct mapping ? */405if (of_property_present(np, "ranges")) {406pr_info("OPAL: Found memory mapped LPC bus on chip %d\n",407opal_lpc_chip_id);408isa_bridge_init_non_pci(np);409} else {410pr_info("OPAL: Found non-mapped LPC bus on chip %d\n",411opal_lpc_chip_id);412413/* Setup special IO ops */414ppc_pci_io = opal_lpc_io;415isa_io_special = true;416}417}418419420