Commit c1cd6c30 authored by Lee Jones's avatar Lee Jones

Merge branches 'ib-mfd-acpi-for-rafael-5.20',...

Merge branches 'ib-mfd-acpi-for-rafael-5.20', 'ib-mfd-edac-i2c-leds-pinctrl-platform-watchdog-5.20' and 'ib-mfd-soc-bcm-5.20' into ibs-for-mfd-merged
...@@ -263,6 +263,7 @@ config EDAC_I10NM ...@@ -263,6 +263,7 @@ config EDAC_I10NM
config EDAC_PND2 config EDAC_PND2
tristate "Intel Pondicherry2" tristate "Intel Pondicherry2"
depends on PCI && X86_64 && X86_MCE_INTEL depends on PCI && X86_64 && X86_MCE_INTEL
select P2SB if X86
help help
Support for error detection and correction on the Intel Support for error detection and correction on the Intel
Pondicherry2 Integrated Memory Controller. This SoC IP is Pondicherry2 Integrated Memory Controller. This SoC IP is
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/platform_data/x86/p2sb.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -232,42 +234,14 @@ static u64 get_mem_ctrl_hub_base_addr(void) ...@@ -232,42 +234,14 @@ static u64 get_mem_ctrl_hub_base_addr(void)
return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15); 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_MCHBAR_SIZE 0x8000
#define DNV_SB_PORT_SIZE 0x10000 #define DNV_SB_PORT_SIZE 0x10000
static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
{ {
struct pci_dev *pdev; struct pci_dev *pdev;
char *base; void __iomem *base;
u64 addr; struct resource r;
unsigned long size; int ret;
if (op == 4) { if (op == 4) {
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL); 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 ...@@ -279,26 +253,30 @@ static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *na
} else { } else {
/* MMIO via memory controller hub base address */ /* MMIO via memory controller hub base address */
if (op == 0 && port == 0x4c) { if (op == 0 && port == 0x4c) {
addr = get_mem_ctrl_hub_base_addr(); memset(&r, 0, sizeof(r));
if (!addr)
r.start = get_mem_ctrl_hub_base_addr();
if (!r.start)
return -ENODEV; return -ENODEV;
size = DNV_MCHBAR_SIZE; r.end = r.start + DNV_MCHBAR_SIZE - 1;
} else { } else {
/* MMIO via sideband register base address */ /* MMIO via sideband register base address */
addr = get_sideband_reg_base_addr(); ret = p2sb_bar(NULL, 0, &r);
if (!addr) if (ret)
return -ENODEV; return ret;
addr += (port << 16);
size = DNV_SB_PORT_SIZE; 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) if (!base)
return -ENODEV; return -ENODEV;
if (sz == 8) if (sz == 8)
*(u32 *)(data + 4) = *(u32 *)(base + off + 4); *(u64 *)data = readq(base + off);
*(u32 *)data = *(u32 *)(base + off); else
*(u32 *)data = readl(base + off);
iounmap(base); iounmap(base);
} }
......
...@@ -108,6 +108,7 @@ config I2C_HIX5HD2 ...@@ -108,6 +108,7 @@ config I2C_HIX5HD2
config I2C_I801 config I2C_I801
tristate "Intel 82801 (ICH/PCH)" tristate "Intel 82801 (ICH/PCH)"
depends on PCI depends on PCI
select P2SB if X86
select CHECK_SIGNATURE if X86 && DMI select CHECK_SIGNATURE if X86 && DMI
select I2C_SMBUS select I2C_SMBUS
help help
......
...@@ -111,6 +111,7 @@ ...@@ -111,6 +111,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h> #include <linux/platform_data/itco_wdt.h>
#include <linux/platform_data/x86/p2sb.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -140,7 +141,6 @@ ...@@ -140,7 +141,6 @@
#define TCOBASE 0x050 #define TCOBASE 0x050
#define TCOCTL 0x054 #define TCOCTL 0x054
#define SBREG_BAR 0x10
#define SBREG_SMBCTRL 0xc6000c #define SBREG_SMBCTRL 0xc6000c
#define SBREG_SMBCTRL_DNV 0xcf000c #define SBREG_SMBCTRL_DNV 0xcf000c
...@@ -1482,45 +1482,24 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev, ...@@ -1482,45 +1482,24 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
.version = 4, .version = 4,
}; };
struct resource *res; struct resource *res;
unsigned int devfn; int ret;
u64 base64_addr;
u32 base_addr;
u8 hidden;
/* /*
* We must access the NO_REBOOT bit over the Primary to Sideband * We must access the NO_REBOOT bit over the Primary to Sideband
* bridge (P2SB). The BIOS prevents the P2SB device from being * (P2SB) bridge.
* enumerated by the PCI subsystem, so we need to unhide/hide it
* to lookup the P2SB BAR.
*/ */
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]; 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) 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 else
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL; res->start += SBREG_SMBCTRL;
res->end = res->start + 3; res->end = res->start + 3;
res->flags = IORESOURCE_MEM;
return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1, return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
tco_res, 2, &pldata, sizeof(pldata)); tco_res, 2, &pldata, sizeof(pldata));
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config LEDS_SIEMENS_SIMATIC_IPC config LEDS_SIEMENS_SIMATIC_IPC
tristate "LED driver for Siemens Simatic IPCs" tristate "LED driver for Siemens Simatic IPCs"
depends on LEDS_CLASS depends on LEDS_GPIO
depends on SIEMENS_SIMATIC_IPC depends on SIEMENS_SIMATIC_IPC
help help
This option enables support for the LEDs of several Industrial PCs This option enables support for the LEDs of several Industrial PCs
from Siemens. from Siemens.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the modules
will be called simatic-ipc-leds. will be called simatic-ipc-leds and simatic-ipc-leds-gpio.
# SPDX-License-Identifier: GPL-2.0 # 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.o
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o
// 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 @@ ...@@ -23,7 +23,7 @@
#define SIMATIC_IPC_LED_PORT_BASE 0x404E #define SIMATIC_IPC_LED_PORT_BASE 0x404E
struct simatic_ipc_led { struct simatic_ipc_led {
unsigned int value; /* mask for io and offset for mem */ unsigned int value; /* mask for io */
char *name; char *name;
struct led_classdev cdev; struct led_classdev cdev;
}; };
...@@ -38,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = { ...@@ -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 = static struct resource simatic_ipc_led_io_res =
DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME); 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) ...@@ -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; 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) static int simatic_ipc_leds_probe(struct platform_device *pdev)
{ {
const struct simatic_ipc_platform *plat = pdev->dev.platform_data; const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
...@@ -117,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) ...@@ -117,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
struct simatic_ipc_led *ipcled; struct simatic_ipc_led *ipcled;
struct led_classdev *cdev; struct led_classdev *cdev;
struct resource *res; struct resource *res;
void __iomem *reg; int err;
int err, type;
u32 val;
switch (plat->devmode) { switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227D: case SIMATIC_IPC_DEVICE_227D:
...@@ -134,52 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) ...@@ -134,52 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
} }
ipcled = simatic_ipc_leds_io; ipcled = simatic_ipc_leds_io;
} }
type = IORESOURCE_IO;
if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) { if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) {
dev_err(dev, "Unable to register IO resource at %pR\n", res); dev_err(dev, "Unable to register IO resource at %pR\n", res);
return -EBUSY; return -EBUSY;
} }
break; 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: default:
return -ENODEV; return -ENODEV;
} }
while (ipcled->value) { while (ipcled->value) {
cdev = &ipcled->cdev; cdev = &ipcled->cdev;
if (type == IORESOURCE_MEM) { cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_set = simatic_ipc_led_set_mem; cdev->brightness_get = simatic_ipc_led_get_io;
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->max_brightness = LED_ON; cdev->max_brightness = LED_ON;
cdev->name = ipcled->name; cdev->name = ipcled->name;
......
...@@ -572,6 +572,7 @@ config LPC_ICH ...@@ -572,6 +572,7 @@ config LPC_ICH
tristate "Intel ICH LPC" tristate "Intel ICH LPC"
depends on PCI depends on PCI
select MFD_CORE select MFD_CORE
select P2SB if X86
help help
The LPC bridge function of the Intel ICH provides support for The LPC bridge function of the Intel ICH provides support for
many functional units. This driver provides needed support for many functional units. This driver provides needed support for
......
...@@ -25,9 +25,52 @@ static const struct mfd_cell bcm2835_power_devs[] = { ...@@ -25,9 +25,52 @@ static const struct mfd_cell bcm2835_power_devs[] = {
{ .name = "bcm2835-power" }, { .name = "bcm2835-power" },
}; };
static int bcm2835_pm_get_pdata(struct platform_device *pdev,
struct bcm2835_pm *pm)
{
if (of_find_property(pm->dev->of_node, "reg-names", NULL)) {
struct resource *res;
pm->base = devm_platform_ioremap_resource_byname(pdev, "pm");
if (IS_ERR(pm->base))
return PTR_ERR(pm->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "asb");
if (res) {
pm->asb = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pm->asb))
pm->asb = NULL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"rpivid_asb");
if (res) {
pm->rpivid_asb = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pm->rpivid_asb))
pm->rpivid_asb = NULL;
}
return 0;
}
/* If no 'reg-names' property is found we can assume we're using old DTB. */
pm->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pm->base))
return PTR_ERR(pm->base);
pm->asb = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(pm->asb))
pm->asb = NULL;
pm->rpivid_asb = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(pm->rpivid_asb))
pm->rpivid_asb = NULL;
return 0;
}
static int bcm2835_pm_probe(struct platform_device *pdev) static int bcm2835_pm_probe(struct platform_device *pdev)
{ {
struct resource *res;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct bcm2835_pm *pm; struct bcm2835_pm *pm;
int ret; int ret;
...@@ -39,10 +82,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev) ...@@ -39,10 +82,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
pm->dev = dev; pm->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ret = bcm2835_pm_get_pdata(pdev, pm);
pm->base = devm_ioremap_resource(dev, res); if (ret)
if (IS_ERR(pm->base)) return ret;
return PTR_ERR(pm->base);
ret = devm_mfd_add_devices(dev, -1, ret = devm_mfd_add_devices(dev, -1,
bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs), bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs),
...@@ -50,30 +92,22 @@ static int bcm2835_pm_probe(struct platform_device *pdev) ...@@ -50,30 +92,22 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
/* We'll use the presence of the AXI ASB regs in the /*
* We'll use the presence of the AXI ASB regs in the
* bcm2835-pm binding as the key for whether we can reference * bcm2835-pm binding as the key for whether we can reference
* the full PM register range and support power domains. * the full PM register range and support power domains.
*/ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (pm->asb)
if (res) { return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
pm->asb = devm_ioremap_resource(dev, res); ARRAY_SIZE(bcm2835_power_devs),
if (IS_ERR(pm->asb)) NULL, 0, NULL);
return PTR_ERR(pm->asb);
ret = devm_mfd_add_devices(dev, -1,
bcm2835_power_devs,
ARRAY_SIZE(bcm2835_power_devs),
NULL, 0, NULL);
if (ret)
return ret;
}
return 0; return 0;
} }
static const struct of_device_id bcm2835_pm_of_match[] = { static const struct of_device_id bcm2835_pm_of_match[] = {
{ .compatible = "brcm,bcm2835-pm-wdt", }, { .compatible = "brcm,bcm2835-pm-wdt", },
{ .compatible = "brcm,bcm2835-pm", }, { .compatible = "brcm,bcm2835-pm", },
{ .compatible = "brcm,bcm2711-pm", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
* Configuration Registers. * Configuration Registers.
* *
* This driver is derived from lpc_sch. * This driver is derived from lpc_sch.
*
* Copyright (c) 2017, 2021-2022 Intel Corporation
* Copyright (c) 2011 Extreme Engineering Solution, Inc. * Copyright (c) 2011 Extreme Engineering Solution, Inc.
* Author: Aaron Sierra <asierra@xes-inc.com> * Author: Aaron Sierra <asierra@xes-inc.com>
* *
...@@ -42,9 +43,11 @@ ...@@ -42,9 +43,11 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/lpc_ich.h> #include <linux/mfd/lpc_ich.h>
#include <linux/platform_data/itco_wdt.h> #include <linux/platform_data/itco_wdt.h>
#include <linux/platform_data/x86/p2sb.h>
#define ACPIBASE 0x40 #define ACPIBASE 0x40
#define ACPIBASE_GPE_OFF 0x28 #define ACPIBASE_GPE_OFF 0x28
...@@ -71,8 +74,6 @@ ...@@ -71,8 +74,6 @@
#define BCR 0xdc #define BCR 0xdc
#define BCR_WPD BIT(0) #define BCR_WPD BIT(0)
#define SPIBASE_APL_SZ 4096
#define GPIOBASE_ICH0 0x58 #define GPIOBASE_ICH0 0x58
#define GPIOCTRL_ICH0 0x5C #define GPIOCTRL_ICH0 0x5C
#define GPIOBASE_ICH6 0x48 #define GPIOBASE_ICH6 0x48
...@@ -143,6 +144,73 @@ static struct mfd_cell lpc_ich_gpio_cell = { ...@@ -143,6 +144,73 @@ static struct mfd_cell lpc_ich_gpio_cell = {
.ignore_resource_conflicts = true, .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 = { static struct mfd_cell lpc_ich_spi_cell = {
.name = "intel-spi", .name = "intel-spi",
...@@ -1086,6 +1154,34 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) ...@@ -1086,6 +1154,34 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
return ret; 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) static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
{ {
u32 val; u32 val;
...@@ -1100,35 +1196,32 @@ static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data) ...@@ -1100,35 +1196,32 @@ static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
return val & BYT_BCR_WPD; 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; u32 bcr;
pci_read_config_dword(pdev, BCR, &bcr); pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
if (!(bcr & BCR_WPD)) { if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD; bcr |= BCR_WPD;
pci_write_config_dword(pdev, BCR, bcr); pci_bus_write_config_dword(bus, devfn, BCR, bcr);
pci_read_config_dword(pdev, BCR, &bcr); pci_bus_read_config_dword(bus, devfn, BCR, &bcr);
} }
return bcr & BCR_WPD; return bcr & BCR_WPD;
} }
static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data) static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
{ {
unsigned int spi = PCI_DEVFN(13, 2); struct pci_dev *pdev = data;
struct pci_bus *bus = data;
u32 bcr;
pci_bus_read_config_dword(bus, spi, BCR, &bcr); return lpc_ich_set_writeable(pdev->bus, pdev->devfn);
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; static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data)
{
struct pci_dev *pdev = data;
return lpc_ich_set_writeable(pdev->bus, PCI_DEVFN(13, 2));
} }
static int lpc_ich_init_spi(struct pci_dev *dev) static int lpc_ich_init_spi(struct pci_dev *dev)
...@@ -1137,6 +1230,7 @@ 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 resource *res = &intel_spi_res[0];
struct intel_spi_boardinfo *info; struct intel_spi_boardinfo *info;
u32 spi_base, rcba; u32 spi_base, rcba;
int ret;
info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
if (!info) if (!info)
...@@ -1167,30 +1261,19 @@ static int lpc_ich_init_spi(struct pci_dev *dev) ...@@ -1167,30 +1261,19 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
} }
break; break;
case INTEL_SPI_BXT: { case INTEL_SPI_BXT:
unsigned int p2sb = PCI_DEVFN(13, 0);
unsigned int spi = PCI_DEVFN(13, 2);
struct pci_bus *bus = dev->bus;
/* /*
* The P2SB is hidden by BIOS and we need to unhide it in * 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 * order to read BAR of the SPI flash device. Once that is
* done we hide it again. * done we hide it again.
*/ */
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0); ret = p2sb_bar(dev->bus, PCI_DEVFN(13, 2), res);
pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0, if (ret)
&spi_base); return ret;
if (spi_base != ~0) {
res->start = spi_base & 0xfffffff0;
res->end = res->start + SPIBASE_APL_SZ - 1;
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; break;
}
default: default:
return -EINVAL; return -EINVAL;
...@@ -1249,6 +1332,12 @@ static int lpc_ich_probe(struct pci_dev *dev, ...@@ -1249,6 +1332,12 @@ static int lpc_ich_probe(struct pci_dev *dev,
cell_added = true; 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) { if (lpc_chipset_info[priv->chipset].spi_type) {
ret = lpc_ich_init_spi(dev); ret = lpc_ich_init_spi(dev);
if (!ret) if (!ret)
......
...@@ -1641,16 +1641,14 @@ EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid); ...@@ -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 *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 *data = NULL;
const struct intel_pinctrl_soc_data **table;
struct acpi_device *adev;
unsigned int i;
adev = ACPI_COMPANION(&pdev->dev); table = device_get_match_data(&pdev->dev);
if (adev) { if (table) {
const void *match = device_get_match_data(&pdev->dev); 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++) { for (i = 0; table[i]; i++) {
if (!strcmp(adev->pnp.unique_id, table[i]->uid)) { if (!strcmp(adev->pnp.unique_id, table[i]->uid)) {
data = table[i]; data = table[i];
...@@ -1664,7 +1662,7 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_ ...@@ -1664,7 +1662,7 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_
if (!id) if (!id)
return ERR_PTR(-ENODEV); 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]; data = table[pdev->id];
} }
......
...@@ -70,6 +70,18 @@ config INTEL_OAKTRAIL ...@@ -70,6 +70,18 @@ config INTEL_OAKTRAIL
enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
here; it will only load on supported platforms. 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 config INTEL_BXTWC_PMIC_TMU
tristate "Intel Broxton Whiskey Cove TMU Driver" tristate "Intel Broxton Whiskey Cove TMU Driver"
depends on INTEL_SOC_PMIC_BXTWC depends on INTEL_SOC_PMIC_BXTWC
......
...@@ -28,6 +28,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o ...@@ -28,6 +28,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
intel_oaktrail-y := oaktrail.o intel_oaktrail-y := oaktrail.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_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 intel_sdsi-y := sdsi.o
obj-$(CONFIG_INTEL_SDSI) += intel_sdsi.o obj-$(CONFIG_INTEL_SDSI) += intel_sdsi.o
intel_vsec-y := vsec.o intel_vsec-y := vsec.o
......
// 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) ...@@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id)
{ {
u8 ledmode = SIMATIC_IPC_DEVICE_NONE; u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
char *pdevname = KBUILD_MODNAME "_leds";
int i; int i;
platform_data.devmode = SIMATIC_IPC_DEVICE_NONE; platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
...@@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id) ...@@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id)
} }
if (ledmode != SIMATIC_IPC_DEVICE_NONE) { if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
if (ledmode == SIMATIC_IPC_DEVICE_127E)
pdevname = KBUILD_MODNAME "_leds_gpio";
platform_data.devmode = ledmode; platform_data.devmode = ledmode;
ipc_led_platform_device = ipc_led_platform_device =
platform_device_register_data(NULL, platform_device_register_data(NULL,
KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE, pdevname, PLATFORM_DEVID_NONE,
&platform_data, &platform_data,
sizeof(struct simatic_ipc_platform)); sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_led_platform_device)) if (IS_ERR(ipc_led_platform_device))
...@@ -101,44 +104,6 @@ static int register_platform_devices(u32 station_id) ...@@ -101,44 +104,6 @@ static int register_platform_devices(u32 station_id)
return 0; 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) static int __init simatic_ipc_init_module(void)
{ {
const struct dmi_system_id *match; const struct dmi_system_id *match;
......
...@@ -126,8 +126,7 @@ ...@@ -126,8 +126,7 @@
#define ASB_AXI_BRDG_ID 0x20 #define ASB_AXI_BRDG_ID 0x20
#define ASB_READ(reg) readl(power->asb + (reg)) #define BCM2835_BRDG_ID 0x62726467
#define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg))
struct bcm2835_power_domain { struct bcm2835_power_domain {
struct generic_pm_domain base; struct generic_pm_domain base;
...@@ -142,24 +141,41 @@ struct bcm2835_power { ...@@ -142,24 +141,41 @@ struct bcm2835_power {
void __iomem *base; void __iomem *base;
/* AXI Async bridge registers. */ /* AXI Async bridge registers. */
void __iomem *asb; void __iomem *asb;
/* RPiVid bridge registers. */
void __iomem *rpivid_asb;
struct genpd_onecell_data pd_xlate; struct genpd_onecell_data pd_xlate;
struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
struct reset_controller_dev reset; struct reset_controller_dev reset;
}; };
static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable)
{ {
void __iomem *base = power->asb;
u64 start; u64 start;
u32 val;
if (!reg) switch (reg) {
case 0:
return 0; return 0;
case ASB_V3D_S_CTRL:
case ASB_V3D_M_CTRL:
if (power->rpivid_asb)
base = power->rpivid_asb;
break;
}
start = ktime_get_ns(); start = ktime_get_ns();
/* Enable the module's async AXI bridges. */ /* Enable the module's async AXI bridges. */
ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); if (enable) {
while (ASB_READ(reg) & ASB_ACK) { val = readl(base + reg) & ~ASB_REQ_STOP;
} else {
val = readl(base + reg) | ASB_REQ_STOP;
}
writel(PM_PASSWORD | val, base + reg);
while (readl(base + reg) & ASB_ACK) {
cpu_relax(); cpu_relax();
if (ktime_get_ns() - start >= 1000) if (ktime_get_ns() - start >= 1000)
return -ETIMEDOUT; return -ETIMEDOUT;
...@@ -168,30 +184,24 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) ...@@ -168,30 +184,24 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
return 0; return 0;
} }
static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
{ {
u64 start; return bcm2835_asb_control(power, reg, true);
}
if (!reg)
return 0;
start = ktime_get_ns();
/* Enable the module's async AXI bridges. */
ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP);
while (!(ASB_READ(reg) & ASB_ACK)) {
cpu_relax();
if (ktime_get_ns() - start >= 1000)
return -ETIMEDOUT;
}
return 0; static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
{
return bcm2835_asb_control(power, reg, false);
} }
static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
{ {
struct bcm2835_power *power = pd->power; struct bcm2835_power *power = pd->power;
/* We don't run this on BCM2711 */
if (power->rpivid_asb)
return 0;
/* Enable functional isolation */ /* Enable functional isolation */
PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
...@@ -213,6 +223,10 @@ static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) ...@@ -213,6 +223,10 @@ static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg)
int inrush; int inrush;
bool powok; bool powok;
/* We don't run this on BCM2711 */
if (power->rpivid_asb)
return 0;
/* If it was already powered on by the fw, leave it that way. */ /* If it was already powered on by the fw, leave it that way. */
if (PM_READ(pm_reg) & PM_POWUP) if (PM_READ(pm_reg) & PM_POWUP)
return 0; return 0;
...@@ -626,13 +640,23 @@ static int bcm2835_power_probe(struct platform_device *pdev) ...@@ -626,13 +640,23 @@ static int bcm2835_power_probe(struct platform_device *pdev)
power->dev = dev; power->dev = dev;
power->base = pm->base; power->base = pm->base;
power->asb = pm->asb; power->asb = pm->asb;
power->rpivid_asb = pm->rpivid_asb;
id = ASB_READ(ASB_AXI_BRDG_ID); id = readl(power->asb + ASB_AXI_BRDG_ID);
if (id != 0x62726467 /* "BRDG" */) { if (id != BCM2835_BRDG_ID /* "BRDG" */) {
dev_err(dev, "ASB register ID returned 0x%08x\n", id); dev_err(dev, "ASB register ID returned 0x%08x\n", id);
return -ENODEV; return -ENODEV;
} }
if (power->rpivid_asb) {
id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
id);
return -ENODEV;
}
}
power->pd_xlate.domains = devm_kcalloc(dev, power->pd_xlate.domains = devm_kcalloc(dev,
ARRAY_SIZE(power_domain_names), ARRAY_SIZE(power_domain_names),
sizeof(*power->pd_xlate.domains), sizeof(*power->pd_xlate.domains),
......
...@@ -1647,6 +1647,7 @@ config SIEMENS_SIMATIC_IPC_WDT ...@@ -1647,6 +1647,7 @@ config SIEMENS_SIMATIC_IPC_WDT
tristate "Siemens Simatic IPC Watchdog" tristate "Siemens Simatic IPC Watchdog"
depends on SIEMENS_SIMATIC_IPC depends on SIEMENS_SIMATIC_IPC
select WATCHDOG_CORE select WATCHDOG_CORE
select P2SB
help help
This driver adds support for several watchdogs found in Industrial This driver adds support for several watchdogs found in Industrial
PCs from Siemens. PCs from Siemens.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_data/x86/p2sb.h>
#include <linux/platform_data/x86/simatic-ipc-base.h> #include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sizes.h> #include <linux/sizes.h>
...@@ -54,9 +55,9 @@ static struct resource io_resource_trigger = ...@@ -54,9 +55,9 @@ static struct resource io_resource_trigger =
DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1, DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1,
KBUILD_MODNAME " WD_TRIGGER_IOADR"); 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 = 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 u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 };
static void __iomem *wd_reset_base_addr; static void __iomem *wd_reset_base_addr;
...@@ -150,6 +151,7 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev) ...@@ -150,6 +151,7 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
struct simatic_ipc_platform *plat = pdev->dev.platform_data; struct simatic_ipc_platform *plat = pdev->dev.platform_data;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res; struct resource *res;
int ret;
switch (plat->devmode) { switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227E: case SIMATIC_IPC_DEVICE_227E:
...@@ -190,15 +192,14 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev) ...@@ -190,15 +192,14 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
if (plat->devmode == SIMATIC_IPC_DEVICE_427E) { if (plat->devmode == SIMATIC_IPC_DEVICE_427E) {
res = &mem_resource; res = &mem_resource;
/* get GPIO base from PCI */ ret = p2sb_bar(NULL, 0, res);
res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1)); if (ret)
if (res->start == 0) return ret;
return -ENODEV;
/* do the final address calculation */ /* do the final address calculation */
res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) + res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) +
PAD_CFG_DW0_GPP_A_23; 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); wd_reset_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(wd_reset_base_addr)) if (IS_ERR(wd_reset_base_addr))
......
...@@ -9,6 +9,7 @@ struct bcm2835_pm { ...@@ -9,6 +9,7 @@ struct bcm2835_pm {
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
void __iomem *asb; void __iomem *asb;
void __iomem *rpivid_asb;
}; };
#endif /* BCM2835_MFD_PM_H */ #endif /* BCM2835_MFD_PM_H */
/* 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 { ...@@ -24,6 +24,4 @@ struct simatic_ipc_platform {
u8 devmode; u8 devmode;
}; };
u32 simatic_ipc_get_membase0(unsigned int p2sb);
#endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */ #endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment