⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lm832x.c

📁 xen虚拟机源代码安装包
💻 C
字号:
/* * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. * * Copyright (C) 2008 Nokia Corporation * Written by Andrzej Zaborowski <andrew@openedhand.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */#include "hw.h"#include "i2c.h"#include "qemu-timer.h"#include "console.h"struct lm_kbd_s {    i2c_slave i2c;    int i2c_dir;    int i2c_cycle;    int reg;    qemu_irq nirq;    uint16_t model;    struct {        qemu_irq out[2];        int in[2][2];    } mux;    uint8_t config;    uint8_t status;    uint8_t acttime;    uint8_t error;    uint8_t clock;    struct {        uint16_t pull;        uint16_t mask;        uint16_t dir;        uint16_t level;        qemu_irq out[16];    } gpio;    struct {        uint8_t dbnctime;        uint8_t size;        int start;        int len;        uint8_t fifo[16];    } kbd;    struct {        uint16_t file[256];	uint8_t faddr;        uint8_t addr[3];        QEMUTimer *tm[3];    } pwm;};#define INT_KEYPAD		(1 << 0)#define INT_ERROR		(1 << 3)#define INT_NOINIT		(1 << 4)#define INT_PWMEND(n)		(1 << (5 + n))#define ERR_BADPAR		(1 << 0)#define ERR_CMDUNK		(1 << 1)#define ERR_KEYOVR		(1 << 2)#define ERR_FIFOOVR		(1 << 6)static void lm_kbd_irq_update(struct lm_kbd_s *s){    qemu_set_irq(s->nirq, !s->status);}static void lm_kbd_gpio_update(struct lm_kbd_s *s){}static void lm_kbd_reset(struct lm_kbd_s *s){    s->config = 0x80;    s->status = INT_NOINIT;    s->acttime = 125;    s->kbd.dbnctime = 3;    s->kbd.size = 0x33;    s->clock = 0x08;    lm_kbd_irq_update(s);    lm_kbd_gpio_update(s);}static void lm_kbd_error(struct lm_kbd_s *s, int err){    s->error |= err;    s->status |= INT_ERROR;    lm_kbd_irq_update(s);}static void lm_kbd_pwm_tick(struct lm_kbd_s *s, int line){}static void lm_kbd_pwm_start(struct lm_kbd_s *s, int line){    lm_kbd_pwm_tick(s, line);}static void lm_kbd_pwm0_tick(void *opaque){    lm_kbd_pwm_tick(opaque, 0);}static void lm_kbd_pwm1_tick(void *opaque){    lm_kbd_pwm_tick(opaque, 1);}static void lm_kbd_pwm2_tick(void *opaque){    lm_kbd_pwm_tick(opaque, 2);}enum {    LM832x_CMD_READ_ID		= 0x80, /* Read chip ID. */    LM832x_CMD_WRITE_CFG	= 0x81, /* Set configuration item. */    LM832x_CMD_READ_INT		= 0x82, /* Get interrupt status. */    LM832x_CMD_RESET		= 0x83, /* Reset, same as external one */    LM823x_CMD_WRITE_PULL_DOWN	= 0x84, /* Select GPIO pull-up/down. */    LM832x_CMD_WRITE_PORT_SEL	= 0x85, /* Select GPIO in/out. */    LM832x_CMD_WRITE_PORT_STATE	= 0x86, /* Set GPIO pull-up/down. */    LM832x_CMD_READ_PORT_SEL	= 0x87, /* Get GPIO in/out. */    LM832x_CMD_READ_PORT_STATE	= 0x88, /* Get GPIO pull-up/down. */    LM832x_CMD_READ_FIFO	= 0x89, /* Read byte from FIFO. */    LM832x_CMD_RPT_READ_FIFO	= 0x8a, /* Read FIFO (no increment). */    LM832x_CMD_SET_ACTIVE	= 0x8b, /* Set active time. */    LM832x_CMD_READ_ERROR	= 0x8c, /* Get error status. */    LM832x_CMD_READ_ROTATOR	= 0x8e, /* Read rotator status. */    LM832x_CMD_SET_DEBOUNCE	= 0x8f, /* Set debouncing time. */    LM832x_CMD_SET_KEY_SIZE	= 0x90, /* Set keypad size. */    LM832x_CMD_READ_KEY_SIZE	= 0x91, /* Get keypad size. */    LM832x_CMD_READ_CFG		= 0x92, /* Get configuration item. */    LM832x_CMD_WRITE_CLOCK	= 0x93, /* Set clock config. */    LM832x_CMD_READ_CLOCK	= 0x94, /* Get clock config. */    LM832x_CMD_PWM_WRITE	= 0x95, /* Write PWM script. */    LM832x_CMD_PWM_START	= 0x96, /* Start PWM engine. */    LM832x_CMD_PWM_STOP		= 0x97, /* Stop PWM engine. */};#define LM832x_MAX_KPX		8#define LM832x_MAX_KPY		12static uint8_t lm_kbd_read(struct lm_kbd_s *s, int reg, int byte){    int ret;    switch (reg) {    case LM832x_CMD_READ_ID:        ret = 0x0400;        break;    case LM832x_CMD_READ_INT:        ret = s->status;        if (!(s->status & INT_NOINIT)) {            s->status = 0;            lm_kbd_irq_update(s);        }        break;    case LM832x_CMD_READ_PORT_SEL:        ret = s->gpio.dir;        break;    case LM832x_CMD_READ_PORT_STATE:        ret = s->gpio.mask;        break;    case LM832x_CMD_READ_FIFO:        if (s->kbd.len <= 1)            return 0x00;        /* Example response from the two commands after a INT_KEYPAD         * interrupt caused by the key 0x3c being pressed:         * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01         *     READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01         * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01         *         * 55 is the code of the key release event serviced in the previous         * interrupt handling.         *         * TODO: find out whether the FIFO is advanced a single character         * before reading every byte or the whole size of the FIFO at the         * last LM832x_CMD_READ_FIFO.  This affects LM832x_CMD_RPT_READ_FIFO         * output in cases where there are more than one event in the FIFO.         * Assume 0xbc and 0x3c events are in the FIFO:         * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9         *     READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9         * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c?         */        s->kbd.start ++;        s->kbd.start &= sizeof(s->kbd.fifo) - 1;        s->kbd.len --;        return s->kbd.fifo[s->kbd.start];    case LM832x_CMD_RPT_READ_FIFO:        if (byte >= s->kbd.len)            return 0x00;        return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)];    case LM832x_CMD_READ_ERROR:        return s->error;    case LM832x_CMD_READ_ROTATOR:        return 0;    case LM832x_CMD_READ_KEY_SIZE:        return s->kbd.size;    case LM832x_CMD_READ_CFG:        return s->config & 0xf;    case LM832x_CMD_READ_CLOCK:        return (s->clock & 0xfc) | 2;    default:        lm_kbd_error(s, ERR_CMDUNK);        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);        return 0x00;    }    return ret >> (byte << 3);}static void lm_kbd_write(struct lm_kbd_s *s, int reg, int byte, uint8_t value){    switch (reg) {    case LM832x_CMD_WRITE_CFG:        s->config = value;        /* This must be done whenever s->mux.in is updated (never).  */        if ((s->config >> 1) & 1)			/* MUX1EN */            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]);        if ((s->config >> 3) & 1)			/* MUX2EN */            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]);        /* TODO: check that this is issued only following the chip reset         * and not in the middle of operation and that it is followed by         * the GPIO ports re-resablishing through WRITE_PORT_SEL and         * WRITE_PORT_STATE (using a timer perhaps) and otherwise output         * warnings.  */        s->status = 0;        lm_kbd_irq_update(s);        s->kbd.len = 0;        s->kbd.start = 0;        s->reg = -1;        break;    case LM832x_CMD_RESET:        if (value == 0xaa)            lm_kbd_reset(s);        else            lm_kbd_error(s, ERR_BADPAR);        s->reg = -1;        break;    case LM823x_CMD_WRITE_PULL_DOWN:        if (!byte)            s->gpio.pull = value;        else {            s->gpio.pull |= value << 8;            lm_kbd_gpio_update(s);            s->reg = -1;        }        break;    case LM832x_CMD_WRITE_PORT_SEL:        if (!byte)            s->gpio.dir = value;        else {            s->gpio.dir |= value << 8;            lm_kbd_gpio_update(s);            s->reg = -1;        }        break;    case LM832x_CMD_WRITE_PORT_STATE:        if (!byte)            s->gpio.mask = value;        else {            s->gpio.mask |= value << 8;            lm_kbd_gpio_update(s);            s->reg = -1;        }        break;    case LM832x_CMD_SET_ACTIVE:        s->acttime = value;        s->reg = -1;        break;    case LM832x_CMD_SET_DEBOUNCE:        s->kbd.dbnctime = value;        s->reg = -1;        if (!value)            lm_kbd_error(s, ERR_BADPAR);        break;    case LM832x_CMD_SET_KEY_SIZE:        s->kbd.size = value;        s->reg = -1;        if (                        (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY ||                        (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX)            lm_kbd_error(s, ERR_BADPAR);        break;    case LM832x_CMD_WRITE_CLOCK:        s->clock = value;        s->reg = -1;        if ((value & 3) && (value & 3) != 3) {            lm_kbd_error(s, ERR_BADPAR);            fprintf(stderr, "%s: invalid clock setting in RCPWM\n",                            __FUNCTION__);        }        /* TODO: Validate that the command is only issued once */        break;    case LM832x_CMD_PWM_WRITE:        if (byte == 0) {            if (!(value & 3) || (value >> 2) > 59) {                lm_kbd_error(s, ERR_BADPAR);                s->reg = -1;                break;            }            s->pwm.faddr = value;            s->pwm.file[s->pwm.faddr] = 0;        } else if (byte == 1) {            s->pwm.file[s->pwm.faddr] |= value << 8;        } else if (byte == 2) {            s->pwm.file[s->pwm.faddr] |= value << 0;            s->reg = -1;        }        break;    case LM832x_CMD_PWM_START:        s->reg = -1;        if (!(value & 3) || (value >> 2) > 59) {            lm_kbd_error(s, ERR_BADPAR);            break;        }        s->pwm.addr[(value & 3) - 1] = value >> 2;        lm_kbd_pwm_start(s, (value & 3) - 1);        break;    case LM832x_CMD_PWM_STOP:        s->reg = -1;        if (!(value & 3)) {            lm_kbd_error(s, ERR_BADPAR);            break;        }        qemu_del_timer(s->pwm.tm[(value & 3) - 1]);        break;    case -1:        lm_kbd_error(s, ERR_BADPAR);        break;    default:        lm_kbd_error(s, ERR_CMDUNK);        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);        break;    }}static void lm_i2c_event(i2c_slave *i2c, enum i2c_event event){    struct lm_kbd_s *s = (struct lm_kbd_s *) i2c;    switch (event) {    case I2C_START_RECV:    case I2C_START_SEND:        s->i2c_cycle = 0;        s->i2c_dir = (event == I2C_START_SEND);        break;    default:        break;    }}static int lm_i2c_rx(i2c_slave *i2c){    struct lm_kbd_s *s = (struct lm_kbd_s *) i2c;    return lm_kbd_read(s, s->reg, s->i2c_cycle ++);}static int lm_i2c_tx(i2c_slave *i2c, uint8_t data){    struct lm_kbd_s *s = (struct lm_kbd_s *) i2c;    if (!s->i2c_cycle)        s->reg = data;    else        lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data);    s->i2c_cycle ++;    return 0;}static void lm_kbd_save(QEMUFile *f, void *opaque){    struct lm_kbd_s *s = (struct lm_kbd_s *) opaque;    int i;    i2c_slave_save(f, &s->i2c);    qemu_put_byte(f, s->i2c_dir);    qemu_put_byte(f, s->i2c_cycle);    qemu_put_byte(f, (uint8_t) s->reg);    qemu_put_8s(f, &s->config);    qemu_put_8s(f, &s->status);    qemu_put_8s(f, &s->acttime);    qemu_put_8s(f, &s->error);    qemu_put_8s(f, &s->clock);    qemu_put_be16s(f, &s->gpio.pull);    qemu_put_be16s(f, &s->gpio.mask);    qemu_put_be16s(f, &s->gpio.dir);    qemu_put_be16s(f, &s->gpio.level);    qemu_put_byte(f, s->kbd.dbnctime);    qemu_put_byte(f, s->kbd.size);    qemu_put_byte(f, s->kbd.start);    qemu_put_byte(f, s->kbd.len);    qemu_put_buffer(f, s->kbd.fifo, sizeof(s->kbd.fifo));    for (i = 0; i < sizeof(s->pwm.file); i ++)        qemu_put_be16s(f, &s->pwm.file[i]);    qemu_put_8s(f, &s->pwm.faddr);    qemu_put_buffer(f, s->pwm.addr, sizeof(s->pwm.addr));    qemu_put_timer(f, s->pwm.tm[0]);    qemu_put_timer(f, s->pwm.tm[1]);    qemu_put_timer(f, s->pwm.tm[2]);}static int lm_kbd_load(QEMUFile *f, void *opaque, int version_id){    struct lm_kbd_s *s = (struct lm_kbd_s *) opaque;    int i;    i2c_slave_load(f, &s->i2c);    s->i2c_dir = qemu_get_byte(f);    s->i2c_cycle = qemu_get_byte(f);    s->reg = (int8_t) qemu_get_byte(f);    qemu_get_8s(f, &s->config);    qemu_get_8s(f, &s->status);    qemu_get_8s(f, &s->acttime);    qemu_get_8s(f, &s->error);    qemu_get_8s(f, &s->clock);    qemu_get_be16s(f, &s->gpio.pull);    qemu_get_be16s(f, &s->gpio.mask);    qemu_get_be16s(f, &s->gpio.dir);    qemu_get_be16s(f, &s->gpio.level);    s->kbd.dbnctime = qemu_get_byte(f);    s->kbd.size = qemu_get_byte(f);    s->kbd.start = qemu_get_byte(f);    s->kbd.len = qemu_get_byte(f);    qemu_get_buffer(f, s->kbd.fifo, sizeof(s->kbd.fifo));    for (i = 0; i < sizeof(s->pwm.file); i ++)        qemu_get_be16s(f, &s->pwm.file[i]);    qemu_get_8s(f, &s->pwm.faddr);    qemu_get_buffer(f, s->pwm.addr, sizeof(s->pwm.addr));    qemu_get_timer(f, s->pwm.tm[0]);    qemu_get_timer(f, s->pwm.tm[1]);    qemu_get_timer(f, s->pwm.tm[2]);    lm_kbd_irq_update(s);    lm_kbd_gpio_update(s);    return 0;}static int lm_kbd_iid = 0;struct i2c_slave *lm8323_init(i2c_bus *bus, qemu_irq nirq){    struct lm_kbd_s *s;    s = (struct lm_kbd_s *) i2c_slave_init(bus, 0, sizeof(struct lm_kbd_s));    s->model = 0x8323;    s->pwm.tm[0] = qemu_new_timer(vm_clock, lm_kbd_pwm0_tick, s);    s->pwm.tm[1] = qemu_new_timer(vm_clock, lm_kbd_pwm1_tick, s);    s->pwm.tm[2] = qemu_new_timer(vm_clock, lm_kbd_pwm2_tick, s);    s->nirq = nirq;    s->i2c.event = lm_i2c_event;    s->i2c.recv = lm_i2c_rx;    s->i2c.send = lm_i2c_tx;    lm_kbd_reset(s);    qemu_register_reset((void *) lm_kbd_reset, s);    register_savevm("LM8323", lm_kbd_iid ++, 0,                    lm_kbd_save, lm_kbd_load, s);    return &s->i2c;}void lm832x_key_event(struct i2c_slave *i2c, int key, int state){    struct lm_kbd_s *s = (struct lm_kbd_s *) i2c;    if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))        return;    if (s->kbd.len >= sizeof(s->kbd.fifo))        return lm_kbd_error(s, ERR_FIFOOVR);    s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] =            key | (state << 7);    /* We never set ERR_KEYOVR because we support multiple keys fine.  */    s->status |= INT_KEYPAD;    lm_kbd_irq_update(s);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -