static int init_adc_register() { // Initialize ADC. The following two lines set the appropriate PMC bit // for the ADC. Easier than mapping PMC registers and then setting the bit. at91adc_clk = clk_get(NULL, // Device pointer - not required. "adc_clk"); // Clock name. clk_enable(at91adc_clk); // Reset the ADC iowrite32(AT91_ADC_SWRST, (adc_channel.membase + AT91_ADC_CR)); // Enable both ADC channels iowrite32((AT91_ADC_CH(1) | AT91_ADC_CH(0)), (adc_channel.membase + AT91_ADC_CHER)); // Configure ADC mode register. // From table 43-31 in page #775 and page#741 of AT91SAM9260 user manual: // Maximum ADC clock frequency = 5MHz = MCK / ((PRESCAL+1) * 2) // PRESCAL = ((MCK / 5MHz) / 2) -1 = ((100MHz / 5MHz)/2)-1) = 9 // Maximum startup time = 15uS = (STARTUP+1)*8/ADC_CLOCK // STARTUP = ((15uS*ADC_CLOK)/8)-1 = ((15uS*5MHz)/8)-1 = 9 // Minimum hold time = 1.2uS = (SHTIM+1)/ADC_CLOCK // SHTIM = (1.2uS*ADC_CLOCK)-1 = (1.2uS*5MHz)-1 = 5, Use 9 to ensure 2uS hold time. // Enable sleep mode and hardware trigger from TIOA output from TC0. iowrite32((AT91_ADC_SHTIM_(9) | AT91_ADC_STARTUP_(9) | AT91_ADC_PRESCAL_(9) | AT91_ADC_SLEEP ), (adc_channel.membase + AT91_ADC_MR)); return 0; }
static int at91_adc_read_chan(int chan){ int val, sr; if(chan<0 || chan>3){ return -EINVAL; } /* disable pull-up resistor */ iowrite32(1 << chan, at91_pioc_base + 0x60); at91_adc_write(AT91_ADC_CHER,AT91_ADC_CH(chan)); //Enable Channel at91_adc_write(AT91_ADC_CR,AT91_ADC_START); //Start the ADC for(sr=0; !(sr & AT91_ADC_EOC(chan)); sr=at91_adc_read(AT91_ADC_SR)) cpu_relax(); val=at91_adc_read(AT91_ADC_CHR(chan)) & AT91_ADC_DATA; //Read up to 10 bits return val; }
/***************************************************************************************** | Module initialization: Allocate device numbers, register device, setup ADC and timer | | counter registers for 100 msec periodic sampling. | *****************************************************************************************/ static int __init at91adc_init (void) { int ret; // Dynamically allocate major number and minor number ret = alloc_chrdev_region(&at91adc_devno, // pointer to where the device number to be stored 0, // first minor number requested 1, // number of devices "at91adc"); // device name if (ret < 0) { printk(KERN_INFO "at91adc: Device number allocation failed\n"); ret = -ENODEV; goto exit_1; } // Initialize cdev structure. cdev_init(&at91adc_cdev, // pointer to the cdev structure &at91adc_fops); // pointer to the file operations structure. at91adc_cdev.owner = THIS_MODULE; at91adc_cdev.ops = &at91adc_fops; // Register the device with kernel ret = cdev_add(&at91adc_cdev, // pointer to the initialized cdev structure at91adc_devno, // device number allocated 1); // number of devices if (ret != 0) { printk(KERN_INFO "at91adc: Device registration failed\n"); ret = -ECANCELED; goto exit_2; } // Character device driver initialization complete. Do device specific initialization now. // Allocate ring buffer memory for storing ADC values for both channels. at91adc_pbuf0 = (unsigned short *)kmalloc((MAX_ADCSAMPLES * sizeof(unsigned short)), // Number of bytes GFP_KERNEL); // Flags at91adc_pbuf1 = (unsigned short *)kmalloc((MAX_ADCSAMPLES * sizeof(unsigned short)), // Number of bytes GFP_KERNEL); // Flags at91adc_pbuf2 = (unsigned short *)kmalloc((MAX_ADCSAMPLES * sizeof(unsigned short)), // Number of bytes GFP_KERNEL); // Flags at91adc_pbuf3 = (unsigned short *)kmalloc((MAX_ADCSAMPLES * sizeof(unsigned short)), // Number of bytes GFP_KERNEL); // Flags if ((at91adc_pbuf0 == NULL) || (at91adc_pbuf1 == NULL) || (at91adc_pbuf2 == NULL) || (at91adc_pbuf3 == NULL)) { printk(KERN_INFO "at91adc: Memory allocation failed\n"); ret = -ECANCELED; goto exit_3; } // Initialize the ring buffer and append index. at91adc_appidx = MAX_ADCSAMPLES; for (ret = 0; ret < MAX_ADCSAMPLES; ret++) { at91adc_pbuf0[ret] = 0; at91adc_pbuf1[ret] = 0; at91adc_pbuf2[ret] = 0; at91adc_pbuf3[ret] = 0; } // Initialize ADC. The following two lines set the appropriate PMC bit // for the ADC. Easier than mapping PMC registers and then setting the bit. at91adc_clk = clk_get(NULL, // Device pointer - not required. "adc_clk"); // Clock name. clk_enable(at91adc_clk); // Map ADC registers to the current address space. at91adc_base = ioremap_nocache(AT91SAM9260_BASE_ADC, // Physical address 64); // Number of bytes to be mapped. if (at91adc_base == NULL) { printk(KERN_INFO "at91adc: ADC memory mapping failed\n"); ret = -EACCES; goto exit_4; } // MUX GPIO pins for ADC (peripheral A) operation at91_set_A_periph(AT91_PIN_PC0, 0); at91_set_A_periph(AT91_PIN_PC1, 0); at91_set_A_periph(AT91_PIN_PC2, 0); at91_set_A_periph(AT91_PIN_PC3, 0); // Reset the ADC iowrite32(AT91_ADC_SWRST, (at91adc_base + AT91_ADC_CR)); // Enable all ADC channels iowrite32((AT91_ADC_CH(3) | AT91_ADC_CH(2) | AT91_ADC_CH(1) | AT91_ADC_CH(0)), (at91adc_base + AT91_ADC_CHER)); // Configure ADC mode register. // From table 43-31 in page #775 and page#741 of AT91SAM9260 user manual: // Maximum ADC clock frequency = 5MHz = MCK / ((PRESCAL+1) * 2) // PRESCAL = ((MCK / 5MHz) / 2) -1 = ((100MHz / 5MHz)/2)-1) = 9 // Maximum startup time = 15uS = (STARTUP+1)*8/ADC_CLOCK // STARTUP = ((15uS*ADC_CLOK)/8)-1 = ((15uS*5MHz)/8)-1 = 9 // Minimum hold time = 1.2uS = (SHTIM+1)/ADC_CLOCK // SHTIM = (1.2uS*ADC_CLOCK)-1 = (1.2uS*5MHz)-1 = 5, Use 9 to ensure 2uS hold time. // Enable sleep mode and hardware trigger from TIOA output from TC0. iowrite32((AT91_ADC_TRGSEL_TC0 | AT91_ADC_SHTIM_(9) | AT91_ADC_STARTUP_(9) | AT91_ADC_PRESCAL_(9) | /*AT91_ADC_SLEEP |*/ AT91_ADC_TRGEN), (at91adc_base + AT91_ADC_MR)); // Initialize Timer Counter module 0. The following two lines set the appropriate // PMC bit for TC0. Easier than mapping PMC registers and then setting the bit. at91tc0_clk = clk_get(NULL, // Device pointer - not required. "tc0_clk"); // Clock name. clk_enable(at91tc0_clk); // Map TC0 registers to the current address space. at91tc0_base = ioremap_nocache(AT91SAM9260_BASE_TC0, // Physical address 64); // Number of bytes to be mapped. if (at91tc0_base == NULL) { printk(KERN_INFO "at91adc: TC0 memory mapping failed\n"); ret = -EACCES; goto exit_5; } // Configure TC0 in waveform mode, TIMER_CLK1 and to generate interrupt on RC compare. // Load 50000 to RC so that with TIMER_CLK1 = MCK/2 = 50MHz, the interrupt will be // generated every 1/50MHz * 50000 = 20nS * 50000 = 1 milli second. // NOTE: Even though AT91_TC_RC is a 32-bit register, only 16-bits are programmble. //printk(KERN_INFO "RC: %08X\n",ioread32(at91tc0_base + AT91_TC_RC)); //printk(KERN_INFO "CMR: %08X\n",ioread32(at91tc0_base + AT91_TC_CMR)); //printk(KERN_INFO "IMR: %08X\n",ioread32(at91tc0_base + AT91_TC_IMR)); //printk(KERN_INFO "BMR: %08X\n",ioread32(at91tc0_base + AT91_TC_BMR)); //printk(KERN_INFO "SR: %08X\n",ioread32(at91tc0_base + AT91_TC_SR)); iowrite32(1134 /*50000*/, (at91tc0_base + AT91_TC_RC)); /// konfiguracja timera 44khz jak zmiana to zmieniæ wartoœæ w rej AT91_TC_RC // skopiowac do ioctl z odpowiednimi case'ami od argumentu arg. iowrite32((AT91_TC_WAVE | AT91_TC_WAVESEL_UP_AUTO), (at91tc0_base + AT91_TC_CMR)); iowrite32(AT91_TC_CPCS, (at91tc0_base + AT91_TC_IDR)); // wy³¹czane przerwanie od timera iowrite32((AT91_TC_SWTRG | AT91_TC_CLKEN), (at91tc0_base + AT91_TC_CCR)); // Install interrupt for TC0. ret = request_irq(AT91SAM9260_ID_TC0, // Interrupt number at91tc0_isr, // Pointer to the interrupt sub-routine IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL, // Flags - fast, shared or contributing to entropy pool "at91adc", // Device name to show as owner in /proc/interrupts NULL); // Private data for shared interrupts ret = 0; if (ret != 0) { printk(KERN_INFO "at91adc: Timer interrupt request failed\n"); ret = -EBUSY; goto exit_6; } printk(KERN_INFO "at91adc: Loaded module\n"); return 0; exit_6: iounmap(at91tc0_base); exit_5: clk_disable(at91tc0_clk); iounmap(at91adc_base); exit_4: clk_disable(at91adc_clk); exit_3: kfree(at91adc_pbuf0); kfree(at91adc_pbuf1); kfree(at91adc_pbuf2); kfree(at91adc_pbuf3); exit_2: // Free device number allocated. unregister_chrdev_region(at91adc_devno, // allocated device number 1); // number of devices exit_1: return ret; }