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
This commit is contained in:
Коммит
c1cd6c305e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,9 +25,52 @@ static const struct mfd_cell bcm2835_power_devs[] = {
|
|||
{ .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)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm2835_pm *pm;
|
||||
int ret;
|
||||
|
@ -39,10 +82,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
|
|||
|
||||
pm->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pm->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pm->base))
|
||||
return PTR_ERR(pm->base);
|
||||
ret = bcm2835_pm_get_pdata(pdev, pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_mfd_add_devices(dev, -1,
|
||||
bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs),
|
||||
|
@ -50,30 +92,22 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
|
|||
if (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
|
||||
* the full PM register range and support power domains.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
pm->asb = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pm->asb))
|
||||
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;
|
||||
}
|
||||
|
||||
if (pm->asb)
|
||||
return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
|
||||
ARRAY_SIZE(bcm2835_power_devs),
|
||||
NULL, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm2835_pm_of_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-pm-wdt", },
|
||||
{ .compatible = "brcm,bcm2835-pm", },
|
||||
{ .compatible = "brcm,bcm2711-pm", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -126,8 +126,7 @@
|
|||
|
||||
#define ASB_AXI_BRDG_ID 0x20
|
||||
|
||||
#define ASB_READ(reg) readl(power->asb + (reg))
|
||||
#define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg))
|
||||
#define BCM2835_BRDG_ID 0x62726467
|
||||
|
||||
struct bcm2835_power_domain {
|
||||
struct generic_pm_domain base;
|
||||
|
@ -142,24 +141,41 @@ struct bcm2835_power {
|
|||
void __iomem *base;
|
||||
/* AXI Async bridge registers. */
|
||||
void __iomem *asb;
|
||||
/* RPiVid bridge registers. */
|
||||
void __iomem *rpivid_asb;
|
||||
|
||||
struct genpd_onecell_data pd_xlate;
|
||||
struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
|
||||
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;
|
||||
u32 val;
|
||||
|
||||
if (!reg)
|
||||
switch (reg) {
|
||||
case 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();
|
||||
|
||||
/* Enable the module's async AXI bridges. */
|
||||
ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP);
|
||||
while (ASB_READ(reg) & ASB_ACK) {
|
||||
if (enable) {
|
||||
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();
|
||||
if (ktime_get_ns() - start >= 1000)
|
||||
return -ETIMEDOUT;
|
||||
|
@ -168,30 +184,24 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
|
||||
{
|
||||
return bcm2835_asb_control(power, reg, true);
|
||||
}
|
||||
|
||||
static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
|
||||
{
|
||||
u64 start;
|
||||
|
||||
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;
|
||||
return bcm2835_asb_control(power, reg, false);
|
||||
}
|
||||
|
||||
static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
|
||||
{
|
||||
struct bcm2835_power *power = pd->power;
|
||||
|
||||
/* We don't run this on BCM2711 */
|
||||
if (power->rpivid_asb)
|
||||
return 0;
|
||||
|
||||
/* Enable functional isolation */
|
||||
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)
|
|||
int inrush;
|
||||
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 (PM_READ(pm_reg) & PM_POWUP)
|
||||
return 0;
|
||||
|
@ -626,13 +640,23 @@ static int bcm2835_power_probe(struct platform_device *pdev)
|
|||
power->dev = dev;
|
||||
power->base = pm->base;
|
||||
power->asb = pm->asb;
|
||||
power->rpivid_asb = pm->rpivid_asb;
|
||||
|
||||
id = ASB_READ(ASB_AXI_BRDG_ID);
|
||||
if (id != 0x62726467 /* "BRDG" */) {
|
||||
id = readl(power->asb + ASB_AXI_BRDG_ID);
|
||||
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
|
||||
dev_err(dev, "ASB register ID returned 0x%08x\n", id);
|
||||
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,
|
||||
ARRAY_SIZE(power_domain_names),
|
||||
sizeof(*power->pd_xlate.domains),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -9,6 +9,7 @@ struct bcm2835_pm {
|
|||
struct device *dev;
|
||||
void __iomem *base;
|
||||
void __iomem *asb;
|
||||
void __iomem *rpivid_asb;
|
||||
};
|
||||
|
||||
#endif /* BCM2835_MFD_PM_H */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче