1 /* 2 * Export a JZ4730 PWM peripheral as a server. 3 * 4 * (c) 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/gpio-jz4730.h> 23 #include <l4/devices/pwm-jz4730.h> 24 25 #include <l4/cxx/ipc_server> 26 #include <l4/re/env> 27 #include <l4/re/util/object_registry> 28 29 #include <stdint.h> 30 #include <stdlib.h> 31 32 #include "pwm-ops.h" 33 #include "memory.h" 34 35 /* Virtual addresses for the GPIO and PWM register blocks. */ 36 37 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 38 static l4_addr_t pwm_virt_base = 0, pwm_virt_base_end = 0; 39 40 41 42 static int setup_memory(void) 43 { 44 if (get_memory("jz4730-gpio", &gpio_virt_base, &gpio_virt_base_end)) 45 return 1; 46 47 if (get_memory("jz4730-pwm", &pwm_virt_base, &pwm_virt_base_end)) 48 return 1; 49 50 return 0; 51 } 52 53 54 55 /* PWM peripheral device. */ 56 57 class Pwm_device_server : public L4::Server_object_t<L4::Kobject> 58 { 59 Pwm_jz4730_chip *_device = 0; 60 int _duty, _period, _prescale; 61 62 public: 63 /* Associate the device with a particular memory region. */ 64 65 explicit Pwm_device_server(Pwm_jz4730_chip *device, int duty, int period, int prescale) 66 : _device(device), _duty(duty), _period(period), _prescale(prescale) 67 { 68 } 69 70 /* Dispatch incoming requests. */ 71 72 int dispatch(l4_umword_t obj, L4::Ipc::Iostream &ios) 73 { 74 l4_msgtag_t tag; 75 int arg; 76 77 (void) obj; 78 ios >> tag; 79 80 switch (tag.label()) 81 { 82 case Pwm_op_disable: 83 disable(); 84 return L4_EOK; 85 86 case Pwm_op_enable: 87 enable(); 88 return L4_EOK; 89 90 case Pwm_op_set_control: 91 ios >> arg; 92 set_control(arg); 93 return L4_EOK; 94 95 case Pwm_op_set_duty: 96 ios >> arg; 97 set_duty(arg); 98 return L4_EOK; 99 100 case Pwm_op_set_period: 101 ios >> arg; 102 set_period(arg); 103 return L4_EOK; 104 105 default: 106 return -L4_EBADPROTO; 107 } 108 } 109 110 void disable() 111 { 112 _device->disable(); 113 } 114 115 void enable() 116 { 117 set_duty(_duty); 118 set_period(_period); 119 set_control(0x80 | _prescale); /* enable | prescale */ 120 } 121 122 /* Set the control register. */ 123 124 void set_control(uint8_t control) 125 { 126 _device->set_control(control); 127 } 128 129 /* Set the PWM duty cycle. */ 130 131 void set_duty(uint16_t duty) 132 { 133 _duty = duty; 134 _device->set_duty(duty); 135 } 136 137 /* Set the PWM period. */ 138 139 void set_period(uint16_t period) 140 { 141 _period = period; 142 _device->set_period(period); 143 } 144 }; 145 146 static L4Re::Util::Registry_server<> server; 147 148 149 150 /* Initialise devices and start the server. */ 151 152 static void run(int number, int duty, int period, int prescale) 153 { 154 Pwm_jz4730_chip pwm_device(pwm_virt_base + number * 0x1000, 155 pwm_virt_base + (number + 1) * 0x1000); 156 157 Gpio_jz4730_chip gpio_port_c(gpio_virt_base + 2 * 0x30, 158 gpio_virt_base + 3 * 0x30, 32); 159 160 /* Enable the PWM output for PC30 or PC31. */ 161 162 gpio_port_c.config_pad(30 + number, Hw::Gpio_chip::Function_alt, 1); 163 164 /* Initialise and register a new server object. */ 165 166 Pwm_device_server server_obj(&pwm_device, duty, period, prescale); 167 server.registry()->register_obj(&server_obj, "pwm"); 168 169 /* Enter the IPC server loop. */ 170 171 server.loop(); 172 } 173 174 175 176 /* Arguments: <PWM device number> <duty> <period> <prescale> */ 177 178 int main(int argc, char *argv[]) 179 { 180 int number, duty, period, prescale; 181 182 if (argc < 5) return 1; 183 184 /* Interpret and restrict the device number. */ 185 186 number = atoi(argv[1]); 187 if ((number < 0) || (number > 1)) return 1; 188 189 /* Interpret and restrict the initial parameters. */ 190 191 duty = atoi(argv[2]); 192 period = atoi(argv[3]); 193 prescale = atoi(argv[4]); 194 if ((duty < 0) || (period < 0) || (prescale < 0)) return 1; 195 196 /* Obtain access to peripheral memory. */ 197 198 if (setup_memory()) return 1; 199 200 run(number, duty, period, prescale); 201 return 0; 202 }