在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    pc8736x芯片的linux驅(qū)動(dòng)源碼

    /* linux/drivers/char/pc8736x_gpio.c

    National Semiconductor PC8736x GPIO driver. Allows a user space

    process to play with the GPIO pins.

    Copyright (c) 2005,2006 Jim Cromie

    adapted from linux/drivers/char/scx200_gpio.c

    Copyright (c) 2001,2002 Christer Weinigel ,

    */

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define DEVNAME “pc8736x_gpio”

    MODULE_AUTHOR(“Jim Cromie “);

    MODULE_DESCRIPTION(“NatSemi/Winbond PC-8736x GPIO Pin Driver”);

    MODULE_LICENSE(“GPL”);

    static int major; /* default to dynamic major */

    module_param(major, int, 0);

    MODULE_PARM_DESC(major, “Major device number”);

    static DEFINE_MUTEX(pc8736x_gpio_config_lock);

    static unsigned pc8736x_gpio_base;

    static u8 pc8736x_gpio_shadow[4];

    #define SIO_BASE1 0x2E /* 1st command-reg to check */

    #define SIO_BASE2 0x4E /* alt command-reg to check */

    #define SIO_SID 0x20 /* SuperI/O ID Register */

    #define SIO_SID_PC87365 0xe5 /* Expected value in ID Register for PC87365 */

    #define SIO_SID_PC87366 0xe9 /* Expected value in ID Register for PC87366 */

    #define SIO_CF1 0x21 /* chip config, bit0 is chip enable */

    #define PC8736X_GPIO_RANGE 16 /* ioaddr range */

    #define PC8736X_GPIO_CT 32 /* minors matching 4 8 bit ports */

    #define SIO_UNIT_SEL 0x7 /* unit select reg */

    #define SIO_UNIT_ACT 0x30 /* unit enable */

    #define SIO_GPIO_UNIT 0x7 /* unit number of GPIO */

    #define SIO_VLM_UNIT 0x0D

    #define SIO_TMS_UNIT 0x0E

    /* config-space addrs to read/write each unit’s runtime addr */

    #define SIO_BASE_HADDR 0x60

    #define SIO_BASE_LADDR 0x61

    /* GPIO config-space pin-control addresses */

    #define SIO_GPIO_PIN_SELECT 0xF0

    #define SIO_GPIO_PIN_CONFIG 0xF1

    #define SIO_GPIO_PIN_EVENT 0xF2

    static unsigned char superio_cmd = 0;

    static unsigned char selected_device = 0xFF; /* bogus start val */

    /* GPIO port runtime access, functionality */

    static int port_offset[] = { 0, 4, 8, 10 }; /* non-uniform offsets ! */

    /* static int event_capable[] = { 1, 1, 0, 0 }; ports 2,3 are hobbled */

    #define PORT_OUT 0

    #define PORT_IN 1

    #define PORT_EVT_EN 2

    #define PORT_EVT_STST 3

    static struct platform_device *pdev; /* use in dev_*() */

    static inline void superio_outb(int addr, int val)

    {

    outb_p(addr, superio_cmd);

    outb_p(val, superio_cmd + 1);

    }

    static inline int superio_inb(int addr)

    {

    outb_p(addr, superio_cmd);

    return inb_p(superio_cmd + 1);

    }

    static int pc8736x_superio_present(void)

    {

    int id;

    /* try the 2 possible values, read a hardware reg to verify */

    superio_cmd = SIO_BASE1;

    id = superio_inb(SIO_SID);

    if (id == SIO_SID_PC87365 || id == SIO_SID_PC87366)

    return superio_cmd;

    superio_cmd = SIO_BASE2;

    id = superio_inb(SIO_SID);

    if (id == SIO_SID_PC87365 || id == SIO_SID_PC87366)

    return superio_cmd;

    return 0;

    }

    static void device_select(unsigned devldn)

    {

    superio_outb(SIO_UNIT_SEL, devldn);

    selected_device = devldn;

    }

    static void select_pin(unsigned iminor)

    {

    /* select GPIO port/pin from device minor number */

    device_select(SIO_GPIO_UNIT);

    superio_outb(SIO_GPIO_PIN_SELECT,

    ((iminor << 1) & 0xF0) | (iminor & 0x7));

    }

    static inline u32 pc8736x_gpio_configure_fn(unsigned index, u32 mask, u32 bits,

    u32 func_slct)

    {

    u32 config, new_config;

    mutex_lock(&pc8736x_gpio_config_lock);

    device_select(SIO_GPIO_UNIT);

    select_pin(index);

    /* read current config value */

    config = superio_inb(func_slct);

    /* set new config */

    new_config = (config & mask) | bits;

    superio_outb(func_slct, new_config);

    mutex_unlock(&pc8736x_gpio_config_lock);

    return config;

    }

    static u32 pc8736x_gpio_configure(unsigned index, u32 mask, u32 bits)

    {

    return pc8736x_gpio_configure_fn(index, mask, bits,

    SIO_GPIO_PIN_CONFIG);

    }

    static int pc8736x_gpio_get(unsigned minor)

    {

    int port, bit, val;

    port = minor >> 3;

    bit = minor & 7;

    val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);

    val >>= bit;

    val &= 1;

    dev_dbg(&pdev->dev, “_gpio_get(%d from %x bit %d) == val %d”,

    minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,

    val);

    return val;

    }

    static void pc8736x_gpio_set(unsigned minor, int val)

    {

    int port, bit, curval;

    minor &= 0x1f;

    port = minor >> 3;

    bit = minor & 7;

    curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);

    dev_dbg(&pdev->dev, “addr:%x cur:%x bit-pos:%d cur-bit:%x + new:%d -> bit-new:%d”,

    pc8736x_gpio_base + port_offset[port] + PORT_OUT,

    curval, bit, (curval & ~(1 << bit)), val, (val << bit));

    val = (curval & ~(1 << bit)) | (val << bit);

    dev_dbg(&pdev->dev, “gpio_set(minor:%d port:%d bit:%d)”

    ” %2x -> %2x”, minor, port, bit, curval, val);

    outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);

    curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);

    val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);

    dev_dbg(&pdev->dev, “wrote %x, read: %x”, curval, val);

    pc8736x_gpio_shadow[port] = val;

    }

    static int pc8736x_gpio_current(unsigned minor)

    {

    int port, bit;

    minor &= 0x1f;

    port = minor >> 3;

    bit = minor & 7;

    return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);

    }

    static void pc8736x_gpio_change(unsigned index)

    {

    pc8736x_gpio_set(index, !pc8736x_gpio_current(index));

    }

    static struct nsc_gpio_ops pc8736x_gpio_ops = {

    .owner = THIS_MODULE,

    .gpio_config = pc8736x_gpio_configure,

    .gpio_dump = nsc_gpio_dump,

    .gpio_get = pc8736x_gpio_get,

    .gpio_set = pc8736x_gpio_set,

    .gpio_change = pc8736x_gpio_change,

    .gpio_current = pc8736x_gpio_current

    };

    static int pc8736x_gpio_open(struct inode *inode, struct file *file)

    {

    unsigned m = iminor(inode);

    file->private_data = &pc8736x_gpio_ops;

    dev_dbg(&pdev->dev, “open %d”, m);

    if (m >= PC8736X_GPIO_CT)

    return -EINVAL;

    return nonseekable_open(inode, file);

    }

    static const struct file_operations pc8736x_gpio_fileops = {

    .owner = THIS_MODULE,

    .open = pc8736x_gpio_open,

    .write = nsc_gpio_write,

    .read = nsc_gpio_read,

    .llseek = no_llseek,

    };

    static void __init pc8736x_init_shadow(void)

    {

    int port;

    /* read the current values driven on the GPIO signals */

    for (port = 0; port < 4; ++port)

    pc8736x_gpio_shadow[port]

    = inb_p(pc8736x_gpio_base + port_offset[port]

    + PORT_OUT);

    }

    static struct cdev pc8736x_gpio_cdev;

    static int __init pc8736x_gpio_init(void)

    {

    int rc;

    dev_t devid;

    pdev = platform_device_alloc(DEVNAME, 0);

    if (!pdev)

    return -ENOMEM;

    rc = platform_device_add(pdev);

    if (rc) {

    rc = -ENODEV;

    goto undo_platform_dev_alloc;

    }

    dev_info(&pdev->dev, “NatSemi pc8736x GPIO Driver Initializing”);

    if (!pc8736x_superio_present()) {

    rc = -ENODEV;

    dev_err(&pdev->dev, “no device found”);

    goto undo_platform_dev_add;

    }

    pc8736x_gpio_ops.dev = &pdev->dev;

    /* Verify that chip and it’s GPIO unit are both enabled.

    My BIOS does this, so I take minimum action here

    */

    rc = superio_inb(SIO_CF1);

    if (!(rc & 0x01)) {

    rc = -ENODEV;

    dev_err(&pdev->dev, “device not enabled”);

    goto undo_platform_dev_add;

    }

    device_select(SIO_GPIO_UNIT);

    if (!superio_inb(SIO_UNIT_ACT)) {

    rc = -ENODEV;

    dev_err(&pdev->dev, “GPIO unit not enabled”);

    goto undo_platform_dev_add;

    }

    /* read the GPIO unit base addr that chip responds to */

    pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8

    | superio_inb(SIO_BASE_LADDR));

    if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DEVNAME)) {

    rc = -ENODEV;

    dev_err(&pdev->dev, “GPIO ioport %x busy”,

    pc8736x_gpio_base);

    goto undo_platform_dev_add;

    }

    dev_info(&pdev->dev, “GPIO ioport %x reserved”, pc8736x_gpio_base);

    if (major) {

    devid = MKDEV(major, 0);

    rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);

    } else {

    rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);

    major = MAJOR(devid);

    }

    if (rc < 0) {

    dev_err(&pdev->dev, “register-chrdev failed: %d”, rc);

    goto undo_request_region;

    }

    if (!major) {

    major = rc;

    dev_dbg(&pdev->dev, “got dynamic major %d”, major);

    }

    pc8736x_init_shadow();

    /* ignore minor errs, and succeed */

    cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);

    cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);

    return 0;

    undo_request_region:

    release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);

    undo_platform_dev_add:

    platform_device_del(pdev);

    undo_platform_dev_alloc:

    platform_device_put(pdev);

    return rc;

    }

    static void __exit pc8736x_gpio_cleanup(void)

    {

    dev_dbg(&pdev->dev, “cleanup”);

    cdev_del(&pc8736x_gpio_cdev);

    unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);

    release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);

    platform_device_unregister(pdev);

    }

    module_init(pc8736x_gpio_init);

    module_exit(pc8736x_gpio_cleanup);

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶(hù)投稿
    上一篇 2022年6月25日 06:08
    下一篇 2022年6月25日 06:09

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息