Commit 4a2c8ac2bc67d689bc7df130d3105af52fa36b18
1 parent
f34c417b
Initial OMAP I^2C controller implementation (communication not tested).
Correct an i2c_start_transfer comment. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3514 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
428 additions
and
2 deletions
hw/i2c.c
... | ... | @@ -51,8 +51,7 @@ int i2c_bus_busy(i2c_bus *bus) |
51 | 51 | return bus->current_dev != NULL; |
52 | 52 | } |
53 | 53 | |
54 | -/* Returns nonzero if the bus is already busy, or is the address is not | |
55 | - valid. */ | |
54 | +/* Returns non-zero if the address is not valid. */ | |
56 | 55 | /* TODO: Make this handle multiple masters. */ |
57 | 56 | int i2c_start_transfer(i2c_bus *bus, int address, int recv) |
58 | 57 | { | ... | ... |
hw/omap.c
... | ... | @@ -3603,6 +3603,421 @@ static void omap_pwt_init(target_phys_addr_t base, struct omap_mpu_state_s *s, |
3603 | 3603 | cpu_register_physical_memory(s->pwt.base, 0x800, iomemtype); |
3604 | 3604 | } |
3605 | 3605 | |
3606 | +/* Inter-Integrated Circuit Controller (only the "New I2C") */ | |
3607 | +struct omap_i2c_s { | |
3608 | + target_phys_addr_t base; | |
3609 | + qemu_irq irq; | |
3610 | + qemu_irq drq[2]; | |
3611 | + i2c_slave slave; | |
3612 | + i2c_bus *bus; | |
3613 | + | |
3614 | + uint8_t mask; | |
3615 | + uint16_t stat; | |
3616 | + uint16_t dma; | |
3617 | + uint16_t count; | |
3618 | + int count_cur; | |
3619 | + uint32_t fifo; | |
3620 | + int rxlen; | |
3621 | + int txlen; | |
3622 | + uint16_t control; | |
3623 | + uint16_t addr[2]; | |
3624 | + uint8_t divider; | |
3625 | + uint8_t times[2]; | |
3626 | + uint16_t test; | |
3627 | +}; | |
3628 | + | |
3629 | +static void omap_i2c_interrupts_update(struct omap_i2c_s *s) | |
3630 | +{ | |
3631 | + qemu_set_irq(s->irq, s->stat & s->mask); | |
3632 | + if ((s->dma >> 15) & 1) /* RDMA_EN */ | |
3633 | + qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ | |
3634 | + if ((s->dma >> 7) & 1) /* XDMA_EN */ | |
3635 | + qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ | |
3636 | +} | |
3637 | + | |
3638 | +/* These are only stubs now. */ | |
3639 | +static void omap_i2c_event(i2c_slave *i2c, enum i2c_event event) | |
3640 | +{ | |
3641 | + struct omap_i2c_s *s = (struct omap_i2c_s *) i2c; | |
3642 | + | |
3643 | + if ((~s->control >> 15) & 1) /* I2C_EN */ | |
3644 | + return; | |
3645 | + | |
3646 | + switch (event) { | |
3647 | + case I2C_START_SEND: | |
3648 | + case I2C_START_RECV: | |
3649 | + s->stat |= 1 << 9; /* AAS */ | |
3650 | + break; | |
3651 | + case I2C_FINISH: | |
3652 | + s->stat |= 1 << 2; /* ARDY */ | |
3653 | + break; | |
3654 | + case I2C_NACK: | |
3655 | + s->stat |= 1 << 1; /* NACK */ | |
3656 | + break; | |
3657 | + } | |
3658 | + | |
3659 | + omap_i2c_interrupts_update(s); | |
3660 | +} | |
3661 | + | |
3662 | +static int omap_i2c_rx(i2c_slave *i2c) | |
3663 | +{ | |
3664 | + struct omap_i2c_s *s = (struct omap_i2c_s *) i2c; | |
3665 | + uint8_t ret = 0; | |
3666 | + | |
3667 | + if ((~s->control >> 15) & 1) /* I2C_EN */ | |
3668 | + return -1; | |
3669 | + | |
3670 | + if (s->txlen) | |
3671 | + ret = s->fifo >> ((-- s->txlen) << 3) & 0xff; | |
3672 | + else | |
3673 | + s->stat |= 1 << 10; /* XUDF */ | |
3674 | + s->stat |= 1 << 4; /* XRDY */ | |
3675 | + | |
3676 | + omap_i2c_interrupts_update(s); | |
3677 | + return ret; | |
3678 | +} | |
3679 | + | |
3680 | +static int omap_i2c_tx(i2c_slave *i2c, uint8_t data) | |
3681 | +{ | |
3682 | + struct omap_i2c_s *s = (struct omap_i2c_s *) i2c; | |
3683 | + | |
3684 | + if ((~s->control >> 15) & 1) /* I2C_EN */ | |
3685 | + return 1; | |
3686 | + | |
3687 | + if (s->rxlen < 4) | |
3688 | + s->fifo |= data << ((s->rxlen ++) << 3); | |
3689 | + else | |
3690 | + s->stat |= 1 << 11; /* ROVR */ | |
3691 | + s->stat |= 1 << 3; /* RRDY */ | |
3692 | + | |
3693 | + omap_i2c_interrupts_update(s); | |
3694 | + return 1; | |
3695 | +} | |
3696 | + | |
3697 | +static void omap_i2c_fifo_run(struct omap_i2c_s *s) | |
3698 | +{ | |
3699 | + int ack = 1; | |
3700 | + | |
3701 | + if (!i2c_bus_busy(s->bus)) | |
3702 | + return; | |
3703 | + | |
3704 | + if ((s->control >> 2) & 1) { /* RM */ | |
3705 | + if ((s->control >> 1) & 1) { /* STP */ | |
3706 | + i2c_end_transfer(s->bus); | |
3707 | + s->control &= ~(1 << 1); /* STP */ | |
3708 | + s->count_cur = s->count; | |
3709 | + } else if ((s->control >> 9) & 1) { /* TRX */ | |
3710 | + while (ack && s->txlen) | |
3711 | + ack = (i2c_send(s->bus, | |
3712 | + (s->fifo >> ((-- s->txlen) << 3)) & | |
3713 | + 0xff) >= 0); | |
3714 | + s->stat |= 1 << 4; /* XRDY */ | |
3715 | + } else { | |
3716 | + while (s->rxlen < 4) | |
3717 | + s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); | |
3718 | + s->stat |= 1 << 3; /* RRDY */ | |
3719 | + } | |
3720 | + } else { | |
3721 | + if ((s->control >> 9) & 1) { /* TRX */ | |
3722 | + while (ack && s->count_cur && s->txlen) { | |
3723 | + ack = (i2c_send(s->bus, | |
3724 | + (s->fifo >> ((-- s->txlen) << 3)) & | |
3725 | + 0xff) >= 0); | |
3726 | + s->count_cur --; | |
3727 | + } | |
3728 | + if (ack && s->count_cur) | |
3729 | + s->stat |= 1 << 4; /* XRDY */ | |
3730 | + if (!s->count_cur) { | |
3731 | + s->stat |= 1 << 2; /* ARDY */ | |
3732 | + s->control &= ~(1 << 10); /* MST */ | |
3733 | + } | |
3734 | + } else { | |
3735 | + while (s->count_cur && s->rxlen < 4) { | |
3736 | + s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); | |
3737 | + s->count_cur --; | |
3738 | + } | |
3739 | + if (s->rxlen) | |
3740 | + s->stat |= 1 << 3; /* RRDY */ | |
3741 | + } | |
3742 | + if (!s->count_cur) { | |
3743 | + if ((s->control >> 1) & 1) { /* STP */ | |
3744 | + i2c_end_transfer(s->bus); | |
3745 | + s->control &= ~(1 << 1); /* STP */ | |
3746 | + s->count_cur = s->count; | |
3747 | + } else { | |
3748 | + s->stat |= 1 << 2; /* ARDY */ | |
3749 | + s->control &= ~(1 << 10); /* MST */ | |
3750 | + } | |
3751 | + } | |
3752 | + } | |
3753 | + | |
3754 | + s->stat |= (!ack) << 1; /* NACK */ | |
3755 | + if (!ack) | |
3756 | + s->control &= ~(1 << 1); /* STP */ | |
3757 | +} | |
3758 | + | |
3759 | +static void omap_i2c_reset(struct omap_i2c_s *s) | |
3760 | +{ | |
3761 | + s->mask = 0; | |
3762 | + s->stat = 0; | |
3763 | + s->dma = 0; | |
3764 | + s->count = 0; | |
3765 | + s->count_cur = 0; | |
3766 | + s->fifo = 0; | |
3767 | + s->rxlen = 0; | |
3768 | + s->txlen = 0; | |
3769 | + s->control = 0; | |
3770 | + s->addr[0] = 0; | |
3771 | + s->addr[1] = 0; | |
3772 | + s->divider = 0; | |
3773 | + s->times[0] = 0; | |
3774 | + s->times[1] = 0; | |
3775 | + s->test = 0; | |
3776 | +} | |
3777 | + | |
3778 | +static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr) | |
3779 | +{ | |
3780 | + struct omap_i2c_s *s = (struct omap_i2c_s *) opaque; | |
3781 | + int offset = addr - s->base; | |
3782 | + uint16_t ret; | |
3783 | + | |
3784 | + switch (offset) { | |
3785 | + case 0x00: /* I2C_REV */ | |
3786 | + /* TODO: set a value greater or equal to real hardware */ | |
3787 | + return 0x11; /* REV */ | |
3788 | + | |
3789 | + case 0x04: /* I2C_IE */ | |
3790 | + return s->mask; | |
3791 | + | |
3792 | + case 0x08: /* I2C_STAT */ | |
3793 | + return s->stat | (i2c_bus_busy(s->bus) << 12); | |
3794 | + | |
3795 | + case 0x0c: /* I2C_IV */ | |
3796 | + ret = ffs(s->stat & s->mask); | |
3797 | + if (ret) | |
3798 | + s->stat ^= 1 << (ret - 1); | |
3799 | + omap_i2c_interrupts_update(s); | |
3800 | + return ret; | |
3801 | + | |
3802 | + case 0x14: /* I2C_BUF */ | |
3803 | + return s->dma; | |
3804 | + | |
3805 | + case 0x18: /* I2C_CNT */ | |
3806 | + return s->count_cur; /* DCOUNT */ | |
3807 | + | |
3808 | + case 0x1c: /* I2C_DATA */ | |
3809 | + ret = 0; | |
3810 | + if (s->control & (1 << 14)) { /* BE */ | |
3811 | + ret |= ((s->fifo >> 0) & 0xff) << 8; | |
3812 | + ret |= ((s->fifo >> 8) & 0xff) << 0; | |
3813 | + } else { | |
3814 | + ret |= ((s->fifo >> 8) & 0xff) << 8; | |
3815 | + ret |= ((s->fifo >> 0) & 0xff) << 0; | |
3816 | + } | |
3817 | + if (s->rxlen == 1) { | |
3818 | + s->stat |= 1 << 15; /* SBD */ | |
3819 | + s->rxlen = 0; | |
3820 | + } else if (s->rxlen > 1) { | |
3821 | + if (s->rxlen > 2) | |
3822 | + s->fifo >>= 16; | |
3823 | + s->rxlen -= 2; | |
3824 | + } else | |
3825 | + /* XXX: remote access (qualifier) error - what's that? */; | |
3826 | + if (!s->rxlen) { | |
3827 | + s->stat |= ~(1 << 3); /* RRDY */ | |
3828 | + if (((s->control >> 10) & 1) && /* MST */ | |
3829 | + ((~s->control >> 9) & 1)) { /* TRX */ | |
3830 | + s->stat |= 1 << 2; /* ARDY */ | |
3831 | + s->control &= ~(1 << 10); /* MST */ | |
3832 | + } | |
3833 | + } | |
3834 | + s->stat &= ~(1 << 11); /* ROVR */ | |
3835 | + omap_i2c_fifo_run(s); | |
3836 | + omap_i2c_interrupts_update(s); | |
3837 | + return ret; | |
3838 | + | |
3839 | + case 0x24: /* I2C_CON */ | |
3840 | + return s->control; | |
3841 | + | |
3842 | + case 0x28: /* I2C_OA */ | |
3843 | + return s->addr[0]; | |
3844 | + | |
3845 | + case 0x2c: /* I2C_SA */ | |
3846 | + return s->addr[1]; | |
3847 | + | |
3848 | + case 0x30: /* I2C_PSC */ | |
3849 | + return s->divider; | |
3850 | + | |
3851 | + case 0x34: /* I2C_SCLL */ | |
3852 | + return s->times[0]; | |
3853 | + | |
3854 | + case 0x38: /* I2C_SCLH */ | |
3855 | + return s->times[1]; | |
3856 | + | |
3857 | + case 0x3c: /* I2C_SYSTEST */ | |
3858 | + if (s->test & (1 << 15)) { /* ST_EN */ | |
3859 | + s->test ^= 0xa; | |
3860 | + return s->test; | |
3861 | + } else | |
3862 | + return s->test & ~0x300f; | |
3863 | + } | |
3864 | + | |
3865 | + OMAP_BAD_REG(addr); | |
3866 | + return 0; | |
3867 | +} | |
3868 | + | |
3869 | +static void omap_i2c_write(void *opaque, target_phys_addr_t addr, | |
3870 | + uint32_t value) | |
3871 | +{ | |
3872 | + struct omap_i2c_s *s = (struct omap_i2c_s *) opaque; | |
3873 | + int offset = addr - s->base; | |
3874 | + int nack; | |
3875 | + | |
3876 | + switch (offset) { | |
3877 | + case 0x00: /* I2C_REV */ | |
3878 | + case 0x08: /* I2C_STAT */ | |
3879 | + case 0x0c: /* I2C_IV */ | |
3880 | + OMAP_BAD_REG(addr); | |
3881 | + return; | |
3882 | + | |
3883 | + case 0x04: /* I2C_IE */ | |
3884 | + s->mask = value & 0x1f; | |
3885 | + break; | |
3886 | + | |
3887 | + case 0x14: /* I2C_BUF */ | |
3888 | + s->dma = value & 0x8080; | |
3889 | + if (value & (1 << 15)) /* RDMA_EN */ | |
3890 | + s->mask &= ~(1 << 3); /* RRDY_IE */ | |
3891 | + if (value & (1 << 7)) /* XDMA_EN */ | |
3892 | + s->mask &= ~(1 << 4); /* XRDY_IE */ | |
3893 | + break; | |
3894 | + | |
3895 | + case 0x18: /* I2C_CNT */ | |
3896 | + s->count = value; /* DCOUNT */ | |
3897 | + break; | |
3898 | + | |
3899 | + case 0x1c: /* I2C_DATA */ | |
3900 | + if (s->txlen > 2) { | |
3901 | + /* XXX: remote access (qualifier) error - what's that? */ | |
3902 | + break; | |
3903 | + } | |
3904 | + s->fifo <<= 16; | |
3905 | + s->txlen += 2; | |
3906 | + if (s->control & (1 << 14)) { /* BE */ | |
3907 | + s->fifo |= ((value >> 8) & 0xff) << 8; | |
3908 | + s->fifo |= ((value >> 0) & 0xff) << 0; | |
3909 | + } else { | |
3910 | + s->fifo |= ((value >> 0) & 0xff) << 8; | |
3911 | + s->fifo |= ((value >> 8) & 0xff) << 0; | |
3912 | + } | |
3913 | + s->stat &= ~(1 << 10); /* XUDF */ | |
3914 | + if (s->txlen > 2) | |
3915 | + s->stat &= ~(1 << 4); /* XRDY */ | |
3916 | + omap_i2c_fifo_run(s); | |
3917 | + omap_i2c_interrupts_update(s); | |
3918 | + break; | |
3919 | + | |
3920 | + case 0x24: /* I2C_CON */ | |
3921 | + s->control = value & 0xcf07; | |
3922 | + if (~value & (1 << 15)) { /* I2C_EN */ | |
3923 | + omap_i2c_reset(s); | |
3924 | + break; | |
3925 | + } | |
3926 | + if (~value & (1 << 10)) { /* MST */ | |
3927 | + printf("%s: I^2C slave mode not supported\n", __FUNCTION__); | |
3928 | + break; | |
3929 | + } | |
3930 | + if (value & (1 << 9)) { /* XA */ | |
3931 | + printf("%s: 10-bit addressing mode not supported\n", __FUNCTION__); | |
3932 | + break; | |
3933 | + } | |
3934 | + if (value & (1 << 0)) { /* STT */ | |
3935 | + nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ | |
3936 | + (~value >> 9) & 1); /* TRX */ | |
3937 | + s->stat |= nack << 1; /* NACK */ | |
3938 | + s->control &= ~(1 << 0); /* STT */ | |
3939 | + if (nack) | |
3940 | + s->control &= ~(1 << 1); /* STP */ | |
3941 | + else | |
3942 | + omap_i2c_fifo_run(s); | |
3943 | + omap_i2c_interrupts_update(s); | |
3944 | + } | |
3945 | + break; | |
3946 | + | |
3947 | + case 0x28: /* I2C_OA */ | |
3948 | + s->addr[0] = value & 0x3ff; | |
3949 | + i2c_set_slave_address(&s->slave, value & 0x7f); | |
3950 | + break; | |
3951 | + | |
3952 | + case 0x2c: /* I2C_SA */ | |
3953 | + s->addr[1] = value & 0x3ff; | |
3954 | + break; | |
3955 | + | |
3956 | + case 0x30: /* I2C_PSC */ | |
3957 | + s->divider = value; | |
3958 | + break; | |
3959 | + | |
3960 | + case 0x34: /* I2C_SCLL */ | |
3961 | + s->times[0] = value; | |
3962 | + break; | |
3963 | + | |
3964 | + case 0x38: /* I2C_SCLH */ | |
3965 | + s->times[1] = value; | |
3966 | + break; | |
3967 | + | |
3968 | + case 0x3c: /* I2C_SYSTEST */ | |
3969 | + s->test = value & 0xf00f; | |
3970 | + if (value & (1 << 15)) /* ST_EN */ | |
3971 | + printf("%s: System Test not supported\n", __FUNCTION__); | |
3972 | + break; | |
3973 | + | |
3974 | + default: | |
3975 | + OMAP_BAD_REG(addr); | |
3976 | + return; | |
3977 | + } | |
3978 | +} | |
3979 | + | |
3980 | +static CPUReadMemoryFunc *omap_i2c_readfn[] = { | |
3981 | + omap_badwidth_read16, | |
3982 | + omap_i2c_read, | |
3983 | + omap_badwidth_read16, | |
3984 | +}; | |
3985 | + | |
3986 | +static CPUWriteMemoryFunc *omap_i2c_writefn[] = { | |
3987 | + omap_badwidth_write16, | |
3988 | + omap_i2c_write, | |
3989 | + omap_i2c_write, /* TODO: Only the last fifo write can be 8 bit. */ | |
3990 | +}; | |
3991 | + | |
3992 | +struct omap_i2c_s *omap_i2c_init(target_phys_addr_t base, | |
3993 | + qemu_irq irq, qemu_irq *dma, omap_clk clk) | |
3994 | +{ | |
3995 | + int iomemtype; | |
3996 | + struct omap_i2c_s *s = (struct omap_i2c_s *) | |
3997 | + qemu_mallocz(sizeof(struct omap_i2c_s)); | |
3998 | + | |
3999 | + s->base = base; | |
4000 | + s->irq = irq; | |
4001 | + s->drq[0] = dma[0]; | |
4002 | + s->drq[1] = dma[1]; | |
4003 | + s->slave.event = omap_i2c_event; | |
4004 | + s->slave.recv = omap_i2c_rx; | |
4005 | + s->slave.send = omap_i2c_tx; | |
4006 | + s->bus = i2c_init_bus(); | |
4007 | + omap_i2c_reset(s); | |
4008 | + | |
4009 | + iomemtype = cpu_register_io_memory(0, omap_i2c_readfn, | |
4010 | + omap_i2c_writefn, s); | |
4011 | + cpu_register_physical_memory(s->base, 0x800, iomemtype); | |
4012 | + | |
4013 | + return s; | |
4014 | +} | |
4015 | + | |
4016 | +i2c_bus *omap_i2c_bus(struct omap_i2c_s *s) | |
4017 | +{ | |
4018 | + return s->bus; | |
4019 | +} | |
4020 | + | |
3606 | 4021 | /* General chip reset */ |
3607 | 4022 | static void omap_mpu_reset(void *opaque) |
3608 | 4023 | { |
... | ... | @@ -3634,6 +4049,8 @@ static void omap_mpu_reset(void *opaque) |
3634 | 4049 | omap_gpio_reset(mpu->gpio); |
3635 | 4050 | omap_uwire_reset(mpu->microwire); |
3636 | 4051 | omap_pwl_reset(mpu); |
4052 | + omap_pwt_reset(mpu); | |
4053 | + omap_i2c_reset(mpu->i2c); | |
3637 | 4054 | cpu_reset(mpu->env); |
3638 | 4055 | } |
3639 | 4056 | |
... | ... | @@ -3758,6 +4175,9 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, |
3758 | 4175 | omap_pwl_init(0xfffb5800, s, omap_findclk(s, "clk32-kHz")); |
3759 | 4176 | omap_pwt_init(0xfffb6000, s, omap_findclk(s, "xtal_osc_12m")); |
3760 | 4177 | |
4178 | + s->i2c = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C], | |
4179 | + &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck")); | |
4180 | + | |
3761 | 4181 | qemu_register_reset(omap_mpu_reset, s); |
3762 | 4182 | |
3763 | 4183 | return s; | ... | ... |
hw/omap.h
... | ... | @@ -475,6 +475,11 @@ struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base, |
475 | 475 | void omap_uwire_attach(struct omap_uwire_s *s, |
476 | 476 | struct uwire_slave_s *slave, int chipselect); |
477 | 477 | |
478 | +struct omap_i2c_s; | |
479 | +struct omap_i2c_s *omap_i2c_init(target_phys_addr_t base, | |
480 | + qemu_irq irq, qemu_irq *dma, omap_clk clk); | |
481 | +i2c_bus *omap_i2c_bus(struct omap_i2c_s *s); | |
482 | + | |
478 | 483 | /* omap_lcdc.c */ |
479 | 484 | struct omap_lcd_panel_s; |
480 | 485 | void omap_lcdc_reset(struct omap_lcd_panel_s *s); |
... | ... | @@ -550,6 +555,8 @@ struct omap_mpu_state_s { |
550 | 555 | omap_clk clk; |
551 | 556 | } pwt; |
552 | 557 | |
558 | + struct omap_i2c_s *i2c; | |
559 | + | |
553 | 560 | /* MPU private TIPB peripherals */ |
554 | 561 | struct omap_intr_handler_s *ih[2]; |
555 | 562 | ... | ... |