Immutable branch between MFD, EDAC, I2C, LEDs, PinCtrl, Platform and Watchdog due for the v5.20 merge window
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmLP5zwACgkQUa+KL4f8 d2F7ug/+JZHACjvMyqTbzxz9aYegb1VDpnxd+h8eYIKx4KNc0C8YfLhi4949T8cp RWuc98Z4MHvFQhe+KOH2CS9mxx6NUHlrNIZiDgDBQB9lUVHD6/U9mFrj4wlYt9Cw PMvUzJAzuQvkZPoDeA4EI8Sk6/5kaR0ZbaHAZM2ejAomJpdqPoOC3HMVsFVR6YT6 0QBsigPMBlHGCqw0DAEf6lPtXdItnaiA5BxmO1dTQHJhupumzam0+0O2OYXtGubG jZQd6L9fCDaGuHo0Vl9upfCkeNEw5tvp8rjFkZIBmajzcwAiT6iZBmaqC+GO8j1T ofq1hXpfklnAHJyXppSzxbAEuYLgyVnJM11gtPDLdowYmbpFpPay7OvktRgp9fwH Ctk6WXkNRJDEN32+3C5OgK/cJT7JP1uLzNxGf6x+g+3dmWO421eL47roHzacGNxj MyhNemXXViXSliBKUkFY8LC0JQZs1kfMoVYmftRZ4aaw510KVT4ltuURetKl/MeR +FjLNxn5rOfFj1Qqg/wF6S9vJ2YNSmawTcY/1TEuEyzpHlH3oBLcK2cojAtpCTjG DH9mqT5tuqvPdztGkT5ppTdLaJXv0q4bcbXQbF3GdTsr6cftx68eBc62k53pUtAb DB/9Xi7eihFvwqlAReWvUu/UjDObCNSg88GcO4cyePzD9DPf7Yc= =1y62 -----END PGP SIGNATURE----- Merge tag 'ib-mfd-edac-i2c-leds-pinctrl-platform-watchdog-v5.20' into review-hans Immutable branch between MFD, EDAC, I2C, LEDs, PinCtrl, Platform and Watchdog due for the v5.20 merge window
This commit is contained in:
commit
8906ced9a9
@ -263,6 +263,7 @@ config EDAC_I10NM
|
||||
config EDAC_PND2
|
||||
tristate "Intel Pondicherry2"
|
||||
depends on PCI && X86_64 && X86_MCE_INTEL
|
||||
select P2SB if X86
|
||||
help
|
||||
Support for error detection and correction on the Intel
|
||||
Pondicherry2 Integrated Memory Controller. This SoC IP is
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_data/x86/p2sb.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/processor.h>
|
||||
@ -232,42 +234,14 @@ static u64 get_mem_ctrl_hub_base_addr(void)
|
||||
return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15);
|
||||
}
|
||||
|
||||
static u64 get_sideband_reg_base_addr(void)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
u32 hi, lo;
|
||||
u8 hidden;
|
||||
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x19dd, NULL);
|
||||
if (pdev) {
|
||||
/* Unhide the P2SB device, if it's hidden */
|
||||
pci_read_config_byte(pdev, 0xe1, &hidden);
|
||||
if (hidden)
|
||||
pci_write_config_byte(pdev, 0xe1, 0);
|
||||
|
||||
pci_read_config_dword(pdev, 0x10, &lo);
|
||||
pci_read_config_dword(pdev, 0x14, &hi);
|
||||
lo &= 0xfffffff0;
|
||||
|
||||
/* Hide the P2SB device, if it was hidden before */
|
||||
if (hidden)
|
||||
pci_write_config_byte(pdev, 0xe1, hidden);
|
||||
|
||||
pci_dev_put(pdev);
|
||||
return (U64_LSHIFT(hi, 32) | U64_LSHIFT(lo, 0));
|
||||
} else {
|
||||
return 0xfd000000;
|
||||
}
|
||||
}
|
||||
|
||||
#define DNV_MCHBAR_SIZE 0x8000
|
||||
#define DNV_SB_PORT_SIZE 0x10000
|
||||
static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
char *base;
|
||||
u64 addr;
|
||||
unsigned long size;
|
||||
void __iomem *base;
|
||||
struct resource r;
|
||||
int ret;
|
||||
|
||||
if (op == 4) {
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL);
|
||||
@ -279,26 +253,30 @@ static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *na
|
||||
} else {
|
||||
/* MMIO via memory controller hub base address */
|
||||
if (op == 0 && port == 0x4c) {
|
||||
addr = get_mem_ctrl_hub_base_addr();
|
||||
if (!addr)
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
||||
r.start = get_mem_ctrl_hub_base_addr();
|
||||
if (!r.start)
|
||||
return -ENODEV;
|
||||
size = DNV_MCHBAR_SIZE;
|
||||
r.end = r.start + DNV_MCHBAR_SIZE - 1;
|
||||
} else {
|
||||
/* MMIO via sideband register base address */
|
||||
addr = get_sideband_reg_base_addr();
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
addr += (port << 16);
|
||||
size = DNV_SB_PORT_SIZE;
|
||||
ret = p2sb_bar(NULL, 0, &r);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
r.start += (port << 16);
|
||||
r.end = r.start + DNV_SB_PORT_SIZE - 1;
|
||||
}
|
||||
|
||||
base = ioremap((resource_size_t)addr, size);
|
||||
base = ioremap(r.start, resource_size(&r));
|
||||
if (!base)
|
||||
return -ENODEV;
|
||||
|
||||
if (sz == 8)
|
||||
*(u32 *)(data + 4) = *(u32 *)(base + off + 4);
|
||||
*(u32 *)data = *(u32 *)(base + off);
|
||||
*(u64 *)data = readq(base + off);
|
||||
else
|
||||
*(u32 *)data = readl(base + off);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ config I2C_HIX5HD2
|
||||
config I2C_I801
|
||||
tristate "Intel 82801 (ICH/PCH)"
|
||||
depends on PCI
|
||||
select P2SB if X86
|
||||
select CHECK_SIGNATURE if X86 && DMI
|
||||
select I2C_SMBUS
|
||||
help
|
||||
|
@ -111,6 +111,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
#include <linux/platform_data/x86/p2sb.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
@ -140,7 +141,6 @@
|
||||
#define TCOBASE 0x050
|
||||
#define TCOCTL 0x054
|
||||
|
||||
#define SBREG_BAR 0x10
|
||||
#define SBREG_SMBCTRL 0xc6000c
|
||||
#define SBREG_SMBCTRL_DNV 0xcf000c
|
||||
|
||||
@ -1482,45 +1482,24 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
.version = 4,
|
||||
};
|
||||
struct resource *res;
|
||||
unsigned int devfn;
|
||||
u64 base64_addr;
|
||||
u32 base_addr;
|
||||
u8 hidden;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We must access the NO_REBOOT bit over the Primary to Sideband
|
||||
* bridge (P2SB). The BIOS prevents the P2SB device from being
|
||||
* enumerated by the PCI subsystem, so we need to unhide/hide it
|
||||
* to lookup the P2SB BAR.
|
||||
* (P2SB) bridge.
|
||||
*/
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
|
||||
|
||||
/* Unhide the P2SB device, if it is hidden */
|
||||
pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden);
|
||||
if (hidden)
|
||||
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);
|
||||
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
|
||||
base64_addr = base_addr & 0xfffffff0;
|
||||
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
|
||||
base64_addr |= (u64)base_addr << 32;
|
||||
|
||||
/* Hide the P2SB device, if it was hidden before */
|
||||
if (hidden)
|
||||
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden);
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
res = &tco_res[1];
|
||||
ret = p2sb_bar(pci_dev->bus, 0, res);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
|
||||
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL_DNV;
|
||||
res->start += SBREG_SMBCTRL_DNV;
|
||||
else
|
||||
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
|
||||
res->start += SBREG_SMBCTRL;
|
||||
|
||||
res->end = res->start + 3;
|
||||
res->flags = IORESOURCE_MEM;
|
||||
|
||||
return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
|
||||
tco_res, 2, &pldata, sizeof(pldata));
|
||||
|
@ -1,11 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config LEDS_SIEMENS_SIMATIC_IPC
|
||||
tristate "LED driver for Siemens Simatic IPCs"
|
||||
depends on LEDS_CLASS
|
||||
depends on LEDS_GPIO
|
||||
depends on SIEMENS_SIMATIC_IPC
|
||||
help
|
||||
This option enables support for the LEDs of several Industrial PCs
|
||||
from Siemens.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called simatic-ipc-leds.
|
||||
To compile this driver as a module, choose M here: the modules
|
||||
will be called simatic-ipc-leds and simatic-ipc-leds-gpio.
|
||||
|
@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o
|
||||
|
105
drivers/leds/simple/simatic-ipc-leds-gpio.c
Normal file
105
drivers/leds/simple/simatic-ipc-leds-gpio.c
Normal file
@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Siemens SIMATIC IPC driver for GPIO based LEDs
|
||||
*
|
||||
* Copyright (c) Siemens AG, 2022
|
||||
*
|
||||
* Authors:
|
||||
* Henning Schild <henning.schild@siemens.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 0, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 1, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 2, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 3, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 4, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 5, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct gpio_led simatic_ipc_gpio_leds[] = {
|
||||
{ .name = "green:" LED_FUNCTION_STATUS "-3" },
|
||||
{ .name = "red:" LED_FUNCTION_STATUS "-1" },
|
||||
{ .name = "green:" LED_FUNCTION_STATUS "-1" },
|
||||
{ .name = "red:" LED_FUNCTION_STATUS "-2" },
|
||||
{ .name = "green:" LED_FUNCTION_STATUS "-2" },
|
||||
{ .name = "red:" LED_FUNCTION_STATUS "-3" },
|
||||
};
|
||||
|
||||
static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = {
|
||||
.num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds),
|
||||
.leds = simatic_ipc_gpio_leds,
|
||||
};
|
||||
|
||||
static struct platform_device *simatic_leds_pdev;
|
||||
|
||||
static int simatic_ipc_leds_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
gpiod_remove_lookup_table(&simatic_ipc_led_gpio_table);
|
||||
platform_device_unregister(simatic_leds_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simatic_ipc_leds_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_desc *gpiod;
|
||||
int err;
|
||||
|
||||
gpiod_add_lookup_table(&simatic_ipc_led_gpio_table);
|
||||
simatic_leds_pdev = platform_device_register_resndata(NULL,
|
||||
"leds-gpio", PLATFORM_DEVID_NONE, NULL, 0,
|
||||
&simatic_ipc_gpio_leds_pdata,
|
||||
sizeof(simatic_ipc_gpio_leds_pdata));
|
||||
if (IS_ERR(simatic_leds_pdev)) {
|
||||
err = PTR_ERR(simatic_leds_pdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* PM_BIOS_BOOT_N */
|
||||
gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 6, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpiod)) {
|
||||
err = PTR_ERR(gpiod);
|
||||
goto out;
|
||||
}
|
||||
gpiod_put(gpiod);
|
||||
|
||||
/* PM_WDT_OUT */
|
||||
gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 7, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpiod)) {
|
||||
err = PTR_ERR(gpiod);
|
||||
goto out;
|
||||
}
|
||||
gpiod_put(gpiod);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
simatic_ipc_leds_gpio_remove(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver simatic_ipc_led_gpio_driver = {
|
||||
.probe = simatic_ipc_leds_gpio_probe,
|
||||
.remove = simatic_ipc_leds_gpio_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
}
|
||||
};
|
||||
module_platform_driver(simatic_ipc_led_gpio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" KBUILD_MODNAME);
|
||||
MODULE_SOFTDEP("pre: platform:leds-gpio");
|
||||
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
|
@ -23,7 +23,7 @@
|
||||
#define SIMATIC_IPC_LED_PORT_BASE 0x404E
|
||||
|
||||
struct simatic_ipc_led {
|
||||
unsigned int value; /* mask for io and offset for mem */
|
||||
unsigned int value; /* mask for io */
|
||||
char *name;
|
||||
struct led_classdev cdev;
|
||||
};
|
||||
@ -38,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
/* the actual start will be discovered with PCI, 0 is a placeholder */
|
||||
static struct resource simatic_ipc_led_mem_res = DEFINE_RES_MEM_NAMED(0, SZ_4K, KBUILD_MODNAME);
|
||||
|
||||
static void __iomem *simatic_ipc_led_memory;
|
||||
|
||||
static struct simatic_ipc_led simatic_ipc_leds_mem[] = {
|
||||
{0x500 + 0x1A0, "red:" LED_FUNCTION_STATUS "-1"},
|
||||
{0x500 + 0x1A8, "green:" LED_FUNCTION_STATUS "-1"},
|
||||
{0x500 + 0x1C8, "red:" LED_FUNCTION_STATUS "-2"},
|
||||
{0x500 + 0x1D0, "green:" LED_FUNCTION_STATUS "-2"},
|
||||
{0x500 + 0x1E0, "red:" LED_FUNCTION_STATUS "-3"},
|
||||
{0x500 + 0x198, "green:" LED_FUNCTION_STATUS "-3"},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct resource simatic_ipc_led_io_res =
|
||||
DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME);
|
||||
|
||||
@ -88,28 +73,6 @@ static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd)
|
||||
return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness;
|
||||
}
|
||||
|
||||
static void simatic_ipc_led_set_mem(struct led_classdev *led_cd,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct simatic_ipc_led *led = cdev_to_led(led_cd);
|
||||
void __iomem *reg = simatic_ipc_led_memory + led->value;
|
||||
u32 val;
|
||||
|
||||
val = readl(reg);
|
||||
val = (val & ~1) | (brightness == LED_OFF);
|
||||
writel(val, reg);
|
||||
}
|
||||
|
||||
static enum led_brightness simatic_ipc_led_get_mem(struct led_classdev *led_cd)
|
||||
{
|
||||
struct simatic_ipc_led *led = cdev_to_led(led_cd);
|
||||
void __iomem *reg = simatic_ipc_led_memory + led->value;
|
||||
u32 val;
|
||||
|
||||
val = readl(reg);
|
||||
return (val & 1) ? LED_OFF : led_cd->max_brightness;
|
||||
}
|
||||
|
||||
static int simatic_ipc_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
|
||||
@ -117,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
|
||||
struct simatic_ipc_led *ipcled;
|
||||
struct led_classdev *cdev;
|
||||
struct resource *res;
|
||||
void __iomem *reg;
|
||||
int err, type;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
switch (plat->devmode) {
|
||||
case SIMATIC_IPC_DEVICE_227D:
|
||||
@ -134,52 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
|
||||
}
|
||||
ipcled = simatic_ipc_leds_io;
|
||||
}
|
||||
type = IORESOURCE_IO;
|
||||
if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) {
|
||||
dev_err(dev, "Unable to register IO resource at %pR\n", res);
|
||||
return -EBUSY;
|
||||
}
|
||||
break;
|
||||
case SIMATIC_IPC_DEVICE_127E:
|
||||
res = &simatic_ipc_led_mem_res;
|
||||
ipcled = simatic_ipc_leds_mem;
|
||||
type = IORESOURCE_MEM;
|
||||
|
||||
/* get GPIO base from PCI */
|
||||
res->start = simatic_ipc_get_membase0(PCI_DEVFN(13, 0));
|
||||
if (res->start == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* do the final address calculation */
|
||||
res->start = res->start + (0xC5 << 16);
|
||||
res->end += res->start;
|
||||
|
||||
simatic_ipc_led_memory = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(simatic_ipc_led_memory))
|
||||
return PTR_ERR(simatic_ipc_led_memory);
|
||||
|
||||
/* initialize power/watchdog LED */
|
||||
reg = simatic_ipc_led_memory + 0x500 + 0x1D8; /* PM_WDT_OUT */
|
||||
val = readl(reg);
|
||||
writel(val & ~1, reg);
|
||||
|
||||
reg = simatic_ipc_led_memory + 0x500 + 0x1C0; /* PM_BIOS_BOOT_N */
|
||||
val = readl(reg);
|
||||
writel(val | 1, reg);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
while (ipcled->value) {
|
||||
cdev = &ipcled->cdev;
|
||||
if (type == IORESOURCE_MEM) {
|
||||
cdev->brightness_set = simatic_ipc_led_set_mem;
|
||||
cdev->brightness_get = simatic_ipc_led_get_mem;
|
||||
} else {
|
||||
cdev->brightness_set = simatic_ipc_led_set_io;
|
||||
cdev->brightness_get = simatic_ipc_led_get_io;
|
||||
}
|
||||
cdev->brightness_set = simatic_ipc_led_set_io;
|
||||
cdev->brightness_get = simatic_ipc_led_get_io;
|
||||
cdev->max_brightness = LED_ON;
|
||||
cdev->name = ipcled->name;
|
||||
|
||||
|
@ -572,6 +572,7 @@ config LPC_ICH
|
||||
tristate "Intel ICH LPC"
|
||||
depends on PCI
|
||||
select MFD_CORE
|
||||
select P2SB if X86
|
||||
help
|
||||
The LPC bridge function of the Intel ICH provides support for
|
||||
many functional units. This driver provides needed support for
|
||||
|
@ -8,7 +8,8 @@
|
||||
* Configuration Registers.
|
||||
*
|
||||
* This driver is derived from lpc_sch.
|
||||
|
||||
*
|
||||
* Copyright (c) 2017, 2021-2022 Intel Corporation
|
||||
* Copyright (c) 2011 Extreme Engineering Solution, Inc.
|
||||
* Author: Aaron Sierra <asierra@xes-inc.com>
|
||||
*
|
||||
@ -42,9 +43,11 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
#include <linux/platform_data/x86/p2sb.h>
|
||||
|
||||
#define ACPIBASE 0x40
|
||||
#define ACPIBASE_GPE_OFF 0x28
|
||||
@ -71,8 +74,6 @@
|
||||
#define BCR 0xdc
|
||||
#define BCR_WPD BIT(0)
|
||||
|
||||
#define SPIBASE_APL_SZ 4096
|
||||
|
||||
#define GPIOBASE_ICH0 0x58
|
||||
#define GPIOCTRL_ICH0 0x5C
|
||||
#define GPIOBASE_ICH6 0x48
|
||||
@ -143,6 +144,73 @@ static struct mfd_cell lpc_ich_gpio_cell = {
|
||||
.ignore_resource_conflicts = true,
|
||||
};
|
||||
|
||||
#define APL_GPIO_NORTH 0
|
||||
#define APL_GPIO_NORTHWEST 1
|
||||
#define APL_GPIO_WEST 2
|
||||
#define APL_GPIO_SOUTHWEST 3
|
||||
#define APL_GPIO_NR_DEVICES 4
|
||||
|
||||
/* Offset data for Apollo Lake GPIO controllers */
|
||||
static resource_size_t apl_gpio_offsets[APL_GPIO_NR_DEVICES] = {
|
||||
[APL_GPIO_NORTH] = 0xc50000,
|
||||
[APL_GPIO_NORTHWEST] = 0xc40000,
|
||||
[APL_GPIO_WEST] = 0xc70000,
|
||||
[APL_GPIO_SOUTHWEST] = 0xc00000,
|
||||
};
|
||||
|
||||
#define APL_GPIO_RESOURCE_SIZE 0x1000
|
||||
|
||||
#define APL_GPIO_IRQ 14
|
||||
|
||||
static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = {
|
||||
[APL_GPIO_NORTH] = {
|
||||
DEFINE_RES_MEM(0, 0),
|
||||
DEFINE_RES_IRQ(APL_GPIO_IRQ),
|
||||
},
|
||||
[APL_GPIO_NORTHWEST] = {
|
||||
DEFINE_RES_MEM(0, 0),
|
||||
DEFINE_RES_IRQ(APL_GPIO_IRQ),
|
||||
},
|
||||
[APL_GPIO_WEST] = {
|
||||
DEFINE_RES_MEM(0, 0),
|
||||
DEFINE_RES_IRQ(APL_GPIO_IRQ),
|
||||
},
|
||||
[APL_GPIO_SOUTHWEST] = {
|
||||
DEFINE_RES_MEM(0, 0),
|
||||
DEFINE_RES_IRQ(APL_GPIO_IRQ),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell apl_gpio_devices[APL_GPIO_NR_DEVICES] = {
|
||||
[APL_GPIO_NORTH] = {
|
||||
.name = "apollolake-pinctrl",
|
||||
.id = APL_GPIO_NORTH,
|
||||
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTH]),
|
||||
.resources = apl_gpio_resources[APL_GPIO_NORTH],
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
[APL_GPIO_NORTHWEST] = {
|
||||
.name = "apollolake-pinctrl",
|
||||
.id = APL_GPIO_NORTHWEST,
|
||||
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTHWEST]),
|
||||
.resources = apl_gpio_resources[APL_GPIO_NORTHWEST],
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
[APL_GPIO_WEST] = {
|
||||
.name = "apollolake-pinctrl",
|
||||
.id = APL_GPIO_WEST,
|
||||
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_WEST]),
|
||||
.resources = apl_gpio_resources[APL_GPIO_WEST],
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
[APL_GPIO_SOUTHWEST] = {
|
||||
.name = "apollolake-pinctrl",
|
||||
.id = APL_GPIO_SOUTHWEST,
|
||||
.num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_SOUTHWEST]),
|
||||
.resources = apl_gpio_resources[APL_GPIO_SOUTHWEST],
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lpc_ich_spi_cell = {
|
||||
.name = "intel-spi",
|
||||
@ -1086,6 +1154,34 @@ wdt_done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpc_ich_init_pinctrl(struct pci_dev *dev)
|
||||
{
|
||||
struct resource base;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* Check, if GPIO has been exported as an ACPI device */
|
||||
if (acpi_dev_present("INT3452", NULL, -1))
|
||||
return -EEXIST;
|
||||
|
||||
ret = p2sb_bar(dev->bus, 0, &base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(apl_gpio_devices); i++) {
|
||||
struct resource *mem = &apl_gpio_resources[i][0];
|
||||
resource_size_t offset = apl_gpio_offsets[i];
|
||||
|
||||
/* Fill MEM resource */
|
||||
mem->start = base.start + offset;
|
||||
mem->end = base.start + offset + APL_GPIO_RESOURCE_SIZE - 1;
|
||||
mem->flags = base.flags;
|
||||
}
|
||||
|
||||
return mfd_add_devices(&dev->dev, 0, apl_gpio_devices,
|
||||
ARRAY_SIZE(apl_gpio_devices), NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
|
||||
{
|
||||
u32 val;
|
||||
@ -1100,35 +1196,32 @@ static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
|
||||
return val & BYT_BCR_WPD;
|
||||
}
|
||||
|
||||
static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
|
||||
static bool lpc_ich_set_writeable(struct pci_bus *bus, unsigned int devfn)
|
||||
{
|
||||
struct pci_dev *pdev = data;
|
||||
u32 bcr;
|
||||
|
||||
pci_read_config_dword(pdev, BCR, &bcr);
|
||||
pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
|
||||
if (!(bcr & BCR_WPD)) {
|
||||
bcr |= BCR_WPD;
|
||||
pci_write_config_dword(pdev, BCR, bcr);
|
||||
pci_read_config_dword(pdev, BCR, &bcr);
|
||||
pci_bus_write_config_dword(bus, devfn, BCR, bcr);
|
||||
pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
|
||||
}
|
||||
|
||||
return bcr & BCR_WPD;
|
||||
}
|
||||
|
||||
static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
|
||||
{
|
||||
struct pci_dev *pdev = data;
|
||||
|
||||
return lpc_ich_set_writeable(pdev->bus, pdev->devfn);
|
||||
}
|
||||
|
||||
static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data)
|
||||
{
|
||||
unsigned int spi = PCI_DEVFN(13, 2);
|
||||
struct pci_bus *bus = data;
|
||||
u32 bcr;
|
||||
struct pci_dev *pdev = data;
|
||||
|
||||
pci_bus_read_config_dword(bus, spi, BCR, &bcr);
|
||||
if (!(bcr & BCR_WPD)) {
|
||||
bcr |= BCR_WPD;
|
||||
pci_bus_write_config_dword(bus, spi, BCR, bcr);
|
||||
pci_bus_read_config_dword(bus, spi, BCR, &bcr);
|
||||
}
|
||||
|
||||
return bcr & BCR_WPD;
|
||||
return lpc_ich_set_writeable(pdev->bus, PCI_DEVFN(13, 2));
|
||||
}
|
||||
|
||||
static int lpc_ich_init_spi(struct pci_dev *dev)
|
||||
@ -1137,6 +1230,7 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
|
||||
struct resource *res = &intel_spi_res[0];
|
||||
struct intel_spi_boardinfo *info;
|
||||
u32 spi_base, rcba;
|
||||
int ret;
|
||||
|
||||
info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
@ -1167,30 +1261,19 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
|
||||
}
|
||||
break;
|
||||
|
||||
case INTEL_SPI_BXT: {
|
||||
unsigned int p2sb = PCI_DEVFN(13, 0);
|
||||
unsigned int spi = PCI_DEVFN(13, 2);
|
||||
struct pci_bus *bus = dev->bus;
|
||||
|
||||
case INTEL_SPI_BXT:
|
||||
/*
|
||||
* The P2SB is hidden by BIOS and we need to unhide it in
|
||||
* order to read BAR of the SPI flash device. Once that is
|
||||
* done we hide it again.
|
||||
*/
|
||||
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0);
|
||||
pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0,
|
||||
&spi_base);
|
||||
if (spi_base != ~0) {
|
||||
res->start = spi_base & 0xfffffff0;
|
||||
res->end = res->start + SPIBASE_APL_SZ - 1;
|
||||
ret = p2sb_bar(dev->bus, PCI_DEVFN(13, 2), res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->set_writeable = lpc_ich_bxt_set_writeable;
|
||||
info->data = bus;
|
||||
}
|
||||
|
||||
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1);
|
||||
info->set_writeable = lpc_ich_bxt_set_writeable;
|
||||
info->data = dev;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -1249,6 +1332,12 @@ static int lpc_ich_probe(struct pci_dev *dev,
|
||||
cell_added = true;
|
||||
}
|
||||
|
||||
if (priv->chipset == LPC_APL) {
|
||||
ret = lpc_ich_init_pinctrl(dev);
|
||||
if (!ret)
|
||||
cell_added = true;
|
||||
}
|
||||
|
||||
if (lpc_chipset_info[priv->chipset].spi_type) {
|
||||
ret = lpc_ich_init_spi(dev);
|
||||
if (!ret)
|
||||
|
@ -1641,16 +1641,14 @@ EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid);
|
||||
|
||||
const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_device *pdev)
|
||||
{
|
||||
const struct intel_pinctrl_soc_data * const *table;
|
||||
const struct intel_pinctrl_soc_data *data = NULL;
|
||||
const struct intel_pinctrl_soc_data **table;
|
||||
struct acpi_device *adev;
|
||||
unsigned int i;
|
||||
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (adev) {
|
||||
const void *match = device_get_match_data(&pdev->dev);
|
||||
table = device_get_match_data(&pdev->dev);
|
||||
if (table) {
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
unsigned int i;
|
||||
|
||||
table = (const struct intel_pinctrl_soc_data **)match;
|
||||
for (i = 0; table[i]; i++) {
|
||||
if (!strcmp(adev->pnp.unique_id, table[i]->uid)) {
|
||||
data = table[i];
|
||||
@ -1664,7 +1662,7 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_
|
||||
if (!id)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
table = (const struct intel_pinctrl_soc_data **)id->driver_data;
|
||||
table = (const struct intel_pinctrl_soc_data * const *)id->driver_data;
|
||||
data = table[pdev->id];
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,18 @@ config INTEL_OAKTRAIL
|
||||
enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
|
||||
here; it will only load on supported platforms.
|
||||
|
||||
config P2SB
|
||||
bool "Primary to Sideband (P2SB) bridge access support"
|
||||
depends on PCI
|
||||
help
|
||||
The Primary to Sideband (P2SB) bridge is an interface to some
|
||||
PCI devices connected through it. In particular, SPI NOR controller
|
||||
in Intel Apollo Lake SoC is one of such devices.
|
||||
|
||||
The main purpose of this library is to unhide P2SB device in case
|
||||
firmware kept it hidden on some platforms in order to access devices
|
||||
behind it.
|
||||
|
||||
config INTEL_BXTWC_PMIC_TMU
|
||||
tristate "Intel Broxton Whiskey Cove TMU Driver"
|
||||
depends on INTEL_SOC_PMIC_BXTWC
|
||||
|
@ -28,6 +28,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o
|
||||
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
|
||||
intel_oaktrail-y := oaktrail.o
|
||||
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
|
||||
intel_p2sb-y := p2sb.o
|
||||
obj-$(CONFIG_P2SB) += intel_p2sb.o
|
||||
intel_sdsi-y := sdsi.o
|
||||
obj-$(CONFIG_INTEL_SDSI) += intel_sdsi.o
|
||||
intel_vsec-y := vsec.o
|
||||
|
133
drivers/platform/x86/intel/p2sb.c
Normal file
133
drivers/platform/x86/intel/p2sb.c
Normal file
@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Primary to Sideband (P2SB) bridge access support
|
||||
*
|
||||
* Copyright (c) 2017, 2021-2022 Intel Corporation.
|
||||
*
|
||||
* Authors: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
* Jonathan Yong <jonathan.yong@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_data/x86/p2sb.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
||||
#define P2SBC 0xe0
|
||||
#define P2SBC_HIDE BIT(8)
|
||||
|
||||
static const struct x86_cpu_id p2sb_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, PCI_DEVFN(13, 0)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, PCI_DEVFN(31, 1)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_D, PCI_DEVFN(31, 1)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, PCI_DEVFN(31, 1)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, PCI_DEVFN(31, 1)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, PCI_DEVFN(31, 1)),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, PCI_DEVFN(31, 1)),
|
||||
{}
|
||||
};
|
||||
|
||||
static int p2sb_get_devfn(unsigned int *devfn)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
id = x86_match_cpu(p2sb_cpu_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
*devfn = (unsigned int)id->driver_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
|
||||
{
|
||||
/* Copy resource from the first BAR of the device in question */
|
||||
*mem = pdev->resource[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
int ret;
|
||||
|
||||
pdev = pci_scan_single_device(bus, devfn);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = p2sb_read_bar0(pdev, mem);
|
||||
|
||||
pci_stop_and_remove_bus_device(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR
|
||||
* @bus: PCI bus to communicate with
|
||||
* @devfn: PCI slot and function to communicate with
|
||||
* @mem: memory resource to be filled in
|
||||
*
|
||||
* The BIOS prevents the P2SB device from being enumerated by the PCI
|
||||
* subsystem, so we need to unhide and hide it back to lookup the BAR.
|
||||
*
|
||||
* if @bus is NULL, the bus 0 in domain 0 will be used.
|
||||
* If @devfn is 0, it will be replaced by devfn of the P2SB device.
|
||||
*
|
||||
* Caller must provide a valid pointer to @mem.
|
||||
*
|
||||
* Locking is handled by pci_rescan_remove_lock mutex.
|
||||
*
|
||||
* Return:
|
||||
* 0 on success or appropriate errno value on error.
|
||||
*/
|
||||
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
|
||||
{
|
||||
struct pci_dev *pdev_p2sb;
|
||||
unsigned int devfn_p2sb;
|
||||
u32 value = P2SBC_HIDE;
|
||||
int ret;
|
||||
|
||||
/* Get devfn for P2SB device itself */
|
||||
ret = p2sb_get_devfn(&devfn_p2sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* if @bus is NULL, use bus 0 in domain 0 */
|
||||
bus = bus ?: pci_find_bus(0, 0);
|
||||
|
||||
/*
|
||||
* Prevent concurrent PCI bus scan from seeing the P2SB device and
|
||||
* removing via sysfs while it is temporarily exposed.
|
||||
*/
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
/* Unhide the P2SB device, if needed */
|
||||
pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value);
|
||||
if (value & P2SBC_HIDE)
|
||||
pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0);
|
||||
|
||||
pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb);
|
||||
if (devfn)
|
||||
ret = p2sb_scan_and_read(bus, devfn, mem);
|
||||
else
|
||||
ret = p2sb_read_bar0(pdev_p2sb, mem);
|
||||
pci_stop_and_remove_bus_device(pdev_p2sb);
|
||||
|
||||
/* Hide the P2SB device, if it was hidden */
|
||||
if (value & P2SBC_HIDE)
|
||||
pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, P2SBC_HIDE);
|
||||
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mem->flags == 0)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(p2sb_bar);
|
@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id)
|
||||
{
|
||||
u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
|
||||
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
|
||||
char *pdevname = KBUILD_MODNAME "_leds";
|
||||
int i;
|
||||
|
||||
platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
|
||||
@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id)
|
||||
}
|
||||
|
||||
if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
|
||||
if (ledmode == SIMATIC_IPC_DEVICE_127E)
|
||||
pdevname = KBUILD_MODNAME "_leds_gpio";
|
||||
platform_data.devmode = ledmode;
|
||||
ipc_led_platform_device =
|
||||
platform_device_register_data(NULL,
|
||||
KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE,
|
||||
pdevname, PLATFORM_DEVID_NONE,
|
||||
&platform_data,
|
||||
sizeof(struct simatic_ipc_platform));
|
||||
if (IS_ERR(ipc_led_platform_device))
|
||||
@ -101,44 +104,6 @@ static int register_platform_devices(u32 station_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: this should eventually be done with generic P2SB discovery code
|
||||
* the individual drivers for watchdogs and LEDs access memory that implements
|
||||
* GPIO, but pinctrl will not come up because of missing ACPI entries
|
||||
*
|
||||
* While there is no conflict a cleaner solution would be to somehow bring up
|
||||
* pinctrl even with these ACPI entries missing, and base the drivers on pinctrl.
|
||||
* After which the following function could be dropped, together with the code
|
||||
* poking the memory.
|
||||
*/
|
||||
/*
|
||||
* Get membase address from PCI, used in leds and wdt module. Here we read
|
||||
* the bar0. The final address calculation is done in the appropriate modules
|
||||
*/
|
||||
u32 simatic_ipc_get_membase0(unsigned int p2sb)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
u32 bar0 = 0;
|
||||
/*
|
||||
* The GPIO memory is in bar0 of the hidden P2SB device.
|
||||
* Unhide the device to have a quick look at it, before we hide it
|
||||
* again.
|
||||
* Also grab the pci rescan lock so that device does not get discovered
|
||||
* and remapped while it is visible.
|
||||
* This code is inspired by drivers/mfd/lpc_ich.c
|
||||
*/
|
||||
bus = pci_find_bus(0, 0);
|
||||
pci_lock_rescan_remove();
|
||||
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0);
|
||||
pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0);
|
||||
|
||||
bar0 &= ~0xf;
|
||||
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1);
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
return bar0;
|
||||
}
|
||||
EXPORT_SYMBOL(simatic_ipc_get_membase0);
|
||||
|
||||
static int __init simatic_ipc_init_module(void)
|
||||
{
|
||||
const struct dmi_system_id *match;
|
||||
|
@ -1647,6 +1647,7 @@ config SIEMENS_SIMATIC_IPC_WDT
|
||||
tristate "Siemens Simatic IPC Watchdog"
|
||||
depends on SIEMENS_SIMATIC_IPC
|
||||
select WATCHDOG_CORE
|
||||
select P2SB
|
||||
help
|
||||
This driver adds support for several watchdogs found in Industrial
|
||||
PCs from Siemens.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_data/x86/p2sb.h>
|
||||
#include <linux/platform_data/x86/simatic-ipc-base.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
@ -54,9 +55,9 @@ static struct resource io_resource_trigger =
|
||||
DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1,
|
||||
KBUILD_MODNAME " WD_TRIGGER_IOADR");
|
||||
|
||||
/* the actual start will be discovered with pci, 0 is a placeholder */
|
||||
/* the actual start will be discovered with p2sb, 0 is a placeholder */
|
||||
static struct resource mem_resource =
|
||||
DEFINE_RES_MEM_NAMED(0, SZ_4, "WD_RESET_BASE_ADR");
|
||||
DEFINE_RES_MEM_NAMED(0, 0, "WD_RESET_BASE_ADR");
|
||||
|
||||
static u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 };
|
||||
static void __iomem *wd_reset_base_addr;
|
||||
@ -150,6 +151,7 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
|
||||
struct simatic_ipc_platform *plat = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
switch (plat->devmode) {
|
||||
case SIMATIC_IPC_DEVICE_227E:
|
||||
@ -190,15 +192,14 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
|
||||
if (plat->devmode == SIMATIC_IPC_DEVICE_427E) {
|
||||
res = &mem_resource;
|
||||
|
||||
/* get GPIO base from PCI */
|
||||
res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1));
|
||||
if (res->start == 0)
|
||||
return -ENODEV;
|
||||
ret = p2sb_bar(NULL, 0, res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* do the final address calculation */
|
||||
res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) +
|
||||
PAD_CFG_DW0_GPP_A_23;
|
||||
res->end += res->start;
|
||||
res->end = res->start + SZ_4 - 1;
|
||||
|
||||
wd_reset_base_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(wd_reset_base_addr))
|
||||
|
28
include/linux/platform_data/x86/p2sb.h
Normal file
28
include/linux/platform_data/x86/p2sb.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Primary to Sideband (P2SB) bridge access support
|
||||
*/
|
||||
|
||||
#ifndef _PLATFORM_DATA_X86_P2SB_H
|
||||
#define _PLATFORM_DATA_X86_P2SB_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
struct pci_bus;
|
||||
struct resource;
|
||||
|
||||
#if IS_BUILTIN(CONFIG_P2SB)
|
||||
|
||||
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem);
|
||||
|
||||
#else /* CONFIG_P2SB */
|
||||
|
||||
static inline int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_P2SB is not set */
|
||||
|
||||
#endif /* _PLATFORM_DATA_X86_P2SB_H */
|
@ -24,6 +24,4 @@ struct simatic_ipc_platform {
|
||||
u8 devmode;
|
||||
};
|
||||
|
||||
u32 simatic_ipc_get_membase0(unsigned int p2sb);
|
||||
|
||||
#endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user