brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · ecf6892 Raw
285 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PISMO memory driver - http://www.pismoworld.org/4 *5 * For ARM Realview and Versatile platforms6 */7#include <linux/init.h>8#include <linux/module.h>9#include <linux/i2c.h>10#include <linux/slab.h>11#include <linux/platform_device.h>12#include <linux/spinlock.h>13#include <linux/mutex.h>14#include <linux/mtd/physmap.h>15#include <linux/mtd/plat-ram.h>16#include <linux/mtd/pismo.h>17 18#define PISMO_NUM_CS	519 20struct pismo_cs_block {21	u8	type;22	u8	width;23	__le16	access;24	__le32	size;25	u32	reserved[2];26	char	device[32];27} __packed;28 29struct pismo_eeprom {30	struct pismo_cs_block cs[PISMO_NUM_CS];31	char	board[15];32	u8	sum;33} __packed;34 35struct pismo_mem {36	phys_addr_t base;37	u32	size;38	u16	access;39	u8	width;40	u8	type;41};42 43struct pismo_data {44	struct i2c_client	*client;45	void			(*vpp)(void *, int);46	void			*vpp_data;47	struct platform_device	*dev[PISMO_NUM_CS];48};49 50static void pismo_set_vpp(struct platform_device *pdev, int on)51{52	struct i2c_client *client = to_i2c_client(pdev->dev.parent);53	struct pismo_data *pismo = i2c_get_clientdata(client);54 55	pismo->vpp(pismo->vpp_data, on);56}57 58static unsigned int pismo_width_to_bytes(unsigned int width)59{60	width &= 15;61	if (width > 2)62		return 0;63	return 1 << width;64}65 66static int pismo_eeprom_read(struct i2c_client *client, void *buf, u8 addr,67			     size_t size)68{69	int ret;70	struct i2c_msg msg[] = {71		{72			.addr = client->addr,73			.len = sizeof(addr),74			.buf = &addr,75		}, {76			.addr = client->addr,77			.flags = I2C_M_RD,78			.len = size,79			.buf = buf,80		},81	};82 83	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));84 85	return ret == ARRAY_SIZE(msg) ? size : -EIO;86}87 88static int pismo_add_device(struct pismo_data *pismo, int i,89			    struct pismo_mem *region, const char *name,90			    void *pdata, size_t psize)91{92	struct platform_device *dev;93	struct resource res = { };94	phys_addr_t base = region->base;95	int ret;96 97	if (base == ~0)98		return -ENXIO;99 100	res.start = base;101	res.end = base + region->size - 1;102	res.flags = IORESOURCE_MEM;103 104	dev = platform_device_alloc(name, i);105	if (!dev)106		return -ENOMEM;107	dev->dev.parent = &pismo->client->dev;108 109	do {110		ret = platform_device_add_resources(dev, &res, 1);111		if (ret)112			break;113 114		ret = platform_device_add_data(dev, pdata, psize);115		if (ret)116			break;117 118		ret = platform_device_add(dev);119		if (ret)120			break;121 122		pismo->dev[i] = dev;123		return 0;124	} while (0);125 126	platform_device_put(dev);127	return ret;128}129 130static int pismo_add_nor(struct pismo_data *pismo, int i,131			 struct pismo_mem *region)132{133	struct physmap_flash_data data = {134		.width = region->width,135	};136 137	if (pismo->vpp)138		data.set_vpp = pismo_set_vpp;139 140	return pismo_add_device(pismo, i, region, "physmap-flash",141		&data, sizeof(data));142}143 144static int pismo_add_sram(struct pismo_data *pismo, int i,145			  struct pismo_mem *region)146{147	struct platdata_mtd_ram data = {148		.bankwidth = region->width,149	};150 151	return pismo_add_device(pismo, i, region, "mtd-ram",152		&data, sizeof(data));153}154 155static void pismo_add_one(struct pismo_data *pismo, int i,156			  const struct pismo_cs_block *cs, phys_addr_t base)157{158	struct device *dev = &pismo->client->dev;159	struct pismo_mem region;160 161	region.base = base;162	region.type = cs->type;163	region.width = pismo_width_to_bytes(cs->width);164	region.access = le16_to_cpu(cs->access);165	region.size = le32_to_cpu(cs->size);166 167	if (region.width == 0) {168		dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width);169		return;170	}171 172	/*173	 * FIXME: may need to the platforms memory controller here, but at174	 * the moment we assume that it has already been correctly setup.175	 * The memory controller can also tell us the base address as well.176	 */177 178	dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n",179		i, cs->device, region.type, region.access, region.size / 1024);180 181	switch (region.type) {182	case 0:183		break;184	case 1:185		/* static DOC */186		break;187	case 2:188		/* static NOR */189		pismo_add_nor(pismo, i, &region);190		break;191	case 3:192		/* static RAM */193		pismo_add_sram(pismo, i, &region);194		break;195	}196}197 198static void pismo_remove(struct i2c_client *client)199{200	struct pismo_data *pismo = i2c_get_clientdata(client);201	int i;202 203	for (i = 0; i < ARRAY_SIZE(pismo->dev); i++)204		platform_device_unregister(pismo->dev[i]);205 206	kfree(pismo);207}208 209static int pismo_probe(struct i2c_client *client)210{211	struct pismo_pdata *pdata = client->dev.platform_data;212	struct pismo_eeprom eeprom;213	struct pismo_data *pismo;214	int ret, i;215 216	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {217		dev_err(&client->dev, "functionality mismatch\n");218		return -EIO;219	}220 221	pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);222	if (!pismo)223		return -ENOMEM;224 225	pismo->client = client;226	if (pdata) {227		pismo->vpp = pdata->set_vpp;228		pismo->vpp_data = pdata->vpp_data;229	}230	i2c_set_clientdata(client, pismo);231 232	ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));233	if (ret < 0) {234		dev_err(&client->dev, "error reading EEPROM: %d\n", ret);235		goto exit_free;236	}237 238	dev_info(&client->dev, "%.15s board found\n", eeprom.board);239 240	for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)241		if (eeprom.cs[i].type != 0xff)242			pismo_add_one(pismo, i, &eeprom.cs[i],243				      pdata->cs_addrs[i]);244 245	return 0;246 247 exit_free:248	kfree(pismo);249	return ret;250}251 252static const struct i2c_device_id pismo_id[] = {253	{ "pismo" },254	{ },255};256MODULE_DEVICE_TABLE(i2c, pismo_id);257 258static struct i2c_driver pismo_driver = {259	.driver	= {260		.name	= "pismo",261	},262	.probe		= pismo_probe,263	.remove		= pismo_remove,264	.id_table	= pismo_id,265};266 267static int __init pismo_init(void)268{269	BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);270	BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);271 272	return i2c_add_driver(&pismo_driver);273}274module_init(pismo_init);275 276static void __exit pismo_exit(void)277{278	i2c_del_driver(&pismo_driver);279}280module_exit(pismo_exit);281 282MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");283MODULE_DESCRIPTION("PISMO memory driver");284MODULE_LICENSE("GPL");285