cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ipaq-micro.c (11197B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Compaq iPAQ h3xxx Atmel microcontroller companion support
      4 *
      5 * This is an Atmel AT90LS8535 with a special flashed-in firmware that
      6 * implements the special protocol used by this driver.
      7 *
      8 * based on previous kernel 2.4 version by Andrew Christian
      9 * Author : Alessandro Gardich <gremlin@gremlin.it>
     10 * Author : Dmitry Artamonow <mad_soft@inbox.ru>
     11 * Author : Linus Walleij <linus.walleij@linaro.org>
     12 */
     13
     14#include <linux/module.h>
     15#include <linux/init.h>
     16#include <linux/interrupt.h>
     17#include <linux/pm.h>
     18#include <linux/delay.h>
     19#include <linux/device.h>
     20#include <linux/platform_device.h>
     21#include <linux/io.h>
     22#include <linux/mfd/core.h>
     23#include <linux/mfd/ipaq-micro.h>
     24#include <linux/string.h>
     25#include <linux/random.h>
     26#include <linux/slab.h>
     27#include <linux/list.h>
     28
     29#include <mach/hardware.h>
     30
     31static void ipaq_micro_trigger_tx(struct ipaq_micro *micro)
     32{
     33	struct ipaq_micro_txdev *tx = &micro->tx;
     34	struct ipaq_micro_msg *msg = micro->msg;
     35	int i, bp;
     36	u8 checksum;
     37	u32 val;
     38
     39	bp = 0;
     40	tx->buf[bp++] = CHAR_SOF;
     41
     42	checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f);
     43	tx->buf[bp++] = checksum;
     44
     45	for (i = 0; i < msg->tx_len; i++) {
     46		tx->buf[bp++] = msg->tx_data[i];
     47		checksum += msg->tx_data[i];
     48	}
     49
     50	tx->buf[bp++] = checksum;
     51	tx->len = bp;
     52	tx->index = 0;
     53
     54	/* Enable interrupt */
     55	val = readl(micro->base + UTCR3);
     56	val |= UTCR3_TIE;
     57	writel(val, micro->base + UTCR3);
     58}
     59
     60int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg)
     61{
     62	unsigned long flags;
     63
     64	dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len);
     65
     66	spin_lock_irqsave(&micro->lock, flags);
     67	if (micro->msg) {
     68		list_add_tail(&msg->node, &micro->queue);
     69		spin_unlock_irqrestore(&micro->lock, flags);
     70		return 0;
     71	}
     72	micro->msg = msg;
     73	ipaq_micro_trigger_tx(micro);
     74	spin_unlock_irqrestore(&micro->lock, flags);
     75	return 0;
     76}
     77EXPORT_SYMBOL(ipaq_micro_tx_msg);
     78
     79static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
     80{
     81	int i;
     82
     83	dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
     84
     85	spin_lock(&micro->lock);
     86	switch (id) {
     87	case MSG_VERSION:
     88	case MSG_EEPROM_READ:
     89	case MSG_EEPROM_WRITE:
     90	case MSG_BACKLIGHT:
     91	case MSG_NOTIFY_LED:
     92	case MSG_THERMAL_SENSOR:
     93	case MSG_BATTERY:
     94		/* Handle synchronous messages */
     95		if (micro->msg && micro->msg->id == id) {
     96			struct ipaq_micro_msg *msg = micro->msg;
     97
     98			memcpy(msg->rx_data, data, len);
     99			msg->rx_len = len;
    100			complete(&micro->msg->ack);
    101			if (!list_empty(&micro->queue)) {
    102				micro->msg = list_entry(micro->queue.next,
    103							struct ipaq_micro_msg,
    104							node);
    105				list_del_init(&micro->msg->node);
    106				ipaq_micro_trigger_tx(micro);
    107			} else
    108				micro->msg = NULL;
    109			dev_dbg(micro->dev, "OK RX message 0x%02x\n", id);
    110		} else {
    111			dev_err(micro->dev,
    112				"out of band RX message 0x%02x\n", id);
    113			if (!micro->msg)
    114				dev_info(micro->dev, "no message queued\n");
    115			else
    116				dev_info(micro->dev, "expected message %02x\n",
    117					 micro->msg->id);
    118		}
    119		break;
    120	case MSG_KEYBOARD:
    121		if (micro->key)
    122			micro->key(micro->key_data, len, data);
    123		else
    124			dev_dbg(micro->dev, "key message ignored, no handle\n");
    125		break;
    126	case MSG_TOUCHSCREEN:
    127		if (micro->ts)
    128			micro->ts(micro->ts_data, len, data);
    129		else
    130			dev_dbg(micro->dev, "touchscreen message ignored, no handle\n");
    131		break;
    132	default:
    133		dev_err(micro->dev,
    134			"unknown msg %d [%d] ", id, len);
    135		for (i = 0; i < len; ++i)
    136			pr_cont("0x%02x ", data[i]);
    137		pr_cont("\n");
    138	}
    139	spin_unlock(&micro->lock);
    140}
    141
    142static void micro_process_char(struct ipaq_micro *micro, u8 ch)
    143{
    144	struct ipaq_micro_rxdev *rx = &micro->rx;
    145
    146	switch (rx->state) {
    147	case STATE_SOF:	/* Looking for SOF */
    148		if (ch == CHAR_SOF)
    149			rx->state = STATE_ID; /* Next byte is the id and len */
    150		break;
    151	case STATE_ID: /* Looking for id and len byte */
    152		rx->id = (ch & 0xf0) >> 4;
    153		rx->len = (ch & 0x0f);
    154		rx->index = 0;
    155		rx->chksum = ch;
    156		rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM;
    157		break;
    158	case STATE_DATA: /* Looking for 'len' data bytes */
    159		rx->chksum += ch;
    160		rx->buf[rx->index] = ch;
    161		if (++rx->index == rx->len)
    162			rx->state = STATE_CHKSUM;
    163		break;
    164	case STATE_CHKSUM: /* Looking for the checksum */
    165		if (ch == rx->chksum)
    166			micro_rx_msg(micro, rx->id, rx->len, rx->buf);
    167		rx->state = STATE_SOF;
    168		break;
    169	}
    170}
    171
    172static void micro_rx_chars(struct ipaq_micro *micro)
    173{
    174	u32 status, ch;
    175
    176	while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) {
    177		ch = readl(micro->base + UTDR);
    178		if (status & UTSR1_PRE)
    179			dev_err(micro->dev, "rx: parity error\n");
    180		else if (status & UTSR1_FRE)
    181			dev_err(micro->dev, "rx: framing error\n");
    182		else if (status & UTSR1_ROR)
    183			dev_err(micro->dev, "rx: overrun error\n");
    184		micro_process_char(micro, ch);
    185	}
    186}
    187
    188static void ipaq_micro_get_version(struct ipaq_micro *micro)
    189{
    190	struct ipaq_micro_msg msg = {
    191		.id = MSG_VERSION,
    192	};
    193
    194	ipaq_micro_tx_msg_sync(micro, &msg);
    195	if (msg.rx_len == 4) {
    196		memcpy(micro->version, msg.rx_data, 4);
    197		micro->version[4] = '\0';
    198	} else if (msg.rx_len == 9) {
    199		memcpy(micro->version, msg.rx_data, 4);
    200		micro->version[4] = '\0';
    201		/* Bytes 4-7 are "pack", byte 8 is "boot type" */
    202	} else {
    203		dev_err(micro->dev,
    204			"illegal version message %d bytes\n", msg.rx_len);
    205	}
    206}
    207
    208static void ipaq_micro_eeprom_read(struct ipaq_micro *micro,
    209				   u8 address, u8 len, u8 *data)
    210{
    211	struct ipaq_micro_msg msg = {
    212		.id = MSG_EEPROM_READ,
    213	};
    214	u8 i;
    215
    216	for (i = 0; i < len; i++) {
    217		msg.tx_data[0] = address + i;
    218		msg.tx_data[1] = 1;
    219		msg.tx_len = 2;
    220		ipaq_micro_tx_msg_sync(micro, &msg);
    221		memcpy(data + (i * 2), msg.rx_data, 2);
    222	}
    223}
    224
    225static char *ipaq_micro_str(u8 *wchar, u8 len)
    226{
    227	char retstr[256];
    228	u8 i;
    229
    230	for (i = 0; i < len / 2; i++)
    231		retstr[i] = wchar[i * 2];
    232	return kstrdup(retstr, GFP_KERNEL);
    233}
    234
    235static u16 ipaq_micro_to_u16(u8 *data)
    236{
    237	return data[1] << 8 | data[0];
    238}
    239
    240static void __init ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
    241{
    242	u8 dump[256];
    243	char *str;
    244
    245	ipaq_micro_eeprom_read(micro, 0, 128, dump);
    246	str = ipaq_micro_str(dump, 10);
    247	if (str) {
    248		dev_info(micro->dev, "HW version %s\n", str);
    249		kfree(str);
    250	}
    251	str = ipaq_micro_str(dump+10, 40);
    252	if (str) {
    253		dev_info(micro->dev, "serial number: %s\n", str);
    254		/* Feed the random pool with this */
    255		add_device_randomness(str, strlen(str));
    256		kfree(str);
    257	}
    258	str = ipaq_micro_str(dump+50, 20);
    259	if (str) {
    260		dev_info(micro->dev, "module ID: %s\n", str);
    261		kfree(str);
    262	}
    263	str = ipaq_micro_str(dump+70, 10);
    264	if (str) {
    265		dev_info(micro->dev, "product revision: %s\n", str);
    266		kfree(str);
    267	}
    268	dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80));
    269	dev_info(micro->dev, "frame rate: %u fps\n",
    270		 ipaq_micro_to_u16(dump+82));
    271	dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
    272	dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
    273	dev_info(micro->dev, "color display: %s\n",
    274		 ipaq_micro_to_u16(dump+88) ? "yes" : "no");
    275	dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
    276	dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
    277	dev_info(micro->dev, "screen: %u x %u\n",
    278		 ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96));
    279}
    280
    281static void micro_tx_chars(struct ipaq_micro *micro)
    282{
    283	struct ipaq_micro_txdev *tx = &micro->tx;
    284	u32 val;
    285
    286	while ((tx->index < tx->len) &&
    287	       (readl(micro->base + UTSR1) & UTSR1_TNF)) {
    288		writel(tx->buf[tx->index], micro->base + UTDR);
    289		tx->index++;
    290	}
    291
    292	/* Stop interrupts */
    293	val = readl(micro->base + UTCR3);
    294	val &= ~UTCR3_TIE;
    295	writel(val, micro->base + UTCR3);
    296}
    297
    298static void micro_reset_comm(struct ipaq_micro *micro)
    299{
    300	struct ipaq_micro_rxdev *rx = &micro->rx;
    301	u32 val;
    302
    303	if (micro->msg)
    304		complete(&micro->msg->ack);
    305
    306	/* Initialize Serial channel protocol frame */
    307	rx->state = STATE_SOF;  /* Reset the state machine */
    308
    309	/* Set up interrupts */
    310	writel(0x01, micro->sdlc + 0x0); /* Select UART mode */
    311
    312	/* Clean up CR3 */
    313	writel(0x0, micro->base + UTCR3);
    314
    315	/* Format: 8N1 */
    316	writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0);
    317
    318	/* Baud rate: 115200 */
    319	writel(0x0, micro->base + UTCR1);
    320	writel(0x1, micro->base + UTCR2);
    321
    322	/* Clear SR0 */
    323	writel(0xff, micro->base + UTSR0);
    324
    325	/* Enable RX int, disable TX int */
    326	writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3);
    327	val = readl(micro->base + UTCR3);
    328	val &= ~UTCR3_TIE;
    329	writel(val, micro->base + UTCR3);
    330}
    331
    332static irqreturn_t micro_serial_isr(int irq, void *dev_id)
    333{
    334	struct ipaq_micro *micro = dev_id;
    335	struct ipaq_micro_txdev *tx = &micro->tx;
    336	u32 status;
    337
    338	status = readl(micro->base + UTSR0);
    339	do {
    340		if (status & (UTSR0_RID | UTSR0_RFS)) {
    341			if (status & UTSR0_RID)
    342				/* Clear the Receiver IDLE bit */
    343				writel(UTSR0_RID, micro->base + UTSR0);
    344			micro_rx_chars(micro);
    345		}
    346
    347		/* Clear break bits */
    348		if (status & (UTSR0_RBB | UTSR0_REB))
    349			writel(status & (UTSR0_RBB | UTSR0_REB),
    350			       micro->base + UTSR0);
    351
    352		if (status & UTSR0_TFS)
    353			micro_tx_chars(micro);
    354
    355		status = readl(micro->base + UTSR0);
    356
    357	} while (((tx->index < tx->len) && (status & UTSR0_TFS)) ||
    358		 (status & (UTSR0_RFS | UTSR0_RID)));
    359
    360	return IRQ_HANDLED;
    361}
    362
    363static const struct mfd_cell micro_cells[] = {
    364	{ .name = "ipaq-micro-backlight", },
    365	{ .name = "ipaq-micro-battery", },
    366	{ .name = "ipaq-micro-keys", },
    367	{ .name = "ipaq-micro-ts", },
    368	{ .name = "ipaq-micro-leds", },
    369};
    370
    371static int __maybe_unused micro_resume(struct device *dev)
    372{
    373	struct ipaq_micro *micro = dev_get_drvdata(dev);
    374
    375	micro_reset_comm(micro);
    376	mdelay(10);
    377
    378	return 0;
    379}
    380
    381static int __init micro_probe(struct platform_device *pdev)
    382{
    383	struct ipaq_micro *micro;
    384	struct resource *res;
    385	int ret;
    386	int irq;
    387
    388	micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL);
    389	if (!micro)
    390		return -ENOMEM;
    391
    392	micro->dev = &pdev->dev;
    393
    394	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    395	micro->base = devm_ioremap_resource(&pdev->dev, res);
    396	if (IS_ERR(micro->base))
    397		return PTR_ERR(micro->base);
    398
    399	micro->sdlc = devm_platform_ioremap_resource(pdev, 1);
    400	if (IS_ERR(micro->sdlc))
    401		return PTR_ERR(micro->sdlc);
    402
    403	micro_reset_comm(micro);
    404
    405	irq = platform_get_irq(pdev, 0);
    406	if (irq < 0)
    407		return -EINVAL;
    408	ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr,
    409			       IRQF_SHARED, "ipaq-micro",
    410			       micro);
    411	if (ret) {
    412		dev_err(&pdev->dev, "unable to grab serial port IRQ\n");
    413		return ret;
    414	} else
    415		dev_info(&pdev->dev, "grabbed serial port IRQ\n");
    416
    417	spin_lock_init(&micro->lock);
    418	INIT_LIST_HEAD(&micro->queue);
    419	platform_set_drvdata(pdev, micro);
    420
    421	ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells,
    422			      ARRAY_SIZE(micro_cells), NULL, 0, NULL);
    423	if (ret) {
    424		dev_err(&pdev->dev, "error adding MFD cells");
    425		return ret;
    426	}
    427
    428	/* Check version */
    429	ipaq_micro_get_version(micro);
    430	dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version);
    431	ipaq_micro_eeprom_dump(micro);
    432
    433	return 0;
    434}
    435
    436static const struct dev_pm_ops micro_dev_pm_ops = {
    437	SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
    438};
    439
    440static struct platform_driver micro_device_driver = {
    441	.driver   = {
    442		.name	= "ipaq-h3xxx-micro",
    443		.pm	= &micro_dev_pm_ops,
    444		.suppress_bind_attrs = true,
    445	},
    446};
    447builtin_platform_driver_probe(micro_device_driver, micro_probe);