brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 3a89349 Raw
385 lines · c
1/*2 * B53 register access through memory mapped registers3 *4 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>5 *6 * Permission to use, copy, modify, and/or distribute this software for any7 * purpose with or without fee is hereby granted, provided that the above8 * copyright notice and this permission notice appear in all copies.9 *10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 */18 19#include <linux/bits.h>20#include <linux/kernel.h>21#include <linux/module.h>22#include <linux/of.h>23#include <linux/io.h>24#include <linux/platform_device.h>25#include <linux/platform_data/b53.h>26 27#include "b53_priv.h"28 29struct b53_mmap_priv {30	void __iomem *regs;31};32 33static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)34{35	struct b53_mmap_priv *priv = dev->priv;36	void __iomem *regs = priv->regs;37 38	*val = readb(regs + (page << 8) + reg);39 40	return 0;41}42 43static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)44{45	struct b53_mmap_priv *priv = dev->priv;46	void __iomem *regs = priv->regs;47 48	if (WARN_ON(reg % 2))49		return -EINVAL;50 51	if (dev->pdata && dev->pdata->big_endian)52		*val = ioread16be(regs + (page << 8) + reg);53	else54		*val = readw(regs + (page << 8) + reg);55 56	return 0;57}58 59static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)60{61	struct b53_mmap_priv *priv = dev->priv;62	void __iomem *regs = priv->regs;63 64	if (WARN_ON(reg % 4))65		return -EINVAL;66 67	if (dev->pdata && dev->pdata->big_endian)68		*val = ioread32be(regs + (page << 8) + reg);69	else70		*val = readl(regs + (page << 8) + reg);71 72	return 0;73}74 75static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)76{77	struct b53_mmap_priv *priv = dev->priv;78	void __iomem *regs = priv->regs;79 80	if (WARN_ON(reg % 2))81		return -EINVAL;82 83	if (reg % 4) {84		u16 lo;85		u32 hi;86 87		if (dev->pdata && dev->pdata->big_endian) {88			lo = ioread16be(regs + (page << 8) + reg);89			hi = ioread32be(regs + (page << 8) + reg + 2);90		} else {91			lo = readw(regs + (page << 8) + reg);92			hi = readl(regs + (page << 8) + reg + 2);93		}94 95		*val = ((u64)hi << 16) | lo;96	} else {97		u32 lo;98		u16 hi;99 100		if (dev->pdata && dev->pdata->big_endian) {101			lo = ioread32be(regs + (page << 8) + reg);102			hi = ioread16be(regs + (page << 8) + reg + 4);103		} else {104			lo = readl(regs + (page << 8) + reg);105			hi = readw(regs + (page << 8) + reg + 4);106		}107 108		*val = ((u64)hi << 32) | lo;109	}110 111	return 0;112}113 114static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)115{116	struct b53_mmap_priv *priv = dev->priv;117	void __iomem *regs = priv->regs;118	u32 hi, lo;119 120	if (WARN_ON(reg % 4))121		return -EINVAL;122 123	if (dev->pdata && dev->pdata->big_endian) {124		lo = ioread32be(regs + (page << 8) + reg);125		hi = ioread32be(regs + (page << 8) + reg + 4);126	} else {127		lo = readl(regs + (page << 8) + reg);128		hi = readl(regs + (page << 8) + reg + 4);129	}130 131	*val = ((u64)hi << 32) | lo;132 133	return 0;134}135 136static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)137{138	struct b53_mmap_priv *priv = dev->priv;139	void __iomem *regs = priv->regs;140 141	writeb(value, regs + (page << 8) + reg);142 143	return 0;144}145 146static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,147			    u16 value)148{149	struct b53_mmap_priv *priv = dev->priv;150	void __iomem *regs = priv->regs;151 152	if (WARN_ON(reg % 2))153		return -EINVAL;154 155	if (dev->pdata && dev->pdata->big_endian)156		iowrite16be(value, regs + (page << 8) + reg);157	else158		writew(value, regs + (page << 8) + reg);159 160	return 0;161}162 163static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,164			    u32 value)165{166	struct b53_mmap_priv *priv = dev->priv;167	void __iomem *regs = priv->regs;168 169	if (WARN_ON(reg % 4))170		return -EINVAL;171 172	if (dev->pdata && dev->pdata->big_endian)173		iowrite32be(value, regs + (page << 8) + reg);174	else175		writel(value, regs + (page << 8) + reg);176 177	return 0;178}179 180static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,181			    u64 value)182{183	if (WARN_ON(reg % 2))184		return -EINVAL;185 186	if (reg % 4) {187		u32 hi = (u32)(value >> 16);188		u16 lo = (u16)value;189 190		b53_mmap_write16(dev, page, reg, lo);191		b53_mmap_write32(dev, page, reg + 2, hi);192	} else {193		u16 hi = (u16)(value >> 32);194		u32 lo = (u32)value;195 196		b53_mmap_write32(dev, page, reg, lo);197		b53_mmap_write16(dev, page, reg + 4, hi);198	}199 200	return 0;201}202 203static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,204			    u64 value)205{206	u32 hi, lo;207 208	hi = upper_32_bits(value);209	lo = lower_32_bits(value);210 211	if (WARN_ON(reg % 4))212		return -EINVAL;213 214	b53_mmap_write32(dev, page, reg, lo);215	b53_mmap_write32(dev, page, reg + 4, hi);216 217	return 0;218}219 220static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg,221			       u16 *value)222{223	return -EIO;224}225 226static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg,227				u16 value)228{229	return -EIO;230}231 232static const struct b53_io_ops b53_mmap_ops = {233	.read8 = b53_mmap_read8,234	.read16 = b53_mmap_read16,235	.read32 = b53_mmap_read32,236	.read48 = b53_mmap_read48,237	.read64 = b53_mmap_read64,238	.write8 = b53_mmap_write8,239	.write16 = b53_mmap_write16,240	.write32 = b53_mmap_write32,241	.write48 = b53_mmap_write48,242	.write64 = b53_mmap_write64,243	.phy_read16 = b53_mmap_phy_read16,244	.phy_write16 = b53_mmap_phy_write16,245};246 247static int b53_mmap_probe_of(struct platform_device *pdev,248			     struct b53_platform_data **ppdata)249{250	struct device_node *np = pdev->dev.of_node;251	struct device_node *of_ports, *of_port;252	struct device *dev = &pdev->dev;253	struct b53_platform_data *pdata;254	void __iomem *mem;255 256	mem = devm_platform_ioremap_resource(pdev, 0);257	if (IS_ERR(mem))258		return PTR_ERR(mem);259 260	pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data),261			     GFP_KERNEL);262	if (!pdata)263		return -ENOMEM;264 265	pdata->regs = mem;266	pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev);267	pdata->big_endian = of_property_read_bool(np, "big-endian");268 269	of_ports = of_get_child_by_name(np, "ports");270	if (!of_ports) {271		dev_err(dev, "no ports child node found\n");272		return -EINVAL;273	}274 275	for_each_available_child_of_node(of_ports, of_port) {276		u32 reg;277 278		if (of_property_read_u32(of_port, "reg", &reg))279			continue;280 281		if (reg < B53_N_PORTS)282			pdata->enabled_ports |= BIT(reg);283	}284 285	of_node_put(of_ports);286	*ppdata = pdata;287 288	return 0;289}290 291static int b53_mmap_probe(struct platform_device *pdev)292{293	struct device_node *np = pdev->dev.of_node;294	struct b53_platform_data *pdata = pdev->dev.platform_data;295	struct b53_mmap_priv *priv;296	struct b53_device *dev;297	int ret;298 299	if (!pdata && np) {300		ret = b53_mmap_probe_of(pdev, &pdata);301		if (ret) {302			dev_err(&pdev->dev, "OF probe error\n");303			return ret;304		}305	}306 307	if (!pdata)308		return -EINVAL;309 310	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);311	if (!priv)312		return -ENOMEM;313 314	priv->regs = pdata->regs;315 316	dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);317	if (!dev)318		return -ENOMEM;319 320	dev->pdata = pdata;321 322	platform_set_drvdata(pdev, dev);323 324	return b53_switch_register(dev);325}326 327static void b53_mmap_remove(struct platform_device *pdev)328{329	struct b53_device *dev = platform_get_drvdata(pdev);330 331	if (dev)332		b53_switch_remove(dev);333}334 335static void b53_mmap_shutdown(struct platform_device *pdev)336{337	struct b53_device *dev = platform_get_drvdata(pdev);338 339	if (dev)340		b53_switch_shutdown(dev);341 342	platform_set_drvdata(pdev, NULL);343}344 345static const struct of_device_id b53_mmap_of_table[] = {346	{347		.compatible = "brcm,bcm3384-switch",348		.data = (void *)BCM63XX_DEVICE_ID,349	}, {350		.compatible = "brcm,bcm6318-switch",351		.data = (void *)BCM63268_DEVICE_ID,352	}, {353		.compatible = "brcm,bcm6328-switch",354		.data = (void *)BCM63XX_DEVICE_ID,355	}, {356		.compatible = "brcm,bcm6362-switch",357		.data = (void *)BCM63XX_DEVICE_ID,358	}, {359		.compatible = "brcm,bcm6368-switch",360		.data = (void *)BCM63XX_DEVICE_ID,361	}, {362		.compatible = "brcm,bcm63268-switch",363		.data = (void *)BCM63268_DEVICE_ID,364	}, {365		.compatible = "brcm,bcm63xx-switch",366		.data = (void *)BCM63XX_DEVICE_ID,367	}, { /* sentinel */ }368};369MODULE_DEVICE_TABLE(of, b53_mmap_of_table);370 371static struct platform_driver b53_mmap_driver = {372	.probe = b53_mmap_probe,373	.remove_new = b53_mmap_remove,374	.shutdown = b53_mmap_shutdown,375	.driver = {376		.name = "b53-switch",377		.of_match_table = b53_mmap_of_table,378	},379};380 381module_platform_driver(b53_mmap_driver);382MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");383MODULE_DESCRIPTION("B53 MMAP access driver");384MODULE_LICENSE("Dual BSD/GPL");385