Cc: Greg KH<greg@xxxxxxxxx>
Cc: Michael BÃsch<mb@xxxxxxxxx>
Cc: Larry Finger<Larry.Finger@xxxxxxxxxxxx>
Cc: George Kashperko<george@xxxxxxxxxxx>
Cc: Arend van Spriel<arend@xxxxxxxxxxxx>
Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
Cc: Russell King<rmk@xxxxxxxxxxxxxxxx>
Cc: Arnd Bergmann<arnd@xxxxxxxx>
Cc: Andy Botting<andy@xxxxxxxxxxxxxxx>
Cc: linuxdriverproject<devel@xxxxxxxxxxxxxxxxxxxxxx>
Cc: linux-kernel@xxxxxxxxxxxxxxx<linux-kernel@xxxxxxxxxxxxxxx>
Signed-off-by: RafaÅ MiÅecki<zajec5@xxxxxxxxx>
---
Greg: is this what you expected from dynamic allocation and documentation?
Did I miss any other comments about something to change?
V2: Rename to axi
Use DEFINE_PCI_DEVICE_TABLE in bridge
Make use of pr_fmt and pr_*
Store core class
Rename bridge to not b43 specific
Replace magic 0x1000 with BCMAI_CORE_SIZE
Remove some old "ssb" names and defines
Move BCMAI_ADDR_BASE def
Add drvdata field
V3: Fix reloading (kfree issue)
Add 14e4:0x4331
Fix non-initialized struct issue
Drop useless inline functions wrappers for pci core drv
Proper pr_* usage
V3.1: Include forgotten changes (pr_* and include related)
Explain why we dare to implement empty release function
V4: Add ABI documentation
Move struct device to wrapper and alloc it dynamically
checkpatch.pl pointed fixes
---
Documentation/ABI/testing/sysfs-bus-axi | 31 +++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/axi/Kconfig | 33 +++
drivers/axi/Makefile | 7 +
drivers/axi/TODO | 3 +
drivers/axi/axi_pci_bridge.c | 33 +++
drivers/axi/axi_private.h | 37 +++
drivers/axi/core.c | 51 ++++
drivers/axi/driver_chipcommon.c | 87 +++++++
drivers/axi/driver_chipcommon_pmu.c | 134 ++++++++++
drivers/axi/driver_pci.c | 163 ++++++++++++
drivers/axi/host_pci.c | 178 +++++++++++++
drivers/axi/main.c | 271 ++++++++++++++++++++
drivers/axi/scan.c | 392 +++++++++++++++++++++++++++++
drivers/axi/scan.h | 56 ++++
include/linux/axi/axi.h | 227 +++++++++++++++++
include/linux/axi/axi_driver_chipcommon.h | 308 ++++++++++++++++++++++
include/linux/axi/axi_driver_pci.h | 89 +++++++
include/linux/axi/axi_regs.h | 34 +++
include/linux/mod_devicetable.h | 17 ++
scripts/mod/file2alias.c | 21 ++
22 files changed, 2175 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-axi
create mode 100644 drivers/axi/Kconfig
create mode 100644 drivers/axi/Makefile
create mode 100644 drivers/axi/TODO
create mode 100644 drivers/axi/axi_pci_bridge.c
create mode 100644 drivers/axi/axi_private.h
create mode 100644 drivers/axi/core.c
create mode 100644 drivers/axi/driver_chipcommon.c
create mode 100644 drivers/axi/driver_chipcommon_pmu.c
create mode 100644 drivers/axi/driver_pci.c
create mode 100644 drivers/axi/host_pci.c
create mode 100644 drivers/axi/main.c
create mode 100644 drivers/axi/scan.c
create mode 100644 drivers/axi/scan.h
create mode 100644 include/linux/axi/axi.h
create mode 100644 include/linux/axi/axi_driver_chipcommon.h
create mode 100644 include/linux/axi/axi_driver_pci.h
create mode 100644 include/linux/axi/axi_regs.h
diff --git a/Documentation/ABI/testing/sysfs-bus-axi b/Documentation/ABI/testing/sysfs-bus-axi
new file mode 100644
index 0000000..6223612
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-axi
@@ -0,0 +1,31 @@
+What: /sys/bus/axi/devices/.../class
+Date: April 2011
+KernelVersion: 2.6.40
+Contact: RafaÅ MiÅecki<zajec5@xxxxxxxxx>
+Description:
+ Each AXI core is identified by few fields, including class it
+ belongs to. See include/linux/axi/axi.h for possible values.
+
+What: /sys/bus/axi/devices/.../manuf
+Date: April 2011
+KernelVersion: 2.6.40
+Contact: RafaÅ MiÅecki<zajec5@xxxxxxxxx>
+Description:
+ Each AXI core has it's manufacturer id. See
+ include/linux/axi/axi.h for possible values.
+
+What: /sys/bus/axi/devices/.../rev
+Date: April 2011
+KernelVersion: 2.6.40
+Contact: RafaÅ MiÅecki<zajec5@xxxxxxxxx>
+Description:
+ AXI cores of the same type can still slightly differ depending
+ on their revision. Use it for detailed programming.
+
+What: /sys/bus/axi/devices/.../id
+Date: April 2011
+KernelVersion: 2.6.40
+Contact: RafaÅ MiÅecki<zajec5@xxxxxxxxx>
+Description:
+ There are a few types of AXI cores, they can be identified by
+ id field.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 177c7d1..1244e8c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -68,6 +68,8 @@ source "drivers/watchdog/Kconfig"
source "drivers/ssb/Kconfig"
+source "drivers/axi/Kconfig"
+
source "drivers/mfd/Kconfig"
source "drivers/regulator/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 3f135b6..6e1979b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -110,6 +110,7 @@ obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
+obj-$(CONFIG_AXI) += axi/
obj-$(CONFIG_VHOST_NET) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig
new file mode 100644
index 0000000..6221af0
--- /dev/null
+++ b/drivers/axi/Kconfig
@@ -0,0 +1,33 @@
+config AXI_POSSIBLE
+ bool
+ depends on HAS_IOMEM&& HAS_DMA
+ default y
+
+menu "AMBA AXI"
+ depends on AXI_POSSIBLE
+
+config AXI
+ tristate "AXI support"
+ depends on AXI_POSSIBLE
+ help
+ Bus driver for one of the Advanced Microcontroller Bus Architecture
+ interfaces: Advanced eXtensible Interface.
+
+config AXI_HOST_PCI_POSSIBLE
+ bool
+ depends on AXI&& PCI = y
+ default y
+
+config AXI_HOST_PCI
+ bool "Support for AXI on PCI-host bus"
+ depends on AXI_HOST_PCI_POSSIBLE
+
+config AXI_DEBUG
+ bool "AXI debugging"
+ depends on AXI
+ help
+ This turns on additional debugging messages.
+
+ If unsure, say N
+
+endmenu
diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile
new file mode 100644
index 0000000..50d6797
--- /dev/null
+++ b/drivers/axi/Makefile
@@ -0,0 +1,7 @@
+axi-y += main.o scan.o core.o
+axi-y += driver_chipcommon.o driver_chipcommon_pmu.o
+axi-y += driver_pci.o
+axi-$(CONFIG_AXI_HOST_PCI) += host_pci.o axi_pci_bridge.o
+obj-$(CONFIG_AXI) += axi.o
+
+ccflags-$(CONFIG_AXI_DEBUG) := -DDEBUG
diff --git a/drivers/axi/TODO b/drivers/axi/TODO
new file mode 100644
index 0000000..5190336
--- /dev/null
+++ b/drivers/axi/TODO
@@ -0,0 +1,3 @@
+- Interrupts
+- Defines for PCI core driver
+- Convert axi_bus->cores into linked list
diff --git a/drivers/axi/axi_pci_bridge.c b/drivers/axi/axi_pci_bridge.c
new file mode 100644
index 0000000..17e882c
--- /dev/null
+++ b/drivers/axi/axi_pci_bridge.c
@@ -0,0 +1,33 @@
+/*
+ * AXI PCI bridge module
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "axi_private.h"
+
+#include<linux/axi/axi.h>
+#include<linux/pci.h>
+
+static DEFINE_PCI_DEVICE_TABLE(axi_pci_bridge_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, axi_pci_bridge_tbl);
+
+static struct pci_driver axi_pci_bridge_driver = {
+ .name = "axi-pci-bridge",
+ .id_table = axi_pci_bridge_tbl,
+};
+
+int __init axi_pci_bridge_init(void)
+{
+ return axi_host_pci_register(&axi_pci_bridge_driver);
+}
+
+void __exit axi_pci_bridge_exit(void)
+{
+ axi_host_pci_unregister(&axi_pci_bridge_driver);
+}
diff --git a/drivers/axi/axi_private.h b/drivers/axi/axi_private.h
new file mode 100644
index 0000000..756efb6
--- /dev/null
+++ b/drivers/axi/axi_private.h
@@ -0,0 +1,37 @@
+#ifndef LINUX_AXI_PRIVATE_H_
+#define LINUX_AXI_PRIVATE_H_
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
+
+#include<linux/axi/axi.h>
+
+#define AXI_ADDR_BASE 0x18000000
+#define AXI_WRAP_BASE 0x18100000
+
+#define AXI_CORE_SIZE 0x1000
+
+struct axi_bus;
+
+/* main.c */
+extern int axi_bus_register(struct axi_bus *bus);
+extern void axi_bus_unregister(struct axi_bus *bus);
+
+/* scan.c */
+int axi_bus_scan(struct axi_bus *bus);
+
+#ifdef CONFIG_AXI_HOST_PCI
+/* b43_pci_ai_bridge.c */
+extern int __init axi_pci_bridge_init(void);
+extern void __exit axi_pci_bridge_exit(void);
+
+/* host_pci.c */
+extern int axi_host_pci_register(struct pci_driver *driver);
+static inline void axi_host_pci_unregister(struct pci_driver *driver)
+{
+ pci_unregister_driver(driver);
+}
+#endif /* CONFIG_AXI_HOST_PCI */
+
+#endif
diff --git a/drivers/axi/core.c b/drivers/axi/core.c
new file mode 100644
index 0000000..3d79749
--- /dev/null
+++ b/drivers/axi/core.c
@@ -0,0 +1,51 @@
+/*
+ * AMBA AXI
+ * Core ops
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "axi_private.h"
+#include<linux/axi/axi.h>
+
+bool axi_core_is_enabled(struct axi_device *core)
+{
+ if ((axi_aread32(core, AXI_IOCTL)& (AXI_IOCTL_CLK | AXI_IOCTL_FGC))
+ != AXI_IOCTL_CLK)
+ return false;
+ if (axi_aread32(core, AXI_RESET_CTL)& AXI_RESET_CTL_RESET)
+ return false;
+ return true;
+}
+EXPORT_SYMBOL(axi_core_is_enabled);
+
+static void axi_core_disable(struct axi_device *core, u32 flags)
+{
+ if (axi_aread32(core, AXI_RESET_CTL)& AXI_RESET_CTL_RESET)
+ return;
+
+ axi_awrite32(core, AXI_IOCTL, flags);
+ axi_aread32(core, AXI_IOCTL);
+ udelay(10);
+
+ axi_awrite32(core, AXI_RESET_CTL, AXI_RESET_CTL_RESET);
+ udelay(1);
+}
+
+int axi_core_enable(struct axi_device *core, u32 flags)
+{
+ axi_core_disable(core, flags);
+
+ axi_awrite32(core, AXI_IOCTL, (AXI_IOCTL_CLK | AXI_IOCTL_FGC | flags));
+ axi_aread32(core, AXI_IOCTL);
+
+ axi_awrite32(core, AXI_RESET_CTL, 0);
+ udelay(1);
+
+ axi_awrite32(core, AXI_IOCTL, (AXI_IOCTL_CLK | flags));
+ axi_aread32(core, AXI_IOCTL);
+ udelay(1);
+
+ return 0;
+}
+EXPORT_SYMBOL(axi_core_enable);
diff --git a/drivers/axi/driver_chipcommon.c b/drivers/axi/driver_chipcommon.c
new file mode 100644
index 0000000..b3087df
--- /dev/null
+++ b/drivers/axi/driver_chipcommon.c
@@ -0,0 +1,87 @@
+/*
+ * AMBA AXI
+ * ChipCommon core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch<mb@xxxxxxxxx>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "axi_private.h"
+#include<linux/axi/axi.h>
+
+static inline u32 axi_cc_write32_masked(struct axi_drv_cc *cc, u16 offset,
+ u32 mask, u32 value)
+{
+ value&= mask;
+ value |= axi_cc_read32(cc, offset)& ~mask;
+ axi_cc_write32(cc, offset, value);
+
+ return value;
+}
+
+void axi_core_chipcommon_init(struct axi_drv_cc *cc)
+{
+ if (cc->core->id.rev>= 11)
+ cc->status = axi_cc_read32(cc, AXI_CC_CHIPSTAT);
+ cc->capabilities = axi_cc_read32(cc, AXI_CC_CAP);
+ if (cc->core->id.rev>= 35)
+ cc->capabilities_ext = axi_cc_read32(cc, AXI_CC_CAP_EXT);
+
+ axi_cc_write32(cc, 0x58, 0);
+ axi_cc_write32(cc, 0x5C, 0);
+
+ if (cc->capabilities& AXI_CC_CAP_PMU)
+ axi_pmu_init(cc);
+ if (cc->capabilities& AXI_CC_CAP_PCTL)
+ pr_err("Power control not implemented!\n");
+}
+
+/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
+void axi_chipco_watchdog_timer_set(struct axi_drv_cc *cc, u32 ticks)
+{
+ /* instant NMI */
+ axi_cc_write32(cc, AXI_CC_WATCHDOG, ticks);
+}
+
+void axi_chipco_irq_mask(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ axi_cc_write32_masked(cc, AXI_CC_IRQMASK, mask, value);
+}
+
+u32 axi_chipco_irq_status(struct axi_drv_cc *cc, u32 mask)
+{
+ return axi_cc_read32(cc, AXI_CC_IRQSTAT)& mask;
+}
+
+u32 axi_chipco_gpio_in(struct axi_drv_cc *cc, u32 mask)
+{
+ return axi_cc_read32(cc, AXI_CC_GPIOIN)& mask;
+}
+
+u32 axi_chipco_gpio_out(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ return axi_cc_write32_masked(cc, AXI_CC_GPIOOUT, mask, value);
+}
+
+u32 axi_chipco_gpio_outen(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ return axi_cc_write32_masked(cc, AXI_CC_GPIOOUTEN, mask, value);
+}
+
+u32 xaxi_chipco_gpio_control(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ return axi_cc_write32_masked(cc, AXI_CC_GPIOCTL, mask, value);
+}
+EXPORT_SYMBOL(xaxi_chipco_gpio_control);
+
+u32 axi_chipco_gpio_intmask(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ return axi_cc_write32_masked(cc, AXI_CC_GPIOIRQ, mask, value);
+}
+
+u32 axi_chipco_gpio_polarity(struct axi_drv_cc *cc, u32 mask, u32 value)
+{
+ return axi_cc_write32_masked(cc, AXI_CC_GPIOPOL, mask, value);
+}
diff --git a/drivers/axi/driver_chipcommon_pmu.c b/drivers/axi/driver_chipcommon_pmu.c
new file mode 100644
index 0000000..b57a9d0
--- /dev/null
+++ b/drivers/axi/driver_chipcommon_pmu.c
@@ -0,0 +1,134 @@
+/*
+ * AMBA AXI
+ * ChipCommon Power Management Unit driver
+ *
+ * Copyright 2009, Michael Buesch<mb@xxxxxxxxx>
+ * Copyright 2007, Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "axi_private.h"
+#include<linux/axi/axi.h>
+
+static void axi_chipco_chipctl_maskset(struct axi_drv_cc *cc,
+ u32 offset, u32 mask, u32 set)
+{
+ u32 value;
+
+ axi_cc_read32(cc, AXI_CC_CHIPCTL_ADDR);
+ axi_cc_write32(cc, AXI_CC_CHIPCTL_ADDR, offset);
+ axi_cc_read32(cc, AXI_CC_CHIPCTL_ADDR);
+ value = axi_cc_read32(cc, AXI_CC_CHIPCTL_DATA);
+ value&= mask;
+ value |= set;
+ axi_cc_write32(cc, AXI_CC_CHIPCTL_DATA, value);
+ axi_cc_read32(cc, AXI_CC_CHIPCTL_DATA);
+}
+
+static void axi_pmu_pll_init(struct axi_drv_cc *cc)
+{
+ struct axi_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ case 0x4331:
+ case 43224:
+ case 43225:
+ break;
+ default:
+ pr_err("PLL init unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+}
+
+static void axi_pmu_resources_init(struct axi_drv_cc *cc)
+{
+ struct axi_bus *bus = cc->core->bus;
+ u32 min_msk = 0, max_msk = 0;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ min_msk = 0x200D;
+ max_msk = 0xFFFF;
+ break;
+ case 43224:
+ break;
+ default:
+ pr_err("PMU resource config unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+
+ /* Set the resource masks. */
+ if (min_msk)
+ axi_cc_write32(cc, AXI_CC_PMU_MINRES_MSK, min_msk);
+ if (max_msk)
+ axi_cc_write32(cc, AXI_CC_PMU_MAXRES_MSK, max_msk);
+}
+
+void axi_pmu_swreg_init(struct axi_drv_cc *cc)
+{
+ struct axi_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ case 0x4331:
+ case 43224:
+ break;
+ default:
+ pr_err("PMU switch/regulators init unknown for device "
+ "0x%04X\n", bus->chipinfo.id);
+ }
+}
+
+void axi_pmu_workarounds(struct axi_drv_cc *cc)
+{
+ struct axi_bus *bus = cc->core->bus;
+
+ switch (bus->chipinfo.id) {
+ case 0x4313:
+ axi_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
+ break;
+ case 0x4331:
+ pr_err("Enabling Ext PA lines not implemented\n");
+ break;
+ case 43224:
+ if (bus->chipinfo.rev == 0) {
+ pr_err("Workarounds for 43224 rev 0 not fully "
+ "implemented\n");
+ axi_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
+ } else {
+ axi_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
+ }
+ break;
+ default:
+ pr_err("Workarounds unknown for device 0x%04X\n",
+ bus->chipinfo.id);
+ }
+}
+
+void axi_pmu_init(struct axi_drv_cc *cc)
+{
+ u32 pmucap;
+
+ pmucap = axi_cc_read32(cc, AXI_CC_PMU_CAP);
+ cc->pmu.rev = (pmucap& AXI_CC_PMU_CAP_REVISION);
+
+ pr_debug("Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev,
+ pmucap);
+
+ if (cc->pmu.rev == 1)
+ axi_cc_mask32(cc, AXI_CC_PMU_CTL,
+ ~AXI_CC_PMU_CTL_NOILPONW);
+ else
+ axi_cc_set32(cc, AXI_CC_PMU_CTL,
+ AXI_CC_PMU_CTL_NOILPONW);
+
+ if (cc->core->id.id == 0x4329&& cc->core->id.rev == 2)
+ pr_err("Fix for 4329b0 bad LPOM state not implemented!\n");
+
+ axi_pmu_pll_init(cc);
+ axi_pmu_resources_init(cc);
+ axi_pmu_swreg_init(cc);
+ axi_pmu_workarounds(cc);
+}
diff --git a/drivers/axi/driver_pci.c b/drivers/axi/driver_pci.c
new file mode 100644
index 0000000..fc4ab25
--- /dev/null
+++ b/drivers/axi/driver_pci.c
@@ -0,0 +1,163 @@
+/*
+ * AMBA AXI
+ * PCI Core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch<mb@xxxxxxxxx>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "axi_private.h"
+#include<linux/axi/axi.h>