brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · fac0287 Raw
315 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * TQ-Systems PLD MFD core driver, based on vendor driver by4 * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>5 *6 * Copyright (c) 2015 TQ-Systems GmbH7 * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch>8 */9 10#include <linux/delay.h>11#include <linux/dmi.h>12#include <linux/i2c.h>13#include <linux/io.h>14#include <linux/mfd/core.h>15#include <linux/module.h>16#include <linux/platform_data/i2c-ocores.h>17#include <linux/platform_device.h>18 19#define TQMX86_IOBASE	0x18020#define TQMX86_IOSIZE	0x2021#define TQMX86_IOBASE_I2C	0x1a022#define TQMX86_IOSIZE_I2C	0xa23#define TQMX86_IOBASE_WATCHDOG	0x18b24#define TQMX86_IOSIZE_WATCHDOG	0x225#define TQMX86_IOBASE_GPIO	0x18d26#define TQMX86_IOSIZE_GPIO	0x427 28#define TQMX86_REG_BOARD_ID	0x0029#define TQMX86_REG_BOARD_ID_E38M	130#define TQMX86_REG_BOARD_ID_50UC	231#define TQMX86_REG_BOARD_ID_E38C	332#define TQMX86_REG_BOARD_ID_60EB	433#define TQMX86_REG_BOARD_ID_E39MS	534#define TQMX86_REG_BOARD_ID_E39C1	635#define TQMX86_REG_BOARD_ID_E39C2	736#define TQMX86_REG_BOARD_ID_70EB	837#define TQMX86_REG_BOARD_ID_80UC	938#define TQMX86_REG_BOARD_ID_110EB	1139#define TQMX86_REG_BOARD_ID_E40M	1240#define TQMX86_REG_BOARD_ID_E40S	1341#define TQMX86_REG_BOARD_ID_E40C1	1442#define TQMX86_REG_BOARD_ID_E40C2	1543#define TQMX86_REG_BOARD_REV	0x0144#define TQMX86_REG_IO_EXT_INT	0x0645#define TQMX86_REG_IO_EXT_INT_NONE		046#define TQMX86_REG_IO_EXT_INT_7			147#define TQMX86_REG_IO_EXT_INT_9			248#define TQMX86_REG_IO_EXT_INT_12		349#define TQMX86_REG_IO_EXT_INT_MASK		0x350#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT	451#define TQMX86_REG_SAUC		0x1752 53#define TQMX86_REG_I2C_DETECT	0x1a754#define TQMX86_REG_I2C_DETECT_SOFT		0xa555 56static uint gpio_irq;57module_param(gpio_irq, uint, 0);58MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)");59 60static const struct resource tqmx_i2c_soft_resources[] = {61	DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C),62};63 64static const struct resource tqmx_watchdog_resources[] = {65	DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG),66};67 68/*69 * The IRQ resource must be first, since it is updated with the70 * configured IRQ in the probe function.71 */72static struct resource tqmx_gpio_resources[] = {73	DEFINE_RES_IRQ(0),74	DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO),75};76 77static struct i2c_board_info tqmx86_i2c_devices[] = {78	{79		/* 4K EEPROM at 0x50 */80		I2C_BOARD_INFO("24c32", 0x50),81	},82};83 84static struct ocores_i2c_platform_data ocores_platform_data = {85	.num_devices = ARRAY_SIZE(tqmx86_i2c_devices),86	.devices = tqmx86_i2c_devices,87};88 89static const struct mfd_cell tqmx86_i2c_soft_dev[] = {90	{91		.name = "ocores-i2c",92		.platform_data = &ocores_platform_data,93		.pdata_size = sizeof(ocores_platform_data),94		.resources = tqmx_i2c_soft_resources,95		.num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources),96	},97};98 99static const struct mfd_cell tqmx86_devs[] = {100	{101		.name = "tqmx86-wdt",102		.resources = tqmx_watchdog_resources,103		.num_resources = ARRAY_SIZE(tqmx_watchdog_resources),104		.ignore_resource_conflicts = true,105	},106	{107		.name = "tqmx86-gpio",108		.resources = tqmx_gpio_resources,109		.num_resources = ARRAY_SIZE(tqmx_gpio_resources),110		.ignore_resource_conflicts = true,111	},112};113 114static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc)115{116	switch (board_id) {117	case TQMX86_REG_BOARD_ID_E38M:118		return "TQMxE38M";119	case TQMX86_REG_BOARD_ID_50UC:120		return "TQMx50UC";121	case TQMX86_REG_BOARD_ID_E38C:122		return "TQMxE38C";123	case TQMX86_REG_BOARD_ID_60EB:124		return "TQMx60EB";125	case TQMX86_REG_BOARD_ID_E39MS:126		return (sauc == 0xff) ? "TQMxE39M" : "TQMxE39S";127	case TQMX86_REG_BOARD_ID_E39C1:128		return "TQMxE39C1";129	case TQMX86_REG_BOARD_ID_E39C2:130		return "TQMxE39C2";131	case TQMX86_REG_BOARD_ID_70EB:132		return "TQMx70EB";133	case TQMX86_REG_BOARD_ID_80UC:134		return "TQMx80UC";135	case TQMX86_REG_BOARD_ID_110EB:136		return "TQMx110EB";137	case TQMX86_REG_BOARD_ID_E40M:138		return "TQMxE40M";139	case TQMX86_REG_BOARD_ID_E40S:140		return "TQMxE40S";141	case TQMX86_REG_BOARD_ID_E40C1:142		return "TQMxE40C1";143	case TQMX86_REG_BOARD_ID_E40C2:144		return "TQMxE40C2";145	default:146		return "Unknown";147	}148}149 150static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id)151{152	switch (board_id) {153	case TQMX86_REG_BOARD_ID_50UC:154	case TQMX86_REG_BOARD_ID_60EB:155	case TQMX86_REG_BOARD_ID_70EB:156	case TQMX86_REG_BOARD_ID_80UC:157	case TQMX86_REG_BOARD_ID_110EB:158	case TQMX86_REG_BOARD_ID_E40M:159	case TQMX86_REG_BOARD_ID_E40S:160	case TQMX86_REG_BOARD_ID_E40C1:161	case TQMX86_REG_BOARD_ID_E40C2:162		return 24000;163	case TQMX86_REG_BOARD_ID_E39MS:164	case TQMX86_REG_BOARD_ID_E39C1:165	case TQMX86_REG_BOARD_ID_E39C2:166		return 25000;167	case TQMX86_REG_BOARD_ID_E38M:168	case TQMX86_REG_BOARD_ID_E38C:169		return 33000;170	default:171		dev_warn(dev, "unknown board %d, assuming 24MHz LPC clock\n",172			 board_id);173		return 24000;174	}175}176 177static int tqmx86_probe(struct platform_device *pdev)178{179	u8 board_id, sauc, rev, i2c_det, io_ext_int_val;180	struct device *dev = &pdev->dev;181	u8 gpio_irq_cfg, readback;182	const char *board_name;183	void __iomem *io_base;184	int err;185 186	switch (gpio_irq) {187	case 0:188		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE;189		break;190	case 7:191		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7;192		break;193	case 9:194		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9;195		break;196	case 12:197		gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12;198		break;199	default:200		pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq);201		return -EINVAL;202	}203 204	io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE);205	if (!io_base)206		return -ENOMEM;207 208	board_id = ioread8(io_base + TQMX86_REG_BOARD_ID);209	sauc = ioread8(io_base + TQMX86_REG_SAUC);210	board_name = tqmx86_board_id_to_name(board_id, sauc);211	rev = ioread8(io_base + TQMX86_REG_BOARD_REV);212 213	dev_info(dev,214		 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n",215		 board_name, board_id, rev >> 4, rev & 0xf);216 217	/*218	 * The I2C_DETECT register is in the range assigned to the I2C driver219	 * later, so we don't extend TQMX86_IOSIZE. Use inb() for this one-off220	 * access instead of ioport_map + unmap.221	 */222	i2c_det = inb(TQMX86_REG_I2C_DETECT);223 224	if (gpio_irq_cfg) {225		io_ext_int_val =226			gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT;227		iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT);228		readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);229		if (readback != io_ext_int_val) {230			dev_warn(dev, "GPIO interrupts not supported.\n");231			return -EINVAL;232		}233 234		/* Assumes the IRQ resource is first. */235		tqmx_gpio_resources[0].start = gpio_irq;236	} else {237		tqmx_gpio_resources[0].flags = 0;238	}239 240	ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id);241 242	if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) {243		err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,244					   tqmx86_i2c_soft_dev,245					   ARRAY_SIZE(tqmx86_i2c_soft_dev),246					   NULL, 0, NULL);247		if (err)248			return err;249	}250 251	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,252				    tqmx86_devs,253				    ARRAY_SIZE(tqmx86_devs),254				    NULL, 0, NULL);255}256 257static int tqmx86_create_platform_device(const struct dmi_system_id *id)258{259	struct platform_device *pdev;260	int err;261 262	pdev = platform_device_alloc("tqmx86", -1);263	if (!pdev)264		return -ENOMEM;265 266	err = platform_device_add(pdev);267	if (err)268		platform_device_put(pdev);269 270	return err;271}272 273static const struct dmi_system_id tqmx86_dmi_table[] __initconst = {274	{275		.ident = "TQMX86",276		.matches = {277			DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"),278			DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),279		},280		.callback = tqmx86_create_platform_device,281	},282	{283		.ident = "TQMX86",284		.matches = {285			DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"),286			DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),287		},288		.callback = tqmx86_create_platform_device,289	},290	{}291};292MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table);293 294static struct platform_driver tqmx86_driver = {295	.driver		= {296		.name	= "tqmx86",297	},298	.probe		= tqmx86_probe,299};300 301static int __init tqmx86_init(void)302{303	if (!dmi_check_system(tqmx86_dmi_table))304		return -ENODEV;305 306	return platform_driver_register(&tqmx86_driver);307}308 309module_init(tqmx86_init);310 311MODULE_DESCRIPTION("TQMx86 PLD Core Driver");312MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");313MODULE_LICENSE("GPL");314MODULE_ALIAS("platform:tqmx86");315