| /* | 
 |  * Broadcom specific AMBA | 
 |  * System on Chip (SoC) Host | 
 |  * | 
 |  * Licensed under the GNU/GPL. See COPYING for details. | 
 |  */ | 
 |  | 
 | #include "bcma_private.h" | 
 | #include "scan.h" | 
 | #include <linux/slab.h> | 
 | #include <linux/module.h> | 
 | #include <linux/of_address.h> | 
 | #include <linux/bcma/bcma.h> | 
 | #include <linux/bcma/bcma_soc.h> | 
 |  | 
 | static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset) | 
 | { | 
 | 	return readb(core->io_addr + offset); | 
 | } | 
 |  | 
 | static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset) | 
 | { | 
 | 	return readw(core->io_addr + offset); | 
 | } | 
 |  | 
 | static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset) | 
 | { | 
 | 	return readl(core->io_addr + offset); | 
 | } | 
 |  | 
 | static void bcma_host_soc_write8(struct bcma_device *core, u16 offset, | 
 | 				 u8 value) | 
 | { | 
 | 	writeb(value, core->io_addr + offset); | 
 | } | 
 |  | 
 | static void bcma_host_soc_write16(struct bcma_device *core, u16 offset, | 
 | 				 u16 value) | 
 | { | 
 | 	writew(value, core->io_addr + offset); | 
 | } | 
 |  | 
 | static void bcma_host_soc_write32(struct bcma_device *core, u16 offset, | 
 | 				 u32 value) | 
 | { | 
 | 	writel(value, core->io_addr + offset); | 
 | } | 
 |  | 
 | #ifdef CONFIG_BCMA_BLOCKIO | 
 | static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer, | 
 | 				     size_t count, u16 offset, u8 reg_width) | 
 | { | 
 | 	void __iomem *addr = core->io_addr + offset; | 
 |  | 
 | 	switch (reg_width) { | 
 | 	case sizeof(u8): { | 
 | 		u8 *buf = buffer; | 
 |  | 
 | 		while (count) { | 
 | 			*buf = __raw_readb(addr); | 
 | 			buf++; | 
 | 			count--; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	case sizeof(u16): { | 
 | 		__le16 *buf = buffer; | 
 |  | 
 | 		WARN_ON(count & 1); | 
 | 		while (count) { | 
 | 			*buf = (__force __le16)__raw_readw(addr); | 
 | 			buf++; | 
 | 			count -= 2; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	case sizeof(u32): { | 
 | 		__le32 *buf = buffer; | 
 |  | 
 | 		WARN_ON(count & 3); | 
 | 		while (count) { | 
 | 			*buf = (__force __le32)__raw_readl(addr); | 
 | 			buf++; | 
 | 			count -= 4; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	default: | 
 | 		WARN_ON(1); | 
 | 	} | 
 | } | 
 |  | 
 | static void bcma_host_soc_block_write(struct bcma_device *core, | 
 | 				      const void *buffer, | 
 | 				      size_t count, u16 offset, u8 reg_width) | 
 | { | 
 | 	void __iomem *addr = core->io_addr + offset; | 
 |  | 
 | 	switch (reg_width) { | 
 | 	case sizeof(u8): { | 
 | 		const u8 *buf = buffer; | 
 |  | 
 | 		while (count) { | 
 | 			__raw_writeb(*buf, addr); | 
 | 			buf++; | 
 | 			count--; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	case sizeof(u16): { | 
 | 		const __le16 *buf = buffer; | 
 |  | 
 | 		WARN_ON(count & 1); | 
 | 		while (count) { | 
 | 			__raw_writew((__force u16)(*buf), addr); | 
 | 			buf++; | 
 | 			count -= 2; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	case sizeof(u32): { | 
 | 		const __le32 *buf = buffer; | 
 |  | 
 | 		WARN_ON(count & 3); | 
 | 		while (count) { | 
 | 			__raw_writel((__force u32)(*buf), addr); | 
 | 			buf++; | 
 | 			count -= 4; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	default: | 
 | 		WARN_ON(1); | 
 | 	} | 
 | } | 
 | #endif /* CONFIG_BCMA_BLOCKIO */ | 
 |  | 
 | static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset) | 
 | { | 
 | 	if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n")) | 
 | 		return ~0; | 
 | 	return readl(core->io_wrap + offset); | 
 | } | 
 |  | 
 | static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset, | 
 | 				  u32 value) | 
 | { | 
 | 	if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n")) | 
 | 		return; | 
 | 	writel(value, core->io_wrap + offset); | 
 | } | 
 |  | 
 | static const struct bcma_host_ops bcma_host_soc_ops = { | 
 | 	.read8		= bcma_host_soc_read8, | 
 | 	.read16		= bcma_host_soc_read16, | 
 | 	.read32		= bcma_host_soc_read32, | 
 | 	.write8		= bcma_host_soc_write8, | 
 | 	.write16	= bcma_host_soc_write16, | 
 | 	.write32	= bcma_host_soc_write32, | 
 | #ifdef CONFIG_BCMA_BLOCKIO | 
 | 	.block_read	= bcma_host_soc_block_read, | 
 | 	.block_write	= bcma_host_soc_block_write, | 
 | #endif | 
 | 	.aread32	= bcma_host_soc_aread32, | 
 | 	.awrite32	= bcma_host_soc_awrite32, | 
 | }; | 
 |  | 
 | int __init bcma_host_soc_register(struct bcma_soc *soc) | 
 | { | 
 | 	struct bcma_bus *bus = &soc->bus; | 
 |  | 
 | 	/* iomap only first core. We have to read some register on this core | 
 | 	 * to scan the bus. | 
 | 	 */ | 
 | 	bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1); | 
 | 	if (!bus->mmio) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* Host specific */ | 
 | 	bus->hosttype = BCMA_HOSTTYPE_SOC; | 
 | 	bus->ops = &bcma_host_soc_ops; | 
 |  | 
 | 	/* Initialize struct, detect chip */ | 
 | 	bcma_init_bus(bus); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int __init bcma_host_soc_init(struct bcma_soc *soc) | 
 | { | 
 | 	struct bcma_bus *bus = &soc->bus; | 
 | 	int err; | 
 |  | 
 | 	bus->dev = soc->dev; | 
 |  | 
 | 	/* Scan bus and initialize it */ | 
 | 	err = bcma_bus_early_register(bus); | 
 | 	if (err) | 
 | 		iounmap(bus->mmio); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | #ifdef CONFIG_OF | 
 | static int bcma_host_soc_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct device *dev = &pdev->dev; | 
 | 	struct device_node *np = dev->of_node; | 
 | 	struct bcma_bus *bus; | 
 | 	int err; | 
 |  | 
 | 	/* Alloc */ | 
 | 	bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL); | 
 | 	if (!bus) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	bus->dev = dev; | 
 |  | 
 | 	/* Map MMIO */ | 
 | 	bus->mmio = of_iomap(np, 0); | 
 | 	if (!bus->mmio) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* Host specific */ | 
 | 	bus->hosttype = BCMA_HOSTTYPE_SOC; | 
 | 	bus->ops = &bcma_host_soc_ops; | 
 |  | 
 | 	/* Initialize struct, detect chip */ | 
 | 	bcma_init_bus(bus); | 
 |  | 
 | 	/* Register */ | 
 | 	err = bcma_bus_register(bus); | 
 | 	if (err) | 
 | 		goto err_unmap_mmio; | 
 |  | 
 | 	platform_set_drvdata(pdev, bus); | 
 |  | 
 | 	return err; | 
 |  | 
 | err_unmap_mmio: | 
 | 	iounmap(bus->mmio); | 
 | 	return err; | 
 | } | 
 |  | 
 | static int bcma_host_soc_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct bcma_bus *bus = platform_get_drvdata(pdev); | 
 |  | 
 | 	bcma_bus_unregister(bus); | 
 | 	iounmap(bus->mmio); | 
 | 	platform_set_drvdata(pdev, NULL); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct of_device_id bcma_host_soc_of_match[] = { | 
 | 	{ .compatible = "brcm,bus-axi", }, | 
 | 	{}, | 
 | }; | 
 | MODULE_DEVICE_TABLE(of, bcma_host_soc_of_match); | 
 |  | 
 | static struct platform_driver bcma_host_soc_driver = { | 
 | 	.driver = { | 
 | 		.name = "bcma-host-soc", | 
 | 		.of_match_table = bcma_host_soc_of_match, | 
 | 	}, | 
 | 	.probe		= bcma_host_soc_probe, | 
 | 	.remove		= bcma_host_soc_remove, | 
 | }; | 
 |  | 
 | int __init bcma_host_soc_register_driver(void) | 
 | { | 
 | 	return platform_driver_register(&bcma_host_soc_driver); | 
 | } | 
 |  | 
 | void __exit bcma_host_soc_unregister_driver(void) | 
 | { | 
 | 	platform_driver_unregister(&bcma_host_soc_driver); | 
 | } | 
 | #endif /* CONFIG_OF */ |