1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Generic IXP4xx beeper driver 4 * 5 * Copyright (C) 2005 Tower Technologies 6 * 7 * based on nslu2-io.c 8 * Copyright (C) 2004 Karen Spearel 9 * 10 * Author: Alessandro Zummo <a.zummo@towertech.it> 11 * Maintainers: http://www.nslu2-linux.org/ 12 */ 13 14#include <linux/module.h> 15#include <linux/input.h> 16#include <linux/delay.h> 17#include <linux/platform_device.h> 18#include <linux/interrupt.h> 19#include <linux/gpio.h> 20#include <mach/hardware.h> 21 22MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 23MODULE_DESCRIPTION("ixp4xx beeper driver"); 24MODULE_LICENSE("GPL"); 25MODULE_ALIAS("platform:ixp4xx-beeper"); 26 27static DEFINE_SPINLOCK(beep_lock); 28 29static int ixp4xx_timer2_irq; 30 31static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) 32{ 33 unsigned long flags; 34 35 spin_lock_irqsave(&beep_lock, flags); 36 37 if (count) { 38 gpio_direction_output(pin, 0); 39 *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; 40 } else { 41 gpio_direction_output(pin, 1); 42 gpio_direction_input(pin); 43 *IXP4XX_OSRT2 = 0; 44 } 45 46 spin_unlock_irqrestore(&beep_lock, flags); 47} 48 49static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 50{ 51 unsigned int pin = (unsigned int) input_get_drvdata(dev); 52 unsigned int count = 0; 53 54 if (type != EV_SND) 55 return -1; 56 57 switch (code) { 58 case SND_BELL: 59 if (value) 60 value = 1000; 61 case SND_TONE: 62 break; 63 default: 64 return -1; 65 } 66 67 if (value > 20 && value < 32767) 68 count = (ixp4xx_timer_freq / (value * 4)) - 1; 69 70 ixp4xx_spkr_control(pin, count); 71 72 return 0; 73} 74 75static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) 76{ 77 unsigned int pin = (unsigned int) dev_id; 78 79 /* clear interrupt */ 80 *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND; 81 82 /* flip the beeper output */ 83 gpio_set_value(pin, !gpio_get_value(pin)); 84 85 return IRQ_HANDLED; 86} 87 88static int ixp4xx_spkr_probe(struct platform_device *dev) 89{ 90 struct input_dev *input_dev; 91 int irq; 92 int err; 93 94 input_dev = input_allocate_device(); 95 if (!input_dev) 96 return -ENOMEM; 97 98 input_set_drvdata(input_dev, (void *) dev->id); 99 100 input_dev->name = "ixp4xx beeper", 101 input_dev->phys = "ixp4xx/gpio"; 102 input_dev->id.bustype = BUS_HOST; 103 input_dev->id.vendor = 0x001f; 104 input_dev->id.product = 0x0001; 105 input_dev->id.version = 0x0100; 106 input_dev->dev.parent = &dev->dev; 107 108 input_dev->evbit[0] = BIT_MASK(EV_SND); 109 input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); 110 input_dev->event = ixp4xx_spkr_event; 111 112 irq = platform_get_irq(dev, 0); 113 if (irq < 0) { 114 err = irq; 115 goto err_free_device; 116 } 117 118 err = gpio_request(dev->id, "ixp4-beeper"); 119 if (err) 120 goto err_free_device; 121 122 err = request_irq(irq, &ixp4xx_spkr_interrupt, 123 IRQF_NO_SUSPEND, "ixp4xx-beeper", 124 (void *) dev->id); 125 if (err) 126 goto err_free_gpio; 127 ixp4xx_timer2_irq = irq; 128 129 err = input_register_device(input_dev); 130 if (err) 131 goto err_free_irq; 132 133 platform_set_drvdata(dev, input_dev); 134 135 return 0; 136 137 err_free_irq: 138 free_irq(irq, (void *)dev->id); 139 err_free_gpio: 140 gpio_free(dev->id); 141 err_free_device: 142 input_free_device(input_dev); 143 144 return err; 145} 146 147static int ixp4xx_spkr_remove(struct platform_device *dev) 148{ 149 struct input_dev *input_dev = platform_get_drvdata(dev); 150 unsigned int pin = (unsigned int) input_get_drvdata(input_dev); 151 152 input_unregister_device(input_dev); 153 154 /* turn the speaker off */ 155 disable_irq(ixp4xx_timer2_irq); 156 ixp4xx_spkr_control(pin, 0); 157 158 free_irq(ixp4xx_timer2_irq, (void *)dev->id); 159 gpio_free(dev->id); 160 161 return 0; 162} 163 164static void ixp4xx_spkr_shutdown(struct platform_device *dev) 165{ 166 struct input_dev *input_dev = platform_get_drvdata(dev); 167 unsigned int pin = (unsigned int) input_get_drvdata(input_dev); 168 169 /* turn off the speaker */ 170 disable_irq(ixp4xx_timer2_irq); 171 ixp4xx_spkr_control(pin, 0); 172} 173 174static struct platform_driver ixp4xx_spkr_platform_driver = { 175 .driver = { 176 .name = "ixp4xx-beeper", 177 }, 178 .probe = ixp4xx_spkr_probe, 179 .remove = ixp4xx_spkr_remove, 180 .shutdown = ixp4xx_spkr_shutdown, 181}; 182module_platform_driver(ixp4xx_spkr_platform_driver); 183 184