isst_if_mmio.c (4877B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel Speed Select Interface: MMIO Interface 4 * Copyright (c) 2019, Intel Corporation. 5 * All rights reserved. 6 * 7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 8 */ 9 10#include <linux/module.h> 11#include <linux/pci.h> 12#include <linux/sched/signal.h> 13#include <linux/uaccess.h> 14#include <uapi/linux/isst_if.h> 15 16#include "isst_if_common.h" 17 18struct isst_mmio_range { 19 int beg; 20 int end; 21}; 22 23static struct isst_mmio_range mmio_range_devid_0[] = { 24 {0x04, 0x14}, 25 {0x20, 0xD0}, 26}; 27 28static struct isst_mmio_range mmio_range_devid_1[] = { 29 {0x04, 0x14}, 30 {0x20, 0x11C}, 31}; 32 33struct isst_if_device { 34 void __iomem *punit_mmio; 35 u32 range_0[5]; 36 u32 range_1[64]; 37 struct isst_mmio_range *mmio_range; 38 struct mutex mutex; 39}; 40 41static long isst_if_mmio_rd_wr(u8 *cmd_ptr, int *write_only, int resume) 42{ 43 struct isst_if_device *punit_dev; 44 struct isst_if_io_reg *io_reg; 45 struct pci_dev *pdev; 46 47 io_reg = (struct isst_if_io_reg *)cmd_ptr; 48 49 if (io_reg->reg % 4) 50 return -EINVAL; 51 52 if (io_reg->read_write && !capable(CAP_SYS_ADMIN)) 53 return -EPERM; 54 55 pdev = isst_if_get_pci_dev(io_reg->logical_cpu, 0, 0, 1); 56 if (!pdev) 57 return -EINVAL; 58 59 punit_dev = pci_get_drvdata(pdev); 60 if (!punit_dev) 61 return -EINVAL; 62 63 if (io_reg->reg < punit_dev->mmio_range[0].beg || 64 io_reg->reg > punit_dev->mmio_range[1].end) 65 return -EINVAL; 66 67 /* 68 * Ensure that operation is complete on a PCI device to avoid read 69 * write race by using per PCI device mutex. 70 */ 71 mutex_lock(&punit_dev->mutex); 72 if (io_reg->read_write) { 73 writel(io_reg->value, punit_dev->punit_mmio+io_reg->reg); 74 *write_only = 1; 75 } else { 76 io_reg->value = readl(punit_dev->punit_mmio+io_reg->reg); 77 *write_only = 0; 78 } 79 mutex_unlock(&punit_dev->mutex); 80 81 return 0; 82} 83 84static const struct pci_device_id isst_if_ids[] = { 85 { PCI_DEVICE_DATA(INTEL, RAPL_PRIO_DEVID_0, &mmio_range_devid_0)}, 86 { PCI_DEVICE_DATA(INTEL, RAPL_PRIO_DEVID_1, &mmio_range_devid_1)}, 87 { 0 }, 88}; 89MODULE_DEVICE_TABLE(pci, isst_if_ids); 90 91static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 92{ 93 struct isst_if_device *punit_dev; 94 struct isst_if_cmd_cb cb; 95 u32 mmio_base, pcu_base; 96 u64 base_addr; 97 int ret; 98 99 punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL); 100 if (!punit_dev) 101 return -ENOMEM; 102 103 ret = pcim_enable_device(pdev); 104 if (ret) 105 return ret; 106 107 ret = pci_read_config_dword(pdev, 0xD0, &mmio_base); 108 if (ret) 109 return ret; 110 111 ret = pci_read_config_dword(pdev, 0xFC, &pcu_base); 112 if (ret) 113 return ret; 114 115 pcu_base &= GENMASK(10, 0); 116 base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12; 117 punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256); 118 if (!punit_dev->punit_mmio) 119 return -ENOMEM; 120 121 mutex_init(&punit_dev->mutex); 122 pci_set_drvdata(pdev, punit_dev); 123 punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; 124 125 memset(&cb, 0, sizeof(cb)); 126 cb.cmd_size = sizeof(struct isst_if_io_reg); 127 cb.offset = offsetof(struct isst_if_io_regs, io_reg); 128 cb.cmd_callback = isst_if_mmio_rd_wr; 129 cb.owner = THIS_MODULE; 130 ret = isst_if_cdev_register(ISST_IF_DEV_MMIO, &cb); 131 if (ret) 132 mutex_destroy(&punit_dev->mutex); 133 134 return ret; 135} 136 137static void isst_if_remove(struct pci_dev *pdev) 138{ 139 struct isst_if_device *punit_dev; 140 141 punit_dev = pci_get_drvdata(pdev); 142 isst_if_cdev_unregister(ISST_IF_DEV_MMIO); 143 mutex_destroy(&punit_dev->mutex); 144} 145 146static int __maybe_unused isst_if_suspend(struct device *device) 147{ 148 struct isst_if_device *punit_dev = dev_get_drvdata(device); 149 int i; 150 151 for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) 152 punit_dev->range_0[i] = readl(punit_dev->punit_mmio + 153 punit_dev->mmio_range[0].beg + 4 * i); 154 for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) { 155 u32 addr; 156 157 addr = punit_dev->mmio_range[1].beg + 4 * i; 158 if (addr > punit_dev->mmio_range[1].end) 159 break; 160 punit_dev->range_1[i] = readl(punit_dev->punit_mmio + addr); 161 } 162 163 return 0; 164} 165 166static int __maybe_unused isst_if_resume(struct device *device) 167{ 168 struct isst_if_device *punit_dev = dev_get_drvdata(device); 169 int i; 170 171 for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) 172 writel(punit_dev->range_0[i], punit_dev->punit_mmio + 173 punit_dev->mmio_range[0].beg + 4 * i); 174 for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) { 175 u32 addr; 176 177 addr = punit_dev->mmio_range[1].beg + 4 * i; 178 if (addr > punit_dev->mmio_range[1].end) 179 break; 180 181 writel(punit_dev->range_1[i], punit_dev->punit_mmio + addr); 182 } 183 184 return 0; 185} 186 187static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, isst_if_suspend, isst_if_resume); 188 189static struct pci_driver isst_if_pci_driver = { 190 .name = "isst_if_pci", 191 .id_table = isst_if_ids, 192 .probe = isst_if_probe, 193 .remove = isst_if_remove, 194 .driver.pm = &isst_if_pm_ops, 195}; 196 197module_pci_driver(isst_if_pci_driver); 198 199MODULE_LICENSE("GPL v2"); 200MODULE_DESCRIPTION("Intel speed select interface mmio driver");